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