1
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.layout.CanonicMultiStageLayouter;
35 import y.layout.EdgeLabelLayout;
36 import y.layout.FixNodeLayoutStage;
37 import y.layout.LabelLayoutTranslator;
38 import y.layout.LayoutOrientation;
39 import y.layout.Layouter;
40 import y.layout.hierarchic.IncrementalHierarchicLayouter;
41 import y.layout.labeling.AbstractLabelingAlgorithm;
42 import y.layout.labeling.GreedyMISLabeling;
43 import y.layout.labeling.MISLabelingAlgorithm;
44 import y.layout.orthogonal.OrthogonalLayouter;
45 import y.layout.router.polyline.EdgeRouter;
46 import y.layout.tree.GenericTreeLayouter;
47 import y.layout.tree.TreeReductionStage;
48 import y.option.CompoundEditor;
49 import y.option.DefaultEditorFactory;
50 import y.option.Editor;
51 import y.option.EditorFactory;
52 import y.option.ItemEditor;
53 import y.option.OptionHandler;
54 import y.util.DataProviders;
55 import y.view.EdgeLabel;
56 import y.view.EditMode;
57 import y.view.Graph2D;
58 import y.view.Graph2DLayoutExecutor;
59 import y.view.Graph2DViewActions;
60 import y.view.PopupMode;
61 import y.view.Selections;
62 import y.view.SmartEdgeLabelModel;
63 import y.view.YLabel;
64
65 import javax.swing.AbstractAction;
66 import javax.swing.Action;
67 import javax.swing.ActionMap;
68 import javax.swing.JComboBox;
69 import javax.swing.JComponent;
70 import javax.swing.JPanel;
71 import javax.swing.JPopupMenu;
72 import javax.swing.JToolBar;
73 import java.awt.BorderLayout;
74 import java.awt.Dimension;
75 import java.awt.EventQueue;
76 import java.awt.RenderingHints;
77 import java.awt.event.ActionEvent;
78 import java.awt.event.ActionListener;
79 import java.beans.PropertyChangeEvent;
80 import java.beans.PropertyChangeListener;
81 import java.net.URL;
82 import java.util.ArrayList;
83 import java.util.Iterator;
84 import java.util.List;
85 import java.util.Locale;
86 import java.util.Map;
87
88
101 public class PreferredLabelPlacementDemo extends DemoBase {
102 private static final String VISUALIZING_DESCRIPTOR_LABELING_CONFIG_NAME = "VISUALIZING_DESCRIPTOR_LABELING_CONFIG";
104 private static final String VISUALIZING_BOUNDS_LABELING_CONFIG_NAME = "VISUALIZING_BOUNDS_LABELING_CONFIG";
105 private static final int TOOLS_PANEL_WIDTH = 350;
106
107 private final PreferredPlacementOptionHandler optionHandler;
108 private boolean isSelectionFinished;
109 private Layouter layouter;
110 private String labelVisualization;
111
112 public PreferredLabelPlacementDemo() {
113 this(null);
114 }
115
116 public PreferredLabelPlacementDemo(final String helpFilePath) {
117 view.setPreferredSize(new Dimension(650, 400));
119 view.setWorldRect(0, 0, 650, 400);
120 view.setFitContentOnResize(true);
121
122 optionHandler = new PreferredPlacementOptionHandler();
124 optionHandler.addChildPropertyChangeListener(
125 new PropertyChangeListener() {
126 public void propertyChange(final PropertyChangeEvent evt) {
127 if (isSelectionFinished && !hasUndefinedChanged(evt)) {
128 optionHandler.commitValues(view.getGraph2D());
131 runLayout(false);
132 }
133 }
134 });
135 contentPane.add(createToolsPanel(helpFilePath), BorderLayout.EAST);
136
137 final Selections.SelectionStateObserver sso = new Selections.SelectionStateObserver() {
139 protected void updateSelectionState(Graph2D graph) {
140 isSelectionFinished = false;
141 optionHandler.updateValues(graph);
142 isSelectionFinished = true;
143 }
144 };
145 view.getGraph2D().addGraph2DSelectionListener(sso);
146 view.getGraph2D().addGraphListener(sso);
147
148 view.getGraph2D().addDataProvider(
150 FixNodeLayoutStage.FIXED_NODE_DPKEY,
151 DataProviders.createConstantDataProvider(Boolean.TRUE));
152
153 loadGraph("resource/labeledgraph.graphml");
155 }
156
157 private static boolean hasUndefinedChanged(final PropertyChangeEvent evt) {
158 return "valueUndefined".equals(evt.getPropertyName()) &&
159 Boolean.TRUE.equals((Boolean)evt.getOldValue()) &&
160 Boolean.FALSE.equals((Boolean)evt.getNewValue());
161 }
162
163
166 protected EditMode createEditMode() {
167 final EditMode editMode = new EditMode();
168 editMode.allowEdgeCreation(false);
169 editMode.allowNodeCreation(false);
170 editMode.allowBendCreation(false);
171 editMode.allowNodeEditing(false);
172 editMode.allowResizeNodes(false);
173 editMode.allowMoveSelection(false);
174 return editMode;
175 }
176
177 protected void initialize() {
178 initializeLabelRendering();
179 }
180
181 protected boolean isClipboardEnabled() {
182 return false;
183 }
184
185 private void initializeLabelRendering() {
186 final RenderingHints rh = view.getRenderingHints();
188 rh.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
189 view.setRenderingHints(rh);
190 YLabel.setFractionMetricsForSizeCalculationEnabled(true);
191 }
192
193 private static AbstractLabelingAlgorithm createGreedyMISLabeling() {
194 final GreedyMISLabeling labeling = new GreedyMISLabeling();
196 labeling.setOptimizationStrategy(MISLabelingAlgorithm.OPTIMIZATION_BALANCED);
197 labeling.setPlaceEdgeLabels(true);
198 labeling.setPlaceNodeLabels(false);
199 return labeling;
200 }
201
202 private Layouter createIncrementalHierarchicLayouter(final byte layoutOrientation) {
203 final IncrementalHierarchicLayouter ihl = new IncrementalHierarchicLayouter();
204 ihl.setIntegratedEdgeLabelingEnabled(true);
205 ihl.setLayoutOrientation(layoutOrientation);
206 disableAutoFlipping(ihl);
207 return ihl;
208 }
209
210 private Layouter createGenericTreeLayouter() {
211 final TreeReductionStage reductionStage = new TreeReductionStage();
212
213 reductionStage.setNonTreeEdgeSelectionKey(Layouter.SELECTED_EDGES);
215 final EdgeRouter nonTreeEdgeRouter = new EdgeRouter();
216 nonTreeEdgeRouter.setSphereOfAction(EdgeRouter.ROUTE_SELECTED_EDGES);
217 nonTreeEdgeRouter.setSelectedEdgesDpKey(reductionStage.getNonTreeEdgeSelectionKey());
218 reductionStage.setNonTreeEdgeRouter(nonTreeEdgeRouter);
219
220 reductionStage.setNonTreeEdgeLabelSelectionKey("NonTreeEdgeLabelSelectionKey");
222 final AbstractLabelingAlgorithm labelingAlgorithm = createGreedyMISLabeling();
223 labelingAlgorithm.setSelection(reductionStage.getNonTreeEdgeLabelSelectionKey());
224 reductionStage.setNonTreeEdgeLabelingAlgorithm(labelingAlgorithm);
225
226 final GenericTreeLayouter genericTreeLayouter = new GenericTreeLayouter();
227 genericTreeLayouter.setIntegratedEdgeLabeling(true);
228 genericTreeLayouter.prependStage(reductionStage);
229 disableAutoFlipping(genericTreeLayouter);
230 return genericTreeLayouter;
231 }
232
233 private Layouter createOrthogonalLayouter() {
234 final OrthogonalLayouter orthogonalLayouter = new OrthogonalLayouter();
235 orthogonalLayouter.setIntegratedEdgeLabelingEnabled(true);
236 disableAutoFlipping(orthogonalLayouter);
237 return orthogonalLayouter;
238 }
239
240 private void disableAutoFlipping(final CanonicMultiStageLayouter canonicMultiStageLayouter) {
241 final LabelLayoutTranslator labelLayouter = (LabelLayoutTranslator) canonicMultiStageLayouter.getLabelLayouter();
242 labelLayouter.setAutoFlippingEnabled(false);
243 }
244
245
250 void runLayout(final boolean fitViewToContent) {
251 final Graph2DLayoutExecutor layoutExecutor = new Graph2DLayoutExecutor();
253 layoutExecutor.getLayoutMorpher().setKeepZoomFactor(!fitViewToContent);
254 layoutExecutor.doLayout(view, new FixNodeLayoutStage(layouter));
255 }
256
257 protected void configureDefaultRealizers() {
258 super.configureDefaultRealizers();
259
260 final YLabel.Factory factory = EdgeLabel.getFactory();
262
263 final Map visualizingDescriptorConfigurationMap = factory.createDefaultConfigurationMap();
264 VisualizingDescriptorLabelConfiguration visualizingDescriptorLabelConfig = new VisualizingDescriptorLabelConfiguration();
265 visualizingDescriptorLabelConfig.setAutoFlippingEnabled(false);
266 visualizingDescriptorConfigurationMap.put(YLabel.Painter.class, visualizingDescriptorLabelConfig);
267 visualizingDescriptorConfigurationMap.put(YLabel.Layout.class, visualizingDescriptorLabelConfig);
268 visualizingDescriptorConfigurationMap.put(YLabel.BoundsProvider.class, visualizingDescriptorLabelConfig);
269 factory.addConfiguration(VISUALIZING_DESCRIPTOR_LABELING_CONFIG_NAME, visualizingDescriptorConfigurationMap);
270
271 final Map visualizeBoundsConfigurationMap = factory.createDefaultConfigurationMap();
272 VisualizingBoundsLabelConfiguration visualizeBoundsLabelConfig = new VisualizingBoundsLabelConfiguration();
273 visualizeBoundsLabelConfig.setAutoFlippingEnabled(false);
274 visualizeBoundsConfigurationMap.put(YLabel.Painter.class, visualizeBoundsLabelConfig);
275 visualizeBoundsConfigurationMap.put(YLabel.Layout.class, visualizeBoundsLabelConfig);
276 visualizeBoundsConfigurationMap.put(YLabel.BoundsProvider.class, visualizeBoundsLabelConfig);
277 factory.addConfiguration(VISUALIZING_BOUNDS_LABELING_CONFIG_NAME, visualizeBoundsConfigurationMap);
278 }
279
280 protected EdgeLabel createEdgeLabel() {
281 final EdgeLabel label = new EdgeLabel("Label");
282 final SmartEdgeLabelModel edgeLabelModel = new SmartEdgeLabelModel();
283 label.setLabelModel(edgeLabelModel, edgeLabelModel.getDefaultParameter());
284 label.setConfiguration(labelVisualization);
285 return label;
286 }
287
288
291 private JPanel createToolsPanel(final String helpFilePath) {
292 final JPanel toolsPanel = new JPanel(new BorderLayout());
293 toolsPanel.add(createOptionHandlerComponent(optionHandler), BorderLayout.NORTH);
294
295 if (helpFilePath != null) {
296 final URL url = getResource(helpFilePath);
297 if (url == null) {
298 System.err.println("Could not locate help file: " + helpFilePath);
299 } else {
300 final JComponent helpPane = createHelpPane(url);
301 if (helpPane != null) {
302 helpPane.setMinimumSize(new Dimension(200, 200));
303 helpPane.setPreferredSize(new Dimension(TOOLS_PANEL_WIDTH, 400));
304 toolsPanel.add(helpPane, BorderLayout.CENTER);
305 }
306 }
307 }
308 return toolsPanel;
309 }
310
311
314 protected void registerViewModes() {
315 final EditMode mode = createEditMode();
316 mode.setPopupMode(new DemoPopupMode());
317 view.addViewMode(mode);
318 }
319
320
324 protected void registerViewActions() {
325 super.registerViewActions();
326 final ActionMap actionMap = view.getCanvasComponent().getActionMap();
327 actionMap.remove(Graph2DViewActions.EDIT_LABEL);
328 }
329
330
333 protected Action createDeleteSelectionAction() {
334 final ActionMap actionMap = view.getCanvasComponent().getActionMap();
335 final Graph2DViewActions.DeleteSelectionAction deleteAction =
336 (Graph2DViewActions.DeleteSelectionAction) actionMap.get(Graph2DViewActions.DELETE_SELECTION);
337 deleteAction.putValue(Action.SMALL_ICON, getIconResource("resource/delete.png"));
338 deleteAction.putValue(Action.SHORT_DESCRIPTION, "Delete Selection");
339 deleteAction.setDeletionMask(Graph2DViewActions.DeleteSelectionAction.TYPE_EDGE_LABEL);
340 return deleteAction;
341 }
342
343
346 protected JToolBar createToolBar() {
347 final JToolBar toolBar = super.createToolBar();
348 toolBar.addSeparator();
349 toolBar.add(createLayoutersComboBox());
350 toolBar.addSeparator(TOOLBAR_SMALL_SEPARATOR);
351 toolBar.add(createLayoutButton());
352 toolBar.addSeparator();
353 toolBar.add(createLabelVisualizationsComboBox());
354 return toolBar;
355 }
356
357 private JComboBox createLayoutersComboBox() {
358 final JComboBox comboBox = new JComboBox(
359 new Object[]{
360 "Generic Label Placement",
361 "Hierarchic Layout, Top-Down",
362 "Hierarchic Layout, Left-Right",
363 "Generic Tree Layout",
364 "Orthogonal Layout"});
365 final Layouter[] layouters = {
366 createGreedyMISLabeling(),
367 createIncrementalHierarchicLayouter(LayoutOrientation.TOP_TO_BOTTOM),
368 createIncrementalHierarchicLayouter(LayoutOrientation.LEFT_TO_RIGHT),
369 createGenericTreeLayouter(),
370 createOrthogonalLayouter()};
371
372 comboBox.setMaximumSize(comboBox.getPreferredSize());
373 comboBox.addActionListener(
374 new ActionListener() {
375 public void actionPerformed(ActionEvent e) {
376 layouter = layouters[comboBox.getSelectedIndex()];
377 runLayout(true);
378 }
379 });
380 layouter = layouters[0];
381 return comboBox;
382 }
383
384 private JComponent createLayoutButton() {
385 return createActionControl(
386 new AbstractAction(
387 "Layout", SHARED_LAYOUT_ICON) {
388 public void actionPerformed(ActionEvent e) {
389 runLayout(true);
390 }
391 });
392 }
393
394 private JComboBox createLabelVisualizationsComboBox() {
395 final JComboBox comboBox = new JComboBox( new Object[]{"Visualize Label Descriptor", "Visualize Label Bounds"});
396 final String[] visualizations = {VISUALIZING_DESCRIPTOR_LABELING_CONFIG_NAME, VISUALIZING_BOUNDS_LABELING_CONFIG_NAME};
397
398 comboBox.setMaximumSize(comboBox.getPreferredSize());
399 comboBox.addActionListener(
400 new ActionListener() {
401 public void actionPerformed(ActionEvent e) {
402 labelVisualization = visualizations[comboBox.getSelectedIndex()];
403 setSelectedLabelVisualization();
404 view.updateView();
405 }
406 });
407 labelVisualization = visualizations[0];
408 return comboBox;
409 }
410
411
414 protected void loadGraph(URL resource) {
415 super.loadGraph(resource);
416
417 DemoDefaults.applyRealizerDefaults(view.getGraph2D());
418 setSelectedLabelVisualization();
419
420 runLayout(true);
421 getUndoManager().resetQueue();
422 }
423
424 private void setSelectedLabelVisualization() {
425 final Graph2D graph = view.getGraph2D();
426 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
427 final EdgeLabelLayout[] labelLayouts = graph.getEdgeLabelLayout(ec.edge());
428 for (int i = 0; i < labelLayouts.length; i++) {
429 final EdgeLabel label = (EdgeLabel) labelLayouts[i];
430 label.setConfiguration(labelVisualization);
431 }
432 }
433 }
434
435
438 class DemoPopupMode extends PopupMode {
439 public JPopupMenu getEdgePopup(final Edge edge) {
440 final JPopupMenu popupMenu = new JPopupMenu();
441 popupMenu.add(
442 new AbstractAction("Add Label") {
443 public void actionPerformed(ActionEvent e) {
444 final Graph2D graph2D = view.getGraph2D();
446 graph2D.firePreEvent();
447 try {
448 graph2D.backupRealizers(graph2D.edges());
449 graph2D.backupRealizers(graph2D.nodes());
450
451 final EdgeLabel label = createEdgeLabel();
453 graph2D.getRealizer(edge).addLabel(label);
454
455 runLayout(true);
457 } finally {
458 graph2D.firePostEvent();
459 }
460 }
461 });
462 return popupMenu;
463 }
464 }
465
466
470 private static JComponent createOptionHandlerComponent(final OptionHandler oh) {
471 final EditorFactory defaultEditorFactory = new DefaultEditorFactory();
472 final Editor editor = defaultEditorFactory.createEditor(oh);
473
474 final List stack = new ArrayList();
476 stack.add(editor);
477 while (!stack.isEmpty()) {
478 Object editorObj = stack.remove(stack.size() - 1);
479 if (editorObj instanceof ItemEditor) {
480 ((ItemEditor) editorObj).setAutoAdopt(true);
481 ((ItemEditor) editorObj).setAutoCommit(true);
482 }
483 if (editorObj instanceof CompoundEditor) {
484 for (Iterator iter = ((CompoundEditor) editorObj).editors(); iter.hasNext();) {
485 stack.add(iter.next());
486 }
487 }
488 }
489
490 final JComponent optionComponent = editor.getComponent();
492 optionComponent.setMinimumSize(new Dimension(200, 50));
493 return optionComponent;
494 }
495
496
499 public static void main(String[] args) {
500 EventQueue.invokeLater(new Runnable() {
501 public void run() {
502 Locale.setDefault(Locale.ENGLISH);
503 initLnF();
504 (new PreferredLabelPlacementDemo("resource/preferredlabelplacementdemohelp.html")).start("Preferred Label Placement Demo");
505 }
506 });
507 }
508 }
509