001: /*
002: * $Id: JGraphEditorComboBox.java,v 1.2 2007/03/25 13:13:16 gaudenz Exp $
003: * Copyright (c) 2001-2005, Gaudenz Alder
004: *
005: * All rights reserved.
006: *
007: * See LICENSE file for license details. If you are unable to locate
008: * this file please contact info (at) jgraph (dot) com.
009: */
010: package com.jgraph.editor.factory;
011:
012: import java.awt.Color;
013: import java.awt.Component;
014: import java.awt.Dimension;
015: import java.awt.Graphics;
016: import java.awt.Point;
017: import java.awt.event.ActionEvent;
018: import java.awt.event.ActionListener;
019: import java.awt.geom.Rectangle2D;
020: import java.util.Hashtable;
021: import java.util.LinkedList;
022: import java.util.List;
023: import java.util.Map;
024:
025: import javax.swing.BorderFactory;
026: import javax.swing.Icon;
027: import javax.swing.JComboBox;
028: import javax.swing.JComponent;
029: import javax.swing.JLabel;
030: import javax.swing.JList;
031: import javax.swing.ListCellRenderer;
032: import javax.swing.border.Border;
033:
034: import org.jgraph.JGraph;
035: import org.jgraph.graph.AttributeMap;
036: import org.jgraph.graph.CellView;
037: import org.jgraph.graph.CellViewRenderer;
038: import org.jgraph.graph.DefaultGraphModel;
039: import org.jgraph.graph.EdgeRenderer;
040: import org.jgraph.graph.EdgeView;
041: import org.jgraph.graph.GraphConstants;
042: import org.jgraph.graph.VertexView;
043: import org.w3c.dom.Node;
044:
045: import com.jgraph.editor.JGraphEditorAction;
046:
047: /**
048: * Combo box for selecting cell styles. The class provides a series of factory
049: * methods to be added to an editor factory. The following methods are provided:
050: * {@link BorderComboFactoryMethod}, {@link LineDecorationComboFactoryMethod}
051: * and {@link LineWidthComboFactoryMethod}.
052: */
053: public class JGraphEditorComboBox extends JComboBox {
054:
055: /**
056: * Default array of borders.
057: */
058: public static Border[] defaultBorders = new Border[] {
059: BorderFactory.createRaisedBevelBorder(),
060: BorderFactory.createLoweredBevelBorder(),
061: BorderFactory.createEtchedBorder(),
062: BorderFactory.createLineBorder(Color.black) };
063:
064: /**
065: * Default array of widths.
066: */
067: public static float[] defaultWidths = new float[] { 1, 2, 3, 4, 5,
068: 6, 8 };
069:
070: /**
071: * Default array of patterns.
072: */
073: protected static float[][] defaultPatterns = new float[][] {
074: new float[] { 1, 1 }, new float[] { 2, 2 },
075: new float[] { 4, 2 }, new float[] { 4, 4 },
076: new float[] { 8, 2 }, new float[] { 8, 4 },
077: new float[] { 4, 4, 16, 4 } };
078:
079: /**
080: * Default array of fillable arrows.
081: */
082: public static int[] defaultFillableDecorations = new int[] {
083: GraphConstants.ARROW_CLASSIC,
084: GraphConstants.ARROW_TECHNICAL,
085: GraphConstants.ARROW_DIAMOND, GraphConstants.ARROW_CIRCLE };
086:
087: /**
088: * Default array or arrows.
089: */
090: public static int[] defaultDecorations = new int[] {
091: GraphConstants.ARROW_CLASSIC, GraphConstants.ARROW_SIMPLE,
092: GraphConstants.ARROW_TECHNICAL,
093: GraphConstants.ARROW_DIAMOND, GraphConstants.ARROW_CIRCLE,
094: GraphConstants.ARROW_LINE, GraphConstants.ARROW_DOUBLELINE };
095:
096: /**
097: * Shared graph instance for rendering.
098: */
099: protected static JGraph backingGraph = new JGraph(
100: new DefaultGraphModel());
101:
102: /**
103: * Initial view attributes for restoring.
104: */
105: protected AttributeMap initialAttributes;
106:
107: /**
108: * Constructs a new combo box for the specified attribute maps. Uses an edge
109: * view for preview of <code>edgePreview</code> is true, otherwise a
110: * vertex view is used.
111: *
112: * @param attributes
113: * The array of attributes for the combo box entries.
114: * @param edgePreview
115: * Whether to use an edge for preview.
116: */
117: public JGraphEditorComboBox(Map[] attributes, boolean edgePreview) {
118: this (attributes, null, edgePreview);
119: }
120:
121: /**
122: * Constructs a new combo box for the specified attribute maps. Uses an edge
123: * view for preview if <code>edgePreview</code> is true, otherwise a
124: * vertex view is used.
125: *
126: * @param attributes
127: * The array of attributes for the combo box entries.
128: * @param view
129: * The cell view to use for previewing the attributes.
130: * @param edgePreview
131: * Whether to use an edge for preview.
132: */
133: public JGraphEditorComboBox(Map[] attributes, CellView view,
134: boolean edgePreview) {
135: super (attributes);
136: setOpaque(false);
137: if (view == null)
138: view = (edgePreview) ? createEdgeView()
139: : createVertexView();
140: initialAttributes = new AttributeMap(view.getAttributes());
141: GraphConstants.setRemoveAll(initialAttributes, true);
142: setRenderer(new CellViewRendererBridge(view));
143: setMinimumSize(new Dimension(44, 20));
144: setPreferredSize(new Dimension(44, 20));
145: setMaximumSize(new Dimension(44, 20));
146: }
147:
148: /**
149: * Returns a new vertex to be used for preview. This implementation returns
150: * a {@link VertexView} with a string user object.
151: *
152: * @return Returns a new vertex view for preview.
153: */
154: protected CellView createVertexView() {
155: VertexView view = new VertexView("");
156: Dimension dim = getVertexViewSize();
157: Rectangle2D bounds = new Rectangle2D.Double(0, 0, dim.width,
158: dim.height);
159: GraphConstants.setBounds(view.getAttributes(), bounds);
160: return view;
161: }
162:
163: /**
164: * Hook for subclassers to modify the size of the vertex view.
165: *
166: * @return Returns the size of the vertex view.
167: */
168: protected Dimension getVertexViewSize() {
169: return new Dimension(18, 18);
170: }
171:
172: /**
173: * Returns a new edge to be used for preview. This implementation returns a
174: * {@link JGraphComboEdgeView} with a string user object and a set of
175: * default points.
176: *
177: * @return Returns a new edge view for preview.
178: */
179: protected CellView createEdgeView() {
180: EdgeView view = new JGraphComboEdgeView(" ");
181: List points = new LinkedList();
182: points.add(new Point(2, 6));
183: points.add(new Point(14, 6));
184: GraphConstants.setPoints(view.getAttributes(), points);
185: GraphConstants.setBeginSize(view.getAttributes(), 8);
186: GraphConstants.setEndSize(view.getAttributes(), 8);
187: return view;
188: }
189:
190: /**
191: * Returns the backing graph which is used for rendering previews.
192: *
193: * @return Returns the backing graph.
194: */
195: public static JGraph getBackingGraph() {
196: return backingGraph;
197: }
198:
199: /**
200: * Sets the backing graph which should be used for rendering previews.
201: *
202: * @param backingGraph
203: * The backing graph to set.
204: */
205: public static void setBackingGraph(JGraph backingGraph) {
206: JGraphEditorComboBox.backingGraph = backingGraph;
207: }
208:
209: /**
210: * This class returns the renderer of the view configured for the selected
211: * combo box item. Note: If the map contains an icon then this renderer (a
212: * JLabel) with the icon is returned.
213: */
214: public class CellViewRendererBridge extends JLabel implements
215: ListCellRenderer {
216:
217: /**
218: * Reference to the view this renderer bridge uses for rendering.
219: */
220: protected CellView view = null;
221:
222: /**
223: * Constructs a new renderer bridge using <code>view</code> to render
224: * the list entries.
225: *
226: * @param view
227: * The view to renderer the list entries with.
228: */
229: public CellViewRendererBridge(CellView view) {
230: setHorizontalAlignment(CENTER);
231: setVerticalAlignment(CENTER);
232: this .view = view;
233: }
234:
235: /**
236: * Returns a configured renderer for the specified value.
237: *
238: * @param list
239: * The list that contains the entry.
240: * @param value
241: * The value to be rendered.
242: * @param index
243: * The index of the value.
244: * @param isSelected
245: * Whether the value should be rendered as selected.
246: * @param hasFocus
247: * Whether the value should be renderer as focused.
248: * @return Returns a renderer for the specified value.
249: */
250: public Component getListCellRendererComponent(JList list,
251: Object value, int index, boolean isSelected,
252: boolean hasFocus) {
253: Icon icon = null;
254: if (value instanceof Map) {
255: Map map = (Map) value;
256: icon = GraphConstants.getIcon(map);
257: if (icon == null) {
258: view.getAllAttributes().clear();
259: view.changeAttributes(backingGraph
260: .getGraphLayoutCache(), initialAttributes);
261: view.changeAttributes(backingGraph
262: .getGraphLayoutCache(), map);
263: }
264: }
265: if (icon != null) {
266: setIcon(icon);
267: return this ;
268: } else {
269: Component r = view.getRendererComponent(backingGraph,
270: isSelected, hasFocus, false);
271: if (r instanceof JComponent) {
272: final JComponent c = (JComponent) r;
273: final Dimension d = new Dimension((int) view
274: .getBounds().getWidth(), (int) view
275: .getBounds().getHeight());
276:
277: // Returns an outer renderer that may be freely sized by the
278: // combo rendering process. The component paints the cell
279: // view renderer at the size of the contained cell view.
280: JComponent wrapper = new JComponent() {
281:
282: /**
283: * Paints the cell view renderer at the correct size.
284: *
285: * @param g
286: * The graphics to paint the inner renderer
287: * on.
288: */
289: public void paint(Graphics g) {
290: c.setBounds(0, 0, d.width, d.height);
291: c.paint(g);
292: }
293:
294: };
295: wrapper.setMinimumSize(d);
296: wrapper.setPreferredSize(d);
297: return wrapper;
298: }
299: return r;
300: }
301: }
302: }
303:
304: /**
305: * This class provides a an edge view with a special renderer.
306: */
307: protected static class JGraphComboEdgeView extends EdgeView {
308:
309: /**
310: * Holds the custom renderer.
311: */
312: protected static CellViewRenderer renderer = new JGraphComboEdgeRenderer();
313:
314: /**
315: * Constructs an empty vertex view.
316: */
317: public JGraphComboEdgeView() {
318: super ();
319: }
320:
321: /**
322: * Constructs a new combo edge view for the specified cell.
323: *
324: * @param cell
325: * The cell to create the edge view for.
326: */
327: public JGraphComboEdgeView(Object cell) {
328: super (cell);
329: }
330:
331: /**
332: * Returns the custom renderer for this view.
333: *
334: * @return Returns the custom renderer.
335: */
336: public CellViewRenderer getRenderer() {
337: return renderer;
338: }
339:
340: }
341:
342: /**
343: * This class removes the translate call in the paint method. This is
344: * required to render a list cell.
345: */
346: protected static class JGraphComboEdgeRenderer extends EdgeRenderer {
347:
348: /**
349: * Overrides parent method to avoid translating the graphics before
350: * painting.
351: *
352: * @param g
353: * The graphics object to avoid translation for.
354: */
355: protected void translateGraphics(Graphics g) {
356: // do not translate
357: }
358: }
359:
360: /**
361: * Provides a factory method to construct a border combo box.
362: */
363: public static class BorderComboFactoryMethod extends
364: JGraphEditorFactoryMethod {
365:
366: /**
367: * Defines the default name for factory methods of this kind.
368: */
369: public static String NAME = "createBorderCombo";
370:
371: /**
372: * Constructs a new border combo factory method using {@link #NAME}.
373: */
374: public BorderComboFactoryMethod() {
375: super (NAME);
376: }
377:
378: /**
379: * Returns a new border combo box for <code>configuration</code>.
380: *
381: * @param configuration
382: * The configuration to use for creating the combo box.
383: */
384: public Component createInstance(Node configuration) {
385: Map[] attrs = new Hashtable[defaultBorders.length + 1];
386: attrs[0] = new Hashtable();
387: GraphConstants
388: .setRemoveAttributes(attrs[0], new Object[] {
389: GraphConstants.BORDER,
390: GraphConstants.BORDERCOLOR });
391: for (int i = 0; i < defaultBorders.length; i++) {
392: attrs[i + 1] = new Hashtable(2);
393: GraphConstants.setBorder(attrs[i + 1],
394: defaultBorders[i]);
395: }
396: JGraphEditorComboBox comboBox = new JGraphEditorComboBox(
397: attrs, false);
398: comboBox.addActionListener(new ComboBoxListener());
399: comboBox.setFocusable(false);
400: return comboBox;
401: }
402:
403: }
404:
405: /**
406: * Provides a factory method to construct a linewidth combo box.
407: */
408: public static class LineWidthComboFactoryMethod extends
409: JGraphEditorFactoryMethod {
410:
411: /**
412: * Defines the default name for factory methods of this kind.
413: */
414: public static String NAME = "createLineWidthCombo";
415:
416: /**
417: * Constructs a new line width combo factory method using {@link #NAME}.
418: */
419: public LineWidthComboFactoryMethod() {
420: super (NAME);
421: }
422:
423: /**
424: * Returns a new linewidth combo box for <code>configuration</code>.
425: *
426: * @param configuration
427: * The configuration to use for creating the combo box.
428: */
429: public Component createInstance(Node configuration) {
430: Map[] attrs = new Hashtable[defaultWidths.length];
431: for (int i = 0; i < defaultWidths.length; i++) {
432: attrs[i] = new Hashtable(2);
433: GraphConstants.setLineWidth(attrs[i], defaultWidths[i]);
434: }
435: JGraphEditorComboBox comboBox = new JGraphEditorComboBox(
436: attrs, true);
437: comboBox.addActionListener(new ComboBoxListener());
438: comboBox.setFocusable(false);
439: return comboBox;
440: }
441:
442: }
443:
444: /**
445: * Provides a factory method to construct a dashpattern combo box.
446: */
447: public static class DashPatternComboFactoryMethod extends
448: JGraphEditorFactoryMethod {
449:
450: /**
451: * Defines the default name for factory methods of this kind.
452: */
453: public static String NAME = "createDashPatternCombo";
454:
455: /**
456: * Constructs a new dash pattern combo factory method using
457: * {@link #NAME}.
458: */
459: public DashPatternComboFactoryMethod() {
460: super (NAME);
461: }
462:
463: /**
464: * Returns a new dashpattern combo box for <code>configuration</code>.
465: *
466: * @param configuration
467: * The configuration to use for creating the combo box.
468: */
469: public Component createInstance(Node configuration) {
470: Map[] attrs = new Hashtable[defaultPatterns.length + 1];
471: attrs[0] = new Hashtable();
472: GraphConstants.setRemoveAttributes(attrs[0],
473: new Object[] { GraphConstants.DASHPATTERN });
474: for (int i = 0; i < defaultPatterns.length; i++) {
475: attrs[i + 1] = new Hashtable(2);
476: GraphConstants.setDashPattern(attrs[i + 1],
477: defaultPatterns[i]);
478: }
479: JGraphEditorComboBox comboBox = new JGraphEditorComboBox(
480: attrs, true);
481: comboBox.addActionListener(new ComboBoxListener());
482: comboBox.setFocusable(false);
483: return comboBox;
484: }
485:
486: }
487:
488: /**
489: * Provides a factory method to construct a line decoration combo box.
490: */
491: public static class LineDecorationComboFactoryMethod extends
492: JGraphEditorFactoryMethod {
493:
494: /**
495: * Defines the default name for factory methods of this kind.
496: */
497: public static String NAME = "createLineDecorationCombo";
498:
499: /**
500: * Constructs a new line decoration combo factory method using
501: * {@link #NAME}.
502: */
503: public LineDecorationComboFactoryMethod() {
504: super (NAME);
505: }
506:
507: /**
508: * Returns a new line decoration combo box for
509: * <code>configuration</code>.
510: *
511: * @param configuration
512: * The configuration to use for creating the combo box.
513: */
514: public Component createInstance(Node configuration) {
515: int all = defaultDecorations.length
516: + defaultFillableDecorations.length;
517: Map[] attrs = new Hashtable[2 * all + 1];
518: attrs[0] = new Hashtable();
519: GraphConstants.setRemoveAttributes(attrs[0], new Object[] {
520: GraphConstants.LINEBEGIN, GraphConstants.LINEEND,
521: GraphConstants.BEGINFILL, GraphConstants.ENDFILL });
522:
523: // Adds end decorations, not-filled and filled
524: for (int i = 0; i < defaultPatterns.length; i++) {
525: Map m = attrs[i + 1] = new Hashtable(2);
526: GraphConstants.setLineEnd(m, defaultDecorations[i]);
527: GraphConstants.setEndFill(m, false);
528: }
529: for (int i = 0; i < defaultFillableDecorations.length; i++) {
530: Map m = attrs[defaultDecorations.length + i + 1] = new Hashtable(
531: 2);
532: GraphConstants.setLineEnd(m,
533: defaultFillableDecorations[i]);
534: GraphConstants.setEndFill(m, true);
535: }
536:
537: // Adds begin decorations, not-filled and filled
538: for (int i = 0; i < defaultPatterns.length; i++) {
539: Map m = attrs[all + i + 1] = new Hashtable(2);
540: GraphConstants.setLineBegin(m, defaultDecorations[i]);
541: GraphConstants.setBeginFill(m, false);
542: }
543: for (int i = 0; i < defaultFillableDecorations.length; i++) {
544: Map m = attrs[defaultDecorations.length + all + i + 1] = new Hashtable(
545: 2);
546: GraphConstants.setLineBegin(m,
547: defaultFillableDecorations[i]);
548: GraphConstants.setBeginFill(m, true);
549: }
550:
551: // Constructs and returns the combo box
552: JGraphEditorComboBox comboBox = new JGraphEditorComboBox(
553: attrs, true);
554: comboBox.addActionListener(new ComboBoxListener());
555: comboBox.setFocusable(false);
556: return comboBox;
557: }
558:
559: }
560:
561: /**
562: * This class edits the selection of the focused graph according to the
563: * selected combo box item.
564: */
565: public static class ComboBoxListener implements ActionListener {
566:
567: /**
568: * Redirects selection of a combo box item to changing the respective
569: * attrbiutes on the focused graph.
570: *
571: * @param e
572: * The object that describes the event.
573: */
574: public void actionPerformed(ActionEvent e) {
575: JComboBox sender = (JComboBox) e.getSource();
576:
577: // Gets focused component before showing dialogs.
578: Component component = JGraphEditorAction
579: .getPermanentFocusOwner();
580: Object userObject = getSelection(sender);
581: if (userObject instanceof Map) {
582:
583: // Edits cells in the focused graph
584: if (component instanceof JGraph) {
585: JGraph graph = (JGraph) component;
586: graph.getGraphLayoutCache().edit(
587: graph.getSelectionCells(),
588: new Hashtable((Map) userObject));
589: }
590: }
591: }
592:
593: /**
594: * Hook for subclassers to process the selection and return and map for
595: * the default implementation to process.
596: *
597: * @param box
598: * The combo box to return the selected item for.
599: * @return Returns the selected item in <code>box</code>.
600: */
601: protected Object getSelection(JComboBox box) {
602: return box.getSelectedItem();
603: }
604:
605: }
606:
607: }
|