1   /*
2    *  Copyright (c) 1998-2005, The University of Sheffield.
3    *
4    *  This file is part of GATE (see http://gate.ac.uk/), and is free
5    *  software, licenced under the GNU Library General Public License,
6    *  Version 2, June 1991 (in the distribution as file licence.html,
7    *  and also available at http://gate.ac.uk/gate/licence.html).
8    *
9    *  Valentin Tablan, 22 March 2004
10   *
11   *  $Id: DocumentEditor.java,v 1.14 2005/01/11 13:51:35 ian Exp $
12   */
13  package gate.gui.docview;
14  
15  import java.awt.*;
16  import java.awt.event.*;
17  import java.util.*;
18  import java.util.List;
19  
20  import javax.swing.*;
21  
22  import gate.*;
23  import gate.creole.*;
24  import gate.gui.ActionsPublisher;
25  import gate.swing.VerticalTextIcon;
26  import gate.util.GateRuntimeException;
27  
28  /**
29   * This is the GATE Document viewer/editor. This class is only the shell of the
30   * main document VR, which gets populated with views (objects that implement
31   * the {@link DocumentView} interface.
32   */
33  
34  public class DocumentEditor extends AbstractVisualResource
35                              implements ActionsPublisher {
36  
37    /**
38     * The document view is just an empty shell. This method publishes the actions
39     * from the contained views. 
40     */
41    public List getActions() {
42      List actions = new ArrayList();
43      Iterator viewIter;
44      if(getCentralViews() != null){
45        viewIter = getCentralViews().iterator();
46        while(viewIter.hasNext()){
47          actions.addAll(((DocumentView)viewIter.next()).getActions());
48        }
49      }
50      if(getHorizontalViews() != null){
51        viewIter = getHorizontalViews().iterator();
52        while(viewIter.hasNext()){
53          actions.addAll(((DocumentView)viewIter.next()).getActions());
54        }
55      }
56      if(getVerticalViews() != null){
57        viewIter = getVerticalViews().iterator();
58        while(viewIter.hasNext()){
59          actions.addAll(((DocumentView)viewIter.next()).getActions());
60        }
61      }
62      return actions;
63    }
64  
65  
66    /* (non-Javadoc)
67     * @see gate.Resource#init()
68     */
69    public Resource init() throws ResourceInstantiationException {
70      addComponentListener(new ComponentAdapter() {
71        public void componentHidden(ComponentEvent e) {
72        }
73        public void componentMoved(ComponentEvent e) {
74        }
75        public void componentResized(ComponentEvent e) {
76        }
77        //lazily build the GUI only when needed
78        public void componentShown(ComponentEvent e) {
79          if(!viewsInited) initViews();
80        }
81      });
82  
83      return this;
84    }
85    
86    public void cleanup(){
87      Iterator viewsIter;
88      if(centralViews != null){ 
89        viewsIter= centralViews.iterator();
90        while(viewsIter.hasNext()) ((Resource)viewsIter.next()).cleanup();
91        centralViews.clear();
92      }
93      if(horizontalViews != null){
94        viewsIter = horizontalViews.iterator();
95        while(viewsIter.hasNext()) ((Resource)viewsIter.next()).cleanup();
96        horizontalViews.clear();
97      }
98      if(verticalViews != null){
99        viewsIter = verticalViews.iterator();
100       while(viewsIter.hasNext()) ((Resource)viewsIter.next()).cleanup();
101       verticalViews.clear();
102     }
103   }
104   
105   protected void initViews(){
106     viewsInited = true;
107     //start building the UI
108     setLayout(new BorderLayout());
109     JProgressBar progressBar = new JProgressBar();
110     progressBar.setStringPainted(true);
111     progressBar.setMaximumSize(new Dimension(Integer.MAX_VALUE, progressBar.getPreferredSize().height));
112     add(progressBar, BorderLayout.CENTER);
113 
114     progressBar.setString("Building views");
115     progressBar.setValue(10);
116 
117     //create the skeleton UI
118     topSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, null, null);
119     topSplit.setResizeWeight(0.3);
120     bottomSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, topSplit, null);
121     bottomSplit.setResizeWeight(0.7);
122     horizontalSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, bottomSplit, null);
123     horizontalSplit.setResizeWeight(0.7);
124 
125     //create the bars
126     topBar = new JToolBar(JToolBar.HORIZONTAL);
127     topBar.setFloatable(false);
128     add(topBar, BorderLayout.NORTH);
129 
130 //    bottomBar = new JToolBar(JToolBar.HORIZONTAL);
131 //    bottomBar.setFloatable(false);
132 //    add(bottomBar, BorderLayout.SOUTH);
133 
134 //    leftBar = new JToolBar(JToolBar.VERTICAL);
135 //    leftBar.setFloatable(false);
136 //    add(leftBar, BorderLayout.WEST);
137 
138 //    rightBar = new JToolBar(JToolBar.VERTICAL);
139 //    rightBar.setFloatable(false);
140 //    add(rightBar, BorderLayout.EAST);
141 
142     progressBar.setValue(40);
143 
144     
145     centralViews = new ArrayList();
146     verticalViews = new ArrayList();
147     horizontalViews = new ArrayList();
148 
149     //parse all Creole resources and look for document views
150     Set vrSet = Gate.getCreoleRegister().getVrTypes();
151     List viewTypes = new ArrayList();
152     Iterator vrIter = vrSet.iterator();
153     while(vrIter.hasNext()){
154       ResourceData rData = (ResourceData)Gate.getCreoleRegister().
155                            get(vrIter.next());
156       try{
157         if(DocumentView.class.isAssignableFrom(rData.getResourceClass())){
158           viewTypes.add(rData);
159         }
160       }catch(ClassNotFoundException cnfe){
161         cnfe.printStackTrace();
162       }
163     }
164     //sort view types by label
165     Collections.sort(viewTypes, new Comparator(){
166       public int compare(Object o1, Object o2){
167         ResourceData rd1 = (ResourceData)o1;
168         ResourceData rd2 = (ResourceData)o2;
169         return rd1.getName().compareTo(rd2.getName());
170       }
171     });
172     Iterator viewIter = viewTypes.iterator();
173     while(viewIter.hasNext()){
174       ResourceData rData = (ResourceData)viewIter.next();
175       try{
176         //create the resource
177         DocumentView aView = (DocumentView)Factory.
178                              createResource(rData.getClassName());
179         aView.setTarget(document);
180         aView.setOwner(this);
181         //add the view
182         addView(aView, rData.getName());
183       }catch(ResourceInstantiationException rie){
184             rie.printStackTrace();
185       }
186     }
187     //select the main central view only
188     if(centralViews.size() > 0) setCentralView(0);
189     
190     //populate the main VIEW
191     remove(progressBar);
192     add(horizontalSplit, BorderLayout.CENTER);
193     validate();
194   }
195   
196   public List getCentralViews(){
197     return centralViews == null ? null : 
198       Collections.unmodifiableList(centralViews);
199   }
200   
201   public List getHorizontalViews(){
202     return horizontalViews == null ? null : 
203       Collections.unmodifiableList(horizontalViews);
204   }
205   
206   public List getVerticalViews(){
207     return verticalViews == null ? null : 
208       Collections.unmodifiableList(verticalViews);
209   }
210   
211 
212   /**
213    * Registers a new view by adding it to the right list and creating the 
214    * activation button for it.
215    * @param view
216    */
217   protected void addView(DocumentView view, String name){
218     topBar.add(Box.createHorizontalStrut(5));
219     switch(view.getType()){
220       case DocumentView.CENTRAL :
221         centralViews.add(view);
222 //        leftBar.add(new ViewButton(view, name));
223         topBar.add(new ViewButton(view, name));
224         break;
225       case DocumentView.VERTICAL :
226         verticalViews.add(view);
227 //        rightBar.add(new ViewButton(view, name));
228         topBar.add(new ViewButton(view, name));
229         break;
230       case DocumentView.HORIZONTAL :
231         horizontalViews.add(view);
232         topBar.add(new ViewButton(view, name));
233 //        bottomBar.add(new ViewButton(view, name));
234         break;
235       default :
236         throw new GateRuntimeException(getClass().getName() +  ": Invalid view type");
237     }
238   }
239   
240   
241   /**
242    * Gets the currently showing top view
243    * @return a {@link DocumentView} object.
244    */
245   protected DocumentView getTopView(){
246     if(topViewIdx == -1) return null;
247     else return(DocumentView)horizontalViews.get(topViewIdx);
248   }
249   
250   /**
251    * Shows a new top view based on an index in the {@link #horizontalViews}
252    * list.
253    * @param index the index in {@link #horizontalViews} list for the new
254    * view to be shown.
255    */
256   protected void setTopView(int index){
257     //deactivate current view
258     DocumentView oldView = getTopView();
259     if(oldView != null){
260       oldView.setActive(false);
261     }
262     topViewIdx = index;
263     if(topViewIdx == -1) setTopView(null);
264     else{
265       DocumentView newView = (DocumentView)horizontalViews.get(topViewIdx);
266       //hide if shown at the bottom
267       if(bottomViewIdx == topViewIdx){
268         setBottomView(null);
269         bottomViewIdx  = -1;
270       }
271       //activate if necessary
272       if(!newView.isActive()){
273         newView.setActive(true);
274       }
275       //show the new view
276       setTopView(newView);
277     }
278   }
279 
280   /**
281    * Sets a new UI component in the top location. This method is intended to 
282    * only be called from {@link #setTopView(int)}.
283    * @param view the new view to be shown.
284    */
285   protected void setTopView(DocumentView view){
286     topSplit.setTopComponent(view == null ? null : view.getGUI());
287     topSplit.resetToPreferredSizes();
288     updateBar(topBar);
289     validate();
290   }
291 
292   /**
293    * Gets the currently showing central view
294    * @return a {@link DocumentView} object.
295    */
296   protected DocumentView getCentralView(){
297     if(centralViewIdx == -1) return null;
298     else return(DocumentView)centralViews.get(centralViewIdx);
299   }
300   
301   /**
302    * Shows a new central view based on an index in the {@link #centralViews}
303    * list.
304    * @param index the index in {@link #centralViews} list for the new
305    * view to be shown.
306    */
307   protected void setCentralView(int index){
308     //deactivate current view
309     DocumentView oldView = getCentralView();
310     if(oldView != null){
311       oldView.setActive(false);
312     }
313     centralViewIdx = index;
314     if(centralViewIdx == -1) setCentralView(null);
315     else{
316       DocumentView newView = (DocumentView)centralViews.get(centralViewIdx);
317       //activate if necessary
318       if(!newView.isActive()){
319         newView.setActive(true);
320       }
321       //show the new view
322       setCentralView(newView);
323     }
324   }
325 
326   /**
327    * Sets a new UI component in the central location. This method is intended to 
328    * only be called from {@link #setCentralView(int)}.
329    * @param view the new view to be shown.
330    */
331   protected void setCentralView(DocumentView view){
332     topSplit.setBottomComponent(view == null ? null : view.getGUI());
333     topSplit.resetToPreferredSizes();
334 //    updateBar(leftBar);
335     updateBar(topBar);
336     validate();
337   }
338   
339   
340   /**
341    * Gets the currently showing bottom view
342    * @return a {@link DocumentView} object.
343    */
344   protected DocumentView getBottomView(){
345     if(bottomViewIdx == -1) return null;
346     else return(DocumentView)horizontalViews.get(bottomViewIdx);
347   }
348   
349   /**
350    * Shows a new bottom view based on an index in the {@link #horizontalViews}
351    * list.
352    * @param index the index in {@link #horizontalViews} list for the new
353    * view to be shown.
354    */
355   protected void setBottomView(int index){
356     //deactivate current view
357     DocumentView oldView = getBottomView();
358     if(oldView != null){
359       oldView.setActive(false);
360     }
361     bottomViewIdx = index;
362     if(bottomViewIdx == -1){
363       setBottomView(null);
364     }else{
365       DocumentView newView = (DocumentView)horizontalViews.get(bottomViewIdx);
366       //hide if shown at the top
367       if(topViewIdx == bottomViewIdx){
368         setTopView(null);
369         topViewIdx  = -1;
370       }
371       //activate if necessary
372       if(!newView.isActive()){
373         newView.setActive(true);
374       }
375       //show the new view
376       setBottomView(newView);
377     }
378   }
379 
380   /**
381    * Sets a new UI component in the top location. This method is intended to 
382    * only be called from {@link #setBottomView(int)}.
383    * @param view the new view to be shown.
384    */
385   protected void setBottomView(DocumentView view){
386     bottomSplit.setBottomComponent(view == null ? null : view.getGUI());
387     bottomSplit.resetToPreferredSizes();
388 //    updateBar(bottomBar);
389     updateBar(topBar);
390     validate();
391   }
392   
393   
394   /**
395    * Gets the currently showing right view
396    * @return a {@link DocumentView} object.
397    */
398   protected DocumentView getRightView(){
399     if(rightViewIdx == -1) return null;
400     else return(DocumentView)verticalViews.get(rightViewIdx);
401   }
402   
403   /**
404    * Shows a new right view based on an index in the {@link #verticalViews}
405    * list.
406    * @param index the index in {@link #verticalViews} list for the new
407    * view to be shown.
408    */
409   protected void setRightView(int index){
410     //deactivate current view
411     DocumentView oldView = getRightView();
412     if(oldView != null){
413       oldView.setActive(false);
414     }
415     rightViewIdx = index;
416     if(rightViewIdx == -1) setRightView(null);
417     else{
418       DocumentView newView = (DocumentView)verticalViews.get(rightViewIdx);
419       //activate if necessary
420       if(!newView.isActive()){
421         newView.setActive(true);
422       }
423       //show the new view
424       setRightView(newView);
425     }
426   }
427 
428   /**
429    * Sets a new UI component in the right hand side location. This method is 
430    * intended to only be called from {@link #setRightView(int)}.
431    * @param view the new view to be shown.
432    */
433   protected void setRightView(DocumentView view){
434     horizontalSplit.setRightComponent(view == null ? null : view.getGUI());
435 //    updateBar(rightBar);
436     updateBar(topBar);
437     validate();
438   }  
439   
440 
441   protected void updateSplitLocation(JSplitPane split, int foo){
442     Component left = split.getLeftComponent();
443     Component right = split.getRightComponent();
444     if(left == null){
445       split.setDividerLocation(0);
446       return;
447     }
448     if(right == null){ 
449       split.setDividerLocation(1);
450       return;
451     }
452     Dimension leftPS = left.getPreferredSize();
453     Dimension rightPS = right.getPreferredSize();
454     double location = split.getOrientation() == JSplitPane.HORIZONTAL_SPLIT ? 
455       (double)leftPS.width / (leftPS.width + rightPS.width) :
456       (double)leftPS.height / (leftPS.height + rightPS.height);
457     split.setDividerLocation(location);
458   }
459   
460   /* (non-Javadoc)
461    * @see gate.VisualResource#setTarget(java.lang.Object)
462    */
463   public void setTarget(Object target) {
464     this.document = (Document)target;
465   }
466 
467   /**
468    * Updates the selected state of the buttons on one of the toolbars. 
469    * @param toolbar
470    */
471   protected void updateBar(JToolBar toolbar){
472     Component btns[] = toolbar.getComponents();
473     if(btns != null){
474       for(int i = 0; i < btns.length; i++){
475         if(btns[i] instanceof ViewButton) 
476           ((ViewButton)btns[i]).updateSelected();
477       }
478     }
479   }
480 
481   protected class ViewButton extends JToggleButton{
482     public ViewButton(DocumentView aView, String name){
483       super();
484       setSelected(false);
485 //      setBorder(null);
486       this.view = aView;
487       setText(name);
488       
489 //      if(aView.getType() == DocumentView.HORIZONTAL){
490 //        setText(name);
491 //      }else if(aView.getType() == DocumentView.CENTRAL){
492 //        setIcon(new VerticalTextIcon(this, name, VerticalTextIcon.ROTATE_LEFT));
493 //      }else if(aView.getType() == DocumentView.VERTICAL){
494 //        setIcon(new VerticalTextIcon(this, name, 
495 //                                     VerticalTextIcon.ROTATE_RIGHT));
496 //      }
497       
498       addActionListener(new ActionListener(){
499         public void actionPerformed(ActionEvent evt){
500           if(isSelected()){
501             //show this new view
502             switch(view.getType()){
503               case DocumentView.CENTRAL:
504                 setCentralView(centralViews.indexOf(view));
505                 break;
506               case DocumentView.VERTICAL:
507                 setRightView(verticalViews.indexOf(view));
508                 break;
509               case DocumentView.HORIZONTAL:
510                 if(ViewButton.this.getParent() == topBar){
511                   setTopView(horizontalViews.indexOf(view));
512                 }else{
513                   setBottomView(horizontalViews.indexOf(view));
514                 }
515                 break;
516             }
517           }else{
518             //hide this view
519             switch(view.getType()){
520               case DocumentView.CENTRAL:
521                 setCentralView(-1);
522                 break;
523               case DocumentView.VERTICAL:
524                 setRightView(-1);
525                 break;
526               case DocumentView.HORIZONTAL:
527                 if(ViewButton.this.getParent() == topBar){
528                   setTopView(-1);
529                 }else{
530                   setBottomView(-1);
531                 }
532                 break;
533             }
534           }
535         }
536       });
537     }
538     
539     public void updateSelected(){
540       switch(view.getType()){
541         case DocumentView.CENTRAL:
542           setSelected(getCentralView() == view);
543           break;
544         case DocumentView.VERTICAL:
545           setSelected(getRightView() == view);
546           break;
547         case DocumentView.HORIZONTAL:
548           if(ViewButton.this.getParent() == topBar){
549             setSelected(getTopView() == view);
550           }else{
551             setSelected(getBottomView() == view);
552           }
553           break;
554       }
555     }
556     DocumentView view;
557   }
558 
559   protected JSplitPane horizontalSplit;
560   protected JSplitPane topSplit;
561   protected JSplitPane bottomSplit;
562 
563   protected JToolBar topBar;
564 //  protected JToolBar rightBar;
565 //  protected JToolBar leftBar;
566 //  protected JToolBar bottomBar;
567 
568   protected Document document;
569 
570 
571   /**
572    * A list of {@link DocumentView} objects of type {@link DocumentView#CENTRAL}
573    */
574   protected List centralViews;
575   
576   /**
577    * A list of {@link DocumentView} objects of type 
578    * {@link DocumentView#VERTICAL}
579    */
580   protected List verticalViews;
581 
582   /**
583    * A list of {@link DocumentView} objects of type 
584    * {@link DocumentView#HORIZONTAL}
585    */
586   protected List horizontalViews;
587 
588   /**
589    * The index in {@link #centralViews} of the currently active central view.
590    * <code>-1</code> if none is active.
591    */
592   protected int centralViewIdx = -1;
593 
594   /**
595    * The index in {@link #verticalViews} of the currently active right view.
596    * <code>-1</code> if none is active.
597    */
598   protected int rightViewIdx = -1;
599   
600   /**
601    * The index in {@link #horizontalViews} of the currently active top view.
602    * <code>-1</code> if none is active.
603    */
604   protected int topViewIdx = -1;
605   
606   /**
607    * The index in {@link #horizontalViews} of the currently active bottom view.
608    * <code>-1</code> if none is active.
609    */
610   protected int bottomViewIdx = -1;
611   
612   protected boolean viewsInited = false;
613 
614 }