/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License.
*/
/** * Editor kit implementation for base document * * @author Miloslav Metelka * @version 1.00
*/
publicclass BaseKit extends DefaultEditorKit {
/** * Flag indicating that the JTextComponent.paste() is in progress. * Checked in BaseDocument.read() to ignore clearing of the regions * for trailing-whitespace-removal.
*/ static ThreadLocal<Boolean> IN_PASTE = new ThreadLocal<Boolean>();
/** Remove characters to the begining of the word or
* the previous word if caret is not directly at word */ publicstaticfinal String removePreviousWordAction = "remove-word-previous"; // NOI18N
/** Remove characters to the end of the word or
* the next word if caret is not directly at word */ publicstaticfinal String removeNextWordAction = "remove-word-next"; // NOI18N
/** Remove to the begining of the line */ publicstaticfinal String removeLineBeginAction = "remove-line-begin"; // NOI18N
/** Toggle the typing mode to overwrite mode or back to insert mode */ publicstaticfinal String toggleTypingModeAction = "toggle-typing-mode"; // NOI18N
/** Change the selected text or current character to uppercase */ publicstaticfinal String toUpperCaseAction = "to-upper-case"; // NOI18N
/** Change the selected text or current character to lowercase */ publicstaticfinal String toLowerCaseAction = "to-lower-case"; // NOI18N
/** Switch the case of the selected text or current character */ publicstaticfinal String switchCaseAction = "switch-case"; // NOI18N
/** Shift line right action */ publicstaticfinal String shiftLineRightAction = "shift-line-right"; // NOI18N
/** Shift line left action */ publicstaticfinal String shiftLineLeftAction = "shift-line-left"; // NOI18N
/** Action that scrolls the window so that caret is at the center of the window */ publicstaticfinal String adjustWindowCenterAction = "adjust-window-center"; // NOI18N
/** Action that scrolls the window so that caret is at the top of the window */ publicstaticfinal String adjustWindowTopAction = "adjust-window-top"; // NOI18N
/** Action that scrolls the window so that caret is at the bottom of the window */ publicstaticfinal String adjustWindowBottomAction = "adjust-window-bottom"; // NOI18N
/** Action that moves the caret so that caret is at the center of the window */ publicstaticfinal String adjustCaretCenterAction = "adjust-caret-center"; // NOI18N
/** Action that moves the caret so that caret is at the top of the window */ publicstaticfinal String adjustCaretTopAction = "adjust-caret-top"; // NOI18N
/** Action that moves the caret so that caret is at the bottom of the window */ publicstaticfinal String adjustCaretBottomAction = "adjust-caret-bottom"; // NOI18N
/** Format part of the document text using Indent */ publicstaticfinal String formatAction = "format"; // NOI18N
/** Indent part of the document text using Indent */ publicstaticfinal String indentAction = "indent"; // NOI18N
/** First non-white character on the line */ publicstaticfinal String firstNonWhiteAction = "first-non-white"; // NOI18N
/** Last non-white character on the line */ publicstaticfinal String lastNonWhiteAction = "last-non-white"; // NOI18N
/** First non-white character on the line */ publicstaticfinal String selectionFirstNonWhiteAction = "selection-first-non-white"; // NOI18N
/** Last non-white character on the line */ publicstaticfinal String selectionLastNonWhiteAction = "selection-last-non-white"; // NOI18N
/** Select the nearest identifier around caret */ publicstaticfinal String selectIdentifierAction = "select-identifier"; // NOI18N
/** Select the next parameter (after the comma) in the given context */ publicstaticfinal String selectNextParameterAction = "select-next-parameter"; // NOI18N
/** Go to the previous position stored in the jump-list */ publicstaticfinal String jumpListNextAction = "jump-list-next"; // NOI18N
/** Go to the next position stored in the jump-list */ publicstaticfinal String jumpListPrevAction = "jump-list-prev"; // NOI18N
/** Go to the last position in the previous component stored in the jump-list */ publicstaticfinal String jumpListNextComponentAction = "jump-list-next-component"; // NOI18N
/** Go to the next position in the previous component stored in the jump-list */ publicstaticfinal String jumpListPrevComponentAction = "jump-list-prev-component"; // NOI18N
/** Scroll window one line up */ publicstaticfinal String scrollUpAction = "scroll-up"; // NOI18N
/** Scroll window one line down */ publicstaticfinal String scrollDownAction = "scroll-down"; // NOI18N
/** Prefix of all macro-based actions */ publicstaticfinal String macroActionPrefix = "macro-"; // NOI18N
/** Start recording of macro. Only one macro recording can be active at the time */ publicstaticfinal String startMacroRecordingAction = "start-macro-recording"; //NOI18N
/** Stop the active recording */ publicstaticfinal String stopMacroRecordingAction = "stop-macro-recording"; //NOI18N
/** Name of the action moving caret to the first column on the line */ publicstaticfinal String lineFirstColumnAction = "caret-line-first-column"; // NOI18N
/** Insert the current Date and Time */ publicstaticfinal String insertDateTimeAction = "insert-date-time"; // NOI18N
/** Name of the action moving caret to the first * column on the line and extending the selection
*/ publicstaticfinal String selectionLineFirstColumnAction = "selection-line-first-column"; // NOI18N
/** Name of the action for generating of Glyph Gutter popup menu*/ publicstaticfinal String generateGutterPopupAction = "generate-gutter-popup"; // NOI18N
/** Toggle visibility of line numbers*/ publicstaticfinal String toggleLineNumbersAction = "toggle-line-numbers"; // NOI18N
/** * Navigational boundaries for "home" and "end" actions. If defined on the target component, * home/end will move the caret first to the boundary, and only after that proceeds as usual (to the start/end of line). * The property must contain {@link PositionRegion} instance
*/ privatestaticfinal String PROP_NAVIGATE_BOUNDARIES = "NetBeansEditor.navigateBoundaries"; // NOI18N
/** * Gets an editor kit from its implemetation class. * * <p>Please be careful when using this method and make sure that you understand * how it works and what the deference is from using <code>MimeLookup</code>. * This method simply creates an instance of <code>BaseKit</code> from * its implementation class passed in as a parameter. It completely ignores * the registry of editor kits in <code>MimeLookup</code>, which has severe * consequences. * * <div class="nonnormative"> * <p>The usuall pattern for using editor kits is to start with a mime type * of a document (ie. file) that you want to edit, then use some registry * for editor kits to look up the kit for your mime type and finally set the * kit in a <code>JTextComponent</code>, let it create a <code>Document</code> * and load it with data. The registry can generally be anything, but in Netbeans * we use <code>MimeLookup</code> (JDK for example uses * <code>JEditorPane.createEditorKitForContentType</code>). * * <p>The editor kits are registered in <code>MimeLookup</code> for each * particular mime type and the registry itself does not impose any rules on * the editor kit implementations other than extending the <code>EditorKit</code> * class. This for example means that the same implemantation of <code>EditorKit</code> * can be used for multiple mime types. This is exactly how XML editor kit * is reused for various flavors of XML documents (e.g. ant build scripts, * web app descriptors, etc). * * <p>Netbeans did not always have <code>MimeLookup</code> * and it also used a different approach for registering and retrieving * editor kits. This old approach was based on implemetation classes rather than on mime * types and while it is still more or less functional for the old kit * implementations, it is fundamentally broken and should not be used any more. * The code below demonstrates probably the biggest mistake when thinking * in the old ways. * * <pre> * // WARNING: The code below is a demonstration of a common mistake that * // people do when using <code>BaseKit.getKit</code>. * * JTextComponent component = ...; // Let's say we have a component * Class kitClass = Utilities.getKitClass(component); * String mimeType = BaseKit.getKit(kitClass).getContentType(); * </pre> * * <p>The problem with the above code is that it blindely assumes that each * kit class can be uniquely mapped to a mime type. This is not true! The * same can be achieved in much easier way, which always works. * * <pre> * JTextComponent component = ...; // Let's say we have a component * String mimeType = component.getUI().getEditorKit(component).getContentType(); * </pre> * </div> * * @param kitClass An implementation class of the editor kit that should * be returned. If the <code>kitClass</code> is not <code>BaseKit</code> or * its subclass the instance of bare <code>BaseKit</code> will be returned. * * @return An instance of the <code>kitClass</code> or <code>BaseKit</code>. * @deprecated Use <code>CloneableEditorSupport.getEditorKit</code> or * <code>MimeLookup</code> instead to find <code>EditorKit</code> for a mime * type.
*/
@Deprecated publicstatic BaseKit getKit(Class kitClass) { if (kitClass != null && BaseKit.class.isAssignableFrom(kitClass) && BaseKit.class != kitClass) {
String mimeType = KitsTracker.getInstance().findMimeType(kitClass); if (mimeType != null) {
EditorKit kit = MimeLookup.getLookup(MimePath.parse(mimeType)).lookup(EditorKit.class); if (kit instanceof BaseKit) { return (BaseKit) kit;
}
}
} else {
kitClass = BaseKit.class;
}
/** * Creates a new instance of <code>BaseKit</code>. * * <div class="nonnormative"> * <p>You should not need to instantiate editor kits * directly under normal circumstances. There is a few ways how you can get * instance of <code>EditorKit</code> depending on what you already have * available: * * <ul> * <li><b>mime type</b> - Use <code>CloneableEditorSupport.getEditorKit(yourMimeType)</code> * to get the <code>EditorKit</code> registered for your mime type or use * the following code <code>MimeLookup.getLookup(MimePath.parse(yourMimeType)).lookup(EditorKit.class)</code> * and check for <code>null</code>. * <li><b>JTextComponent</b> - Simply call * <code>JTextComponent.getUI().getEditorKit(JTextComponent)</code> passing * in the same component. * </ul> * </div>
*/ public BaseKit() { // possibly register synchronized (kits) { if (kits.get(this.getClass()) == null) {
kits.put(this.getClass(), this); // register itself
}
} // Directly implementing searchable editor kit would require module dependency changes // of any modules using BaseKit reference so make a wrapper instead
org.netbeans.modules.editor.lib2.actions.EditorActionUtilities.registerSearchableKit(this,
searchableKit = new SearchableKit(this));
}
/** Clone this editor kit */ public @Override Object clone() { returnthis; // no need to create another instance
}
/** Fetches a factory that is suitable for producing * views of any models that are produced by this * kit. The default is to have the UI produce the * factory, so this method has no implementation. * * @return the view factory
*/ public @Override ViewFactory getViewFactory() { return org.netbeans.modules.editor.lib2.view.ViewFactoryImpl.INSTANCE;
}
/** Create caret to navigate through document */ public @Override Caret createCaret() { returnnew EditorCaret();
}
/** * Create new instance of syntax coloring scanner * @param doc document to operate on. It can be null in the cases the syntax * creation is not related to the particular document * * @deprecated Please use Lexer instead, for details see * <a href="@org-netbeans-modules-lexer@/index.html">Lexer</a>.
*/
@Deprecated public Syntax createSyntax(Document doc) { returnnew DefaultSyntax();
}
/** * Create the syntax used for formatting. * * @deprecated Please use Editor Indentation API instead, for details see * <a href="@org-netbeans-modules-editor-indent@/index.html">Editor Indentation</a>.
*/
@Deprecated public Syntax createFormatSyntax(Document doc) { return createSyntax(doc);
}
/** * Create syntax support * * @deprecated Please use Lexer instead, for details see * <a href="@org-netbeans-modules-lexer@/index.html">Lexer</a>.
*/
@Deprecated public SyntaxSupport createSyntaxSupport(BaseDocument doc) { returnnew SyntaxSupport(doc);
}
// XXX: formatting cleanup // /** // * Create the formatter appropriate for this kit // * @deprecated Please use Editor Indentation API instead, for details see // * <a href="@org-netbeans-modules-editor-indent@/index.html">Editor Indentation</a>. // */ // public Formatter createFormatter() { // return new Formatter(this.getClass()); // }
/** * Create extended UI for printing a document. * @deprecated this method is no longer being called by {@link EditorUI}. * {@link #createPrintEditorUI(BaseDocument, boolean, boolean)} is being * called instead.
*/
@Deprecated protected EditorUI createPrintEditorUI(BaseDocument doc) { returnnew EditorUI(doc);
}
/** * Create extended UI for printing a document. * * @param doc document for which the extended UI is being created. * @param usePrintColoringMap use printing coloring settings instead * of the regular ones. * @param lineNumberEnabled if set to false the line numbers will not be printed. * If set to true the visibility of line numbers depends on the settings * for the line number visibility.
*/ protected EditorUI createPrintEditorUI(BaseDocument doc, boolean usePrintColoringMap, boolean lineNumberEnabled) {
public MultiKeymap getKeymap() { synchronized (KEYMAPS_AND_ACTIONS_LOCK) {
MimePath mimePath = MimePath.parse(getContentType());
MultiKeymap km = (MultiKeymap)kitKeymaps.get(mimePath);
if (km == null) { // keymap not yet constructed // construct new keymap
km = new MultiKeymap("Keymap for " + mimePath.getPath()); // NOI18N
// retrieve key bindings for this kit and super kits
KeyBindingSettings kbs = MimeLookup.getLookup(mimePath).lookup(KeyBindingSettings.class);
List<org.netbeans.api.editor.settings.MultiKeyBinding> mkbList = kbs.getKeyBindings();
List<JTextComponent.KeyBinding> editorMkbList = new ArrayList<JTextComponent.KeyBinding>();
// go through all levels and collect key bindings
km.load(editorMkbList.toArray(new JTextComponent.KeyBinding[0]), getActionMap());
km.setDefaultAction(getActionMap().get(defaultKeyTypedAction));
kitKeymaps.put(mimePath, km);
}
return km;
}
}
/** Inserts content from the given stream. */ public @Override void read(Reader in, Document doc, int pos) throws IOException, BadLocationException { if (doc instanceof BaseDocument) {
((BaseDocument)doc).read(in, pos); // delegate it to document
} else { super.read(in, doc, pos);
}
}
/** Writes content from a document to the given stream */ public @Override void write(Writer out, Document doc, int pos, int len) throws IOException, BadLocationException { if (doc instanceof BaseDocument) {
((BaseDocument)doc).write(out, pos, len);
} else { super.write(out, doc, pos, len);
}
}
/** Creates map with [name, action] pairs from the given * array of actions.
*/ publicstaticvoid addActionsToMap(Map<String, Action> map, Action[] actions, String logActionsType) { boolean fineLoggable = LOG.isLoggable(Level.FINE); if (fineLoggable) {
LOG.fine(logActionsType + " start --------------------\n");
} for (int i = 0; i < actions.length; i++) {
Action a = actions[i]; if (a == null) {
LOG.info("actions[] contains null at index " + i +
((i > 0) ? ". Preceding action is " + actions[i - 1] : ".")); continue;
}
String name = (String) a.getValue(Action.NAME); if (name == null) {
LOG.info("Null Action.NAME property of action " + a); continue;
}
if (fineLoggable) {
String overriding = map.containsKey(name) ? " OVERRIDING\n" : "\n"; // NOI18N
LOG.fine(" " + name + ": " + a + overriding); // NOI18N
}
map.put(name, a); // NOI18N
} if (fineLoggable) {
LOG.fine(logActionsType + " end ----------------------\n");
}
}
/** Converts map with [name, action] back * to array of actions.
*/ publicstatic Action[] mapToActions(Map map) {
Action[] actions = new Action[map.size()]; int i = 0; for (Iterator iter = map.values().iterator() ; iter.hasNext() ;) {
actions[i++] = (Action)iter.next();
} return actions;
}
/** Called after the kit is installed into JEditorPane */ public @Override void install(JEditorPane c) {
assert (SwingUtilities.isEventDispatchThread()) // expected in AWT only
: "BaseKit.install() incorrectly called from non-AWT thread."; // NOI18N
// Mark that the editor's multi keymap adheres to context API in status displayer
c.putClientProperty("context-api-aware", Boolean.TRUE); // NOI18N
// Add default help IDs derived from the kit's mime type, #61618. // If the kit itself is HelpCtx.Provider it will be called from CloneableEditor.getHelpCtx() if (!(thisinstanceof HelpCtx.Provider)) {
HelpCtx.setHelpIDString(c, getContentType().replace('/', '.').replace('+', '.')); //NOI18N
}
// setup the keymap tracker and initialize the keymap
MultiKeymap keymap; synchronized (KEYMAPS_AND_ACTIONS_LOCK) {
MimePath mimePath = MimePath.get(getContentType());
KeybindingsAndPreferencesTracker tracker = keymapTrackers.get(mimePath); if (tracker == null) {
tracker = new KeybindingsAndPreferencesTracker(mimePath.getPath());
keymapTrackers.put(mimePath, tracker);
}
tracker.addComponent(c);
keymap = getKeymap();
}
List<Action> actionsList = translateActionNameList(actionNamesList); // translate names to actions for (Action a : actionsList) {
a.actionPerformed(new ActionEvent(c, ActionEvent.ACTION_PERFORMED, "")); // NOI18N
}
}
public @Override void deinstall(JEditorPane c) { assert (SwingUtilities.isEventDispatchThread()) // expected in AWT only
: "BaseKit.deinstall() incorrectly called from non-AWT thread."; // NOI18N
executeDeinstallActions(c);
// reset the keymap and remove the component from the tracker
c.setKeymap(null); synchronized (KEYMAPS_AND_ACTIONS_LOCK) {
MimePath mimePath = MimePath.get(getContentType());
KeybindingsAndPreferencesTracker tracker = keymapTrackers.get(mimePath); if (tracker != null) {
tracker.removeComponent(c);
}
}
BaseTextUI.uninstallUIWatcher(c);
// #41209: reset ancestor override flag if previously set if (c.getClientProperty("ancestorOverride") != null) { // NOI18N
c.putClientProperty("ancestorOverride", Boolean.FALSE); // NOI18N
}
}
List<Action> actionsList = translateActionNameList(actionNamesList); // translate names to actions for (Action a : actionsList) {
a.actionPerformed(new ActionEvent(c, ActionEvent.ACTION_PERFORMED, "")); // NOI18N
}
}
/** Initialize document by adding the draw-layers for example. */ protectedvoid initDocument(BaseDocument doc) {
}
/** Create actions that this kit supports. To use the actions of the parent kit * it's better instead of using super.createActions() to use * getKit(super.getClass()).getActions() because it can reuse existing * parent actions.
*/ protected Action[] createActions() { returnnew Action[] { // new DefaultKeyTypedAction() - overriden in ExtKit
insertBreakActionDef,
insertTabActionDef,
deletePrevCharActionDef,
deleteNextCharActionDef,
cutActionDef,
copyActionDef,
pasteActionDef, new PasteAction(true),
removeTabActionDef, //new ActionFactory.RemoveWordAction(), #47709
removeSelectionActionDef,
undoActionDef,
redoActionDef, //new ActionFactory.ToggleLineNumbersAction(), new ActionFactory.ToggleRectangularSelectionAction(),
// Self test actions // new EditorDebug.SelfTestAction(), // new EditorDebug.DumpPlanesAction(), // new EditorDebug.DumpSyntaxMarksAction()
};
}
/** * @deprecated Without any replacement.
*/
@Deprecated protected Action[] getMacroActions() { returnnew Action[0];
}
/** Get actions associated with this kit. createActions() is called * to get basic list and then customActions are added.
*/ public @Override final Action[] getActions() {
Action[] actions = (Action []) addActionsToMap()[0];
if (!keyBindingsUpdaterInited) {
keyBindingsUpdaterInited = true;
KeyBindingsUpdater.get(getContentType()).addKit(this); // Update key bindings in actions
}
if (actions == null || actionMap == null) { // Initialize actions - use the following actions: // 1. Declared "global" actions (declared in the xml layer under "Editors/Actions") // 2. Declared "mime-type" actions (declared in the xml layer under "Editors/mime-type/Actions") // 3. Result of createActions() // 4. Custom actions (EditorPreferencesKeys.CUSTOM_ACTION_LIST) // Higher levels override actions with same Action.NAME
actions = getDeclaredActions(); // non-null
actionMap = new HashMap<String, Action>(actions.length << 1);
addActionsToMap(actionMap, actions, "Declared actions"); // NOI18N
Action[] createActionsMethodResult = createActions(); if (createActionsMethodResult != null) {
addActionsToMap(actionMap, createActionsMethodResult, "Actions from createActions()"); // NOI18N
}
// At this moment the actions are constructed completely // The actions will be updated now if necessary
updateActions();
}
returnnew Object [] { actions, actionMap };
}
}
/** * Get actions declared in the xml layer. They may be overriden by result * of <code>createActions()</code> and finally by result of <code>getCustomActions()</code>. * * @return non-null list of declared actions.
*/ protected Action[] getDeclaredActions() { returnnew Action[0];
}
/** Update the actions right after their creation was finished. * The <code>getActions()</code> and <code>getActionByName()</code> * can be used safely in this method. * The implementation must call <code>super.updateActions()</code> so that * the updating in parent is performed too.
*/ protectedvoid updateActions() {
}
/** Get action from its name. */ public Action getActionByName(String name) { return (name != null) ? (Action)getActionMap().get(name) : null;
}
public List<Action> translateActionNameList(List<String> actionNameList) {
List<Action> ret = new ArrayList<Action>(); if (actionNameList != null) { for(String actionName : actionNameList) {
Action a = getActionByName(actionName); if (a != null) {
ret.add(a);
}
}
} return ret;
}
/** * Checks that the action will result in an insertion into document. * Returns true for readonly docs as well. * * @param evt action event * @return true, if the action event will result in insertion; readonly doc status is not * checked.
*/ staticboolean isValidDefaultTypedAction(ActionEvent evt) { // Check whether the modifiers are OK int mod = evt.getModifiers(); boolean ctrl = ((mod & ActionEvent.CTRL_MASK) != 0); boolean alt = org.openide.util.Utilities.isMac() ? ((mod & ActionEvent.META_MASK) != 0) :
((mod & ActionEvent.ALT_MASK) != 0); return !(alt || ctrl);
}
public DefaultKeyTypedAction() { // Construct with defaultKeyTypedAction name to retain full compatibility for extending actions super(defaultKeyTypedAction, CLEAR_STATUS_TEXT);
putValue(BaseAction.NO_KEYBINDING, Boolean.TRUE);
LOG.fine("DefaultKeyTypedAction with enhanced logging, see issue #145306"); //NOI18N
}
publicvoid actionPerformed (final ActionEvent evt, final JTextComponent target) { if ((target != null) && (evt != null)) {
if (!isValidDefaultTypedAction(evt)) { return;
}
// Check whether the target is enabled and editable if (!target.isEditable() || !target.isEnabled()) {
target.getToolkit().beep(); return;
}
// reset magic caret position if (target.getCaret() != null) {
target.getCaret().setMagicCaretPosition(null);
}
// determine if typed char is valid final String cmd = evt.getActionCommand(); if (isValidDefaultTypedCommand(evt)) { if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "Processing command char: {0}", Integer.toHexString(cmd.charAt(0))); //NOI18N
}
if (((Boolean) result[0]).booleanValue()) {
transaction.afterInsertion();
// XXX: this is potentially wrong and we may need to call this with // the original cmd; or maybe only if insertionText == cmd; but maybe // it does not matter, because nobody seems to be overwriting this method anyway
checkIndent(target, (String) result[1]);
} // else text insertion failed
}
} finally {
transaction.close();
}
} catch (BadLocationException ble) {
LOG.log(Level.FINE, null, ble);
target.getToolkit().beep();
}
} else { if (LOG.isLoggable(Level.FINE)) {
StringBuilder sb = new StringBuilder(); for(int i = 0; i < cmd.length(); i++) {
String hex = Integer.toHexString(cmd.charAt(i));
sb.append(hex); if (i + 1 < cmd.length()) {
sb.append(" ");
}
}
LOG.log(Level.FINE, "Invalid command: {0}", sb); //NOI18N
}
}
}
}
/** * Hook to insert the given string at the given position into * the given document in insert-mode, no selection, writeable * document. Designed to be overridden by subclasses that want * to intercept inserted characters. * * @deprecated Please use Typing Hooks instead, for details see * <a href="@org-netbeans-modules-editor-lib2@/index.html">Editor Library 2</a>.
*/ protectedvoid insertString(BaseDocument doc, int dotPos,
Caret caret,
String str, boolean overwrite) throws BadLocationException
{ if (overwrite) {
doc.remove(dotPos, 1);
}
doc.insertString(dotPos, str, null);
}
/** * Hook to insert the given string at the given position into * the given document in insert-mode with selection visible * Designed to be overridden by subclasses that want * to intercept inserted characters. * * @deprecated Please use Typing Hooks instead, for details see * <a href="@org-netbeans-modules-editor-lib2@/index.html">Editor Library 2</a>.
*/ protectedvoid replaceSelection(
JTextComponent target, int dotPos,
Caret caret,
String str, boolean overwrite) throws BadLocationException
{
target.replaceSelection(str);
}
/** * Check whether there was any important character typed * so that the line should be possibly reformatted. * * @deprecated Please use <a href="@org-netbeans-modules-editor-indent-support@/org/netbeans/modules/editor/indent/spi/support/AutomatedIndenting.html">AutomatedIndentig</a> * or Typing Hooks instead, for details see * <a href="@org-netbeans-modules-editor-lib2@/index.html">Editor Library 2</a>.
*/ protectedvoid checkIndent(JTextComponent target, String typedText) {
}
privatevoid performTextInsertion(JTextComponent target, int insertionOffset, String insertionText, int caretPosition, boolean formatNewLines) throws BadLocationException { final BaseDocument doc = (BaseDocument)target.getDocument();
try {
NavigationHistory.getEdits().markWaypoint(target, insertionOffset, false, true);
} catch (BadLocationException e) {
LOG.log(Level.WARNING, "Can't add position to the history of edits.", e); //NOI18N
}
privateint computeInsertionOffset(Caret caret) { if (Utilities.isSelectionShowing(caret)) { return Math.min(caret.getMark(), caret.getDot());
} else { return caret.getDot();
}
}
} // End of DefaultKeyTypedAction class
/** * @deprecated Please do not subclass this class. Use Typing Hooks instead, for details see * <a href="@org-netbeans-modules-editor-lib2@/index.html">Editor Library 2</a>.
*/
@Deprecated publicstaticclass InsertBreakAction extends LocalBaseAction {
/** * Hook called before any changes to the document. The value * returned is passed intact to the other hook. * * @deprecated Please use Typing Hooks instead, for details see * <a href="@org-netbeans-modules-editor-lib2@/index.html">Editor Library 2</a>.
*/ protected Object beforeBreak(JTextComponent target, BaseDocument doc, Caret caret) { returnnull;
}
/** * Hook called after the enter was inserted and cursor * repositioned. * * @param data the object returned from previously called * {@link #beforeBreak(javax.swing.text.JTextComponent, org.netbeans.editor.BaseDocument, javax.swing.text.Caret)} hook. * By default <code>null</code>. * * @deprecated Please use Typing Hooks instead, for details see * <a href="@org-netbeans-modules-editor-lib2@/index.html">Editor Library 2</a>.
*/ protectedvoid afterBreak(JTextComponent target, BaseDocument doc, Caret caret, Object data) {
}
public InsertTabAction() { super(insertTabAction, MAGIC_POSITION_RESET | ABBREV_RESET | WORD_MATCH_RESET);
}
publicvoid actionPerformed(final ActionEvent evt, final JTextComponent target) { if (target != null) { if (!target.isEditable() || !target.isEnabled()) {
target.getToolkit().beep(); return;
}
final Caret caret = target.getCaret(); final BaseDocument doc = (BaseDocument)target.getDocument(); final Indent indenter = Indent.get(doc);
indenter.lock(); try {
doc.runAtomicAsUser (new Runnable () { publicvoid run () {
DocumentUtilities.setTypingModification(doc, true); try { if(caret instanceof EditorCaret) {
EditorCaret editorCaret = (EditorCaret) caret;
editorCaret.moveCarets(new CaretMoveHandler() {
@Override publicvoid moveCarets(CaretMoveContext context) { for (CaretInfo caretInfo : context.getOriginalSortedCarets()) { if (caretInfo.isSelection()) { // block selected try { int start = Math.min(caretInfo.getDot(), caretInfo.getMark()); int end = Math.max(caretInfo.getDot(), caretInfo.getMark());
String replacedText = doc.getText(start, end - start); if (replacedText.trim().isEmpty()) {
doc.remove(start, end - start);
insertTabString(doc, start);
} else { boolean selectionAtLineStart = Utilities.getRowStart(doc, start) == start;
changeBlockIndent(doc, start, end, +1); if (selectionAtLineStart) { int newSelectionStartOffset = start; int lineStartOffset = Utilities.getRowStart(doc, start); if (lineStartOffset != newSelectionStartOffset) {
context.setDotAndMark(caretInfo,
doc.createPosition(lineStartOffset), Position.Bias.Forward,
doc.createPosition(end), Position.Bias.Forward);
}
}
}
} catch (GuardedException ge) {
LOG.log(Level.FINE, null, ge);
target.getToolkit().beep();
} catch (BadLocationException e) {
LOG.log(Level.WARNING, null, e);
}
} else { // no selected text int dotOffset = caretInfo.getDot(); try { // is there any char on this line before cursor? int indent = Utilities.getRowIndent(doc, dotOffset); // test whether we should indent if (indent == -1) { // find caret column int caretCol = Utilities.getVisualColumn(doc, dotOffset); // find next tab column int nextTabCol = Utilities.getNextTabColumn(doc, dotOffset);
indenter.reindent(dotOffset);
dotOffset = caretInfo.getDot(); int newCaretCol = Utilities.getVisualColumn(doc, dotOffset); if (newCaretCol <= caretCol) { // find indent of the first previous non-white row int upperCol = Utilities.getRowIndent(doc, dotOffset, false);
changeRowIndent(doc, dotOffset, upperCol > nextTabCol ? upperCol : nextTabCol); // Fix of #32240
dotOffset = caretInfo.getDot();
Position newDotPos = doc.createPosition(Utilities.getRowEnd(doc, dotOffset));
context.setDot(caretInfo, newDotPos, Position.Bias.Forward);
}
} else { // already chars on the line
insertTabString(doc, dotOffset);
}
} catch (GuardedException ge) {
LOG.log(Level.FINE, null, ge);
target.getToolkit().beep();
} catch (BadLocationException e) { // use the same pos
target.getToolkit().beep();
LOG.log(Level.FINE, null, e);
}
}
}
}
});
} else { if (Utilities.isSelectionShowing(caret)) { // block selected try { if (target.getSelectedText().trim().isEmpty()) {
doc.remove(target.getSelectionStart(), target.getSelectionEnd() - target.getSelectionStart());
insertTabString(doc, target.getSelectionStart());
} else { boolean selectionAtLineStart = Utilities.getRowStart(doc, target.getSelectionStart()) == target.getSelectionStart();
changeBlockIndent(doc, target.getSelectionStart(), target.getSelectionEnd(), +1); if (selectionAtLineStart) { int newSelectionStartOffset = target.getSelectionStart(); int lineStartOffset = Utilities.getRowStart(doc, newSelectionStartOffset); if (lineStartOffset != newSelectionStartOffset)
target.select(lineStartOffset, target.getSelectionEnd());
}
}
} catch (GuardedException ge) {
LOG.log(Level.FINE, null, ge);
target.getToolkit().beep();
} catch (BadLocationException e) {
LOG.log(Level.WARNING, null, e);
}
} else { // no selected text int dotPos = caret.getDot(); try { // is there any char on this line before cursor? int indent = Utilities.getRowIndent(doc, dotPos); // test whether we should indent if (indent == -1) { // find caret column int caretCol = Utilities.getVisualColumn(doc, dotPos); // find next tab column int nextTabCol = Utilities.getNextTabColumn(doc, dotPos);
indenter.reindent(dotPos);
dotPos = caret.getDot(); int newCaretCol = Utilities.getVisualColumn(doc, dotPos); if (newCaretCol <= caretCol) { // find indent of the first previous non-white row int upperCol = Utilities.getRowIndent(doc, dotPos, false);
changeRowIndent(doc, dotPos, upperCol > nextTabCol ? upperCol : nextTabCol); // Fix of #32240
dotPos = caret.getDot();
caret.setDot(Utilities.getRowEnd(doc, dotPos));
}
} else { // already chars on the line
insertTabString(doc, dotPos);
}
} catch (GuardedException ge) {
LOG.log(Level.FINE, null, ge);
target.getToolkit().beep();
} catch (BadLocationException e) { // use the same pos
target.getToolkit().beep();
LOG.log(Level.FINE, null, e);
}
}
}
} finally {
DocumentUtilities.setTypingModification(doc, false);
}
}
});
} finally {
indenter.unlock();
}
}
}
}
/** * Compound action that encapsulates several actions * @deprecated this action is no longer used.
*/
@Deprecated publicstaticclass CompoundAction extends LocalBaseAction {
public CompoundAction(String nm, Action actions[]) { this(nm, 0, actions);
}
public CompoundAction(String nm, int resetMask, Action actions[]) { super(nm, resetMask); this.actions = actions;
}
publicvoid actionPerformed(ActionEvent evt, JTextComponent target) { if (target != null) { for (int i = 0; i < actions.length; i++) {
Action a = actions[i]; if (a instanceof BaseAction) {
((BaseAction)a).actionPerformed(evt, target);
} else {
a.actionPerformed(evt);
}
}
}
}
}
/** * Compound action that gets and executes its actions * depending on the kit of the component. * The other advantage is that it doesn't create additional * instances of compound actions. * @deprecated this action is no longer used. It is reimplemented in editor.actions module.
*/
@Deprecated publicstaticclass KitCompoundAction extends LocalBaseAction {
public KitCompoundAction(String nm, String actionNames[]) { this(nm, 0, actionNames);
}
public KitCompoundAction(String nm, int resetMask, String actionNames[]) { super(nm, resetMask); this.actionNames = actionNames;
}
publicvoid actionPerformed(ActionEvent evt, JTextComponent target) { if (target != null) {
BaseKit kit = Utilities.getKit(target); if (kit != null) { for (int i = 0; i < actionNames.length; i++) {
Action a = kit.getActionByName(actionNames[i]); if (a != null) { if (a instanceof BaseAction) {
((BaseAction)a).actionPerformed(evt, target);
} else {
a.actionPerformed(evt);
}
}
}
}
}
}
}
/** * @deprecated this action is no longer used. It is reimplemented in editor.actions module.
*/ // @EditorActionRegistration(name = insertContentAction)
@Deprecated publicstaticclass InsertContentAction extends LocalBaseAction {
publicvoid actionPerformed(ActionEvent evt, JTextComponent target) { if (target != null) { if (!target.isEditable() || !target.isEnabled()) {
target.getToolkit().beep(); return;
}
target.replaceSelection(text);
}
}
}
/** * @deprecated Please do not subclass this class. Use Typing Hooks instead, for details see * <a href="@org-netbeans-modules-editor-lib2@/index.html">Editor Library 2</a>.
*/
@Deprecated publicstaticclass DeleteCharAction extends LocalBaseAction {
/** * This may be used to overcome a JDK bug on Mac OS X (NB issue #219853).
*/ privatestaticfinalboolean disableDeleteFromScreenMenu = Boolean.TRUE.equals( Boolean.getBoolean("netbeans.editor.disable.delete.from.screen.menu"));
publicvoid actionPerformed(final ActionEvent evt, final JTextComponent target) { if (target != null) { // ScreenMenuItem from screen menu on Mac OS X should extend java.awt.MenuItem if (disableDeleteFromScreenMenu &&
((evt.getSource() instanceof java.awt.MenuItem) ||
(evt.getSource() instanceof javax.swing.JMenuItem)))
{ return;
} if (!target.isEditable() || !target.isEnabled()) {
target.getToolkit().beep(); return;
}
if (LOG.isLoggable(Level.FINER)) {
StringBuilder sb = new StringBuilder("DeleteCharAction stackTrace: \n"); for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
sb.append(ste.toString()).append("\n");
}
LOG.log(Level.FINER, sb.toString());
}
/** * @deprecated Please use Typing Hooks instead, for details see * <a href="@org-netbeans-modules-editor-lib2@/index.html">Editor Library 2</a>.
*/ protectedvoid charBackspaced(BaseDocument doc, int dotPos, Caret caret, char ch) throws BadLocationException {
}
/** * @deprecated Please use Typing Hooks instead, for details see * <a href="@org-netbeans-modules-editor-lib2@/index.html">Editor Library 2</a>.
*/ protectedvoid charDeleted(BaseDocument doc, int dotPos, Caret caret, char ch) throws BadLocationException {
}
} // End of DeleteCharAction class
/** * @deprecated this action is no longer used. It is reimplemented in editor.actions module.
*/
@Deprecated // @EditorActionRegistration(name = readOnlyAction) publicstaticclass ReadOnlyAction extends LocalBaseAction {
/** * @deprecated this action is no longer used. It is reimplemented in editor.actions module.
*/
@Deprecated // @EditorActionRegistration(name = writableAction) publicstaticclass WritableAction extends LocalBaseAction {
publicvoid actionPerformed(final ActionEvent evt, final JTextComponent target) { if (target != null) { if (!target.isEditable() || !target.isEnabled()) {
target.getToolkit().beep(); return;
}
try {
NavigationHistory.getEdits().markWaypoint(target, target.getCaret().getDot(), false, true);
} catch (BadLocationException e) {
LOG.log(Level.WARNING, "Can't add position to the history of edits.", e); //NOI18N
}
final BaseDocument doc = (BaseDocument)target.getDocument();
doc.runAtomicAsUser (new Runnable () {
@Override publicvoid run () {
DocumentUtilities.setTypingModification(doc, true); try { // If there is no selection then pre-select a current line including newline // If on last line (without newline) then insert newline at very end temporarily // then cut and remove previous newline. int removeNewlineOffset = -1;
Caret caret = target.getCaret(); boolean disableNoSelectionCopy = Boolean.getBoolean("org.netbeans.editor.disable.no.selection.copy"); if(!disableNoSelectionCopy &&
(!(caret instanceof EditorCaret)) || !(((EditorCaret)caret).getCarets().size() > 1)) { if (!Utilities.isSelectionShowing(target)) {
Element elem = ((AbstractDocument) target.getDocument()).getParagraphElement(
target.getCaretPosition()); int lineStartOffset = elem.getStartOffset(); int lineEndOffset = elem.getEndOffset(); if (lineEndOffset == doc.getLength() + 1) { // Very end // Temporarily insert extra newline try {
doc.insertString(lineEndOffset - 1, "\n", null); if (lineStartOffset > 0) { // Only when not on first line
removeNewlineOffset = lineStartOffset - 1;
}
} catch (BadLocationException e) { // could not insert extra newline
}
}
target.select(lineStartOffset, lineEndOffset);
}
}
// Retrieve caret magic position and attempt to retain // the x-coordinate information and use it // for setting of the new caret position
Point magicCaretPosition = caretInfo.getMagicCaretPosition(); if (magicCaretPosition == null) {
magicCaretPosition = new Point(caretBounds.x, caretBounds.y);
}
Rectangle visibleBounds = target.getVisibleRect(); int newCaretOffset;
Rectangle newCaretBounds;
// Check whether caret was contained in the original visible window if (visibleBounds.contains(caretBounds)) { // Clone present view bounds
Rectangle newVisibleBounds = new Rectangle(visibleBounds); // Do viewToModel() and modelToView() with the left top corner // of the currently visible view. If that line is not fully visible // then it should be the bottom line of the previous page // (if it's fully visible then the line above it). int topLeftOffset = target.viewToModel(new Point(
visibleBounds.x, visibleBounds.y));
Rectangle topLeftLineBounds = target.modelToView(topLeftOffset);
// newVisibleBounds.y will hold bottom of new view if (topLeftLineBounds.y != visibleBounds.y) {
newVisibleBounds.y = topLeftLineBounds.y + topLeftLineBounds.height;
} // Component view starts right at the line boundary // Go back by the view height
newVisibleBounds.y -= visibleBounds.height;
// Find the new caret bounds by using relative y position // on the original caret bounds. If the caret's new relative bounds // would be visually above the old bounds // the view should be shifted so that the relative bounds // are the same (user's eyes do not need to move). int caretRelY = caretBounds.y - visibleBounds.y; int caretNewY = newVisibleBounds.y + caretRelY;
newCaretOffset = target.viewToModel(new Point(magicCaretPosition.x, caretNewY));
newCaretBounds = target.modelToView(newCaretOffset); if (newCaretBounds.y < caretNewY) { // Need to go one line down to retain the top line // of the present newVisibleBounds to be fully visible. // Attempt to go forward by height of caret
newCaretOffset = target.viewToModel(new Point(magicCaretPosition.x,
newCaretBounds.y + newCaretBounds.height));
newCaretBounds = target.modelToView(newCaretOffset);
}
// Shift the new visible bounds so that the caret // does not visually move
newVisibleBounds.y = newCaretBounds.y - caretRelY;
// Scroll the window to the requested rectangle
target.scrollRectToVisible(newVisibleBounds);
} else { // Caret outside of originally visible window // Shift the dot by the visible bounds height
Point newCaretPoint = new Point(magicCaretPosition.x,
caretBounds.y - visibleBounds.height);
newCaretOffset = target.viewToModel(newCaretPoint);
newCaretBounds = target.modelToView(newCaretOffset);
}
// Retrieve caret magic position and attempt to retain // the x-coordinate information and use it // for setting of the new caret position
Point magicCaretPosition = caret.getMagicCaretPosition(); if (magicCaretPosition == null) {
magicCaretPosition = new Point(caretBounds.x, caretBounds.y);
}
Rectangle visibleBounds = target.getVisibleRect(); int newCaretOffset;
Rectangle newCaretBounds;
// Check whether caret was contained in the original visible window if (visibleBounds.contains(caretBounds)) { // Clone present view bounds
Rectangle newVisibleBounds = new Rectangle(visibleBounds); // Do viewToModel() and modelToView() with the left top corner // of the currently visible view. If that line is not fully visible // then it should be the bottom line of the previous page // (if it's fully visible then the line above it). int topLeftOffset = target.viewToModel(new Point(
visibleBounds.x, visibleBounds.y));
Rectangle topLeftLineBounds = target.modelToView(topLeftOffset);
// newVisibleBounds.y will hold bottom of new view if (topLeftLineBounds.y != visibleBounds.y) {
newVisibleBounds.y = topLeftLineBounds.y + topLeftLineBounds.height;
} // Component view starts right at the line boundary // Go back by the view height
newVisibleBounds.y -= visibleBounds.height;
// Find the new caret bounds by using relative y position // on the original caret bounds. If the caret's new relative bounds // would be visually above the old bounds // the view should be shifted so that the relative bounds // are the same (user's eyes do not need to move). int caretRelY = caretBounds.y - visibleBounds.y; int caretNewY = newVisibleBounds.y + caretRelY;
newCaretOffset = target.viewToModel(new Point(magicCaretPosition.x, caretNewY));
newCaretBounds = target.modelToView(newCaretOffset); if (newCaretBounds.y < caretNewY) { // Need to go one line down to retain the top line // of the present newVisibleBounds to be fully visible. // Attempt to go forward by height of caret
newCaretOffset = target.viewToModel(new Point(magicCaretPosition.x,
newCaretBounds.y + newCaretBounds.height));
newCaretBounds = target.modelToView(newCaretOffset);
}
// Shift the new visible bounds so that the caret // does not visually move
newVisibleBounds.y = newCaretBounds.y - caretRelY;
// Scroll the window to the requested rectangle
target.scrollRectToVisible(newVisibleBounds);
} else { // Caret outside of originally visible window // Shift the dot by the visible bounds height
Point newCaretPoint = new Point(magicCaretPosition.x,
caretBounds.y - visibleBounds.height);
newCaretOffset = target.viewToModel(newCaretPoint);
newCaretBounds = target.modelToView(newCaretOffset);
}
// Retrieve caret magic position and attempt to retain // the x-coordinate information and use it // for setting of the new caret position
Point magicCaretPosition = caretInfo.getMagicCaretPosition(); if (magicCaretPosition == null) {
magicCaretPosition = new Point(caretBounds.x, caretBounds.y);
}
Rectangle visibleBounds = target.getVisibleRect(); int newCaretOffset;
Rectangle newCaretBounds;
// Check whether caret was contained in the original visible window if (visibleBounds.contains(caretBounds)) { // Clone present view bounds
Rectangle newVisibleBounds = new Rectangle(visibleBounds); // Do viewToModel() and modelToView() with the left bottom corner // of the currently visible view. // That line should be the top line of the next page. int bottomLeftOffset = target.viewToModel(new Point(
visibleBounds.x, visibleBounds.y + visibleBounds.height));
Rectangle bottomLeftLineBounds = target.modelToView(bottomLeftOffset);
// newVisibleBounds.y will hold bottom of new view
newVisibleBounds.y = bottomLeftLineBounds.y;
// Find the new caret bounds by using relative y position // on the original caret bounds. If the caret's new relative bounds // would be visually below the old bounds // the view should be shifted so that the relative bounds // are the same (user's eyes do not need to move). int caretRelY = caretBounds.y - visibleBounds.y; int caretNewY = newVisibleBounds.y + caretRelY;
newCaretOffset = target.viewToModel(new Point(magicCaretPosition.x, caretNewY));
newCaretBounds = target.modelToView(newCaretOffset); if (newCaretBounds.y > caretNewY) { // Need to go one line above to retain the top line // of the present newVisibleBounds to be fully visible. // Attempt to go up by height of caret.
newCaretOffset = target.viewToModel(new Point(magicCaretPosition.x,
newCaretBounds.y - newCaretBounds.height));
newCaretBounds = target.modelToView(newCaretOffset);
}
// Shift the new visible bounds so that the caret // does not visually move
newVisibleBounds.y = newCaretBounds.y - caretRelY;
// Scroll the window to the requested rectangle
target.scrollRectToVisible(newVisibleBounds);
} else { // Caret outside of originally visible window // Shift the dot by the visible bounds height
Point newCaretPoint = new Point(magicCaretPosition.x,
caretBounds.y + visibleBounds.height);
newCaretOffset = target.viewToModel(newCaretPoint);
newCaretBounds = target.modelToView(newCaretOffset);
}
// Retrieve caret magic position and attempt to retain // the x-coordinate information and use it // for setting of the new caret position
Point magicCaretPosition = caret.getMagicCaretPosition(); if (magicCaretPosition == null) {
magicCaretPosition = new Point(caretBounds.x, caretBounds.y);
}
Rectangle visibleBounds = target.getVisibleRect(); int newCaretOffset;
Rectangle newCaretBounds;
// Check whether caret was contained in the original visible window if (visibleBounds.contains(caretBounds)) { // Clone present view bounds
Rectangle newVisibleBounds = new Rectangle(visibleBounds); // Do viewToModel() and modelToView() with the left bottom corner // of the currently visible view. // That line should be the top line of the next page. int bottomLeftOffset = target.viewToModel(new Point(
visibleBounds.x, visibleBounds.y + visibleBounds.height));
Rectangle bottomLeftLineBounds = target.modelToView(bottomLeftOffset);
// newVisibleBounds.y will hold bottom of new view
newVisibleBounds.y = bottomLeftLineBounds.y;
// Find the new caret bounds by using relative y position // on the original caret bounds. If the caret's new relative bounds // would be visually below the old bounds // the view should be shifted so that the relative bounds // are the same (user's eyes do not need to move). int caretRelY = caretBounds.y - visibleBounds.y; int caretNewY = newVisibleBounds.y + caretRelY;
newCaretOffset = target.viewToModel(new Point(magicCaretPosition.x, caretNewY));
newCaretBounds = target.modelToView(newCaretOffset); if (newCaretBounds.y > caretNewY) { // Need to go one line above to retain the top line // of the present newVisibleBounds to be fully visible. // Attempt to go up by height of caret.
newCaretOffset = target.viewToModel(new Point(magicCaretPosition.x,
newCaretBounds.y - newCaretBounds.height));
newCaretBounds = target.modelToView(newCaretOffset);
}
// Shift the new visible bounds so that the caret // does not visually move
newVisibleBounds.y = newCaretBounds.y - caretRelY;
// Scroll the window to the requested rectangle
target.scrollRectToVisible(newVisibleBounds);
} else { // Caret outside of originally visible window // Shift the dot by the visible bounds height
Point newCaretPoint = new Point(magicCaretPosition.x,
caretBounds.y + visibleBounds.height);
newCaretOffset = target.viewToModel(newCaretPoint);
newCaretBounds = target.modelToView(newCaretOffset);
}
publicvoid actionPerformed(ActionEvent evt, final JTextComponent target) { if (target != null) {
Caret caret = target.getCaret(); final Document doc = target.getDocument(); if (doc != null && caret instanceof EditorCaret) { final EditorCaret editorCaret = (EditorCaret) caret;
doc.render(new Runnable() {
@Override publicvoid run() {
editorCaret.moveCarets(new CaretMoveHandler() {
@Override publicvoid moveCarets(CaretMoveContext context) { for (CaretInfo caretInfo : context.getOriginalSortedCarets()) { try { int dot = caretInfo.getDot(); // #232675: if bounds are defined, use them rather than line start/end
Object o = target.getClientProperty(PROP_NAVIGATE_BOUNDARIES);
PositionRegion bounds = null; if (o instanceof PositionRegion) {
bounds = (PositionRegion) o; int start = bounds.getStartOffset(); int end = bounds.getEndOffset(); if (dot > start && dot <= end) { // move to the region start
dot = start;
} else {
bounds = null;
}
}
if (bounds == null) { int lineStartPos = Utilities.getRowStart(target, dot); if (homeKeyColumnOne) { // to first column
dot = lineStartPos;
} else { // either to line start or text start
BaseDocument doc = (BaseDocument) target.getDocument(); int textStartPos = Utilities.getRowFirstNonWhite(doc, lineStartPos); if (textStartPos < 0) { // no text on the line
textStartPos = Utilities.getRowEnd(target, lineStartPos);
} elseif (textStartPos < lineStartPos) { /* NETBEANS-980: On a wrap line oher than the first; go only as
far as to the first character on the wrap line. */
textStartPos = lineStartPos;
} if (dot == lineStartPos) { // go to the text start pos
dot = textStartPos;
} elseif (dot <= textStartPos) {
dot = lineStartPos;
} else {
dot = textStartPos;
}
}
} // For partial view hierarchy check bounds
dot = Math.max(dot, target.getUI().getRootView(target).getStartOffset());
String actionName = (String) getValue(Action.NAME); boolean select = selectionBeginLineAction.equals(actionName)
|| selectionLineFirstColumnAction.equals(actionName);
// If possible scroll the view to its begining horizontally // to ease user's orientation in the code.
Rectangle r = target.modelToView(dot);
Rectangle visRect = target.getVisibleRect(); if (r.getMaxX() < visRect.getWidth()) {
r.x = 0;
target.scrollRectToVisible(r);
}
Position dotPos = doc.createPosition(dot); if (select) {
context.moveDot(caretInfo, dotPos, Position.Bias.Forward);
} else {
context.setDot(caretInfo, dotPos, Position.Bias.Forward);
}
} catch (BadLocationException e) {
target.getToolkit().beep();
}
}
}
}, new MoveCaretsOrigin(
MoveCaretsOrigin.DIRECT_NAVIGATION, SwingConstants.WEST)
);
}
});
} else { try { int dot = caret.getDot(); // #232675: if bounds are defined, use them rather than line start/end
Object o = target.getClientProperty(PROP_NAVIGATE_BOUNDARIES);
PositionRegion bounds = null; if (o instanceof PositionRegion) {
bounds = (PositionRegion)o; int start = bounds.getStartOffset(); int end = bounds.getEndOffset(); if (dot > start && dot <= end) { // move to the region start
dot = start;
} else {
bounds = null;
}
}
if (bounds == null) { int lineStartPos = Utilities.getRowStart(target, dot); if (homeKeyColumnOne) { // to first column
dot = lineStartPos;
} else { // either to line start or text start int textStartPos = Utilities.getRowFirstNonWhite(((BaseDocument)doc), lineStartPos); if (textStartPos < 0) { // no text on the line
textStartPos = Utilities.getRowEnd(target, lineStartPos);
} elseif (textStartPos < lineStartPos) { // Wrap line case (see above).
textStartPos = lineStartPos;
} if (dot == lineStartPos) { // go to the text start pos
dot = textStartPos;
} elseif (dot <= textStartPos) {
dot = lineStartPos;
} else {
dot = textStartPos;
}
}
} // For partial view hierarchy check bounds
dot = Math.max(dot, target.getUI().getRootView(target).getStartOffset());
String actionName = (String) getValue(Action.NAME); boolean select = selectionBeginLineAction.equals(actionName)
|| selectionLineFirstColumnAction.equals(actionName);
// If possible scroll the view to its begining horizontally // to ease user's orientation in the code.
Rectangle r = target.modelToView(dot);
Rectangle visRect = target.getVisibleRect(); if (r.getMaxX() < visRect.getWidth()) {
r.x = 0;
target.scrollRectToVisible(r);
}
publicvoid actionPerformed(ActionEvent evt, final JTextComponent target) { if (target != null) { final Caret caret = target.getCaret(); if(caret instanceof EditorCaret) {
Document doc = target.getDocument();
doc.render(new Runnable() {
@Override publicvoid run() {
((EditorCaret) caret).moveCarets(new CaretMoveHandler() {
@Override publicvoid moveCarets(CaretMoveContext context) { for (CaretInfo caretInfo : context.getOriginalSortedCarets()) { try { // #232675: if bounds are defined, use them rather than line start/end
Object o = target.getClientProperty(PROP_NAVIGATE_BOUNDARIES); int dot = -1;
if (o instanceof PositionRegion) {
PositionRegion bounds = (PositionRegion) o; int start = bounds.getStartOffset(); int end = bounds.getEndOffset(); int d = caretInfo.getDot(); if (d >= start && d < end) { // move to the region start
dot = end;
}
}
if (dot == -1) {
dot = Utilities.getRowEnd(target, caretInfo.getDot());
}
// For partial view hierarchy check bounds
dot = Math.min(dot, target.getUI().getRootView(target).getEndOffset()); boolean select = selectionEndLineAction.equals(getValue(Action.NAME));
Position dotPos = context.getDocument().createPosition(dot); if (select) {
context.moveDot(caretInfo, dotPos, Position.Bias.Forward);
} else {
context.setDot(caretInfo, dotPos, Position.Bias.Forward);
} // now move the magic caret position far to the right
Rectangle r = target.modelToView(dot); if (r != null) {
Point p = new Point(MAGIC_POSITION_MAX, r.y);
context.setMagicCaretPosition(caretInfo, p);
}
} catch (BadLocationException e) {
e.printStackTrace();
target.getToolkit().beep();
}
}
}
}, new MoveCaretsOrigin(
MoveCaretsOrigin.DIRECT_NAVIGATION, SwingConstants.EAST)
);
}
});
} else { try { // #232675: if bounds are defined, use them rather than line start/end
Object o = target.getClientProperty(PROP_NAVIGATE_BOUNDARIES); int dot = -1;
if (o instanceof PositionRegion) {
PositionRegion bounds = (PositionRegion)o; int start = bounds.getStartOffset(); int end = bounds.getEndOffset(); int d = caret.getDot(); if (d >= start && d < end) { // move to the region start
dot = end;
}
}
if (dot == -1) {
dot = Utilities.getRowEnd(target, caret.getDot());
}
// For partial view hierarchy check bounds
dot = Math.min(dot, target.getUI().getRootView(target).getEndOffset()); boolean select = selectionEndLineAction.equals(getValue(Action.NAME)); if (select) {
caret.moveDot(dot);
} else {
caret.setDot(dot);
} // now move the magic caret position far to the right
Rectangle r = target.modelToView(dot); if (r!=null){
Point p = new Point(MAGIC_POSITION_MAX, r.y);
caret.setMagicCaretPosition(p);
}
} catch (BadLocationException e) {
e.printStackTrace();
target.getToolkit().beep();
}
}
}
}
}
publicvoid actionPerformed(ActionEvent evt, final JTextComponent target) { if (target != null) { final BaseDocument doc = (BaseDocument)target.getDocument();
doc.runAtomic(new Runnable() { publicvoid run() { try {
Element lineRootElem = doc.getDefaultRootElement(); int count = lineRootElem.getElementCount(); boolean removed = false; for (int x = 0; x < count; x++) {
Element elem = lineRootElem.getElement(x); int start = elem.getStartOffset(); int end = elem.getEndOffset();
CharSequence line = DocumentUtilities.getText(doc, start, end - start); int endIndex = line.length() - 1; if (endIndex >= 0 && line.charAt(endIndex) == '\n') {
endIndex--; if (endIndex >= 0 && line.charAt(endIndex) == '\r') {
endIndex--;
}
} int index = endIndex; while (index >= 0 && Character.isWhitespace(line.charAt(index)) && line.charAt(index) != '\n') {
index--;
} if (index < endIndex) {
doc.remove(start + index + 1, endIndex - index);
removed = true;
}
} // for if (removed) {
StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(BaseKit.class, "TrailingSpacesWereRemoved_Lbl")); // NOI18N
} else {
StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(BaseKit.class, "TrailingSpacesWereNotRemoved_Lbl")); // NOI18N
}
} catch (BadLocationException e) {
e.printStackTrace();
target.getToolkit().beep();
}
}
});
}
}
}
/* package */ static final class DefaultSyntax extends Syntax {
privatestaticfinalint ISI_TEXT = 0;
public DefaultSyntax() {
tokenContextPath = DefaultSyntaxTokenContext.CONTEXT.getContextPath();
}
protected TokenID parseToken() { // The main loop that reads characters one by one follows while (offset < stopOffset) { char ch = buffer[offset]; // get the current character
switch (state) { // switch by the current internal state case INIT: switch (ch) { case'\n':
offset++; return DefaultSyntaxTokenContext.EOL; default:
state = ISI_TEXT; break;
} break;
case ISI_TEXT: switch (ch) { case'\n':
state = INIT; return DefaultSyntaxTokenContext.TEXT;
} break;
} // end of switch(state)
offset++; // move to the next char
}
switch (state) { case ISI_TEXT:
state = INIT; return DefaultSyntaxTokenContext.TEXT;
}
// need to continue on another buffer returnnull;
}
} // End of DefaultSyntax class
/* package */ static final class DefaultSyntaxTokenContext extends TokenContext {
void fireActionsChange() {
ChangeEvent evt = new ChangeEvent(this); for (ChangeListener listener : actionsListenerList.getListeners()) {
listener.stateChanged(evt);
}
}
}
/** Modify the line to move the text starting at dotPos one tab * column to the right. Whitespace preceeding dotPos may be * replaced by a TAB character if tabs expanding is on. * @param doc document to operate on * @param dotPos insertion point
*/ staticvoid insertTabString (final BaseDocument doc, finalint dotPos) throws BadLocationException { final BadLocationException[] badLocationExceptions = new BadLocationException [1];
doc.runAtomic (new Runnable () { publicvoid run () { try { // Determine first white char before dotPos int rsPos = Utilities.getRowStart(doc, dotPos); int startPos = Utilities.getFirstNonWhiteBwd(doc, dotPos, rsPos);
startPos = (startPos >= 0) ? (startPos + 1) : rsPos;
// Search for the first non-common char char[] removeChars = doc.getChars(startPos, dotPos - startPos); int ind = 0; while (ind < removeChars.length && removeChars[ind] == tabStr.charAt(ind)) {
ind++;
}
/** Increase/decrease indentation of the block of the code. Document * is atomically locked during the operation. * <br> * If indent is in between multiplies of shiftwidth it jumps to multiplies of shiftwidth. * * @param doc document to operate on * @param startPos starting line position * @param endPos ending line position * @param shiftCnt positive/negative count of shiftwidths by which indentation * should be shifted right/left
*/ staticvoid changeBlockIndent (final BaseDocument doc, finalint startPos, finalint endPos, finalint shiftCnt) throws BadLocationException {
GuardedDocument gdoc = (doc instanceof GuardedDocument)
? (GuardedDocument)doc : null; if (gdoc != null){ for (int i = startPos; i<endPos; i++){ if (gdoc.isPosGuarded(i)){
java.awt.Toolkit.getDefaultToolkit().beep(); return;
}
}
}
final BadLocationException[] badLocationExceptions = new BadLocationException [1];
doc.runAtomic (new Runnable () { publicvoid run () { try { int shiftWidth = doc.getShiftWidth(); if (shiftWidth <= 0) { return;
} int indentDelta = shiftCnt * shiftWidth; int end = (endPos > 0 && Utilities.getRowStart(doc, endPos) == endPos) ?
endPos - 1 : endPos;
int lineStartOffset = Utilities.getRowStart(doc, startPos ); int lineCount = Utilities.getRowCount(doc, startPos, end);
Integer delta = null; for (int i = lineCount - 1; i >= 0; i--) { int indent = Utilities.getRowIndent(doc, lineStartOffset); if (indent >= 0 && delta == null) {
delta = ((indent + indentDelta + (shiftCnt < 0 ? shiftWidth - 1 : 0))
/ shiftWidth * shiftWidth) - indent;
} int newIndent = (indent < 0) ? 0 : // Zero indent if row is white
indent + delta;
changeRowIndent(doc, lineStartOffset, Math.max(newIndent, 0));
lineStartOffset = Utilities.getRowStart(doc, lineStartOffset, +1);
}
} catch (BadLocationException ex) {
badLocationExceptions [0] = ex;
}
}
}); if (badLocationExceptions[0] != null) throw badLocationExceptions [0];
}
/** Shift line either left or right */ staticvoid shiftLine(BaseDocument doc, int dotPos, boolean right) throws BadLocationException { int ind = doc.getShiftWidth(); if (!right) {
ind = -ind;
}
if (Utilities.isRowWhite(doc, dotPos)) {
ind += Utilities.getVisualColumn(doc, dotPos);
} else {
ind += Utilities.getRowIndent(doc, dotPos);
}
ind = Math.max(ind, 0);
changeRowIndent(doc, dotPos, ind);
}
/** Shift block either left or right */ staticvoid shiftBlock (final BaseDocument doc, finalint startPos, finalint endPos, finalboolean right) throws BadLocationException {
GuardedDocument gdoc = (doc instanceof GuardedDocument)
? (GuardedDocument)doc : null; if (gdoc != null){ for (int i = startPos; i<endPos; i++){ if (gdoc.isPosGuarded(i)){
java.awt.Toolkit.getDefaultToolkit().beep(); return;
}
}
}
final BadLocationException[] badLocationExceptions = new BadLocationException [1];
doc.runAtomic (new Runnable () { publicvoid run () { try { int shiftWidth = doc.getShiftWidth(); if (shiftWidth <= 0) { return;
} int indentDelta = right ? shiftWidth : -shiftWidth; int end = (endPos > 0 && Utilities.getRowStart(doc, endPos) == endPos) ?
endPos - 1 : endPos;
int lineStartOffset = Utilities.getRowStart(doc, startPos ); int lineCount = Utilities.getRowCount(doc, startPos, end); for (int i = lineCount - 1; i >= 0; i--) { int indent = Utilities.getRowIndent(doc, lineStartOffset); int newIndent = (indent == -1) ? 0 : // Zero indent if row is white
indent + indentDelta;
/** * Set null TextUI to an editor pane upon setting a null editor kit.
*/ privatestaticfinalclass ClearUIForNullKitListener implements PropertyChangeListener {
static ClearUIForNullKitListener INSTANCE = new ClearUIForNullKitListener();
@Override publicvoid propertyChange(PropertyChangeEvent evt) { if ("editorKit".equals(evt.getPropertyName())) { if (evt.getNewValue() == null && (evt.getSource() instanceof JEditorPane)) {
JEditorPane pane = (JEditorPane)evt.getSource(); // It's necessary to detach BasicTextUI's handler from property listening on document // to allow GC of the pane and related objects. // BTW cannot verify that null kit is present since pane.getEditorKit() lazily creates the kit // Unfortunately JTextComponent.getUI() cannot return null since // JTextComponent's code assumes it to be non-null (while e.g. JComponent properly checks it for null). // Thus use an empty impl of a TextUI.
pane.setUI(new NullTextUI());
pane.removePropertyChangeListener(this);
}
}
}
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 und die Messung sind noch experimentell.