DrawApplet.java
/*
* @(#)DrawApplet.java
*
* Project: JHotdraw - a GUI framework for technical drawings
* http://www.jhotdraw.org
* http://jhotdraw.sourceforge.net
* Copyright: © by the original author(s) and all contributors
* License: Lesser GNU Public License (LGPL)
* http://www.opensource.org/licenses/lgpl-license.html
*/
package CH.ifa.draw.applet;
import javax.swing.*;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import java.net.*;
import CH.ifa.draw.framework.*;
import CH.ifa.draw.standard.*;
import CH.ifa.draw.figures.*;
import CH.ifa.draw.util.*;
/**
* DrawApplication defines a standard presentation for
* a drawing editor that is run as an applet. The presentation is
* customized in subclasses.<p>
* Supported applet parameters: <br>
* <i>DRAWINGS</i>: a blank separated list of drawing names that is
* shown in the drawings choice.
*
* @version <$CURRENT_VERSION$>
*/
public class DrawApplet
extends JApplet
implements DrawingEditor, PaletteListener, VersionRequester {
private transient Drawing fDrawing;
private transient Tool fTool;
private transient DrawingView fView;
private transient ToolButton fDefaultToolButton;
private transient ToolButton fSelectedToolButton;
private transient boolean fSimpleUpdate;
private transient JButton fUpdateButton;
private transient JComboBox fFrameColor;
private transient JComboBox fFillColor;
private transient JComboBox fTextColor;
private transient JComboBox fArrowChoice;
private transient JComboBox fFontChoice;
private transient Thread fSleeper;
private Iconkit fIconkit;
private transient UndoManager myUndoManager;
static String fgUntitled = "untitled";
private static final String fgDrawPath = "/CH/ifa/draw/";
public static final String IMAGES = fgDrawPath+"images/";
/**
* Initializes the applet and creates its contents.
*/
public void init() {
getVersionControlStrategy().assertCompatibleVersion();
setUndoManager(new UndoManager());
fIconkit = new Iconkit(this);
getContentPane().setLayout(new BorderLayout());
fView = createDrawingView();
JPanel attributes = createAttributesPanel();
createAttributeChoices(attributes);
getContentPane().add("North", attributes);
JPanel toolPanel = createToolPalette();
createTools(toolPanel);
getContentPane().add("West", toolPanel);
getContentPane().add("Center", (Component)view());
JPanel buttonPalette = createButtonPanel();
createButtons(buttonPalette);
getContentPane().add("South", buttonPalette);
initDrawing();
// JFC should have its own internal double buffering...
//setBufferedDisplayUpdate();
setupAttributes();
}
public void addViewChangeListener(ViewChangeListener vsl) {
}
public void removeViewChangeListener(ViewChangeListener vsl) {
}
/*
* Gets the iconkit to be used in the applet.
*/
/**** not sure whether this will still be needed on 1.1 enabled browsers
protected Iconkit iconkit() {
if (fIconkit == null) {
startSleeper();
loadAllImages(this); // blocks until images loaded
stopSleeper();
}
return fIconkit;
} */
/**
* Creates the attributes panel.
*/
protected JPanel createAttributesPanel() {
JPanel panel = new JPanel();
panel.setLayout(new PaletteLayout(2, new Point(2,2), false));
return panel;
}
/**
* Creates the attribute choices. Override to add additional
* choices.
*/
protected void createAttributeChoices(JPanel panel) {
panel.add(new JLabel("Fill"));
fFillColor = createColorChoice("FillColor");
panel.add(fFillColor);
panel.add(new JLabel("Text"));
fTextColor = createColorChoice("TextColor");
panel.add(fTextColor);
panel.add(new JLabel("Pen"));
fFrameColor = createColorChoice("FrameColor");
panel.add(fFrameColor);
panel.add(new JLabel("Arrow"));
CommandChoice choice = new CommandChoice();
fArrowChoice = choice;
choice.addItem(new ChangeAttributeCommand("none", "ArrowMode", new Integer(PolyLineFigure.ARROW_TIP_NONE), this));
choice.addItem(new ChangeAttributeCommand("at Start", "ArrowMode", new Integer(PolyLineFigure.ARROW_TIP_START), this));
choice.addItem(new ChangeAttributeCommand("at End", "ArrowMode", new Integer(PolyLineFigure.ARROW_TIP_END), this));
choice.addItem(new ChangeAttributeCommand("at Both", "ArrowMode", new Integer(PolyLineFigure.ARROW_TIP_BOTH), this));
panel.add(fArrowChoice);
panel.add(new JLabel("Font"));
fFontChoice = createFontChoice();
panel.add(fFontChoice);
}
/**
* Creates the color choice for the given attribute.
*/
protected JComboBox createColorChoice(String attribute) {
CommandChoice choice = new CommandChoice();
for (int i = 0; i < ColorMap.size(); i++)
choice.addItem(
new ChangeAttributeCommand(
ColorMap.name(i),
attribute,
ColorMap.color(i),
this
)
);
return choice;
}
/**
* Creates the font choice. The choice is filled with
* all the fonts supported by the toolkit.
*/
protected JComboBox createFontChoice() {
CommandChoice choice = new CommandChoice();
String fonts[] = Toolkit.getDefaultToolkit().getFontList();
for (int i = 0; i < fonts.length; i++) {
choice.addItem(new ChangeAttributeCommand(fonts[i], "FontName", fonts[i], this));
}
return choice;
}
/**
* Creates the buttons panel.
*/
protected JPanel createButtonPanel() {
JPanel panel = new JPanel();
panel.setLayout(new PaletteLayout(2, new Point(2,2), false));
return panel;
}
/**
* Creates the buttons shown in the buttons panel. Override to
* add additional buttons.
* @param panel the buttons panel.
*/
protected void createButtons(JPanel panel) {
panel.add(new Filler(24,20));
JComboBox drawingChoice = new JComboBox();
drawingChoice.addItem(fgUntitled);
String param = getParameter("DRAWINGS");
if (param == null) {
param = "";
}
StringTokenizer st = new StringTokenizer(param);
while (st.hasMoreTokens()) {
drawingChoice.addItem(st.nextToken());
}
// offer choice only if more than one
if (drawingChoice.getItemCount() > 1) {
panel.add(drawingChoice);
}
else {
panel.add(new JLabel(fgUntitled));
}
drawingChoice.addItemListener(
new ItemListener() {
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
loadDrawing((String)e.getItem());
}
}
}
);
panel.add(new Filler(6,20));
JButton button;
button = new CommandButton(new DeleteCommand("Delete", this));
panel.add(button);
button = new CommandButton(new DuplicateCommand("Duplicate", this));
panel.add(button);
button = new CommandButton(new GroupCommand("Group", this));
panel.add(button);
button = new CommandButton(new UngroupCommand("Ungroup", this));
panel.add(button);
button = new JButton("Help");
button.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent event) {
showHelp();
}
}
);
panel.add(button);
fUpdateButton = new JButton("Simple Update");
fUpdateButton.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent event) {
if (fSimpleUpdate) {
setBufferedDisplayUpdate();
}
else {
setSimpleDisplayUpdate();
}
}
}
);
// panel.add(fUpdateButton); // not shown currently
}
/**
* Creates the tools palette.
*/
protected JPanel createToolPalette() {
JPanel palette = new JPanel();
palette.setLayout(new PaletteLayout(2,new Point(2,2)));
return palette;
}
/**
* Creates the tools. By default only the selection tool is added.
* Override this method to add additional tools.
* Call the inherited method to include the selection tool.
* @param palette the palette where the tools are added.
*/
protected void createTools(JPanel palette) {
Tool tool = createSelectionTool();
fDefaultToolButton = createToolButton(IMAGES + "SEL", "Selection Tool", tool);
palette.add(fDefaultToolButton);
}
/**
* Creates the selection tool used in this editor. Override to use
* a custom selection tool.
*/
protected Tool createSelectionTool() {
return new SelectionTool(this);
}
/**
* Creates a tool button with the given image, tool, and text
*/
protected ToolButton createToolButton(String iconName, String toolName, Tool tool) {
return new ToolButton(this, iconName, toolName, tool);
}
/**
* Creates the drawing used in this application.
* You need to override this method to use a Drawing
* subclass in your application. By default a standard
* Drawing is returned.
*/
protected Drawing createDrawing() {
return new StandardDrawing();
}
/**
* Creates the drawing view used in this application.
* You need to override this method to use a DrawingView
* subclass in your application. By default a standard
* DrawingView is returned.
*/
protected DrawingView createDrawingView() {
return new StandardDrawingView(this, 410, 370);
}
/**
* Handles a user selection in the palette.
* @see PaletteListener
*/
public void paletteUserSelected(PaletteButton button) {
ToolButton toolButton = (ToolButton) button;
setTool(toolButton.tool(), toolButton.name());
setSelected(toolButton);
}
/**
* Handles when the mouse enters or leaves a palette button.
* @see PaletteListener
*/
public void paletteUserOver(PaletteButton button, boolean inside) {
if (inside) {
showStatus(((ToolButton) button).name());
}
else {
showStatus(fSelectedToolButton.name());
}
}
/**
* Gets the current drawing.
* @see DrawingEditor
*/
public Drawing drawing() {
return fDrawing;
}
/**
* Gets the current tool.
* @see DrawingEditor
*/
public Tool tool() {
return fTool;
}
/**
* Gets the current drawing view.
* @see DrawingEditor
*/
public DrawingView view() {
return fView;
}
public DrawingView[] views() {
return new DrawingView[] { view() } ;
}
/**
* Sets the default tool of the editor.
* @see DrawingEditor
*/
public void toolDone() {
setTool(fDefaultToolButton.tool(), fDefaultToolButton.name());
setSelected(fDefaultToolButton);
}
/**
* Handles a change of the current selection. Updates all
* menu items that are selection sensitive.
* @see DrawingEditor
*/
public void figureSelectionChanged(DrawingView view) {
setupAttributes();
}
public void viewSelectionChanged(DrawingView oldView, DrawingView newView) {
}
private void initDrawing() {
fDrawing = createDrawing();
view().setDrawing(fDrawing);
toolDone();
}
private void setTool(Tool t, String name) {
if (fTool != null) {
fTool.deactivate();
}
fTool = t;
if (fTool != null) {
showStatus(name);
fTool.activate();
}
}
private void setSelected(ToolButton button) {
if (fSelectedToolButton != null) {
fSelectedToolButton.reset();
}
fSelectedToolButton = button;
if (fSelectedToolButton != null) {
fSelectedToolButton.select();
}
}
protected void loadDrawing(String param) {
if (param == fgUntitled) {
fDrawing.release();
initDrawing();
return;
}
String filename = getParameter(param);
if (filename != null) {
readDrawing(filename);
}
}
private void readDrawing(String filename) {
toolDone();
String type = guessType(filename);
if (type.equals("storable")) {
readFromStorableInput(filename);
}
else if (type.equals("serialized")) {
readFromObjectInput(filename);
}
else {
showStatus("Unknown file type");
}
}
private void readFromStorableInput(String filename) {
try {
URL url = new URL(getCodeBase(), filename);
InputStream stream = url.openStream();
StorableInput input = new StorableInput(stream);
fDrawing.release();
fDrawing = (Drawing)input.readStorable();
view().setDrawing(fDrawing);
}
catch (IOException e) {
initDrawing();
showStatus("Error:" + e);
}
}
private void readFromObjectInput(String filename) {
try {
URL url = new URL(getCodeBase(), filename);
InputStream stream = url.openStream();
ObjectInput input = new ObjectInputStream(stream);
fDrawing.release();
fDrawing = (Drawing)input.readObject();
view().setDrawing(fDrawing);
}
catch (IOException e) {
initDrawing();
showStatus("Error: " + e);
}
catch (ClassNotFoundException e) {
initDrawing();
showStatus("Class not found: " + e);
}
}
private String guessType(String file) {
if (file.endsWith(".draw")) {
return "storable";
}
if (file.endsWith(".ser")) {
return "serialized";
}
return "unknown";
}
private void setupAttributes() {
Color frameColor = (Color) AttributeFigure.getDefaultAttribute("FrameColor");
Color fillColor = (Color) AttributeFigure.getDefaultAttribute("FillColor");
Color textColor = (Color) AttributeFigure.getDefaultAttribute("TextColor");
Integer arrowMode = (Integer) AttributeFigure.getDefaultAttribute("ArrowMode");
String fontName = (String) AttributeFigure.getDefaultAttribute("FontName");
FigureEnumeration k = view().selectionElements();
while (k.hasMoreElements()) {
Figure f = k.nextFigure();
frameColor = (Color) f.getAttribute("FrameColor");
fillColor = (Color) f.getAttribute("FillColor");
textColor = (Color) f.getAttribute("TextColor");
arrowMode = (Integer) f.getAttribute("ArrowMode");
fontName = (String) f.getAttribute("FontName");
}
fFrameColor.setSelectedIndex(ColorMap.colorIndex(frameColor));
fFillColor.setSelectedIndex(ColorMap.colorIndex(fillColor));
//fTextColor.select(ColorMap.colorIndex(textColor));
if (arrowMode != null) {
fArrowChoice.setSelectedIndex(arrowMode.intValue());
}
if (fontName != null) {
fFontChoice.setSelectedItem(fontName);
}
}
protected void setSimpleDisplayUpdate() {
view().setDisplayUpdate(new SimpleUpdateStrategy());
fUpdateButton.setText("Simple Update");
fSimpleUpdate = true;
}
protected void setBufferedDisplayUpdate() {
view().setDisplayUpdate(new BufferedUpdateStrategy());
fUpdateButton.setText("Buffered Update");
fSimpleUpdate = false;
}
/**
* Shows a help page for the applet. The URL of the help
* page is derived as follows: codeBase+appletClassname+Help.html"
*/
protected void showHelp() {
try {
String appletPath = getClass().getName().replace('.', '/');
URL url = new URL(getCodeBase(), appletPath + "Help.html");
getAppletContext().showDocument(url, "Help");
}
catch (IOException e) {
showStatus("Help file not found");
}
}
protected void setUndoManager(UndoManager newUndoManager) {
myUndoManager = newUndoManager;
}
public UndoManager getUndoManager() {
return myUndoManager;
}
protected VersionControlStrategy getVersionControlStrategy() {
return new StandardVersionControlStrategy(this);
}
/**
* Subclasses should override this method to specify to which versions of
* JHotDraw they are compatible. A string array is returned so it is possible
* to specify several version numbers of JHotDraw to which the application
* is compatible with.
*
* @return all versions number of JHotDraw the application is compatible with.
*/
public String[] getRequiredVersions() {
String[] requiredVersions = new String[1];
// return the version of the package we are in
requiredVersions[0] = VersionManagement.getPackageVersion(DrawApplet.class.getPackage());
return requiredVersions;
}
/**
* *** netscape browser work around ***
*/
private void startSleeper() {
if (fSleeper == null) {
fSleeper = new SleeperThread(this);
}
fSleeper.start();
}
private void stopSleeper() {
if (fSleeper != null) {
fSleeper.stop();
}
}
}
class SleeperThread extends Thread {
JApplet fApplet;
SleeperThread(JApplet applet) {
fApplet = applet;
}
public void run() {
try {
for (;;) {
fApplet.showStatus("loading icons...");
sleep(50);
}
}
catch (InterruptedException e) {
return;
}
}
}