/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ package netscape.plugin.composer.mapedit; import java.awt.*; import java.applet.*; import java.awt.image.*; import java.net.URL; import java.io.*; import netscape.plugin.composer.*; import netscape.plugin.composer.io.*; import java.util.*; // For ResourceBundle, etc. import netscape.security.PrivilegeManager; /* ****************** The Plugin *************************************************/ public class MapEdit extends Plugin { public static void main(String []args) { Debug.println("in MapEdit.main()" + args); netscape.test.plugin.composer.Test.perform(args,new MapEdit()); } /** Returns whether resources are now loaded. */ boolean loadResources() { if (!resLoaded) { // Load resources. international. try { Debug.println("Loading resources"); // This method not supported in the Navigator yet. // res = ResourceBundle.getResourceBundle("netscape.test.plugin.composer.MapEditResource",null); res = new MapEditResource(); resLoaded = true; } catch (MissingResourceException e) { } } return resLoaded; } private boolean resLoaded = false; private ResourceBundle res = null; public String getName() { if (loadResources()) return res.getString("image_map_editor"); else return "Image Map Editor"; } public String getCategory() { if (loadResources()) return res.getString("html_tools"); else return "HTML Tools"; } public String getHint() { if (loadResources()) return res.getString("the_hint"); else return super.getHint(); } public boolean perform(Document document) throws IOException { ////// --------------------------------//// Debug.println("Image Map Editor test 8"); ////// --------------------------------//// if (!loadResources()) { System.err.println("Map Editor could not load resources."); return false; } MapEditApp app = new MapEditApp(document,res); // Catch any errors in init(). if (app.initSuccessful()) { app.show(); } boolean ret = app.waitForExit(); // Let Cafe users see the output before the window goes away. if (false) { // if (Debug.debug()) { try { if (true) { Debug.println("Hit return"); System.in.read(); } else { Debug.println("SLEEP for a few seconds"); Thread.sleep(10000); } } catch (Exception e) {} } return ret; } } /* ********************* The actual Application ***************/ class MapEditApp extends Frame implements OkCallback { private boolean initOk = false; public boolean initSuccessful() {return initOk;} public MapEditApp(Document d,ResourceBundle rB) { super(rB.getString("frame_title")); res = rB; document = d; areas = new Vector(); if (document == null) { failure(res.getString("doc_is_null")); return; } // Try to find the first image in the selection. if (!parseDoc(true)) { // Next, look for any image in the document. if (!parseDoc(false)) { // Give up, no images. failure(res.getString("no_images_in_document")); return; } } // imageName, imageLocation, and mapName should now be valid. // Security stuff PrivilegeManager.getPrivilegeManager().enablePrivilege("UniversalFileAccess"); PrivilegeManager.getPrivilegeManager().enablePrivilege("UniversalConnect"); Debug.println("got privileges"); // Load image URL imageURL = null; try { imageURL = new URL(document.getBase(),imageName); } catch (java.net.MalformedURLException e) { failure(e.toString()); return; } Debug.println("created dummy URL"); try { image = Toolkit.getDefaultToolkit().getImage(imageURL); } catch (Exception e) { failure(e.toString()); return; } Debug.println("start image load"); // Force image loading. MediaTracker tracker = new MediaTracker(this); tracker.addImage(image,0); try { tracker.waitForID(0); } catch (InterruptedException e) { image = null; } if (image != null) { imageDim = new Dimension(image.getWidth(this),image.getHeight(this)); imageRect = new Rectangle(imageDim); } if (image == null || imageDim.width < 0 || imageDim.height < 0) { failure(Format.formatLookup(res,"could_not_load_image",imageName)); return; } Debug.println("finish image load"); createUI(); syncAreaList(); resize(MapEditApp.APP_WIDTH,MapEditApp.APP_HEIGHT); initOk = true; // Flag so we can stop running the app. isRunning = true; } synchronized void failure(String message) { if (failureDialog == null) failureDialog = new InfoDialog(this,res,"Map Editor Error",message,this); if (!failureDialog.isVisible()) failureDialog.show(); Debug.println("FAILURE:" + message); } private InfoDialog failureDialog = null; // Callback from the failure InfoDialog. Quit the application. public synchronized void ok() { Debug.println("ok callback called"); if (isRunning) { // success will be false. stopRunning(); } } synchronized public boolean waitForExit() { while ( isRunning ) { try { Debug.println("MapEditApp.waitForExit is wait()ing."); wait(); } catch ( InterruptedException e){ } } hide(); return success; } synchronized void stopRunning() { Debug.println("stop running"); isRunning = false; notifyAll(); } /*********************** PARSING/UNPARSING CODE *********************/ // Find first image in selection or in document. // Sets imageName, mapName, imageLocation. // Returns whether an image was found. private boolean parseDoc(boolean onlyInSelection) { // Start from beginning of document. LexicalStream inStream = null; try { // Read entire document into a string and use the string // for the LexicalStream. Reader in = document.getInput(); StringBuffer b = new StringBuffer(); int bufSize = 1000; char[] buf = new char[bufSize]; for(;;) { int moved = in.read(buf); if ( moved < 0 ) break; b.append(buf, 0, moved); } in.close(); inStream = new LexicalStream(b.toString()); } catch (IOException e) { failure(e.toString()); } imageLocation = -1; boolean inSelection = false; Token token; try { while ((token = inStream.next()) != null) { imageLocation++; // 0 for the first token. // Keep track of selection start/end. if (token instanceof Comment) { Comment comment = (Comment)token; if (!inSelection && comment.isSelectionStart()) inSelection = true; if (inSelection && comment.isSelectionEnd()) inSelection = false; } // Find first IMG tag. if ((!onlyInSelection || inSelection) && token instanceof Tag) { Tag tag = (Tag)token; if (tag.getName().equals("IMG")) { // Image found. if (!tag.isOpen() || !tag.containsAttribute("SRC")) { failure(res.getString("first_img_invalid")); } String value = tag.lookupAttribute("USEMAP"); // If image has an image map, read in it's tags and // use its name. if (value != null && value.length() > 0) { mapName = StripPound(value); parseMap(); } // Else, a new map, generate a map name. else { // NSMAP + 6 digits. mapName = "NSMAP" + (int)Math.floor((Math.random() * 999999)); } imageName = tag.lookupAttribute("SRC"); if (imageName == null || imageName.length() == 0) { // failure(new MessageFormat(res.getString("invalid_img_tag")).format(args)); failure(Format.formatLookup(res,"invalid_img_tag",imageName)); } return true; } // if "IMG" } // if Tag } // while } catch (IOException e) { failure(e.toString()); } return false; } /** Search from beginning of doc, find first map with name mapName. Use it to construct the Vector areas. */ private void parseMap() { // Note: Starting from the beginning. LexicalStream stream = null; try { stream = new LexicalStream(document.getInput()); } catch (IOException e) { failure(e.toString()); } Token t; try { while ((t = stream.next()) != null) { if (t instanceof Tag) { Tag tag = (Tag)t; if (tag.getName().equals("MAP") && tag.isOpen() && mapName.equals(tag.lookupAttribute("NAME"))) { parseAreas(stream); } } } } catch (IOException e) { failure(e.toString()); } // If we don't find an image map corresponding to mapName, areas will just // be an empty Vector. } /** Read in all tags until a closing tag is hit. */ private void parseAreas(LexicalStream stream) { try { Token token; while ((token = stream.next()) != null) { if (token instanceof Tag) { Tag tag = (Tag)token; if (tag.getName().equals("MAP") && tag.isClose()) return; // Give each Area class a chance to parse the tag. Area area; if ((area = DefaultArea.fromTag(tag,document.getBase())) != null) { // We could make the default area a real area, for now just store the // default URL. defaultURL.str = area.getURL(); continue; } if ((area = RectArea.fromTag(tag,document.getBase())) != null) { // area.clip(imageRect); areas.addElement(area); continue; } if ((area = CircleArea.fromTag(tag,document.getBase())) != null) { // area.clip(imageRect); areas.addElement(area); continue; } if ((area = PolyArea.fromTag(tag,document.getBase())) != null) { // area.clip(imageRect); areas.addElement(area); continue; } } // if } // while } catch (IOException e) { failure(e.toString()); } } /** Strips leading '#' if found. */ private String StripPound(String in) { if (in.startsWith("#")) return in.substring(1); else return in; } /** Write document to the Document output stream. */ private void writeDoc() { // Start from beginning of document. LexicalStream inStream = null; Writer outStream = null; try { inStream = new LexicalStream(document.getInput()); outStream = document.getOutput(); } catch (IOException e) { failure(e.toString()); } // Between and of the corresponding to the first image. boolean insideMap = false; int location = -1; // so is 0 for first token. Token token; try { while ((token = inStream.next()) != null) { // At top so gets incremented even if we "continue" inside body of "while". location++; if (token instanceof Tag) { Tag tag = (Tag)token; // Change IMG tag to use new map. May just set it to the prev value. if (location == imageLocation) { if (!tag.getName().equals("IMG")) { failure(res.getString("error_reading_twice")); } // Only write out if at least one area or the default area. if (!areas.isEmpty() || defaultURL.notEmpty()) { tag.addAttribute("USEMAP","#" + mapName); outStream.write(tag.toString() + "\n"); // Write the image map right after the image. writeMap(outStream); } // If no areas, don't output image map. else { // Remove USEMAP if it was there. tag.removeAttribute("USEMAP"); outStream.write(tag.toString()); } continue; } // Eliminate beginning corresponding to the first image. if (!insideMap && tag.getName().equals("MAP") && tag.isOpen() && mapName.equals(tag.lookupAttribute("NAME"))) { insideMap = true; continue; } // Eliminate trailing if (insideMap && tag.getName().equals("MAP") && tag.isClose()) { insideMap = false; continue; } // Kill all tags between and if (insideMap && tag.getName().equals("AREA")) { continue; } } // Tag outStream.write(token.toString()); } // while outStream.close(); } catch (IOException e) { if (outStream != null) try {outStream.close();} catch (IOException e2) {} failure(e.toString()); } } private void writeMap(Writer out) throws IOException { Tag map = new Tag("MAP"); map.addAttribute("NAME",mapName); out.write(map.toString() + "\n"); Enumeration e = areas.elements(); while (e.hasMoreElements()) { Tag area = ((Area)e.nextElement()).toTag(); out.write(area.toString() + "\n"); } // Write default area last. if (defaultURL.notEmpty()) { out.write(new DefaultArea(defaultURL.str).toTag().toString() + "\n"); } out.write(new Tag("MAP",false).toString() + "\n"); } /***************** END PARSING/UNPARSING CODE ******************************/ /************************** UI CODE ************************************/ static private final boolean areaListOnDefault = false; private void createUI() { setLayout(new BorderLayout()); header = new Panel(); header.setBackground(Color.lightGray); IconCheckBoxGroup grp = new IconCheckBoxGroup(); header.setLayout(new FlowLayout(FlowLayout.LEFT,3,4)); header.add(arrowCheckBox = new ArrowCheckBox(grp,mode == SELECT, new OkCallback() {public void ok() { select();} })); header.add(new RectCheckBox(grp,mode == CREATE_RECT_AREA, new OkCallback() {public void ok() { createArea(CREATE_RECT_AREA);} })); header.add(new CircleCheckBox(grp,mode == CREATE_CIRCLE_AREA, new OkCallback() {public void ok() { createArea(CREATE_CIRCLE_AREA);} })); header.add(new PolyCheckBox(grp,mode == CREATE_POLY_AREA, new OkCallback() {public void ok() { createArea(CREATE_POLY_AREA);} })); Label l = new Label(res.getString("url"),Label.RIGHT); header.add(l); urlComponent = new TextField(40); urlComponent.setEditable(true); urlComponent.setText(new String(defaultURL.str)); header.add(urlComponent); header.add(areaListButton = new Checkbox(res.getString("area_list"),null,areaListOnDefault)); add("North",header); canvas = new BlankCanvas(this); canvas.setBackground(Color.white); add("Center",canvas); areaList = new SizedList(); add("East",areaList); if (!areaListOnDefault) { areaList.hide(); } doneButton = new Button(res.getString("done")); cancelButton = new Button(res.getString("cancel")); Panel footer = new Panel(); footer.setBackground(Color.lightGray); footer.setLayout(new FlowLayout(FlowLayout.CENTER, 30, 9)); footer.add(doneButton); footer.add(cancelButton); add("South",footer); } // offset is vector from canvas origin to image origin. private Dimension offset = new Dimension(0,0); private BlankCanvas canvas; private Panel header; private TextComponent urlComponent; private Button doneButton; private Button cancelButton; private Checkbox areaListButton; private List areaList; private IconCheckBox arrowCheckBox; // Keep image centered if canvas is bigger than image. private static int reshapeHelper(int canvasSize,int imageSize,int offset) { int offsetNew; if (canvasSize > imageSize) { offsetNew = (canvasSize - imageSize) / 2; } else { offsetNew = Math.min(offset,0); } return offsetNew; } /** The image canvas has been reshaped to this width and height. */ void reshapeCanvas(int width,int height) { offset.width = reshapeHelper(width,imageDim.width,offset.width); offset.height = reshapeHelper(height,imageDim.height,offset.height); } public synchronized void paintMe(Graphics g) { // Debug.println("painting"); g.drawImage(image,offset.width,offset.height,this); // Draw region for default. DefaultArea.draw(g,offset,imageRect,selected == -1); // Draw in reverse order, so that lowest Area is on top. // First area in list takes precedence for selection. for (int index = areas.size() - 1; index >= 0; index--) { Area a = (Area)areas.elementAt(index); a.draw(g,offset,index == selected); } } // Use instead of empty or null string. private String listString(Object o) { String url = ((Area)o).getURL(); if (url != null && url.length() > 0) { return url; } else { return res.getString("unspecified_area"); } } private void syncAreaList() { areaList.clear(); Enumeration e = areas.elements(); while (e.hasMoreElements()) { areaList.addItem(listString(e.nextElement())); } if (defaultURL.notEmpty()) { areaList.addItem("> " + defaultURL.str); } selectAreaList(selected); } private void selectAreaList(int val) { // Just to be sure, unselect all. MAC won't do it for you. int current = areaList.getSelectedIndex(); if (current != -1) { areaList.deselect(current); } if (val == -1) { if (defaultURL.notEmpty()) { Debug.assert(areas.size() + 1 == areaList.countItems(),"list not synced"); // select last item. areaList.select(areas.size()); } else { Debug.assert(areas.size() == areaList.countItems(),"list not synced"); } } else { areaList.select(val); } Debug.println("selectAreaList: val was " + val + " List had" + current + " List is now " + areaList.getSelectedIndex()); } /* ************** Helper code for dealing with areas ************** */ private void setSelected(int n) { setSelected(n,false); } // fromAreaList prevents recursion with the areaList private void setSelected(int n,boolean fromAreaList) { Debug.println("setSelected " + n); if (selected < -1 || selected >= areas.size()) { Debug.println("Attempting to select invalid index."); return; } // first copy current value from the UI text element to the internal // representation. flushURLComponent(); if (completing()) { Debug.println("MapEditApp.select() when not done with prev operation."); // Don't allow changing the selection while completing an area. return; } selected = n; // Update UI. if (n == -1) urlComponent.setText(new String(defaultURL.str)); else urlComponent.setText(((Area)areas.elementAt(n)).getURL().toString()); // Make areaList match current selection. if (!fromAreaList) { selectAreaList(selected); } } /** Are we in the process of completing an area. */ private boolean completing() { // Something is selected and it is not fully created. return (selected != -1 && !((Area)areas.elementAt(selected)).completed()); } private void deleteSelected() { deleteSelected(false); } private void deleteSelected(boolean fromAreaList) { Debug.println("Deleted selected = " + selected); if (selected < -1 || selected >= areas.size()) { Debug.println("Attempting to delete with invalid index."); return; } if (selected == -1) { if (defaultURL.notEmpty()) { // Just clear out the current string. defaultURL.str = ""; } } else { // This should remove the currently selected element. areas.removeElementAt(selected); } selected = -1; urlComponent.setText(new String(defaultURL.str)); syncAreaList(); } // Copy current value from the UI text element to the internal // representation. private void flushURLComponent() { String url = urlComponent.getText(); Debug.println("FLUSH, component has <" + ((url == null) ? "-NULL-" : url) + ">"); if (selected != -1) { Area area = (Area)areas.elementAt(selected); // Only change if necessary. if (!area.getURL().equals(url)) { ((Area)areas.elementAt(selected)).setURL(new String(url)); syncAreaList(); } } else { // Set default url if (!defaultURL.str.equals(url)) { defaultURL.str = new String(url); syncAreaList(); } } } /* *************** End helper code ******************** */ // Translate an index from an AWT event to the index for the // Vector. -1 means the extraString was selected. public int translateEventIndex(int val) { if (val >= areas.size()) { return -1; } return val; } public synchronized boolean handleEvent(Event e) { if (e.id == Event.WINDOW_DESTROY) { cancel(); return true; } else if (e.target == doneButton) { if (e.id == Event.ACTION_EVENT) { done(); return true; } } else if (e.target == cancelButton) { if (e.id == Event.ACTION_EVENT) { cancel(); return true; } } // Show/hide areaList. else if (e.target == areaListButton) { if (e.id == Event.ACTION_EVENT) { boolean val = ((Boolean)e.arg).booleanValue(); if (val) { areaList.show(); } else { areaList.hide(); } validate(); return true; } } else if (e.target == areaList) { // Select according to the areaList. if (e.id == Event.LIST_SELECT) { int val = ((Integer)e.arg).intValue(); int translated = translateEventIndex(val); Debug.println("LIST_SELECT event received, evt " + val + ", translate " + translated); setSelected(translated,true); canvas.repaint(); return true; } // Delete selected area. if (e.id == Event.KEY_PRESS && (e.key == Event.BACK_SPACE)) { // Not Event.DELETE, because it produces "?" on the MAC. Debug.println("DELETE in area list event"); deleteSelected(true); canvas.repaint(); return true; } } // Flush the text field into the area. else if (e.target == urlComponent) { if (e.id == Event.ACTION_EVENT || e.id == Event.LOST_FOCUS) { flushURLComponent(); } } else if (e.target == this && (e.id == Event.KEY_PRESS || e.id == Event.KEY_ACTION)) { // Cafe 1.53 sends key events here. return keyDown(e); } return super.handleEvent(e); } synchronized boolean keyDown(Event e) { Debug.println("keyDown: " + e.key); checkKillDoubleClick(e); // Move/delete selected if (selected != -1) { if (e.key == Event.BACK_SPACE) { // Not Event.DELETE, because it produces "?" on the MAC. deleteSelected(); canvas.repaint(); return true; } boolean arrow = false; int dx = 0; int dy = 0; if (e.key == Event.UP) { dy = -1; arrow = true; } else if (e.key == Event.RIGHT) { dx = 1; arrow = true; } else if (e.key == Event.DOWN) { dy = 1; arrow = true; } else if (e.key == Event.LEFT) { dx = -1; arrow = true; } if (arrow) { if (e.modifiers != 0) { // Don't care about return value. ((Area)areas.elementAt(selected)).resizeBy(dx,dy,imageRect); } else { // Don't care about return value. ((Area)areas.elementAt(selected)).moveBy(dx,dy,imageRect); } canvas.repaint(); return true; } } // Force completion, delete area if failure. if (e.key == ' ') { if (completing()) { if (! ((Area)areas.elementAt(selected)).forceCompletion() ) { deleteSelected(); canvas.repaint(); return true; } // Finished completing an area, go back to selection mode. if (((Area)areas.elementAt(selected)).completed()) { // This will call MapEditApp.select() arrowCheckBox.setState(true); } } } return false; } // p is in image coordinates private int areaAt(Point p) { // Find first area that contains p. for (int n = 0; n < areas.size(); n++) { if (((Area)areas.elementAt(n)).containsPoint(p.x,p.y)) { return n; } } return -1; } /* Clip p (in image coords) to the image rectangle. */ private void clipImage(Point p) { p.x = Math.max(p.x,0); p.y = Math.max(p.y,0); p.x = Math.min(p.x,imageDim.width); p.y = Math.min(p.y,imageDim.height); } synchronized boolean mouseUp(Event e) { Debug.println("mouseUp"); Point pImage = new Point(e.x - offset.width,e.y - offset.height); if (completing()) { clipImage(pImage); // In the process of completing an area, let the area handle it. ((Area)areas.elementAt(selected)).mouseUp(pImage,imageRect); // Finished completing an area, go back to selection mode. if (((Area)areas.elementAt(selected)).completed()) { // This will call MapEditApp.select() arrowCheckBox.setState(true); } } canvas.repaint(); return true; } Point doubleClick = null; synchronized boolean mouseDown(Event e) { Debug.println("mouseDown"); // Just for the fun of it. requestFocus(); Point pImage = new Point(e.x - offset.width,e.y - offset.height); // Select if (mode == SELECT) { int n = areaAt(pImage); // If n == -1, we select the background. setSelected(n); if (n != -1) { // Get point in the Area's coordinate system // Used for dragging selected areas. Rectangle r = ((Area)areas.elementAt(selected)).boundingBox(); dragPoint.x = pImage.x - r.x; dragPoint.y = pImage.y - r.y; } } else { clipImage(pImage); if (completing()) { if (doubleClick != null) { // On double click, try to force completion if (!((Area)areas.elementAt(selected)).forceCompletion()) { deleteSelected(); } // Finished completing an area, go back to selection mode. if (((Area)areas.elementAt(selected)).completed()) { // This will call MapEditApp.select() arrowCheckBox.setState(true); } } else { // In the process of completing an area, let the area handle it. ((Area)areas.elementAt(selected)).mouseDown(pImage,imageRect); } } else { // Start creating a new area. switch (mode) { case CREATE_RECT_AREA: { Debug.println("calling RectArea first mouse down"); areas.addElement(RectArea.firstMouseDown(pImage)); syncAreaList(); } break; case CREATE_CIRCLE_AREA: { areas.addElement(CircleArea.firstMouseDown(pImage)); syncAreaList(); } break; case CREATE_POLY_AREA: { areas.addElement(PolyArea.firstMouseDown(pImage)); syncAreaList(); } break; default: Debug.println("invalid mode"); } setSelected(areas.size() - 1); // Last element. } } canvas.repaint(); doubleClick = new Point(e.x,e.y); return true; } void checkKillDoubleClick(Event e) { // Don't kill doubleClick if event (x,y) hasn't changed. if (doubleClick != null && (doubleClick.x != e.x || doubleClick.y != e.y)) { doubleClick = null; } } synchronized boolean mouseDragged(Event e) { Debug.println("mouseDragged"); checkKillDoubleClick(e); Point pImage = new Point(e.x - offset.width,e.y - offset.height); boolean dirty = false; // Move region with pointer if (mode == SELECT && selected != -1) { // Move so that pImage is a dragPoint with respect to the Area. Area area = (Area)areas.elementAt(selected); Rectangle r = area.boundingBox(); int dx = pImage.x - r.x - dragPoint.x; int dy = pImage.y - r.y - dragPoint.y; if (area.moveBy(dx,dy,imageRect)) { dirty = true; } } if (completing()) { clipImage(pImage); // In the process of completing an area, let the area handle it. ((Area)areas.elementAt(selected)).mouseDragged(pImage,imageRect); dirty = true; } if (dirty) { canvas.repaint(REPAINT_TIME); } return true; } synchronized boolean mouseMoved(Event e) { Debug.println("mouseMoved"); checkKillDoubleClick(e); Point pImage = new Point(e.x - offset.width,e.y - offset.height); // In the process of completing an area, let the area handle it. if (completing()) { clipImage(pImage); ((Area)areas.elementAt(selected)).mouseMoved(pImage,imageRect); canvas.repaint(REPAINT_TIME); } return true; } private void checkAreas() { Enumeration e = areas.elements(); while (e.hasMoreElements()) { Area a = (Area)e.nextElement(); if (a.getURL() == null || a.getURL().length() == 0) { // TODO: Need to warn the user at this point. Debug.println("Found an area with invalid URL."); } } } private void done() { flushURLComponent(); if (completing()) { deleteSelected(); } // Don't setSelected(-1) because that won't do anything if completing() is true. checkAreas(); writeDoc(); success = true; stopRunning(); } private void cancel() { success = false; // redundant stopRunning(); } // public so can be called by callbacks. public void select() { mode = SELECT; // Unselect everything. flushURLComponent(); if (completing()) { deleteSelected(); } // May want to edit just created area. // setSelected(-1); canvas.repaint(); } // public so can be called by callbacks. public void createArea(int newMode) { mode = newMode; // Unselect everything. flushURLComponent(); if (completing()) { deleteSelected(); } setSelected(-1); canvas.repaint(); } public void setDocument(Document doc) { document = doc; } private boolean isRunning = false; // Are we inside Application.run(). private String imageName; private int imageLocation; // In tokens, starting with 0. private String mapName; private Document document; private Image image; private Dimension imageDim; private Rectangle imageRect; // For convenience, (0,0,imageDim.width,imageDim.height) public static final int APP_WIDTH = 600; public static final int APP_HEIGHT = 400; private final static int REPAINT_TIME = 50; // All international strings. private ResourceBundle res = null; /** List of all areas. */ private Vector areas; // Index into areas, -1 means none selected. private int selected = -1; private StringRef defaultURL = new StringRef(); // What the user is doing. private final static int SELECT = 0; private final static int CREATE_RECT_AREA = 1; private final static int CREATE_CIRCLE_AREA = 2; private final static int CREATE_POLY_AREA = 3; private int mode = SELECT; // Used for dragging selected areas. private Point dragPoint = new Point(0,0); // Did plugin succeed. private boolean success = false; } /** Double buffered, asks the MapEditApp to redraw for it. */ class BlankCanvas extends Canvas { BlankCanvas(MapEditApp mEdtApp) { mapEditApp = mEdtApp; } // Just send it to the MapEditApp synchronized public boolean handleEvent(Event e) { if (e.target == this) { switch (e.id) { case Event.KEY_PRESS: case Event.KEY_ACTION: return mapEditApp.keyDown(e); case Event.MOUSE_DOWN: return mapEditApp.mouseDown(e); case Event.MOUSE_UP: return mapEditApp.mouseUp(e); case Event.MOUSE_MOVE: return mapEditApp.mouseMoved(e); case Event.MOUSE_DRAG: return mapEditApp.mouseDragged(e); } } return false; } public synchronized void reshape(int x, int y, int width, int height) { // do the real stuff super.reshape(x, y, width, height); mapEditApp.reshapeCanvas(width,height); // Create offscreen image for double buffer. offscreen = createImage(width,height); offDim.width = width; offDim.height = height; repaint(); } synchronized public void paint(Graphics g) { mapEditApp.paintMe(g); } public synchronized void update(Graphics g) { if (offscreen != null) { Debug.println("BlankCanvas.paint"); Graphics gOff = offscreen.getGraphics(); gOff.setColor(getBackground()); gOff.fillRect(0,0,offDim.width,offDim.height); paint(gOff); g.drawImage(offscreen,0,0,mapEditApp); } /* g.setColor(getBackground()); g.fillRect(0,0,offDim.width,offDim.height); paint(g); */ } private Image offscreen = null; private Dimension offDim = new Dimension(); private MapEditApp mapEditApp; }