diff options
Diffstat (limited to 'propertysheet/src/org/eclipse/wb/internal/core/utils/ui')
9 files changed, 1575 insertions, 0 deletions
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/DrawUtils.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/DrawUtils.java new file mode 100644 index 0000000..f7cc09d --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/DrawUtils.java @@ -0,0 +1,337 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui; + +import com.google.common.io.Closeables; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +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.Rectangle; +import org.eclipse.swt.widgets.Display; +import org.eclipse.wb.draw2d.IColorConstants; + +import java.io.InputStream; +import java.net.URL; + +/** + * Utilities for drawing on {@link GC}. + * + * @author scheglov_ke + * @coverage core.ui + */ +public class DrawUtils { + private static final String DOTS = "..."; + + //////////////////////////////////////////////////////////////////////////// + // + // Drawing + // + //////////////////////////////////////////////////////////////////////////// + /** + * Draws given text clipped horizontally and centered vertically. + */ + public static final void drawStringCV(GC gc, String text, int x, int y, int width, int height) { + Rectangle oldClipping = gc.getClipping(); + try { + gc.setClipping(new Rectangle(x, y, width, height)); + // + int textStartY = y + (height - gc.getFontMetrics().getHeight()) / 2; + gc.drawString(clipString(gc, text, width), x, textStartY, true); + } finally { + gc.setClipping(oldClipping); + } + } + + /** + * Draws given text clipped or centered horizontally and centered vertically. + */ + public static final void drawStringCHCV(GC gc, String text, int x, int y, int width, int height) { + int textStartY = y + (height - gc.getFontMetrics().getHeight()) / 2; + Point textSize = gc.stringExtent(text); + // + if (textSize.x > width) { + gc.drawString(clipString(gc, text, width), x, textStartY); + } else { + gc.drawString(text, x + (width - textSize.x) / 2, textStartY); + } + } + + /** + * Draws image at given <code>x</code> and centered vertically. + */ + public static final void drawImageCV(GC gc, Image image, int x, int y, int height) { + if (image != null) { + Rectangle imageBounds = image.getBounds(); + gc.drawImage(image, x, y + (height - imageBounds.height) / 2); + } + } + + /** + * Draws image at given <code>x</code> and centered vertically. + */ + public static final void drawImageCHCV(GC gc, Image image, int x, int y, int width, int height) { + if (image != null) { + Rectangle imageBounds = image.getBounds(); + int centerX = (width - imageBounds.width) / 2; + int centerY = y + (height - imageBounds.height) / 2; + gc.drawImage(image, x + centerX, centerY); + } + } + + /** + * Draws {@link Image} on {@link GC} centered in given {@link Rectangle}. If {@link Image} is + * bigger that {@link Rectangle}, {@link Image} will be scaled down as needed with keeping + * proportions. + */ + public static void drawScaledImage(GC gc, Image image, Rectangle targetRectangle) { + int imageWidth = image.getBounds().width; + int imageHeight = image.getBounds().height; + // prepare scaled image size + int newImageWidth; + int newImageHeight; + if (imageWidth <= targetRectangle.width && imageHeight <= targetRectangle.height) { + newImageWidth = imageWidth; + newImageHeight = imageHeight; + } else { + // prepare minimal scale + double k; + { + double k_w = targetRectangle.width / (double) imageWidth; + double k_h = targetRectangle.height / (double) imageHeight; + k = Math.min(k_w, k_h); + } + // calculate scaled image size + newImageWidth = (int) (imageWidth * k); + newImageHeight = (int) (imageHeight * k); + } + // draw image centered in target rectangle + int destX = targetRectangle.x + (targetRectangle.width - newImageWidth) / 2; + int destY = targetRectangle.y + (targetRectangle.height - newImageHeight) / 2; + gc.drawImage(image, 0, 0, imageWidth, imageHeight, destX, destY, newImageWidth, newImageHeight); + } + + /** + * @return the string clipped to have width less than given. Clipping is done as trailing "...". + */ + public static String clipString(GC gc, String text, int width) { + if (width <= 0) { + return ""; + } + // check if text already fits in given width + if (gc.stringExtent(text).x <= width) { + return text; + } + // use average count of characters as base + int count = Math.min(width / gc.getFontMetrics().getAverageCharWidth(), text.length()); + if (gc.stringExtent(text.substring(0, count) + DOTS).x > width) { + while (count > 0 && gc.stringExtent(text.substring(0, count) + DOTS).x > width) { + count--; + } + } else { + while (count < text.length() - 1 + && gc.stringExtent(text.substring(0, count + 1) + DOTS).x < width) { + count++; + } + } + return text.substring(0, count) + DOTS; + } + + /** + * Draws {@link String} in rectangle, wraps at any character (not by words). + */ + public static void drawTextWrap(GC gc, String text, int x, int y, int width, int height) { + int y_ = y; + int x_ = x; + int lineHeight = 0; + for (int i = 0; i < text.length(); i++) { + String c = text.substring(i, i + 1); + Point extent = gc.stringExtent(c); + if (x_ + extent.x > x + width) { + y_ += lineHeight; + if (y_ > y + height) { + return; + } + x_ = x; + } + gc.drawText(c, x_, y_); + x_ += extent.x; + lineHeight = Math.max(lineHeight, extent.y); + } + } + + /** + * Draws 3D highlight rectangle. + */ + public static void drawHighlightRectangle(GC gc, int x, int y, int width, int height) { + int right = x + width - 1; + int bottom = y + height - 1; + // + Color oldForeground = gc.getForeground(); + try { + gc.setForeground(IColorConstants.buttonLightest); + gc.drawLine(x, y, right, y); + gc.drawLine(x, y, x, bottom); + // + gc.setForeground(IColorConstants.buttonDarker); + gc.drawLine(right, y, right, bottom); + gc.drawLine(x, bottom, right, bottom); + } finally { + gc.setForeground(oldForeground); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Images + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the {@link Image} loaded relative to given {@link Class}. + */ + public static Image loadImage(Class<?> clazz, String path) { + try { + URL resource = clazz.getResource(path); + if (resource != null) { + InputStream stream = resource.openStream(); + try { + return new Image(null, stream); + } finally { + Closeables.closeQuietly(stream); + } + } + } catch (Throwable e) { + } + return null; + } + + /** + * @return the thumbnail {@link Image} of required size for given "big" {@link Image}, centered or + * scaled down. + */ + public static Image getThubmnail(Image image, + int minWidth, + int minHeight, + int maxWidth, + int maxHeight) { + Rectangle imageBounds = image.getBounds(); + int imageWidth = imageBounds.width; + int imageHeight = imageBounds.height; + if (imageWidth < minWidth && imageHeight < minHeight) { + // create "thumbnail" Image with required size + Image thumbnail = new Image(null, minWidth, minHeight); + GC gc = new GC(thumbnail); + try { + drawImageCHCV(gc, image, 0, 0, minWidth, minHeight); + } finally { + gc.dispose(); + } + // recreate "thumbnail" Image with transparent pixel + try { + ImageData thumbnailData = thumbnail.getImageData(); + thumbnailData.transparentPixel = thumbnailData.getPixel(0, 0); + return new Image(null, thumbnailData); + } finally { + thumbnail.dispose(); + } + } else if (imageWidth <= maxWidth && imageHeight <= maxHeight) { + return new Image(null, image, SWT.IMAGE_COPY); + } else { + double kX = (double) maxWidth / imageWidth; + double kY = (double) maxHeight / imageHeight; + double k = Math.max(kX, kY); + int dWidth = (int) (imageWidth * k); + int dHeight = (int) (imageHeight * k); + ImageData scaledImageData = image.getImageData().scaledTo(dWidth, dHeight); + return new Image(null, scaledImageData); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Colors + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return new {@link Color} based on given {@link Color} and shifted on given value to make it + * darker or lighter. + */ + public static Color getShiftedColor(Color color, int delta) { + int r = Math.max(0, Math.min(color.getRed() + delta, 255)); + int g = Math.max(0, Math.min(color.getGreen() + delta, 255)); + int b = Math.max(0, Math.min(color.getBlue() + delta, 255)); + return new Color(color.getDevice(), r, g, b); + } + + /** + * @return <code>true</code> if the given <code>color</code> is dark. + */ + public static boolean isDarkColor(Color c) { + int value = + (int) Math.sqrt(c.getRed() + * c.getRed() + * .241 + + c.getGreen() + * c.getGreen() + * .691 + + c.getBlue() + * c.getBlue() + * .068); + return value < 130; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Fonts + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the bold version of given {@link Font}. + */ + public static Font getBoldFont(Font baseFont) { + FontData[] boldData = getModifiedFontData(baseFont, SWT.BOLD); + return new Font(Display.getCurrent(), boldData); + } + + /** + * @return the italic version of given {@link Font}. + */ + public static Font getBoldItalicFont(Font baseFont) { + FontData[] boldData = getModifiedFontData(baseFont, SWT.BOLD | SWT.ITALIC); + return new Font(Display.getCurrent(), boldData); + } + + /** + * @return the italic version of given {@link Font}. + */ + public static Font getItalicFont(Font baseFont) { + FontData[] boldData = getModifiedFontData(baseFont, SWT.ITALIC); + return new Font(Display.getCurrent(), boldData); + } + + /** + * @return the array of {@link FontData} with the specified style. + */ + private static FontData[] getModifiedFontData(Font baseFont, int style) { + FontData[] baseData = baseFont.getFontData(); + FontData[] styleData = new FontData[baseData.length]; + for (int i = 0; i < styleData.length; i++) { + FontData base = baseData[i]; + styleData[i] = new FontData(base.getName(), base.getHeight(), base.getStyle() | style); + } + return styleData; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridDataFactory.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridDataFactory.java new file mode 100644 index 0000000..4ccda40 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridDataFactory.java @@ -0,0 +1,541 @@ +/******************************************************************************* + * Copyright (c) 2005, 2011 IBM Corporation 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui; + +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Control; + +/** + * This class provides a convienient shorthand for creating and initializing GridData. This offers + * several benefits over creating GridData normal way: + * + * <ul> + * <li>The same factory can be used many times to create several GridData instances</li> + * <li>The setters on GridDataFactory all return "this", allowing them to be chained</li> + * <li>GridDataFactory uses vector setters (it accepts Points), making it easy to set X and Y values + * together</li> + * </ul> + * + * <p> + * GridDataFactory instances are created using one of the static methods on this class. + * </p> + * + * <p> + * Example usage: + * </p> + * <code> + * + * //////////////////////////////////////////////////////////// + * // Example 1: Typical grid data for a non-wrapping label + * + * // GridDataFactory version + * GridDataFactory.fillDefaults().applyTo(myLabel); + * + * // Equivalent SWT version + * GridData labelData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL); + * myLabel.setLayoutData(labelData); + * + * /////////////////////////////////////////////////////////// + * // Example 2: Typical grid data for a wrapping label + * + * // GridDataFactory version + * GridDataFactory.fillDefaults() + * .align(SWT.FILL, SWT.CENTER) + * .hint(150, SWT.DEFAULT) + * .grab(true, false) + * .applyTo(wrappingLabel); + * + * // Equivalent SWT version + * GridData wrappingLabelData = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_CENTER); + * wrappingLabelData.minimumWidth = 1; + * wrappingLabelData.widthHint = 150; + * wrappingLabel.setLayoutData(wrappingLabelData); + * + * ////////////////////////////////////////////////////////////// + * // Example 3: Typical grid data for a scrollable control (a list box, tree, table, etc.) + * + * // GridDataFactory version + * GridDataFactory.fillDefaults().grab(true, true).hint(150, 150).applyTo(listBox); + * + * // Equivalent SWT version + * GridData listBoxData = new GridData(GridData.FILL_BOTH); + * listBoxData.widthHint = 150; + * listBoxData.heightHint = 150; + * listBoxData.minimumWidth = 1; + * listBoxData.minimumHeight = 1; + * listBox.setLayoutData(listBoxData); + * + * ///////////////////////////////////////////////////////////// + * // Example 4: Typical grid data for a button + * + * // GridDataFactory version + * Point preferredSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, false); + * Point hint = Geometry.max(LayoutConstants.getMinButtonSize(), preferredSize); + * GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).hint(hint).applyTo(button); + * + * // Equivalent SWT version + * Point preferredSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, false); + * Point hint = Geometry.max(LayoutConstants.getMinButtonSize(), preferredSize); + * GridData buttonData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_CENTER); + * buttonData.widthHint = hint.x; + * buttonData.heightHint = hint.y; + * button.setLayoutData(buttonData); + * </code> + * + * <p> + * IMPORTANT: WHEN ASSIGNING LAYOUT DATA TO A CONTROL, BE SURE TO USE + * gridDataFactory.applyTo(control) AND NEVER control.setLayoutData(gridDataFactory). + * </p> + * + * @since 3.2 + */ +public final class GridDataFactory { + private final Control m_control; + private final PixelConverter m_pixelConverter; + private final GridData m_data; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + private GridDataFactory(Control control, GridData gridData) { + m_control = control; + m_pixelConverter = new PixelConverter(m_control); + // + m_data = gridData; + if (m_control.getLayoutData() != m_data) { + m_control.setLayoutData(m_data); + } + } + + /** + * Creates new {@link GridDataFactory} with new {@link GridData}. + */ + public static GridDataFactory create(Control control) { + return new GridDataFactory(control, new GridData()); + } + + /** + * Creates new {@link GridDataFactory} for modifying {@link GridData} already installed in + * control. + */ + public static GridDataFactory modify(Control control) { + GridData gridData; + { + Object existingLayoutData = control.getLayoutData(); + if (existingLayoutData instanceof GridData) { + gridData = (GridData) existingLayoutData; + } else { + gridData = new GridData(); + } + } + return new GridDataFactory(control, gridData); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Span + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets the GridData span. The span controls how many cells are filled by the control. + * + * @param hSpan + * number of columns spanned by the control + * @param vSpan + * number of rows spanned by the control + * @return this + */ + public GridDataFactory span(int hSpan, int vSpan) { + m_data.horizontalSpan = hSpan; + m_data.verticalSpan = vSpan; + return this; + } + + /** + * Sets the GridData span. The span controls how many cells are filled by the control. + * + * @param hSpan + * number of columns spanned by the control + * @return this + */ + public GridDataFactory spanH(int hSpan) { + m_data.horizontalSpan = hSpan; + return this; + } + + /** + * Sets the GridData span. The span controls how many cells are filled by the control. + * + * @param vSpan + * number of rows spanned by the control + * @return this + */ + public GridDataFactory spanV(int vSpan) { + m_data.verticalSpan = vSpan; + return this; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Hint + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets the width and height hints. The width and height hints override the control's preferred + * size. If either hint is set to SWT.DEFAULT, the control's preferred size is used. + * + * @param xHint + * horizontal hint (pixels), or SWT.DEFAULT to use the control's preferred size + * @param yHint + * vertical hint (pixels), or SWT.DEFAULT to use the control's preferred size + * @return this + */ + public GridDataFactory hint(int xHint, int yHint) { + m_data.widthHint = xHint; + m_data.heightHint = yHint; + return this; + } + + /** + * Sets hint in chars. + */ + public GridDataFactory hintC(int xHintInChars, int yHintInChars) { + hintHC(xHintInChars); + hintVC(yHintInChars); + return this; + } + + /** + * Sets the width hint. + * + * @return this + */ + public GridDataFactory hintH(int xHint) { + m_data.widthHint = xHint; + return this; + } + + /** + * Sets the width hint to the minimum of current hint and given <code>otherHint</code>. + * + * @return this + */ + public GridDataFactory hintHMin(int otherHint) { + m_data.widthHint = Math.min(m_data.widthHint, otherHint); + return this; + } + + /** + * Sets the width hint in chars. + * + * @return this + */ + public GridDataFactory hintHC(int hintInChars) { + return hintH(m_pixelConverter.convertWidthInCharsToPixels(hintInChars)); + } + + /** + * Sets the width hint. + * + * @return this + */ + public GridDataFactory hintHU(int hintInDLU) { + return hintH(m_pixelConverter.convertHorizontalDLUsToPixels(hintInDLU)); + } + + /** + * Sets the height hint. + * + * @return this + */ + public GridDataFactory hintV(int yHint) { + m_data.heightHint = yHint; + return this; + } + + /** + * Sets the height hint in chars. + * + * @return this + */ + public GridDataFactory hintVC(int hintInChars) { + return hintV(m_pixelConverter.convertHeightInCharsToPixels(hintInChars)); + } + + /** + * Increments horizontal hint on given value. + * + * @return this + */ + public GridDataFactory hintHAdd(int increment) { + return hintV(m_data.widthHint + increment); + } + + /** + * Increments vertical hint on given value. + * + * @return this + */ + public GridDataFactory hintVAdd(int increment) { + return hintV(m_data.heightHint + increment); + } + + /** + * Sets the width and height hints. The width and height hints override the control's preferred + * size. If either hint is set to SWT.DEFAULT, the control's preferred size is used. + * + * @param hint + * size (pixels) to be used instead of the control's preferred size. If the x or y values + * are set to SWT.DEFAULT, the control's computeSize() method will be used to obtain that + * dimension of the preferred size. + * @return this + */ + public GridDataFactory hint(Point hint) { + m_data.widthHint = hint.x; + m_data.heightHint = hint.y; + return this; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Minimum size + // + //////////////////////////////////////////////////////////////////////////// + public GridDataFactory minH(int minimumWidth) { + m_data.minimumWidth = minimumWidth; + return this; + } + + public GridDataFactory minHC(int widthInChars) { + return minH(m_pixelConverter.convertWidthInCharsToPixels(widthInChars)); + } + + public GridDataFactory minV(int minimumHeight) { + m_data.minimumHeight = minimumHeight; + return this; + } + + public GridDataFactory minVC(int heightInChars) { + return minV(m_pixelConverter.convertHeightInCharsToPixels(heightInChars)); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Alignment + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets the alignment of the control within its cell. + * + * @param hAlign + * horizontal alignment. One of SWT.BEGINNING, SWT.CENTER, SWT.END, or SWT.FILL. + * @param vAlign + * vertical alignment. One of SWT.BEGINNING, SWT.CENTER, SWT.END, or SWT.FILL. + * @return this + */ + public GridDataFactory align(int hAlign, int vAlign) { + m_data.horizontalAlignment = hAlign; + m_data.verticalAlignment = vAlign; + return this; + } + + /** + * Sets the horizontal and vertical alignment to GridData.FILL. + */ + public GridDataFactory fill() { + return align(GridData.FILL, GridData.FILL); + } + + /** + * Sets the horizontal alignment of the control within its cell. + * + * @param hAlign + * horizontal alignment. One of SWT.BEGINNING, SWT.CENTER, SWT.END, or SWT.FILL. + * @return this + */ + public GridDataFactory alignH(int hAlign) { + m_data.horizontalAlignment = hAlign; + return this; + } + + /** + * Sets the horizontal alignment of the control to GridData.BEGINNING + * + * @return this + */ + public GridDataFactory alignHL() { + return alignH(GridData.BEGINNING); + } + + /** + * Sets the horizontal alignment of the control to GridData.CENTER + * + * @return this + */ + public GridDataFactory alignHC() { + return alignH(GridData.CENTER); + } + + /** + * Sets the horizontal alignment of the control to GridData.FILL + * + * @return this + */ + public GridDataFactory alignHF() { + return alignH(GridData.FILL); + } + + /** + * Sets the horizontal alignment of the control to GridData.FILL + * + * @return this + */ + public GridDataFactory fillH() { + return alignHF(); + } + + /** + * Sets the horizontal alignment of the control to GridData.END + * + * @return this + */ + public GridDataFactory alignHR() { + return alignH(GridData.END); + } + + /** + * Sets the vertical alignment of the control within its cell. + * + * @param vAlign + * vertical alignment. One of SWT.BEGINNING, SWT.CENTER, SWT.END, or SWT.FILL. + * @return this + */ + public GridDataFactory alignV(int vAlign) { + m_data.verticalAlignment = vAlign; + return this; + } + + /** + * Sets the vertical alignment of the control to GridData.BEGINNING + * + * @return this + */ + public GridDataFactory alignVT() { + return alignV(GridData.BEGINNING); + } + + /** + * Sets the vertical alignment of the control to GridData.CENTER + * + * @return this + */ + public GridDataFactory alignVM() { + return alignV(GridData.CENTER); + } + + /** + * Sets the vertical alignment of the control to GridData.FILL + * + * @return this + */ + public GridDataFactory alignVF() { + return alignV(GridData.FILL); + } + + /** + * Sets the vertical alignment of the control to GridData.FILL + * + * @return this + */ + public GridDataFactory fillV() { + return alignVF(); + } + + /** + * Sets the vertical alignment of the control to GridData.END + * + * @return this + */ + public GridDataFactory alignVB() { + return alignV(GridData.END); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Indent + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets the indent of the control within the cell in pixels. + */ + public GridDataFactory indentH(int hIndent) { + m_data.horizontalIndent = hIndent; + return this; + } + + /** + * Sets the indent of the control within the cell in characters. + */ + public GridDataFactory indentHC(int hIndent) { + m_data.horizontalIndent = m_pixelConverter.convertWidthInCharsToPixels(hIndent); + return this; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Grab + // + //////////////////////////////////////////////////////////////////////////// + /** + * Determines whether extra horizontal or vertical space should be allocated to this control's + * column when the layout resizes. If any control in the column is set to grab horizontal then the + * whole column will grab horizontal space. If any control in the row is set to grab vertical then + * the whole row will grab vertical space. + * + * @param horizontal + * true if the control's column should grow horizontally + * @param vertical + * true if the control's row should grow vertically + * @return this + */ + public GridDataFactory grab(boolean horizontal, boolean vertical) { + m_data.grabExcessHorizontalSpace = horizontal; + m_data.grabExcessVerticalSpace = vertical; + return this; + } + + public GridDataFactory grabH() { + m_data.grabExcessHorizontalSpace = true; + return this; + } + + public GridDataFactory grabV() { + m_data.grabExcessVerticalSpace = true; + return this; + } + + public GridDataFactory grab() { + return grab(true, true); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Exclude + // + //////////////////////////////////////////////////////////////////////////// + public GridDataFactory exclude(boolean value) { + m_data.exclude = value; + return this; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridLayoutFactory.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridLayoutFactory.java new file mode 100644 index 0000000..f6dc58e --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridLayoutFactory.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui; + +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Layout; + +/** + * GridLayoutFactory provides a convenient shorthand for creating and initializing GridLayout. + * + * @author scheglov_ke + */ +public final class GridLayoutFactory { + private final GridLayout m_layout; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + private GridLayoutFactory(Composite composite, GridLayout layout) { + m_layout = layout; + composite.setLayout(m_layout); + } + + public static GridLayoutFactory create(Composite composite) { + return new GridLayoutFactory(composite, new GridLayout()); + } + + public static GridLayoutFactory modify(Composite composite) { + Layout layout = composite.getLayout(); + if (layout instanceof GridLayout) { + return new GridLayoutFactory(composite, (GridLayout) layout); + } + return create(composite); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets number of columns in {@link GridLayout}. + */ + public GridLayoutFactory columns(int numColumns) { + m_layout.numColumns = numColumns; + return this; + } + + /** + * Specifies whether all columns in the layout will be forced to have the same width. + */ + public GridLayoutFactory equalColumns() { + m_layout.makeColumnsEqualWidth = true; + return this; + } + + /** + * Sets the horizontal margins. + */ + public GridLayoutFactory marginsH(int margins) { + m_layout.marginWidth = margins; + return this; + } + + /** + * Sets the vertical margins. + */ + public GridLayoutFactory marginsV(int margins) { + m_layout.marginHeight = margins; + return this; + } + + /** + * Sets the horizontal/vertical margins. + */ + public GridLayoutFactory margins(int margins) { + m_layout.marginWidth = m_layout.marginHeight = margins; + return this; + } + + /** + * Sets zero horizontal and vertical margins. + */ + public GridLayoutFactory noMargins() { + m_layout.marginWidth = m_layout.marginHeight = 0; + return this; + } + + /** + * Sets zero horizontal and vertical spacing. + */ + public GridLayoutFactory noSpacing() { + m_layout.horizontalSpacing = m_layout.verticalSpacing = 0; + return this; + } + + /** + * Sets horizontal spacing. + */ + public GridLayoutFactory spacingH(int spacing) { + m_layout.horizontalSpacing = spacing; + return this; + } + + /** + * Sets vertical spacing. + */ + public GridLayoutFactory spacingV(int spacing) { + m_layout.verticalSpacing = spacing; + return this; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/ImageImageDescriptor.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/ImageImageDescriptor.java new file mode 100644 index 0000000..d5fc39f --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/ImageImageDescriptor.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; + +/** + * Implementation of {@link ImageDescriptor} for {@link Image} instance. + * + * @author scheglov_ke + * @coverage core.ui + */ +public final class ImageImageDescriptor extends ImageDescriptor { + private final Image m_Image; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public ImageImageDescriptor(Image image) { + m_Image = image; + } + + //////////////////////////////////////////////////////////////////////////// + // + // ImageDescriptor + // + //////////////////////////////////////////////////////////////////////////// + @Override + public ImageData getImageData() { + return m_Image == null ? null : m_Image.getImageData(); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/PixelConverter.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/PixelConverter.java new file mode 100644 index 0000000..1e3699f --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/PixelConverter.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.widgets.Control; + +/** + * Helper class for converting DLU and char size into pixels. + * + * Based on code from JDT UI. + * + * @author scheglov_ke + */ +public class PixelConverter { + private final FontMetrics fFontMetrics; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructors + // + //////////////////////////////////////////////////////////////////////////// + public PixelConverter(Control control) { + GC gc = new GC(control); + gc.setFont(control.getFont()); + fFontMetrics = gc.getFontMetrics(); + gc.dispose(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Conversions + // + //////////////////////////////////////////////////////////////////////////// + /** + * see org.eclipse.jface.dialogs.DialogPage#convertHeightInCharsToPixels(int) + */ + public int convertHeightInCharsToPixels(int chars) { + return Dialog.convertHeightInCharsToPixels(fFontMetrics, chars); + } + + /** + * see org.eclipse.jface.dialogs.DialogPage#convertHorizontalDLUsToPixels(int) + */ + public int convertHorizontalDLUsToPixels(int dlus) { + return Dialog.convertHorizontalDLUsToPixels(fFontMetrics, dlus); + } + + /** + * see org.eclipse.jface.dialogs.DialogPage#convertVerticalDLUsToPixels(int) + */ + public int convertVerticalDLUsToPixels(int dlus) { + return Dialog.convertVerticalDLUsToPixels(fFontMetrics, dlus); + } + + /** + * see org.eclipse.jface.dialogs.DialogPage#convertWidthInCharsToPixels(int) + */ + public int convertWidthInCharsToPixels(int chars) { + return Dialog.convertWidthInCharsToPixels(fFontMetrics, chars); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/UiUtils.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/UiUtils.java new file mode 100644 index 0000000..126efba --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/UiUtils.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui; + +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.widgets.Shell; + +/** + * Utilities for UI. + * + * @author scheglov_ke + */ +public class UiUtils { + //////////////////////////////////////////////////////////////////////////// + // + // Message dialogs + // + //////////////////////////////////////////////////////////////////////////// + /** + * Opens standard warning dialog. + */ + public static void openWarning(Shell parent, String title, String message) { + MessageDialog dialog = + new MessageDialog(parent, + title, + null, + message, + MessageDialog.WARNING, + new String[]{IDialogConstants.OK_LABEL}, + 0); + dialog.open(); + } +}
\ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/ResizableDialog.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/ResizableDialog.java new file mode 100644 index 0000000..307c2f6 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/ResizableDialog.java @@ -0,0 +1,221 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui.dialogs; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.plugin.AbstractUIPlugin; + +/** + * {@link Dialog} that remembers location/size between usage sessions. + * + * @author scheglov_ke + * @coverage core.ui + */ +public abstract class ResizableDialog extends Dialog { + /** + * Key for accessing {@link Dialog} from its {@link Shell}. + */ + public static final String KEY_DIALOG = "KEY_DIALOG"; + //////////////////////////////////////////////////////////////////////////// + // + // Internal constants + // + //////////////////////////////////////////////////////////////////////////// + private static final String X = "x"; + private static final String Y = "y"; + private static final String WIDTH = "width"; + private static final String HEIGHT = "height"; + //////////////////////////////////////////////////////////////////////////// + // + // Instance fields + // + //////////////////////////////////////////////////////////////////////////// + private final AbstractUIPlugin m_plugin; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public ResizableDialog(Shell parentShell, AbstractUIPlugin plugin) { + super(parentShell); + m_plugin = plugin; + setShellStyle(getShellStyle() | SWT.RESIZE | SWT.MAX); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Size + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected Point getInitialSize() { + // track the current dialog bounds + installDialogBoundsTracker(); + // answer the size from the previous incarnation + Point defaultSize = getDefaultSize(); + if ((getShellStyle() & SWT.RESIZE) != 0) { + Rectangle oldBounds = loadBounds(); + if (oldBounds != null) { + Rectangle displayBounds = getShell().getDisplay().getBounds(); + int width = Math.min(displayBounds.width, Math.max(oldBounds.width, defaultSize.x)); + int height = Math.min(displayBounds.height, Math.max(oldBounds.height, defaultSize.y)); + return new Point(width, height); + } + } + // use default size + return defaultSize; + } + + /** + * @return the default size of dialog. + */ + protected Point getDefaultSize() { + return super.getInitialSize(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Location + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected Point getInitialLocation(Point initialSize) { + Rectangle windowBounds; + { + Shell windowShell = m_plugin.getWorkbench().getActiveWorkbenchWindow().getShell(); + windowBounds = windowShell.getBounds(); + } + // answer the location from the previous incarnation + Rectangle bounds = loadBounds(); + if (bounds != null) { + int x = bounds.x; + int y = bounds.y; + int maxX = windowBounds.x + windowBounds.width - initialSize.x; + int maxY = windowBounds.y + windowBounds.height - initialSize.y; + if (x > maxX) { + x = maxX; + } + if (y > maxY) { + y = maxY; + } + if (x < windowBounds.x) { + x = windowBounds.x; + } + if (y < windowBounds.y) { + y = windowBounds.y; + } + return new Point(x, y); + } + // default location - centered on workbench window + int x = windowBounds.x + (windowBounds.width - initialSize.x) / 2; + int y = windowBounds.y + (windowBounds.height - initialSize.y) / 2; + return new Point(x, y); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Bounds + // + //////////////////////////////////////////////////////////////////////////// + /** + * Loads bounds from {@link IDialogSettings}. + */ + private Rectangle loadBounds() { + IDialogSettings settings = getDialogSettings(); + try { + return new Rectangle(settings.getInt(X), + settings.getInt(Y), + settings.getInt(WIDTH), + settings.getInt(HEIGHT)); + } catch (NumberFormatException e) { + return null; + } + } + + /** + * Saves bounds to {@link IDialogSettings}. + */ + private void saveBounds(Rectangle bounds) { + IDialogSettings settings = getDialogSettings(); + settings.put(X, bounds.x); + settings.put(Y, bounds.y); + settings.put(WIDTH, bounds.width); + settings.put(HEIGHT, bounds.height); + } + + /** + * @return the {@link IDialogSettings} for this dialog with this type. + */ + protected IDialogSettings getDialogSettings() { + IDialogSettings settings = m_plugin.getDialogSettings(); + String sectionName = getDialogSettingsSectionName(); + if (settings.getSection(sectionName) == null) { + return settings.addNewSection(sectionName); + } + return settings.getSection(sectionName); + } + + /** + * @return the name of section for dialog specific bounds. By default uses name of {@link Class}, + * but if same dialog is used for displaying different content, then may be overridden. + */ + protected String getDialogSettingsSectionName() { + return getClass().getName(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Size tracking + // + //////////////////////////////////////////////////////////////////////////// + protected Rectangle cachedBounds; + + private void installDialogBoundsTracker() { + getShell().addControlListener(new ControlListener() { + public void controlMoved(ControlEvent e) { + cachedBounds = getShell().getBounds(); + } + + public void controlResized(ControlEvent e) { + cachedBounds = getShell().getBounds(); + } + }); + } + + @Override + public boolean close() { + boolean shellMaximized = getShell().getMaximized(); + boolean closed = super.close(); + if (closed && !shellMaximized && cachedBounds != null) { + saveBounds(cachedBounds); + } + return closed; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Shell + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + newShell.setData(KEY_DIALOG, this); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/StringsDialog.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/StringsDialog.java new file mode 100644 index 0000000..745bcc3 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/StringsDialog.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui.dialogs; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; + +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.eclipse.wb.internal.core.utils.execution.ExecutionUtils; +import org.eclipse.wb.internal.core.utils.execution.RunnableObjectEx; + +import java.io.BufferedReader; +import java.io.StringReader; +import java.util.List; + +/** + * The dialog for editing array of {@link String}'s. + * + * @author scheglov_ke + * @coverage core.ui + */ +public class StringsDialog extends TextDialog { + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public StringsDialog(Shell parentShell, + AbstractUIPlugin plugin, + String titleText, + String headerText, + String footerText) { + super(parentShell, plugin, titleText, headerText, footerText); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Items + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets the items to edit. + */ + public void setItems(String[] items) { + setText(Joiner.on('\n').join(items)); + } + + /** + * @return the edited items. + */ + public String[] getItems() { + return ExecutionUtils.runObjectLog(new RunnableObjectEx<String[]>() { + @Override + public String[] runObject() throws Exception { + List<String> strings = Lists.newArrayList(); + BufferedReader br = new BufferedReader(new StringReader(getText())); + while (true) { + String s = br.readLine(); + if (s == null) { + break; + } + strings.add(s); + } + return strings.toArray(new String[strings.size()]); + } + }, new String[0]); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/TextDialog.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/TextDialog.java new file mode 100644 index 0000000..320fa32 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/TextDialog.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui.dialogs; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.eclipse.wb.internal.core.utils.ui.GridDataFactory; +import org.eclipse.wb.internal.core.utils.ui.GridLayoutFactory; + +/** + * The dialog for editing multiline text. + * + * @author scheglov_ke + * @coverage core.ui + */ +public class TextDialog extends ResizableDialog { + private final String m_titleText; + private final String m_headerText; + private final String m_footerText; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public TextDialog(Shell parentShell, + AbstractUIPlugin plugin, + String titleText, + String headerText, + String footerText) { + super(parentShell, plugin); + m_titleText = titleText; + m_headerText = headerText; + m_footerText = footerText; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Text + // + //////////////////////////////////////////////////////////////////////////// + private String m_text; + + /** + * Sets the text to edit. + */ + public final void setText(String text) { + m_text = text; + } + + /** + * @return the edited text. + */ + public final String getText() { + return m_text; + } + + //////////////////////////////////////////////////////////////////////////// + // + // GUI + // + //////////////////////////////////////////////////////////////////////////// + protected Text m_textWidget; + + @Override + protected Control createDialogArea(Composite parent) { + Composite area = (Composite) super.createDialogArea(parent); + GridLayoutFactory.create(area); + // header + new Label(area, SWT.NONE).setText(m_headerText); + // Text widget + { + m_textWidget = new Text(area, SWT.BORDER | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); + GridDataFactory.create(m_textWidget).grab().fill().hintVC(10); + m_textWidget.setText(m_text); + // handle Ctrl+Enter as OK + m_textWidget.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.stateMask == SWT.CTRL && e.keyCode == SWT.CR) { + okPressed(); + } + } + }); + } + // footer + new Label(area, SWT.NONE).setText(m_footerText); + // + return area; + } + + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + newShell.setText(m_titleText); + } + + @Override + protected void okPressed() { + m_text = m_textWidget.getText(); + super.okPressed(); + } +} |