/*
* View.java - jEdit view
* :tabSize=4:indentSize=4:noTabs=false:
* :folding=explicit:collapseFolds=1:
*
* Copyright (C) 1998, 2004 Slava Pestov
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.gjt.sp.jedit;
//{{{ Imports
import java.awt.*;
import java.awt.Desktop.Action;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.net.Socket;
import java.util.*;
import java.util.List;
import java.util.function.Consumer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.LayoutFocusTraversalPolicy;
import javax.swing.MenuSelectionManager;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import org.jedit.options.CombinedOptions;
import org.gjt.sp.jedit.EditBus.EBHandler;
import org.gjt.sp.jedit.bufferset.BufferSet;
import org.gjt.sp.jedit.bufferset.BufferSetManager;
import org.gjt.sp.jedit.gui.AboutDialog;
import org.gjt.sp.jedit.gui.ActionBar;
import org.gjt.sp.jedit.gui.CloseDialog;
import org.gjt.sp.jedit.gui.DefaultInputHandler;
import org.gjt.sp.jedit.gui.DockableWindowFactory;
import org.gjt.sp.jedit.gui.DockableWindowManager;
import org.gjt.sp.jedit.gui.HistoryModel;
import org.gjt.sp.jedit.gui.DockingFrameworkProvider;
import org.gjt.sp.jedit.gui.InputHandler;
import org.gjt.sp.jedit.gui.StatusBar;
import org.gjt.sp.jedit.gui.ToolBarManager;
import org.gjt.sp.jedit.gui.VariableGridLayout;
import org.gjt.sp.jedit.gui.DockableWindowManager.DockingLayout;
import org.gjt.sp.jedit.input.InputHandlerProvider;
import org.gjt.sp.jedit.manager.BufferManager;
import org.gjt.sp.jedit.manager.ViewManager;
import org.gjt.sp.jedit.msg.BufferUpdate;
import org.gjt.sp.jedit.msg.EditPaneUpdate;
import org.gjt.sp.jedit.msg.PropertiesChanged;
import org.gjt.sp.jedit.msg.SearchSettingsChanged;
import org.gjt.sp.jedit.msg.ViewUpdate;
import org.gjt.sp.jedit.options.GeneralOptionPane;
import org.gjt.sp.jedit.search.CurrentBufferSet;
import org.gjt.sp.jedit.search.SearchAndReplace;
import org.gjt.sp.jedit.search.SearchBar;
import org.gjt.sp.jedit.textarea.JEditTextArea;
import org.gjt.sp.jedit.textarea.ScrollListener;
import org.gjt.sp.jedit.textarea.TextArea;
import org.gjt.sp.jedit.visitors.JEditVisitor;
import org.gjt.sp.jedit.visitors.JEditVisitorAdapter;
import org.gjt.sp.util.GenericGUIUtilities;
import org.gjt.sp.util.Log;
import org.gjt.sp.util.StandardUtilities;
//}}}
/**
* A <code>View</code> is jEdit's top-level frame window.<p>
*
* In a BeanShell script, you can obtain the current view instance from the
* <code>view</code> variable.<p>
*
* The largest component it contains is an {@link EditPane} that in turn
* contains a {@link org.gjt.sp.jedit.textarea.JEditTextArea} that displays a
* {@link Buffer}.
* A view can have more than one edit pane in a split window configuration.
* A view also contains a menu bar, an optional toolbar and other window
* decorations, as well as docked windows.<p>
*
* The <b>View</b> class performs two important operations
* dealing with plugins: creating plugin menu items, and managing dockable
* windows.
*
* <ul>
* <li>When a view is being created, its initialization routine
* iterates through the collection of loaded plugins and constructs the
* <b>Plugins</b> menu using the properties as specified in the
* {@link EditPlugin} class.</li>
* <li>The view also creates and initializes a
* {@link org.gjt.sp.jedit.gui.DockableWindowManager}
* object. This object is
* responsible for creating, closing and managing dockable windows.</li>
* </ul>
*
* This class does not have a public constructor.
* Views can be opened and closed using methods in the <code>jEdit</code>
* class.
*
* @see org.gjt.sp.jedit.jEdit#newView(View)
* @see org.gjt.sp.jedit.jEdit#newView(View,Buffer)
* @see org.gjt.sp.jedit.jEdit#newView(View,Buffer,boolean)
* @see org.gjt.sp.jedit.jEdit#closeView(View)
*
* @author Slava Pestov
* @author John Gellene (API documentation)
* @version $Id: View.java 25349 2020-06-10 12:07:06Z makarius $
*/
public class View extends JFrame implements InputHandlerProvider
{
public static final EditPane[] EMPTY_EDIT_PANES_ARRAY = new EditPane[0];
//{{{ User interface
//{{{ ToolBar-related constants
public static final String VIEW_DOCKING_FRAMEWORK_PROPERTY = "view.docking.framework";
private static final String ORIGINAL_DOCKING_FRAMEWORK = "Original";
public static final String DOCKING_FRAMEWORK_PROVIDER_SERVICE =
"org.gjt.sp.jedit.gui.DockingFrameworkProvider";
private static DockingFrameworkProvider dockingFrameworkProvider;
//{{{ Groups
/**
* The group of tool bars above the DockableWindowManager
* @see #addToolBar(int,int,java.awt.Component)
* @since jEdit 4.0pre7
*/
public static final int TOP_GROUP = 0;
/**
* The group of tool bars below the DockableWindowManager
* @see #addToolBar(int,int,java.awt.Component)
* @since jEdit 4.0pre7
*/
public static final int BOTTOM_GROUP = 1;
public static final int DEFAULT_GROUP = TOP_GROUP;
//}}}
//{{{ Layers
// Common layers
/**
* The highest possible layer.
* @see #addToolBar(int,int,java.awt.Component)
* @since jEdit 4.0pre7
*/
public static final int TOP_LAYER = Integer.MAX_VALUE;
/**
* The default layer for tool bars with no preference.
* @see #addToolBar(int,int,java.awt.Component)
* @since jEdit 4.0pre7
*/
public static final int DEFAULT_LAYER = 0;
/**
* The lowest possible layer.
* @see #addToolBar(int,int,java.awt.Component)
* @since jEdit 4.0pre7
*/
public static final int BOTTOM_LAYER = Integer.MIN_VALUE;
// Layers for top group
/**
* Above system tool bar layer.
* @see #addToolBar(int,int,java.awt.Component)
* @since jEdit 4.0pre7
*/
public static final int ABOVE_SYSTEM_BAR_LAYER = 150;
/**
* System tool bar layer.
* jEdit uses this for the main tool bar.
* @see #addToolBar(int,int,java.awt.Component)
* @since jEdit 4.0pre7
*/
public static final int SYSTEM_BAR_LAYER = 100;
/**
* Below system tool bar layer.
* @see #addToolBar(int,int,java.awt.Component)
* @since jEdit 4.0pre7
*/
public static final int BELOW_SYSTEM_BAR_LAYER = 75;
/**
* Search bar layer.
* @see #addToolBar(int,int,java.awt.Component)
* @since jEdit 4.0pre7
*/
public static final int SEARCH_BAR_LAYER = 75;
/**
* Below search bar layer.
* @see #addToolBar(int,int,java.awt.Component)
* @since jEdit 4.0pre7
*/
public static final int BELOW_SEARCH_BAR_LAYER = 50;
// Layers for bottom group
/**
* Action bar layer.
* @see #addToolBar(int,int,java.awt.Component)
* @since jEdit 4.2pre1
*/
public static final int ACTION_BAR_LAYER = -75;
/**
* Status bar layer.
* @see #addToolBar(int,int,java.awt.Component)
* @since jEdit 4.2pre1
*/
public static final int STATUS_BAR_LAYER = -100;
/**
* Status bar layer.
* @see #addToolBar(int,int,java.awt.Component)
* @since jEdit 4.2pre1
*/
public static final int BELOW_STATUS_BAR_LAYER = -150;
//}}}
//}}}
//{{{ getDockableWindowManager() method
/**
* Returns the dockable window manager associated with this view.
* @since jEdit 2.6pre3
*/
public DockableWindowManager getDockableWindowManager()
{
return dockableWindowManager;
} //}}}
//{{{ getDockingFrameworkName() method
public static String getDockingFrameworkName()
{
String framework = jEdit.getProperty(
VIEW_DOCKING_FRAMEWORK_PROPERTY, ORIGINAL_DOCKING_FRAMEWORK);
return framework;
} //}}}
//{{{ getDockingFrameworkProvider() method
public static DockingFrameworkProvider getDockingFrameworkProvider()
{
if (dockingFrameworkProvider == null)
{
String framework = getDockingFrameworkName();
dockingFrameworkProvider = (DockingFrameworkProvider)
ServiceManager.getService(
DOCKING_FRAMEWORK_PROVIDER_SERVICE, framework);
if (dockingFrameworkProvider == null)
{
Log.log(Log.ERROR, View.class, "No docking framework " + framework +
" available, using the original one");
dockingFrameworkProvider = (DockingFrameworkProvider)
ServiceManager.getService(
DOCKING_FRAMEWORK_PROVIDER_SERVICE, ORIGINAL_DOCKING_FRAMEWORK);
}
}
return dockingFrameworkProvider;
} //}}}
//{{{ getToolBar() method
/**
* Returns the view's tool bar.
* @since jEdit 4.2pre1
*/
public Container getToolBar()
{
return toolBar;
} //}}}
//{{{ addToolBar() methods
/**
* Adds a tool bar to this view.
* @param toolBar The tool bar
*/
public void addToolBar(Component toolBar)
{
addToolBar(DEFAULT_GROUP, DEFAULT_LAYER, toolBar);
}
/**
* Adds a tool bar to this view.
* @param group The tool bar group to add to
* @param toolBar The tool bar
* @see org.gjt.sp.jedit.gui.ToolBarManager
* @since jEdit 4.0pre7
*/
public void addToolBar(int group, Component toolBar)
{
addToolBar(group, DEFAULT_LAYER, toolBar);
}
/**
* Adds a tool bar to this view.
* @param group The tool bar group to add to
* @param layer The layer of the group to add to
* @param toolBar The tool bar
* @see org.gjt.sp.jedit.gui.ToolBarManager
* @since jEdit 4.0pre7
*/
public void addToolBar(int group, int layer, Component toolBar)
{
toolBarManager.addToolBar(group, layer, toolBar);
getRootPane().revalidate();
} //}}}
//{{{ removeToolBar() method
/**
* Removes a tool bar from this view.
* @param toolBar The tool bar
*/
public void removeToolBar(Component toolBar)
{
if (toolBarManager == null) return;
if (toolBar == null) return;
toolBarManager.removeToolBar(toolBar);
getRootPane().revalidate();
} //}}}
//{{{ showWaitCursor() method
/**
* Shows the wait cursor. This method and
* {@link #hideWaitCursor()} are implemented using a reference
* count of requests for wait cursors, so that nested calls work
* correctly; however, you should be careful to use these methods in
* tandem.<p>
*
* To ensure that {@link #hideWaitCursor()} is always called
* after a {@link #showWaitCursor()}, use a
* <code>try</code>/<code>finally</code> block, like this:
* <pre>try
*{
* view.showWaitCursor();
* // ...
*}
*finally
*{
* view.hideWaitCursor();
*}</pre>
*/
public synchronized void showWaitCursor()
{
if(waitCount++ == 0)
{
Cursor cursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
setCursor(cursor);
visit(new SetCursorVisitor(cursor));
}
} //}}}
//{{{ hideWaitCursor() method
/**
* Hides the wait cursor.
*/
public synchronized void hideWaitCursor()
{
if(waitCount > 0)
waitCount--;
if(waitCount == 0)
{
// still needed even though glass pane
// has a wait cursor
Cursor cursor = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
setCursor(cursor);
visit(new SetCursorVisitor(cursor));
}
} //}}}
//{{{ getSearchBar() method
/**
* Returns the search bar.
* @since jEdit 2.4pre4
*/
public final SearchBar getSearchBar()
{
return searchBar;
} //}}}
//{{{ getActionBar() method
/**
* Returns the action bar.
* @since jEdit 4.2pre3
*/
public final ActionBar getActionBar()
{
return actionBar;
} //}}}
//{{{ getStatus() method
/**
* Returns the status bar. The
* {@link org.gjt.sp.jedit.gui.StatusBar#setMessage(String)} and
* {@link org.gjt.sp.jedit.gui.StatusBar#setMessageAndClear(String)} methods can
* be called on the return value of this method to display status
* information to the user.
* @since jEdit 3.2pre2
*/
public StatusBar getStatus()
{
return status;
} //}}}
//{{{ quickIncrementalSearch() method
/**
* Quick search.
* @since jEdit 4.0pre3
*/
public void quickIncrementalSearch(boolean word)
{
if(searchBar == null)
searchBar = new SearchBar(this,true);
if(searchBar.getParent() == null)
addToolBar(TOP_GROUP,SEARCH_BAR_LAYER,searchBar);
searchBar.setHyperSearch(false);
JEditTextArea textArea = getTextArea();
if(word)
{
String text = textArea.getSelectedText();
if(text == null)
{
textArea.selectWord();
text = textArea.getSelectedText();
}
else if(text.indexOf('\n') != -1)
text = null;
if(text != null && SearchAndReplace.getRegexp())
text = SearchAndReplace.escapeRegexp(text,false);
searchBar.getField().setText(text);
}
searchBar.getField().requestFocus();
searchBar.getField().selectAll();
} //}}}
//{{{ quickHyperSearch() method
/**
* Quick HyperSearch.
* @since jEdit 4.0pre3
*/
public void quickHyperSearch(boolean word)
{
JEditTextArea textArea = getTextArea();
if(word)
{
String text = textArea.getSelectedText();
if(text == null)
{
textArea.selectWord();
text = textArea.getSelectedText();
}
if(text != null && text.indexOf('\n') == -1)
{
if(SearchAndReplace.getRegexp())
{
text = SearchAndReplace.escapeRegexp(
text,false);
}
HistoryModel.getModel("find").addItem(text);
SearchAndReplace.setSearchString(text);
SearchAndReplace.setSearchFileSet(new CurrentBufferSet());
SearchAndReplace.hyperSearch(this);
return;
}
}
if(searchBar == null)
searchBar = new SearchBar(this,true);
if(searchBar.getParent() == null)
addToolBar(TOP_GROUP,SEARCH_BAR_LAYER,searchBar);
searchBar.setHyperSearch(true);
searchBar.getField().setText(null);
searchBar.getField().requestFocus();
searchBar.getField().selectAll();
} //}}}
//{{{ actionBar() method
/**
* Shows the action bar if needed, and sends keyboard focus there.
* @since jEdit 4.2pre1
*/
public void actionBar()
{
if(actionBar == null)
actionBar = new ActionBar(this,true);
if(actionBar.getParent() == null)
addToolBar(BOTTOM_GROUP,ACTION_BAR_LAYER,actionBar);
actionBar.goToActionBar();
} //}}}
//}}}
//{{{ Input handling
//{{{ getKeyEventInterceptor() method
/**
* Returns the listener that will handle all key events in this
* view, if any.
* @return the key event interceptor or null
*/
public KeyListener getKeyEventInterceptor()
{
return inputHandler.getKeyEventInterceptor();
} //}}}
//{{{ setKeyEventInterceptor() method
/**
* Sets the listener that will handle all key events in this
* view. For example, the complete word command uses this so
* that all key events are passed to the word list popup while
* it is visible.
* @param listener The key event interceptor.
*/
public void setKeyEventInterceptor(KeyListener listener)
{
inputHandler.setKeyEventInterceptor(listener);
} //}}}
//{{{ getInputHandler() method
/**
* Returns the input handler.
*/
@Override
public InputHandler getInputHandler()
{
return inputHandler;
} //}}}
//{{{ setInputHandler() method
/**
* Sets the input handler.
* @param inputHandler The new input handler
*/
public void setInputHandler(InputHandler inputHandler)
{
this.inputHandler = inputHandler;
} //}}}
//{{{ getMacroRecorder() method
/**
* Returns the macro recorder.
*/
public Macros.Recorder getMacroRecorder()
{
return recorder;
} //}}}
//{{{ setMacroRecorder() method
/**
* Sets the macro recorder.
* @param recorder The macro recorder
*/
public void setMacroRecorder(Macros.Recorder recorder)
{
this.recorder = recorder;
} //}}}
//{{{ processKeyEvent() method
/**
* Forwards key events directly to the input handler.
* This is slightly faster than using a KeyListener
* because some Swing overhead is avoided.
*/
@Override
public void processKeyEvent(KeyEvent evt)
{
inputHandler.processKeyEvent(evt,VIEW, false);
if(!evt.isConsumed())
super.processKeyEvent(evt);
} //}}}
//{{{ processKeyEvent() method
/**
* Forwards key events directly to the input handler.
* This is slightly faster than using a KeyListener
* because some Swing overhead is avoided.
*/
public void processKeyEvent(KeyEvent evt, boolean calledFromTextArea)
{
processKeyEvent(evt,calledFromTextArea
? TEXT_AREA
: VIEW);
} //}}}
//{{{ processKeyEvent() method
public static final int VIEW = 0;
public static final int TEXT_AREA = 1;
public static final int ACTION_BAR = 2;
/**
* Forwards key events directly to the input handler.
* This is slightly faster than using a KeyListener
* because some Swing overhead is avoided.
*/
public void processKeyEvent(KeyEvent evt, int from)
{
inputHandler.processKeyEvent(evt, from, false);
if(!evt.isConsumed())
super.processKeyEvent(evt);
}
//}}}
//}}}
//{{{ Buffers, edit panes, split panes
//{{{ splitHorizontally() method
/**
* Splits the view horizontally.
* @return the new editPane
* @since jEdit 4.1pre2
*/
public EditPane splitHorizontally()
{
return split(JSplitPane.VERTICAL_SPLIT);
} //}}}
//{{{ splitVertically() method
/**
* Splits the view vertically.
* @return the new editPane
* @since jEdit 4.1pre2
*/
public EditPane splitVertically()
{
return split(JSplitPane.HORIZONTAL_SPLIT);
} //}}}
//{{{ split() method
/**
* Splits the view.
* @param orientation the orientation {@link javax.swing.JSplitPane#HORIZONTAL_SPLIT} or
* {@link javax.swing.JSplitPane#VERTICAL_SPLIT}
* @return the new editPane
* @since jEdit 4.1pre2
*/
public EditPane split(int orientation)
{
PerspectiveManager.setPerspectiveDirty(true);
editPane.saveCaretInfo();
EditPane oldEditPane = editPane;
EditPane newEditPane = createEditPane(oldEditPane.getBufferSet(), oldEditPane.getBuffer());
// setEditPane(newEditPane);
newEditPane.loadCaretInfo();
JComponent oldParent = (JComponent)oldEditPane.getParent();
final JSplitPane newSplitPane = new JSplitPane(orientation);
newSplitPane.setOneTouchExpandable(true);
newSplitPane.setBorder(null);
newSplitPane.setMinimumSize(new Dimension(0,0));
newSplitPane.setResizeWeight(0.5);
int parentSize = orientation == JSplitPane.VERTICAL_SPLIT
? oldEditPane.getHeight() : oldEditPane.getWidth();
final int dividerPosition = (int)((parentSize
- newSplitPane.getDividerSize()) * 0.5);
newSplitPane.setDividerLocation(dividerPosition);
if(oldParent instanceof JSplitPane)
{
JSplitPane oldSplitPane = (JSplitPane)oldParent;
int dividerPos = oldSplitPane.getDividerLocation();
Component left = oldSplitPane.getLeftComponent();
if(left == oldEditPane)
oldSplitPane.setLeftComponent(newSplitPane);
else
oldSplitPane.setRightComponent(newSplitPane);
newSplitPane.setLeftComponent(oldEditPane);
newSplitPane.setRightComponent(newEditPane);
oldSplitPane.setDividerLocation(dividerPos);
}
else
{
splitPane = newSplitPane;
newSplitPane.setLeftComponent(oldEditPane);
newSplitPane.setRightComponent(newEditPane);
setMainContent(newSplitPane);
}
EventQueue.invokeLater(() -> newSplitPane.setDividerLocation(dividerPosition));
newEditPane.focusOnTextArea();
return newEditPane;
} //}}}
//{{{ unsplit() method
/**
* Unsplits the view.
* @since jEdit 2.3pre2
*/
public void unsplit()
{
if(splitPane != null)
{
lastSplitConfig = getSplitConfig();
PerspectiveManager.setPerspectiveDirty(true);
BufferSet.Scope scope = jEdit.getBufferSetManager().getScope();
for(EditPane _editPane: getEditPanes())
{
if(editPane != _editPane)
{
if (scope == BufferSet.Scope.editpane)
mergeBufferSets(editPane, _editPane);
_editPane.close();
}
}
setMainContent(editPane);
splitPane = null;
updateTitle();
editPane.focusOnTextArea();
}
else
javax.swing.UIManager.getLookAndFeel().provideErrorFeedback(null);
} //}}}
//{{{ unsplitCurrent() method
/**
* Removes the current split.
* @since jEdit 2.3pre2
*/
public void unsplitCurrent()
{
if(splitPane != null)
{
lastSplitConfig = getSplitConfig();
PerspectiveManager.setPerspectiveDirty(true);
// find first split pane parenting current edit pane
Component comp = editPane;
while(!(comp instanceof JSplitPane) && comp != null)
{
comp = comp.getParent();
}
BufferSet.Scope scope = jEdit.getBufferSetManager().getScope();
// get rid of any edit pane that is a child
// of the current edit pane's parent splitter
for(EditPane _editPane: getEditPanes())
{
if(GenericGUIUtilities.isAncestorOf(comp,_editPane)
&& _editPane != editPane)
{
if (scope == BufferSet.Scope.editpane)
mergeBufferSets(editPane, _editPane);
_editPane.close();
}
}
JComponent parent = comp == null ? null : (JComponent)comp.getParent();
if(parent instanceof JSplitPane)
{
JSplitPane parentSplit = (JSplitPane)parent;
int pos = parentSplit.getDividerLocation();
if(parentSplit.getLeftComponent() == comp)
parentSplit.setLeftComponent(editPane);
else
parentSplit.setRightComponent(editPane);
parentSplit.setDividerLocation(pos);
parent.revalidate();
}
else
{
setMainContent(editPane);
splitPane = null;
}
updateTitle();
editPane.focusOnTextArea();
}
else
javax.swing.UIManager.getLookAndFeel().provideErrorFeedback(null);
} //}}}
//{{{ resplit() method
/**
* Restore the split configuration as it was before unsplitting.
*
* @since jEdit 4.3pre1
*/
public void resplit()
{
if(lastSplitConfig == null)
javax.swing.UIManager.getLookAndFeel().provideErrorFeedback(null);
else
setSplitConfig(null,lastSplitConfig);
} //}}}
//{{{ getSplitConfig() method
/**
* Split configurations are recorded in a simple RPN "language".
* @return The split configuration, describing where splitpanes
* are, which buffers are open in each EditPane, etc.
*
*/
public String getSplitConfig()
{
StringBuilder splitConfig = new StringBuilder();
if(splitPane != null)
getSplitConfig(splitPane,splitConfig);
else
{
appendToSplitConfig(splitConfig, editPane);
}
return splitConfig.toString();
} //}}}
//{{{ setSplitConfig() method
/**
* sets the split configuration as per the splitConfig.
*
* @param buffer if null, checks all buffers to restore View's split config.
* @param splitConfig the split config, as returned by getSplitConfig()
*/
public void setSplitConfig(Buffer buffer, String splitConfig)
{
try
{
Component comp = restoreSplitConfig(buffer,splitConfig);
setMainContent(comp);
updateTitle();
}
catch(IOException e)
{
// this should never throw an exception.
throw new InternalError();
}
} //}}}
//{{{ nextTextArea() method
/**
* Moves keyboard focus to the next text area.
* @since jEdit 2.7pre4
*/
public void nextTextArea()
{
EditPane[] editPanes = getEditPanes();
for(int i = 0; i < editPanes.length; i++)
{
if(editPane == editPanes[i])
{
if(i == editPanes.length - 1)
editPanes[0].focusOnTextArea();
else
editPanes[i+1].focusOnTextArea();
break;
}
}
} //}}}
//{{{ prevTextArea() method
/**
* Moves keyboard focus to the previous text area.
* @since jEdit 2.7pre4
*/
public void prevTextArea()
{
EditPane[] editPanes = getEditPanes();
for(int i = 0; i < editPanes.length; i++)
{
if(editPane == editPanes[i])
{
if(i == 0)
editPanes[editPanes.length - 1].focusOnTextArea();
else
editPanes[i-1].focusOnTextArea();
break;
}
}
} //}}}
//{{{ getSplitPane() method
/**
* Returns the top-level split pane, if any.
* @return the top JSplitPane if any.
* @since jEdit 2.3pre2
*/
public JSplitPane getSplitPane()
{
return splitPane;
} //}}}
//{{{ getBuffer() method
/**
* Returns the current edit pane's buffer.
* @return the current edit pane's buffer, it can be null
*/
public Buffer getBuffer()
{
if(editPane == null)
return null;
else
return editPane.getBuffer();
} //}}}
//{{{ getBuffers() method
/**
* Returns all Buffers opened in this View,
* Sorted according to View options. (as of jEdit 5.2)
* With order preserved for unsorted buffersets (as of jEdit 5.3)
* @since jEdit 5.1
*/
public Buffer[] getBuffers()
{
BufferSetManager mgr = jEdit.getBufferSetManager();
Collection<Buffer> retval = null;
for (EditPane ep: getEditPanes())
{
BufferSet bs = ep.getBufferSet();
if (retval == null) {
Comparator<Buffer> sorter = bs.getSorter();
if (sorter == null)
retval = new LinkedHashSet<>();
else
retval = new TreeSet<>(sorter);
}
Collections.addAll(retval, bs.getAllBuffers());
// If scope is not editpane, then all buffersets
// are the same and we got what we need.
if (mgr.getScope() != BufferSet.Scope.editpane)
break;
}
Buffer[] bufs = new Buffer[retval.size()];
retval.toArray(bufs);
return bufs;
}//}}}
//{{{ setBuffer() method
/**
* Sets the current edit pane's buffer.
* @param buffer The buffer
*/
public void setBuffer(Buffer buffer)
{
editPane.setBuffer(buffer);
} //}}}
//{{{ goToBuffer() method
/**
* If this buffer is open in one of the view's edit panes, sets focus
* to that edit pane. Otherwise, opens the buffer in the currently
* active edit pane.
* @param buffer The buffer
* @return the current edit pane
* @since jEdit 4.2pre1
*/
public EditPane goToBuffer(Buffer buffer)
{
return showBuffer(buffer, true);
} //}}}
//{{{ showBuffer() method
/**
* If this buffer is open in one of the view's edit panes, activates
* that edit pane. Otherwise, opens the buffer in the currently
* active edit pane. But the focus is not moved.
* @param buffer The buffer to show
* @return the current edit pane
* @since jEdit 4.3pre13
*/
public EditPane showBuffer(Buffer buffer)
{
return showBuffer(buffer, false);
} //}}}
//{{{ getTextArea() method
/**
* Returns the current edit pane's text area.
* @return the current edit pane's text area, or <b>null</b> if there is no edit pane yet
*/
public JEditTextArea getTextArea()
{
if(editPane == null)
return null;
else
return editPane.getTextArea();
} //}}}
//{{{ getEditPane() method
/**
* Returns the current edit pane.
* @return the current edit pane
* @since jEdit 2.5pre2
*/
public EditPane getEditPane()
{
return editPane;
} //}}}
//{{{ getEditPanes() method
/**
* Returns all edit panes.
* @return an array of all edit panes in the view
* @since jEdit 2.5pre2
*/
public EditPane[] getEditPanes()
{
List<EditPane> list = new ArrayList<>();
forEachEditPane(list::add);
return list.toArray(EMPTY_EDIT_PANES_ARRAY);
} //}}}
/**
* Perform the given action on every EditPane
*
* @param action an action to perform on every EditPane
*/
public void forEachEditPane(Consumer<? super EditPane> action)
{
Objects.requireNonNull(action);
if(splitPane == null)
{
if (editPane != null)
{
// it can be null if it is called during the view creation.
action.accept(editPane);
}
}
else
{
performAction(splitPane, action);
}
}
//{{{ getViewConfig() method
/**
* @return a ViewConfig instance for the current view
* @since jEdit 4.2pre1
*/
public ViewConfig getViewConfig()
{
ViewConfig config = new ViewConfig();
config.plainView = isPlainView();
config.splitConfig = getSplitConfig();
config.extState = getExtendedState();
config.docking = dockableWindowManager.getDockingLayout(config);
config.title = userTitle;
String prefix = config.plainView ? "plain-view" : "view";
switch (config.extState)
{
case Frame.MAXIMIZED_BOTH:
case Frame.ICONIFIED:
config.x = jEdit.getIntegerProperty(prefix + ".x",getX());
config.y = jEdit.getIntegerProperty(prefix + ".y",getY());
config.width = jEdit.getIntegerProperty(prefix + ".width",getWidth());
config.height = jEdit.getIntegerProperty(prefix + ".height",getHeight());
break;
case Frame.MAXIMIZED_VERT:
config.x = getX();
config.y = jEdit.getIntegerProperty(prefix + ".y",getY());
config.width = getWidth();
config.height = jEdit.getIntegerProperty(prefix + ".height",getHeight());
break;
case Frame.MAXIMIZED_HORIZ:
config.x = jEdit.getIntegerProperty(prefix + ".x",getX());
config.y = getY();
config.width = jEdit.getIntegerProperty(prefix + ".width",getWidth());
config.height = getHeight();
break;
case Frame.NORMAL:
default:
config.x = getX();
config.y = getY();
config.width = getWidth();
config.height = getHeight();
break;
}
return config;
} //}}}
//}}}
//{{{ isClosed() method
/**
* Returns true if this view has been closed with
* {@link jEdit#closeView(View)}.
* @return true if the view is closed
*/
public boolean isClosed()
{
return closed;
} //}}}
//{{{ isPlainView() method
/**
* Returns true if this is an auxilliary view with no dockable windows.
* @return true if the view is plain
* @since jEdit 4.1pre2
*/
public boolean isPlainView()
{
return plainView;
} //}}}
//{{{ getNext() method
/**
* Returns the next view in the list.
* @return the next view
*/
public View getNext()
{
return next;
} //}}}
//{{{ getPrev() method
/**
* Returns the previous view in the list.
* @return the preview view
*/
public View getPrev()
{
return prev;
} //}}}
//{{{ setPrev() method
/**
* Set the previous view in linked list.
* To be used by {@link BufferManager} only
* @since jEdit 5.6pre1
* @param prev
*/
public void setPrev(View prev)
{
this.prev = prev;
} //}}}
//{{{ setNext() method
/**
* Set the next view in linked list.
* To be used by {@link BufferManager} only
* @since jEdit 5.6pre1
* @param next
*/
public void setNext(View next)
{
this.next = next;
} //}}}
//{{{ handlePropertiesChanged()
@EBHandler
public void handlePropertiesChanged(PropertiesChanged msg)
{
propertiesChanged();
} //}}}
//{{{ handleSearchSettingsChanged() method
@EBHandler
public void handleSearchSettingsChanged(SearchSettingsChanged msg)
{
if(searchBar != null)
searchBar.update();
} //}}}
//{{{ getMinimumSize() method
@Override
public Dimension getMinimumSize()
{
return new Dimension(0,0);
} //}}}
//{{{ setWaitSocket() method
/**
* This socket is closed when the buffer is closed.
*/
public void setWaitSocket(Socket waitSocket)
{
this.waitSocket = waitSocket;
} //}}}
//{{{ toString() method
@Override
public String toString()
{
return getClass().getName() + '['
+ (jEdit.getActiveView() == this
? "active" : "inactive")
+ ']';
} //}}}
//{{{ updateTitle() method
/**
* Updates the title bar.
*/
public void updateTitle()
{
List<Buffer> buffers = new ArrayList<>();
EditPane[] editPanes = getEditPanes();
for (EditPane ep : editPanes)
{
Buffer buffer = ep.getBuffer();
if (!buffers.contains(buffer))
buffers.add(buffer);
}
StringBuilder title = new StringBuilder();
if (userTitle != null)
title.append(userTitle);
else
title.append(jEdit.getProperty("view.title"));
for(int i = 0; i < buffers.size(); i++)
{
if(i != 0)
title.append(", ");
Buffer buffer = buffers.get(i);
title.append(showFullPath && !buffer.isNewFile()
? buffer.getPath(true) : buffer.getName());
if(buffer.isDirty())
title.append(jEdit.getProperty("view.title.dirty"));
}
setTitle(title.toString());
} //}}}
//{{{ setUserTitle() method
/**
* Sets a user-defined title for this view instead of the "view.title" property.
*/
public void setUserTitle(String title)
{
userTitle = title + " - ";
updateTitle();
} //}}}
//{{{ showUserTitleDialog() method
/**
* Shows a dialog for selecting a user-defined title for this view.
*/
public void showUserTitleDialog()
{
String title = JOptionPane.showInputDialog(this, jEdit.getProperty(
"view.title.select"));
if (title == null)
return;
setUserTitle(title);
} //}}}
//{{{ getPrefixFocusOwner() method
public Component getPrefixFocusOwner()
{
return prefixFocusOwner;
} //}}}
//{{{ setPrefixFocusOwner() method
public void setPrefixFocusOwner(Component prefixFocusOwner)
{
this.prefixFocusOwner = prefixFocusOwner;
} //}}}
//{{{ visit() method
/**
* Visit the the editpanes and textareas of the view
* @param visitor the visitor
* @since jEdit 4.3pre13
*/
public void visit(JEditVisitor visitor)
{
EditPane[] panes = getEditPanes();
for (EditPane editPane : panes)
{
visitor.visit(editPane);
visitor.visit(editPane.getTextArea());
}
} //}}}
// {{{ closeAllMenus()
/** closes any popup menus that may have been opened
@since jEdit 4.4pre1
*/
public void closeAllMenus()
{
MenuSelectionManager.defaultManager().clearSelectedPath();
KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner();
} // }}}
//{{{ Package-private members
View prev;
View next;
//{{{ View constructor
View(Buffer buffer, ViewConfig config)
{
fullScreenMode = false;
menuBar = null;
plainView = config.plainView;
enableEvents(AWTEvent.KEY_EVENT_MASK);
// OS X users expect a preview of the window rather than an icon
if (!OperatingSystem.isMacOS())
setIconImage(GUIUtilities.getEditorIcon());
mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());
dockableWindowManager = getDockingFrameworkProvider().create(this,
DockableWindowFactory.getInstance(), config);
userTitle = config.title;
dockableWindowManager.setMainPanel(mainPanel);
topToolBars = new JPanel(new VariableGridLayout(
VariableGridLayout.FIXED_NUM_COLUMNS,
1));
bottomToolBars = new JPanel(new VariableGridLayout(
VariableGridLayout.FIXED_NUM_COLUMNS,
1));
toolBarManager = new ToolBarManager(topToolBars, bottomToolBars);
status = new StatusBar(this);
inputHandler = new DefaultInputHandler(this,(DefaultInputHandler)
jEdit.getInputHandler());
setSplitConfig(buffer,config.splitConfig);
getContentPane().add(BorderLayout.CENTER,dockableWindowManager);
dockableWindowManager.init();
// tool bar and status bar gets added in propertiesChanged()
// depending in the 'tool bar alternate layout' setting.
propertiesChanged();
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowHandler());
setFocusTraversalPolicy(new MyFocusTraversalPolicy());
EditBus.addToBus(this);
GUIUtilities.addSizeSaver(this, null, plainView ? "plain-view" : "view");
} //}}}
//{{{ updateFullScreenProps() method
public void updateFullScreenProps()
{
boolean alternateLayout = jEdit.getBooleanProperty(
"view.toolbar.alternateLayout");
boolean showMenu = jEdit.getBooleanProperty("fullScreenIncludesMenu");
boolean showToolbars = jEdit.getBooleanProperty("fullScreenIncludesToolbar");
boolean showStatus = jEdit.getBooleanProperty("fullScreenIncludesStatus");
if (! showMenu)
{
menuBar = getJMenuBar();
setJMenuBar(null);
}
else if (menuBar != null)
setJMenuBar(menuBar);
// Note: Bottom toolbar is the action bar, which is always enabled
loadToolBars();
if (alternateLayout)
{
if (! showStatus)
removeToolBar(status);
else
addToolBar(BOTTOM_GROUP,STATUS_BAR_LAYER,status);
}
else
{
if (! showStatus)
getContentPane().remove(status);
else
getContentPane().add(BorderLayout.SOUTH,status);
}
} //}}}
//{{{ isFullScreenMode method
public boolean isFullScreenMode()
{
return fullScreenMode;
}//}}}
//{{{ toggleFullScreen() method
public void toggleFullScreen()
{
fullScreenMode = !fullScreenMode;
GraphicsDevice sd = getGraphicsConfiguration().getDevice();
dispose();
if (fullScreenMode)
{
updateFullScreenProps();
windowedBounds = getBounds();
setUndecorated(true);
setBounds(sd.getDefaultConfiguration().getBounds());
validate();
}
else
{
boolean showStatus = plainView ? jEdit.getBooleanProperty("view.status.plainview.visible") :
jEdit.getBooleanProperty("view.status.visible");
if (menuBar != null && getJMenuBar() != menuBar)
setJMenuBar(menuBar);
boolean alternateLayout = jEdit.getBooleanProperty(
"view.toolbar.alternateLayout");
loadToolBars();
if (showStatus)
{
if (alternateLayout)
addToolBar(BOTTOM_GROUP,STATUS_BAR_LAYER,status);
else
getContentPane().add(BorderLayout.SOUTH,status);
}
setUndecorated(false);
setBounds(windowedBounds);
}
setVisible(true);
toFront();
closeAllMenus();
// so you can keep typing in your editpane afterwards...
editPane.getTextArea().requestFocus();
EditBus.send(new ViewUpdate(this,ViewUpdate.FULL_SCREEN_TOGGLED));
} //}}}
//{{{ confirmToCloseDirty() methods
/**
* If the view contains dirty buffers which will be closed on
* closing the view, show the confirmation dialog for user.
* @return
* true if there are no such buffers or user select OK
* to close the view; false if user select Cancel
*/
boolean confirmToCloseDirty()
{
boolean autosaveUntitled = jEdit.getBooleanProperty("autosaveUntitled");
boolean suppressNotSavedConfirmUntitled = jEdit.getBooleanProperty("suppressNotSavedConfirmUntitled") || autosaveUntitled;
Set<Buffer> checkingBuffers = getOpenBuffers();
ViewManager viewManager = jEdit.getViewManager();
viewManager
.getViews()
.stream()
.filter(view -> view != this)
.forEach(view -> checkingBuffers.removeAll(view.getOpenBuffers()));
for (Buffer buffer: checkingBuffers)
{
if (buffer.isDirty() && !(buffer.isUntitled() && suppressNotSavedConfirmUntitled))
{
return new CloseDialog(this, checkingBuffers).isOK();
}
}
return true;
} //}}}
//{{{ close() method
void close()
{
EditBus.send(new ViewUpdate(this,ViewUpdate.CLOSED));
closed = true;
// save dockable window geometry, and close 'em
dockableWindowManager.close();
EditBus.removeFromBus(this);
dispose();
EditPane[] editPanes = getEditPanes();
for (EditPane ep : editPanes)
ep.close();
// null some variables so that retaining references
// to closed views won't hurt as much.
toolBarManager = null;
toolBar = null;
searchBar = null;
splitPane = null;
inputHandler = null;
recorder = null;
getContentPane().removeAll();
// notify clients with -wait
if(waitSocket != null)
{
try
{
waitSocket.getOutputStream().write('\0');
waitSocket.getOutputStream().flush();
waitSocket.getInputStream().close();
waitSocket.getOutputStream().close();
waitSocket.close();
}
catch(IOException io)
{
//Log.log(Log.ERROR,this,io);
}
}
} //}}}
//}}}
//{{{ Private members
//{{{ Instance variables
private boolean closed;
private final DockableWindowManager dockableWindowManager;
private final JPanel mainPanel;
private final JPanel topToolBars;
private final JPanel bottomToolBars;
private ToolBarManager toolBarManager;
private Container toolBar;
private SearchBar searchBar;
private ActionBar actionBar;
private EditPane editPane;
private JSplitPane splitPane;
private String lastSplitConfig;
private final StatusBar status;
private InputHandler inputHandler;
private Macros.Recorder recorder;
private Component prefixFocusOwner;
private int waitCount;
private boolean showFullPath;
private final boolean plainView;
private Socket waitSocket;
private Component mainContent;
private boolean fullScreenMode;
private Rectangle windowedBounds;
private JMenuBar menuBar;
private String userTitle;
//}}}
//{{{ setMainContent() method
private void setMainContent(Component c)
{
if (mainContent != null)
mainPanel.remove(mainContent);
mainContent = c;
mainPanel.add(mainContent, BorderLayout.CENTER);
if (c instanceof JSplitPane)
{
splitPane = (JSplitPane)c;
}
else
{
splitPane = null;
editPane = (EditPane)c;
}
mainPanel.revalidate();
mainPanel.repaint();
} //}}}
//{{{ getEditPanes() method
private static void performAction(Component comp, Consumer<? super EditPane> action)
{
if(comp instanceof EditPane)
action.accept((EditPane) comp);
else if(comp instanceof JSplitPane)
{
JSplitPane split = (JSplitPane)comp;
performAction(split.getLeftComponent(), action);
performAction(split.getRightComponent(), action);
}
} //}}}
//{{{ showBuffer() method
private EditPane showBuffer(Buffer buffer, boolean focus)
{
if(editPane.getBuffer() == buffer
&& editPane.getTextArea().getVisibleLines() > 1)
{
if (focus)
editPane.focusOnTextArea();
return editPane;
}
EditPane[] editPanes = getEditPanes();
for (EditPane ep : editPanes)
{
if (ep.getBuffer() == buffer
/* ignore zero-height splits, etc */
&& ep.getTextArea().getVisibleLines() > 1)
{
setEditPane(ep);
if (focus)
ep.focusOnTextArea();
return ep;
}
}
editPane.setBuffer(buffer, focus);
return editPane;
} //}}}
//{{{ getSplitConfig() method
/*
* The split config is recorded in a simple RPN "language".
*/
private static void getSplitConfig(JSplitPane splitPane,
StringBuilder splitConfig)
{
Component right = splitPane.getRightComponent();
appendToSplitConfig(splitConfig, right);
splitConfig.append(' ');
Component left = splitPane.getLeftComponent();
appendToSplitConfig(splitConfig, left);
splitConfig.append(' ');
splitConfig.append(splitPane.getDividerLocation());
splitConfig.append(' ');
splitConfig.append(splitPane.getOrientation()
== JSplitPane.VERTICAL_SPLIT ? "vertical" : "horizontal");
} //}}}
//{{{ appendToSplitConfig() method
/**
* Append the Component to the split config.
* The component must be a JSplitPane or an EditPane
*
* @param splitConfig the split config
* @param component the component
*/
private static void appendToSplitConfig(StringBuilder splitConfig, Component component)
{
if(component instanceof JSplitPane)
{
// the component is a JSplitPane
getSplitConfig((JSplitPane)component,splitConfig);
}
else
{
boolean autosaveUntitled = jEdit.getBooleanProperty("autosaveUntitled");
// the component is an editPane
EditPane editPane = (EditPane) component;
splitConfig.append('"');
Buffer editPaneBuffer = editPane.getBuffer();
splitConfig.append(StandardUtilities.charsToEscapes(editPaneBuffer.getPath()));
splitConfig.append("\" buffer");
BufferSet bufferSet = editPane.getBufferSet();
Buffer[] buffers = bufferSet.getAllBuffers();
for (Buffer buffer : buffers)
{
if (!buffer.isNewFile() || (buffer.isUntitled() && autosaveUntitled))
{
splitConfig.append(" \"");
splitConfig.append(StandardUtilities.charsToEscapes(buffer.getPath() ));
splitConfig.append("\" buff");
}
}
splitConfig.append(" \"");
splitConfig.append(jEdit.getBufferSetManager().getScope());
splitConfig.append("\" bufferset");
}
} //}}}
//{{{ restoreSplitConfig() method
private Component restoreSplitConfig(Buffer buffer, String splitConfig)
throws IOException
// this is where checked exceptions piss me off. this method only uses
// a StringReader which can never throw an exception...
{
BufferManager bufferManager = jEdit.getBufferManager();
if(buffer != null)
{
return editPane = createEditPane(buffer);
}
else if(splitConfig == null || splitConfig.trim().length() == 0)
{
Buffer buf = bufferManager.getFirst();
if (buf == null)
{
buf = BufferSetManager.createUntitledBuffer();
}
return editPane = createEditPane(buf);
}
List<Buffer> buffers = bufferManager.getBuffers();
Stack<Object> stack = new Stack<>();
// we create a stream tokenizer for parsing a simple
// stack-based language
StreamTokenizer st = new StreamTokenizer(new StringReader(
splitConfig));
st.whitespaceChars(0,' ');
/* all printable ASCII characters */
st.wordChars('#','~');
st.commentChar('!');
st.quoteChar('"');
st.eolIsSignificant(false);
Collection<Buffer> editPaneBuffers = new ArrayList<>();
loop: while (true)
{
switch(st.nextToken())
{
case StreamTokenizer.TT_EOF:
break loop;
case StreamTokenizer.TT_WORD:
if("vertical".equals(st.sval) || "horizontal".equals(st.sval))
{
int orientation
= "vertical".equals(st.sval)
? JSplitPane.VERTICAL_SPLIT
: JSplitPane.HORIZONTAL_SPLIT;
int divider = (Integer) stack.pop();
Object obj1 = stack.pop();
Object obj2 = stack.pop();
// Backward compatibility with pre-bufferset versions
if (obj1 instanceof Buffer)
{
Buffer b1 = buffer = (Buffer) obj1;
obj1 = editPane = createEditPane(b1);
}
if (obj2 instanceof Buffer)
{
Buffer b2 = (Buffer) obj2;
obj2 = createEditPane(b2);
}
stack.push(splitPane = new JSplitPane(
orientation,
(Component)obj1,
(Component)obj2));
splitPane.setOneTouchExpandable(true);
splitPane.setBorder(null);
splitPane.setMinimumSize(
new Dimension(0,0));
splitPane.setDividerLocation(divider);
}
else if("buffer".equals(st.sval))
{
Object obj = stack.pop();
if(obj instanceof Integer)
{
int index = (Integer) obj;
if(index >= 0 && index < buffers.size())
buffer = buffers.get(index);
}
else if(obj instanceof String)
{
String path = (String)obj;
Optional<Buffer> bufferOptional = bufferManager.getBuffer(path);
buffer = bufferOptional.orElseGet(() ->
{
Buffer buf = jEdit.openTemporary(jEdit.getActiveView(), null,
path, true, null, true);
jEdit.commitTemporary(buf);
return buf;
});
}
if(buffer == null)
buffer = bufferManager.getFirst();
stack.push(buffer);
editPaneBuffers.add(buffer);
}
else if ("buff".equals(st.sval))
{
String path = (String)stack.pop();
Optional<Buffer> bufferOptional = bufferManager.getBuffer(path);
if (bufferOptional.isEmpty())
{
Log.log(Log.WARNING, this, "Buffer " + path + " doesn't exist");
}
else
{
buffer = bufferOptional.get();
editPaneBuffers.add(buffer);
}
}
else if ("bufferset".equals(st.sval))
{
// pop the bufferset scope. Not used anymore but still here for compatibility
// with old perspectives
stack.pop();
buffer = (Buffer) stack.pop();
editPane = createEditPane(buffer);
stack.push(editPane);
BufferSet bufferSet = editPane.getBufferSet();
int i = 0;
for (Buffer buff : editPaneBuffers)
{
bufferSet.addBufferAt(buff,i);
i++;
}
editPaneBuffers.clear();
}
break;
case StreamTokenizer.TT_NUMBER:
stack.push((int)st.nval);
break;
case '"':
stack.push(st.sval);
break;
}
}
// Backward compatibility with pre-bufferset versions
Object obj = stack.peek();
if (obj instanceof Buffer)
{
obj = editPane = createEditPane((Buffer)obj);
}
updateGutterBorders();
return (Component)obj;
} //}}}
//{{{ propertiesChanged() method
/**
* Reloads various settings from the properties.
*/
private void propertiesChanged()
{
JMenuBar mbar = GUIUtilities.loadMenuBar("view.mbar");
// menu bar mnemonics take precedence over other shortcut definitions
for (int i = 0; i < mbar.getMenuCount(); i++)
{
JMenu menu = mbar.getMenu(i);
if (menu != null)
{
int mnemonic = menu.getMnemonic();
if (mnemonic != 0)
{
Object keyBinding = inputHandler.getKeyBinding("A+" + Character.toLowerCase((char) mnemonic));
if (keyBinding != null)
{
menu.setMnemonic(0);
}
}
}
}
setJMenuBar(mbar);
if (Desktop.isDesktopSupported())
{
Desktop desktop = Desktop.getDesktop();
if (desktop.isSupported(Action.APP_QUIT_HANDLER))
{
desktop.setQuitHandler((e, response) ->
{
jEdit.exit(jEdit.getActiveView(), true);
response.cancelQuit();
});
}
if (desktop.isSupported(Action.APP_ABOUT))
{
desktop.setAboutHandler((e) -> new AboutDialog(jEdit.getActiveView()));
}
if (desktop.isSupported(Action.APP_PREFERENCES))
{
desktop.setPreferencesHandler((e) -> new CombinedOptions(jEdit.getActiveView(), 0));
}
}
loadToolBars();
showFullPath = jEdit.getBooleanProperty("view.showFullPath");
updateTitle();
status.propertiesChanged();
removeToolBar(status);
getContentPane().remove(status);
boolean showStatus = plainView ? jEdit.getBooleanProperty("view.status.plainview.visible") :
jEdit.getBooleanProperty("view.status.visible");
if (jEdit.getBooleanProperty("view.toolbar.alternateLayout"))
{
getContentPane().add(BorderLayout.NORTH,topToolBars);
getContentPane().add(BorderLayout.SOUTH,bottomToolBars);
if (showStatus)
addToolBar(BOTTOM_GROUP,STATUS_BAR_LAYER,status);
}
else
{
mainPanel.add(topToolBars, BorderLayout.NORTH);
mainPanel.add(bottomToolBars, BorderLayout.SOUTH);
if (showStatus)
getContentPane().add(BorderLayout.SOUTH,status);
}
updateBufferSwitcherStates();
getRootPane().revalidate();
//SwingUtilities.updateComponentTreeUI(getRootPane());
if (fullScreenMode)
updateFullScreenProps();
} //}}}
//{{{ updateBufferSwitcherStates() method
/**
* Enables or Disables the "Focus Buffer Switcher" menu item in the View menu
* depending on the visible state of the buffer switcher. The menu item
* is intended to have the same effect as clicking on the buffer switcher
* combo box, and it doesn't make sense to have this action available if
* the buffer switcher isn't visible.
* Also shows or hides the Buffer Switcher itself, since this can be invoked after
* the toggle buffer switcher action.
*/
public void updateBufferSwitcherStates()
{
boolean show = jEdit.getBooleanProperty("view.showBufferSwitcher");
JMenuBar menubar = getJMenuBar();
if (menubar == null)
{
return;
}
String viewmenu_label = jEdit.getProperty("view.label");
viewmenu_label = viewmenu_label.replace("$", "");
String sbs_label = jEdit.getProperty("focus-buffer-switcher.label");
sbs_label = sbs_label.replace("$", "");
JMenu viewmenu = null;
for (int i = 0; i < menubar.getMenuCount(); i++)
{
JMenu menu = menubar.getMenu(i);
if (menu.getText().equals(viewmenu_label))
{
viewmenu = menu;
break;
}
}
if (viewmenu != null)
{
for (int i = 0; i < viewmenu.getMenuComponentCount(); i++)
{
Component item = viewmenu.getMenuComponent(i);
if (item instanceof JMenuItem && ((JMenuItem)item).getText().equals(sbs_label))
{
item.setEnabled(show);
// viewmenu.invalidate();
}
}
}
// Toggle the visibility of the BufferSwitcher itself
// todo : flatmap
jEdit.getViewManager()
.forEach(view -> {
for (EditPane ep: view.getEditPanes())
ep.loadBufferSwitcher();
});
} //}}}
//{{{ loadToolBars() method
private void loadToolBars()
{
if(!plainView && (fullScreenMode ?
jEdit.getBooleanProperty("fullScreenIncludesToolbar") :
jEdit.getBooleanProperty("view.showToolbar")))
{
if(toolBar != null)
toolBarManager.removeToolBar(toolBar);
toolBar = GUIUtilities.loadToolBar("view.toolbar");
addToolBar(TOP_GROUP, SYSTEM_BAR_LAYER, toolBar);
}
else if(toolBar != null)
{
removeToolBar(toolBar);
toolBar = null;
}
if(searchBar != null)
{
searchBar.propertiesChanged();
removeToolBar(searchBar);
}
if(jEdit.getBooleanProperty("view.showSearchbar") && !plainView)
{
if(searchBar == null)
searchBar = new SearchBar(this,false);
addToolBar(TOP_GROUP,SEARCH_BAR_LAYER,searchBar);
}
} //}}}
//{{{ createEditPane() methods
private EditPane createEditPane(@Nonnull Buffer buffer)
{
return createEditPane(null, buffer);
}
private EditPane createEditPane(@Nullable BufferSet bufferSetSource, @Nonnull Buffer buffer)
{
EditPane editPane = new EditPane(this, bufferSetSource, buffer);
JEditTextArea textArea = editPane.getTextArea();
textArea.addFocusListener(new FocusHandler());
textArea.addCaretListener(new CaretHandler());
textArea.addScrollListener(new ScrollHandler());
EditBus.send(new EditPaneUpdate(editPane,EditPaneUpdate.CREATED));
return editPane;
} //}}}
//{{{ setEditPane() method
private void setEditPane(EditPane editPane)
{
this.editPane = editPane;
status.updateCaretStatus();
status.updateBufferStatus();
status.updateMiscStatus();
// repaint the gutter so that the border color
// reflects the focus state
updateGutterBorders();
EditBus.send(new ViewUpdate(this,ViewUpdate.EDIT_PANE_CHANGED));
} //}}}
//{{{ handleBufferUpdate() method
@EBHandler
public void handleBufferUpdate(BufferUpdate msg)
{
Buffer buffer = msg.getBuffer();
if(msg.getWhat() == BufferUpdate.DIRTY_CHANGED
|| msg.getWhat() == BufferUpdate.LOADED)
{
EditPane[] editPanes = getEditPanes();
for (EditPane ep : editPanes)
{
if (ep.getBuffer() == buffer)
{
updateTitle();
break;
}
}
}
} //}}}
//{{{ handleEditPaneUpdate() method
@EBHandler
public void handleEditPaneUpdate(EditPaneUpdate msg)
{
EditPane editPane = msg.getEditPane();
if(editPane != null &&
editPane.getView() == this
&& msg.getWhat() == EditPaneUpdate.BUFFER_CHANGED
&& editPane.getBuffer().isLoaded())
{
closeDuplicateBuffers(msg);
status.updateCaretStatus();
status.updateBufferStatus();
status.updateMiscStatus();
}
} //}}}
//{{{ handleViewUpdate() method
@EBHandler
public void handleViewUpdate(ViewUpdate msg)
{
// only have my view handle each update message
if (msg.getView() == null || msg.getView() != this) return;
final int check = jEdit.getIntegerProperty("checkFileStatus");
if ((check == 0) || !jEdit.isStartupDone()) return;
// "buffer visit" also includes checking the buffer when you change editpanes.
// "buffer visit" also includes checking the buffer when you activate view, coming from
// another program, which could have alterered file on disk.
if ((msg.getWhat() == ViewUpdate.EDIT_PANE_CHANGED || msg.getWhat() == ViewUpdate.ACTIVATED) &&
((check & GeneralOptionPane.checkFileStatus_focusBuffer) > 0))
jEdit.checkBufferStatus(View.this, true);
else if ((msg.getWhat() == ViewUpdate.ACTIVATED) &&
(check & GeneralOptionPane.checkFileStatus_focus) > 0)
jEdit.checkBufferStatus(View.this,
(check != GeneralOptionPane.checkFileStatus_focus));
}//}}}
//{{{ closeDuplicateBuffers() method
/** Used if exclusive buffersets are enabled */
private void closeDuplicateBuffers(EditPaneUpdate epu)
{
if (!jEdit.getBooleanProperty("buffersets.exclusive"))
return;
final BufferSet.Scope scope = jEdit.getBufferSetManager().getScope();
if (scope == BufferSet.Scope.global)
return;
final EditPane ep = epu.getEditPane();
/* Only one view needs to handle this message, since
we iterate through all the other views */
final View view = ep.getView();
if (view != this)
return;
final Buffer b = ep.getBuffer();
if (b.isDirty()) return;
jEdit.visit(new JEditVisitorAdapter()
{
@Override
public void visit(EditPane editPane)
{
if (editPane == ep ||
(scope == BufferSet.Scope.view && editPane.getView() == view))
return;
if (editPane.getBufferSet().indexOf(b) < 0)
return;
jEdit.getBufferSetManager().removeBuffer(editPane, b);
}
});
} //}}}
//{{{ updateGutterBorders() method
/**
* Updates the borders of all gutters in this view to reflect the
* currently focused text area.
* @since jEdit 2.6final
*/
private void updateGutterBorders()
{
EditPane[] editPanes = getEditPanes();
for (EditPane editPane : editPanes)
editPane.getTextArea().getGutter().updateBorder();
} //}}}
//{{{ getOpenBuffers() method
private Set<Buffer> getOpenBuffers()
{
Set<Buffer> openBuffers = new LinkedHashSet<>();
for (EditPane editPane: getEditPanes())
{
openBuffers.addAll(Arrays.asList(
editPane.getBufferSet().getAllBuffers()));
}
return openBuffers;
} //}}}
//{{{ mergeBufferSets() method
/**
* Merge a EditPane's BufferSet into another one.
* This is used on unsplitting panes not to close buffers.
* @param target the target bufferSet where we will merge buffers from source
* @param source the source bufferSet
*/
private static void mergeBufferSets(EditPane target, EditPane source)
{
BufferSetManager manager = jEdit.getBufferSetManager();
for (Buffer buffer: source.getBufferSet().getAllBuffers())
{
manager.addBuffer(target, buffer);
}
} //}}}
//}}}
//{{{ Inner classes
//{{{ CaretHandler class
private class CaretHandler implements CaretListener
{
@Override
public void caretUpdate(CaretEvent evt)
{
if(evt.getSource() == getTextArea())
status.updateCaretStatus();
}
} //}}}
//{{{ FocusHandler class
private class FocusHandler extends FocusAdapter
{
@Override
public void focusGained(FocusEvent evt)
{
// walk up hierarchy, looking for an EditPane
Component comp = (Component)evt.getSource();
while(!(comp instanceof EditPane))
{
if(comp == null)
return;
comp = comp.getParent();
}
if(comp != editPane)
setEditPane((EditPane)comp);
else
updateGutterBorders();
}
} //}}}
//{{{ ScrollHandler class
private class ScrollHandler implements ScrollListener
{
@Override
public void scrolledVertically(TextArea textArea)
{
if(getTextArea() == textArea)
status.updateCaretStatus();
}
@Override
public void scrolledHorizontally(TextArea textArea) {}
} //}}}
//{{{ WindowHandler class
private class WindowHandler extends WindowAdapter
{
@Override
public void windowActivated(WindowEvent evt)
{
boolean appFocus = false;
boolean viewChanged = false;
View oldView = jEdit.getActiveViewInternal();
// check if view is changed
if (oldView != View.this) viewChanged = true;
/* ACTIVATED currently means whenever the View gets focus.
Ideally it should be only when the View changes or we are
focusing on a View after previously using in another application.
Currently, we also get ACTIVATED messages after a closed jEdit dialog.
I consider this a bug which we should fix some day. */
if (evt.getOppositeWindow() == null) appFocus = true;
jEdit.setActiveView(View.this);
// Log.log(Log.DEBUG, this, "appFocus:" + appFocus + " viewChanged:" + viewChanged);
if (appFocus || viewChanged)
EditBus.send(new ViewUpdate(View.this, ViewUpdate.ACTIVATED));
}
@Override
public void windowClosing(WindowEvent evt)
{
jEdit.closeView(View.this);
}
} //}}}
//{{{ ViewConfig class
public static class ViewConfig
{
public int x, y, width, height, extState;
public boolean plainView;
public String splitConfig;
public DockingLayout docking;
public String title;
public ViewConfig()
{
}
public ViewConfig(boolean plainView)
{
this.plainView = plainView;
String prefix = plainView ? "plain-view" : "view";
x = jEdit.getIntegerProperty(prefix + ".x",0);
y = jEdit.getIntegerProperty(prefix + ".y",0);
width = jEdit.getIntegerProperty(prefix + ".width",0);
height = jEdit.getIntegerProperty(prefix + ".height",0);
extState = jEdit.getIntegerProperty(prefix + ".extendedState", Frame.NORMAL);
}
public ViewConfig(boolean plainView, String splitConfig,
int x, int y, int width, int height, int extState)
{
this.plainView = plainView;
this.splitConfig = splitConfig;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.extState = extState;
}
} //}}}
//{{{ isInsideScreen() method
private static boolean isInsideScreen(View parent, Rectangle r)
{
Rectangle bounds;
if (parent == null)
bounds = GenericGUIUtilities.getScreenBounds();
else
bounds = parent.getGraphicsConfiguration().getBounds();
int minWidth = jEdit.getIntegerProperty("view.minStartupWidth");
int minHeight = jEdit.getIntegerProperty("view.minStartupHeight");
return r.x + r.width > bounds.x + minWidth && // right edge at minWidth pixels on the right of the left bound
r.x < bounds.x + bounds.width - minWidth && // left edge at minWidth pixels on the left of the right bound
r.y + r.height > bounds.y + minHeight && // bottom edge at minHeight pixels under the top bound
r.y < bounds.y + bounds.height - minHeight; // top edge at minHeight pixels on the top of the bottom bound
} //}}}
public void adjust(View parent, ViewConfig config)
{
if(config.width != 0 && config.height != 0)
{
Rectangle desired = new Rectangle(
config.x, config.y, config.width, config.height);
if (! isInsideScreen(parent, desired))
setLocationRelativeTo(parent);
else
{
if(OperatingSystem.isX11() && Debug.GEOMETRY_WORKAROUND)
new GUIUtilities.UnixWorkaround(this,"view",desired,config.extState);
else
{
setBounds(desired);
setExtendedState(config.extState);
}
}
}
else
setLocationRelativeTo(parent);
}
//{{{ MyFocusTraversalPolicy class
private static class MyFocusTraversalPolicy extends LayoutFocusTraversalPolicy
{
@Override
public Component getDefaultComponent(Container focusCycleRoot)
{
return GUIUtilities.getView(focusCycleRoot).getTextArea();
}
} //}}}
//{{{ SetCursorVisitor class
private static class SetCursorVisitor extends JEditVisitorAdapter
{
private final Cursor cursor;
SetCursorVisitor(Cursor cursor)
{
this.cursor = cursor;
}
@Override
public void visit(EditPane editPane)
{
editPane.setCursor(cursor);
}
}//}}}
//}}}
}
¤ Dauer der Verarbeitung: 0.117 Sekunden
(vorverarbeitet)
¤
|
Haftungshinweis
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.
|