Ptidej-v5.2-master/PADL Serialiser DB4O Tests/rsc/JHotDraw v5.1/src/CH/ifa/draw/application/DrawApplication.java
/*
 * @(#)DrawApplication.java 5.1
 *
 */

package CH.ifa.draw.application;

import java.awt.Adjustable;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FileDialog;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.MenuShortcut;
import java.awt.Panel;
import java.awt.Point;
import java.awt.PrintJob;
import java.awt.ScrollPane;
import java.awt.TextField;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;

import CH.ifa.draw.figures.GroupCommand;
import CH.ifa.draw.figures.PolyLineFigure;
import CH.ifa.draw.figures.UngroupCommand;
import CH.ifa.draw.framework.Drawing;
import CH.ifa.draw.framework.DrawingEditor;
import CH.ifa.draw.framework.DrawingView;
import CH.ifa.draw.framework.Tool;
import CH.ifa.draw.standard.AlignCommand;
import CH.ifa.draw.standard.BringToFrontCommand;
import CH.ifa.draw.standard.BufferedUpdateStrategy;
import CH.ifa.draw.standard.ChangeAttributeCommand;
import CH.ifa.draw.standard.CopyCommand;
import CH.ifa.draw.standard.CutCommand;
import CH.ifa.draw.standard.DeleteCommand;
import CH.ifa.draw.standard.DuplicateCommand;
import CH.ifa.draw.standard.PasteCommand;
import CH.ifa.draw.standard.SelectionTool;
import CH.ifa.draw.standard.SendToBackCommand;
import CH.ifa.draw.standard.SimpleUpdateStrategy;
import CH.ifa.draw.standard.StandardDrawing;
import CH.ifa.draw.standard.StandardDrawingView;
import CH.ifa.draw.standard.ToggleGridCommand;
import CH.ifa.draw.standard.ToolButton;
import CH.ifa.draw.util.ColorMap;
import CH.ifa.draw.util.CommandMenu;
import CH.ifa.draw.util.Iconkit;
import CH.ifa.draw.util.PaletteButton;
import CH.ifa.draw.util.PaletteLayout;
import CH.ifa.draw.util.PaletteListener;
import CH.ifa.draw.util.StorableInput;
import CH.ifa.draw.util.StorableOutput;

/**
 * DrawApplication defines a standard presentation for standalone drawing
 * editors. The presentation is customized in subclasses. The application is
 * started as follows:
 * 
 * <pre>
 * public static void main(String[] args) {
 * 	MayDrawApp window = new MyDrawApp();
 * 	window.open();
 * }
 * </pre>
 */

public class DrawApplication extends Frame implements DrawingEditor,
		PaletteListener {

	private Drawing fDrawing;
	private Tool fTool;
	private Iconkit fIconkit;

	private TextField fStatusLine;
	private StandardDrawingView fView;
	private ToolButton fDefaultToolButton;
	private ToolButton fSelectedToolButton;

	private String fDrawingFilename;
	static String fgUntitled = "untitled";

	// the image resource path
	private static final String fgDrawPath = "/CH/ifa/draw/";
	public static final String IMAGES = fgDrawPath + "images/";

	/**
	 * The index of the file menu in the menu bar.
	 */
	public static final int FILE_MENU = 0;
	/**
	 * The index of the edit menu in the menu bar.
	 */
	public static final int EDIT_MENU = 1;
	/**
	 * The index of the alignment menu in the menu bar.
	 */
	public static final int ALIGNMENT_MENU = 2;
	/**
	 * The index of the attributes menu in the menu bar.
	 */
	public static final int ATTRIBUTES_MENU = 3;

	/**
	 * Constructs a drawing window with a default title.
	 */
	public DrawApplication() {
		super("JHotDraw");
	}

	/**
	 * Constructs a drawing window with the given title.
	 */
	public DrawApplication(String title) {
		super(title);
	}

	/**
	 * Opens the window and initializes its contents. Clients usually only call
	 * but don't override it.
	 */

	public void open() {
		fIconkit = new Iconkit(this);
		setLayout(new BorderLayout());

		fView = createDrawingView();
		Component contents = createContents(fView);
		add("Center", contents);
		// add("Center", fView);

		Panel tools = createToolPalette();
		createTools(tools);
		add("West", tools);

		fStatusLine = createStatusLine();
		add("South", fStatusLine);

		MenuBar mb = new MenuBar();
		createMenus(mb);
		setMenuBar(mb);

		initDrawing();
		Dimension d = defaultSize();
		setSize(d.width, d.height);

		addListeners();

		show();
	}

	/**
	 * Registers the listeners for this window
	 */
	protected void addListeners() {
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent event) {
				exit();
			}
		});
	}

	private void initDrawing() {
		fDrawing = createDrawing();
		fDrawingFilename = fgUntitled;
		fView.setDrawing(fDrawing);
		toolDone();
	}

	/**
	 * Creates the standard menus. Clients override this method to add
	 * additional menus.
	 */
	protected void createMenus(MenuBar mb) {
		mb.add(createFileMenu());
		mb.add(createEditMenu());
		mb.add(createAlignmentMenu());
		mb.add(createAttributesMenu());
		mb.add(createDebugMenu());
	}

	/**
	 * Creates the file menu. Clients override this method to add additional
	 * menu items.
	 */
	protected Menu createFileMenu() {
		Menu menu = new Menu("File");
		MenuItem mi = new MenuItem("New", new MenuShortcut('n'));
		mi.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				promptNew();
			}
		});
		menu.add(mi);

		mi = new MenuItem("Open...", new MenuShortcut('o'));
		mi.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				promptOpen();
			}
		});
		menu.add(mi);

		mi = new MenuItem("Save As...", new MenuShortcut('s'));
		mi.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				promptSaveAs();
			}
		});
		menu.add(mi);

		mi = new MenuItem("Save As Serialized...");
		mi.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				promptSaveAsSerialized();
			}
		});
		menu.add(mi);
		menu.addSeparator();
		mi = new MenuItem("Print...", new MenuShortcut('p'));
		mi.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				print();
			}
		});
		menu.add(mi);
		menu.addSeparator();
		mi = new MenuItem("Exit");
		mi.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				exit();
			}
		});
		menu.add(mi);
		return menu;
	}

	/**
	 * Creates the edit menu. Clients override this method to add additional
	 * menu items.
	 */
	protected Menu createEditMenu() {
		CommandMenu menu = new CommandMenu("Edit");
		menu.add(new CutCommand("Cut", fView), new MenuShortcut('x'));
		menu.add(new CopyCommand("Copy", fView), new MenuShortcut('c'));
		menu.add(new PasteCommand("Paste", fView), new MenuShortcut('v'));
		menu.addSeparator();
		menu.add(new DuplicateCommand("Duplicate", fView),
				new MenuShortcut('d'));
		menu.add(new DeleteCommand("Delete", fView));
		menu.addSeparator();
		menu.add(new GroupCommand("Group", fView));
		menu.add(new UngroupCommand("Ungroup", fView));
		menu.addSeparator();
		menu.add(new SendToBackCommand("Send to Back", fView));
		menu.add(new BringToFrontCommand("Bring to Front", fView));
		return menu;
	}

	/**
	 * Creates the alignment menu. Clients override this method to add
	 * additional menu items.
	 */
	protected Menu createAlignmentMenu() {
		CommandMenu menu = new CommandMenu("Align");
		menu.add(new ToggleGridCommand("Toggle Snap to Grid", fView, new Point(
				4, 4)));
		menu.addSeparator();
		menu.add(new AlignCommand("Lefts", fView, AlignCommand.LEFTS));
		menu.add(new AlignCommand("Centers", fView, AlignCommand.CENTERS));
		menu.add(new AlignCommand("Rights", fView, AlignCommand.RIGHTS));
		menu.addSeparator();
		menu.add(new AlignCommand("Tops", fView, AlignCommand.TOPS));
		menu.add(new AlignCommand("Middles", fView, AlignCommand.MIDDLES));
		menu.add(new AlignCommand("Bottoms", fView, AlignCommand.BOTTOMS));
		return menu;
	}

	/**
	 * Creates the debug menu. Clients override this method to add additional
	 * menu items.
	 */
	protected Menu createDebugMenu() {
		Menu menu = new Menu("Debug");

		MenuItem mi = new MenuItem("Simple Update");
		mi.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				fView.setDisplayUpdate(new SimpleUpdateStrategy());
			}
		});
		menu.add(mi);

		mi = new MenuItem("Buffered Update");
		mi.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				fView.setDisplayUpdate(new BufferedUpdateStrategy());
			}
		});
		menu.add(mi);
		return menu;
	}

	/**
	 * Creates the attributes menu and its submenus. Clients override this
	 * method to add additional menu items.
	 */
	protected Menu createAttributesMenu() {
		Menu menu = new Menu("Attributes");
		menu.add(createColorMenu("Fill Color", "FillColor"));
		menu.add(createColorMenu("Pen Color", "FrameColor"));
		menu.add(createArrowMenu());
		menu.addSeparator();
		menu.add(createFontMenu());
		menu.add(createFontSizeMenu());
		menu.add(createFontStyleMenu());
		menu.add(createColorMenu("Text Color", "TextColor"));
		return menu;
	}

	/**
	 * Creates the color menu.
	 */
	protected Menu createColorMenu(String title, String attribute) {
		CommandMenu menu = new CommandMenu(title);
		for (int i = 0; i < ColorMap.size(); i++)
			menu.add(new ChangeAttributeCommand(ColorMap.name(i), attribute,
					ColorMap.color(i), fView));
		return menu;
	}

	/**
	 * Creates the arrows menu.
	 */
	protected Menu createArrowMenu() {
		CommandMenu menu = new CommandMenu("Arrow");
		menu.add(new ChangeAttributeCommand("none", "ArrowMode", new Integer(
				PolyLineFigure.ARROW_TIP_NONE), fView));
		menu.add(new ChangeAttributeCommand("at Start", "ArrowMode",
				new Integer(PolyLineFigure.ARROW_TIP_START), fView));
		menu.add(new ChangeAttributeCommand("at End", "ArrowMode", new Integer(
				PolyLineFigure.ARROW_TIP_END), fView));
		menu.add(new ChangeAttributeCommand("at Both", "ArrowMode",
				new Integer(PolyLineFigure.ARROW_TIP_BOTH), fView));
		return menu;
	}

	/**
	 * Creates the fonts menus. It installs all available fonts supported by the
	 * toolkit implementation.
	 */
	protected Menu createFontMenu() {
		CommandMenu menu = new CommandMenu("Font");
		String fonts[] = Toolkit.getDefaultToolkit().getFontList();
		for (int i = 0; i < fonts.length; i++)
			menu.add(new ChangeAttributeCommand(fonts[i], "FontName", fonts[i],
					fView));
		return menu;
	}

	/**
	 * Creates the font style menu with entries (Plain, Italic, Bold).
	 */
	protected Menu createFontStyleMenu() {
		CommandMenu menu = new CommandMenu("Font Style");
		menu.add(new ChangeAttributeCommand("Plain", "FontStyle", new Integer(
				Font.PLAIN), fView));
		menu.add(new ChangeAttributeCommand("Italic", "FontStyle", new Integer(
				Font.ITALIC), fView));
		menu.add(new ChangeAttributeCommand("Bold", "FontStyle", new Integer(
				Font.BOLD), fView));
		return menu;
	}

	/**
	 * Creates the font size menu.
	 */
	protected Menu createFontSizeMenu() {
		CommandMenu menu = new CommandMenu("Font Size");
		int sizes[] = { 9, 10, 12, 14, 18, 24, 36, 48, 72 };
		for (int i = 0; i < sizes.length; i++) {
			menu.add(new ChangeAttributeCommand(Integer.toString(sizes[i]),
					"FontSize", new Integer(sizes[i]), fView));
		}
		return menu;
	}

	/**
	 * Creates the tool palette.
	 */
	protected Panel createToolPalette() {
		Panel palette = new Panel();
		palette.setBackground(Color.lightGray);
		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(Panel 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(view());
	}

	/**
	 * 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 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 StandardDrawingView createDrawingView() {
		Dimension d = getDrawingViewSize();
		return new StandardDrawingView(this, d.width, d.height);
	}

	/**
	 * Override to define the dimensions of the drawing view.
	 */
	protected Dimension getDrawingViewSize() {
		return new Dimension(400, 600);
	}

	/**
	 * 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 contents component of the application frame. By default the
	 * DrawingView is returned in a ScrollPane.
	 */
	protected Component createContents(StandardDrawingView view) {
		ScrollPane sp = new ScrollPane();
		Adjustable vadjust = sp.getVAdjustable();
		Adjustable hadjust = sp.getHAdjustable();
		hadjust.setUnitIncrement(16);
		vadjust.setUnitIncrement(16);

		sp.add(view);
		return sp;
	}

	/**
	 * Sets the drawing to be edited.
	 */
	public void setDrawing(Drawing drawing) {
		fView.setDrawing(drawing);
		fDrawing = drawing;
	}

	/**
	 * Gets the default size of the window.
	 */
	protected Dimension defaultSize() {
		return new Dimension(430, 406);
	}

	/**
	 * Creates the status line.
	 */
	protected TextField createStatusLine() {
		TextField field = new TextField("No Tool", 40);
		field.setEditable(false);
		return field;
	}

	/**
	 * 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) {
		ToolButton toolButton = (ToolButton) button;
		if (inside)
			showStatus(toolButton.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;
	}

	/**
	 * Sets the default tool of the editor.
	 * 
	 * @see DrawingEditor
	 */
	public void toolDone() {
		if (fDefaultToolButton != null) {
			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 selectionChanged(DrawingView view) {
		MenuBar mb = getMenuBar();
		CommandMenu editMenu = (CommandMenu) mb.getMenu(EDIT_MENU);
		editMenu.checkEnabled();
		CommandMenu alignmentMenu = (CommandMenu) mb.getMenu(ALIGNMENT_MENU);
		alignmentMenu.checkEnabled();
	}

	/**
	 * Shows a status message.
	 * 
	 * @see DrawingEditor
	 */
	public void showStatus(String string) {
		fStatusLine.setText(string);
	}

	private void setTool(Tool t, String name) {
		if (fTool != null)
			fTool.deactivate();
		fTool = t;
		if (fTool != null) {
			fStatusLine.setText(name);
			fTool.activate();
		}
	}

	private void setSelected(ToolButton button) {
		if (fSelectedToolButton != null)
			fSelectedToolButton.reset();
		fSelectedToolButton = button;
		if (fSelectedToolButton != null)
			fSelectedToolButton.select();
	}

	/**
	 * Exits the application. You should never override this method
	 */
	public void exit() {
		destroy();
		setVisible(false); // hide the Frame
		dispose(); // tell windowing system to free resources
		System.exit(0);
	}

	/**
	 * Handles additional clean up operations. Override to destroy or release
	 * drawing editor resources.
	 */
	protected void destroy() {
	}

	/**
	 * Resets the drawing to a new empty drawing.
	 */
	public void promptNew() {
		initDrawing();
	}

	/**
	 * Shows a file dialog and opens a drawing.
	 */
	public void promptOpen() {
		FileDialog dialog = new FileDialog(this, "Open File...",
				FileDialog.LOAD);
		dialog.show();
		String filename = dialog.getFile();
		if (filename != null) {
			filename = stripTrailingAsterisks(filename);
			String dirname = dialog.getDirectory();
			loadDrawing(dirname + filename);
		}
		dialog.dispose();
	}

	/**
	 * Shows a file dialog and saves drawing.
	 */
	public void promptSaveAs() {
		toolDone();
		String path = getSavePath("Save File...");
		if (path != null) {
			if (!path.endsWith(".draw"))
				path += ".draw";
			saveAsStorableOutput(path);
		}
	}

	/**
	 * Shows a file dialog and saves drawing.
	 */
	public void promptSaveAsSerialized() {
		toolDone();
		String path = getSavePath("Save File...");
		if (path != null) {
			if (!path.endsWith(".ser"))
				path += ".ser";
			saveAsObjectOutput(path);
		}
	}

	/**
	 * Prints the drawing.
	 */
	public void print() {
		fTool.deactivate();
		PrintJob printJob = getToolkit().getPrintJob(this, "Print Drawing",
				null);

		if (printJob != null) {
			Graphics pg = printJob.getGraphics();

			if (pg != null) {
				fView.printAll(pg);
				pg.dispose(); // flush page
			}
			printJob.end();
		}
		fTool.activate();
	}

	private String getSavePath(String title) {
		String path = null;
		FileDialog dialog = new FileDialog(this, title, FileDialog.SAVE);
		dialog.show();
		String filename = dialog.getFile();
		if (filename != null) {
			filename = stripTrailingAsterisks(filename);
			String dirname = dialog.getDirectory();
			path = dirname + filename;
		}
		dialog.dispose();
		return path;
	}

	private String stripTrailingAsterisks(String filename) {
		// workaround for bug on NT
		if (filename.endsWith("*.*"))
			return filename.substring(0, filename.length() - 4);
		else
			return filename;
	}

	private void saveAsStorableOutput(String file) {
		// TBD: should write a MIME header
		try {
			FileOutputStream stream = new FileOutputStream(file);
			StorableOutput output = new StorableOutput(stream);
			output.writeStorable(fDrawing);
			output.close();
		} catch (IOException e) {
			showStatus(e.toString());
		}
	}

	private void saveAsObjectOutput(String file) {
		// TBD: should write a MIME header
		try {
			FileOutputStream stream = new FileOutputStream(file);
			ObjectOutput output = new ObjectOutputStream(stream);
			output.writeObject(fDrawing);
			output.close();
		} catch (IOException e) {
			showStatus(e.toString());
		}
	}

	private void loadDrawing(String file) {
		toolDone();
		String type = guessType(file);
		if (type.equals("storable"))
			readFromStorableInput(file);
		else if (type.equals("serialized"))
			readFromObjectInput(file);
		else
			showStatus("Unknown file type");
	}

	private void readFromStorableInput(String file) {
		try {
			FileInputStream stream = new FileInputStream(file);
			StorableInput input = new StorableInput(stream);
			fDrawing.release();
			fDrawing = (Drawing) input.readStorable();
			fView.setDrawing(fDrawing);
		} catch (IOException e) {
			initDrawing();
			showStatus("Error: " + e);
		}
	}

	private void readFromObjectInput(String file) {
		try {
			FileInputStream stream = new FileInputStream(file);
			ObjectInput input = new ObjectInputStream(stream);
			fDrawing.release();
			fDrawing = (Drawing) input.readObject();
			fView.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";
	}
}
