Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/gjt/sp/jedit/View.java


1   /*
2    * View.java - jEdit view
3    * :tabSize=8:indentSize=8:noTabs=false:
4    * :folding=explicit:collapseFolds=1:
5    *
6    * Copyright (C) 1998, 2003 Slava Pestov
7    *
8    * This program is free software; you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License
10   * as published by the Free Software Foundation; either version 2
11   * of the License, or any later version.
12   *
13   * This program is distributed in the hope that it will be useful,
14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   * GNU General Public License for more details.
17   *
18   * You should have received a copy of the GNU General Public License
19   * along with this program; if not, write to the Free Software
20   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21   */
22  
23  package org.gjt.sp.jedit;
24  
25  //{{{ Imports
26  import javax.swing.event.*;
27  import javax.swing.text.*;
28  import javax.swing.*;
29  import java.awt.*;
30  import java.awt.event.*;
31  import java.io.IOException;
32  import java.io.StreamTokenizer;
33  import java.io.StringReader;
34  import java.net.Socket;
35  import java.util.*;
36  import org.gjt.sp.jedit.msg.*;
37  import org.gjt.sp.jedit.gui.*;
38  import org.gjt.sp.jedit.search.*;
39  import org.gjt.sp.jedit.textarea.*;
40  import org.gjt.sp.util.Log;
41  //}}}
42  
43  /**
44   * A <code>View</code> is jEdit's top-level frame window.<p>
45   *
46   * In a BeanShell script, you can obtain the current view instance from the
47   * <code>view</code> variable.<p>
48   *
49   * The largest component it contains is an {@link EditPane} that in turn
50   * contains a {@link org.gjt.sp.jedit.textarea.JEditTextArea} that displays a
51   * {@link Buffer}.
52   * A view can have more than one edit pane in a split window configuration.
53   * A view also contains a menu bar, an optional toolbar and other window
54   * decorations, as well as docked windows.<p>
55   *
56   * The <b>View</b> class performs two important operations
57   * dealing with plugins: creating plugin menu items, and managing dockable
58   * windows.
59   *
60   * <ul>
61   * <li>When a view is being created, its initialization routine
62   * iterates through the collection of loaded plugins and constructs the
63   * <b>Plugins</b> menu using the properties as specified in the
64   * {@link EditPlugin} class.</li>
65   * <li>The view also creates and initializes a
66   * {@link org.gjt.sp.jedit.gui.DockableWindowManager}
67   * object.  This object is
68   * responsible for creating, closing and managing dockable windows.</li>
69   * </ul>
70   *
71   * This class does not have a public constructor.
72   * Views can be opened and closed using methods in the <code>jEdit</code>
73   * class.
74   *
75   * @see org.gjt.sp.jedit.jEdit#newView(View)
76   * @see org.gjt.sp.jedit.jEdit#newView(View,Buffer)
77   * @see org.gjt.sp.jedit.jEdit#newView(View,Buffer,boolean)
78   * @see org.gjt.sp.jedit.jEdit#closeView(View)
79   *
80   * @author Slava Pestov
81   * @author John Gellene (API documentation)
82   * @version $Id: View.java,v 1.107 2003/11/18 22:31:48 spestov Exp $
83   */
84  public class View extends JFrame implements EBComponent
85  {
86    //{{{ User interface
87  
88    //{{{ ToolBar-related constants
89  
90    //{{{ Groups
91    /**
92     * The group of tool bars above the DockableWindowManager
93     * @see #addToolBar(int,int,java.awt.Component)
94     * @since jEdit 4.0pre7
95     */
96    public static final int TOP_GROUP = 0;
97  
98    /**
99     * The group of tool bars below the DockableWindowManager
100    * @see #addToolBar(int,int,java.awt.Component)
101    * @since jEdit 4.0pre7
102    */
103   public static final int BOTTOM_GROUP = 1;
104   public static final int DEFAULT_GROUP = TOP_GROUP;
105   //}}}
106 
107   //{{{ Layers
108 
109   // Common layers
110   /**
111    * The highest possible layer.
112    * @see #addToolBar(int,int,java.awt.Component)
113    * @since jEdit 4.0pre7
114    */
115   public static final int TOP_LAYER = Integer.MAX_VALUE;
116 
117   /**
118    * The default layer for tool bars with no preference.
119    * @see #addToolBar(int,int,java.awt.Component)
120    * @since jEdit 4.0pre7
121    */
122   public static final int DEFAULT_LAYER = 0;
123 
124   /**
125    * The lowest possible layer.
126    * @see #addToolBar(int,int,java.awt.Component)
127    * @since jEdit 4.0pre7
128    */
129   public static final int BOTTOM_LAYER = Integer.MIN_VALUE;
130 
131   // Layers for top group
132   /**
133    * Above system tool bar layer.
134    * @see #addToolBar(int,int,java.awt.Component)
135    * @since jEdit 4.0pre7
136    */
137   public static final int ABOVE_SYSTEM_BAR_LAYER = 150;
138 
139   /**
140    * System tool bar layer.
141    * jEdit uses this for the main tool bar.
142    * @see #addToolBar(int,int,java.awt.Component)
143    * @since jEdit 4.0pre7
144    */
145   public static final int SYSTEM_BAR_LAYER = 100;
146 
147   /**
148    * Below system tool bar layer.
149    * @see #addToolBar(int,int,java.awt.Component)
150    * @since jEdit 4.0pre7
151    */
152   public static final int BELOW_SYSTEM_BAR_LAYER = 75;
153 
154   /**
155    * Search bar layer.
156    * @see #addToolBar(int,int,java.awt.Component)
157    * @since jEdit 4.0pre7
158    */
159   public static final int SEARCH_BAR_LAYER = 75;
160 
161   /**
162    * Below search bar layer.
163    * @see #addToolBar(int,int,java.awt.Component)
164    * @since jEdit 4.0pre7
165    */
166   public static final int BELOW_SEARCH_BAR_LAYER = 50;
167 
168   // Layers for bottom group
169   /**
170    * @deprecated Status bar no longer added as a tool bar.
171    */
172   public static final int ABOVE_ACTION_BAR_LAYER = -50;
173 
174   /**
175    * Action bar layer.
176    * @see #addToolBar(int,int,java.awt.Component)
177    * @since jEdit 4.2pre1
178    */
179   public static final int ACTION_BAR_LAYER = -75;
180 
181   /**
182    * Status bar layer.
183    * @see #addToolBar(int,int,java.awt.Component)
184    * @since jEdit 4.2pre1
185    */
186   public static final int STATUS_BAR_LAYER = -100;
187 
188   /**
189    * Status bar layer.
190    * @see #addToolBar(int,int,java.awt.Component)
191    * @since jEdit 4.2pre1
192    */
193   public static final int BELOW_STATUS_BAR_LAYER = -150;
194   //}}}
195 
196   //}}}
197 
198   //{{{ getDockableWindowManager() method
199   /**
200    * Returns the dockable window manager associated with this view.
201    * @since jEdit 2.6pre3
202    */
203   public DockableWindowManager getDockableWindowManager()
204   {
205     return dockableWindowManager;
206   } //}}}
207 
208   //{{{ getToolBar() method
209   /**
210    * Returns the view's tool bar.
211    * @since jEdit 4.2pre1
212    */
213   public Box getToolBar()
214   {
215     return toolBar;
216   } //}}}
217 
218   //{{{ addToolBar() method
219   /**
220    * Adds a tool bar to this view.
221    * @param toolBar The tool bar
222    */
223   public void addToolBar(Component toolBar)
224   {
225     addToolBar(DEFAULT_GROUP, DEFAULT_LAYER, toolBar);
226   } //}}}
227 
228   //{{{ addToolBar() method
229   /**
230    * Adds a tool bar to this view.
231    * @param group The tool bar group to add to
232    * @param toolBar The tool bar
233    * @see org.gjt.sp.jedit.gui.ToolBarManager
234    * @since jEdit 4.0pre7
235    */
236   public void addToolBar(int group, Component toolBar)
237   {
238     addToolBar(group, DEFAULT_LAYER, toolBar);
239   } //}}}
240 
241   //{{{ addToolBar() method
242   /**
243    * Adds a tool bar to this view.
244    * @param group The tool bar group to add to
245    * @param layer The layer of the group to add to
246    * @param toolBar The tool bar
247    * @see org.gjt.sp.jedit.gui.ToolBarManager
248    * @since jEdit 4.0pre7
249    */
250   public void addToolBar(int group, int layer, Component toolBar)
251   {
252     toolBarManager.addToolBar(group, layer, toolBar);
253     getRootPane().revalidate();
254   } //}}}
255 
256   //{{{ removeToolBar() method
257   /**
258    * Removes a tool bar from this view.
259    * @param toolBar The tool bar
260    */
261   public void removeToolBar(Component toolBar)
262   {
263     toolBarManager.removeToolBar(toolBar);
264     getRootPane().revalidate();
265   } //}}}
266 
267   //{{{ showWaitCursor() method
268   /**
269    * Shows the wait cursor. This method and
270    * {@link #hideWaitCursor()} are implemented using a reference
271    * count of requests for wait cursors, so that nested calls work
272    * correctly; however, you should be careful to use these methods in
273    * tandem.<p>
274    *
275    * To ensure that {@link #hideWaitCursor()} is always called
276    * after a {@link #showWaitCursor()}, use a
277    * <code>try</code>/<code>finally</code> block, like this:
278    * <pre>try
279    *{
280    *    view.showWaitCursor();
281    *    // ...
282    *}
283    *finally
284    *{
285    *    view.hideWaitCursor();
286    *}</pre>
287    */
288   public synchronized void showWaitCursor()
289   {
290     if(waitCount++ == 0)
291     {
292       Cursor cursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
293       setCursor(cursor);
294       EditPane[] editPanes = getEditPanes();
295       for(int i = 0; i < editPanes.length; i++)
296       {
297         EditPane editPane = editPanes[i];
298         editPane.getTextArea().getPainter()
299           .setCursor(cursor);
300       }
301     }
302   } //}}}
303 
304   //{{{ hideWaitCursor() method
305   /**
306    * Hides the wait cursor.
307    */
308   public synchronized void hideWaitCursor()
309   {
310     if(waitCount > 0)
311       waitCount--;
312 
313     if(waitCount == 0)
314     {
315       // still needed even though glass pane
316       // has a wait cursor
317       Cursor cursor = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
318       setCursor(cursor);
319       cursor = Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR);
320       EditPane[] editPanes = getEditPanes();
321       for(int i = 0; i < editPanes.length; i++)
322       {
323         EditPane editPane = editPanes[i];
324         editPane.getTextArea().getPainter()
325           .setCursor(cursor);
326       }
327     }
328   } //}}}
329 
330   //{{{ getSearchBar() method
331   /**
332    * Returns the search bar.
333    * @since jEdit 2.4pre4
334    */
335   public final SearchBar getSearchBar()
336   {
337     return searchBar;
338   } //}}}
339 
340   //{{{ getActionBar() method
341   /**
342    * Returns the action bar.
343    * @since jEdit 4.2pre3
344    */
345   public final ActionBar getActionBar()
346   {
347     return actionBar;
348   } //}}}
349 
350   //{{{ getStatus() method
351   /**
352    * Returns the status bar. The
353    * {@link org.gjt.sp.jedit.gui.StatusBar#setMessage(String)} and
354    * {@link org.gjt.sp.jedit.gui.StatusBar#setMessageAndClear(String)} methods can
355    * be called on the return value of this method to display status
356    * information to the user.
357    * @since jEdit 3.2pre2
358    */
359   public StatusBar getStatus()
360   {
361     return status;
362   } //}}}
363 
364   //{{{ quickIncrementalSearch() method
365   /**
366    * Quick search.
367    * @since jEdit 4.0pre3
368    */
369   public void quickIncrementalSearch(boolean word)
370   {
371     if(searchBar == null)
372       searchBar = new SearchBar(this,true);
373     if(searchBar.getParent() == null)
374       addToolBar(TOP_GROUP,SEARCH_BAR_LAYER,searchBar);
375 
376     searchBar.setHyperSearch(false);
377 
378     JEditTextArea textArea = getTextArea();
379 
380     if(word)
381     {
382       String text = textArea.getSelectedText();
383       if(text == null)
384       {
385         textArea.selectWord();
386         text = textArea.getSelectedText();
387       }
388       else if(text.indexOf('\n') != -1)
389         text = null;
390 
391       searchBar.getField().setText(text);
392     }
393 
394     searchBar.getField().requestFocus();
395     searchBar.getField().selectAll();
396   } //}}}
397 
398   //{{{ quickHyperSearch() method
399   /**
400    * Quick HyperSearch.
401    * @since jEdit 4.0pre3
402    */
403   public void quickHyperSearch(boolean word)
404   {
405     JEditTextArea textArea = getTextArea();
406 
407     if(word)
408     {
409       String text = textArea.getSelectedText();
410       if(text == null)
411       {
412         textArea.selectWord();
413         text = textArea.getSelectedText();
414       }
415 
416       if(text != null && text.indexOf('\n') == -1)
417       {
418         HistoryModel.getModel("find").addItem(text);
419         SearchAndReplace.setSearchString(text);
420         SearchAndReplace.setSearchFileSet(new CurrentBufferSet());
421         SearchAndReplace.hyperSearch(this);
422 
423         return;
424       }
425     }
426 
427     if(searchBar == null)
428       searchBar = new SearchBar(this,true);
429     if(searchBar.getParent() == null)
430       addToolBar(TOP_GROUP,SEARCH_BAR_LAYER,searchBar);
431 
432     searchBar.setHyperSearch(true);
433     searchBar.getField().setText(null);
434     searchBar.getField().requestFocus();
435     searchBar.getField().selectAll();
436   } //}}}
437 
438   //{{{ actionBar() method
439   /**
440    * Shows the action bar if needed, and sends keyboard focus there.
441    * @since jEdit 4.2pre1
442    */
443   public void actionBar()
444   {
445     if(actionBar == null)
446       actionBar = new ActionBar(this,true);
447     if(actionBar.getParent() == null)
448       addToolBar(BOTTOM_GROUP,ACTION_BAR_LAYER,actionBar);
449 
450     actionBar.goToActionBar();
451   } //}}}
452 
453   //}}}
454 
455   //{{{ Input handling
456 
457   //{{{ getKeyEventInterceptor() method
458   /**
459    * Returns the listener that will handle all key events in this
460    * view, if any.
461    */
462   public KeyListener getKeyEventInterceptor()
463   {
464     return keyEventInterceptor;
465   } //}}}
466 
467   //{{{ setKeyEventInterceptor() method
468   /**
469    * Sets the listener that will handle all key events in this
470    * view. For example, the complete word command uses this so
471    * that all key events are passed to the word list popup while
472    * it is visible.
473    * @param comp The component
474    */
475   public void setKeyEventInterceptor(KeyListener listener)
476   {
477     this.keyEventInterceptor = listener;
478   } //}}}
479 
480   //{{{ getInputHandler() method
481   /**
482    * Returns the input handler.
483    */
484   public InputHandler getInputHandler()
485   {
486     return inputHandler;
487   } //}}}
488 
489   //{{{ setInputHandler() method
490   /**
491    * Sets the input handler.
492    * @param inputHandler The new input handler
493    */
494   public void setInputHandler(InputHandler inputHandler)
495   {
496     this.inputHandler = inputHandler;
497   } //}}}
498 
499   //{{{ getMacroRecorder() method
500   /**
501    * Returns the macro recorder.
502    */
503   public Macros.Recorder getMacroRecorder()
504   {
505     return recorder;
506   } //}}}
507 
508   //{{{ setMacroRecorder() method
509   /**
510    * Sets the macro recorder.
511    * @param recorder The macro recorder
512    */
513   public void setMacroRecorder(Macros.Recorder recorder)
514   {
515     this.recorder = recorder;
516   } //}}}
517 
518   //{{{ processKeyEvent() method
519   /**
520    * Forwards key events directly to the input handler.
521    * This is slightly faster than using a KeyListener
522    * because some Swing overhead is avoided.
523    */
524   public void processKeyEvent(KeyEvent evt)
525   {
526     processKeyEvent(evt,VIEW);
527   } //}}}
528 
529   //{{{ processKeyEvent() method
530   /**
531    * Forwards key events directly to the input handler.
532    * This is slightly faster than using a KeyListener
533    * because some Swing overhead is avoided.
534    */
535   public void processKeyEvent(KeyEvent evt, boolean calledFromTextArea)
536   {
537     processKeyEvent(evt,calledFromTextArea
538       ? TEXT_AREA
539       : VIEW);
540   } //}}}
541 
542   //{{{ processKeyEvent() method
543   public static final int VIEW = 0;
544   public static final int TEXT_AREA = 1;
545   public static final int ACTION_BAR = 2;
546   /**
547    * Forwards key events directly to the input handler.
548    * This is slightly faster than using a KeyListener
549    * because some Swing overhead is avoided.
550    */
551   public void processKeyEvent(KeyEvent evt, int from)
552   {
553     if(Debug.DUMP_KEY_EVENTS && from != VIEW)
554     {
555       Log.log(Log.DEBUG,this,"Key event: "
556         + GrabKeyDialog.toString(evt));
557     }
558 
559     if(getTextArea().hasFocus() && from == VIEW)
560       return;
561 
562     evt = _preprocessKeyEvent(evt);
563     if(evt == null)
564       return;
565 
566     switch(evt.getID())
567     {
568     case KeyEvent.KEY_TYPED:
569       boolean focusOnTextArea = false;
570       // if the user pressed eg C+e n n in the
571       // search bar we want focus to go back there
572       // after the prefix is done
573       if(prefixFocusOwner != null)
574       {
575         if(prefixFocusOwner.isShowing())
576         {
577           prefixFocusOwner.requestFocus();
578           focusOnTextArea = true;
579         }
580       }
581 
582       if(keyEventInterceptor != null)
583         keyEventInterceptor.keyTyped(evt);
584       else if(from == ACTION_BAR
585         || inputHandler.isPrefixActive()
586         || getTextArea().hasFocus())
587       {
588         KeyEventTranslator.Key keyStroke
589           = KeyEventTranslator
590           .translateKeyEvent(evt);
591         if(keyStroke != null)
592         {
593           if(inputHandler.handleKey(keyStroke))
594             evt.consume();
595         }
596       }
597 
598       // we might have been closed as a result of
599       // the above
600       if(isClosed())
601         return;
602 
603       // this is a weird hack.
604       // we don't want C+e a to insert 'a' in the
605       // search bar if the search bar has focus...
606       if(inputHandler.isPrefixActive())
607       {
608         if(getFocusOwner() instanceof JTextComponent)
609         {
610           prefixFocusOwner = getFocusOwner();
611           getTextArea().requestFocus();
612         }
613         else if(focusOnTextArea)
614         {
615           getTextArea().requestFocus();
616         }
617         else
618         {
619           prefixFocusOwner = null;
620         }
621       }
622       else
623       {
624         prefixFocusOwner = null;
625       }
626 
627       break;
628     case KeyEvent.KEY_PRESSED:
629       if(keyEventInterceptor != null)
630         keyEventInterceptor.keyPressed(evt);
631       else
632       {
633         /* boolean */ focusOnTextArea = false;
634         if(prefixFocusOwner != null)
635         {
636           if(prefixFocusOwner.isShowing())
637           {
638             prefixFocusOwner.requestFocus();
639             focusOnTextArea = true;
640           }
641           prefixFocusOwner = null;
642         }
643 
644         KeyEventTranslator.Key keyStroke
645           = KeyEventTranslator
646           .translateKeyEvent(evt);
647         if(keyStroke != null)
648         {
649           if(inputHandler.handleKey(keyStroke))
650             evt.consume();
651         }
652 
653         // we might have been closed as a result of
654         // the above
655         if(isClosed())
656           return;
657 
658         // this is a weird hack.
659         // we don't want C+e a to insert 'a' in the
660         // search bar if the search bar has focus...
661         if(inputHandler.isPrefixActive())
662         {
663           if(getFocusOwner() instanceof JTextComponent)
664           {
665             prefixFocusOwner = getFocusOwner();
666             getTextArea().requestFocus();
667           }
668           else if(focusOnTextArea)
669           {
670             getTextArea().requestFocus();
671           }
672           else
673           {
674             prefixFocusOwner = null;
675           }
676         }
677         else
678         {
679           prefixFocusOwner = null;
680         }
681       }
682       break;
683     case KeyEvent.KEY_RELEASED:
684       if(keyEventInterceptor != null)
685         keyEventInterceptor.keyReleased(evt);
686       break;
687     }
688 
689     if(!evt.isConsumed())
690       super.processKeyEvent(evt);
691   } //}}}
692 
693   //}}}
694 
695   //{{{ Buffers, edit panes, split panes
696 
697   //{{{ splitHorizontally() method
698   /**
699    * Splits the view horizontally.
700    * @since jEdit 4.1pre2
701    */
702   public EditPane splitHorizontally()
703   {
704     return split(JSplitPane.VERTICAL_SPLIT);
705   } //}}}
706 
707   //{{{ splitVertically() method
708   /**
709    * Splits the view vertically.
710    * @since jEdit 4.1pre2
711    */
712   public EditPane splitVertically()
713   {
714     return split(JSplitPane.HORIZONTAL_SPLIT);
715   } //}}}
716 
717   //{{{ split() method
718   /**
719    * Splits the view.
720    * @since jEdit 4.1pre2
721    */
722   public EditPane split(int orientation)
723   {
724     editPane.saveCaretInfo();
725     EditPane oldEditPane = editPane;
726     setEditPane(createEditPane(oldEditPane.getBuffer()));
727     editPane.loadCaretInfo();
728 
729     JComponent oldParent = (JComponent)oldEditPane.getParent();
730 
731     final JSplitPane newSplitPane = new JSplitPane(orientation);
732     newSplitPane.setOneTouchExpandable(true);
733     newSplitPane.setBorder(null);
734     newSplitPane.setMinimumSize(new Dimension(0,0));
735 
736     int parentSize = (orientation == JSplitPane.VERTICAL_SPLIT
737       ? oldEditPane.getHeight() : oldEditPane.getWidth());
738     final int dividerPosition = (int)((double)(parentSize
739       - newSplitPane.getDividerSize()) * 0.5);
740     newSplitPane.setDividerLocation(dividerPosition);
741 
742     if(oldParent instanceof JSplitPane)
743     {
744       JSplitPane oldSplitPane = (JSplitPane)oldParent;
745       int dividerPos = oldSplitPane.getDividerLocation();
746 
747       Component left = oldSplitPane.getLeftComponent();
748 
749       if(left == oldEditPane)
750         oldSplitPane.setLeftComponent(newSplitPane);
751       else
752         oldSplitPane.setRightComponent(newSplitPane);
753 
754       newSplitPane.setLeftComponent(oldEditPane);
755       newSplitPane.setRightComponent(editPane);
756 
757       oldSplitPane.setDividerLocation(dividerPos);
758     }
759     else
760     {
761       this.splitPane = newSplitPane;
762 
763       newSplitPane.setLeftComponent(oldEditPane);
764       newSplitPane.setRightComponent(editPane);
765 
766       oldParent.add(newSplitPane,0);
767       oldParent.revalidate();
768     }
769 
770     SwingUtilities.invokeLater(new Runnable()
771     {
772       public void run()
773       {
774         newSplitPane.setDividerLocation(dividerPosition);
775       }
776     });
777 
778     editPane.focusOnTextArea();
779 
780     return editPane;
781   } //}}}
782 
783   //{{{ unsplit() method
784   /**
785    * Unsplits the view.
786    * @since jEdit 2.3pre2
787    */
788   public void unsplit()
789   {
790     if(splitPane != null)
791     {
792       EditPane[] editPanes = getEditPanes();
793       for(int i = 0; i < editPanes.length; i++)
794       {
795         EditPane _editPane = editPanes[i];
796         if(editPane != _editPane)
797           _editPane.close();
798       }
799 
800       JComponent parent = (JComponent)splitPane.getParent();
801 
802       parent.remove(splitPane);
803       parent.add(editPane,0);
804       parent.revalidate();
805 
806       splitPane = null;
807       updateTitle();
808 
809       editPane.focusOnTextArea();
810     }
811     else
812       getToolkit().beep();
813   } //}}}
814 
815   //{{{ unsplitCurrent() method
816   /**
817    * Removes the current split.
818    * @since jEdit 2.3pre2
819    */
820   public void unsplitCurrent()
821   {
822     if(splitPane != null)
823     {
824       // find first split pane parenting current edit pane
825       Component comp = editPane;
826       while(!(comp instanceof JSplitPane))
827       {
828         comp = comp.getParent();
829       }
830 
831       // get rid of any edit pane that is a child
832       // of the current edit pane's parent splitter
833       EditPane[] editPanes = getEditPanes();
834       for(int i = 0; i < editPanes.length; i++)
835       {
836         EditPane _editPane = editPanes[i];
837         if(GUIUtilities.isAncestorOf(comp,_editPane)
838           && _editPane != editPane)
839           _editPane.close();
840       }
841 
842       JComponent parent = (JComponent)comp.getParent();
843 
844       if(parent instanceof JSplitPane)
845       {
846         JSplitPane parentSplit = (JSplitPane)parent;
847         int pos = parentSplit.getDividerLocation();
848         if(parentSplit.getLeftComponent() == comp)
849           parentSplit.setLeftComponent(editPane);
850         else
851           parentSplit.setRightComponent(editPane);
852         parentSplit.setDividerLocation(pos);
853       }
854       else
855       {
856         parent.remove(comp);
857         parent.add(editPane,0);
858         splitPane = null;
859       }
860 
861       parent.revalidate();
862 
863       updateTitle();
864 
865       editPane.focusOnTextArea();
866     }
867     else
868       getToolkit().beep();
869   } //}}}
870 
871   //{{{ nextTextArea() method
872   /**
873    * Moves keyboard focus to the next text area.
874    * @since jEdit 2.7pre4
875    */
876   public void nextTextArea()
877   {
878     EditPane[] editPanes = getEditPanes();
879     for(int i = 0; i < editPanes.length; i++)
880     {
881       if(editPane == editPanes[i])
882       {
883         if(i == editPanes.length - 1)
884           editPanes[0].focusOnTextArea();
885         else
886           editPanes[i+1].focusOnTextArea();
887         break;
888       }
889     }
890   } //}}}
891 
892   //{{{ prevTextArea() method
893   /**
894    * Moves keyboard focus to the previous text area.
895    * @since jEdit 2.7pre4
896    */
897   public void prevTextArea()
898   {
899     EditPane[] editPanes = getEditPanes();
900     for(int i = 0; i < editPanes.length; i++)
901     {
902       if(editPane == editPanes[i])
903       {
904         if(i == 0)
905           editPanes[editPanes.length - 1].focusOnTextArea();
906         else
907           editPanes[i-1].focusOnTextArea();
908         break;
909       }
910     }
911   } //}}}
912 
913   //{{{ getSplitPane() method
914   /**
915    * Returns the top-level split pane, if any.
916    * @since jEdit 2.3pre2
917    */
918   public JSplitPane getSplitPane()
919   {
920     return splitPane;
921   } //}}}
922 
923   //{{{ getBuffer() method
924   /**
925    * Returns the current edit pane's buffer.
926    */
927   public Buffer getBuffer()
928   {
929     if(editPane == null)
930       return null;
931     else
932       return editPane.getBuffer();
933   } //}}}
934 
935   //{{{ setBuffer() method
936   /**
937    * Sets the current edit pane's buffer.
938    */
939   public void setBuffer(Buffer buffer)
940   {
941     editPane.setBuffer(buffer);
942   } //}}}
943 
944   //{{{ goToBuffer() method
945   /**
946    * If this buffer is open in one of the view's edit panes, sets focus
947    * to that edit pane. Otherwise, opens the buffer in the currently
948    * active edit pane.
949    * @param buffer The buffer
950    * @since jEdit 4.2pre1
951    */
952   public EditPane goToBuffer(Buffer buffer)
953   {
954     if(editPane.getBuffer() == buffer)
955     {
956       editPane.focusOnTextArea();
957       return editPane;
958     }
959 
960     EditPane[] editPanes = getEditPanes();
961     for(int i = 0; i < editPanes.length; i++)
962     {
963       EditPane ep = editPanes[i];
964       if(ep.getBuffer() == buffer
965         /* ignore zero-height splits, etc */
966         && ep.getTextArea().getVisibleLines() > 1)
967       {
968         setEditPane(ep);
969         ep.focusOnTextArea();
970         return ep;
971       }
972     }
973     setBuffer(buffer);
974     return editPane;
975   } //}}}
976 
977   //{{{ getTextArea() method
978   /**
979    * Returns the current edit pane's text area.
980    */
981   public JEditTextArea getTextArea()
982   {
983     if(editPane == null)
984       return null;
985     else
986       return editPane.getTextArea();
987   } //}}}
988 
989   //{{{ getEditPane() method
990   /**
991    * Returns the current edit pane.
992    * @since jEdit 2.5pre2
993    */
994   public EditPane getEditPane()
995   {
996     return editPane;
997   } //}}}
998 
999   //{{{ getEditPanes() method
1000  /**
1001   * Returns all edit panes.
1002   * @since jEdit 2.5pre2
1003   */
1004  public EditPane[] getEditPanes()
1005  {
1006    if(splitPane == null)
1007    {
1008      EditPane[] ep = { editPane };
1009      return ep;
1010    }
1011    else
1012    {
1013      Vector vec = new Vector();
1014      getEditPanes(vec,splitPane);
1015      EditPane[] ep = new EditPane[vec.size()];
1016      vec.copyInto(ep);
1017      return ep;
1018    }
1019  } //}}}
1020
1021  //{{{ getViewConfig() method
1022  /**
1023   * @since jEdit 4.2pre1
1024   */
1025  public ViewConfig getViewConfig()
1026  {
1027    StringBuffer splitConfig = new StringBuffer();
1028    if(splitPane != null)
1029      getSplitConfig(splitPane,splitConfig);
1030    else
1031    {
1032      splitConfig.append('"');
1033      splitConfig.append(MiscUtilities.charsToEscapes(
1034        getBuffer().getPath()));
1035      splitConfig.append("\" buffer");
1036    }
1037
1038    ViewConfig config = new ViewConfig();
1039    config.plainView = isPlainView();
1040    config.splitConfig = splitConfig.toString();
1041    config.x = getX();
1042    config.y = getY();
1043    config.width = getWidth();
1044    config.height = getHeight();
1045    config.extState = GUIUtilities.getExtendedState(this);
1046
1047    config.top = dockableWindowManager.getTopDockingArea().getCurrent();
1048    config.left = dockableWindowManager.getLeftDockingArea().getCurrent();
1049    config.bottom = dockableWindowManager.getBottomDockingArea().getCurrent();
1050    config.right = dockableWindowManager.getRightDockingArea().getCurrent();
1051
1052    config.topPos = dockableWindowManager.getTopDockingArea().getDimension();
1053    config.leftPos = dockableWindowManager.getLeftDockingArea().getDimension();
1054    config.bottomPos = dockableWindowManager.getBottomDockingArea().getDimension();
1055    config.rightPos = dockableWindowManager.getRightDockingArea().getDimension();
1056
1057    return config;
1058  } //}}}
1059
1060  //}}}
1061
1062  //{{{ isClosed() method
1063  /**
1064   * Returns true if this view has been closed with
1065   * {@link jEdit#closeView(View)}.
1066   */
1067  public boolean isClosed()
1068  {
1069    return closed;
1070  } //}}}
1071
1072  //{{{ isPlainView() method
1073  /**
1074   * Returns true if this is an auxilliary view with no dockable windows.
1075   * @since jEdit 4.1pre2
1076   */
1077  public boolean isPlainView()
1078  {
1079    return plainView;
1080  } //}}}
1081
1082  //{{{ getNext() method
1083  /**
1084   * Returns the next view in the list.
1085   */
1086  public View getNext()
1087  {
1088    return next;
1089  } //}}}
1090
1091  //{{{ getPrev() method
1092  /**
1093   * Returns the previous view in the list.
1094   */
1095  public View getPrev()
1096  {
1097    return prev;
1098  } //}}}
1099
1100  //{{{ handleMessage() method
1101  public void handleMessage(EBMessage msg)
1102  {
1103    if(msg instanceof PropertiesChanged)
1104      propertiesChanged();
1105    else if(msg instanceof SearchSettingsChanged)
1106    {
1107      if(searchBar != null)
1108        searchBar.update();
1109    }
1110    else if(msg instanceof BufferUpdate)
1111      handleBufferUpdate((BufferUpdate)msg);
1112    else if(msg instanceof EditPaneUpdate)
1113      handleEditPaneUpdate((EditPaneUpdate)msg);
1114    else if(msg instanceof PluginUpdate)
1115    {
1116      if(actionBar != null)
1117        actionBar.actionListChanged();
1118    }
1119  } //}}}
1120
1121  //{{{ getMinimumSize() method
1122  public Dimension getMinimumSize()
1123  {
1124    return new Dimension(0,0);
1125  } //}}}
1126
1127  //{{{ setWaitSocket() method
1128  /**
1129   * This socket is closed when the buffer is closed.
1130   */
1131  public void setWaitSocket(Socket waitSocket)
1132  {
1133    this.waitSocket = waitSocket;
1134  } //}}}
1135
1136  //{{{ toString() method
1137  public String toString()
1138  {
1139    return getClass().getName() + "["
1140      + (jEdit.getActiveView() == this
1141      ? "active" : "inactive")
1142      + "]";
1143  } //}}}
1144
1145  //{{{ Package-private members
1146  View prev;
1147  View next;
1148
1149  //{{{ View constructor
1150  View(Buffer buffer, ViewConfig config)
1151  {
1152    this.plainView = config.plainView;
1153
1154    enableEvents(AWTEvent.KEY_EVENT_MASK);
1155
1156    setIconImage(GUIUtilities.getEditorIcon());
1157
1158    dockableWindowManager = new DockableWindowManager(this,config);
1159
1160    topToolBars = new JPanel(new VariableGridLayout(
1161      VariableGridLayout.FIXED_NUM_COLUMNS,
1162      1));
1163    bottomToolBars = new JPanel(new VariableGridLayout(
1164      VariableGridLayout.FIXED_NUM_COLUMNS,
1165      1));
1166
1167    toolBarManager = new ToolBarManager(topToolBars, bottomToolBars);
1168
1169    status = new StatusBar(this);
1170
1171    inputHandler = new DefaultInputHandler(this,(DefaultInputHandler)
1172      jEdit.getInputHandler());
1173
1174    try
1175    {
1176      Component comp = restoreSplitConfig(buffer,config.splitConfig);
1177      dockableWindowManager.add(comp,0);
1178    }
1179    catch(IOException e)
1180    {
1181      // this should never throw an exception.
1182      throw new InternalError();
1183    }
1184
1185    getContentPane().add(BorderLayout.CENTER,dockableWindowManager);
1186
1187    dockableWindowManager.init();
1188
1189    // tool bar and status bar gets added in propertiesChanged()
1190    // depending in the 'tool bar alternate layout' setting.
1191    propertiesChanged();
1192
1193    setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
1194    addWindowListener(new WindowHandler());
1195
1196    EditBus.addToBus(this);
1197
1198    SearchDialog.preloadSearchDialog(this);
1199  } //}}}
1200
1201  //{{{ close() method
1202  void close()
1203  {
1204    GUIUtilities.saveGeometry(this,plainView ? "plain-view" : "view");
1205    closed = true;
1206
1207    // save dockable window geometry, and close 'em
1208    dockableWindowManager.close();
1209
1210    EditBus.removeFromBus(this);
1211    dispose();
1212
1213    EditPane[] editPanes = getEditPanes();
1214    for(int i = 0; i < editPanes.length; i++)
1215      editPanes[i].close();
1216
1217    // null some variables so that retaining references
1218    // to closed views won't hurt as much.
1219    toolBarManager = null;
1220    toolBar = null;
1221    searchBar = null;
1222    splitPane = null;
1223    inputHandler = null;
1224    recorder = null;
1225
1226    getContentPane().removeAll();
1227
1228    // notify clients with -wait
1229    if(waitSocket != null)
1230    {
1231      try
1232      {
1233        waitSocket.getOutputStream().write('\0');
1234        waitSocket.getOutputStream().flush();
1235        waitSocket.getInputStream().close();
1236        waitSocket.getOutputStream().close();
1237        waitSocket.close();
1238      }
1239      catch(IOException io)
1240      {
1241        //Log.log(Log.ERROR,this,io);
1242      }
1243    }
1244  } //}}}
1245
1246  //{{{ updateTitle() method
1247  /**
1248   * Updates the title bar.
1249   */
1250  void updateTitle()
1251  {
1252    Vector buffers = new Vector();
1253    EditPane[] editPanes = getEditPanes();
1254    for(int i = 0; i < editPanes.length; i++)
1255    {
1256      Buffer buffer = editPanes[i].getBuffer();
1257      if(buffers.indexOf(buffer) == -1)
1258        buffers.addElement(buffer);
1259    }
1260
1261    StringBuffer title = new StringBuffer(jEdit.getProperty("view.title"));
1262    for(int i = 0; i < buffers.size(); i++)
1263    {
1264      if(i != 0)
1265        title.append(", ");
1266
1267      Buffer buffer = (Buffer)buffers.elementAt(i);
1268      title.append((showFullPath && !buffer.isNewFile())
1269        ? buffer.getPath() : buffer.getName());
1270      if(buffer.isDirty())
1271        title.append(jEdit.getProperty("view.title.dirty"));
1272    }
1273    setTitle(title.toString());
1274  } //}}}
1275
1276  //}}}
1277
1278  //{{{ Private members
1279
1280  //{{{ Instance variables
1281  private boolean closed;
1282
1283  private DockableWindowManager dockableWindowManager;
1284
1285  private JPanel topToolBars;
1286  private JPanel bottomToolBars;
1287  private ToolBarManager toolBarManager;
1288
1289  private Box toolBar;
1290  private SearchBar searchBar;
1291  private ActionBar actionBar;
1292
1293  private EditPane editPane;
1294  private JSplitPane splitPane;
1295
1296  private StatusBar status;
1297
1298  private KeyListener keyEventInterceptor;
1299  private InputHandler inputHandler;
1300  private Macros.Recorder recorder;
1301  private Component prefixFocusOwner;
1302
1303  private int waitCount;
1304
1305  private boolean showFullPath;
1306
1307  private boolean plainView;
1308
1309  private Socket waitSocket;
1310  //}}}
1311
1312  //{{{ getEditPanes() method
1313  private void getEditPanes(Vector vec, Component comp)
1314  {
1315    if(comp instanceof EditPane)
1316      vec.addElement(comp);
1317    else if(comp instanceof JSplitPane)
1318    {
1319      JSplitPane split = (JSplitPane)comp;
1320      getEditPanes(vec,split.getLeftComponent());
1321      getEditPanes(vec,split.getRightComponent());
1322    }
1323  } //}}}
1324
1325  //{{{ getSplitConfig() method
1326  /*
1327   * The split config is recorded in a simple RPN "language".
1328   */
1329  private void getSplitConfig(JSplitPane splitPane,
1330    StringBuffer splitConfig)
1331  {
1332    Component right = splitPane.getRightComponent();
1333    if(right instanceof JSplitPane)
1334      getSplitConfig((JSplitPane)right,splitConfig);
1335    else
1336    {
1337      splitConfig.append('"');
1338      splitConfig.append(MiscUtilities.charsToEscapes(
1339        ((EditPane)right).getBuffer().getPath()));
1340      splitConfig.append("\" buffer");
1341    }
1342
1343    splitConfig.append(' ');
1344
1345    Component left = splitPane.getLeftComponent();
1346    if(left instanceof JSplitPane)
1347      getSplitConfig((JSplitPane)left,splitConfig);
1348    else
1349    {
1350      splitConfig.append('"');
1351      splitConfig.append(MiscUtilities.charsToEscapes(
1352        ((EditPane)left).getBuffer().getPath()));
1353      splitConfig.append("\" buffer");
1354    }
1355
1356    splitConfig.append(' ');
1357    splitConfig.append(splitPane.getDividerLocation());
1358    splitConfig.append(' ');
1359    splitConfig.append(splitPane.getOrientation()
1360      == JSplitPane.VERTICAL_SPLIT ? "vertical" : "horizontal");
1361  } //}}}
1362
1363  //{{{ restoreSplitConfig() method
1364  private Component restoreSplitConfig(Buffer buffer, String splitConfig)
1365    throws IOException
1366  // this is where checked exceptions piss me off. this method only uses
1367  // a StringReader which can never throw an exception...
1368  {
1369    if(buffer != null)
1370      return (editPane = createEditPane(buffer));
1371    else if(splitConfig == null)
1372      return (editPane = createEditPane(jEdit.getFirstBuffer()));
1373
1374    Buffer[] buffers = jEdit.getBuffers();
1375
1376    Stack stack = new Stack();
1377
1378    // we create a stream tokenizer for parsing a simple
1379    // stack-based language
1380    StreamTokenizer st = new StreamTokenizer(new StringReader(
1381      splitConfig));
1382    st.whitespaceChars(0,' ');
1383    /* all printable ASCII characters */
1384    st.wordChars('#','~');
1385    st.commentChar('!');
1386    st.quoteChar('"');
1387    st.eolIsSignificant(false);
1388
1389loop:    for(;;)
1390    {
1391      switch(st.nextToken())
1392      {
1393      case StreamTokenizer.TT_EOF:
1394        break loop;
1395      case StreamTokenizer.TT_WORD:
1396        if(st.sval.equals("vertical") ||
1397          st.sval.equals("horizontal"))
1398        {
1399          int orientation
1400            = (st.sval.equals("vertical")
1401            ? JSplitPane.VERTICAL_SPLIT
1402            : JSplitPane.HORIZONTAL_SPLIT);
1403          int divider = ((Integer)stack.pop())
1404            .intValue();
1405          stack.push(splitPane = new JSplitPane(
1406            orientation,
1407            (Component)stack.pop(),
1408            (Component)stack.pop()));
1409          splitPane.setOneTouchExpandable(true);
1410          splitPane.setBorder(null);
1411          splitPane.setMinimumSize(
1412            new Dimension(0,0));
1413          splitPane.setDividerLocation(divider);
1414        }
1415        else if(st.sval.equals("buffer"))
1416        {
1417          Object obj = stack.pop();
1418          if(obj instanceof Integer)
1419          {
1420            int index = ((Integer)obj).intValue();
1421            if(index >= 0 && index < buffers.length)
1422              buffer = buffers[index];
1423          }
1424          else if(obj instanceof String)
1425          {
1426            String path = (String)obj;
1427            buffer = jEdit.getBuffer(path);
1428          }
1429
1430          if(buffer == null)
1431            buffer = jEdit.getFirstBuffer();
1432
1433          stack.push(editPane = createEditPane(
1434            buffer));
1435        }
1436        break;
1437      case StreamTokenizer.TT_NUMBER:
1438        stack.push(new Integer((int)st.nval));
1439        break;
1440      case '"':
1441        stack.push(st.sval);
1442        break;
1443      }
1444    }
1445
1446    updateGutterBorders();
1447
1448    return (Component)stack.peek();
1449  } //}}}
1450
1451  //{{{ propertiesChanged() method
1452  /**
1453   * Reloads various settings from the properties.
1454   */
1455  private void propertiesChanged()
1456  {
1457    setJMenuBar(GUIUtilities.loadMenuBar("view.mbar"));
1458
1459    loadToolBars();
1460
1461    showFullPath = jEdit.getBooleanProperty("view.showFullPath");
1462    updateTitle();
1463
1464    status.propertiesChanged();
1465
1466    removeToolBar(status);
1467    getContentPane().remove(status);
1468
1469    if(jEdit.getBooleanProperty("view.toolbar.alternateLayout"))
1470    {
1471      getContentPane().add(BorderLayout.NORTH,topToolBars);
1472      getContentPane().add(BorderLayout.SOUTH,bottomToolBars);
1473      if(!plainView && jEdit.getBooleanProperty("view.status.visible"))
1474        addToolBar(BOTTOM_GROUP,STATUS_BAR_LAYER,status);
1475    }
1476    else
1477    {
1478      dockableWindowManager.add(topToolBars,
1479        DockableWindowManager.DockableLayout
1480        .TOP_TOOLBARS,0);
1481      dockableWindowManager.add(bottomToolBars,
1482        DockableWindowManager.DockableLayout
1483        .BOTTOM_TOOLBARS,0);
1484      if(!plainView && jEdit.getBooleanProperty("view.status.visible"))
1485        getContentPane().add(BorderLayout.SOUTH,status);
1486    }
1487
1488    getRootPane().revalidate();
1489
1490    //SwingUtilities.updateComponentTreeUI(getRootPane());
1491  } //}}}
1492
1493  //{{{ loadToolBars() method
1494  private void loadToolBars()
1495  {
1496    if(jEdit.getBooleanProperty("view.showToolbar") && !plainView)
1497    {
1498      if(toolBar != null)
1499        toolBarManager.removeToolBar(toolBar);
1500
1501      toolBar = GUIUtilities.loadToolBar("view.toolbar");
1502
1503      addToolBar(TOP_GROUP, SYSTEM_BAR_LAYER, toolBar);
1504    }
1505    else if(toolBar != null)
1506    {
1507      removeToolBar(toolBar);
1508      toolBar = null;
1509    }
1510
1511    if(searchBar != null)
1512      removeToolBar(searchBar);
1513
1514    if(jEdit.getBooleanProperty("view.showSearchbar") && !plainView)
1515    {
1516      if(searchBar == null)
1517        searchBar = new SearchBar(this,false);
1518      searchBar.propertiesChanged();
1519      addToolBar(TOP_GROUP,SEARCH_BAR_LAYER,searchBar);
1520    }
1521  } //}}}
1522
1523  //{{{ createEditPane() method
1524  private EditPane createEditPane(Buffer buffer)
1525  {
1526    EditPane editPane = new EditPane(this,buffer);
1527    JEditTextArea textArea = editPane.getTextArea();
1528    textArea.addFocusListener(new FocusHandler());
1529    textArea.addCaretListener(new CaretHandler());
1530    textArea.addScrollListener(new ScrollHandler());
1531    EditBus.send(new EditPaneUpdate(editPane,EditPaneUpdate.CREATED));
1532    return editPane;
1533  } //}}}
1534
1535  //{{{ setEditPane() method
1536  private void setEditPane(EditPane editPane)
1537  {
1538    this.editPane = editPane;
1539    status.updateCaretStatus();
1540    status.updateBufferStatus();
1541    status.updateMiscStatus();
1542
1543    // repaint the gutter so that the border color
1544    // reflects the focus state
1545    updateGutterBorders();
1546
1547    EditBus.send(new ViewUpdate(this,ViewUpdate.EDIT_PANE_CHANGED));
1548  } //}}}
1549
1550  //{{{ handleBufferUpdate() method
1551  private void handleBufferUpdate(BufferUpdate msg)
1552  {
1553    Buffer buffer = msg.getBuffer();
1554    if(msg.getWhat() == BufferUpdate.DIRTY_CHANGED
1555      || msg.getWhat() == BufferUpdate.LOADED)
1556    {
1557      EditPane[] editPanes = getEditPanes();
1558      for(int i = 0; i < editPanes.length; i++)
1559      {
1560        if(editPanes[i].getBuffer() == buffer)
1561        {
1562          updateTitle();
1563          break;
1564        }
1565      }
1566    }
1567  } //}}}
1568
1569  //{{{ handleEditPaneUpdate() method
1570  private void handleEditPaneUpdate(EditPaneUpdate msg)
1571  {
1572    EditPane editPane = msg.getEditPane();
1573    if(editPane.getView() == this
1574      && msg.getWhat() == EditPaneUpdate.BUFFER_CHANGED
1575      && editPane.getBuffer().isLoaded())
1576    {
1577      status.updateCaretStatus();
1578      status.updateBufferStatus();
1579      status.updateMiscStatus();
1580    }
1581  } //}}}
1582
158