Java tutorial
/******************************************************************************* * Copyright (c) 2006-2013 The RCP Company and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * The RCP Company - initial API and implementation *******************************************************************************/ package com.rcpcompany.uibindings.uiAttributes; import java.util.List; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.util.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.graphics.TextLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.forms.FormColors; import org.eclipse.ui.plugin.AbstractUIPlugin; import com.rcpcompany.uibindings.Constants; import com.rcpcompany.uibindings.IManager; import com.rcpcompany.uibindings.IUIAttribute; import com.rcpcompany.uibindings.internal.Activator; /** * A painter for {@link IUIAttribute}. * <p> * Used to paint a single virtual {@link IUIAttribute} for a label provider, a grid renderer or * similar. * <p> * Kept as close as possible to StyledCellLabelProvider. * * @author Tonny Madsen, The RCP Company */ public class UIAttributePainter { /** * Extra cell height used on XP to ensure the cells that the correct space around checkboxes. */ private static final int EXTRA_CELL_HEIGHT = 3; /** * The margin around the image and text of a cell. */ public static final int MARGIN = 3; /** * The parent control of this painter. * <p> * Certain attributes of the control is used... */ private final Control myParentControl; /** * The minimum height for a cell to accommodate the check box images. */ private static int myMinHeight; /** * Returns the minimum height for a cell to accommodate the check box images. * * @return the minimum height */ public static int getMinHeight() { return myMinHeight; } private static final String CHECKED_KEY = UIAttributePainter.class.getName() + "$CHECKED"; private static final String UNCHECKED_KEY = UIAttributePainter.class.getName() + "$UNCHECKED"; /** * Constructs and returns a new UI Attribute painter for the specified parent control. * * @param parentControl the parent control * @param attribute the UI attribute */ public UIAttributePainter(Control parentControl, IUIAttribute attribute) { myParentControl = parentControl; myAttribute = attribute; /* * Here - and not static - as the Display may not be setup correctly before now */ if (JFaceResources.getImageRegistry().getDescriptor(CHECKED_KEY) == null) { JFaceResources.getImageRegistry().put(UNCHECKED_KEY, makeShot(parentControl, false)); JFaceResources.getImageRegistry().put(CHECKED_KEY, makeShot(parentControl, true)); } final FormColors colors = IManager.Factory.getManager().getFormToolkit(parentControl).getColors(); mySelectionBackground = JFaceResources.getColorRegistry() .get(Constants.COLOR_DEFINITIONS_SELECTION_FOCUS_BACKGROUND); mySelectionNoFocusBackground = JFaceResources.getColorRegistry() .get(Constants.COLOR_DEFINITIONS_SELECTION_NO_FOCUS_BACKGROUND); mySelectionForeground = myParentControl.getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT); myFocusBorder = colors.getBorderColor(); } /** * The attribute painted by this painter. */ private IUIAttribute myAttribute = null; /** * The horizontal alignment. */ private int myHorizontalAlignment = SWT.NONE; private Color myDefaultBackground = null; private boolean myFocus = false; private boolean mySelected = false; /** * Whether to use the internal values for the painter. */ private boolean myInternalValues = false; private String myInternalText = null; private Image myInternalImage = null; /* * Cached results from preparePainter(...) */ private Image myPreparedImage = null; private Rectangle myPreparedImageBounds = null; private TextLayout myPreparedTextLayout = null; private Rectangle myPreparedTextBounds = null; private int myPreparedTotalX = 0; private int myPreparedTextWidthDelta; /** * Prepared the painter.. * * @param gc the GC to use */ protected void preparePainter(GC gc) { myPreparedImage = null; myPreparedImageBounds = null; myPreparedTextBounds = null; myPreparedTotalX = 0; /* * Image */ myPreparedImage = getDisplayImage(); if (myPreparedImage != null) { myPreparedImageBounds = myPreparedImage.getBounds(); // center the image in the given space myPreparedTotalX += myPreparedImageBounds.width; } /* * Text */ final String t = getDisplayText(); if (t != null) { if (myPreparedTextLayout == null) { myPreparedTextLayout = new TextLayout(myParentControl.getDisplay()); myPreparedTextLayout .setOrientation(myParentControl.getStyle() & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT)); } /* * Set to two different values, to make sure the styles are reset... */ myPreparedTextLayout.setText(""); myPreparedTextLayout.setText(t); myPreparedTextLayout.setFont(getAttribute().getFont()); // text width without any styles final int originalTextWidth = myPreparedTextLayout.getBounds().width; boolean containsOtherFont = false; final List<StyleRange> styleRanges = getAttribute().getStyleRanges(); if (styleRanges != null && !styleRanges.isEmpty()) { // user filled styled ranges for (final StyleRange styleRange : styleRanges) { myPreparedTextLayout.setStyle(styleRange, styleRange.start, styleRange.start + styleRange.length - 1); if (styleRange.font != null) { containsOtherFont = true; } } } if (containsOtherFont) { myPreparedTextWidthDelta = myPreparedTextLayout.getBounds().width - originalTextWidth; } else { myPreparedTextWidthDelta = 0; } myPreparedTextBounds = myPreparedTextLayout.getBounds(); if (myPreparedImageBounds != null) { myPreparedTotalX += MARGIN; myPreparedTextBounds.x += myPreparedImageBounds.width + MARGIN; } myPreparedTotalX += myPreparedTextBounds.width; } } /** * Measures and updates the bounds needed to paint this UI attribute. * * @param gc the GC to use * @param bounds the current bounds for the attribute */ public void measure(GC gc, Rectangle bounds) { preparePainter(gc); bounds.width += myPreparedTextWidthDelta; } /** * Returns the width difference for text of this cell when applying fonts to the text. * * @param gc the GC to use for the calculation * @return the size difference */ public int getTextWidthDelta(GC gc) { preparePainter(gc); return myPreparedTextWidthDelta; } /** * Paints this UI attribute within the specified bounds. * * @param gc the GC to use * @param areaBounds the bounds of the area */ public void paint(GC gc, Rectangle areaBounds) { paintTextAndImage(gc, areaBounds); } private void paintTextAndImage(GC gc, Rectangle areaBounds) { preparePainter(gc); // Remember colors to restore the GC later final Color oldForeground = gc.getForeground(); final Color oldBackground = gc.getBackground(); /* * Colors */ Color foreground = oldForeground; final Color f = getAttribute().getForeground(); if (f != null) { foreground = f; } if (getAttribute().isEnabled() == Boolean.FALSE) { foreground = Display.getCurrent().getSystemColor(SWT.COLOR_GRAY); } // if (!foreground.equals(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK))) { // LogUtils.debug(this, "TODO foreground not black, but " + foreground.getRGB()); // } Color background = getDefaultBackground(); final Color b = getAttribute().getBackground(); if (b != null) { background = b; } if (isSelected() || hasFocus()) { if (myParentControl.isFocusControl()) { background = mySelectionBackground; foreground = mySelectionForeground; } else { background = mySelectionNoFocusBackground; } } if (background != null) { gc.setBackground(background); } // LogUtils.debug(this, "\nf=" + gc.getForeground() + " " + foreground + // "\nb=" + gc.getBackground() + " " // + background); gc.fillRectangle(areaBounds); /* * Border */ if (hasFocus() && myParentControl.isFocusControl()) { gc.setForeground(myFocusBorder); // gc.drawFocus(areaBounds.x, areaBounds.y, areaBounds.width, // areaBounds.height); final int oldLineWidth = gc.getLineWidth(); gc.setLineWidth(2); gc.drawRectangle(areaBounds.x + 1, areaBounds.y + 1, areaBounds.width - 2, areaBounds.height - 2); gc.setLineWidth(oldLineWidth); } gc.setForeground(foreground); int offsetX = 0; switch (getHorizontalAlignment()) { case SWT.CENTER: offsetX = Math.max(0, (areaBounds.width - myPreparedTotalX) / 2); break; case SWT.RIGHT: offsetX = Math.max(0, areaBounds.width - myPreparedTotalX - MARGIN); break; case SWT.NONE: case SWT.LEFT: default: offsetX = MARGIN; break; } if (myPreparedImageBounds != null) { final int x = myPreparedImageBounds.x + areaBounds.x + offsetX; final int y = myPreparedImageBounds.y + areaBounds.y + Math.max(0, (areaBounds.height - myPreparedImageBounds.height) / 2); gc.drawImage(myPreparedImage, x, y); } if (myPreparedTextBounds != null) { final int x = myPreparedTextBounds.x + areaBounds.x + offsetX; final int y = myPreparedTextBounds.y + areaBounds.y + Math.max(0, (areaBounds.height - myPreparedTextBounds.height) / 2); myPreparedTextLayout.draw(gc, x, y); } gc.setForeground(oldForeground); gc.setBackground(oldBackground); } private final Color mySelectionBackground; private final Color mySelectionNoFocusBackground; private final Color mySelectionForeground; private final Color myFocusBorder; /** * Returns the text used for the current attribute if any. * * @return the display text or null * */ public String getDisplayText() { if (myInternalValues) return myInternalText; final IObservableValue displayValue = getAttribute().getCurrentValue(); if (displayValue == null) return null; final Object value = displayValue.getValue(); if (value == null) return null; return value.toString(); } /** * Returns the image used for the current attribute if any. * * @return the display image or null * */ public Image getDisplayImage() { if (myInternalValues) return myInternalImage; return getAttribute().getImage(); } /** * Return the current attribute of the painter. * * @return the attribute */ public IUIAttribute getAttribute() { return myAttribute; } /** * Returns the current horizontal alignment. * * @return the alignment */ public int getHorizontalAlignment() { return myHorizontalAlignment; } /** * Sets the horizontal alignment of the painter - one or {@link SWT#LEAD}, {@link SWT#CENTER}, * or {@link SWT#TRAIL}. * * @param alignment the new alignment */ public void setHorizontalAlignment(int alignment) { myHorizontalAlignment = alignment; } /** * Sets the default background of the painter area. * * @param defaultBackground the background or <code>null</code> */ public void setDefaultBackground(Color defaultBackground) { myDefaultBackground = defaultBackground; } /** * Returns the current default background color. * * @return the color or <code>null</code> */ public Color getDefaultBackground() { return myDefaultBackground; } /** * Makes an image of a Check button in selected or un-selected mode. * * @param control the parent control * @param type selected or not * @return the image */ private Image makeShot(Control control, boolean type) { /* * First try to load the image directly from the plugin... */ final String osname = System.getProperty("os.name"); //$NON-NLS-1$ final String osversion = System.getProperty("os.version"); //$NON-NLS-1$ String imageName = "images/checkbox/" + osname + "-" + osversion; /* * Check for classic theme */ final RGB widgetBackgroundColor = control.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND).getRGB(); if (widgetBackgroundColor.red == 212 && widgetBackgroundColor.green == 208 && widgetBackgroundColor.blue == 200) { imageName += "-classic"; } imageName += "-" + type + ".png"; final ImageDescriptor id = AbstractUIPlugin.imageDescriptorFromPlugin(Activator.ID, imageName); if (id != null) { final Image i = id.createImage(); final Rectangle bounds = i.getBounds(); if (bounds.height + EXTRA_CELL_HEIGHT > myMinHeight) { myMinHeight = bounds.height + EXTRA_CELL_HEIGHT; } return i; } /* * Hopefully no platform uses exactly this color because we'll make it transparent in the * image. */ final Color greenScreen = new Color(control.getDisplay(), 222, 223, 224); final Shell shell = new Shell(control.getShell(), SWT.NO_TRIM); // otherwise we have a default gray color shell.setBackground(greenScreen); if (Util.isMac()) { final Button button2 = new Button(shell, SWT.CHECK); final Point bsize = button2.computeSize(SWT.DEFAULT, SWT.DEFAULT); // otherwise an image is stretched by width bsize.x = Math.max(bsize.x - 1, bsize.y - 1); bsize.y = Math.max(bsize.x - 1, bsize.y - 1); button2.setSize(bsize); button2.setLocation(100, 100); } final Button button = new Button(shell, SWT.CHECK); button.setBackground(greenScreen); button.setSelection(type); // otherwise an image is located in a corner button.setLocation(1, 1); final Point bsize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT); // otherwise an image is stretched by width bsize.x = Math.max(bsize.x - 1, bsize.y - 1); bsize.y = Math.max(bsize.x - 1, bsize.y - 1); button.setSize(bsize); shell.setSize(bsize); final Image image; GC gc = null; try { shell.open(); gc = new GC(shell); image = new Image(control.getDisplay(), bsize.x, bsize.y); gc.copyArea(image, 0, 0); } finally { if (gc != null) { gc.dispose(); } shell.close(); } final ImageData imageData = image.getImageData(); imageData.transparentPixel = imageData.palette.getPixel(greenScreen.getRGB()); final Image img = new Image(control.getDisplay(), imageData); image.dispose(); if (bsize.y + EXTRA_CELL_HEIGHT > myMinHeight) { myMinHeight = bsize.y + EXTRA_CELL_HEIGHT; } // final ImageLoader imageLoader = new ImageLoader(); // imageLoader.data = new ImageData[] { imageData }; // // imageLoader.save("/tmp/" + imageName, SWT.IMAGE_PNG); return img; } /** * Sets the painter to paint a image only based on the checked state. * <p> * Three states based on value: * <dl> * <dt><code>true</code></dt> * <dd>The painter will show a check in checked state</dd> * <dt><code>false</code></dt> * <dd>The painter will show a check in unchecked state</dd> * <dt><code>null</code></dt> * <dd>The painter will <em>not</em> show a check</dd> * </dl> * * @param checked the wanted state */ public void setCheckbox(Boolean checked) { if (checked == null) { setInternalValues(null, null); return; } if (checked) { setInternalValues(JFaceResources.getImageRegistry().get(CHECKED_KEY), null); } else { setInternalValues(JFaceResources.getImageRegistry().get(UNCHECKED_KEY), null); } } public void setInternalValues(Image image, String text) { myInternalImage = image; myInternalText = text; myInternalValues = (image != null) || (text != null); } /** * Sets whether the painter has focus or not. * * @param hasFocus if it has focus */ public void setFocus(boolean hasFocus) { myFocus = hasFocus; } /** * Whether the painter has focus or not. * * @return <code>true</code> if the painter has focus */ public boolean hasFocus() { return myFocus; } /** * Sets whether the painter is selected or not. * * @param isSelected the new selected state */ public void setSelected(boolean isSelected) { mySelected = isSelected; } /** * Whether the painter is selected. * * @return <code>true</code> if selected */ public boolean isSelected() { return mySelected; } /** * Returns the size of the area for this cell. * * @param gc the GC used for ther cell * @return the size of the needed area */ public Point getSize(GC gc) { preparePainter(gc); final Rectangle a = new Rectangle(0, 0, 0, 0); if (myPreparedImageBounds != null) { a.add(myPreparedImageBounds); } if (myPreparedTextBounds != null) { a.add(myPreparedTextBounds); } /* * Make sure the minimum eight is the same as the * * Was: For Windows XP, we must add a little to the size as cells otherwise gets too small. */ if (a.height < getMinHeight()) { a.height = getMinHeight(); } return new Point(a.width, a.height); } }