1
28 package demo.view.rendering;
29
30 import demo.view.DemoBase;
31 import y.geom.YRectangle;
32 import y.view.BevelNodePainter;
33 import y.view.GenericNodeRealizer;
34 import y.view.LineType;
35 import y.view.NodeLabel;
36 import y.view.NodePort;
37 import y.view.NodeRealizer;
38 import y.view.PortConfigurationAdapter;
39 import y.view.SelectionPortPainter;
40 import y.view.ShadowNodePainter;
41 import y.view.ShapeNodeRealizer;
42 import y.view.ShapePortConfiguration;
43 import y.view.ShinyPlateNodePainter;
44 import y.view.YLabel;
45 import y.view.YRenderingHints;
46 import y.view.hierarchy.DefaultGenericAutoBoundsFeature;
47 import y.view.hierarchy.DefaultHierarchyGraphFactory;
48 import y.view.hierarchy.GenericGroupNodeRealizer;
49 import y.view.hierarchy.GroupNodePainter;
50 import y.view.hierarchy.HierarchyManager;
51
52 import java.awt.BorderLayout;
53 import java.awt.Color;
54 import java.awt.Component;
55 import java.awt.EventQueue;
56 import java.awt.GradientPaint;
57 import java.awt.Graphics2D;
58 import java.awt.GridBagConstraints;
59 import java.awt.GridBagLayout;
60 import java.awt.GridLayout;
61 import java.awt.Insets;
62 import java.awt.Paint;
63 import java.awt.RenderingHints;
64 import java.awt.Shape;
65 import java.awt.Stroke;
66 import java.awt.event.ActionEvent;
67 import java.awt.geom.Rectangle2D;
68 import java.awt.geom.RoundRectangle2D;
69 import java.util.HashMap;
70 import java.util.Locale;
71 import java.util.Map;
72 import javax.swing.AbstractAction;
73 import javax.swing.AbstractButton;
74 import javax.swing.Action;
75 import javax.swing.BorderFactory;
76 import javax.swing.ButtonGroup;
77 import javax.swing.JCheckBox;
78 import javax.swing.JComponent;
79 import javax.swing.JPanel;
80 import javax.swing.JRadioButton;
81 import javax.swing.JSplitPane;
82
83
101 public class LevelOfDetailDemo extends DemoBase {
102
113 static final class HeaderNodePainter implements GenericNodeRealizer.Painter {
114 static final Color BACKGROUND = new Color(153, 204, 255);
115
116
122 static boolean isSloppyShapePaintingEnabled( final Graphics2D graphics ) {
123 return ShapeNodeRealizer.VALUE_SLOPPY_RECT_PAINTING_OFF.equals(
124 graphics.getRenderingHint(ShapeNodeRealizer.KEY_SLOPPY_RECT_PAINTING));
125 }
126
127
136 static boolean shouldPaintPorts( final Graphics2D graphics, final boolean sloppy ) {
137 if (sloppy) {
138 return YRenderingHints.VALUE_NODE_PORT_PAINTING_ON.equals(
139 graphics.getRenderingHint(YRenderingHints.KEY_NODE_PORT_PAINTING));
140 } else {
141 return !YRenderingHints.VALUE_NODE_PORT_PAINTING_OFF.equals(
142 graphics.getRenderingHint(YRenderingHints.KEY_NODE_PORT_PAINTING));
143 }
144 }
145
146
155 static boolean shouldPaintLabels( final Graphics2D graphics, final boolean sloppy ) {
156 if (sloppy) {
157 return YRenderingHints.VALUE_NODE_LABEL_PAINTING_ON.equals(
158 graphics.getRenderingHint(YRenderingHints.KEY_NODE_LABEL_PAINTING));
159 } else {
160 return !YRenderingHints.VALUE_NODE_LABEL_PAINTING_OFF.equals(
161 graphics.getRenderingHint(YRenderingHints.KEY_NODE_LABEL_PAINTING));
162 }
163 }
164
165
166 public void paint( final NodeRealizer context, final Graphics2D graphics ) {
167 paint(context, graphics, false);
168 }
169
170 public void paintSloppy( final NodeRealizer context, final Graphics2D graphics ) {
171 paint(context, graphics, true);
172 }
173
174
180 void paint(
181 final NodeRealizer context,
182 final Graphics2D graphics,
183 final boolean sloppy
184 ) {
185 if (!context.isVisible()) {
186 return;
187 }
188
189 if (!sloppy &&
190 context.isSelected() &&
191 YRenderingHints.isSelectionPaintingEnabled(graphics)) {
194 context.paintHotSpots(graphics);
195 }
196
197 final Shape shape = createShape(context, graphics, sloppy);
198 paintFilledShape(context, graphics, shape, sloppy);
199 paintShapeBorder(context, graphics, shape);
200
201 if (shouldPaintPorts(graphics, sloppy)) {
203 context.paintPorts(graphics);
204 }
205
206 if (shouldPaintLabels(graphics, sloppy)) {
208 context.paintText(graphics);
209 }
210 }
211
212
219 Shape createShape(
220 final NodeRealizer context,
221 final Graphics2D graphics,
222 final boolean sloppy
223 ) {
224 if (sloppy && !isSloppyShapePaintingEnabled(graphics)) {
226 return new Rectangle2D.Double(
228 context.getX(), context.getY(),
229 context.getWidth(), context.getHeight());
230 } else {
231 return new RoundRectangle2D.Double(
233 context.getX(), context.getY(),
234 context.getWidth(), context.getHeight(),
235 8, 8);
236 }
237 }
238
239
247 void paintFilledShape(
248 final NodeRealizer context,
249 final Graphics2D graphics,
250 final Shape shape,
251 final boolean sloppy
252 ) {
253 if (context.isTransparent()) {
254 return;
255 }
256
257 if (sloppy || !YRenderingHints.isGradientPaintingEnabled(graphics)) {
259 final Color fc = context.getFillColor();
261 if (fc != null) {
262 final Color oldColor = graphics.getColor();
263
264 graphics.setColor(fc);
265 graphics.fill(shape);
266
267 graphics.setColor(oldColor);
268 }
269 } else {
270 Color fc1 = context.getFillColor();
272 Color fc2 = context.getFillColor2();
273 if (fc1 != null || fc2 != null) {
274 final Paint oldPaint = graphics.getPaint();
275
276 if (fc1 == null) {
277 fc1 = new Color(fc2.getRed(), fc2.getGreen(), fc2.getBlue(), 0);
278 }
279 if (fc2 == null) {
280 fc2 = new Color(fc1.getRed(), fc1.getGreen(), fc1.getBlue(), 0);
281 }
282
283 final float y = (float) context.getY();
284 final double x = context.getX();
285 final double w = context.getWidth();
286 graphics.setPaint(new GradientPaint(
287 (float) (x + w * 0.33), y, fc1,
288 (float) (x + w), y, fc2));
289 graphics.fill(shape);
290
291 graphics.setPaint(oldPaint);
292 }
293 }
294
295 if (context.labelCount() > 0) {
297 final Shape oldClip = graphics.getClip();
298 final Color oldColor = graphics.getColor();
299
300 final YRectangle r = context.getLabel().getBox();
301 if (oldClip == null) {
302 final double x = context.getX();
303 final double w = context.getWidth();
304 final double minX = Math.min(r.x, x);
305 final double maxX = Math.max(r.x + r.width, x + w);
306 graphics.clip(new Rectangle2D.Double(minX, r.getY(), maxX - minX, r.getHeight()));
307 } else {
308 final Rectangle2D cb = oldClip.getBounds2D();
309 graphics.clip(new Rectangle2D.Double(cb.getX(), r.getY(), cb.getWidth(), r.getHeight()));
310 }
311 graphics.setColor(BACKGROUND);
312 graphics.fill(shape);
313
314 graphics.setColor(oldColor);
315 graphics.setClip(oldClip);
316 }
317 }
318
319
326 void paintShapeBorder(
327 final NodeRealizer context,
328 final Graphics2D graphics,
329 final Shape shape
330 ) {
331 final Color lc = context.getLineColor();
332 if (lc != null) {
333 final LineType lt = context.getLineType();
334 if (lt != null) {
335 final Color oldColor = graphics.getColor();
336 final Stroke oldStroke = graphics.getStroke();
337
338 graphics.setColor(lc);
339 graphics.setStroke(lt);
340 graphics.draw(shape);
341
342 graphics.setStroke(oldStroke);
343 graphics.setColor(oldColor);
344 }
345 }
346 }
347 }
348
349
350
351
354 private static final String CONFIGURATION_GROUP = "GroupingDemo_GROUP_NODE";
355
359 private static final String CONFIGURATION_BEVEL = "LevelOfDetailDemo#BevelNode";
360
363 private static final String CONFIGURATION_PORT = "LevelOfDetailDemo#Port";
364
365
369 private static final double PAINT_DETAIL_THRESHOLD = 0.5;
370
371
375 private boolean fractionMetricsForSizeCalculationEnabled;
376
377 public LevelOfDetailDemo() {
378 this(null);
379 }
380
381 public LevelOfDetailDemo( final String helpFilePath ) {
382 configureDefaultGroupNodeRealizers();
383 configureDefaultPorts();
384
385
386 view.setPaintDetailThreshold(PAINT_DETAIL_THRESHOLD);
387 view.getRenderingHints().put(
388 RenderingHints.KEY_FRACTIONALMETRICS,
389 RenderingHints.VALUE_FRACTIONALMETRICS_ON);
390 fractionMetricsForSizeCalculationEnabled = YLabel.isFractionMetricsForSizeCalculationEnabled();
393 YLabel.setFractionMetricsForSizeCalculationEnabled(true);
394
395
396 final GridBagConstraints gbc = new GridBagConstraints();
398 final JPanel controls = new JPanel(new GridBagLayout());
399 gbc.anchor = GridBagConstraints.NORTHWEST;
400 gbc.fill = GridBagConstraints.NONE;
401 gbc.gridx = 0;
402 gbc.gridy = 0;
403 gbc.weightx = 0;
404 gbc.weighty = 0;
405 controls.add(createOptionPane(), gbc);
406 gbc.fill = GridBagConstraints.BOTH;
407 ++gbc.gridy;
408 gbc.weightx = 1;
409 gbc.weighty = 1;
410 controls.add(new JPanel(), gbc);
411
412 contentPane.remove(view);
413 contentPane.add(new JSplitPane(
414 JSplitPane.HORIZONTAL_SPLIT,
415 controls,
416 view), BorderLayout.CENTER);
417
418 addHelpPane(helpFilePath);
419
420
421 loadInitialGraph();
422 }
423
424 protected void initialize() {
425 new HierarchyManager(view.getGraph2D());
428 }
429
430
434 public void dispose() {
435 YLabel.setFractionMetricsForSizeCalculationEnabled(fractionMetricsForSizeCalculationEnabled);
436 }
437
438
441 protected void loadInitialGraph() {
442 loadGraph("resource/LevelOfDetailDemo.graphml");
443 }
444
445
449 private Component createOptionPane() {
450 final GridBagConstraints gbc = new GridBagConstraints();
451 final JPanel optionPane = new JPanel(new GridBagLayout());
452 gbc.anchor = GridBagConstraints.CENTER;
453 gbc.fill = GridBagConstraints.HORIZONTAL;
454 gbc.gridx = 0;
455 gbc.gridy = 0;
456 gbc.insets = new Insets(0, 0, 10, 0);
457 gbc.weightx = 1;
458 gbc.weighty = 0;
459
460 final StateEditor se = new StateEditor();
462 se.controls[0].setText("Depends on zoom");
463 se.controls[1].setText("Always detailed");
464 se.controls[2].setText("Always sloppy");
465 se.setAction(new AbstractAction("Level of Detail") {
466 public void actionPerformed( final ActionEvent e ) {
467 if (Boolean.TRUE.equals(se.getState())) {
468 view.setPaintDetailThreshold(0);
470 } else if (Boolean.FALSE.equals(se.getState())) {
471 view.setPaintDetailThreshold(Double.MAX_VALUE);
473 } else {
474 view.setPaintDetailThreshold(PAINT_DETAIL_THRESHOLD);
477 }
478 view.updateView();
479 }
480 });
481 optionPane.add(se, gbc);
482
483 gbc.insets = new Insets(0, 0, 0, 0);
484 ++gbc.gridy;
485 optionPane.add(createOnOffDefaultControl(
486 YRenderingHints.KEY_GROUP_STATE_PAINTING,
487 YRenderingHints.VALUE_GROUP_STATE_PAINTING_ON,
488 YRenderingHints.VALUE_GROUP_STATE_PAINTING_OFF
489 ), gbc);
490 ++gbc.gridy;
491 optionPane.add(createOnOffDefaultControl(
492 YRenderingHints.KEY_EDGE_LABEL_PAINTING,
493 YRenderingHints.VALUE_EDGE_LABEL_PAINTING_ON,
494 YRenderingHints.VALUE_EDGE_LABEL_PAINTING_OFF
495 ), gbc);
496 ++gbc.gridy;
497 optionPane.add(createOnOffDefaultControl(
498 YRenderingHints.KEY_NODE_LABEL_PAINTING,
499 YRenderingHints.VALUE_NODE_LABEL_PAINTING_ON,
500 YRenderingHints.VALUE_NODE_LABEL_PAINTING_OFF
501 ), gbc);
502 ++gbc.gridy;
503 optionPane.add(createOnOffDefaultControl(
504 YRenderingHints.KEY_NODE_PORT_PAINTING,
505 YRenderingHints.VALUE_NODE_PORT_PAINTING_ON,
506 YRenderingHints.VALUE_NODE_PORT_PAINTING_OFF
507 ), gbc);
508
509 ++gbc.gridy;
510 optionPane.add(createOnOffControl(
511 YRenderingHints.KEY_GRADIENT_PAINTING,
512 YRenderingHints.VALUE_GRADIENT_PAINTING_OFF
513 ), gbc);
514 ++gbc.gridy;
515 optionPane.add(createOnOffControl(
516 YRenderingHints.KEY_SELECTION_PAINTING,
517 YRenderingHints.VALUE_SELECTION_PAINTING_OFF
518 ), gbc);
519 ++gbc.gridy;
520 optionPane.add(createOnOffControl(
521 YRenderingHints.KEY_SHADOW_PAINTING,
522 YRenderingHints.VALUE_SHADOW_PAINTING_OFF
523 ), gbc);
524 ++gbc.gridy;
525 optionPane.add(createOnOffControl(
526 ShapeNodeRealizer.KEY_SLOPPY_RECT_PAINTING,
527 ShapeNodeRealizer.VALUE_SLOPPY_RECT_PAINTING_OFF
528 ), gbc);
529 ++gbc.gridy;
530 optionPane.add(createOnOffControl(
531 YRenderingHints.KEY_SLOPPY_POLYLINE_PAINTING,
532 YRenderingHints.VALUE_SLOPPY_POLYLINE_PAINTING_OFF
533 ), gbc);
534
535 return optionPane;
536 }
537
538
549 private Component createOnOffDefaultControl(
550 final RenderingHints.Key key,
551 final Object valueOn,
552 final Object valueOff
553 ) {
554 String name = key.toString();
555 if (name.endsWith(" key")) {
556 name = name.substring(0, name.length() - 4);
557 }
558 if (name.endsWith(" enable")) {
559 name = name.substring(0, name.length() - 7);
560 }
561
562 final StateEditor se = new StateEditor();
563 se.setAction(new AbstractAction(name) {
564 public void actionPerformed( final ActionEvent e ) {
565 final RenderingHints hints = view.getRenderingHints();
566 if (Boolean.TRUE.equals(se.getState())) {
567 hints.put(key, valueOn);
568 } else if (Boolean.FALSE.equals(se.getState())) {
569 hints.put(key, valueOff);
570 } else {
571 hints.remove(key);
572 }
573 view.updateView();
574 }
575 });
576 return se;
577 }
578
579
586 private JComponent createOnOffControl(
587 final RenderingHints.Key key,
588 final Object value
589 ) {
590 String name = key.toString();
591 if (name.endsWith(" key")) {
592 name = name.substring(0, name.length() - 4);
593 }
594 if (name.endsWith(" enable")) {
595 name = name.substring(0, name.length() - 7);
596 }
597
598 final JCheckBox jcb = new JCheckBox();
599 jcb.setSelected(true);
600 jcb.setAction(new AbstractAction(name) {
601 public void actionPerformed( final ActionEvent e ) {
602 final RenderingHints hints = view.getRenderingHints();
603 if (jcb.isSelected()) {
604 hints.remove(key);
605 } else {
606 hints.put(key, value);
607 }
608 view.updateView();
609 }
610 });
611
612 return jcb;
613 }
614
615
619 protected void configureDefaultRealizers() {
620 super.configureDefaultRealizers();
621
622 final GenericNodeRealizer.Factory factory = GenericNodeRealizer.getFactory();
623 final Map map = factory.createDefaultConfigurationMap();
624 final BevelNodePainter painter = new BevelNodePainter();
625 painter.setDrawShadow(true);
626 map.put(GenericNodeRealizer.Painter.class, painter);
627 factory.addConfiguration(CONFIGURATION_BEVEL, map);
628 }
629
630
634 private void configureDefaultGroupNodeRealizers() {
635 final Map map = GenericGroupNodeRealizer.createDefaultConfigurationMap();
637
638 final GroupNodePainter gnp = new GroupNodePainter(new HeaderNodePainter());
639 map.put(GenericNodeRealizer.Painter.class, new ShadowNodePainter(gnp));
640 map.put(GenericNodeRealizer.ContainsTest.class, gnp);
641 map.put(GenericNodeRealizer.GenericMouseInputEditorProvider.class, gnp);
642 map.put(GenericNodeRealizer.Initializer.class, gnp);
643
644 final DefaultGenericAutoBoundsFeature abf = new DefaultGenericAutoBoundsFeature();
645 abf.setConsiderNodeLabelSize(true);
646 map.put(GenericGroupNodeRealizer.GenericAutoBoundsFeature.class, abf);
647 map.put(GenericNodeRealizer.GenericSizeConstraintProvider.class, abf);
648 map.put(GenericNodeRealizer.LabelBoundsChangedHandler.class, abf);
649
650 final GenericNodeRealizer.Factory factory = GenericNodeRealizer.getFactory();
651 factory.addConfiguration(CONFIGURATION_GROUP, map);
652
653
654 final GenericGroupNodeRealizer gnr = new GenericGroupNodeRealizer();
655
656 gnr.setConfiguration(CONFIGURATION_GROUP);
658
659 gnr.setFillColor(new Color(228, 245, 255));
661 gnr.setFillColor2(Color.WHITE);
662 gnr.setLineColor(new Color(102, 102, 153));
663 final NodeLabel label = gnr.getLabel();
664 label.setBackgroundColor(null);
665 label.setTextColor(Color.BLACK);
666 label.setFontSize(15);
667
668
669 final DefaultHierarchyGraphFactory hgf = (DefaultHierarchyGraphFactory)
671 view.getGraph2D().getHierarchyManager().getGraphFactory();
672
673 hgf.setProxyNodeRealizerEnabled(true);
674
675 hgf.setDefaultGroupNodeRealizer(gnr.createCopy());
676 hgf.setDefaultFolderNodeRealizer(gnr.createCopy());
677 }
678
679
684 private void configureDefaultPorts() {
685
689 final ShinyPlateNodePainter impl = new ShinyPlateNodePainter();
691 impl.setDrawShadow(false);
692 final HashMap nodeImpls = new HashMap();
693 nodeImpls.put(GenericNodeRealizer.ContainsTest.class, impl);
694 nodeImpls.put(GenericNodeRealizer.Painter.class, impl);
695
696 final PortConfigurationAdapter adapter =
698 new PortConfigurationAdapter(nodeImpls);
699 adapter.setFillColor(new Color(51, 102, 255));
700 adapter.setFillColor2(null);
701 adapter.setLineColor(new Color(0, 102, 255));
702
703 final ShapePortConfiguration boundsProvider = new ShapePortConfiguration();
705 boundsProvider.setSize(16, 16);
706
707 final NodePort.Factory factory = NodePort.getFactory();
708 final Map portImpls = factory.createDefaultConfigurationMap();
709 portImpls.put(NodePort.ContainsTest.class, adapter);
710 final SelectionPortPainter painter = new SelectionPortPainter(adapter);
711 painter.setStyle(SelectionPortPainter.STYLE_SMOOTHED);
712 portImpls.put(NodePort.Painter.class, painter);
713 portImpls.put(NodePort.BoundsProvider.class, boundsProvider);
714 factory.addConfiguration(CONFIGURATION_PORT, portImpls);
715 }
716
717 public static void main( String[] args ) {
718 EventQueue.invokeLater(new Runnable() {
719 public void run() {
720 Locale.setDefault(Locale.ENGLISH);
721 initLnF();
722 (new LevelOfDetailDemo("resource/levelofdetailhelp.html")).start();
723 }
724 });
725 }
726
727
728
731 private static final class StateEditor extends JComponent {
732 private final AbstractButton[] controls;
733 private Action action;
734 private Boolean state;
735 private String name;
736
737 StateEditor() {
738 setLayout(new GridLayout(3, 1));
739 name = "";
740 final ButtonGroup group = new ButtonGroup();
741 controls = new AbstractButton[3];
742 for (int i = 0; i < controls.length; ++i) {
743 controls[i] = new JRadioButton();
744 group.add(controls[i]);
745 add(controls[i]);
746 }
747 controls[0].setSelected(true);
748 controls[0].setAction(new AbstractAction("Default") {
749 public void actionPerformed( final ActionEvent e ) {
750 state = null;
751 fireActionPerformed();
752 }
753 });
754 controls[1].setAction(new AbstractAction("On") {
755 public void actionPerformed( final ActionEvent e ) {
756 state = Boolean.TRUE;
757 fireActionPerformed();
758 }
759 });
760 controls[2].setAction(new AbstractAction("Off") {
761 public void actionPerformed( final ActionEvent e ) {
762 state = Boolean.FALSE;
763 fireActionPerformed();
764 }
765 });
766 setBorder(BorderFactory.createTitledBorder(""));
767 }
768
769
774 public Boolean getState() {
775 return state;
776 }
777
778
783 public void setState( final Boolean state ) {
784 if (state == null) {
785 if (this.state != state) {
786 controls[0].setSelected(true);
787 }
788 } else if (Boolean.TRUE == state) {
789 if (this.state != state) {
790 controls[1].setSelected(true);
791 }
792 } else if (Boolean.FALSE == state) {
793 if (this.state != state) {
794 controls[2].setSelected(true);
795 }
796 }
797 }
798
799
805 public Action getAction() {
806 return action;
807 }
808
809
814 public void setAction( final Action action ) {
815 this.action = action;
816 String name = "";
817 if (action != null) {
818 final Object value = action.getValue(Action.NAME);
819 if (value instanceof String) {
820 name = (String) value;
821 }
822 }
823 setBorder(BorderFactory.createTitledBorder(name));
824 this.name = name;
825 }
826
827 private void fireActionPerformed() {
828 final Action action = this.action;
829 if (action != null) {
830 action.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, name));
831 }
832 }
833 }
834 }
835