0001: package com.xoetrope.svg;
0002:
0003: import com.kitfox.svg.Defs;
0004: import com.kitfox.svg.Group;
0005: import com.kitfox.svg.SVGCache;
0006: import com.kitfox.svg.SVGDiagram;
0007: import com.kitfox.svg.SVGDisplayPanel;
0008: import com.kitfox.svg.SVGElement;
0009: import com.kitfox.svg.SVGElementException;
0010: import com.kitfox.svg.SVGException;
0011: import com.kitfox.svg.SVGLoaderHelper;
0012: import com.kitfox.svg.SVGRoot;
0013: import com.kitfox.svg.SVGUniverse;
0014: import com.kitfox.svg.ShapeElement;
0015: import com.kitfox.svg.animation.Animate;
0016: import com.kitfox.svg.animation.AnimationElement;
0017: import com.xoetrope.carousel.build.BuildProperties;
0018: import java.awt.BorderLayout;
0019: import java.awt.Color;
0020: import java.awt.Container;
0021: import java.awt.Cursor;
0022: import java.awt.Dimension;
0023: import java.awt.Graphics;
0024: import java.awt.Graphics2D;
0025: import java.awt.Point;
0026: import java.awt.Rectangle;
0027: import java.awt.RenderingHints;
0028: import java.awt.Shape;
0029: import java.awt.event.ActionEvent;
0030: import java.awt.event.ActionListener;
0031: import java.awt.event.FocusEvent;
0032: import java.awt.event.MouseEvent;
0033: import java.awt.event.MouseListener;
0034: import java.awt.event.MouseMotionListener;
0035: import java.awt.event.ComponentAdapter;
0036: import java.awt.event.ComponentEvent;
0037: import java.awt.geom.Point2D;
0038: import java.awt.geom.Rectangle2D;
0039: import java.awt.image.BufferedImage;
0040: import java.awt.image.RenderedImage;
0041: import java.io.File;
0042: import java.net.URI;
0043: import java.net.URL;
0044: import java.text.DecimalFormat;
0045: import java.text.NumberFormat;
0046: import java.util.ArrayList;
0047: import java.util.Locale;
0048: import java.util.Vector;
0049: import javax.swing.BorderFactory;
0050: import javax.swing.JComponent;
0051: import javax.swing.JFrame;
0052: import javax.swing.JLayeredPane;
0053: import javax.swing.JPanel;
0054: import javax.swing.SpringLayout;
0055: import javax.swing.text.NumberFormatter;
0056: import net.xoetrope.swing.XPanel;
0057: import net.xoetrope.xui.XProject;
0058: import net.xoetrope.xui.XProjectManager;
0059:
0060: import net.xoetrope.xui.XAttributedComponent;
0061:
0062: /**
0063: *
0064: *
0065: * <p> Copyright (c) Xoetrope Ltd., 2001-2006, This software is licensed under
0066: * the GNU Public License (GPL), please see license.txt for more details. If
0067: * you make commercial use of this software you must purchase a commercial
0068: * license from Xoetrope.</p>
0069: * <p> $Revision: 1.2 $</p>
0070: */
0071: public class XSvgImageMap extends JPanel implements
0072: MouseMotionListener, MouseListener, XAttributedComponent {
0073: private String file, displayGroup;
0074: private SVGUniverse universe;
0075: private SVGDiagram diagram;
0076: private SVGElement element;
0077: private URI uri;
0078: private File svgFile;
0079: private double w, h;
0080: private String[] baseShapes;
0081: private double scaleX, scaleY, centreX, centreY, previousWidth,
0082: previousHeight;
0083: private int savedX, savedY, savedW, savedH;
0084: private XSvgRolloverFinder rolloverFinder;
0085: private URL url, previousUrl;
0086: private XProject currentProject;
0087: private boolean setRollOvers, firstResize, drawImages;
0088: private BufferedImage bufferedImage;
0089: private Group selected;
0090: private int bufferX, bufferY;
0091: private XRenderingSemaphore semaphore;
0092: private BufferedImage[] images;
0093: private ArrayList listeners;
0094: private JPanel glassPane;
0095: private XPointSystem pointSystem;
0096: private boolean firstZoom, coalesce;
0097: private double zoomHeight, zoomWidth, initialX, initialY, panX,
0098: panY, previousPanX, previousPanY;
0099: private DecimalFormat formatter;
0100:
0101: protected String blockPrefix = "b_";
0102: protected String rolloverPrefix = "r_";
0103:
0104: private boolean preserveAspect = true;
0105: private boolean centreImage = true;
0106:
0107: /**
0108: * Class constructor.
0109: */
0110: public XSvgImageMap() {
0111: currentProject = XProjectManager.getCurrentProject();
0112:
0113: setLayout(new BorderLayout());
0114: universe = SVGCache.getSVGUniverse();
0115: addMouseMotionListener(this ); // add motion listener for SVGDisplayPanel
0116: addMouseListener(this ); // add motion listener for SVGDisplayPanel
0117: diagram = null;
0118: uri = null;
0119:
0120: displayGroup = null;
0121: setRollOvers = true;
0122:
0123: scaleX = 1.0;
0124: scaleY = 1.0;
0125: centreX = 0.0;
0126: centreY = 0.0;
0127:
0128: previousWidth = getWidth();
0129: previousHeight = getHeight();
0130:
0131: glassPane = new JPanel();
0132: glassPane.setLayout(new BorderLayout());
0133: glassPane.setOpaque(false);
0134: glassPane.setLayout(null);
0135:
0136: JLayeredPane layeredPane = new JLayeredPane();
0137: layeredPane.add(glassPane, JLayeredPane.PALETTE_LAYER);
0138:
0139: add(layeredPane);
0140:
0141: // component listener used to listen for component resize events
0142: this .addComponentListener(new ComponentAdapter() {
0143: public void componentResized(ComponentEvent evt) {
0144: resize();
0145: // resetBuffer();
0146: repaint();
0147: glassPane.setSize(getWidth(), getHeight());
0148: }
0149: });
0150:
0151: setDoubleBuffered(false);
0152: setOpaque(false);
0153:
0154: semaphore = new XRenderingSemaphore(1);
0155: listeners = new ArrayList();
0156: pointSystem = new XPointSystem(this );
0157: NumberFormat nf = NumberFormat.getNumberInstance(Locale.US);
0158: formatter = (DecimalFormat) nf;
0159: formatter.applyPattern(".00000");
0160: }
0161:
0162: /**
0163: * Returns the glass pane component of the layered pane
0164: * @return the <CODE>JPanel</CODE> component returned
0165: */
0166: public JPanel getGlassPane() {
0167: return glassPane;
0168: }
0169:
0170: /**
0171: * Converts a coordinate from the glasspane coordinate system to the svg map coordinate system.
0172: * @param x a <CODE>double</CODE> specifying the x coordinate to be converted.
0173: * @param y a <CODE>double</CODE> specifying the y coordinate to be converted.
0174: * @return <CODE>Point2D</CODE> object containing the converted coordinates.
0175: */
0176: public Point2D glassPaneToSvg(double x, double y) {
0177: return pointSystem.glassPaneToSvg(x, y);
0178: }
0179:
0180: /**
0181: * Converts a coordinate from the svg map coordinate system to the glasspane coordinate system.
0182: * @param x a <CODE>double</CODE> specifying the x coordinate to be converted.
0183: * @param y a <CODE>double</CODE> specifying the y coordinate to be converted.
0184: * @return <CODE>Point2D</CODE> object containing the converted coordinates.
0185: */
0186: public Point2D svgToGlassPane(double x, double y) {
0187: return pointSystem.svgToGlassPane(x, y);
0188: }
0189:
0190: /**
0191: * Set the compare interfaces for comparing groups/elements for
0192: * identification of the rollovers
0193: * @param sim the comparison class
0194: */
0195: public void setSvgRolloverFinder(XSvgRolloverFinder sim) {
0196: rolloverFinder = sim;
0197: }
0198:
0199: /**
0200: * Get the compare interface
0201: * @return the interface object or null if none has been set.
0202: */
0203: public XSvgRolloverFinder getSvgRolloverFinder() {
0204: return rolloverFinder;
0205: }
0206:
0207: /**
0208: * Set the prefix for each block within the svg image.
0209: * @param prefix <CODE>String</CODE> specifying the block prefix.
0210: */
0211: public void setBlockPrefix(String prefix) {
0212: rolloverPrefix = prefix;
0213: }
0214:
0215: /**
0216: * Set the prefix for each rollover within the svg image.
0217: * @param prefix <CODE>String</CODE> specifying the rollover prefix.
0218: */
0219: public void setRolloverPrefix(String prefix) {
0220: rolloverPrefix = prefix;
0221: }
0222:
0223: /**
0224: * Used to set the URL pointing to the SVG image that is to be displayed.
0225: * @param newUrl <code>URL</code> object specifying the location of the SVG image.
0226: */
0227: public void setURL(URL newUrl) {
0228: url = newUrl;
0229: w = 0;
0230: h = 0;
0231: display();
0232: }
0233:
0234: /**
0235: * Used to return the URL pointing to the SVG image that is being displayed.
0236: * @return <code>URL</code> object returned specifying the location of the SVG image.
0237: */
0238: public URL getURL() {
0239: return url;
0240: }
0241:
0242: /**
0243: * Displays the SVG image by adding the file to an SVGUniverse instance,
0244: * extracting an SVGDiagram instance from SVGUniverse and displaying the diagram in an SVGDisplayPanel
0245: */
0246: public SVGDiagram display() {
0247: if (url != null) {
0248: try {
0249: uri = universe
0250: .loadSVG(url.openStream(), url.toString());
0251: diagram = universe.getDiagram(uri);
0252: bufferedImage = null;
0253: resize();
0254: previousUrl = url;
0255: } catch (Exception ex) {
0256: if (BuildProperties.DEBUG)
0257: ex.printStackTrace();
0258: }
0259: }
0260:
0261: return diagram;
0262: }
0263:
0264: /**
0265: * Reset the image buffer meaning that the image will be re-rendered the next time
0266: * the paintComponent method is called.
0267: */
0268: public void resetBuffer() {
0269: bufferedImage = null;
0270: }
0271:
0272: /**
0273: * Notify all listening components that the svg has been re-rendered.
0274: */
0275: public void notifyListeners() {
0276: firePropertyChange("bufferedImage", null, bufferedImage);
0277: }
0278:
0279: /**
0280: * Used to paint a buffered image of the rendered svg to the component.
0281: * @param g the delegate <CODE>Graphics</CODE> object.
0282: */
0283: public void paintComponent(Graphics g) {
0284: if ((previousUrl == null) || !url.equals(previousUrl))
0285: display();
0286:
0287: if (diagram != null) {
0288: Graphics2D g2d = (Graphics2D) g.create();
0289: getParent().setBackground(Color.white);
0290:
0291: if (bufferedImage == null) {
0292: SVGRoot root = diagram.getRoot();
0293: int[] attr = root.getPresAbsolute("viewBox")
0294: .getIntList();
0295: bufferedImage = new BufferedImage(getWidth(),
0296: getHeight(), BufferedImage.TYPE_INT_ARGB);
0297: Graphics2D buffer = bufferedImage.createGraphics();
0298: buffer.setRenderingHint(
0299: RenderingHints.KEY_ANTIALIASING,
0300: RenderingHints.VALUE_ANTIALIAS_ON);
0301: diagram.setIgnoringClipHeuristic(true);
0302:
0303: semaphore.acquire();
0304:
0305: try {
0306: root.render(buffer);
0307: } catch (Exception ex) {
0308: if (BuildProperties.DEBUG)
0309: ex.printStackTrace();
0310: }
0311:
0312: semaphore.release();
0313:
0314: buffer.dispose();
0315:
0316: bufferX = 0;
0317: bufferY = 0;
0318: }
0319:
0320: g2d.drawImage(bufferedImage, bufferX, bufferY, this );
0321:
0322: if (drawImages)
0323: drawAuxilaryImages(g2d);
0324:
0325: g2d.dispose();
0326: }
0327: }
0328:
0329: /**
0330: * Resizes the SVG image to fit the panel
0331: */
0332: public void resize() {
0333: if (diagram == null)
0334: return;
0335:
0336: SVGRoot root = diagram.getRoot(); // get header of svg file
0337:
0338: try {
0339: double width = root.getPresAbsolute("width")
0340: .getDoubleValue();
0341: double height = root.getPresAbsolute("height")
0342: .getDoubleValue();
0343: double pWidth = getWidth();
0344: double pHeight = getHeight();
0345:
0346: if ((w == 0) || (h == 0)) {
0347: w = (int) width;
0348: h = (int) height;
0349: }
0350:
0351: // if statement used to check aspect ratio of svg image
0352: if (((pHeight / h) * w) > pWidth) { // check if width is proportional to height
0353: pHeight = pWidth / w * h; // modify height to be proportional to width
0354: scaleX = width / pWidth; // calculate scale factor for image width
0355: scaleY = height / pHeight; // calculate scale factor for image height
0356:
0357: // calculate centre position for image along y-axis
0358: centreY = (((getHeight() - pHeight) * scaleY) / 2.0)
0359: * -1.0;
0360: centreX = 0.0;
0361: } else {
0362: pWidth = pHeight / h * w; // modify width to be proportional to height
0363: scaleX = width / pWidth; // calculate scale factor for image width
0364: scaleY = height / pHeight; // calculate scale factor for image height
0365:
0366: // calculate centre position for image along x-axis
0367: centreX = (((getWidth() - pWidth) * scaleX) / 2.0)
0368: * -1.0;
0369: centreY = 0.0;
0370: }
0371:
0372: if (preserveAspect) {
0373: scaleX = Math.max(scaleX, scaleY);
0374: scaleY = scaleX;
0375: }
0376:
0377: // set viewBox attributes (x,y,width,-height)
0378: String viewBox = formatter.format(centreX) + " "
0379: + formatter.format(centreY) + " "
0380: + formatter.format(width * scaleX) + " "
0381: + formatter.format(height * scaleY);
0382:
0383: root.setAttribute("viewBox", AnimationElement.AT_XML,
0384: viewBox);
0385: root.build(); // rebuild root of svg image
0386:
0387: resetBuffer();
0388: repaint();
0389: notifyListeners();
0390:
0391: firstZoom = true;
0392: initialX = centreX;
0393: initialY = centreY;
0394: previousPanX = 0;
0395: previousPanY = 0;
0396: panX = 0;
0397: panY = 0;
0398: } catch (Exception ex) {
0399: if (BuildProperties.DEBUG)
0400: ex.printStackTrace();
0401: }
0402: }
0403:
0404: /**
0405: * Used to pan the image based on percentage of the current x and y locations.
0406: * @param x <CODE>double</CODE> specifying the percentage to increase/decrease the x-coordinate by.
0407: * @param y <CODE>double</CODE> specifying the percentage to increase/decrease the y-coordinate by.
0408: */
0409: public void pan(double x, double y) {
0410: SVGRoot root = diagram.getRoot();
0411: double[] attr = root.getPresAbsolute("viewBox").getDoubleList();
0412:
0413: boolean negX = false;
0414: boolean negY = false;
0415:
0416: if (Double.toString(x).charAt(0) == '-') {
0417: x = Double.parseDouble(Double.toString(x).substring(1));
0418: negX = true;
0419: }
0420:
0421: if (Double.toString(y).charAt(0) == '-') {
0422: y = Double.parseDouble(Double.toString(y).substring(1));
0423: negY = true;
0424: }
0425:
0426: double difX = (attr[2] * x) - attr[2];
0427: double difY = (attr[3] * y) - attr[3];
0428:
0429: double newX, newY;
0430: if (negX)
0431: newX = attr[0] - difX;
0432: else
0433: newX = attr[0] + difX;
0434:
0435: if (negY)
0436: newY = attr[1] - difY;
0437: else
0438: newY = attr[1] + difY;
0439:
0440: String viewBox = formatter.format(newX) + " "
0441: + formatter.format(newY) + " " + attr[2] + " "
0442: + attr[3];
0443: try {
0444: root.setAttribute("viewBox", AnimationElement.AT_XML,
0445: viewBox);
0446: root.build();
0447: } catch (Exception ex) {
0448: if (BuildProperties.DEBUG)
0449: ex.printStackTrace();
0450: }
0451:
0452: if (!coalesce) {
0453: resetBuffer();
0454: repaint();
0455: notifyListeners();
0456: }
0457:
0458: panX = previousPanX + (newX - initialX);
0459: panY = previousPanY + (newY - initialY);
0460: }
0461:
0462: /**
0463: * Resets the image to its original displayed coordinates.
0464: */
0465: public void resetPanning() {
0466: resize();
0467: }
0468:
0469: /**
0470: * Zooms the image by the scaling factor specified.
0471: * @param zoom <CODE>double</CODE> specifying the scaling factor.
0472: */
0473: public void zoom(double zoom) {
0474: previousPanX = panX;
0475: previousPanY = panY;
0476:
0477: if (diagram == null)
0478: return;
0479:
0480: SVGRoot root = diagram.getRoot(); // get header of svg file
0481:
0482: try {
0483: double[] attr = root.getPresAbsolute("viewBox")
0484: .getDoubleList();
0485: double width = root.getPresAbsolute("width")
0486: .getDoubleValue();
0487: double height = root.getPresAbsolute("height")
0488: .getDoubleValue();
0489: double pWidth = getWidth();
0490: double pHeight = getHeight();
0491:
0492: double nonZoomWidth, nonZoomHeight, zW, zH;
0493:
0494: // if statement used to check aspect ratio of svg image
0495: if (((pHeight / height) * width) > pWidth) { // check if width is proportional to height
0496: pHeight = pWidth / width * height; // modify height to be proportional to width
0497: scaleX = width / pWidth; // calculate scale factor for image width
0498: scaleY = height / pHeight; // calculate scale factor for image height
0499:
0500: nonZoomWidth = width * scaleX;
0501: nonZoomHeight = height * scaleY;
0502:
0503: if (firstZoom) {
0504: zoomWidth = scaleX;
0505: zoomHeight = scaleY;
0506: firstZoom = false;
0507: }
0508:
0509: scaleX = zoomWidth / zoom;
0510: scaleY = zoomHeight / zoom;
0511: zoomWidth = scaleX;
0512: zoomHeight = scaleY;
0513:
0514: zW = width * scaleX;
0515: zH = height * scaleY;
0516:
0517: // calculate centre position for image along y-axis
0518: centreY = (((getHeight() - pHeight) * scaleY) / 2.0)
0519: * -1.0;
0520: centreX = 0.0;
0521: } else {
0522: pWidth = pHeight / height * width; // modify width to be proportional to height
0523: scaleX = width / pWidth; // calculate scale factor for image width
0524: scaleY = height / pHeight; // calculate scale factor for image height
0525:
0526: nonZoomWidth = width * scaleX;
0527: nonZoomHeight = height * scaleY;
0528:
0529: if (firstZoom) {
0530: zoomWidth = scaleX;
0531: zoomHeight = scaleY;
0532: firstZoom = false;
0533: }
0534:
0535: scaleX = zoomWidth / zoom;
0536: scaleY = zoomHeight / zoom;
0537: zoomWidth = scaleX;
0538: zoomHeight = scaleY;
0539:
0540: zW = width * scaleX;
0541: zH = height * scaleY;
0542:
0543: // calculate centre position for image along x-axis
0544: centreX = (((getWidth() - pWidth) * scaleX) / 2.0)
0545: * -1.0;
0546: centreY = 0.0;
0547: }
0548:
0549: centreX = centreX + panX + ((nonZoomWidth - zW) / 2.0)
0550: * (pWidth / width);
0551: centreY = centreY + panY + ((nonZoomHeight - zH) / 2.0)
0552: * (pHeight / height);
0553:
0554: if (preserveAspect) {
0555: scaleX = Math.max(scaleX, scaleY);
0556: scaleY = scaleX;
0557: }
0558:
0559: String viewBox = formatter.format(centreX) + " "
0560: + formatter.format(centreY) + " "
0561: + formatter.format(width * scaleX) + " "
0562: + formatter.format(height * scaleY);
0563:
0564: initialX = centreX;
0565: initialY = centreY;
0566:
0567: root.setAttribute("viewBox", AnimationElement.AT_XML,
0568: viewBox);
0569: root.build(); // rebuild root of svg image
0570:
0571: if (!coalesce) {
0572: resetBuffer();
0573: repaint();
0574: notifyListeners();
0575: }
0576: } catch (Exception ex) {
0577: if (BuildProperties.DEBUG)
0578: ex.printStackTrace();
0579: }
0580: }
0581:
0582: /**
0583: * Start coalesce, of panning and zooming actions.
0584: * The svg won't be repainted until the endCoalesce method is called.
0585: */
0586: public void startCoalesce() {
0587: coalesce = true;
0588: }
0589:
0590: /**
0591: * Stop coalescing, meaning the image will be repainted after the next pan or zoom action.
0592: */
0593: public void endCoalesce() {
0594: coalesce = false;
0595: }
0596:
0597: /**
0598: * Set position of where the pan method is to begin panning from in the next pan action.
0599: * @param x <CODE>double</CODE> specifying the x coordinate of where the image is to be panned from.
0600: * @param y <CODE>double</CODE> specifying the y coordinate of where the image is to be panned from.
0601: */
0602: public void setPanCoords(double x, double y) {
0603: panX = previousPanX + (x - initialX);
0604: panY = previousPanY + (y - initialY);
0605: }
0606:
0607: /**
0608: * Resets the image to its original zoomed view.
0609: */
0610: public void resetZoom() {
0611: resize();
0612: }
0613:
0614: /**
0615: * Set the auxilary images to be displayed when dragging the image map.
0616: * @param images <CODE>BufferedImage</CODE> array containing the auxilary images to be displayed.
0617: */
0618: public void setAuxilaryImages(BufferedImage[] images) {
0619: this .images = images;
0620: }
0621:
0622: /**
0623: * Draws the auxilary images within the image map component.
0624: * @param g2d the <CODE>Graphics2D</CODE> object used to draw the images.
0625: */
0626: public void drawAuxilaryImages(Graphics2D g2d) {
0627: if (images != null) {
0628: g2d.drawImage(images[0],
0629: (bufferX - (images[0].getWidth())),
0630: (int) (bufferY - (images[0].getHeight())), this );
0631: g2d.drawImage(images[1], bufferX, (bufferY - (images[1]
0632: .getHeight())), this );
0633: g2d.drawImage(images[2], (bufferX + getWidth()),
0634: (bufferY - (images[2].getHeight())), this );
0635:
0636: g2d
0637: .drawImage(images[3], (bufferX - (images[3]
0638: .getWidth())), (bufferY), this );
0639: g2d.drawImage(images[4], (bufferX + getWidth()), bufferY,
0640: this );
0641:
0642: g2d.drawImage(images[5], (bufferX - images[5].getWidth()),
0643: (bufferY + getHeight()), this );
0644: g2d.drawImage(images[6], bufferX, (bufferY + getHeight()),
0645: this );
0646: g2d.drawImage(images[7], (bufferX + getWidth()),
0647: (bufferY + getHeight()), this );
0648: }
0649: }
0650:
0651: /**
0652: * Sets whether the auxilary images are to be drawn or not.
0653: * @param b <CODE>boolean</CODE> specifying whether the auxilary images are to be drawn or not.
0654: */
0655: public void setDrawAuxImages(boolean b) {
0656: drawImages = b;
0657: }
0658:
0659: /**
0660: * Returns the semaphore used by this class to block on rendering events.
0661: * @return XRenderingSemaphore the semaphore instance used by this class.
0662: */
0663: public XRenderingSemaphore getSemaphore() {
0664: return semaphore;
0665: }
0666:
0667: /**
0668: * Set the location of the buffered image in the panel.
0669: * @param x the x coordinate of the buffered image.
0670: * @param y the y coordinate of the buffered image.
0671: */
0672: public void setImageLocation(int x, int y) {
0673: bufferX = x;
0674: bufferY = y;
0675: }
0676:
0677: /**
0678: * Sets the buffered image to be painted.
0679: * @param b <CODE>BufferedImage</CODE> set to true if movable image is to be repainted.
0680: */
0681: public void setImage(BufferedImage b) {
0682: bufferedImage = b;
0683: }
0684:
0685: /**
0686: * Returns the rendered buffered image of the svg displayed by this class
0687: * @return the returned <CODE>BufferedImage</CODE> instance
0688: */
0689: public BufferedImage getBufferedImage() {
0690: return bufferedImage;
0691: }
0692:
0693: /**
0694: * Returns the instance of the SVG diagram used to access the image data.
0695: */
0696: public SVGDiagram getSvgDiagram() {
0697: return diagram;
0698: }
0699:
0700: /**
0701: * Sets the viewbox at the specified coordinates and preserves the aspect ratio of the image.
0702: * @param x <CODE>double</CODE> specifying the x coordinate
0703: * @param y <CODE>double</CODE> specifying the y coordinate
0704: * @param width <CODE>double</CODE> specifying the width
0705: * @param height <CODE>double</CODE> specifying the height
0706: */
0707: public void setViewBox(double x, double y, double width,
0708: double height) {
0709: SVGRoot root = diagram.getRoot(); // get header of svg file
0710: double newWidth = 0, newHeight = 0;
0711: double pWidth = getWidth();
0712: double pHeight = getHeight();
0713: double startWidth = root.getPresAbsolute("width")
0714: .getDoubleValue();
0715: double startHeight = root.getPresAbsolute("height")
0716: .getDoubleValue();
0717:
0718: if (pWidth < (pHeight * (startHeight / startWidth))) {
0719: newWidth = (width * (startWidth / pWidth));
0720: newHeight = (newWidth * (startHeight / startWidth));
0721: y = y
0722: - (((newHeight - height) / (startHeight / pHeight)) / 2.0);
0723: } else {
0724: newHeight = (height * (startHeight / pHeight));
0725: newWidth = (newHeight * (startWidth / startHeight));
0726: x = x
0727: - (((newWidth - width) / (startWidth / pWidth)) / 2.0);
0728: }
0729:
0730: String viewBox = Integer.toString((int) (x)) + " "
0731: + Integer.toString((int) (y)) + " "
0732: + Integer.toString((int) (newWidth)) + " "
0733: + Integer.toString((int) (newHeight));
0734:
0735: try {
0736: root.setAttribute("viewBox", AnimationElement.AT_XML,
0737: viewBox);
0738: root.build(); // rebuild root of svg image
0739: resetBuffer();
0740: repaint();
0741: } catch (Exception ex) {
0742: if (BuildProperties.DEBUG)
0743: ex.printStackTrace();
0744: }
0745: }
0746:
0747: /**
0748: * Extracts Group objects from SVG file and
0749: * loads them into the corresponding arrays
0750: */
0751: public void loadArrays() {
0752: SVGRoot root = diagram.getRoot(); // get header of svg file
0753: Vector vector = new Vector();
0754: root.getChildren(vector);
0755:
0756: int bSize = 0;
0757:
0758: /**
0759: * Loop to remove non-Group objects from vector
0760: * and count amount of base groups and roll-over
0761: * groups for arrays
0762: */
0763:
0764: int loop = 0;
0765: int numElements = vector.size();
0766: String[] bases = new String[numElements];
0767: while (loop < vector.size()) {
0768: SVGElement element = (SVGElement) vector.get(loop);
0769: if (!(element instanceof Group))
0770: vector.remove(loop);
0771: else {
0772: String elementId = element.getId();
0773: if ((elementId.startsWith(blockPrefix))
0774: || (rolloverFinder != null && rolloverFinder
0775: .isRolloverName(elementId))) {
0776: bases[bSize] = elementId;
0777: bSize += 1;
0778: }
0779: loop++;
0780: }
0781: }
0782:
0783: /**
0784: * Extract base groups from vector and add to baseShapes array
0785: */
0786: baseShapes = new String[bSize];
0787: for (int i = 0; i < bSize; i++)
0788: baseShapes[i] = bases[i];
0789:
0790: bases = null;
0791:
0792: if (rolloverFinder != null)
0793: rolloverFinder.setup(vector, baseShapes);
0794: }
0795:
0796: /**
0797: * Sets the visibility of a group
0798: * @param group <code>String</code> specifying the SVG grouping to be displayed
0799: * @param v <code>boolean</code> specifying whether the group is to be set visible or hidden
0800: */
0801: public void setVisibility(String group, boolean v) {
0802: selected = (Group) diagram.getElement(group);
0803: try {
0804: selected.setAttribute("visibility",
0805: AnimationElement.AT_XML, v ? "visible" : "hidden");
0806: selected.updateTime(0.0);
0807:
0808: resetBuffer();
0809: repaint();
0810: } catch (Exception ex) {
0811: if (BuildProperties.DEBUG)
0812: ex.printStackTrace();
0813: }
0814: }
0815:
0816: /**
0817: * Monitors mouse movement and calls checkBlock method.
0818: * to check if the mouse pointer position is within an SVG group
0819: * @param e the <code>MouseEvent</code> that occured
0820: */
0821: public void mouseMoved(MouseEvent e) {
0822: if (setRollOvers) {
0823: try {
0824: if (baseShapes == null)
0825: return;
0826:
0827: if (displayGroup != null) // reset visible group to hidden
0828: setVisibility(displayGroup, false);
0829:
0830: int i = 0;
0831: boolean found = false;
0832: while ((i < baseShapes.length) && !found) { // loop through group names and check if
0833: if (checkBlock(e.getPoint(), baseShapes[i])) {
0834: if (rolloverFinder != null)
0835: displayGroup = rolloverFinder
0836: .getRolloverName(displayGroup);
0837: else
0838: displayGroup = rolloverPrefix
0839: + baseShapes[i]
0840: .substring(blockPrefix
0841: .length());
0842: found = true;
0843: }
0844: i++;
0845: }
0846:
0847: if (found == false) // if the mouse pointer is not within a group
0848: displayGroup = null;
0849:
0850: if (displayGroup != null) // display hidden group
0851: setVisibility(displayGroup, true);
0852: } catch (Exception ex) {
0853: if (BuildProperties.DEBUG)
0854: ex.printStackTrace();
0855: }
0856: }
0857:
0858: fireActionPerformed(e);
0859: }
0860:
0861: /**
0862: * Monitors mouse dragged events.
0863: * @param e the <code>MouseEvent</code> that occured
0864: */
0865: public void mouseDragged(MouseEvent e) {
0866: mouseMoved(e);
0867: }
0868:
0869: /**
0870: * Checks if the mouse pointer position is within a particular group
0871: * @param p <code>Point</code> specifying the current mouse pointer position
0872: * @param s <code>String</code> specifying the name of the group to be checked
0873: */
0874: private boolean checkBlock(Point p, String s) {
0875: Group group = (Group) diagram.getElement(s);
0876: Shape shape = group.getShape();
0877:
0878: /**
0879: * Checks if the mouse pointer is contained within the shape based on x-coordinate,
0880: * current scaling and centered position
0881: */
0882: if (shape.contains((p.getX() * scaleX + centreX), (p.getY()
0883: * scaleY + centreY)))
0884: return true;
0885:
0886: return false;
0887: }
0888:
0889: /**
0890: * Used to set the attributes of a componenent without knowing the individual accessor methods
0891: * <OL>
0892: * <LI>content, value=The SVG image to be displayed by the component or</LI>
0893: * <LI>imagename, value=The SVG image to be displayed by the component</LI>
0894: * <LI>opaque, value=true for an opaque image (fills the entire client area)</LI>
0895: * </OL>
0896: * @param attribName <code>String</code> specifying the attribute name
0897: * @param attribValue <code>Object</code> specifying the attribute value
0898: */
0899: public int setAttribute(String attribName, Object attribValue) {
0900: String attribNameLwr = attribName.toLowerCase();
0901: String attribValueLwr = ((String) attribValue).toLowerCase();
0902:
0903: if (attribNameLwr.equals("content")
0904: || attribNameLwr.equals("imagename"))
0905: setURL(currentProject.findResource(attribValue.toString()));
0906: else if (attribName.equals("opaque"))
0907: setOpaque(attribValueLwr.equals("true"));
0908:
0909: return 0;
0910: }
0911:
0912: /**
0913: * Set the content of the image map.
0914: * @param attribValue <CODE>Object</CODE> specifying the content.
0915: */
0916: public void setImageName(String attribValue) {
0917: setAttribute("content", attribValue);
0918: }
0919:
0920: /**
0921: * Returns the content of the image map.
0922: * @return <CODE>String</CODE> specifying the content value.
0923: */
0924: public String getImageName() {
0925: if (url != null) {
0926: String name = url.toString();
0927: name = name.substring(name.lastIndexOf("/") + 1);
0928: return name;
0929: }
0930:
0931: return null;
0932: }
0933:
0934: /**
0935: * Returns the url of the image displayed in the image map.
0936: * @return <CODE>String</CODE> specifying the content value.
0937: */
0938: public String getUrl() {
0939: if (url != null)
0940: return url.toString();
0941:
0942: return null;
0943: }
0944:
0945: /**
0946: * Allow rollovers or not based on boolean value.
0947: * @param setRollOvers <CODE>boolean</CODE> used to set if rollovers are to be allowed or not.
0948: */
0949: public void setRollOverStatus(boolean setRollOvers) {
0950: this .setRollOvers = setRollOvers;
0951: }
0952:
0953: /**
0954: * Return the scaleFactor currently being applied to the x-axis.
0955: */
0956: public double getScaleX() {
0957: return scaleX;
0958: }
0959:
0960: /**
0961: * Return the scaleFactor currently being applied to the y-axis.
0962: */
0963: public double getScaleY() {
0964: return scaleY;
0965: }
0966:
0967: /**
0968: * Return the center position of the image on the x-axis.
0969: */
0970: public double getCenterX() {
0971: return centreX;
0972: }
0973:
0974: /**
0975: * Return the center position of the image on the y-axis.
0976: */
0977: public double getCenterY() {
0978: return centreY;
0979: }
0980:
0981: /**
0982: * Adds a mouse listener to this component to listen for mouse events.
0983: * @param l the <CODE>MouseListener</CODE> that is to be added.
0984: */
0985: public void addMapMouseListener(MouseListener l) {
0986: addMouseListener(l);
0987: super .addMouseListener(this );
0988:
0989: listeners.add(l);
0990: listeners.add(this );
0991: }
0992:
0993: /**
0994: * Removes a mouse listener to this component.
0995: * @param l the <CODE>MouseListener</CODE> that is to be removed.
0996: */
0997: public void removeMapMouseListener(MouseListener l) {
0998: removeMouseListener(l);
0999: super .removeMouseListener(this );
1000:
1001: listeners.remove(l);
1002: listeners.remove(this );
1003: }
1004:
1005: /**
1006: * Detect if mouse has exited the component and reset visible rollove to hidden.
1007: */
1008: public void mouseExited(MouseEvent e) {
1009: if (displayGroup != null) // reset visible group to hidden
1010: setVisibility(displayGroup, false);
1011:
1012: fireActionPerformed(e);
1013: }
1014:
1015: public void mouseClicked(MouseEvent e) {
1016: fireActionPerformed(e);
1017: }
1018:
1019: public void mousePressed(MouseEvent e) {
1020: fireActionPerformed(e);
1021: }
1022:
1023: public void mouseReleased(MouseEvent e) {
1024: fireActionPerformed(e);
1025: }
1026:
1027: public void mouseEntered(MouseEvent e) {
1028: fireActionPerformed(e);
1029: }
1030:
1031: /**
1032: * Used to add an <code>ActionListener</code> to the list of action listeners.
1033: * @param al the listener to be added
1034: */
1035: public void addActionListener(ActionListener al) {
1036: listenerList.add(ActionListener.class, al);
1037: }
1038:
1039: /**
1040: * Removes an <code>ActionListener</code> from the button.
1041: * If the listener is the currently set <code>Action</code>
1042: * for the button, then the <code>Action</code>
1043: * is set to <code>null</code>.
1044: *
1045: * @param l the listener to be removed
1046: */
1047: public void removeActionListener(ActionListener l) {
1048: listenerList.remove(ActionListener.class, l);
1049: }
1050:
1051: /**
1052: * Notifies all mouse listeners that have registered interest for
1053: * notification on mouse event types. The event instance
1054: * is lazily created using the <code>event</code>
1055: * parameter.
1056: *
1057: * @param event the <code>MouseEvent</code> object
1058: */
1059: protected void fireActionPerformed(MouseEvent event) {
1060: // Guaranteed to return a non-null array
1061: MouseEvent e = null;
1062: // Process the listeners last to first, notifying
1063: // those that are interested in this event
1064: for (int i = 0; i < listeners.size(); i++) {
1065: if (listeners.get(i) instanceof MouseListener) {
1066: // Lazily create the event:
1067: if (e == null) {
1068: switch (event.getID()) {
1069: case MouseEvent.MOUSE_ENTERED:
1070: e = new MouseEvent(XSvgImageMap.this ,
1071: MouseEvent.MOUSE_MOVED, 0, 0, 0, 0, 0,
1072: false);
1073: ((MouseListener) listeners.get(i))
1074: .mouseEntered(e);
1075: case MouseEvent.MOUSE_EXITED:
1076: e = new MouseEvent(XSvgImageMap.this ,
1077: MouseEvent.MOUSE_EXITED, 0, 0, 0, 0, 0,
1078: false);
1079: ((MouseListener) listeners.get(i))
1080: .mouseExited(e);
1081: case MouseEvent.MOUSE_RELEASED:
1082: e = new MouseEvent(XSvgImageMap.this ,
1083: MouseEvent.MOUSE_RELEASED, 0, 0, 0, 0,
1084: 0, false);
1085: ((MouseListener) listeners.get(i))
1086: .mouseReleased(e);
1087: case MouseEvent.MOUSE_PRESSED:
1088: e = new MouseEvent(XSvgImageMap.this ,
1089: MouseEvent.MOUSE_PRESSED, 0, 0, 0, 0,
1090: 0, false);
1091: ((MouseListener) listeners.get(i))
1092: .mousePressed(e);
1093: case MouseEvent.MOUSE_CLICKED:
1094: e = new MouseEvent(XSvgImageMap.this ,
1095: MouseEvent.MOUSE_CLICKED, 0, 0, 0, 0,
1096: 0, false);
1097: ((MouseListener) listeners.get(i))
1098: .mouseClicked(e);
1099: }
1100: }
1101: }
1102: }
1103: }
1104:
1105: /**
1106: * Notifies all listeners that have registered interest for
1107: * notification on this event type. The event instance
1108: * is lazily created using the <code>event</code>
1109: * parameter.
1110: *
1111: * @param event the <code>ActionEvent</code> object
1112: * @see EventListenerList
1113: */
1114: protected void fireActionPerformed(ActionEvent event) {
1115: // Guaranteed to return a non-null array
1116: Object[] listeners = listenerList.getListenerList();
1117: ActionEvent e = null;
1118: // Process the listeners last to first, notifying
1119: // those that are interested in this event
1120: for (int i = listeners.length - 2; i >= 0; i -= 2) {
1121: if (listeners[i] == ActionListener.class) {
1122: // Lazily create the event:
1123: if (e == null) {
1124: String actionCommand = event.getActionCommand();
1125: e = new ActionEvent(XSvgImageMap.this ,
1126: ActionEvent.ACTION_PERFORMED,
1127: actionCommand, event.getWhen(), event
1128: .getModifiers());
1129: }
1130: ((ActionListener) listeners[i + 1]).actionPerformed(e);
1131: }
1132: }
1133: }
1134:
1135: /**
1136: * Returns the id of the rollover block currently selected.
1137: * @param e the triggered <CODE>MouseEvent</CODE>.
1138: * @return the id <CODE>String</CODE>.
1139: */
1140: public String getSelectedRollover(MouseEvent e) {
1141: if (selected != null) {
1142: if (checkBlock(e.getPoint(), selected.getId())) {
1143: return selected.getId();
1144: }
1145: }
1146:
1147: return null;
1148: }
1149:
1150: /**
1151: * Does the component preserve the aspect ratio of the image?
1152: * @return true if the aspect ratio is preserved.
1153: */
1154: public boolean getPreserveAspect() {
1155: return preserveAspect;
1156: }
1157:
1158: /**
1159: * Set the aspect ratio preservation flag
1160: * @param preserve true to preserve the aspect
1161: */
1162: public void setPreserveAspect(boolean preserve) {
1163: preserveAspect = preserve;
1164: }
1165: }
|