1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.6. 
4    ** 
5    ** yWorks proprietary/confidential. Use is subject to license terms.
6    **
7    ** Redistribution of this file or of an unauthorized byte-code version
8    ** of this file is strictly forbidden.
9    **
10   ** Copyright (c) 2000-2008 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
14  package demo.view.realizer;
15  
16  import demo.view.DemoBase;
17  import y.base.DataProvider;
18  import y.base.Node;
19  import y.util.DataProviderAdapter;
20  import y.view.CellEditorMode;
21  import y.view.EditMode;
22  import y.view.GenericNodeRealizer;
23  import y.view.Graph2DView;
24  import y.view.NodeCellEditor;
25  import y.view.NodeCellRenderer;
26  import y.view.NodeCellRendererPainter;
27  import y.view.NodeRealizer;
28  import y.view.ShapeNodeRealizer;
29  import y.view.SimpleUserDataHandler;
30  
31  import javax.swing.AbstractAction;
32  import javax.swing.AbstractCellEditor;
33  import javax.swing.BorderFactory;
34  import javax.swing.ImageIcon;
35  import javax.swing.JComboBox;
36  import javax.swing.JComponent;
37  import javax.swing.JLabel;
38  import javax.swing.JPanel;
39  import javax.swing.JTable;
40  import javax.swing.JTextField;
41  import javax.swing.JToggleButton;
42  import javax.swing.JToolBar;
43  import javax.swing.table.DefaultTableModel;
44  import java.awt.BorderLayout;
45  import java.awt.Insets;
46  import java.awt.event.ActionEvent;
47  import java.awt.event.ActionListener;
48  import java.awt.event.KeyAdapter;
49  import java.awt.event.KeyEvent;
50  import java.beans.PropertyChangeEvent;
51  import java.beans.PropertyChangeListener;
52  import java.net.URL;
53  import java.util.Map;
54  
55  
56  /**
57   * This demo shows how yFiles can deal with Swinglike cell rendering and cell editing mechanisms.
58   * It shows both how to customize {@link GenericNodeRealizer} to display JComponents as nodes, and
59   * how to configure {@link y.view.EditMode} to work with {@link CellEditorMode} so that a double click
60   * on a node initiates inline cell editing.
61   */
62  public class SwingRendererDemo extends DemoBase
63  {
64    private GenericNodeRealizer gnr;
65    private ShapeNodeRealizer snr = new ShapeNodeRealizer();
66  
67    private static final boolean INITIAL_ANTIALIASING_STATE = true;
68  
69    /**
70     * Instantiates this demo.
71     */
72    public SwingRendererDemo()
73    {
74      // create a simple NodeCellRenderer and NodeCellEditor instance that work together nicely
75      NodeCellRenderer simpleNodeCellRenderer = new SimpleNodeCellRenderer();
76  
77      // Get the factory to register custom styles/configurations.
78      GenericNodeRealizer.Factory factory = GenericNodeRealizer.getFactory();
79  
80      view.setAntialiasedPainting(INITIAL_ANTIALIASING_STATE);
81  
82      // prepare a GenericNodeRealizer to use the NodeCellRenderer for rendering
83      Map map = factory.createDefaultConfigurationMap();
84      map.put(GenericNodeRealizer.Painter.class, new NodeCellRendererPainter(simpleNodeCellRenderer, NodeCellRendererPainter.USER_DATA_MAP));
85      map.put(GenericNodeRealizer.UserDataHandler.class, new SimpleUserDataHandler(SimpleUserDataHandler.REFERENCE_ON_FAILURE));
86      // register the configuration using the given name
87      factory.addConfiguration("JTextField", map);
88  
89      // create another configuration based on the first one, this time use a more complex renderer
90      map.put(GenericNodeRealizer.Painter.class, new NodeCellRendererPainter(new ComplexNodeCellRenderer(), NodeCellRendererPainter.USER_DATA_MAP));
91      // register it
92      factory.addConfiguration("JTable", map);
93  
94      // instantiate a default node realizer
95      gnr = new GenericNodeRealizer();
96      gnr.setSize(200, 50);
97      gnr.setConfiguration("JTextField");
98      gnr.setUserData("Hello Renderer World!");
99  
100     // create a sample instance
101     view.getGraph2D().setDefaultNodeRealizer(gnr);
102     view.getGraph2D().createNode(150, 50, 200, 50, "");
103 
104     // and another one of the other kind
105     gnr.setConfiguration("JTable");
106     view.getGraph2D().createNode(150, 200, 150, 150, "");
107 
108   }
109 
110   /**
111    * Adds the view modes to the view.
112    * This implementation adds a new EditMode (with showNodeTips enabled) and
113    * a new {@link y.view.AutoDragViewMode}.
114    */
115   protected void registerViewModes() {
116     final NodeCellEditor simpleNodeCellEditor = new SimpleNodeCellEditor();
117     // instantiate an appropriate editor for the complex renderer
118     final NodeCellEditor complexNodeCellEditor = new SwingRendererDemo.ComplexNodeCellEditor();
119 
120     // create a dataprovider that dynamically switches between the different NodeCellEditor instances
121     DataProvider nodeCellEditorProvider = new DataProviderAdapter() {
122       public Object get(Object dataHolder) {
123         NodeRealizer realizer = view.getGraph2D().getRealizer((Node) dataHolder);
124         if (realizer instanceof GenericNodeRealizer){
125           if ("JTextField".equals(((GenericNodeRealizer) realizer).getConfiguration())){
126             return simpleNodeCellEditor;
127           } else {
128             return complexNodeCellEditor;
129           }
130         } else {
131           return null;
132         }
133       }
134     };
135 
136     EditMode editMode = new EditMode();
137     // create the CellEditorMode and give it the multiplexing NodeCellEditor provider,
138     // as well as tell it where to find the user data
139     CellEditorMode cellEditorMode = new CellEditorMode(nodeCellEditorProvider, NodeCellRendererPainter.USER_DATA_MAP);
140     // register it with the EditMode
141     editMode.setEditNodeMode(cellEditorMode);
142     // Disable generic node label assignment in the view since it would spoil the
143     // effect of the node cell editors/renderers.
144     editMode.assignNodeLabel(false);
145 
146 
147     view.addViewMode( editMode );
148   }
149 
150   /** Creates a toolbar that allows to switch the default node realizer type. */
151   protected JToolBar createToolBar()
152   {
153     JToolBar retValue;
154 
155     retValue = super.createToolBar();
156     final JComboBox cb = new JComboBox(new Object[]{"JTextField", "JTable", "Rectangle"});
157     cb.setSelectedIndex(1);
158     retValue.add(cb);
159     cb.addActionListener(new ActionListener()
160     {
161       public void actionPerformed(ActionEvent ae)
162       {
163         if ( !"Rectangle".equals( cb.getSelectedItem().toString() ) ) {
164           gnr.setConfiguration( cb.getSelectedItem().toString() );
165           view.getGraph2D().setDefaultNodeRealizer( gnr );
166         } else {
167           view.getGraph2D().setDefaultNodeRealizer( snr );
168         }
169       }
170     });
171     final URL iconUrl = getClass().getResource("resource/antialiasing.png");
172     final JToggleButton toggleAa = new JToggleButton(new AbstractAction("AA") {
173       {
174         if (iconUrl != null) {
175           putValue(AbstractAction.SMALL_ICON, new ImageIcon(iconUrl));
176         }
177         putValue(AbstractAction.SHORT_DESCRIPTION, "Toggle Anti-Aliasing");
178       }
179 
180       public void actionPerformed(ActionEvent e) {
181         final boolean newAaState = !view.isAntialiasedPainting();
182         view.setAntialiasedPainting(newAaState);
183         view.updateView();
184       }
185     });
186     if (iconUrl != null) {
187       toggleAa.setText("");
188       toggleAa.setMargin(new Insets(0,0,0,0));
189     }
190     toggleAa.setSelected(INITIAL_ANTIALIASING_STATE);
191     retValue.addSeparator();
192     retValue.add(toggleAa);
193 
194     return retValue;
195   }
196 
197   /**
198    * A simple {@link NodeCellEditor} implementation that is based on an even simpler
199    * {@link NodeCellRenderer} implementation.
200    */
201   public static class SimpleNodeCellEditor extends AbstractCellEditor implements NodeCellEditor
202   {
203     // the delegate
204     private final SimpleNodeCellRenderer ncr;
205 
206     public SimpleNodeCellEditor()
207     {
208       // initialize
209       this.ncr = new SimpleNodeCellRenderer();
210       // add editor hooks
211       this.ncr.tf.addActionListener(new ActionListener()
212       {
213         public void actionPerformed(ActionEvent ae)
214         {
215           SimpleNodeCellEditor.this.fireEditingStopped();
216         }
217       });
218       this.ncr.tf.addKeyListener(new KeyAdapter()
219       {
220         public void keyPressed(KeyEvent ke)
221         {
222           if (ke.getKeyCode() ==  KeyEvent.VK_ESCAPE)
223           {
224             SimpleNodeCellEditor.this.fireEditingCanceled();
225           }
226         }
227       });
228     }
229 
230     public JComponent getNodeCellEditorComponent(Graph2DView view, NodeRealizer context, Object value, boolean isSelected)
231     {
232       // get the renderer as editor
233       return ncr.getNodeCellRendererComponent(view, context, value, isSelected);
234     }
235 
236     public Object getCellEditorValue()
237     {
238       // get the value this editor represents
239       return ncr.getValue();
240     }
241   }
242 
243   /**
244    * A simple NodeCellRenderer that uses a JTextField and a JLabel in a JPanel to display the nodes contents.
245    */
246   public static final class SimpleNodeCellRenderer extends JPanel implements NodeCellRenderer
247   {
248     /**
249      * the textfield that holds/displays the actual data
250      */
251     protected JTextField tf;
252 
253     public SimpleNodeCellRenderer()
254     {
255       super(new BorderLayout());
256       // create a nice GUI
257       setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(3,3,3,3), BorderFactory.createEtchedBorder()));
258       add(new JLabel("Content"), BorderLayout.NORTH);
259       add(tf = new JTextField(), BorderLayout.CENTER);
260     }
261 
262     public JComponent getNodeCellRendererComponent(Graph2DView view, NodeRealizer nodeRealizer, Object userObject, boolean selected)
263     {
264       // initialize the textfield
265       tf.setText(String.valueOf(userObject));
266       return this;
267     }
268 
269     public Object getValue()
270     {
271       // return the value of the text field
272       return tf.getText();
273     }
274   }
275 
276   /**
277    * A more sophisticated NodeCellEditor that uses a sophisticated NodeCellRenderer to
278    * display/edit a node.
279    * This implementation displays an editable JTable where the value column is editable.
280    */
281   public static class ComplexNodeCellEditor extends AbstractCellEditor implements NodeCellEditor
282   {
283     // the delegate
284     private final ComplexNodeCellRenderer ncr;
285 
286     public ComplexNodeCellEditor()
287     {
288       this.ncr = new ComplexNodeCellRenderer();
289       // add editor hooks
290       this.ncr.table.addPropertyChangeListener("tableCellEditor", new PropertyChangeListener() {
291         public void propertyChange(PropertyChangeEvent evt) {
292           if (evt.getNewValue() == null && evt.getOldValue() != null){
293             ComplexNodeCellEditor.this.fireEditingStopped();
294           }
295         }
296       });
297     }
298 
299     /**
300      * Delegates the request to the table.
301      */
302     public boolean stopCellEditing() {
303       if (ncr.table.isEditing() && ncr.table.getCellEditor() != null){
304         return ncr.table.getCellEditor().stopCellEditing();
305       } else {
306         fireEditingStopped();
307         return true;
308       }
309     }
310 
311     /**
312      * Delegates the request to the table.
313      */
314     public void cancelCellEditing() {
315       if (ncr.table.isEditing() && ncr.table.getCellEditor() != null){
316         ncr.table.getCellEditor().cancelCellEditing();
317       } else {
318         fireEditingCanceled();
319       }
320     }
321 
322     public JComponent getNodeCellEditorComponent(Graph2DView view, NodeRealizer context, Object value, boolean isSelected)
323     {
324       ncr.getNodeCellRendererComponent(view, context, value, isSelected);
325       return ncr;
326     }
327 
328     public Object getCellEditorValue()
329     {
330       return ncr.getValue();
331     }
332   }
333 
334   /**
335    * A nice renderer that can be used to display data in a JTable
336    */
337   public static final class ComplexNodeCellRenderer extends JPanel implements NodeCellRenderer
338   {
339     // the table
340     protected JTable table;
341     // the data model
342     protected DefaultTableModel tableModel;
343 
344     public ComplexNodeCellRenderer()
345     {
346       super(new BorderLayout());
347 
348       // create a sample table model with the first column being editable
349       tableModel = new DefaultTableModel(new Object[][]{{"Keys", "Values"}}, new Object[]{"Key", "Value"}) {
350         public boolean isCellEditable(int row, int column) {
351           return column == 1;
352         }
353       };
354 
355       setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(3,3,3,3), BorderFactory.createEtchedBorder()));
356       add(table = new JTable(tableModel), BorderLayout.CENTER);
357       add(table.getTableHeader(), BorderLayout.NORTH);
358     }
359 
360     public JComponent getNodeCellRendererComponent(Graph2DView view, NodeRealizer nodeRealizer, Object userObject, boolean selected)
361     {
362       // initialize the value in the model
363       tableModel.setValueAt(userObject, 0, 1);
364       return this;
365     }
366 
367     public Object getValue()
368     {
369       // construct the value from the model
370       return tableModel.getValueAt(0, 1);
371     }
372   }
373 
374 
375   /**
376    * Launches this demo.
377    */
378   public static void main(String[] args)
379   {
380     initLnF();
381     SwingRendererDemo demo = new SwingRendererDemo();
382     demo.start("Swing Renderer Demo");
383   }
384 }
385