1   /****************************************************************************
2    * This demo file is part of yFiles for Java 2.14.
3    * Copyright (c) 2000-2017 by yWorks GmbH, Vor dem Kreuzberg 28,
4    * 72070 Tuebingen, Germany. All rights reserved.
5    * 
6    * yFiles demo files exhibit yFiles for Java functionalities. Any redistribution
7    * of demo files in source code or binary form, with or without
8    * modification, is not permitted.
9    * 
10   * Owners of a valid software license for a yFiles for Java version that this
11   * demo is shipped with are allowed to use the demo source code as basis
12   * for their own yFiles for Java powered applications. Use of such programs is
13   * governed by the rights and conditions as set out in the yFiles for Java
14   * license agreement.
15   * 
16   * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED
17   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19   * NO EVENT SHALL yWorks BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21   * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   *
27   ***************************************************************************/
28  package demo.layout.labeling;
29  
30  import demo.view.DemoBase;
31  import demo.view.DemoDefaults;
32  import y.base.Edge;
33  import y.base.EdgeCursor;
34  import y.base.Graph;
35  import y.layout.EdgeLabelLayout;
36  import y.layout.EdgeLabelModel;
37  import y.layout.RotatedDiscreteEdgeLabelModel;
38  import y.layout.RotatedSliderEdgeLabelModel;
39  import y.layout.labeling.AbstractLabelingAlgorithm;
40  import y.layout.labeling.GreedyMISLabeling;
41  import y.layout.labeling.MISLabelingAlgorithm;
42  import y.option.CompoundEditor;
43  import y.option.ConstraintManager;
44  import y.option.DefaultEditorFactory;
45  import y.option.Editor;
46  import y.option.EditorFactory;
47  import y.option.ItemEditor;
48  import y.option.OptionGroup;
49  import y.option.OptionHandler;
50  import y.util.DataProviderAdapter;
51  import y.view.DefaultLabelConfiguration;
52  import y.view.EdgeLabel;
53  import y.view.EditMode;
54  import y.view.Graph2D;
55  import y.view.Graph2DLayoutExecutor;
56  import y.view.Graph2DViewActions;
57  import y.view.PopupMode;
58  import y.view.SmartEdgeLabelModel;
59  import y.view.YLabel;
60  
61  import javax.swing.AbstractAction;
62  import javax.swing.JComponent;
63  import javax.swing.JMenu;
64  import javax.swing.JMenuBar;
65  import javax.swing.JPanel;
66  import javax.swing.JPopupMenu;
67  import javax.swing.JToolBar;
68  import java.awt.BorderLayout;
69  import java.awt.Color;
70  import java.awt.Dimension;
71  import java.awt.EventQueue;
72  import java.awt.event.ActionEvent;
73  import java.beans.PropertyChangeEvent;
74  import java.beans.PropertyChangeListener;
75  import java.net.URL;
76  import java.util.ArrayList;
77  import java.util.HashMap;
78  import java.util.Iterator;
79  import java.util.List;
80  import java.util.Locale;
81  import java.util.Map;
82  
83  /**
84   * This demo shows how to configure edge labels and the corresponding edge label models as well as how to apply the
85   * generic edge labeling algorithm.
86   * <p/>
87   * To add a new edge label right-click on an edge and choose item "Add Label". The properties of an existing edge label
88   * (i.e., its label text and its preferred placement) can be changed by right-click on the label and choose item "Edit
89   * Properties". Edge labels can be moved to another valid position according to the current label model by using drag
90   * and drop.
91   * <p/>
92   * The demo allows to switch between two sample graphs (using the combo box in the toolbar), i.e., a graph drawn with an
93   * orthogonal layout algorithm as well as a graph drawn with an organic layout algorithm. To manually start the generic
94   * labeling algorithm click on the "Do Generic Labeling" button. Note: after changing one of the properties stated
95   * below, the generic labeling algorithm is applied automatically.
96   *
97   * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/labeling#labeling" target="_blank">Section Automatic Label Placement</a> in the yFiles for Java Developer's Guide
98   */
99  public class EdgeLabelingDemo extends DemoBase {
100   //option handler texts
101   private static final String PROPERTIES_GROUP = "Edge Label Properties";
102   private static final String ROTATION_ANGLE_STRING = "Rotation Angle (Degrees)";
103   private static final String LABELING_MODEL_STRING = "Labeling Model";
104   private static final String ALLOW_90_DEGREE_DEVIATION_STRING = "Allow 90 Degree Deviation";
105   private static final String AUTO_FLIPPING_STRING = "Auto Flipping";
106   private static final String AUTO_ROTATE_STRING = "Auto Rotation";
107   private static final String EDGE_TO_LABEL_DISTANCE_STRING = "Edge To Label Distance";
108 
109   //edge label model constants
110   private static final String MODEL_CENTERED = "Centered";
111   private static final String MODEL_TWO_POS = "2 Pos";
112   private static final String MODEL_SIX_POS = "6 Pos";
113   private static final String MODEL_THREE_POS_CENTER = "3 Pos Center";
114   private static final String MODEL_CENTER_SLIDER = "Center Slider";
115   private static final String MODEL_SIDE_SLIDER = "Side Slider";
116   private static final String[] EDGE_LABEL_MODELS = {
117       MODEL_CENTERED, MODEL_TWO_POS, MODEL_SIX_POS, MODEL_THREE_POS_CENTER, MODEL_CENTER_SLIDER, MODEL_SIDE_SLIDER
118   };
119 
120   private static final String CUSTOM_LABELING_CONFIG_NAME = "CUSTOM_LABELING_CONFIG";
121   private static final Color LABEL_LINE_COLOR = new Color(153, 204, 255, 255);
122   static final Color LABEL_BACKGROUND_COLOR = Color.WHITE;
123   private static final int TOOLS_PANEL_WIDTH = 350;
124 
125   private DefaultLabelConfiguration customLabelConfig;
126   private Map label2Model;
127   private final OptionHandler optionHandler;
128 
129   public EdgeLabelingDemo() {
130     this(null);
131   }
132 
133   public EdgeLabelingDemo(final String helpFilePath) {
134     //set view size and create content pane
135     view.setPreferredSize(new Dimension(650, 400));
136     view.setWorldRect(0, 0, 650, 400);
137     view.setFitContentOnResize(true);
138 
139     // create the labeling option handler and the content pane
140     optionHandler = createOptionHandler();
141     contentPane.add(createToolsPanel(helpFilePath), BorderLayout.EAST);
142 
143     //load initial graph
144     loadGraph("resource/orthogonal.graphml");
145   }
146 
147   protected void initialize() {
148     label2Model = new HashMap();
149     view.getGraph2D().addDataProvider(AbstractLabelingAlgorithm.LABEL_MODEL_DPKEY, new DataProviderAdapter() {
150       public Object get(Object dataHolder) {
151         return label2Model.get(dataHolder);
152       }
153     });
154   }
155 
156   /**
157    * Does the label placement using the generic labeling algorithm.
158    */
159   void doLabelPlacement(final Object selectionKey) {
160     //create a profit model that assigns higher profit to the given angle
161     final double rotationAngle = Math.toRadians(optionHandler.getDouble(ROTATION_ANGLE_STRING));
162     final DemoProfitModel profitModel = new DemoProfitModel(rotationAngle, 1.0, 0.5);
163 
164     //configure and run the layouter
165     final GreedyMISLabeling labelLayouter = new GreedyMISLabeling();
166     labelLayouter.setOptimizationStrategy(MISLabelingAlgorithm.OPTIMIZATION_BALANCED);
167     labelLayouter.setPlaceEdgeLabels(true);
168     labelLayouter.setSelection(selectionKey);
169     labelLayouter.setPlaceNodeLabels(false);
170     labelLayouter.setProfitModel(profitModel);
171     labelLayouter.setCustomProfitModelRatio(0.1);
172 
173     new Graph2DLayoutExecutor().doLayout(view, labelLayouter);
174 
175     view.updateView();
176   }
177 
178   /**
179    * Assigns the current settings of the option handler to all edge labels in the graph and the graph's default edge.
180    */
181   void updateEdgeLabels() {
182     final Graph2D graph = view.getGraph2D();
183     label2Model.clear();
184 
185     // update auto flipping on all existing labels
186     customLabelConfig.setAutoFlippingEnabled(optionHandler.getBool(AUTO_FLIPPING_STRING));
187 
188     // configure the label models
189     final SmartEdgeLabelModel edgeLabelModel = new SmartEdgeLabelModel(); //the model that specifies the dynamic behavior of the label
190     edgeLabelModel.setAutoRotationEnabled(optionHandler.getBool(AUTO_ROTATE_STRING));
191     final CompositeEdgeLabelModel labelingModel = getCurrentEdgeLabelModel(); //the model used by the labeling algorithm (can be different from the edge label model)
192 
193     // update the label of the default edge 
194     final EdgeLabel defaultLabel = graph.getDefaultEdgeRealizer().getLabel();
195     defaultLabel.setLabelModel(edgeLabelModel, edgeLabelModel.getDefaultParameter());
196 
197     //... and update the model of each edge label
198     for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
199       final Edge edge = ec.edge();
200       final EdgeLabelLayout[] labelLayouts = graph.getEdgeLabelLayout(edge);
201       for (int i = 0; i < labelLayouts.length; i++) {
202         final EdgeLabel label = (EdgeLabel) labelLayouts[i];
203         label2Model.put(label, labelingModel);
204 
205         final Object parameter = edgeLabelModel.createModelParameter(label.getOrientedBox(),
206             graph.getEdgeLayout(edge), graph.getNodeLayout(edge.source()), graph.getNodeLayout(edge.target())); //identify the parameter that fits the current position best
207         label.setLabelModel(edgeLabelModel, parameter);
208       }
209     }
210   }
211 
212   protected void configureDefaultRealizers() {
213     super.configureDefaultRealizers();
214 
215     //customize label configuration
216     final YLabel.Factory factory = EdgeLabel.getFactory();
217     final Map defaultConfigImplementationsMap = factory.createDefaultConfigurationMap();
218     customLabelConfig = new DefaultLabelConfiguration();
219     customLabelConfig.setAutoFlippingEnabled(false);
220     defaultConfigImplementationsMap.put(YLabel.Painter.class, customLabelConfig);
221     defaultConfigImplementationsMap.put(YLabel.Layout.class, customLabelConfig);
222     defaultConfigImplementationsMap.put(YLabel.BoundsProvider.class, customLabelConfig);
223     factory.addConfiguration(CUSTOM_LABELING_CONFIG_NAME, defaultConfigImplementationsMap);
224 
225     final EdgeLabel label = view.getGraph2D().getDefaultEdgeRealizer().getLabel();
226     label.setConfiguration(CUSTOM_LABELING_CONFIG_NAME);
227   }
228 
229   /**
230    * Creates an option handler with settings for label model and label size.
231    */
232   private OptionHandler createOptionHandler() {
233     final OptionHandler oh = new OptionHandler("Options");
234     oh.addDouble(ROTATION_ANGLE_STRING, 0.0, 0.0, 360.0);
235     oh.addBool(AUTO_FLIPPING_STRING, true);
236     oh.addEnum(LABELING_MODEL_STRING, EDGE_LABEL_MODELS, 4);
237     oh.addBool(AUTO_ROTATE_STRING, true);
238     oh.addBool(ALLOW_90_DEGREE_DEVIATION_STRING, true);
239     oh.addDouble(EDGE_TO_LABEL_DISTANCE_STRING, 5.0, 1.0, 20.0);
240 
241     OptionGroup og = new OptionGroup();
242     og.setAttribute(OptionGroup.ATTRIBUTE_TITLE, PROPERTIES_GROUP);
243     og.addItem(oh.getItem(ROTATION_ANGLE_STRING));
244     og.addItem(oh.getItem(AUTO_FLIPPING_STRING));
245     og.addItem(oh.getItem(EDGE_TO_LABEL_DISTANCE_STRING));
246     og.addItem(oh.getItem(LABELING_MODEL_STRING));
247     og.addItem(oh.getItem(AUTO_ROTATE_STRING));
248     og.addItem(oh.getItem(ALLOW_90_DEGREE_DEVIATION_STRING));
249 
250     ConstraintManager cm = new ConstraintManager(oh);
251     //only enable item EDGE_TO_LABEL_DISTANCE_STRING for models that do not place labels on the edge segments
252     final String[] nonCenteredModels = {MODEL_TWO_POS, MODEL_SIX_POS, MODEL_SIDE_SLIDER};
253     cm.setEnabledOnCondition(cm.createConditionValueIs(LABELING_MODEL_STRING, nonCenteredModels),
254         oh.getItem(EDGE_TO_LABEL_DISTANCE_STRING));
255 
256     cm.setEnabledOnValueEquals(AUTO_ROTATE_STRING, Boolean.TRUE, ALLOW_90_DEGREE_DEVIATION_STRING);
257 
258     oh.addChildPropertyChangeListener(new PropertyChangeListener() {
259       public void propertyChange(PropertyChangeEvent evt) {
260         updateEdgeLabels();
261         doLabelPlacement(null);
262       }
263     });
264 
265     return oh;
266   }
267 
268   /**
269    * Returns a label model for the current option handler settings.
270    */
271   private CompositeEdgeLabelModel getCurrentEdgeLabelModel() {
272     final byte modelId = getModel(optionHandler.getEnum(LABELING_MODEL_STRING));
273     final double angle = Math.toRadians(optionHandler.getDouble(ROTATION_ANGLE_STRING));
274 
275     final CompositeEdgeLabelModel compositeEdgeLabelModel = new CompositeEdgeLabelModel();
276     compositeEdgeLabelModel.add(getEdgeLabelModel(modelId, optionHandler.getBool(AUTO_ROTATE_STRING),
277         optionHandler.getDouble(EDGE_TO_LABEL_DISTANCE_STRING), angle));
278 
279     if (optionHandler.getBool(ALLOW_90_DEGREE_DEVIATION_STRING)) {
280       //add model that creates label candidates for the alternative angle
281       final double rotatedAngle = (angle + Math.PI * 0.5) % (2.0 * Math.PI);
282       compositeEdgeLabelModel.add(getEdgeLabelModel(modelId, optionHandler.getBool(AUTO_ROTATE_STRING),
283           optionHandler.getDouble(EDGE_TO_LABEL_DISTANCE_STRING), rotatedAngle));
284     }
285 
286     return compositeEdgeLabelModel;
287   }
288 
289   /**
290    * Returns the model type for the specified index.
291    */
292   private static byte getModel(int index) {
293     if (index < 0 || index >= EDGE_LABEL_MODELS.length) {
294       return EdgeLabel.SIDE_SLIDER;
295     }
296 
297     final String modelString = EDGE_LABEL_MODELS[index];
298     if (MODEL_CENTERED.equals(modelString)) {
299       return EdgeLabel.CENTERED;
300     } else if (MODEL_TWO_POS.equals(modelString)) {
301       return EdgeLabel.TWO_POS;
302     } else if (MODEL_SIX_POS.equals(modelString)) {
303       return EdgeLabel.SIX_POS;
304     } else if (MODEL_THREE_POS_CENTER.equals(modelString)) {
305       return EdgeLabel.THREE_CENTER;
306     } else if (MODEL_CENTER_SLIDER.equals(modelString)) {
307       return EdgeLabel.CENTER_SLIDER;
308     } else {
309       return EdgeLabel.SIDE_SLIDER;
310     }
311   }
312 
313   /**
314    * Creates and configures an edge label model using the given parameter.
315    */
316   private static EdgeLabelModel getEdgeLabelModel(byte modelId, boolean autoRotationEnabled, double distance,
317                                                   double angle) {
318     if (modelId == EdgeLabel.CENTER_SLIDER || modelId == EdgeLabel.SIDE_SLIDER) {
319       final byte mode = (modelId == EdgeLabel.CENTER_SLIDER) ?
320           RotatedSliderEdgeLabelModel.CENTER_SLIDER :
321           RotatedSliderEdgeLabelModel.SIDE_SLIDER;
322       RotatedSliderEdgeLabelModel elm = new RotatedSliderEdgeLabelModel(mode);
323       elm.setAutoRotationEnabled(autoRotationEnabled);
324       if (distance < 1.0 && modelId == EdgeLabel.SIDE_SLIDER) {
325         elm.setDistance(1.0); //setting distance to 0 would automatically switch to CENTER_SLIDER model
326       } else {
327         elm.setDistance(distance);
328       }
329       elm.setAngle(angle);
330       elm.setDistanceRelativeToEdge(true);
331       return elm;
332     } else {
333       final int mode;
334       if (modelId == EdgeLabel.TWO_POS) {
335         mode = RotatedDiscreteEdgeLabelModel.TWO_POS;
336       } else if (modelId == EdgeLabel.CENTERED) {
337         mode = RotatedDiscreteEdgeLabelModel.CENTERED;
338       } else if (modelId == EdgeLabel.THREE_CENTER) {
339         mode = RotatedDiscreteEdgeLabelModel.THREE_CENTER;
340       } else {
341         mode = RotatedDiscreteEdgeLabelModel.SIX_POS; //default value
342       }
343       RotatedDiscreteEdgeLabelModel elm = new RotatedDiscreteEdgeLabelModel(mode);
344       elm.setAutoRotationEnabled(autoRotationEnabled);
345       elm.setAngle(angle);
346       elm.setDistance(distance);
347       elm.setPositionRelativeToSegment(true);
348       return elm;
349     }
350   }
351 
352   /**
353    * Creates the tools panel containing the settings and the help panel.
354    */
355   private JPanel createToolsPanel(String helpFilePath) {
356     JPanel toolsPanel = new JPanel(new BorderLayout());
357     toolsPanel.add(createOptionHandlerComponent(optionHandler), BorderLayout.NORTH);
358 
359     if (helpFilePath != null) {
360       final URL url = getResource(helpFilePath);
361       if (url == null) {
362         System.err.println("Could not locate help file: " + helpFilePath);
363       } else {
364         JComponent helpPane = createHelpPane(url);
365         if (helpPane != null) {
366           helpPane.setMinimumSize(new Dimension(200, 200));
367           helpPane.setPreferredSize(new Dimension(TOOLS_PANEL_WIDTH, 400));
368           toolsPanel.add(helpPane, BorderLayout.CENTER);
369         }
370       }
371     }
372 
373     return toolsPanel;
374   }
375 
376   /**
377    * Create a menu bar for this demo.
378    */
379   protected JMenuBar createMenuBar() {
380     JMenuBar menuBar = new JMenuBar();
381 
382     JMenu fileMenu = new JMenu("File");
383     menuBar.add(fileMenu);
384     fileMenu.add(new ExitAction());
385 
386     createExamplesMenu(menuBar);
387 
388     return menuBar;
389   }
390 
391   protected String[] getExampleResources() {
392     return new String[]{
393         "resource/orthogonal.graphml",
394         "resource/organic.graphml"
395     };
396   }
397 
398   /**
399    * Creates an EditMode and adds a popup mode that displays the demo context menu.
400    */
401   protected void registerViewModes() {
402     EditMode mode = new EditMode();
403     mode.setPopupMode(new DemoPopupMode());
404     view.addViewMode(mode);
405   }
406 
407   /**
408    * Creates the default view actions but removes the mnemonic for label editing since it is complicated to update the
409    * model if a new label is created by such an edit.
410    */
411   protected void registerViewActions() {
412     super.registerViewActions();
413     view.getCanvasComponent().getActionMap().remove(Graph2DViewActions.EDIT_LABEL);
414   }
415 
416   /**
417    * Creates the default tool bar and adds additional buttons for label placement.
418    */
419   protected JToolBar createToolBar() {
420     final JToolBar bar = super.createToolBar();
421     bar.addSeparator();
422 
423     //the layout button
424     bar.add(createActionControl(new AbstractAction(
425             "Place Labels", SHARED_LAYOUT_ICON) {
426       public void actionPerformed(ActionEvent e) {
427         doLabelPlacement(null);
428       }
429     }));
430 
431     return bar;
432   }
433 
434   /**
435    * Loads a graph and applies the label configuration to the existing labels.
436    */
437   protected void loadGraph(URL resource) {
438     super.loadGraph(resource);
439 
440     DemoDefaults.applyRealizerDefaults(view.getGraph2D());
441     final Graph2D graph = view.getGraph2D();
442     for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
443       final EdgeLabelLayout[] labelLayouts = graph.getEdgeLabelLayout(ec.edge());
444       for (int i = 0; i < labelLayouts.length; i++) {
445         final EdgeLabel label = (EdgeLabel) labelLayouts[i];
446         label.setConfiguration(CUSTOM_LABELING_CONFIG_NAME);
447       }
448     }
449 
450     updateEdgeLabels();
451     doLabelPlacement(null);
452   }
453 
454   class DemoPopupMode extends PopupMode {
455 
456     public JPopupMenu getEdgePopup(final Edge edge) {
457       JPopupMenu pm = new JPopupMenu();
458       pm.add(new AbstractAction("Add Label") {
459 
460         public void actionPerformed(ActionEvent e) {
461           if (edge == null) {
462             return;
463           }
464 
465           final SmartEdgeLabelModel edgeLabelModel = new SmartEdgeLabelModel(); //the model that specifies the dynamic behavior of the label
466           edgeLabelModel.setAutoRotationEnabled(optionHandler.getBool(AUTO_ROTATE_STRING));
467           final EdgeLabel label = new EdgeLabel("Label");
468           label2Model.put(label, getCurrentEdgeLabelModel()); //sets the model used by the labeling algorithm
469           label.setConfiguration(CUSTOM_LABELING_CONFIG_NAME);
470           label.setLineColor(LABEL_LINE_COLOR);
471           label.setBackgroundColor(LABEL_BACKGROUND_COLOR);
472           label.setLabelModel(edgeLabelModel, edgeLabelModel.getDefaultParameter());
473           view.getGraph2D().getRealizer(edge).addLabel(label);
474 
475           //place the new label
476           final Graph graph = edge.getGraph();
477           if (graph != null) {
478             graph.addDataProvider("SELECTED_LABELS", new DataProviderAdapter() {
479               public boolean getBool(Object dataHolder) {
480                 return label == dataHolder;
481               }
482             });
483             doLabelPlacement("SELECTED_LABELS");
484             graph.removeDataProvider("SELECTED_LABELS");
485           }
486 
487           view.getGraph2D().updateViews();
488         }
489       });
490       return pm;
491     }
492 
493     public JPopupMenu getEdgeLabelPopup(final EdgeLabel label) {
494       JPopupMenu pm = new JPopupMenu();
495       pm.add(new AbstractAction("Edit Label") {
496 
497         public void actionPerformed(ActionEvent e) {
498           if (label != null) {
499             EdgeLabelPropertyHandler ph = new EdgeLabelPropertyHandler(label, view);
500             ph.showEditor(null, OptionHandler.OK_APPLY_CANCEL_BUTTONS);
501           }
502         }
503       });
504       return pm;
505     }
506   }
507 
508   /**
509    * Creates a component for the specified option handler using the default editor factory and sets all of its items to
510    * auto adopt and auto commit.
511    */
512   private static JComponent createOptionHandlerComponent(OptionHandler oh) {
513     final EditorFactory defaultEditorFactory = new DefaultEditorFactory();
514     final Editor editor = defaultEditorFactory.createEditor(oh);
515 
516     //propagate auto adopt and auto commit to editor and its children
517     final List stack = new ArrayList();
518     stack.add(editor);
519     while (!stack.isEmpty()) {
520       Object editorObj = stack.remove(stack.size() - 1);
521       if (editorObj instanceof ItemEditor) {
522         ((ItemEditor) editorObj).setAutoAdopt(true);
523         ((ItemEditor) editorObj).setAutoCommit(true);
524       }
525       if (editorObj instanceof CompoundEditor) {
526         for (Iterator iter = ((CompoundEditor) editorObj).editors(); iter.hasNext();) {
527           stack.add(iter.next());
528         }
529       }
530     }
531 
532     //build and return component
533     JComponent optionComponent = editor.getComponent();
534     optionComponent.setMinimumSize(new Dimension(200, 50));
535     return optionComponent;
536   }
537 
538   public static void main(String[] args) {
539     EventQueue.invokeLater(new Runnable() {
540       public void run() {
541         Locale.setDefault(Locale.ENGLISH);
542         initLnF();
543         (new EdgeLabelingDemo("resource/edgelabelingdemohelp.html")).start("Edge Labeling Demo");
544       }
545     });
546   }
547 }
548