SWT Paint Example : 2D « SWT JFace Eclipse « Java






SWT Paint Example

 
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.accessibility.*;

import java.io.*;
import java.text.*;
import java.util.*;

public class PaintExample {
  private Composite mainComposite;
  private Canvas activeForegroundColorCanvas;
  private Canvas activeBackgroundColorCanvas;
  private Color paintColorBlack, paintColorWhite; // alias for paintColors[0] and [1]
  private Color[] paintColors;
  private Font paintDefaultFont; // do not free
  private static final int numPaletteRows = 3;
  private static final int numPaletteCols = 50;
  private ToolSettings toolSettings; // current active settings
  private PaintSurface paintSurface; // paint surface for drawing

  static final int Pencil_tool = 0;
  static final int Airbrush_tool = 1;
  static final int Line_tool = 2;
  static final int PolyLine_tool = 3;
  static final int Rectangle_tool = 4;
  static final int RoundedRectangle_tool = 5;
  static final int Ellipse_tool = 6;
  static final int Text_tool = 7;
  static final int None_fill = 8;
  static final int Outline_fill = 9;
  static final int Solid_fill = 10;
  static final int Solid_linestyle = 11;
  static final int Dash_linestyle = 12;
  static final int Dot_linestyle = 13;
  static final int DashDot_linestyle = 14;
  static final int Font_options = 15;
  
  static final int Default_tool = Pencil_tool;
  static final int Default_fill = None_fill;
  static final int Default_linestyle = Solid_linestyle;
  
  public static final Tool[] tools = {
    new Tool(Pencil_tool, "Pencil", "tool", SWT.RADIO),
    new Tool(Airbrush_tool, "Airbrush", "tool", SWT.RADIO),
    new Tool(Line_tool, "Line", "tool", SWT.RADIO),
    new Tool(PolyLine_tool, "PolyLine", "tool", SWT.RADIO),
    new Tool(Rectangle_tool, "Rectangle", "tool", SWT.RADIO),
    new Tool(RoundedRectangle_tool, "RoundedRectangle", "tool", SWT.RADIO),
    new Tool(Ellipse_tool, "Ellipse", "tool", SWT.RADIO),
    new Tool(Text_tool, "Text", "tool", SWT.RADIO),
    new Tool(None_fill, "None", "fill", SWT.RADIO, new Integer(ToolSettings.ftNone)),
    new Tool(Outline_fill, "Outline", "fill", SWT.RADIO, new Integer(ToolSettings.ftOutline)),
    new Tool(Solid_fill, "Solid", "fill", SWT.RADIO, new Integer(ToolSettings.ftSolid)),
    new Tool(Solid_linestyle, "Solid", "linestyle", SWT.RADIO, new Integer(SWT.LINE_SOLID)),
    new Tool(Dash_linestyle, "Dash", "linestyle", SWT.RADIO, new Integer(SWT.LINE_DASH)),
    new Tool(Dot_linestyle, "Dot", "linestyle", SWT.RADIO, new Integer(SWT.LINE_DOT)),
    new Tool(DashDot_linestyle, "DashDot", "linestyle", SWT.RADIO, new Integer(SWT.LINE_DASHDOT)),
    new Tool(Font_options, "Font", "options", SWT.PUSH)
  };

  /**
   * Creates an instance of a PaintExample embedded inside
   * the supplied parent Composite.
   * 
   * @param parent the container of the example
   */
  public PaintExample(Composite parent) {
    mainComposite = parent;
    initResources();
    initActions();
    init();
  }

  /**
   * Invokes as a standalone program.
   */
  public static void main(String[] args) {
    Display display = new Display();
    Shell shell = new Shell(display);
    shell.setText(getResourceString("window.title"));
    shell.setLayout(new GridLayout());
    PaintExample instance = new PaintExample(shell);
    instance.createToolBar(shell);
    Composite composite = new Composite(shell, SWT.NONE);
    composite.setLayout(new FillLayout());
    composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    instance.createGUI(composite);
    instance.setDefaults();
    setShellSize(display, shell);
    shell.open();
    while (! shell.isDisposed()) {
      if (! display.readAndDispatch()) display.sleep();
    }
    instance.dispose();
  }
  
  /**
   * Creates the toolbar.
   * Note: Only called by standalone.
   */
  private void createToolBar(Composite parent) {
    ToolBar toolbar = new ToolBar (parent, SWT.NONE);
    String group = null;
    for (int i = 0; i < tools.length; i++) {
      Tool tool = tools[i];
      if (group != null && !tool.group.equals(group)) {
        new ToolItem (toolbar, SWT.SEPARATOR);
      }
      group = tool.group;
      ToolItem item = addToolItem(toolbar, tool);
      if (i == Default_tool || i == Default_fill || i == Default_linestyle) item.setSelection(true);
    }
  }

  /**
   * Adds a tool item to the toolbar.
   * Note: Only called by standalone.
   */
  private ToolItem addToolItem(final ToolBar toolbar, final Tool tool) {
    final String id = tool.group + '.' + tool.name;
    ToolItem item = new ToolItem (toolbar, tool.type);
    item.setText (getResourceString(id + ".label"));
    item.setToolTipText(getResourceString(id + ".tooltip"));
    item.setImage(tool.image);
    item.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        tool.action.run();
      }
    });
    final int childID = toolbar.indexOf(item);
    toolbar.getAccessible().addAccessibleListener(new AccessibleAdapter() {
      public void getName(org.eclipse.swt.accessibility.AccessibleEvent e) {
        if (e.childID == childID) {
          e.result = getResourceString(id + ".description");
        }
      }
    });
    return item;
  }

  /**
   * Sets the default tool item states.
   */
  public void setDefaults() {
    setPaintTool(Default_tool);
    setFillType(Default_fill);
    setLineStyle(Default_linestyle);
    setForegroundColor(paintColorBlack);
    setBackgroundColor(paintColorWhite);
  }

  /**
   * Creates the GUI.
   */
  public void createGUI(Composite parent) {
    GridLayout gridLayout;
    GridData gridData;

    /*** Create principal GUI layout elements ***/    
    Composite displayArea = new Composite(parent, SWT.NONE);
    gridLayout = new GridLayout();
    gridLayout.numColumns = 1;
    displayArea.setLayout(gridLayout);

    // Creating these elements here avoids the need to instantiate the GUI elements
    // in strict layout order.  The natural layout ordering is an artifact of using
    // SWT layouts, but unfortunately it is not the same order as that required to
    // instantiate all of the non-GUI application elements to satisfy referential
    // dependencies.  It is possible to reorder the initialization to some extent, but
    // this can be very tedious.
    
    // paint canvas
    final Canvas paintCanvas = new Canvas(displayArea, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL |
      SWT.NO_REDRAW_RESIZE | SWT.NO_BACKGROUND);
    gridData = new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL);
    paintCanvas.setLayoutData(gridData);
    paintCanvas.setBackground(paintColorWhite);
    
    // color selector frame
    final Composite colorFrame = new Composite(displayArea, SWT.NONE);
    gridData = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL);
    colorFrame.setLayoutData(gridData);

    // tool settings frame
    final Composite toolSettingsFrame = new Composite(displayArea, SWT.NONE);
    gridData = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL);
    toolSettingsFrame.setLayoutData(gridData);

    // status text
    final Text statusText = new Text(displayArea, SWT.BORDER | SWT.SINGLE | SWT.READ_ONLY);
    gridData = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL);
    statusText.setLayoutData(gridData);

    /*** Create the remaining application elements inside the principal GUI layout elements ***/  
    // paintSurface
    paintSurface = new PaintSurface(paintCanvas, statusText, paintColorWhite);

    // finish initializing the tool data
    tools[Pencil_tool].data = new PencilTool(toolSettings, paintSurface);
    tools[Airbrush_tool].data = new AirbrushTool(toolSettings, paintSurface);
    tools[Line_tool].data = new LineTool(toolSettings, paintSurface);
    tools[PolyLine_tool].data = new PolyLineTool(toolSettings, paintSurface);
    tools[Rectangle_tool].data = new RectangleTool(toolSettings, paintSurface);
    tools[RoundedRectangle_tool].data = new RoundedRectangleTool(toolSettings, paintSurface);
    tools[Ellipse_tool].data = new EllipseTool(toolSettings, paintSurface);
    tools[Text_tool].data = new TextTool(toolSettings, paintSurface);

    // colorFrame    
    gridLayout = new GridLayout();
    gridLayout.numColumns = 3;
    gridLayout.marginHeight = 0;
    gridLayout.marginWidth = 0;
    colorFrame.setLayout(gridLayout);

    // activeForegroundColorCanvas, activeBackgroundColorCanvas
    activeForegroundColorCanvas = new Canvas(colorFrame, SWT.BORDER);
    gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
    gridData.heightHint = 24;
    gridData.widthHint = 24;
    activeForegroundColorCanvas.setLayoutData(gridData);

    activeBackgroundColorCanvas = new Canvas(colorFrame, SWT.BORDER);
    gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
    gridData.heightHint = 24;
    gridData.widthHint = 24;
    activeBackgroundColorCanvas.setLayoutData(gridData);

    // paletteCanvas
    final Canvas paletteCanvas = new Canvas(colorFrame, SWT.BORDER | SWT.NO_BACKGROUND);
    gridData = new GridData(GridData.FILL_HORIZONTAL);
    gridData.heightHint = 24;
    paletteCanvas.setLayoutData(gridData);
    paletteCanvas.addListener(SWT.MouseDown, new Listener() {
      public void handleEvent(Event e) {
        Rectangle bounds = paletteCanvas.getClientArea();
        Color color = getColorAt(bounds, e.x, e.y);        
          
        if (e.button == 1) setForegroundColor(color);
        else setBackgroundColor(color);
      }
      private Color getColorAt(Rectangle bounds, int x, int y) {
        if (bounds.height <= 1 && bounds.width <= 1) return paintColorWhite;
        final int row = (y - bounds.y) * numPaletteRows / bounds.height;
        final int col = (x - bounds.x) * numPaletteCols / bounds.width;
        return paintColors[Math.min(Math.max(row * numPaletteCols + col, 0), paintColors.length - 1)];
      }
    });
    Listener refreshListener = new Listener() {
      public void handleEvent(Event e) {
        if (e.gc == null) return;
        Rectangle bounds = paletteCanvas.getClientArea();
        for (int row = 0; row < numPaletteRows; ++row) {
          for (int col = 0; col < numPaletteCols; ++col) {
            final int x = bounds.width * col / numPaletteCols;
            final int y = bounds.height * row / numPaletteRows;
            final int width = Math.max(bounds.width * (col + 1) / numPaletteCols - x, 1);
            final int height = Math.max(bounds.height * (row + 1) / numPaletteRows - y, 1);
            e.gc.setBackground(paintColors[row * numPaletteCols + col]);
            e.gc.fillRectangle(bounds.x + x, bounds.y + y, width, height);
          }
        }
      }
    };
    paletteCanvas.addListener(SWT.Resize, refreshListener);
    paletteCanvas.addListener(SWT.Paint, refreshListener);
    //paletteCanvas.redraw();
    
    // toolSettingsFrame
    gridLayout = new GridLayout();
    gridLayout.numColumns = 4;
    gridLayout.marginHeight = 0;
    gridLayout.marginWidth = 0;
    toolSettingsFrame.setLayout(gridLayout);

    Label label = new Label(toolSettingsFrame, SWT.NONE);
    label.setText(getResourceString("settings.AirbrushRadius.text"));

    final Scale airbrushRadiusScale = new Scale(toolSettingsFrame, SWT.HORIZONTAL);
    airbrushRadiusScale.setMinimum(5);
    airbrushRadiusScale.setMaximum(50);
    airbrushRadiusScale.setSelection(toolSettings.airbrushRadius);
    airbrushRadiusScale.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL));
    airbrushRadiusScale.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        toolSettings.airbrushRadius = airbrushRadiusScale.getSelection();
        updateToolSettings();
      }
    });

    label = new Label(toolSettingsFrame, SWT.NONE);
    label.setText(getResourceString("settings.AirbrushIntensity.text"));

    final Scale airbrushIntensityScale = new Scale(toolSettingsFrame, SWT.HORIZONTAL);
    airbrushIntensityScale.setMinimum(1);
    airbrushIntensityScale.setMaximum(100);
    airbrushIntensityScale.setSelection(toolSettings.airbrushIntensity);
    airbrushIntensityScale.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL));
    airbrushIntensityScale.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        toolSettings.airbrushIntensity = airbrushIntensityScale.getSelection();
        updateToolSettings();
      }
    });
  }
    
  /**
   * Disposes of all resources associated with a particular
   * instance of the PaintExample.
   */  
  public void dispose() {
    if (paintSurface != null) paintSurface.dispose();    
    if (paintColors != null) {
      for (int i = 0; i < paintColors.length; ++i) {
        final Color color = paintColors[i];
        if (color != null) color.dispose();
      }
    }
    paintDefaultFont = null;
    paintColors = null;
    paintSurface = null;
    freeResources();
  }

  /**
   * Frees the resource bundle resources.
   */
  public void freeResources() {
    for (int i = 0; i < tools.length; ++i) {
      Tool tool = tools[i];
      final Image image = tool.image;
      if (image != null) image.dispose();
      tool.image = null;
    }
  }
  
  /**
   * Returns the Display.
   * 
   * @return the display we're using
   */
  public Display getDisplay() {
    return mainComposite.getDisplay();
  }
  
  /**
   * Gets a string from the resource bundle.
   * We don't want to crash because of a missing String.
   * Returns the key if not found.
   */
  public static String getResourceString(String key) {
      return key;
  }

  /**
   * Gets a string from the resource bundle and binds it
   * with the given arguments. If the key is not found,
   * return the key.
   */
  public static String getResourceString(String key, Object[] args) {
    try {
      return MessageFormat.format(getResourceString(key), args);
    } catch (MissingResourceException e) {
      return key;
    } catch (NullPointerException e) {
      return "!" + key + "!";
    }
  }

  /**
   * Initialize colors, fonts, and tool settings.
   */
  private void init() {
    Display display = mainComposite.getDisplay();
    
    paintColorWhite = new Color(display, 255, 255, 255);
    paintColorBlack = new Color(display, 0, 0, 0);
    
    paintDefaultFont = display.getSystemFont();

    paintColors = new Color[numPaletteCols * numPaletteRows];
    paintColors[0] = paintColorBlack;
    paintColors[1] = paintColorWhite;
    for (int i = 2; i < paintColors.length; i++) {
      paintColors[i] = new Color(display,
        ((i*7)%255),((i*23)%255), ((i*51)%255));
    }

    toolSettings = new ToolSettings();
    toolSettings.commonForegroundColor = paintColorBlack;
    toolSettings.commonBackgroundColor = paintColorWhite;
    toolSettings.commonFont = paintDefaultFont;
  }

  /**
   * Sets the action field of the tools
   */
  private void initActions() {
    for (int i = 0; i < tools.length; ++i) {
      final Tool tool = tools[i];
      String group = tool.group;
      if (group.equals("tool")) {
        tool.action = new Runnable() {
          public void run() {
            setPaintTool(tool.id);
          }
        };
      } else if (group.equals("fill")) {
        tool.action = new Runnable() {
          public void run() {
            setFillType(tool.id);
          }
        };
      } else if (group.equals("linestyle")) {
        tool.action = new Runnable() {
          public void run() {
            setLineStyle(tool.id);
          }
        };
      } else if (group.equals("options")) {
        tool.action = new Runnable() {
          public void run() {
            FontDialog fontDialog = new FontDialog(paintSurface.getShell(), SWT.PRIMARY_MODAL);
            FontData[] fontDatum = toolSettings.commonFont.getFontData();
            if (fontDatum != null && fontDatum.length > 0) {
              fontDialog.setFontList(fontDatum);
            }
            fontDialog.setText(getResourceString("options.Font.dialog.title"));

            paintSurface.hideRubberband();
            FontData fontData = fontDialog.open();
            paintSurface.showRubberband();
            if (fontData != null) {
              try {
                Font font = new Font(mainComposite.getDisplay(), fontData);
                toolSettings.commonFont = font;
                updateToolSettings();
              } catch (SWTException ex) {
              }
            }
          }
        };
      }
    }
  }

  /**
   * Loads the image resources.
   */
  public void initResources() {
    final Class clazz = PaintExample.class;
      try {
        for (int i = 0; i < tools.length; ++i) {
          Tool tool = tools[i];
          String id = tool.group + '.' + tool.name;
          InputStream sourceStream = clazz.getResourceAsStream(getResourceString(id + ".image"));
          ImageData source = new ImageData(sourceStream);
          ImageData mask = source.getTransparencyMask();
          tool.image = new Image(null, source, mask);
          try {
            sourceStream.close();
          } catch (IOException e) {
            e.printStackTrace();
          }
        }
        return;
      } catch (Throwable t) {
      }

    String error = "Unable to load resources";
    freeResources();
    throw new RuntimeException(error);
  }

  /**
   * Grabs input focus.
   */
  public void setFocus() {
    mainComposite.setFocus();
  }
  
  /**
   * Sets the tool foreground color.
   * 
   * @param color the new color to use
   */
  public void setForegroundColor(Color color) {
    if (activeForegroundColorCanvas != null)
      activeForegroundColorCanvas.setBackground(color);
    toolSettings.commonForegroundColor = color;
    updateToolSettings();
  }

  /**
   * Set the tool background color.
   * 
   * @param color the new color to use
   */
  public void setBackgroundColor(Color color) {
    if (activeBackgroundColorCanvas != null)
      activeBackgroundColorCanvas.setBackground(color);
    toolSettings.commonBackgroundColor = color;
    updateToolSettings();
  }

  /**
   * Selects a tool given its ID.
   */
  public void setPaintTool(int id) {
    PaintTool paintTool = (PaintTool) tools[id].data;
    paintSurface.setPaintSession(paintTool);
    updateToolSettings();
  }
  
  /**
   * Selects a filltype given its ID.
   */
  public void setFillType(int id) {
    Integer fillType = (Integer) tools[id].data;
    toolSettings.commonFillType = fillType.intValue();
    updateToolSettings();    
  }

  /**
   * Selects line type given its ID.
   */
  public void setLineStyle(int id) {
    Integer lineType = (Integer) tools[id].data;
    toolSettings.commonLineStyle = lineType.intValue();
    updateToolSettings();    
  }

  /**
   * Sets the size of the shell to it's "packed" size,
   * unless that makes it bigger than the display,
   * in which case set it to 9/10 of display size.
   */
  private static void setShellSize (Display display, Shell shell) {
    Rectangle bounds = display.getBounds();
    Point size = shell.computeSize (SWT.DEFAULT, SWT.DEFAULT);
    if (size.x > bounds.width) size.x = bounds.width * 9 / 10;
    if (size.y > bounds.height) size.y = bounds.height * 9 / 10;
    shell.setSize (size);
  }

  /**
   * Notifies the tool that its settings have changed.
   */
  private void updateToolSettings() {
    final PaintTool activePaintTool = paintSurface.getPaintTool();
    if (activePaintTool == null) return;
    
    activePaintTool.endSession();
    activePaintTool.set(toolSettings);
    activePaintTool.beginSession();
  }
}



/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/

/**
 * Tool Settings objects group tool-related configuration information.
 */
class ToolSettings {
  public static final int ftNone = 0, ftOutline = 1, ftSolid = 2;

  /**
   * commonForegroundColor: current tool foreground colour
   */
  public Color commonForegroundColor;

  /**
   * commonBackgroundColor: current tool background colour
   */
  public Color commonBackgroundColor;

  /**
   * commonFont: current font
   */
  public Font commonFont;

  /**
   * commonFillType: current fill type
   * <p>One of ftNone, ftOutline, ftSolid.</p>
   */
  public int commonFillType = ftNone;

  /**
   * commonLineStyle: current line type
   */
  public int commonLineStyle = SWT.LINE_SOLID;
  
  /**
   * airbrushRadius: coverage radius in pixels
   */
  public int airbrushRadius = 10;
  
  /**
   * airbrushIntensity: average surface area coverage in region defined by radius per "jot"
   */
  public int airbrushIntensity = 30;
  
  /**
   * roundedRectangleCornerDiameter: the diameter of curvature of corners in a rounded rectangle
   */
  public int roundedRectangleCornerDiameter = 16;
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/
class Tool {
  public int id;
  public String name;
  public String group;
  public int type;
  public Runnable action;
  public Image image = null;
  public Object data;
  
  public Tool(int id, String name, String group, int type) {
    super();
    this.id = id;
    this.name = name;
    this.group = group;
    this.type = type;
  }

  public Tool(int id, String name, String group, int type, Object data) {
    this(id, name, group, type);
    this.data = data;
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/

/**
 * A text drawing tool.
 */
class TextTool extends BasicPaintSession implements PaintTool {
  private ToolSettings settings;
  private String drawText = PaintExample.getResourceString("tool.Text.settings.defaulttext");

  /**
   * Constructs a PaintTool.
   * 
   * @param toolSettings the new tool settings
   * @param paintSurface the PaintSurface we will render on.
   */
  public TextTool(ToolSettings toolSettings, PaintSurface paintSurface) {
    super(paintSurface);
    set(toolSettings);
  }
  
  /**
   * Sets the tool's settings.
   * 
   * @param toolSettings the new tool settings
   */
  public void set(ToolSettings toolSettings) {
    settings = toolSettings;
  }
  
  /**
   * Returns name associated with this tool.
   * 
   * @return the localized name of this tool
   */
  public String getDisplayName() {
    return PaintExample.getResourceString("tool.Text.label");
  }
  
  /**
   * Activates the tool.
   */
  public void beginSession() {
    getPaintSurface().setStatusMessage(PaintExample.getResourceString(
      "session.Text.message"));
  }
  
  /**
   * Deactivates the tool.
     */
  public void endSession() {
    getPaintSurface().clearRubberbandSelection();
  }
  
  /**
   * Aborts the current operation.
   */
  public void resetSession() {
    getPaintSurface().clearRubberbandSelection();
  }
  
  /**
   * Handles a mouseDown event.
   * 
   * @param event the mouse event detail information
   */
  public void mouseDown(MouseEvent event) {
    if (event.button == 1) {
      // draw with left mouse button
      getPaintSurface().commitRubberbandSelection();
    } else {
      // set text with right mouse button
      getPaintSurface().clearRubberbandSelection();
      Shell shell = getPaintSurface().getShell();
      final Shell dialog = new Shell(shell, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
      dialog.setText(PaintExample.getResourceString("tool.Text.dialog.title"));
      dialog.setLayout(new GridLayout());
      Label label = new Label(dialog, SWT.NONE);
      label.setText(PaintExample.getResourceString("tool.Text.dialog.message"));
      label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
      final Text field = new Text(dialog, SWT.SINGLE | SWT.BORDER);
      field.setText(drawText);
      field.selectAll();
      field.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
      Composite buttons = new Composite(dialog, SWT.NONE);
      GridLayout layout = new GridLayout(2, true);
      layout.marginWidth = 0;
      buttons.setLayout(layout);
      buttons.setLayoutData(new GridData(SWT.END, SWT.CENTER, false, false));
      Button ok = new Button(buttons, SWT.PUSH);
      ok.setText(PaintExample.getResourceString("OK"));
      ok.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
      ok.addSelectionListener(new SelectionAdapter() {
        public void widgetSelected(SelectionEvent e) {
          drawText = field.getText();
          dialog.dispose();
        }
      });
      Button cancel = new Button(buttons, SWT.PUSH);
      cancel.setText(PaintExample.getResourceString("Cancel"));
      cancel.addSelectionListener(new SelectionAdapter() {
        public void widgetSelected(SelectionEvent e) {
          dialog.dispose();
        }
      });
      dialog.setDefaultButton(ok);
      dialog.pack();
      dialog.open();
      Display display = dialog.getDisplay();
      while (! shell.isDisposed() && ! dialog.isDisposed()) {
        if (! display.readAndDispatch()) display.sleep();
      }
    }  
  }

  /**
   * Handles a mouseDoubleClick event.
   * 
   * @param event the mouse event detail information
   */
  public void mouseDoubleClick(MouseEvent event) {
  }

  /**
   * Handles a mouseUp event.
   * 
   * @param event the mouse event detail information
   */
  public void mouseUp(MouseEvent event) {
  }
  
  /**
   * Handles a mouseMove event.
   * 
   * @param event the mouse event detail information
   */
  public void mouseMove(MouseEvent event) {
    final PaintSurface ps = getPaintSurface();
    ps.setStatusCoord(ps.getCurrentPosition());
    ps.clearRubberbandSelection();
    ps.addRubberbandSelection(
      new TextFigure(settings.commonForegroundColor, settings.commonFont,
        drawText, event.x, event.y));
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/

/**
 * 2D Rectangle object
 */
class TextFigure extends Figure {
  private Color  color;
  private Font   font;
  private String text;
  private int x, y;
  /**
   * Constructs a TextFigure
   * 
   * @param color the color for this object
   * @param font  the font for this object
   * @param text  the text to draw, tab and new-line expansion is performed
   * @param x     the virtual X coordinate of the top-left corner of the text bounding box
   * @param y     the virtual Y coordinate of the top-left corner of the text bounding box
   */
  public TextFigure(Color color, Font font, String text, int x, int y) {
    this.color = color; this.font = font; this.text = text; this.x = x; this.y = y;
  }
  public void draw(FigureDrawContext fdc) {
    Point p = fdc.toClientPoint(x, y);
    fdc.gc.setFont(font);
    fdc.gc.setForeground(color);
    fdc.gc.drawText(text, p.x, p.y, true);
  }
  public void addDamagedRegion(FigureDrawContext fdc, Region region) {
    Font oldFont = fdc.gc.getFont();
    fdc.gc.setFont(font);
    Point textExtent = fdc.gc.textExtent(text);
    fdc.gc.setFont(oldFont);
    region.add(fdc.toClientRectangle(x, y, x + textExtent.x, y + textExtent.y));
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/

/**
 * 2D SolidRectangle object
 */
class SolidRoundedRectangleFigure extends Figure {
  private Color color;
  private int x1, y1, x2, y2, diameter;
  /**
   * Constructs a SolidRectangle
   * These objects are defined by any two diametrically opposing corners.
   * 
   * @param color the color for this object
   * @param x1 the virtual X coordinate of the first corner
   * @param y1 the virtual Y coordinate of the first corner
   * @param x2 the virtual X coordinate of the second corner
   * @param y2 the virtual Y coordinate of the second corner
   * @param diameter the diameter of curvature of all four corners
   */
  public SolidRoundedRectangleFigure(Color color, int x1, int y1, int x2, int y2, int diameter) {
    this.color = color; this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2;
    this.diameter = diameter;
  }
  public void draw(FigureDrawContext fdc) {
    Rectangle r = fdc.toClientRectangle(x1, y1, x2, y2);
    fdc.gc.setBackground(color);
    fdc.gc.fillRoundRectangle(r.x, r.y, r.width, r.height, diameter, diameter);
  }
  public void addDamagedRegion(FigureDrawContext fdc, Region region) {
    region.add(fdc.toClientRectangle(x1, y1, x2, y2));
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/
/**
 * 2D SolidRectangle object
 */
class SolidRectangleFigure extends Figure {
  private Color color;
  private int x1, y1, x2, y2;
  /**
   * Constructs a SolidRectangle
   * These objects are defined by any two diametrically opposing corners.
   * 
   * @param color the color for this object
   * @param x1 the virtual X coordinate of the first corner
   * @param y1 the virtual Y coordinate of the first corner
   * @param x2 the virtual X coordinate of the second corner
   * @param y2 the virtual Y coordinate of the second corner
   */
  public SolidRectangleFigure(Color color, int x1, int y1, int x2, int y2) {
    this.color = color; this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2;
  }
  public void draw(FigureDrawContext fdc) {
    Rectangle r = fdc.toClientRectangle(x1, y1, x2, y2);
    fdc.gc.setBackground(color);
    fdc.gc.fillRectangle(r.x, r.y, r.width, r.height);
  }
  public void addDamagedRegion(FigureDrawContext fdc, Region region) {
    region.add(fdc.toClientRectangle(x1, y1, x2, y2));
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/
/**
 * 2D Line object
 */
class SolidPolygonFigure extends Figure {
  private Color color;
  private int[] points;
  /**
   * Constructs a SolidPolygon
   * These objects are defined by a sequence of vertices.
   * 
   * @param color the color for this object
   * @param vertices the array of vertices making up the polygon
   * @param numPoint the number of valid points in the array (n >= 3)
   */
  public SolidPolygonFigure(Color color, Point[] vertices, int numPoints) {
    this.color = color;
    this.points = new int[numPoints * 2];
    for (int i = 0; i < numPoints; ++i) {
      points[i * 2] = vertices[i].x;
      points[i * 2 + 1] = vertices[i].y;
    }
  }
  public void draw(FigureDrawContext fdc) {
    int[] drawPoints = new int[points.length];
    for (int i = 0; i < points.length; i += 2) {
      drawPoints[i] = points[i] * fdc.xScale - fdc.xOffset;
      drawPoints[i + 1] = points[i + 1] * fdc.yScale - fdc.yOffset;
    }
    fdc.gc.setBackground(color);
    fdc.gc.fillPolygon(drawPoints);
  }
  public void addDamagedRegion(FigureDrawContext fdc, Region region) {
    int xmin = Integer.MAX_VALUE, ymin = Integer.MAX_VALUE;
    int xmax = Integer.MIN_VALUE, ymax = Integer.MIN_VALUE;

    for (int i = 0; i < points.length; i += 2) {
      if (points[i] < xmin) xmin = points[i];
      if (points[i] > xmax) xmax = points[i];
      if (points[i+1] < ymin) ymin = points[i+1];
      if (points[i+1] > ymax) ymax = points[i+1];
    }    
    region.add(fdc.toClientRectangle(xmin, ymin, xmax, ymax));
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/
/**
 * 2D Solid Ellipse object
 */
class SolidEllipseFigure extends Figure {
  private Color color;
  private int x1, y1, x2, y2;
  /**
   * Constructs a SolidEllipse
   * These objects are defined by any two diametrically opposing corners of a box
   * bounding the ellipse.
   * 
   * @param color the color for this object
   * @param x1 the virtual X coordinate of the first corner
   * @param y1 the virtual Y coordinate of the first corner
   * @param x2 the virtual X coordinate of the second corner
   * @param y2 the virtual Y coordinate of the second corner
   */
  public SolidEllipseFigure(Color color, int x1, int y1, int x2, int y2) {
    this.color = color; this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2;
  }
  public void draw(FigureDrawContext fdc) {
    Rectangle r = fdc.toClientRectangle(x1, y1, x2, y2);
    fdc.gc.setBackground(color);
    fdc.gc.fillOval(r.x, r.y, r.width, r.height);
  }
  public void addDamagedRegion(FigureDrawContext fdc, Region region) {
    region.add(fdc.toClientRectangle(x1, y1, x2, y2));
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/


/**
 * The superclass for paint tools that contruct objects from individually
 * picked segments.
 */
abstract class SegmentedPaintSession extends BasicPaintSession {
  /**
   * The set of control points making up the segmented selection
   */
  private Vector /* of Point */ controlPoints = new Vector();

  /**
   * The previous figure (so that we can abort with right-button)
   */
  private Figure previousFigure = null;

  /**
   * The current figure (so that we can abort with right-button)
   */
  private Figure currentFigure = null;

  /**
   * Constructs a PaintSession.
   * 
   * @param paintSurface the drawing surface to use
   */
  protected SegmentedPaintSession(PaintSurface paintSurface) {
    super(paintSurface);
  }

  /**
   * Activates the tool.
   */
  public void beginSession() {
    getPaintSurface().setStatusMessage(PaintExample.getResourceString(
      "session.SegmentedInteractivePaint.message.anchorMode"));
    previousFigure = null;
    currentFigure = null;
    controlPoints.clear();
  }
  
  /**
   * Deactivates the tool.
     */
  public void endSession() {
    getPaintSurface().clearRubberbandSelection();
    if (previousFigure != null) getPaintSurface().drawFigure(previousFigure);
  }
  
  /**
   * Resets the tool.
   * Aborts any operation in progress.
   */
  public void resetSession() {
    getPaintSurface().clearRubberbandSelection();
    if (previousFigure != null) getPaintSurface().drawFigure(previousFigure);
    
    getPaintSurface().setStatusMessage(PaintExample.getResourceString(
      "session.SegmentedInteractivePaint.message.anchorMode"));
    previousFigure = null;
    currentFigure = null;
    controlPoints.clear();
  }

  /**
   * Handles a mouseDown event.
   * 
   * @param event the mouse event detail information
   */
  public void mouseDown(MouseEvent event) {
    if (event.button != 1) return;

    getPaintSurface().setStatusMessage(PaintExample.getResourceString(
      "session.SegmentedInteractivePaint.message.interactiveMode"));
    previousFigure = currentFigure;

    if (controlPoints.size() > 0) {
      final Point lastPoint = (Point) controlPoints.elementAt(controlPoints.size() - 1);
      if (lastPoint.x == event.x || lastPoint.y == event.y) return; // spurious event
    }
    controlPoints.add(new Point(event.x, event.y));
  }

  /**
   * Handles a mouseDoubleClick event.
   * 
   * @param event the mouse event detail information
   */
  public void mouseDoubleClick(MouseEvent event) {
    if (event.button != 1) return;
    if (controlPoints.size() >= 2) {
      getPaintSurface().clearRubberbandSelection();
      previousFigure = createFigure(
        (Point[]) controlPoints.toArray(new Point[controlPoints.size()]),
        controlPoints.size(), true);
    }
    resetSession();
  }

  /**
   * Handles a mouseUp event.
   * 
   * @param event the mouse event detail information
   */
  public void mouseUp(MouseEvent event) {
    if (event.button != 1) {
      resetSession(); // abort if right or middle mouse button pressed
      return;
    }
  }
  
  /**
   * Handles a mouseMove event.
   * 
   * @param event the mouse event detail information
   */
  public void mouseMove(MouseEvent event) {
    final PaintSurface ps = getPaintSurface();
    if (controlPoints.size() == 0) {
      ps.setStatusCoord(ps.getCurrentPosition());
      return; // spurious event
    } else {
      ps.setStatusCoordRange((Point) controlPoints.elementAt(controlPoints.size() - 1),
        ps.getCurrentPosition());
    }

    ps.clearRubberbandSelection();
    Point[] points = (Point[]) controlPoints.toArray(new Point[controlPoints.size() + 1]);
    points[controlPoints.size()] = ps.getCurrentPosition();
    currentFigure = createFigure(points, points.length, false);
    ps.addRubberbandSelection(currentFigure);
  }  

  /**
   * Template Method: Creates a Figure for drawing rubberband entities and the final product
   * 
   * @param points the array of control points
   * @param numPoints the number of valid points in the array (n >= 2)
   * @param closed true if the user double-clicked on the final control point
   */
  protected abstract Figure createFigure(Point[] points, int numPoints, boolean closed);
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/
/**
 * A drawing tool.
 */
class RoundedRectangleTool extends DragPaintSession implements PaintTool {
  private ToolSettings settings;

  /**
   * Constructs a RoundedRectangleTool.
   * 
   * @param toolSettings the new tool settings
   * @param paintSurface the PaintSurface we will render on.
   */
  public RoundedRectangleTool(ToolSettings toolSettings, PaintSurface paintSurface) {
    super(paintSurface);
    set(toolSettings);
  }
  
  /**
   * Sets the tool's settings.
   * 
   * @param toolSettings the new tool settings
   */
  public void set(ToolSettings toolSettings) {
    settings = toolSettings;
  }
  
  /**
   * Returns name associated with this tool.
   * 
   * @return the localized name of this tool
   */
  public String getDisplayName() {
    return PaintExample.getResourceString("tool.RoundedRectangle.label");
  }

  /*
   * Template methods for drawing
   */
  protected Figure createFigure(Point a, Point b) {
    ContainerFigure container = new ContainerFigure();
    if (settings.commonFillType != ToolSettings.ftNone)
      container.add(new SolidRoundedRectangleFigure(settings.commonBackgroundColor,
        a.x, a.y, b.x, b.y, settings.roundedRectangleCornerDiameter));
    if (settings.commonFillType != ToolSettings.ftSolid)
      container.add(new RoundedRectangleFigure(settings.commonForegroundColor, settings.commonBackgroundColor,
        settings.commonLineStyle, a.x, a.y, b.x, b.y, settings.roundedRectangleCornerDiameter));
    return container;
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/

/**
 * 2D Rectangle object
 */
class RoundedRectangleFigure extends Figure {
  private Color foregroundColor, backgroundColor;
  private int lineStyle, x1, y1, x2, y2, diameter;
  /**
   * Constructs a Rectangle
   * These objects are defined by any two diametrically opposing corners.
   * 
   * @param color the color for this object
   * @param lineStyle the line style for this object
   * @param x1 the virtual X coordinate of the first corner
   * @param y1 the virtual Y coordinate of the first corner
   * @param x2 the virtual X coordinate of the second corner
   * @param y2 the virtual Y coordinate of the second corner
   * @param diameter the diameter of curvature of all four corners
   */
  public RoundedRectangleFigure(Color foregroundColor, Color backgroundColor, int lineStyle, int x1, int y1, int x2, int y2, int diameter) {
    this.foregroundColor = foregroundColor;
    this.backgroundColor = backgroundColor;
    this.lineStyle = lineStyle;
    this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2;
    this.diameter = diameter;
  }
  public void draw(FigureDrawContext fdc) {
    Rectangle r = fdc.toClientRectangle(x1, y1, x2, y2);
    fdc.gc.setForeground(foregroundColor);
    fdc.gc.setBackground(backgroundColor);
    fdc.gc.setLineStyle(lineStyle);
    fdc.gc.drawRoundRectangle(r.x, r.y, r.width - 1, r.height - 1, diameter, diameter);
    fdc.gc.setLineStyle(SWT.LINE_SOLID);
  }
  public void addDamagedRegion(FigureDrawContext fdc, Region region) {
    region.add(fdc.toClientRectangle(x1, y1, x2, y2));
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/

/**
 * A drawing tool.
 */
class RectangleTool extends DragPaintSession implements PaintTool {
  private ToolSettings settings;

  /**
   * Constructs a RectangleTool.
   * 
   * @param toolSettings the new tool settings
   * @param paintSurface the PaintSurface we will render on.
   */
  public RectangleTool(ToolSettings toolSettings, PaintSurface paintSurface) {
    super(paintSurface);
    set(toolSettings);
  }
  
  /**
   * Sets the tool's settings.
   * 
   * @param toolSettings the new tool settings
   */
  public void set(ToolSettings toolSettings) {
    settings = toolSettings;
  }
  
  /**
   * Returns name associated with this tool.
   * 
   * @return the localized name of this tool
   */
  public String getDisplayName() {
    return PaintExample.getResourceString("tool.Rectangle.label");
  }

  /*
   * Template method for drawing
   */
  protected Figure createFigure(Point a, Point b) {
    switch (settings.commonFillType) {
      default:
      case ToolSettings.ftNone:
        return new RectangleFigure(settings.commonForegroundColor, settings.commonBackgroundColor, settings.commonLineStyle,
          a.x, a.y, b.x, b.y);
      case ToolSettings.ftSolid:
        return new SolidRectangleFigure(settings.commonBackgroundColor, a.x, a.y, b.x, b.y);
      case ToolSettings.ftOutline: {
        ContainerFigure container = new ContainerFigure();
        container.add(new SolidRectangleFigure(settings.commonBackgroundColor, a.x, a.y, b.x, b.y));
        container.add(new RectangleFigure(settings.commonForegroundColor, settings.commonBackgroundColor, settings.commonLineStyle,
          a.x, a.y, b.x, b.y));
        return container;
      }
    }
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/
/**
 * 2D Rectangle object
 */
class RectangleFigure extends Figure {
  private Color foregroundColor, backgroundColor;
  private int lineStyle, x1, y1, x2, y2;
  /**
   * Constructs a Rectangle
   * These objects are defined by any two diametrically opposing corners.
   * 
   * @param color the color for this object
   * @param lineStyle the line style for this object
   * @param x1 the virtual X coordinate of the first corner
   * @param y1 the virtual Y coordinate of the first corner
   * @param x2 the virtual X coordinate of the second corner
   * @param y2 the virtual Y coordinate of the second corner
   */
  public RectangleFigure(Color foregroundColor, Color backgroundColor, int lineStyle, int x1, int y1, int x2, int y2) {
    this.foregroundColor = foregroundColor;
    this.backgroundColor = backgroundColor;
    this.lineStyle = lineStyle;
    this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2;
  }
  public void draw(FigureDrawContext fdc) {
    Rectangle r = fdc.toClientRectangle(x1, y1, x2, y2);
    fdc.gc.setForeground(foregroundColor);
    fdc.gc.setBackground(backgroundColor);
    fdc.gc.setLineStyle(lineStyle);
    fdc.gc.drawRectangle(r.x, r.y, r.width - 1, r.height - 1);
    fdc.gc.setLineStyle(SWT.LINE_SOLID);
  }
  public void addDamagedRegion(FigureDrawContext fdc, Region region) {
    region.add(fdc.toClientRectangle(x1, y1, x2, y2));
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/
/**
 * A polyline drawing tool.
 */
class PolyLineTool extends SegmentedPaintSession implements PaintTool {
  private ToolSettings settings;

  /**
   * Constructs a PolyLineTool.
   * 
   * @param toolSettings the new tool settings
   * @param paintSurface the PaintSurface we will render on.
   */
  public PolyLineTool(ToolSettings toolSettings, PaintSurface paintSurface) {
    super(paintSurface);
    set(toolSettings);
  }
  
  /**
   * Sets the tool's settings.
   * 
   * @param toolSettings the new tool settings
   */
  public void set(ToolSettings toolSettings) {
    settings = toolSettings;
  }

  /**
   * Returns the name associated with this tool.
   * 
   * @return the localized name of this tool
   */
  public String getDisplayName() {
    return PaintExample.getResourceString("tool.PolyLine.label");
  }

  /*
   * Template methods for drawing
   */
  protected Figure createFigure(Point[] points, int numPoints, boolean closed) {
    ContainerFigure container = new ContainerFigure();
    if (closed && settings.commonFillType != ToolSettings.ftNone && numPoints >= 3) {
      container.add(new SolidPolygonFigure(settings.commonBackgroundColor, points, numPoints));
    }
    if (! closed || settings.commonFillType != ToolSettings.ftSolid || numPoints < 3) {
      for (int i = 0; i < numPoints - 1; ++i) {
        final Point a = points[i];
        final Point b = points[i + 1];
        container.add(new LineFigure(settings.commonForegroundColor, settings.commonBackgroundColor, settings.commonLineStyle,
          a.x, a.y, b.x, b.y));
      }
      if (closed) {
        final Point a = points[points.length - 1];
        final Point b = points[0];
        container.add(new LineFigure(settings.commonForegroundColor, settings.commonBackgroundColor, settings.commonLineStyle,
          a.x, a.y, b.x, b.y));
      }
    }
    return container;
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/
/**
 * 2D Point object
 */
class PointFigure extends Figure {
  private Color color;
  private int x, y;
  /**
   * Constructs a Point
   * 
   * @param color the color for this object
   * @param x the virtual X coordinate of the first end-point
   * @param y the virtual Y coordinate of the first end-point
   */
  public PointFigure(Color color, int x, int y) {
    this.color = color; this.x = x; this.y = y;
  }
  public void draw(FigureDrawContext fdc) {
    Point p = fdc.toClientPoint(x, y);
    fdc.gc.setBackground(color);
    fdc.gc.fillRectangle(p.x, p.y, 1, 1);
  }
  public void addDamagedRegion(FigureDrawContext fdc, Region region) {
    region.add(fdc.toClientRectangle(x, y, x, y));
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/
/**
 * A pencil tool.
 */
class PencilTool extends ContinuousPaintSession implements PaintTool {
  private ToolSettings settings;
  
  /**
   * Constructs a pencil tool.
   * 
   * @param toolSettings the new tool settings
   * @param getPaintSurface() the PaintSurface we will render on.
   */
  public PencilTool(ToolSettings toolSettings, PaintSurface paintSurface) {
    super(paintSurface);
    set(toolSettings);
  }
  
  /**
   * Sets the tool's settings.
   * 
   * @param toolSettings the new tool settings
   */
  public void set(ToolSettings toolSettings) {
    settings = toolSettings;
  }

  /**
   * Returns the name associated with this tool.
   * 
   * @return the localized name of this tool
   */
  public String getDisplayName() {
    return PaintExample.getResourceString("tool.Pencil.label");
  }

  /*
   * Template method for drawing
   */
  public void render(final Point point) {
    final PaintSurface ps = getPaintSurface();
    ps.drawFigure(new PointFigure(settings.commonForegroundColor, point.x, point.y));
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/
interface PaintTool extends PaintSession {
  /**
   * Sets the tool's settings.
   * 
   * @param toolSettings the new tool settings
   */
  public void set(ToolSettings toolSettings);
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/

/**
 * Manages a simple drawing surface.
 */
class PaintSurface {
  private Point currentPosition = new Point(0, 0);
  private Canvas paintCanvas;

  private PaintSession paintSession;
  private Image image;
  private Image paintImage; // buffer for refresh blits
  private int   imageWidth, imageHeight;
  private int   visibleWidth, visibleHeight;

  private FigureDrawContext displayFDC = new FigureDrawContext();
  private FigureDrawContext imageFDC = new FigureDrawContext();
  private FigureDrawContext paintFDC = new FigureDrawContext();

  /* Rubberband */
  private ContainerFigure rubberband = new ContainerFigure();
    // the active rubberband selection
  private int rubberbandHiddenNestingCount = 0;
    // always >= 0, if > 0 rubberband has been hidden

  /* Status */
  private Text statusText;
  private String statusActionInfo, statusMessageInfo, statusCoordInfo;

  /**
   * Constructs a PaintSurface.
   * <p>
   * paintCanvas must have SWT.NO_REDRAW_RESIZE and SWT.NO_BACKGROUND styles,
   *     and may have SWT.V_SCROLL and/or SWT.H_SCROLL.
   * </p>
   * @param paintCanvas the Canvas object in which to render
   * @param paintStatus the PaintStatus object to use for providing user feedback
   * @param fillColor the color to fill the canvas with initially
   */
  public PaintSurface(Canvas paintCanvas, Text statusText, Color fillColor) {
    this.paintCanvas = paintCanvas;
    this.statusText = statusText;
    clearStatus();

    /* Set up the drawing surface */
    Rectangle displayRect = paintCanvas.getDisplay().getClientArea();
    imageWidth = displayRect.width;
    imageHeight = displayRect.height;
    image = new Image(paintCanvas.getDisplay(), imageWidth, imageHeight);

    imageFDC.gc = new GC(image);
    imageFDC.gc.setBackground(fillColor);
    imageFDC.gc.fillRectangle(0, 0, imageWidth, imageHeight);
    displayFDC.gc = new GC(paintCanvas);

    /* Initialize the session */
    setPaintSession(null);

    /* Add our listeners */
    paintCanvas.addDisposeListener(new DisposeListener() {
      public void widgetDisposed(DisposeEvent e) {
        displayFDC.gc.dispose();
      }      
    });
    paintCanvas.addMouseListener(new MouseAdapter() {
      public void mouseDown(MouseEvent event) {
        processMouseEventCoordinates(event);
        if (paintSession != null) paintSession.mouseDown(event);
      }
      public void mouseUp(MouseEvent event) {
        processMouseEventCoordinates(event);
        if (paintSession != null) paintSession.mouseUp(event);
      }
      public void mouseDoubleClick(MouseEvent event) {
        processMouseEventCoordinates(event);
        if (paintSession != null) paintSession.mouseDoubleClick(event);
      }      
    });
    paintCanvas.addMouseMoveListener(new MouseMoveListener() {
      public void mouseMove(MouseEvent event) {
        processMouseEventCoordinates(event);
        if (paintSession != null) paintSession.mouseMove(event);
      }
    });
    paintCanvas.addPaintListener(new PaintListener() {
      public void paintControl(PaintEvent event) {
        if (rubberband.isEmpty()) {
          // Nothing to merge, so we just refresh
          event.gc.drawImage(image,
            displayFDC.xOffset + event.x, displayFDC.yOffset + event.y, event.width, event.height,
            event.x, event.y, event.width, event.height);
        } else {
          /*
           * Avoid flicker when merging overlayed objects by constructing the image on
           * a backbuffer first, then blitting it to the screen.
           */
          // Check that the backbuffer is large enough
          if (paintImage != null) {
            Rectangle rect = paintImage.getBounds();
            if ((event.width + event.x > rect.width) ||
              (event.height + event.y > rect.height)) {
              paintFDC.gc.dispose();
              paintImage.dispose();
              paintImage = null;
            }
          }
          if (paintImage == null) {
            Display display = getDisplay();
            Rectangle rect = display.getClientArea();
            paintImage = new Image(display,
              Math.max(rect.width, event.width + event.x),
              Math.max(rect.height, event.height + event.y));
            paintFDC.gc = new GC(paintImage);
          }
          // Setup clipping and the FDC
          Region clipRegion = new Region();
          event.gc.getClipping(clipRegion);          
          paintFDC.gc.setClipping(clipRegion);
          clipRegion.dispose();

          paintFDC.xOffset = displayFDC.xOffset;
          paintFDC.yOffset = displayFDC.yOffset;
          paintFDC.xScale = displayFDC.xScale;
          paintFDC.yScale = displayFDC.yScale;
          
          // Merge the overlayed objects into the image, then blit
          paintFDC.gc.drawImage(image,
            displayFDC.xOffset + event.x, displayFDC.yOffset + event.y, event.width, event.height,
            event.x, event.y, event.width, event.height);
          rubberband.draw(paintFDC);
          event.gc.drawImage(paintImage,
            event.x, event.y, event.width, event.height,
            event.x, event.y, event.width, event.height);
        }
      }
    });
    paintCanvas.addControlListener(new ControlAdapter() {
      public void controlResized(ControlEvent event) {
        handleResize();
      }      
    });

    /* Set up the paint canvas scroll bars */
    ScrollBar horizontal = paintCanvas.getHorizontalBar();
    horizontal.setVisible(true);
    horizontal.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent event) {
        scrollHorizontally((ScrollBar)event.widget);
      }
    });
    ScrollBar vertical = paintCanvas.getVerticalBar();
    vertical.setVisible(true);
    vertical.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent event) {
        scrollVertically((ScrollBar)event.widget);
      }
    });
    handleResize();
  }
  
  /**
   * Disposes of the PaintSurface's resources.
   */
  public void dispose() {
    imageFDC.gc.dispose();
    image.dispose();
    if (paintImage != null) {
      paintImage.dispose();
      paintFDC.gc.dispose();
    }

    currentPosition = null;
    paintCanvas = null;
    paintSession = null;
    image = null;
    paintImage = null;
    displayFDC = null;
    imageFDC = null;
    paintFDC = null;
    rubberband = null;
    statusText = null;
    statusActionInfo = null;
    statusMessageInfo = null;
    statusCoordInfo = null;
  }

  /**
   * Called when we must grab focus.
   */
  public void setFocus()  {
    paintCanvas.setFocus();
  }

  /**
   * Returns the Display on which the PaintSurface resides.
   * @return the Display
   */
  public Display getDisplay() {
    return paintCanvas.getDisplay();
  }

  /**
   * Returns the Shell in which the PaintSurface resides.
   * @return the Shell
   */
  public Shell getShell() {
    return paintCanvas.getShell();
  }

  /**
   * Sets the current paint session.
   * <p>
   * If oldPaintSession != paintSession calls oldPaintSession.end()
   * and paintSession.begin()
   * </p>
   * 
   * @param paintSession the paint session to activate; null to disable all sessions
   */
  public void setPaintSession(PaintSession paintSession) {
    if (this.paintSession != null) {
      if (this.paintSession == paintSession) return;
      this.paintSession.endSession();
    }
    this.paintSession = paintSession;
    clearStatus();
    if (paintSession != null) {
      setStatusAction(paintSession.getDisplayName());
      paintSession.beginSession();
    } else {
      setStatusAction(PaintExample.getResourceString("tool.Null.label"));
      setStatusMessage(PaintExample.getResourceString("session.Null.message"));
    }
  }

  /**
   * Returns the current paint session.
   * 
   * @return the current paint session, null if none is active
   */
  public PaintSession getPaintSession() {
    return paintSession;
  }

  /**
   * Returns the current paint tool.
   * 
   * @return the current paint tool, null if none is active (though some other session
   *         might be)
   */
  public PaintTool getPaintTool() {
    return (paintSession != null && paintSession instanceof PaintTool) ?
      (PaintTool)paintSession : null;
  }

  /**
   * Returns the current position in an interactive operation.
   *
   * @return the last known position of the pointer
   */
  public Point getCurrentPosition() {
    return currentPosition;
  }

  /**
   * Draws a Figure object to the screen and to the backing store permanently.
   * 
   * @param object the object to draw onscreen
   */
  public void drawFigure(Figure object) {
    object.draw(imageFDC);
    object.draw(displayFDC);
  }

  /**
   * Adds a Figure object to the active rubberband selection.
   * <p>
   * This object will be drawn to the screen as a preview and refreshed appropriately
   * until the selection is either cleared or committed.
   * </p>
   * 
   * @param object the object to add to the selection
   */
  public void addRubberbandSelection(Figure object) {
    rubberband.add(object);
    if (! isRubberbandHidden()) object.draw(displayFDC);
  }

  /**
   * Clears the active rubberband selection.
   * <p>
   * Erases any rubberband objects on the screen then clears the selection.
   * </p>
   */
  public void clearRubberbandSelection() {
    if (! isRubberbandHidden()) {
      Region region = new Region();
      rubberband.addDamagedRegion(displayFDC, region);
      Rectangle r = region.getBounds();
      paintCanvas.redraw(r.x, r.y, r.width, r.height, true);
      region.dispose();
    }
    rubberband.clear();

  }

  /**
   * Commits the active rubberband selection.
   * <p>
   * Redraws any rubberband objects on the screen as permanent objects then clears the selection.
   * </p>
   */
  public void commitRubberbandSelection() {
    rubberband.draw(imageFDC);
    if (isRubberbandHidden()) rubberband.draw(displayFDC);
    rubberband.clear();
  }
  
  /**
   * Hides the rubberband (but does not eliminate it).
   * <p>
   * Increments by one the rubberband "hide" nesting count.  The rubberband
   * is hidden from view (but remains active) if it wasn't already hidden.
   * </p>
   */
  public void hideRubberband() {
    if (rubberbandHiddenNestingCount++ <= 0) {
      Region region = new Region();
      rubberband.addDamagedRegion(displayFDC, region);
      Rectangle r = region.getBounds();
      paintCanvas.redraw(r.x, r.y, r.width, r.height, true);
      region.dispose();
    }
  }    

  /**
   * Shows (un-hides) the rubberband.
   * <p>
   * Decrements by one the rubberband "hide" nesting count.  The rubberband
   * is only made visible when showRubberband() has been called once for each
   * previous hideRubberband().  It is not permitted to call showRubberband() if
   * the rubber band is not presently hidden.
   * </p>
   */
  public void showRubberband() {
    if (rubberbandHiddenNestingCount <= 0)
      throw new IllegalStateException("rubberbandHiddenNestingCount > 0");
    if (--rubberbandHiddenNestingCount == 0) {
      rubberband.draw(displayFDC);
    }
  }
  
  /**
   * Determines if the rubberband is hidden.
   * 
   * @return true iff the rubber is hidden
   */
  public boolean isRubberbandHidden() {
    return rubberbandHiddenNestingCount > 0;
  }

  /**
   * Handles a horizontal scroll event
   * 
   * @param scrollbar the horizontal scroll bar that posted this event
   */
  public void scrollHorizontally(ScrollBar scrollBar) {
    if (image == null) return;
    if (imageWidth > visibleWidth) {
      final int oldOffset = displayFDC.xOffset;
      final int newOffset = Math.min(scrollBar.getSelection(), imageWidth - visibleWidth);
      if (oldOffset != newOffset) {
        paintCanvas.update();
        displayFDC.xOffset = newOffset;
        paintCanvas.scroll(Math.max(oldOffset - newOffset, 0), 0, Math.max(newOffset - oldOffset, 0), 0,
          visibleWidth, visibleHeight, false);
      }
    }
  }

  /**
   * Handles a vertical scroll event
   * 
   * @param scrollbar the vertical scroll bar that posted this event
   */
  public void scrollVertically(ScrollBar scrollBar) {
    if (image == null) return;
    if (imageHeight > visibleHeight) {
      final int oldOffset = displayFDC.yOffset;
      final int newOffset = Math.min(scrollBar.getSelection(), imageHeight - visibleHeight);
      if (oldOffset != newOffset) {
        paintCanvas.update();
        displayFDC.yOffset = newOffset;
        paintCanvas.scroll(0, Math.max(oldOffset - newOffset, 0), 0, Math.max(newOffset - oldOffset, 0),
          visibleWidth, visibleHeight, false);
      }
    }
  }
  
  /**
   * Handles resize events
   */
  private void handleResize() {
    paintCanvas.update();

    Rectangle visibleRect = paintCanvas.getClientArea();
    visibleWidth = visibleRect.width;
    visibleHeight = visibleRect.height;

    ScrollBar horizontal = paintCanvas.getHorizontalBar();
    if (horizontal != null) {
      displayFDC.xOffset = Math.min(horizontal.getSelection(), imageWidth - visibleWidth);
      if (imageWidth <= visibleWidth) {
        horizontal.setEnabled(false);
        horizontal.setSelection(0);
      } else {
        horizontal.setEnabled(true);
        horizontal.setValues(displayFDC.xOffset, 0, imageWidth, visibleWidth,
          8, visibleWidth);
      }
    }

    ScrollBar vertical = paintCanvas.getVerticalBar();
    if (vertical != null) {
      displayFDC.yOffset = Math.min(vertical.getSelection(), imageHeight - visibleHeight);
      if (imageHeight <= visibleHeight) {
        vertical.setEnabled(false);
        vertical.setSelection(0);
      } else {
        vertical.setEnabled(true);
        vertical.setValues(displayFDC.yOffset, 0, imageHeight, visibleHeight,
          8, visibleHeight);
      }
    }
  }

  /**
   * Virtualizes MouseEvent coordinates and stores the current position.
   */
  private void processMouseEventCoordinates(MouseEvent event) {
    currentPosition.x = event.x =
      Math.min(Math.max(event.x, 0), visibleWidth - 1) + displayFDC.xOffset;
    currentPosition.y = event.y =
      Math.min(Math.max(event.y, 0), visibleHeight - 1) + displayFDC.yOffset;
  }
  
  /**
   * Clears the status bar.
   */
  public void clearStatus() {
    statusActionInfo = "";
    statusMessageInfo = "";
    statusCoordInfo = "";
    updateStatus();
  }

  /**
   * Sets the status bar action text.
   *
   * @param action the action in progress, null to clear
   */
  public void setStatusAction(String action) {
    statusActionInfo = (action != null) ? action : "";
    updateStatus();
  }
  
  /**
   * Sets the status bar message text.
   * 
   * @param message the message to display, null to clear
   */
  public void setStatusMessage(String message) {
    statusMessageInfo = (message != null) ? message : "";
    updateStatus();
  }

  /**
   * Sets the coordinates in the status bar.
   * 
   * @param coord the coordinates to display, null to clear
   */
  public void setStatusCoord(Point coord) {
    statusCoordInfo = (coord != null) ? PaintExample.getResourceString("status.Coord.format", new Object[]
      { new Integer(coord.x), new Integer(coord.y)}) : "";
    updateStatus();
  }

  /**
   * Sets the coordinate range in the status bar.
   * 
   * @param a the "from" coordinate, must not be null
   * @param b the "to" coordinate, must not be null
   */
  public void setStatusCoordRange(Point a, Point b) {
    statusCoordInfo = PaintExample.getResourceString("status.CoordRange.format", new Object[]
      { new Integer(a.x), new Integer(a.y), new Integer(b.x), new Integer(b.y)});
    updateStatus();
  }

  /**
   * Updates the display.
   */
  private void updateStatus() {
    statusText.setText(
      PaintExample.getResourceString("status.Bar.format", new Object[]
      { statusActionInfo, statusMessageInfo, statusCoordInfo }));
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/

/**
 * Manages an interactive paint session.
 * Note that the coordinates received via the listener interfaces are virtualized to zero-origin
 * relative to the painting surface.
 */ 
interface PaintSession extends MouseListener, MouseMoveListener {
  /**
   * Returns the paint surface associated with this paint session
   * 
   * @return the associated PaintSurface
   */
  public PaintSurface getPaintSurface();

  /**
   * Activates the session.
   * 
   * Note: When overriding this method, call super.beginSession() at method start.
   */
  public abstract void beginSession();
  
  /**
   * Deactivates the session.
     *
   * Note: When overriding this method, call super.endSession() at method exit.
     */
  public abstract void endSession();
  
  /**
   * Resets the session.
   * Aborts any operation in progress.
   * 
   * Note: When overriding this method, call super.resetSession() at method exit.
   */
  public abstract void resetSession();
  
  
  /**
   * Returns the name associated with this tool.
   * 
   * @return the localized name of this tool
   */
  public String getDisplayName();
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/

/**
 * A line drawing tool
 */
class LineTool extends DragPaintSession implements PaintTool {
  private ToolSettings settings;

  /**
   * Constructs a LineTool.
   * 
   * @param toolSettings the new tool settings
   * @param paintSurface the PaintSurface we will render on.
   */
  public LineTool(ToolSettings toolSettings, PaintSurface paintSurface) {
    super(paintSurface);
    set(toolSettings);
  }
  
  /**
   * Sets the tool's settings.
   * 
   * @param toolSettings the new tool settings
   */
  public void set(ToolSettings toolSettings) {
    settings = toolSettings;
  }
  
  /**
   * Returns name associated with this tool.
   * 
   * @return the localized name of this tool
   */
  public String getDisplayName() {
    return PaintExample.getResourceString("tool.Line.label");
  }

  /*
   * Template methods for drawing
   */
  protected Figure createFigure(Point a, Point b) {
    return new LineFigure(settings.commonForegroundColor, settings.commonBackgroundColor, settings.commonLineStyle,
      a.x, a.y, b.x, b.y);
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/

/**
 * 2D Line object
 */
class LineFigure extends Figure {
  private Color foregroundColor, backgroundColor;
  private int lineStyle, x1, y1, x2, y2;
  /**
   * Constructs a Line
   * These objects are defined by their two end-points.
   * 
   * @param color the color for this object
   * @param lineStyle the line style for this object
   * @param x1 the virtual X coordinate of the first end-point
   * @param y1 the virtual Y coordinate of the first end-point
   * @param x2 the virtual X coordinate of the second end-point
   * @param y2 the virtual Y coordinate of the second end-point
   */
  public LineFigure(Color foregroundColor, Color backgroundColor, int lineStyle, int x1, int y1, int x2, int y2) {
    this.foregroundColor = foregroundColor;
    this.backgroundColor = backgroundColor;
    this.lineStyle = lineStyle;
    this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2;
  }
  public void draw(FigureDrawContext fdc) {
    Point p1 = fdc.toClientPoint(x1, y1);
    Point p2 = fdc.toClientPoint(x2, y2);
    fdc.gc.setForeground(foregroundColor);
    fdc.gc.setBackground(backgroundColor);
    fdc.gc.setLineStyle(lineStyle);
    fdc.gc.drawLine(p1.x, p1.y, p2.x, p2.y);
    fdc.gc.setLineStyle(SWT.LINE_SOLID);
  }
  public void addDamagedRegion(FigureDrawContext fdc, Region region) {
    region.add(fdc.toClientRectangle(x1, y1, x2, y2));
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/
class FigureDrawContext {
  /*
   * <p>
   * The GC must be set up as follows
   * (it will be returned to this state upon completion of drawing operations)
   * <ul>
   *   <li>setXORMode(false)
   * </ul>
   * </p>
   */
  public GC gc = null;
  public int xOffset = 0, yOffset = 0; // substract to get GC coords
  public int xScale = 1, yScale = 1;
  
  public Rectangle toClientRectangle(int x1, int y1, int x2, int y2) {
    return new Rectangle(
      Math.min(x1, x2) * xScale - xOffset,
      Math.min(y1, y2) * yScale - yOffset,
      (Math.abs(x2 - x1) + 1) * xScale,
      (Math.abs(y2 - y1) + 1) * yScale);
  }
  public Point toClientPoint(int x, int y) {
    return new Point(x * xScale - xOffset, y * yScale - yOffset);
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/

/**
 * Superinterface for all drawing objects.
 * All drawing objects know how to render themselved to the screen and can draw a
 * temporary version of themselves for previewing the general appearance of the
 * object onscreen before it gets committed.
 */
abstract class Figure {
  /**
   * Draws this object.
   * 
   * @param fdc a parameter block specifying drawing-related information
   */
  public abstract void draw(FigureDrawContext fdc);

  /**
   * Computes the damaged screen region caused by drawing this object (imprecise), then
   * appends it to the supplied region.
   * 
   * @param fdc a parameter block specifying drawing-related information
   * @param region a region to which additional damage areas will be added
   */
  public abstract void addDamagedRegion(FigureDrawContext fdc, Region region);
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/
/**
 * A drawing tool.
 */
class EllipseTool extends DragPaintSession implements PaintTool {
  private ToolSettings settings;

  /**
   * Constructs a EllipseTool.
   * 
   * @param toolSettings the new tool settings
   * @param paintSurface the PaintSurface we will render on.
   */
  public EllipseTool(ToolSettings toolSettings, PaintSurface paintSurface) {
    super(paintSurface);
    set(toolSettings);
  }

  /**
   * Sets the tool's settings.
   * 
   * @param toolSettings the new tool settings
   */
  public void set(ToolSettings toolSettings) {
    settings = toolSettings;
  }

  /**
   * Returns name associated with this tool.
   * 
   * @return the localized name of this tool
   */
  public String getDisplayName() {
    return PaintExample.getResourceString("tool.Ellipse.label");
  }

  /*
   * Template methods for drawing
   */
  protected Figure createFigure(Point a, Point b) {
    ContainerFigure container = new ContainerFigure();
    if (settings.commonFillType != ToolSettings.ftNone)
      container.add(new SolidEllipseFigure(settings.commonBackgroundColor, a.x, a.y, b.x, b.y));
    if (settings.commonFillType != ToolSettings.ftSolid)
      container.add(new EllipseFigure(settings.commonForegroundColor, settings.commonBackgroundColor, settings.commonLineStyle,
        a.x, a.y, b.x, b.y));
    return container;
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/

/**
 * 2D Ellipse object
 */
class EllipseFigure extends Figure {
  private Color foregroundColor, backgroundColor;
  private int lineStyle, x1, y1, x2, y2;
  /**
   * Constructs an Ellipse
   * These objects are defined by any two diametrically opposing corners of a box
   * bounding the ellipse.
   * 
   * @param color the color for this object
   * @param lineStyle the line style for this object
   * @param x1 the virtual X coordinate of the first corner
   * @param y1 the virtual Y coordinate of the first corner
   * @param x2 the virtual X coordinate of the second corner
   * @param y2 the virtual Y coordinate of the second corner
   */
  public EllipseFigure(Color foregroundColor, Color backgroundColor, int lineStyle, int x1, int y1, int x2, int y2) {
    this.foregroundColor = foregroundColor;
    this.backgroundColor = backgroundColor;
    this.lineStyle = lineStyle;
    this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2;
  }
  public void draw(FigureDrawContext fdc) {
    Rectangle r = fdc.toClientRectangle(x1, y1, x2, y2);
    fdc.gc.setForeground(foregroundColor);
    fdc.gc.setBackground(backgroundColor);
    fdc.gc.setLineStyle(lineStyle);
    fdc.gc.drawOval(r.x, r.y, r.width - 1, r.height - 1);
    fdc.gc.setLineStyle(SWT.LINE_SOLID);
  }
  public void addDamagedRegion(FigureDrawContext fdc, Region region) {
    region.add(fdc.toClientRectangle(x1, y1, x2, y2));
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/
/**
 * The superclass for paint tools that use click-drag-release motions to
 * draw objects.
 */
abstract class DragPaintSession extends BasicPaintSession {
  /**
   * True if a click-drag is in progress
   */
  private boolean dragInProgress = false;
  
  /**
   * The position of the first click in a click-drag
   */
  private Point anchorPosition = new Point(-1, -1);

  /**
   * A temporary point
   */
  private Point tempPosition = new Point(-1, -1);
  
  /**
   * Constructs a PaintSession.
   * 
   * @param getPaintSurface() the drawing surface to use
   */
  protected DragPaintSession(PaintSurface paintSurface) {
    super(paintSurface);
  }

  /**
   * Activates the tool.
   */
  public void beginSession() {
    getPaintSurface().
      setStatusMessage(PaintExample.getResourceString("session.DragInteractivePaint.message"));
    anchorPosition.x = -1;
    dragInProgress = false;
  }
  
  /**
   * Deactivates the tool.
     */
  public void endSession() {
  }
  
  /**
   * Resets the tool.
   * Aborts any operation in progress.
   */
  public void resetSession() {
    getPaintSurface().clearRubberbandSelection();
    anchorPosition.x = -1;
    dragInProgress = false;
  }

  /**
   * Handles a mouseDown event.
   * 
   * @param event the mouse event detail information
   */
  public void mouseDown(MouseEvent event) {
    if (event.button != 1) return;
    if (dragInProgress) return; // spurious event
    dragInProgress = true;
    
    anchorPosition.x = event.x;
    anchorPosition.y = event.y;
  }

  /**
   * Handles a mouseDoubleClick event.
   * 
   * @param event the mouse event detail information
   */
  public void mouseDoubleClick(MouseEvent event) {
  }

  /**
   * Handles a mouseUp event.
   * 
   * @param event the mouse event detail information
   */
  public void mouseUp(MouseEvent event) {
    if (event.button != 1) {
      resetSession(); // abort if right or middle mouse button pressed
      return;
    }
    if (! dragInProgress) return; // spurious event
    dragInProgress = false;
    if (anchorPosition.x == -1) return; // spurious event
    
    getPaintSurface().commitRubberbandSelection();
  }
  
  /**
   * Handles a mouseMove event.
   * 
   * @param event the mouse event detail information
   */
  public void mouseMove(MouseEvent event) {
    final PaintSurface ps = getPaintSurface();
    if (! dragInProgress) {
      ps.setStatusCoord(ps.getCurrentPosition());
      return;
    }
    ps.setStatusCoordRange(anchorPosition, ps.getCurrentPosition());
    ps.clearRubberbandSelection();
    tempPosition.x = event.x;
    tempPosition.y = event.y;
    ps.addRubberbandSelection(createFigure(anchorPosition, tempPosition));
  }
  
  /**
   * Template Method: Creates a Figure for drawing rubberband entities and the final product
   * 
   * @param anchor the anchor point
   * @param cursor the point marking the current pointer location
   */
  protected abstract Figure createFigure(Point anchor, Point cursor);
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/

/**
 * The superclass for paint tools that draw continuously along the path
 * traced by the mouse's movement while the button is depressed
 */
abstract class ContinuousPaintSession extends BasicPaintSession {
  /**
   * True if a click-drag is in progress.
   */
  private boolean dragInProgress = false;
  
  /**
   * A cached Point array for drawing.
   */
  private Point[] points = new Point[] { new Point(-1, -1), new Point(-1, -1) };

  /**
   * The time to wait between retriggers in milliseconds.
   */
  private int retriggerInterval = 0;
  
  /**
   * The currently valid RetriggerHandler
   */
  protected Runnable retriggerHandler = null;

  /**
   * Constructs a ContinuousPaintSession.
   * 
   * @param paintSurface the drawing surface to use
   */
  protected ContinuousPaintSession(PaintSurface paintSurface) {
    super(paintSurface);
  }

  /**
   * Sets the retrigger timer.
   * <p>
   * After the timer elapses, if the mouse is still hovering over the same point with the
   * drag button pressed, a new render order is issued and the timer is restarted.
   * </p>
   * @param interval the time in milliseconds to wait between retriggers, 0 to disable
   */
  public void setRetriggerTimer(int interval) {
    retriggerInterval = interval;
  }

  /**
   * Activates the tool.
   */
  public void beginSession() {
    getPaintSurface().
      setStatusMessage(PaintExample.getResourceString("session.ContinuousPaint.message"));
    dragInProgress = false;
  }
  
  /**
   * Deactivates the tool.
     */
  public void endSession() {
    abortRetrigger();
  }
  
  /**
   * Aborts the current operation.
   */
  public void resetSession() {
    abortRetrigger();
  }

  /**
   * Handles a mouseDown event.
   * 
   * @param event the mouse event detail information
   */
  public final void mouseDown(MouseEvent event) {
    if (event.button != 1) return;
    if (dragInProgress) return; // spurious event
    dragInProgress = true;

    points[0].x = event.x;
    points[0].y = event.y;
    render(points[0]);
    prepareRetrigger();
  }

  /**
   * Handles a mouseDoubleClick event.
   * 
   * @param event the mouse event detail information
   */
  public final void mouseDoubleClick(MouseEvent event) {
  }

  /**
   * Handles a mouseUp event.
   * 
   * @param event the mouse event detail information
   */
  public final void mouseUp(MouseEvent event) {
    if (event.button != 1) return;
    if (! dragInProgress) return; // spurious event
    abortRetrigger();
    mouseSegmentFinished(event);
    dragInProgress = false;
  }
  
  /**
   * Handles a mouseMove event.
   * 
   * @param event the mouse event detail information
   */
  public final void mouseMove(MouseEvent event) {
    final PaintSurface ps = getPaintSurface();
    ps.setStatusCoord(ps.getCurrentPosition());
    if (! dragInProgress) return;
    mouseSegmentFinished(event);
    prepareRetrigger();
  }
  
  /**
   * Handle a rendering segment
   * 
   * @param event the mouse event detail information
   */
  private final void mouseSegmentFinished(MouseEvent event) {
    if (points[0].x == -1) return; // spurious event
    if (points[0].x != event.x || points[0].y != event.y) {
      // draw new segment
      points[1].x = event.x;
      points[1].y = event.y;
      renderContinuousSegment();
    }
  }

  /**
   * Draws a continuous segment from points[0] to points[1].
   * Assumes points[0] has been drawn already.
   * 
   * @post points[0] will refer to the same point as points[1]
   */
  protected void renderContinuousSegment() {
    /* A lazy but effective line drawing algorithm */
    final int dX = points[1].x - points[0].x;
    final int dY = points[1].y - points[0].y;
    int absdX = Math.abs(dX);
    int absdY = Math.abs(dY);

    if ((dX == 0) && (dY == 0)) return;
    
    if (absdY > absdX) {
      final int incfpX = (dX << 16) / absdY;
      final int incY = (dY > 0) ? 1 : -1;
      int fpX = points[0].x << 16; // X in fixedpoint format

      while (--absdY >= 0) {
        points[0].y += incY;
        points[0].x = (fpX += incfpX) >> 16;
        render(points[0]);
      }
      if (points[0].x == points[1].x) return;
      points[0].x = points[1].x;
    } else {
      final int incfpY = (dY << 16) / absdX;
      final int incX = (dX > 0) ? 1 : -1;
      int fpY = points[0].y << 16; // Y in fixedpoint format

      while (--absdX >= 0) {
        points[0].x += incX;
        points[0].y = (fpY += incfpY) >> 16;
        render(points[0]);
      }
      if (points[0].y == points[1].y) return;
      points[0].y = points[1].y;
    }
    render(points[0]);
  }    

  /**
   * Prepare the retrigger timer
   */
  private final void prepareRetrigger() {
    if (retriggerInterval > 0) {
      /*
       * timerExec() provides a lightweight mechanism for running code at intervals from within
       * the event loop when timing accuracy is not important.
       *
       * Since it is not possible to cancel a timerExec(), we remember the Runnable that is
       * active in order to distinguish the valid one from the stale ones.  In practice,
       * if the interval is 1/100th of a second, then creating a few hundred new RetriggerHandlers
       * each second will not cause a significant performance hit.
       */
      Display display = getPaintSurface().getDisplay();
      retriggerHandler = new Runnable() {
        public void run() {
          if (retriggerHandler == this) {
            render(points[0]);
            prepareRetrigger();
          }
        }
      };      
      display.timerExec(retriggerInterval, retriggerHandler);
    }
  }

  /**
   * Aborts the retrigger timer
   */
  private final void abortRetrigger() {
    retriggerHandler = null;
  }
  
  /**
   * Template method: Renders a point.
   * @param point, the point to render
   */
  protected abstract void render(Point point);
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/
/**
 * Container for Figure objects with stacking preview mechanism.
 */
class ContainerFigure extends Figure {
  private static final int INITIAL_ARRAY_SIZE = 16;
  
  Figure[]   objectStack = null;
  int      nextIndex = 0;

  /**
   * Constructs an empty Container
   */
  public ContainerFigure() {
  }
  /**
   * Adds an object to the container for later drawing.
   * 
   * @param object the object to add to the drawing list
   */
  public void add(Figure object) {
    if (objectStack == null) {
      objectStack = new Figure[INITIAL_ARRAY_SIZE];
    } else if (objectStack.length <= nextIndex) {
      Figure[] newObjectStack = new Figure[objectStack.length * 2];
      System.arraycopy(objectStack, 0, newObjectStack, 0, objectStack.length);
      objectStack = newObjectStack;
    }
    objectStack[nextIndex] = object;
    ++nextIndex;
  }
  /**
   * Determines if the container is empty.
   * @return true if the container is empty
   */
  public boolean isEmpty() {
    return nextIndex == 0;
  }
  /**
   * Adds an object to the container and draws its preview then updates the supplied preview state.
   * 
   * @param object the object to add to the drawing list
   * @param gc the GC to draw on
   * @param offset the offset to add to virtual coordinates to get display coordinates
   * @param rememberedState the state returned by a previous drawPreview() or addAndPreview()
   *        using this Container, may be null if there was no such previous call
   * @return object state that must be passed to erasePreview() later to erase this object
   */
//  public Object addAndPreview(Figure object, GC gc, Point offset, Object rememberedState) {
//    Object[] stateStack = (Object[]) rememberedState;
//    if (stateStack == null) {
//      stateStack = new Object[INITIAL_ARRAY_SIZE];
//    } else if (stateStack.length <= nextIndex) {
//      Object[] newStateStack = new Object[stateStack.length * 2];
//      System.arraycopy(stateStack, 0, newStateStack, 0, stateStack.length);
//      stateStack = newStateStack;
//    }
//    add(object);
//    stateStack[nextIndex - 1] = object.drawPreview(gc, offset);
//    return stateStack;
//  }
  /**
   * Clears the container.
   * <p>
   * Note that erasePreview() cannot be called after this point to erase any previous
   * drawPreview()'s.
   * </p>
   */
  public void clear() {
    while (--nextIndex > 0) objectStack[nextIndex] = null;
    nextIndex = 0;
  }
  public void draw(FigureDrawContext fdc) {
    for (int i = 0; i < nextIndex; ++i) objectStack[i].draw(fdc);
  }
  public void addDamagedRegion(FigureDrawContext fdc, Region region) {
    for (int i = 0; i < nextIndex; ++i) objectStack[i].addDamagedRegion(fdc, region);
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/
abstract class BasicPaintSession implements PaintSession {
  /**
   * The paint surface
   */
  private PaintSurface paintSurface;

  /**
   * Constructs a PaintSession.
   * 
   * @param paintSurface the drawing surface to use
   */
  protected BasicPaintSession(PaintSurface paintSurface) {
    this.paintSurface = paintSurface;
  }

  /**
   * Returns the paint surface associated with this paint session.
   * 
   * @return the associated PaintSurface
   */
  public PaintSurface getPaintSurface() {
    return paintSurface;
  }
}


/*******************************************************************************
 * Copyright (c) 2000, 2005 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
 *******************************************************************************/

/**
 * An airbrush tool.
 */
class AirbrushTool extends ContinuousPaintSession implements PaintTool {
  private ToolSettings settings;
  private Random random;
  private int cachedRadiusSquared;
  private int cachedNumPoints;
  
  /**
   * Constructs a Tool.
   * 
   * @param toolSettings the new tool settings
   * @param paintSurface the PaintSurface we will render on.
   */
  public AirbrushTool(ToolSettings toolSettings, PaintSurface paintSurface) {
    super(paintSurface);
    random = new Random();
    setRetriggerTimer(10);
    set(toolSettings);
  }
  
  /**
   * Sets the tool's settings.
   * 
   * @param toolSettings the new tool settings
   */
  public void set(ToolSettings toolSettings) {
    // compute things we need to know for drawing
    settings = toolSettings;
    cachedRadiusSquared = settings.airbrushRadius * settings.airbrushRadius;
    cachedNumPoints = 314 * settings.airbrushIntensity * cachedRadiusSquared / 250000;
    if (cachedNumPoints == 0 && settings.airbrushIntensity != 0)
      cachedNumPoints = 1;
  }

  /**
   * Returns the name associated with this tool.
   * 
   * @return the localized name of this tool
   */
  public String getDisplayName() {
    return PaintExample.getResourceString("tool.Airbrush.label");
  }

  /*
   * Template method for drawing
   */
  protected void render(Point point) {
    // Draws a bunch (cachedNumPoints) of random pixels within a specified circle (cachedRadiusSquared).
    ContainerFigure cfig = new ContainerFigure();

    for (int i = 0; i < cachedNumPoints; ++i) {
      int randX, randY;
      do {
        randX = (int) ((random.nextDouble() - 0.5) * settings.airbrushRadius * 2.0);
        randY = (int) ((random.nextDouble() - 0.5) * settings.airbrushRadius * 2.0);
      } while (randX * randX + randY * randY > cachedRadiusSquared);
      cfig.add(new PointFigure(settings.commonForegroundColor, point.x + randX, point.y + randY));
    }
    getPaintSurface().drawFigure(cfig);
  }
}


           
         
  








Related examples in the same category

1.SWT 2D Chart: FlowchartSWT 2D Chart: Flowchart
2.Use of Java2D on SWT or Draw2D graphical contextUse of Java2D on SWT or Draw2D graphical context
3.SWT 2D UnicodeSWT 2D Unicode
4.SWT 2D Simple DemoSWT 2D Simple Demo
5.SWT Draw 2DSWT Draw 2D
6.SWT Draw2D Example
7.XORXOR
8.Animations ExampleAnimations Example
9.Alpha Fade InAlpha Fade In
10.DrawingsDrawings
11.Draw Text DemoDraw Text Demo
12.GC Creation
13.PalettesPalettes
14.TransparencyTransparency
15.Draw2D SampleDraw2D Sample
16.Class AnalyzerClass Analyzer
17.Demonstrates animation. It uses double buffering.Demonstrates animation. It uses double buffering.
18.Demonstrates drawing an ArcDemonstrates drawing an Arc
19.Demonstrates drawing polygonsDemonstrates drawing polygons
20.Demonstrates drawing points. It draws a sine waveDemonstrates drawing points. It draws a sine wave
21.Draw Round RectangleDraw Round Rectangle
22.Displays information about the display deviceDisplays information about the display device
23.Demonstrates the effects of the flags on the constructorDemonstrates the effects of the flags on the constructor
24.Demonstrates how to draw vertical textDemonstrates how to draw vertical text
25.Utility methods for drawing graphics
26.Demonstrates drawing linesDemonstrates drawing lines
27.Demonstrates animationDemonstrates animation
28.Demonstrates how to draw textDemonstrates how to draw text
29.Demonstrates drawing ovalsDemonstrates drawing ovals
30.Demonstrates how to draw text in colorsDemonstrates how to draw text in colors
31.SWT Graphics ExampleSWT Graphics Example
32.SWT OpenGL snippet: draw a square
33.Drawing with transformations, paths and alpha blendingDrawing with transformations, paths and alpha blending
34.Draw lines and polygons with different cap and join stylesDraw lines and polygons with different cap and join styles
35.GC: implement a simple scribble programGC: implement a simple scribble program
36.GC: measure a stringGC: measure a string
37.GC example: draw a thick lineGC example: draw a thick line
38.How to draw directly on an SWT ControlHow to draw directly on an SWT Control
39.SWT useful utilities