View Javadoc

1   // Copyright 2003-2005, FreeHEP.
2   package hep.wired.plot;
3   
4   import java.awt.*;
5   import java.awt.event.*;
6   import java.awt.geom.*;
7   import java.io.*;
8   import java.lang.reflect.*;
9   import java.util.*;
10  import java.util.logging.*;
11  import javax.swing.*;
12  import javax.swing.event.*;
13  import javax.swing.undo.*;
14  
15  import org.jdom.Element;
16  
17  import org.freehep.application.Application;
18  import org.freehep.application.studio.Studio;
19  import org.freehep.graphics2d.VectorGraphics;
20  import org.freehep.swing.Headless;
21  import org.freehep.swing.layout.StackedLayout;
22  import org.freehep.swing.popup.HasPopupItems;
23  import org.freehep.swing.undo.AnimatedEditSupport;
24  import org.freehep.swing.undo.DoableEdit;
25  import org.freehep.swing.undo.UndoableEditProcessor;
26  import org.freehep.xml.io.XMLIO;
27  import org.freehep.xml.io.XMLIOManager;
28  import org.freehep.util.Value;
29  
30  import hep.wired.edit.WiredEdit;
31  import hep.wired.edit.WiredCompoundEdit;
32  import hep.wired.edit.NoOperation;
33  import hep.wired.edit.GraphicsPanelEdit;
34  import hep.wired.edit.Reset;
35  import hep.wired.services.GraphicsPanel;
36  import hep.wired.services.InteractionHandler;
37  import hep.wired.services.RecordPlot;
38  import hep.wired.services.ViewPort;
39  import hep.wired.util.NullEvent;
40  import hep.wired.util.SwingEventMulticaster;
41  
42  /***
43   * Defines a single event display plot.
44   *
45   * @author Mark Donszelmann
46   * @version $Id: WiredPlot.java 2091 2005-07-22 19:05:06Z duns $
47   */
48  public class WiredPlot extends JPanel
49                         implements RecordPlot, HasPopupItems, XMLIO {
50  
51      private static Logger logger = Logger.getLogger(WiredPlot.class.getPackage().getName());
52      private long t0;
53      
54      private String name;
55      private GraphicsPanel graphicsPanel;
56      private UndoableEditSupport undoSupport;
57      
58      private transient boolean selected;
59      private transient boolean needsReset;
60      private transient ChangeListener changeListenerList;
61  
62      // For use in XMLIO restore
63      private WiredPlot() {
64          this(null, null, false, null);
65      }
66  
67      /***
68       * Creates a plot without a record.
69       */
70      public WiredPlot(GraphicsPanel graphicsPanel) {
71          this("Plot", graphicsPanel);
72      }
73  
74      /***
75       * Creates a plot with default GraphicsMode.
76       */
77      public WiredPlot(String name, GraphicsPanel graphicsPanel) {
78          this(name, graphicsPanel, true);
79      }
80      
81      /***
82       * Creates a plot with given parameters.
83       */
84      public WiredPlot(String name, GraphicsPanel graphicsPanel, boolean supportUndo) {
85           this(name, graphicsPanel, supportUndo, null);
86          // NOTE, default interaction handler missing
87      }
88      
89      // NOTE the InteractionHandler is there but never used, only to make this ctor different from 
90      // the one above.
91      private WiredPlot(String name, GraphicsPanel graphicsPanel, boolean supportUndo, InteractionHandler interactionHandler) {
92          super(false);
93          this.name = name;
94          this.selected = false;
95          this.needsReset = true;
96          
97          // set internal behaviour
98          setLayout(new BorderLayout());
99          setOpaque(false);
100 
101         // create graphics panel
102         if (graphicsPanel != null) setGraphicsPanel(graphicsPanel);
103         
104         // undo support
105         if (supportUndo) addUndoSupport();
106         
107         // set the interaction handler
108         if (interactionHandler != null) setInteractionHandler(interactionHandler);
109     }
110 
111     /***
112      * Returns a copy of this plot, attached to the given objects, from the given parameters.
113      */
114     public RecordPlot copy(String name, Object record, boolean supportUndo) {
115         WiredPlot copy = new WiredPlot(name, graphicsPanel.copy(), supportUndo, null);
116 
117         copy.setInteractionHandler(getInteractionHandler());
118         copy.setRecord(record);
119 
120         copy.color = color;
121         copy.lineWidth = lineWidth;
122         copy.frameColor = frameColor;
123         copy.frameWidth = frameWidth;
124         copy.shape = shape;
125 
126         return copy;
127     }
128 
129     public void addChangeListener(ChangeListener listener) {
130         changeListenerList = SwingEventMulticaster.add(changeListenerList, listener);
131     }
132     
133     public void removeChangeListener(ChangeListener listener) {
134         changeListenerList = SwingEventMulticaster.remove(changeListenerList, listener);
135     }
136 
137     private void setGraphicsPanel(GraphicsPanel newPanel) {
138         if (graphicsPanel != null) remove((Component)graphicsPanel);
139         
140         graphicsPanel = newPanel;
141         add((Component)graphicsPanel, BorderLayout.CENTER);
142     }        
143 
144     private void addUndoSupport() {
145         undoSupport = new AnimatedEditSupport(this);
146     }
147     
148     /***
149      * Returns the undo support object, or null if undo was not switched on.
150      */
151     public UndoableEditSupport getUndoableEditSupport() {
152         return undoSupport;
153     }
154     
155     /***
156      * Returns the name of the plot.
157      */       
158     public String getName() {
159         return name;
160     }
161 
162     public boolean requestFocusInWindow() {
163         return ((Component)graphicsPanel).requestFocusInWindow();
164     }
165 
166     /***
167      * Returns the associated Graphics Panel.
168      */
169     public GraphicsPanel getGraphicsPanel() {
170         return graphicsPanel;
171     }
172 
173     public void setSelected(boolean selected) {
174         this.selected = selected;
175         graphicsPanel.setSelected(this, selected);
176         if (handler != null) handler.setSelected(this, selected);
177     }
178     
179     public void repaint() {
180         super.repaint();
181         if (graphicsPanel != null) ((Component)graphicsPanel).repaint();
182     }
183 
184     // NOTE, to make sure calculations can be done inside setBounds in either InteractionPanel or
185     // graphicsPanel, we resize the graphicsPanel and interactionPanel immediately.
186     public void setBounds(int x, int y, int width, int height) {
187         ((Component)graphicsPanel).setBounds(x, y, width, height);
188         if (handler != null) handler.setSize(this, width, height);
189         super.setBounds(x, y, width, height);
190     }
191 
192     public String toString() {
193         return (getName() == null) ? "WiredPlot@"+hashCode() : "WiredPlot: "+getName();
194     }
195 
196 //
197 // UndoableEditProcessor interface
198 //
199     /***
200      * Returns true if given edit is supported, which may depend on the current graphics panel.
201      */
202     public boolean supports(UndoableEdit edit) {
203         if (edit instanceof NoOperation) return true;
204                 
205         if (edit instanceof GraphicsPanelEdit) {
206             GraphicsPanelEdit graphicsPanelEdit = (GraphicsPanelEdit)edit;
207             return graphicsPanelEdit.isSupportedBy(getGraphicsPanel());
208         }
209         
210         return false;
211     }
212 
213     /***
214      * Handles an undoable edit, please use postEdit(WiredEdit).
215      */
216     public void postEdit(UndoableEdit edit) {
217         if (edit instanceof DoableEdit) {
218             postEdit((DoableEdit)edit);
219             return;
220         }
221         logger.warning("Edit ignored: "+edit);
222     }
223 
224     /***
225      * Handles an doable edit, please use postEdit(WiredEdit).
226      */
227     public void postEdit(DoableEdit edit) {
228         if (edit instanceof WiredEdit) {
229             postEdit((WiredEdit) edit);
230             return;
231         } 
232         if (edit instanceof WiredCompoundEdit) {
233             postEdit((WiredCompoundEdit) edit);
234             return;
235         } 
236         logger.warning("Edit should be Wired(Compound)Edit...: "+edit);
237         edit.redo();
238         if (edit.isSignificant()) {
239             if (undoSupport != null) undoSupport.postEdit(edit);
240         }
241     }
242 
243     /***
244      * Handles a Wired Edit. You may post the same edit to different plots, since edits are copied and
245      * afterwards attached to the plot. Any edits posted can be undone, if undoable support was switched
246      * on at creation time of the plot. Each plot has its own undo support.
247      */
248     public void postEdit(WiredEdit edit) {
249         WiredEdit wiredEdit = edit.copy(this);
250         
251 //        logger.info(wiredEdit.toString());
252         wiredEdit.redo();
253         if (wiredEdit.isSignificant()) {
254             if (undoSupport != null) undoSupport.postEdit(wiredEdit);
255         }
256     }
257 
258     /***
259      * Handles a Wired Compound Edit. You may post the same edit to different plots, since edits are copied 
260      * (recursively) and
261      * afterwards attached to the plot. Any edits posted can be undone, if undoable support was switched
262      * on at creation time of the plot. Each plot has its own undo support.
263      */
264     public void postEdit(WiredCompoundEdit edit) {
265         WiredCompoundEdit wiredEdit = edit.copy(this);
266         
267 //        logger.info(wiredEdit.toString());
268         wiredEdit.redo();
269         if (wiredEdit.isSignificant()) {
270             if (undoSupport != null) undoSupport.postEdit(wiredEdit);
271         }
272     }
273 
274     /***
275      * If multiple edits need to count as one major update, call this method to start a compound edit.
276      */
277     public void beginUpdate() {
278         if (undoSupport != null) undoSupport.beginUpdate();
279     }
280 
281     /***
282      * If multiple edits need to count as one major update, call this method to end a compound edit.
283      */
284     public void endUpdate() {
285         if (undoSupport != null) undoSupport.endUpdate();
286     }
287 
288     /***
289      * Returns true if this plot (and its projection) supports the given interactionHandler.
290      */
291     public boolean supports(InteractionHandler interactionHandler) {
292         return interactionHandler != null ? interactionHandler.isSupportedBy(getGraphicsPanel()) : true;
293     }
294 
295 // For the interaction handler
296     private InteractionHandler handler;
297     private transient boolean pressed, dragged, clicked;
298     private transient MouseEvent pressEvent;
299 
300     private transient MouseListener mouseListener;
301     private transient MouseMotionListener mouseMotionListener;
302     private transient MouseWheelListener mouseWheelListener;
303     private transient KeyListener keyListener;
304 
305 // For the feedback shape drawing
306     private Color color = Color.white;
307     private double lineWidth = 1.0;
308     private Color frameColor = Color.black;
309     private double frameWidth = 3.0;   
310     private Shape shape;
311 
312     public void setInteractionHandler(InteractionHandler interactionHandler) {
313 
314         if (selected) {
315             if (handler != null) handler.setSelected(this, false);
316             if (interactionHandler != null) interactionHandler.setSelected(this, true);
317         }
318 
319         Component panel = (Component)graphicsPanel;
320 
321         if (mouseListener != null) panel.removeMouseListener(mouseListener);
322         if (mouseMotionListener != null) panel.removeMouseMotionListener(mouseMotionListener);
323         if (mouseWheelListener != null) panel.removeMouseWheelListener(mouseWheelListener);
324         if (keyListener != null) panel.removeKeyListener(keyListener);
325 
326         handler = interactionHandler;
327         // clear current shape
328         drawShape(null);
329         repaint();
330         
331         if (handler == null) {
332             setCursor(null);
333         } else {
334             handler.setSize(this, getWidth(), getHeight());
335             handler.reset(this, new NullEvent(this));
336             handler.changeCursor(this, new NullEvent(this));
337 
338             mouseListener = new MouseListener() {
339 
340                 public void mousePressed(MouseEvent event) {
341                     changeListenerList.stateChanged(new ChangeEvent(this));
342                     
343                     if (SwingUtilities.isLeftMouseButton(event)) {
344 //                    System.err.println("Pressed ");
345 //                    System.err.print(SwingUtilities.getRoot(event.getComponent()).isFocusOwner());
346                         event.getComponent().requestFocusInWindow();
347 //                    System.err.println(SwingUtilities.getRoot(event.getComponent()).isFocusOwner());
348                         pressed = true;
349                         pressEvent = event;
350                         clicked = true;
351                         dragged = false;
352                         handler.mouseButton1Pressed(WiredPlot.this, event);
353                     }
354                 }
355             
356                 public void mouseReleased(MouseEvent event) {
357                     if (SwingUtilities.isLeftMouseButton(event)) {
358 //                        System.err.print("Released ");
359 //                    System.err.println(SwingUtilities.getRoot(event.getComponent()).isFocusOwner());
360                         if (dragged) {
361                             handler.mouseButton1DragEnded(WiredPlot.this, event);
362                             dragged = false;
363                         }
364                         handler.mouseButton1Released(WiredPlot.this, event);
365                         pressed = false;
366                         pressEvent = null;
367                     }
368                 }
369             
370                 public void mouseClicked(MouseEvent event) {
371                     if (SwingUtilities.isLeftMouseButton(event)) {
372                         if (clicked) {
373 //                        System.err.print("Clicked ");
374 //                    System.err.println(SwingUtilities.getRoot(event.getComponent()).isFocusOwner());
375                             handler.mouseButton1Clicked(WiredPlot.this, event);
376                             clicked = false;
377                         }
378                         pressed = false;
379                         pressEvent = null;
380                         dragged = false;
381                     }
382                 }
383             
384                 public void mouseEntered(MouseEvent event) {
385 //                    System.err.print("Entered ");
386 //                    System.err.println(SwingUtilities.getRoot(event.getComponent()).isFocusOwner());
387                     event.getComponent().requestFocusInWindow();
388                     handler.mouseEntered(WiredPlot.this, event);
389                 }
390                 
391                 public void mouseExited(MouseEvent event) {
392 //                    System.err.print("Exited ");
393 //                    System.err.println(SwingUtilities.getRoot(event.getComponent()).isFocusOwner());
394                     handler.mouseExited(WiredPlot.this, event);
395                 }
396             };
397             panel.addMouseListener(mouseListener);
398             
399             mouseMotionListener = new MouseMotionListener() { 
400                 public void mouseDragged(MouseEvent event) {
401 //                    System.err.print("Dragged ");
402 //                    System.err.println(SwingUtilities.getRoot(event.getComponent()).isFocusOwner());
403                     if (SwingUtilities.isLeftMouseButton(event)) {
404                         if (pressed) {
405                             if (pressEvent != null) {
406                                 dragged = true;
407                                 clicked = false;
408                                 handler.mouseButton1DragStarted(WiredPlot.this, pressEvent);
409                                 pressEvent = null;
410                             }
411                             handler.mouseButton1Dragged(WiredPlot.this, event);
412                         }
413                     }
414                 }
415                 public void mouseMoved(MouseEvent event) {
416 //                    System.err.print("Moved ");
417 //                    System.err.println(SwingUtilities.getRoot(event.getComponent()).isFocusOwner());
418                     handler.mouseMoved(WiredPlot.this, event);
419                 }
420             };
421             panel.addMouseMotionListener(mouseMotionListener);
422             
423             mouseWheelListener = new MouseWheelListener() { 
424                 public void mouseWheelMoved(MouseWheelEvent event) {
425 //                    System.err.print("WheelMoved ");
426 //                    System.err.println(SwingUtilities.getRoot(event.getComponent()).isFocusOwner());
427                     handler.mouseWheelMoved(WiredPlot.this, event);
428                 }
429             };
430             panel.addMouseWheelListener(mouseWheelListener);
431 
432             keyListener = new KeyListener() {
433                 private boolean altKeyDown, ctrlKeyDown, shiftKeyDown, escKeyDown;
434 
435                 public void keyPressed(KeyEvent event) {
436                     boolean consume = false;
437                     switch(event.getKeyCode()) {
438                         case KeyEvent.VK_ALT:
439                             if (!altKeyDown) {
440                                 altKeyDown = true;
441                                 consume = handler.altKeyPressed(WiredPlot.this, event);
442                             }
443                             break;
444                         case KeyEvent.VK_CONTROL:
445                             if (!ctrlKeyDown) {
446                                 ctrlKeyDown = true;
447                                 consume = handler.ctrlKeyPressed(WiredPlot.this, event);
448                             }
449                             break;
450                         case KeyEvent.VK_SHIFT:
451                             if (!shiftKeyDown) {
452                                 shiftKeyDown = true;
453                                 consume = handler.shiftKeyPressed(WiredPlot.this, event);
454                             }
455                             break;
456                         case KeyEvent.VK_ESCAPE:
457                             if (!escKeyDown) {
458                                 escKeyDown = true;
459                                 pressed = false;
460                                 dragged = false;
461                                 handler.reset(WiredPlot.this, event);
462                                 consume = handler.escKeyPressed(WiredPlot.this, event);
463                             }
464                             break;
465                         default:
466                             consume = handler.otherKeyPressed(WiredPlot.this, event);
467                             break;
468                     }
469                     if (consume) event.consume();
470                 }
471             
472                 public void keyReleased(KeyEvent event) {
473                     boolean consume = false;
474                     switch(event.getKeyCode()) {
475                         case KeyEvent.VK_ALT:
476                             if (altKeyDown) {
477                                 altKeyDown = false;
478                                 consume = handler.altKeyReleased(WiredPlot.this, event);
479                             }
480                             break;
481                         case KeyEvent.VK_CONTROL:
482                             if (ctrlKeyDown) {
483                                 ctrlKeyDown = false;
484                                 consume = handler.ctrlKeyReleased(WiredPlot.this, event);
485                             }
486                             break;
487                         case KeyEvent.VK_SHIFT:
488                             if (shiftKeyDown) {
489                                 shiftKeyDown = false;
490                                 consume = handler.shiftKeyReleased(WiredPlot.this, event);
491                             }
492                             break;
493                         case KeyEvent.VK_ESCAPE:
494                             if (escKeyDown) {
495                                 escKeyDown = false;
496                                 consume = handler.escKeyReleased(WiredPlot.this, event);
497                             }
498                             break;
499                         default:
500                             consume = handler.otherKeyReleased(WiredPlot.this, event);
501                             break;
502                     }
503                     if (consume) event.consume();
504                 }        
505         
506                 public void keyTyped(KeyEvent event) {
507                     boolean consume = handler.keyTyped(WiredPlot.this, event);
508                     if (consume) event.consume();                    
509                 }
510             };
511             panel.addKeyListener(keyListener);
512         }
513         panel.requestFocusInWindow();
514     }
515 
516     public void drawShape(Shape shape) {
517         this.shape = shape;
518         // only recopy the graphicspanel, repaint the shape
519         super.repaint();
520     }
521 
522     public void paint(Graphics g) {
523         super.paint(g);
524 
525 // Not needed
526 //        if (graphicsPanel != null) ((Component)graphicsPanel).paint(g);
527 
528         if (shape != null) {
529             VectorGraphics graphics = VectorGraphics.create(g);
530             // set miterlimit to 1 to avoid jaggy edges
531             BasicStroke cs = (BasicStroke)graphics.getStroke();
532             graphics.setStroke(new BasicStroke(cs.getLineWidth(),
533                                                cs.getEndCap(),
534                                                cs.getLineJoin(),
535                                                1.0f,
536                                                cs.getDashArray(),
537                                                cs.getDashPhase()));
538 
539             graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
540                                       RenderingHints.VALUE_ANTIALIAS_ON);
541 
542             graphics.setColor(frameColor);
543             graphics.setLineWidth(frameWidth);
544             graphics.draw(shape);
545             graphics.setLineWidth(lineWidth);
546             graphics.setColor(color);
547             graphics.draw(shape);
548         }        
549     }
550 
551     /***
552      * Return the current interaction handler.
553      */
554     public InteractionHandler getInteractionHandler() {
555         return handler;
556     }
557 
558     /***
559      * Sets the plot to use this Object.
560      */
561     public void setRecord(Object record) {
562         graphicsPanel.setRecord(record);
563         if (handler != null) handler.setRecord(this, record);
564         
565         if (needsReset && (record != null)) {
566             postEdit(new Reset());
567             needsReset = false;
568         }                
569         
570         repaint();
571     }
572 
573     /***
574      * Returns the current Object.
575      */
576     public Object getRecord() { 
577         return graphicsPanel.getRecord();
578     }
579 
580 //
581 // HasPopupItems interface
582 //
583     public JPopupMenu modifyPopupMenu(JPopupMenu menu, Component source, Point p) {
584         Application.getApplication().getXMLMenuBuilder().mergePopupMenu("wiredBasePopupMenu", menu);
585         menu = graphicsPanel.modifyPopupMenu(menu, this);
586         return menu;    
587     }
588         
589 //
590 // XMLIO
591 //
592     public void save(XMLIOManager xmlioManager,
593                      org.jdom.Element nodeEl) {
594         if (name != null) nodeEl.setAttribute("name", name);
595         nodeEl.addContent(xmlioManager.save(graphicsPanel));
596         if (handler != null) nodeEl.addContent(xmlioManager.save(handler));
597         nodeEl.setAttribute("undo", undoSupport != null ? "true" : "false");
598     }
599 
600     public void restore(XMLIOManager xmlioManager,
601                         org.jdom.Element nodeEl) { 
602         name = nodeEl.getAttributeValue("name");
603         Iterator i = nodeEl.getChildren().iterator();
604         setGraphicsPanel((GraphicsPanel)xmlioManager.restore((Element)i.next()));
605         if (i.hasNext()) {
606             setInteractionHandler((InteractionHandler)xmlioManager.restore((Element)i.next()));
607         }
608         if (nodeEl.getAttributeValue("undo","").equals("true")) addUndoSupport();       
609     }     
610 }