1
14 package demo.view.rendering;
15
16 import demo.view.DemoBase;
17 import y.geom.YRectangle;
18 import y.view.BevelNodePainter;
19 import y.view.GenericNodeRealizer;
20 import y.view.LineType;
21 import y.view.NodeLabel;
22 import y.view.NodePort;
23 import y.view.NodeRealizer;
24 import y.view.PortConfigurationAdapter;
25 import y.view.SelectionPortPainter;
26 import y.view.ShadowNodePainter;
27 import y.view.ShapeNodeRealizer;
28 import y.view.ShapePortConfiguration;
29 import y.view.ShinyPlateNodePainter;
30 import y.view.YLabel;
31 import y.view.YRenderingHints;
32 import y.view.hierarchy.DefaultGenericAutoBoundsFeature;
33 import y.view.hierarchy.DefaultHierarchyGraphFactory;
34 import y.view.hierarchy.GenericGroupNodeRealizer;
35 import y.view.hierarchy.GroupNodePainter;
36 import y.view.hierarchy.HierarchyManager;
37
38 import java.awt.BorderLayout;
39 import java.awt.Color;
40 import java.awt.Component;
41 import java.awt.EventQueue;
42 import java.awt.GradientPaint;
43 import java.awt.Graphics2D;
44 import java.awt.GridBagConstraints;
45 import java.awt.GridBagLayout;
46 import java.awt.GridLayout;
47 import java.awt.Insets;
48 import java.awt.Paint;
49 import java.awt.RenderingHints;
50 import java.awt.Shape;
51 import java.awt.Stroke;
52 import java.awt.event.ActionEvent;
53 import java.awt.geom.Rectangle2D;
54 import java.awt.geom.RoundRectangle2D;
55 import java.util.HashMap;
56 import java.util.Locale;
57 import java.util.Map;
58 import javax.swing.AbstractAction;
59 import javax.swing.AbstractButton;
60 import javax.swing.Action;
61 import javax.swing.BorderFactory;
62 import javax.swing.ButtonGroup;
63 import javax.swing.JCheckBox;
64 import javax.swing.JComponent;
65 import javax.swing.JPanel;
66 import javax.swing.JRadioButton;
67 import javax.swing.JSplitPane;
68
69
87 public class LevelOfDetailDemo extends DemoBase {
88
99 static final class HeaderNodePainter implements GenericNodeRealizer.Painter {
100 static final Color BACKGROUND = new Color(153, 204, 255);
101
102
108 static boolean isSloppyShapePaintingEnabled( final Graphics2D graphics ) {
109 return ShapeNodeRealizer.VALUE_SLOPPY_RECT_PAINTING_OFF.equals(
110 graphics.getRenderingHint(ShapeNodeRealizer.KEY_SLOPPY_RECT_PAINTING));
111 }
112
113
122 static boolean shouldPaintPorts( final Graphics2D graphics, final boolean sloppy ) {
123 if (sloppy) {
124 return YRenderingHints.VALUE_NODE_PORT_PAINTING_ON.equals(
125 graphics.getRenderingHint(YRenderingHints.KEY_NODE_PORT_PAINTING));
126 } else {
127 return !YRenderingHints.VALUE_NODE_PORT_PAINTING_OFF.equals(
128 graphics.getRenderingHint(YRenderingHints.KEY_NODE_PORT_PAINTING));
129 }
130 }
131
132
141 static boolean shouldPaintLabels( final Graphics2D graphics, final boolean sloppy ) {
142 if (sloppy) {
143 return YRenderingHints.VALUE_NODE_LABEL_PAINTING_ON.equals(
144 graphics.getRenderingHint(YRenderingHints.KEY_NODE_LABEL_PAINTING));
145 } else {
146 return !YRenderingHints.VALUE_NODE_LABEL_PAINTING_OFF.equals(
147 graphics.getRenderingHint(YRenderingHints.KEY_NODE_LABEL_PAINTING));
148 }
149 }
150
151
152 public void paint( final NodeRealizer context, final Graphics2D graphics ) {
153 paint(context, graphics, false);
154 }
155
156 public void paintSloppy( final NodeRealizer context, final Graphics2D graphics ) {
157 paint(context, graphics, true);
158 }
159
160
166 void paint(
167 final NodeRealizer context,
168 final Graphics2D graphics,
169 final boolean sloppy
170 ) {
171 if (!context.isVisible()) {
172 return;
173 }
174
175 if (!sloppy &&
176 context.isSelected() &&
177 YRenderingHints.isSelectionPaintingEnabled(graphics)) {
180 context.paintHotSpots(graphics);
181 }
182
183 final Shape shape = createShape(context, graphics, sloppy);
184 paintFilledShape(context, graphics, shape, sloppy);
185 paintShapeBorder(context, graphics, shape);
186
187 if (shouldPaintPorts(graphics, sloppy)) {
189 context.paintPorts(graphics);
190 }
191
192 if (shouldPaintLabels(graphics, sloppy)) {
194 context.paintText(graphics);
195 }
196 }
197
198
205 Shape createShape(
206 final NodeRealizer context,
207 final Graphics2D graphics,
208 final boolean sloppy
209 ) {
210 if (sloppy && !isSloppyShapePaintingEnabled(graphics)) {
212 return new Rectangle2D.Double(
214 context.getX(), context.getY(),
215 context.getWidth(), context.getHeight());
216 } else {
217 return new RoundRectangle2D.Double(
219 context.getX(), context.getY(),
220 context.getWidth(), context.getHeight(),
221 8, 8);
222 }
223 }
224
225
233 void paintFilledShape(
234 final NodeRealizer context,
235 final Graphics2D graphics,
236 final Shape shape,
237 final boolean sloppy
238 ) {
239 if (context.isTransparent()) {
240 return;
241 }
242
243 if (sloppy || !YRenderingHints.isGradientPaintingEnabled(graphics)) {
245 final Color fc = context.getFillColor();
247 if (fc != null) {
248 final Color oldColor = graphics.getColor();
249
250 graphics.setColor(fc);
251 graphics.fill(shape);
252
253 graphics.setColor(oldColor);
254 }
255 } else {
256 Color fc1 = context.getFillColor();
258 Color fc2 = context.getFillColor2();
259 if (fc1 != null || fc2 != null) {
260 final Paint oldPaint = graphics.getPaint();
261
262 if (fc1 == null) {
263 fc1 = new Color(fc2.getRed(), fc2.getGreen(), fc2.getBlue(), 0);
264 }
265 if (fc2 == null) {
266 fc2 = new Color(fc1.getRed(), fc1.getGreen(), fc1.getBlue(), 0);
267 }
268
269 final float y = (float) context.getY();
270 final double x = context.getX();
271 final double w = context.getWidth();
272 graphics.setPaint(new GradientPaint(
273 (float) (x + w * 0.33), y, fc1,
274 (float) (x + w), y, fc2));
275 graphics.fill(shape);
276
277 graphics.setPaint(oldPaint);
278 }
279 }
280
281 if (context.labelCount() > 0) {
283 final Shape oldClip = graphics.getClip();
284 final Color oldColor = graphics.getColor();
285
286 final YRectangle r = context.getLabel().getBox();
287 if (oldClip == null) {
288 final double x = context.getX();
289 final double w = context.getWidth();
290 final double minX = Math.min(r.x, x);
291 final double maxX = Math.max(r.x + r.width, x + w);
292 graphics.clip(new Rectangle2D.Double(minX, r.getY(), maxX - minX, r.getHeight()));
293 } else {
294 final Rectangle2D cb = oldClip.getBounds2D();
295 graphics.clip(new Rectangle2D.Double(cb.getX(), r.getY(), cb.getWidth(), r.getHeight()));
296 }
297 graphics.setColor(BACKGROUND);
298 graphics.fill(shape);
299
300 graphics.setColor(oldColor);
301 graphics.setClip(oldClip);
302 }
303 }
304
305
312 void paintShapeBorder(
313 final NodeRealizer context,
314 final Graphics2D graphics,
315 final Shape shape
316 ) {
317 final Color lc = context.getLineColor();
318 if (lc != null) {
319 final LineType lt = context.getLineType();
320 if (lt != null) {
321 final Color oldColor = graphics.getColor();
322 final Stroke oldStroke = graphics.getStroke();
323
324 graphics.setColor(lc);
325 graphics.setStroke(lt);
326 graphics.draw(shape);
327
328 graphics.setStroke(oldStroke);
329 graphics.setColor(oldColor);
330 }
331 }
332 }
333 }
334
335
336
337
340 private static final String CONFIGURATION_GROUP = "GroupingDemo_GROUP_NODE";
341
345 private static final String CONFIGURATION_BEVEL = "LevelOfDetailDemo#BevelNode";
346
349 private static final String CONFIGURATION_PORT = "LevelOfDetailDemo#Port";
350
351
355 private static final double PAINT_DETAIL_THRESHOLD = 0.5;
356
357
361 private boolean fractionMetricsForSizeCalculationEnabled;
362
363 public LevelOfDetailDemo() {
364 this(null);
365 }
366
367 public LevelOfDetailDemo( final String helpFilePath ) {
368 new HierarchyManager(view.getGraph2D());
370
371 configureDefaultGroupNodeRealizers();
372 configureDefaultPorts();
373
374
375 view.setPaintDetailThreshold(PAINT_DETAIL_THRESHOLD);
376 view.getRenderingHints().put(
377 RenderingHints.KEY_FRACTIONALMETRICS,
378 RenderingHints.VALUE_FRACTIONALMETRICS_ON);
379 fractionMetricsForSizeCalculationEnabled = YLabel.isFractionMetricsForSizeCalculationEnabled();
382 YLabel.setFractionMetricsForSizeCalculationEnabled(true);
383
384
385 final GridBagConstraints gbc = new GridBagConstraints();
387 final JPanel controls = new JPanel(new GridBagLayout());
388 gbc.anchor = GridBagConstraints.NORTHWEST;
389 gbc.fill = GridBagConstraints.NONE;
390 gbc.gridx = 0;
391 gbc.gridy = 0;
392 gbc.weightx = 0;
393 gbc.weighty = 0;
394 controls.add(createOptionPane(), gbc);
395 gbc.fill = GridBagConstraints.BOTH;
396 ++gbc.gridy;
397 gbc.weightx = 1;
398 gbc.weighty = 1;
399 controls.add(new JPanel(), gbc);
400
401 contentPane.remove(view);
402 contentPane.add(new JSplitPane(
403 JSplitPane.HORIZONTAL_SPLIT,
404 controls,
405 view), BorderLayout.CENTER);
406
407 addHelpPane(helpFilePath);
408
409
410 loadInitialGraph();
411 }
412
413
417 public void dispose() {
418 YLabel.setFractionMetricsForSizeCalculationEnabled(fractionMetricsForSizeCalculationEnabled);
419 }
420
421
424 protected void loadInitialGraph() {
425 loadGraph("resource/LevelOfDetailDemo.graphml");
426 }
427
428
432 private Component createOptionPane() {
433 final GridBagConstraints gbc = new GridBagConstraints();
434 final JPanel optionPane = new JPanel(new GridBagLayout());
435 gbc.anchor = GridBagConstraints.CENTER;
436 gbc.fill = GridBagConstraints.HORIZONTAL;
437 gbc.gridx = 0;
438 gbc.gridy = 0;
439 gbc.insets = new Insets(0, 0, 10, 0);
440 gbc.weightx = 1;
441 gbc.weighty = 0;
442
443 final StateEditor se = new StateEditor();
445 se.controls[0].setText("Depends on zoom");
446 se.controls[1].setText("Always detailed");
447 se.controls[2].setText("Always sloppy");
448 se.setAction(new AbstractAction("Level of Detail") {
449 public void actionPerformed( final ActionEvent e ) {
450 if (Boolean.TRUE.equals(se.getState())) {
451 view.setPaintDetailThreshold(0);
453 } else if (Boolean.FALSE.equals(se.getState())) {
454 view.setPaintDetailThreshold(Double.MAX_VALUE);
456 } else {
457 view.setPaintDetailThreshold(PAINT_DETAIL_THRESHOLD);
460 }
461 view.updateView();
462 }
463 });
464 optionPane.add(se, gbc);
465
466 gbc.insets = new Insets(0, 0, 0, 0);
467 ++gbc.gridy;
468 optionPane.add(createOnOffDefaultControl(
469 YRenderingHints.KEY_GROUP_STATE_PAINTING,
470 YRenderingHints.VALUE_GROUP_STATE_PAINTING_ON,
471 YRenderingHints.VALUE_GROUP_STATE_PAINTING_OFF
472 ), gbc);
473 ++gbc.gridy;
474 optionPane.add(createOnOffDefaultControl(
475 YRenderingHints.KEY_EDGE_LABEL_PAINTING,
476 YRenderingHints.VALUE_EDGE_LABEL_PAINTING_ON,
477 YRenderingHints.VALUE_EDGE_LABEL_PAINTING_OFF
478 ), gbc);
479 ++gbc.gridy;
480 optionPane.add(createOnOffDefaultControl(
481 YRenderingHints.KEY_NODE_LABEL_PAINTING,
482 YRenderingHints.VALUE_NODE_LABEL_PAINTING_ON,
483 YRenderingHints.VALUE_NODE_LABEL_PAINTING_OFF
484 ), gbc);
485 ++gbc.gridy;
486 optionPane.add(createOnOffDefaultControl(
487 YRenderingHints.KEY_NODE_PORT_PAINTING,
488 YRenderingHints.VALUE_NODE_PORT_PAINTING_ON,
489 YRenderingHints.VALUE_NODE_PORT_PAINTING_OFF
490 ), gbc);
491
492 ++gbc.gridy;
493 optionPane.add(createOnOffControl(
494 YRenderingHints.KEY_GRADIENT_PAINTING,
495 YRenderingHints.VALUE_GRADIENT_PAINTING_OFF
496 ), gbc);
497 ++gbc.gridy;
498 optionPane.add(createOnOffControl(
499 YRenderingHints.KEY_SELECTION_PAINTING,
500 YRenderingHints.VALUE_SELECTION_PAINTING_OFF
501 ), gbc);
502 ++gbc.gridy;
503 optionPane.add(createOnOffControl(
504 YRenderingHints.KEY_SHADOW_PAINTING,
505 YRenderingHints.VALUE_SHADOW_PAINTING_OFF
506 ), gbc);
507 ++gbc.gridy;
508 optionPane.add(createOnOffControl(
509 ShapeNodeRealizer.KEY_SLOPPY_RECT_PAINTING,
510 ShapeNodeRealizer.VALUE_SLOPPY_RECT_PAINTING_OFF
511 ), gbc);
512 ++gbc.gridy;
513 optionPane.add(createOnOffControl(
514 YRenderingHints.KEY_SLOPPY_POLYLINE_PAINTING,
515 YRenderingHints.VALUE_SLOPPY_POLYLINE_PAINTING_OFF
516 ), gbc);
517
518 return optionPane;
519 }
520
521
532 private Component createOnOffDefaultControl(
533 final RenderingHints.Key key,
534 final Object valueOn,
535 final Object valueOff
536 ) {
537 String name = key.toString();
538 if (name.endsWith(" key")) {
539 name = name.substring(0, name.length() - 4);
540 }
541 if (name.endsWith(" enable")) {
542 name = name.substring(0, name.length() - 7);
543 }
544
545 final StateEditor se = new StateEditor();
546 se.setAction(new AbstractAction(name) {
547 public void actionPerformed( final ActionEvent e ) {
548 final RenderingHints hints = view.getRenderingHints();
549 if (Boolean.TRUE.equals(se.getState())) {
550 hints.put(key, valueOn);
551 } else if (Boolean.FALSE.equals(se.getState())) {
552 hints.put(key, valueOff);
553 } else {
554 hints.remove(key);
555 }
556 view.updateView();
557 }
558 });
559 return se;
560 }
561
562
569 private JComponent createOnOffControl(
570 final RenderingHints.Key key,
571 final Object value
572 ) {
573 String name = key.toString();
574 if (name.endsWith(" key")) {
575 name = name.substring(0, name.length() - 4);
576 }
577 if (name.endsWith(" enable")) {
578 name = name.substring(0, name.length() - 7);
579 }
580
581 final JCheckBox jcb = new JCheckBox();
582 jcb.setSelected(true);
583 jcb.setAction(new AbstractAction(name) {
584 public void actionPerformed( final ActionEvent e ) {
585 final RenderingHints hints = view.getRenderingHints();
586 if (jcb.isSelected()) {
587 hints.remove(key);
588 } else {
589 hints.put(key, value);
590 }
591 view.updateView();
592 }
593 });
594
595 return jcb;
596 }
597
598
602 protected void configureDefaultRealizers() {
603 super.configureDefaultRealizers();
604
605 final GenericNodeRealizer.Factory factory = GenericNodeRealizer.getFactory();
606 final Map map = factory.createDefaultConfigurationMap();
607 final BevelNodePainter painter = new BevelNodePainter();
608 painter.setDrawShadow(true);
609 map.put(GenericNodeRealizer.Painter.class, painter);
610 factory.addConfiguration(CONFIGURATION_BEVEL, map);
611 }
612
613
617 private void configureDefaultGroupNodeRealizers() {
618 final Map map = GenericGroupNodeRealizer.createDefaultConfigurationMap();
620
621 final GroupNodePainter gnp = new GroupNodePainter(new HeaderNodePainter());
622 map.put(GenericNodeRealizer.Painter.class, new ShadowNodePainter(gnp));
623 map.put(GenericNodeRealizer.ContainsTest.class, gnp);
624 map.put(GenericNodeRealizer.GenericMouseInputEditorProvider.class, gnp);
625 map.put(GenericNodeRealizer.Initializer.class, gnp);
626
627 final DefaultGenericAutoBoundsFeature abf = new DefaultGenericAutoBoundsFeature();
628 abf.setConsiderNodeLabelSize(true);
629 map.put(GenericGroupNodeRealizer.GenericAutoBoundsFeature.class, abf);
630 map.put(GenericNodeRealizer.GenericSizeConstraintProvider.class, abf);
631 map.put(GenericNodeRealizer.LabelBoundsChangedHandler.class, abf);
632
633 final GenericNodeRealizer.Factory factory = GenericNodeRealizer.getFactory();
634 factory.addConfiguration(CONFIGURATION_GROUP, map);
635
636
637 final GenericGroupNodeRealizer gnr = new GenericGroupNodeRealizer();
638
639 gnr.setConfiguration(CONFIGURATION_GROUP);
641
642 gnr.setFillColor(new Color(228, 245, 255));
644 gnr.setFillColor2(Color.WHITE);
645 gnr.setLineColor(new Color(102, 102, 153));
646 final NodeLabel label = gnr.getLabel();
647 label.setBackgroundColor(null);
648 label.setTextColor(Color.BLACK);
649 label.setFontSize(15);
650
651
652 final DefaultHierarchyGraphFactory hgf = (DefaultHierarchyGraphFactory)
654 view.getGraph2D().getHierarchyManager().getGraphFactory();
655
656 hgf.setProxyNodeRealizerEnabled(true);
657
658 hgf.setDefaultGroupNodeRealizer(gnr.createCopy());
659 hgf.setDefaultFolderNodeRealizer(gnr.createCopy());
660 }
661
662
667 private void configureDefaultPorts() {
668
672 final ShinyPlateNodePainter impl = new ShinyPlateNodePainter();
674 impl.setDrawShadow(false);
675 final HashMap nodeImpls = new HashMap();
676 nodeImpls.put(GenericNodeRealizer.ContainsTest.class, impl);
677 nodeImpls.put(GenericNodeRealizer.Painter.class, impl);
678
679 final PortConfigurationAdapter adapter =
681 new PortConfigurationAdapter(nodeImpls);
682 adapter.setFillColor(new Color(51, 102, 255));
683 adapter.setFillColor2(null);
684 adapter.setLineColor(new Color(0, 102, 255));
685
686 final ShapePortConfiguration boundsProvider = new ShapePortConfiguration();
688 boundsProvider.setSize(16, 16);
689
690 final NodePort.Factory factory = NodePort.getFactory();
691 final Map portImpls = factory.createDefaultConfigurationMap();
692 portImpls.put(NodePort.ContainsTest.class, adapter);
693 final SelectionPortPainter painter = new SelectionPortPainter(adapter);
694 painter.setStyle(SelectionPortPainter.STYLE_SMOOTHED);
695 portImpls.put(NodePort.Painter.class, painter);
696 portImpls.put(NodePort.BoundsProvider.class, boundsProvider);
697 factory.addConfiguration(CONFIGURATION_PORT, portImpls);
698 }
699
700 public static void main( String[] args ) {
701 EventQueue.invokeLater(new Runnable() {
702 public void run() {
703 Locale.setDefault(Locale.ENGLISH);
704 initLnF();
705 (new LevelOfDetailDemo("resource/levelofdetailhelp.html")).start();
706 }
707 });
708 }
709
710
711
714 private static final class StateEditor extends JComponent {
715 private final AbstractButton[] controls;
716 private Action action;
717 private Boolean state;
718 private String name;
719
720 StateEditor() {
721 setLayout(new GridLayout(3, 1));
722 name = "";
723 final ButtonGroup group = new ButtonGroup();
724 controls = new AbstractButton[3];
725 for (int i = 0; i < controls.length; ++i) {
726 controls[i] = new JRadioButton();
727 group.add(controls[i]);
728 add(controls[i]);
729 }
730 controls[0].setSelected(true);
731 controls[0].setAction(new AbstractAction("Default") {
732 public void actionPerformed( final ActionEvent e ) {
733 state = null;
734 fireActionPerformed();
735 }
736 });
737 controls[1].setAction(new AbstractAction("On") {
738 public void actionPerformed( final ActionEvent e ) {
739 state = Boolean.TRUE;
740 fireActionPerformed();
741 }
742 });
743 controls[2].setAction(new AbstractAction("Off") {
744 public void actionPerformed( final ActionEvent e ) {
745 state = Boolean.FALSE;
746 fireActionPerformed();
747 }
748 });
749 setBorder(BorderFactory.createTitledBorder(""));
750 }
751
752
757 public Boolean getState() {
758 return state;
759 }
760
761
766 public void setState( final Boolean state ) {
767 if (state == null) {
768 if (this.state != state) {
769 controls[0].setSelected(true);
770 }
771 } else if (Boolean.TRUE == state) {
772 if (this.state != state) {
773 controls[1].setSelected(true);
774 }
775 } else if (Boolean.FALSE == state) {
776 if (this.state != state) {
777 controls[2].setSelected(true);
778 }
779 }
780 }
781
782
788 public Action getAction() {
789 return action;
790 }
791
792
797 public void setAction( final Action action ) {
798 this.action = action;
799 String name = "";
800 if (action != null) {
801 final Object value = action.getValue(Action.NAME);
802 if (value instanceof String) {
803 name = (String) value;
804 }
805 }
806 setBorder(BorderFactory.createTitledBorder(name));
807 this.name = name;
808 }
809
810 private void fireActionPerformed() {
811 final Action action = this.action;
812 if (action != null) {
813 action.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, name));
814 }
815 }
816 }
817 }
818