diff --git a/editor.actions/build.xml b/editor.actions/build.xml new file mode 100644 --- /dev/null +++ b/editor.actions/build.xml @@ -0,0 +1,47 @@ + + + + + + + diff --git a/editor.actions/manifest.mf b/editor.actions/manifest.mf new file mode 100644 --- /dev/null +++ b/editor.actions/manifest.mf @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +OpenIDE-Module: org.netbeans.modules.editor.actions/1 +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/editor/actions/Bundle.properties +AutoUpdate-Show-In-Client: false +OpenIDE-Module-Provides: org.netbeans.modules.editor.actions diff --git a/editor.actions/nbproject/project.properties b/editor.actions/nbproject/project.properties new file mode 100644 --- /dev/null +++ b/editor.actions/nbproject/project.properties @@ -0,0 +1,46 @@ +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. +# +# The contents of this file are subject to the terms of either the GNU +# General Public License Version 2 only ("GPL") or the Common +# Development and Distribution License("CDDL") (collectively, the +# "License"). You may not use this file except in compliance with the +# License. You can obtain a copy of the License at +# http://www.netbeans.org/cddl-gplv2.html +# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the +# specific language governing permissions and limitations under the +# License. When distributing the software, include this License Header +# Notice in each file and include the License file at +# nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the GPL Version 2 section of the License file that +# accompanied this code. If applicable, add the following below the +# License Header, with the fields enclosed by brackets [] replaced by +# your own identifying information: +# "Portions Copyrighted [year] [name of copyright owner]" +# +# Contributor(s): +# +# The Original Software is NetBeans. The Initial Developer of the Original +# Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun +# Microsystems, Inc. All Rights Reserved. +# +# If you wish your version of this file to be governed by only the CDDL +# or only the GPL Version 2, indicate your decision by adding +# "[Contributor] elects to include this software in this distribution +# under the [CDDL or GPL Version 2] license." If you do not indicate a +# single choice of license, a recipient has the option to distribute +# your version of this file under either the CDDL, the GPL Version 2 or +# to extend the choice of license to its licensees as provided above. +# However, if you add GPL Version 2 code and therefore, elected the GPL +# Version 2 license, then the option applies only if the new code is +# made subject to such option by the copyright holder. + +javac.compilerargs=-Xlint:unchecked +javac.source=1.5 +javadoc.title=Editor Actions +spec.version.base=1.0.0 + +#javadoc.arch=${basedir}/arch.xml +#javadoc.apichanges=${basedir}/apichanges.xml diff --git a/editor.actions/nbproject/project.xml b/editor.actions/nbproject/project.xml new file mode 100644 --- /dev/null +++ b/editor.actions/nbproject/project.xml @@ -0,0 +1,98 @@ + + + + org.netbeans.modules.apisupport.project + + + org.netbeans.modules.editor.actions + + + org.netbeans.modules.editor.lib + + + + 1 + 1.40 + + + + org.netbeans.modules.editor.lib2 + + + + 1 + + + + + org.netbeans.modules.editor.mimelookup + + + + 1 + 1.12 + + + + org.netbeans.modules.editor.settings + + + + 1 + 1.25 + + + + org.openide.util + + + + 7.24 + + + + + + + + diff --git a/editor.actions/src/org/netbeans/modules/editor/actions/Bundle.properties b/editor.actions/src/org/netbeans/modules/editor/actions/Bundle.properties new file mode 100644 --- /dev/null +++ b/editor.actions/src/org/netbeans/modules/editor/actions/Bundle.properties @@ -0,0 +1,51 @@ +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. +# +# The contents of this file are subject to the terms of either the GNU +# General Public License Version 2 only ("GPL") or the Common +# Development and Distribution License("CDDL") (collectively, the +# "License"). You may not use this file except in compliance with the +# License. You can obtain a copy of the License at +# http://www.netbeans.org/cddl-gplv2.html +# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the +# specific language governing permissions and limitations under the +# License. When distributing the software, include this License Header +# Notice in each file and include the License file at +# nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the GPL Version 2 section of the License file that +# accompanied this code. If applicable, add the following below the +# License Header, with the fields enclosed by brackets [] replaced by +# your own identifying information: +# "Portions Copyrighted [year] [name of copyright owner]" +# +# Contributor(s): +# +# The Original Software is NetBeans. The Initial Developer of the Original +# Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun +# Microsystems, Inc. All Rights Reserved. +# +# If you wish your version of this file to be governed by only the CDDL +# or only the GPL Version 2, indicate your decision by adding +# "[Contributor] elects to include this software in this distribution +# under the [CDDL or GPL Version 2] license." If you do not indicate a +# single choice of license, a recipient has the option to distribute +# your version of this file under either the CDDL, the GPL Version 2 or +# to extend the choice of license to its licensees as provided above. +# However, if you add GPL Version 2 code and therefore, elected the GPL +# Version 2 license, then the option applies only if the new code is +# made subject to such option by the copyright holder. + +OpenIDE-Module-Name=Editor Actions +OpenIDE-Module-Display-Category=Editing +OpenIDE-Module-Short-Description=Contains editor actions implementations +OpenIDE-Module-Long-Description=Editor Actions module contains default editor actions implementations. + +# Actions +toggle-toolbar=Toggle Toolbar +toggle-toolbar_menu_text=S&how Editor Toolbar +toggle-line-numbers=Toggle Line Numbers +toggle-line-numbers_menu_text=&Show Line Numbers +goto-declaration=Go to Declaration +goto-declaration_menu_text=Go to &Declaration diff --git a/editor.actions/src/org/netbeans/modules/editor/actions/GotoAction.java b/editor.actions/src/org/netbeans/modules/editor/actions/GotoAction.java new file mode 100644 --- /dev/null +++ b/editor.actions/src/org/netbeans/modules/editor/actions/GotoAction.java @@ -0,0 +1,99 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.editor.actions; + +import org.netbeans.spi.editor.AbstractEditorAction; +import java.awt.event.ActionEvent; +import java.util.logging.Logger; +import javax.swing.text.BadLocationException; +import javax.swing.text.Caret; +import javax.swing.text.JTextComponent; +import org.netbeans.api.editor.EditorActionRegistration; +import org.netbeans.api.editor.EditorActionRegistrations; +import org.netbeans.editor.BaseDocument; +import org.netbeans.editor.Utilities; +import org.netbeans.editor.ext.ExtSyntaxSupport; +import org.netbeans.api.editor.EditorActionNames; + +/** + * Toggle toolbar/lines visibility. + * + * @author Miloslav Metelka + * @since 1.13 + */ + +@EditorActionRegistrations({ + @EditorActionRegistration( + name = EditorActionNames.gotoDeclaration, + menuPath = "GoTo", + menuPosition = 900, + menuText = "#" + EditorActionNames.gotoDeclaration + "_menu_text" + ) +}) +public final class GotoAction extends AbstractEditorAction { + + // -J-Dorg.netbeans.modules.editor.actions.GotoAction.level=FINEST + private static final Logger LOG = Logger.getLogger(GotoAction.class.getName()); + private static final long serialVersionUID = 1L; + + public void actionPerformed(ActionEvent evt, JTextComponent target) { + String actionName = actionName(); + if (EditorActionNames.gotoDeclaration.equals(actionName)) { + resetCaretMagicPosition(target); + BaseDocument doc = Utilities.getDocument(target); + if (doc != null) { + try { + Caret caret = target.getCaret(); + int dotPos = caret.getDot(); + int[] idBlk = Utilities.getIdentifierBlock(doc, dotPos); + ExtSyntaxSupport extSup = (ExtSyntaxSupport)doc.getSyntaxSupport(); + if (idBlk != null) { + int decPos = extSup.findDeclarationPosition(doc.getText(idBlk), idBlk[1]); + if (decPos >= 0) { + caret.setDot(decPos); + } + } + } catch (BadLocationException e) { + } + } + } + } + +} diff --git a/editor.actions/src/org/netbeans/modules/editor/actions/ToggleAction.java b/editor.actions/src/org/netbeans/modules/editor/actions/ToggleAction.java new file mode 100644 --- /dev/null +++ b/editor.actions/src/org/netbeans/modules/editor/actions/ToggleAction.java @@ -0,0 +1,87 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.editor.actions; + +import java.awt.event.ActionEvent; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.text.JTextComponent; +import org.netbeans.spi.editor.AbstractEditorAction; +import org.netbeans.api.editor.EditorActionRegistration; +import org.netbeans.api.editor.EditorActionRegistrations; +import org.netbeans.api.editor.settings.SimpleValueNames; +import org.netbeans.api.editor.EditorActionNames; + +/** + * Toggle toolbar/lines visibility. + * + * @author Miloslav Metelka + */ +@EditorActionRegistrations({ + @EditorActionRegistration( + name = EditorActionNames.toggleToolbar, + menuPath = "View", + menuPosition = 800, + menuText = "#" + EditorActionNames.toggleToolbar + "_menu_text", + preferencesKey = SimpleValueNames.TOOLBAR_VISIBLE_PROP + ), + @EditorActionRegistration( + name = EditorActionNames.toggleLineNumbers, + menuPath = "View", + menuPosition = 850, + menuText = "#" + EditorActionNames.toggleLineNumbers + "_menu_text", + preferencesKey = SimpleValueNames.LINE_NUMBER_VISIBLE + ) +}) +public final class ToggleAction extends AbstractEditorAction { + + private static final Logger LOG = Logger.getLogger(ToggleAction.class.getName()); + + private static final long serialVersionUID = 1L; + + @Override + public void actionPerformed(ActionEvent evt, JTextComponent component) { + // Leave empty - AlwaysEnabledAction toggles state in preferences by default + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("actionPerformed: actionName=" + actionName()); + } + } + +} diff --git a/editor.deprecated.pre61settings/nbproject/project.xml b/editor.deprecated.pre61settings/nbproject/project.xml --- a/editor.deprecated.pre61settings/nbproject/project.xml +++ b/editor.deprecated.pre61settings/nbproject/project.xml @@ -65,6 +65,15 @@ + org.netbeans.modules.editor.lib2 + + + + 1 + + + + org.netbeans.modules.editor.mimelookup diff --git a/editor.deprecated.pre61settings/src/org/netbeans/editor/SettingsDefaults.java b/editor.deprecated.pre61settings/src/org/netbeans/editor/SettingsDefaults.java --- a/editor.deprecated.pre61settings/src/org/netbeans/editor/SettingsDefaults.java +++ b/editor.deprecated.pre61settings/src/org/netbeans/editor/SettingsDefaults.java @@ -48,7 +48,7 @@ import javax.swing.UIManager; import java.util.Map; import java.util.HashMap; -import org.netbeans.modules.editor.lib.EditorPreferencesDefaults; +import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; /** * Default values for the settings. They are used @@ -156,8 +156,8 @@ public static final Color defaultTextLimitLineColor = new Color(255, 235, 235); public static final Integer defaultTextLimitWidth = EditorPreferencesDefaults.defaultTextLimitWidth; - public static final Acceptor defaultIdentifierAcceptor = EditorPreferencesDefaults.defaultIdentifierAcceptor; - public static final Acceptor defaultWhitespaceAcceptor = EditorPreferencesDefaults.defaultWhitespaceAcceptor; +// public static final Acceptor defaultIdentifierAcceptor = EditorPreferencesDefaults.defaultIdentifierAcceptor; +// public static final Acceptor defaultWhitespaceAcceptor = EditorPreferencesDefaults.defaultWhitespaceAcceptor; public static final Float defaultLineHeightCorrection = EditorPreferencesDefaults.defaultLineHeightCorrection; diff --git a/editor.deprecated.pre61settings/src/org/netbeans/editor/SettingsNames.java b/editor.deprecated.pre61settings/src/org/netbeans/editor/SettingsNames.java --- a/editor.deprecated.pre61settings/src/org/netbeans/editor/SettingsNames.java +++ b/editor.deprecated.pre61settings/src/org/netbeans/editor/SettingsNames.java @@ -41,7 +41,7 @@ package org.netbeans.editor; -import org.netbeans.modules.editor.lib.EditorPreferencesKeys; +import org.netbeans.modules.editor.lib2.EditorPreferencesKeys; /** * Names of the base settings defined in the editor. The other packages diff --git a/editor.deprecated.pre61settings/src/org/netbeans/editor/ext/ExtSettingsNames.java b/editor.deprecated.pre61settings/src/org/netbeans/editor/ext/ExtSettingsNames.java --- a/editor.deprecated.pre61settings/src/org/netbeans/editor/ext/ExtSettingsNames.java +++ b/editor.deprecated.pre61settings/src/org/netbeans/editor/ext/ExtSettingsNames.java @@ -42,7 +42,7 @@ package org.netbeans.editor.ext; import org.netbeans.editor.SettingsNames; -import org.netbeans.modules.editor.lib.EditorPreferencesKeys; +import org.netbeans.modules.editor.lib2.EditorPreferencesKeys; /** * Names of the extended editor settings. diff --git a/editor.lib/nbproject/project.xml b/editor.lib/nbproject/project.xml --- a/editor.lib/nbproject/project.xml +++ b/editor.lib/nbproject/project.xml @@ -87,7 +87,7 @@ 1 - 1.25 + 1.30 diff --git a/editor.lib/src/org/netbeans/editor/ActionFactory.java b/editor.lib/src/org/netbeans/editor/ActionFactory.java --- a/editor.lib/src/org/netbeans/editor/ActionFactory.java +++ b/editor.lib/src/org/netbeans/editor/ActionFactory.java @@ -2031,7 +2031,10 @@ } - /** Switch visibility of line numbers in editor */ + /** + * Switch visibility of line numbers in editor + * @deprecated this action is no longer used. It is reimplemented in editor.actions module. + */ //@EditorActionRegistration(name = BaseKit.toggleLineNumbersAction) // Registration in createActions() due to getPopupMenuItem() public static class ToggleLineNumbersAction extends LocalBaseAction { diff --git a/editor.lib/src/org/netbeans/editor/Analyzer.java b/editor.lib/src/org/netbeans/editor/Analyzer.java --- a/editor.lib/src/org/netbeans/editor/Analyzer.java +++ b/editor.lib/src/org/netbeans/editor/Analyzer.java @@ -51,7 +51,7 @@ import javax.swing.text.Document; import javax.swing.text.Segment; import org.netbeans.api.lexer.TokenHierarchy; -import org.netbeans.modules.editor.lib.EditorPreferencesKeys; +import org.netbeans.modules.editor.lib2.EditorPreferencesKeys; import org.netbeans.spi.lexer.MutableTextInput; /** diff --git a/editor.lib/src/org/netbeans/editor/BaseAction.java b/editor.lib/src/org/netbeans/editor/BaseAction.java --- a/editor.lib/src/org/netbeans/editor/BaseAction.java +++ b/editor.lib/src/org/netbeans/editor/BaseAction.java @@ -56,14 +56,17 @@ import javax.swing.text.Caret; /** -* This is the parent of majority of the actions. It implements -* the necessary resetting depending of what is required -* by constructor of target action. -* The other thing implemented here is macro recording. -* -* @author Miloslav Metelka -* @version 1.00 -*/ + * This is the parent of majority of the actions. It implements + * the necessary resetting depending of what is required + * by constructor of target action. + * The other thing implemented here is macro recording. + *
+ * Property "noIconInMenu" can be set to inform menu items not to use action's icon. + *
+ * + * @author Miloslav Metelka + * @version 1.00 + */ public abstract class BaseAction extends TextAction { @@ -120,7 +123,7 @@ * the action's real task is invoked. */ protected int updateMask; - + private static boolean recording; private static StringBuffer macroBuffer = new StringBuffer(); private static StringBuffer textBuffer = new StringBuffer(); diff --git a/editor.lib/src/org/netbeans/editor/BaseCaret.java b/editor.lib/src/org/netbeans/editor/BaseCaret.java --- a/editor.lib/src/org/netbeans/editor/BaseCaret.java +++ b/editor.lib/src/org/netbeans/editor/BaseCaret.java @@ -105,7 +105,7 @@ import org.netbeans.api.editor.settings.FontColorSettings; import org.netbeans.api.editor.settings.SimpleValueNames; import org.netbeans.lib.editor.util.swing.DocumentListenerPriority; -import org.netbeans.modules.editor.lib.EditorPreferencesDefaults; +import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; import org.netbeans.modules.editor.lib.SettingsConversions; import org.openide.util.WeakListeners; @@ -122,10 +122,10 @@ AtomicLockListener, FoldHierarchyListener { /** Caret type representing block covering current character */ - public static final String BLOCK_CARET = "block-caret"; // NOI18N + public static final String BLOCK_CARET = EditorPreferencesDefaults.BLOCK_CARET; // NOI18N /** Default caret type */ - public static final String LINE_CARET = "line-caret"; // NOI18N + public static final String LINE_CARET = EditorPreferencesDefaults.LINE_CARET; // NOI18N /** One dot thin line compatible with Swing default caret */ public static final String THIN_LINE_CARET = "thin-line-caret"; // NOI18N diff --git a/editor.lib/src/org/netbeans/editor/BaseDocument.java b/editor.lib/src/org/netbeans/editor/BaseDocument.java --- a/editor.lib/src/org/netbeans/editor/BaseDocument.java +++ b/editor.lib/src/org/netbeans/editor/BaseDocument.java @@ -92,8 +92,8 @@ import org.netbeans.lib.editor.util.ListenerList; import org.netbeans.lib.editor.util.swing.DocumentListenerPriority; import org.netbeans.modules.editor.lib.EditorPackageAccessor; -import org.netbeans.modules.editor.lib.EditorPreferencesDefaults; -import org.netbeans.modules.editor.lib.EditorPreferencesKeys; +import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; +import org.netbeans.modules.editor.lib2.EditorPreferencesKeys; import org.netbeans.modules.editor.lib.FormatterOverride; import org.netbeans.modules.editor.lib.TrailingWhitespaceRemove; import org.netbeans.modules.editor.lib.SettingsConversions; diff --git a/editor.lib/src/org/netbeans/editor/BaseKit.java b/editor.lib/src/org/netbeans/editor/BaseKit.java --- a/editor.lib/src/org/netbeans/editor/BaseKit.java +++ b/editor.lib/src/org/netbeans/editor/BaseKit.java @@ -74,6 +74,8 @@ import java.util.prefs.PreferenceChangeListener; import java.util.prefs.Preferences; import javax.swing.KeyStroke; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import javax.swing.text.AbstractDocument; import javax.swing.text.EditorKit; import javax.swing.text.Position; @@ -82,9 +84,10 @@ import org.netbeans.api.editor.mimelookup.MimeLookup; import org.netbeans.api.editor.mimelookup.MimePath; import org.netbeans.api.editor.settings.KeyBindingSettings; +import org.netbeans.lib.editor.util.ListenerList; import org.netbeans.lib.editor.util.swing.DocumentUtilities; -import org.netbeans.modules.editor.lib.EditorPreferencesDefaults; -import org.netbeans.modules.editor.lib.EditorPreferencesKeys; +import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; +import org.netbeans.modules.editor.lib2.EditorPreferencesKeys; import org.netbeans.modules.editor.lib.KitsTracker; import org.netbeans.modules.editor.lib.NavigationHistory; import org.netbeans.modules.editor.lib.SettingsConversions; @@ -113,6 +116,7 @@ */ static ThreadLocal IN_PASTE = new ThreadLocal(); + // -J-Dorg.netbeans.editor.BaseKit.level=FINEST private static final Logger LOG = Logger.getLogger(BaseKit.class.getName()); /** split the current line at cursor position */ @@ -352,6 +356,8 @@ public static final int MAGIC_POSITION_MAX = Integer.MAX_VALUE - 1; + private final SearchableKit searchableKit; + // static SettingsChangeListener settingsListener = new SettingsChangeListener() { // public void settingsChange(SettingsChangeEvent evt) { // String settingName = (evt != null) ? evt.getSettingName() : null; @@ -534,6 +540,10 @@ 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 */ @@ -691,21 +701,34 @@ /** Creates map with [name, action] pairs from the given * array of actions. */ - public static Map actionsToMap(Action[] actions) { - Map map = new HashMap(); + public static void addActionsToMap(Map 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) { - throw new IllegalStateException("actions[] contains null at index " + i + - ((i > 0) ? ". Preceding action is " + actions[i - 1] : "")); + 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) { - throw new IllegalStateException("Null Action.NAME property of action " + a); + 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 } - return map; + if (fineLoggable) { + LOG.fine(logActionsType + " end ----------------------\n"); + } } /** Converts map with [name, action] back @@ -848,7 +871,7 @@ removeSelectionActionDef, undoActionDef, redoActionDef, - new ActionFactory.ToggleLineNumbersAction(), + //new ActionFactory.ToggleLineNumbersAction(), new NextWordAction(nextWordAction), new NextWordAction(selectionNextWordAction), new PreviousWordAction(previousWordAction), @@ -895,31 +918,43 @@ * to get basic list and then customActions are added. */ public @Override final Action[] getActions() { - return (Action []) getActionsAndMap()[0]; + return (Action []) addActionsToMap()[0]; } /* package */ Map getActionMap() { - return (Map) getActionsAndMap()[1]; + return (Map) addActionsToMap()[1]; } - private Object[] getActionsAndMap() { + private Object[] addActionsToMap() { synchronized (KEYMAPS_AND_ACTIONS_LOCK) { MimePath mimePath = MimePath.parse(getContentType()); Action[] actions = kitActions.get(mimePath); Map actionMap = kitActionMaps.get(mimePath); if (actions == null || actionMap == null) { - // create map of actions - actions = createActions(); - actionMap = actionsToMap(actions); + // 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/content-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(actions.length << 1); + addActionsToMap(actionMap, actions, "Declared actions"); // NOI18N + + Action[] createActionsMethodResult = createActions(); + if (createActionsMethodResult != null) { + addActionsToMap(actionMap, createActionsMethodResult, "Actions from createActions()"); // NOI18N + } // add custom actions Action[] customActions = getCustomActions(); if (customActions != null) { - actionMap.putAll(actionsToMap(customActions)); - actions = actionMap.values().toArray(new Action[actionMap.values().size()]); + addActionsToMap(actionMap, customActions, "Custom actions"); // NOI18N } + actions = actionMap.values().toArray(new Action[actionMap.values().size()]); + kitActions.put(mimePath, actions); kitActionMaps.put(mimePath, actionMap); @@ -932,6 +967,16 @@ return new Object [] { actions, actionMap }; } } + + /** + * Get actions declared in the xml layer. They may be overriden by result + * of createActions() and finally by result of getCustomActions(). + * + * @return non-null list of declared actions. + */ + protected Action[] getDeclaredActions() { + return new Action[0]; + } /** Update the actions right after their creation was finished. * The getActions() and getActionByName() @@ -1309,7 +1354,11 @@ } } - /** Compound action that encapsulates several actions */ + /** + * Compound action that encapsulates several actions + * @deprecated this action is no longer used. + */ + @Deprecated public static class CompoundAction extends LocalBaseAction { Action[] actions; @@ -1339,11 +1388,14 @@ } } - /** 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. - */ + /** + * 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 public static class KitCompoundAction extends LocalBaseAction { private String[] actionNames; @@ -1387,7 +1439,11 @@ } } - @EditorActionRegistration(name = insertContentAction) + /** + * @deprecated this action is no longer used. It is reimplemented in editor.actions module. + */ +// @EditorActionRegistration(name = insertContentAction) + @Deprecated public static class InsertContentAction extends LocalBaseAction { static final long serialVersionUID =5647751370952797218L; @@ -1501,7 +1557,11 @@ } } - @EditorActionRegistration(name = readOnlyAction) + /** + * @deprecated this action is no longer used. It is reimplemented in editor.actions module. + */ + @Deprecated +// @EditorActionRegistration(name = readOnlyAction) public static class ReadOnlyAction extends LocalBaseAction { static final long serialVersionUID =9204335480208463193L; @@ -1517,7 +1577,11 @@ } } - @EditorActionRegistration(name = writableAction) + /** + * @deprecated this action is no longer used. It is reimplemented in editor.actions module. + */ + @Deprecated +// @EditorActionRegistration(name = writableAction) public static class WritableAction extends LocalBaseAction { static final long serialVersionUID =-5982547952800937954L; @@ -2725,7 +2789,41 @@ for(JTextComponent c : arr) { c.setKeymap(keymap); } + + searchableKit.fireActionsChange(); } } // End of KeymapTracker class + + private static final class SearchableKit implements org.netbeans.modules.editor.lib2.actions.SearchableEditorKit { + + private final BaseKit baseKit; + + private final ListenerList actionsListenerList = new ListenerList(); + + SearchableKit(BaseKit baseKit) { + this.baseKit = baseKit; + } + + public Action getAction(String actionName) { + return baseKit.getActionByName(actionName); + } + + public void addActionsChangeListener(ChangeListener listener) { + actionsListenerList.add(listener); + } + + public void removeActionsChangeListener(ChangeListener listener) { + actionsListenerList.remove(listener); + } + + void fireActionsChange() { + ChangeEvent evt = new ChangeEvent(this); + for (ChangeListener listener : actionsListenerList.getListeners()) { + listener.stateChanged(evt); + } + } + + } + } diff --git a/editor.lib/src/org/netbeans/editor/BaseTextUI.java b/editor.lib/src/org/netbeans/editor/BaseTextUI.java --- a/editor.lib/src/org/netbeans/editor/BaseTextUI.java +++ b/editor.lib/src/org/netbeans/editor/BaseTextUI.java @@ -66,8 +66,8 @@ import org.netbeans.api.editor.settings.SimpleValueNames; import org.netbeans.modules.editor.lib2.EditorApiPackageAccessor; import org.netbeans.editor.view.spi.LockView; -import org.netbeans.modules.editor.lib.EditorPreferencesDefaults; -import org.netbeans.modules.editor.lib.EditorPreferencesKeys; +import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; +import org.netbeans.modules.editor.lib2.EditorPreferencesKeys; import org.netbeans.modules.editor.lib.SettingsConversions; import org.openide.util.WeakListeners; diff --git a/editor.lib/src/org/netbeans/editor/CodeFoldingSideBar.java b/editor.lib/src/org/netbeans/editor/CodeFoldingSideBar.java --- a/editor.lib/src/org/netbeans/editor/CodeFoldingSideBar.java +++ b/editor.lib/src/org/netbeans/editor/CodeFoldingSideBar.java @@ -84,7 +84,7 @@ import org.netbeans.api.editor.settings.FontColorSettings; import org.netbeans.api.editor.settings.SimpleValueNames; import org.netbeans.editor.CodeFoldingSideBar.PaintInfo; -import org.netbeans.modules.editor.lib.EditorPreferencesDefaults; +import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; import org.netbeans.modules.editor.lib.SettingsConversions; import org.openide.util.Lookup; import org.openide.util.LookupEvent; diff --git a/editor.lib/src/org/netbeans/editor/EditorUI.java b/editor.lib/src/org/netbeans/editor/EditorUI.java --- a/editor.lib/src/org/netbeans/editor/EditorUI.java +++ b/editor.lib/src/org/netbeans/editor/EditorUI.java @@ -86,8 +86,8 @@ import org.netbeans.editor.ext.ExtKit; import org.netbeans.editor.ext.ToolTipSupport; import org.netbeans.modules.editor.lib.ColoringMap; -import org.netbeans.modules.editor.lib.EditorPreferencesDefaults; -import org.netbeans.modules.editor.lib.EditorPreferencesKeys; +import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; +import org.netbeans.modules.editor.lib2.EditorPreferencesKeys; import org.netbeans.modules.editor.lib.KitsTracker; import org.netbeans.modules.editor.lib.SettingsConversions; import org.openide.util.WeakListeners; diff --git a/editor.lib/src/org/netbeans/editor/Formatter.java b/editor.lib/src/org/netbeans/editor/Formatter.java --- a/editor.lib/src/org/netbeans/editor/Formatter.java +++ b/editor.lib/src/org/netbeans/editor/Formatter.java @@ -61,7 +61,7 @@ import org.netbeans.api.editor.settings.SimpleValueNames; import org.netbeans.lib.editor.util.CharSequenceUtilities; import org.netbeans.lib.editor.util.swing.DocumentUtilities; -import org.netbeans.modules.editor.lib.EditorPreferencesDefaults; +import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; import org.netbeans.modules.editor.lib.KitsTracker; import org.netbeans.modules.editor.lib.SettingsConversions; import org.openide.util.Lookup; diff --git a/editor.lib/src/org/netbeans/editor/StatusBar.java b/editor.lib/src/org/netbeans/editor/StatusBar.java --- a/editor.lib/src/org/netbeans/editor/StatusBar.java +++ b/editor.lib/src/org/netbeans/editor/StatusBar.java @@ -86,7 +86,7 @@ import org.netbeans.api.editor.settings.FontColorNames; import org.netbeans.api.editor.settings.FontColorSettings; import org.netbeans.api.editor.settings.SimpleValueNames; -import org.netbeans.modules.editor.lib.EditorPreferencesDefaults; +import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; import org.openide.util.NbBundle; import org.openide.util.WeakListeners; diff --git a/editor.lib/src/org/netbeans/editor/Utilities.java b/editor.lib/src/org/netbeans/editor/Utilities.java --- a/editor.lib/src/org/netbeans/editor/Utilities.java +++ b/editor.lib/src/org/netbeans/editor/Utilities.java @@ -61,7 +61,7 @@ import javax.swing.text.View; import org.netbeans.lib.editor.util.CharSequenceUtilities; import org.netbeans.lib.editor.util.swing.DocumentUtilities; -import org.netbeans.modules.editor.lib.EditorPreferencesKeys; +import org.netbeans.modules.editor.lib2.EditorPreferencesKeys; import org.openide.util.NbBundle; /** diff --git a/editor.lib/src/org/netbeans/editor/WordMatch.java b/editor.lib/src/org/netbeans/editor/WordMatch.java --- a/editor.lib/src/org/netbeans/editor/WordMatch.java +++ b/editor.lib/src/org/netbeans/editor/WordMatch.java @@ -52,8 +52,8 @@ import javax.swing.text.BadLocationException; import javax.swing.text.JTextComponent; import org.netbeans.api.editor.mimelookup.MimeLookup; -import org.netbeans.modules.editor.lib.EditorPreferencesDefaults; -import org.netbeans.modules.editor.lib.EditorPreferencesKeys; +import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; +import org.netbeans.modules.editor.lib2.EditorPreferencesKeys; import org.openide.util.WeakListeners; /** Word matching support enables to fill in the rest of the word diff --git a/editor.lib/src/org/netbeans/editor/ext/ExtFormatter.java b/editor.lib/src/org/netbeans/editor/ext/ExtFormatter.java --- a/editor.lib/src/org/netbeans/editor/ext/ExtFormatter.java +++ b/editor.lib/src/org/netbeans/editor/ext/ExtFormatter.java @@ -65,7 +65,7 @@ import org.netbeans.editor.AcceptorFactory; import org.netbeans.editor.BaseKit; import org.netbeans.editor.Syntax; -import org.netbeans.modules.editor.lib.EditorPreferencesKeys; +import org.netbeans.modules.editor.lib2.EditorPreferencesKeys; import org.netbeans.modules.editor.lib.SettingsConversions; import org.openide.util.Lookup; import org.openide.util.WeakListeners; diff --git a/editor.lib/src/org/netbeans/editor/ext/ExtKit.java b/editor.lib/src/org/netbeans/editor/ext/ExtKit.java --- a/editor.lib/src/org/netbeans/editor/ext/ExtKit.java +++ b/editor.lib/src/org/netbeans/editor/ext/ExtKit.java @@ -68,6 +68,7 @@ import org.netbeans.lib.editor.util.CharSequenceUtilities; import org.netbeans.lib.editor.util.swing.DocumentUtilities; import org.netbeans.modules.editor.lib.NavigationHistory; +import org.netbeans.api.editor.EditorActionNames; import org.openide.util.Lookup; import org.openide.util.NbBundle; @@ -111,7 +112,7 @@ public static final String gotoAction = "goto"; // NOI18N /** Goto declaration depending on the context under the caret */ - public static final String gotoDeclarationAction = "goto-declaration"; // NOI18N + public static final String gotoDeclarationAction = EditorActionNames.gotoDeclaration; // NOI18N /** Goto source depending on the context under the caret */ public static final String gotoSourceAction = "goto-source"; // NOI18N @@ -160,7 +161,7 @@ public static final String toggleCommentAction = "toggle-comment"; // NOI18N /** Toggle the toolbar */ - public static final String toggleToolbarAction = "toggle-toolbar"; // NOI18N + public static final String toggleToolbarAction = EditorActionNames.toggleToolbar; /** Trimmed text for go to submenu*/ public static final String TRIMMED_TEXT = "trimmed-text"; //NOI18N @@ -553,8 +554,10 @@ } - /** Action to go to the declaration of the variable under the caret. - */ + /** + * Action to go to the declaration of the variable under the caret. + * @deprecated this action is no longer used. It is reimplemented in editor.actions module. + */ public static class GotoDeclarationAction extends BaseKitLocalizedAction { static final long serialVersionUID =-6440495023918097760L; diff --git a/editor.lib/src/org/netbeans/modules/editor/lib/ColoringMap.java b/editor.lib/src/org/netbeans/modules/editor/lib/ColoringMap.java --- a/editor.lib/src/org/netbeans/modules/editor/lib/ColoringMap.java +++ b/editor.lib/src/org/netbeans/modules/editor/lib/ColoringMap.java @@ -40,6 +40,7 @@ */ package org.netbeans.modules.editor.lib; +import org.netbeans.modules.editor.lib2.EditorPreferencesKeys; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.lang.reflect.Field; diff --git a/editor.lib/src/org/netbeans/modules/editor/lib/SettingsConversions.java b/editor.lib/src/org/netbeans/modules/editor/lib/SettingsConversions.java --- a/editor.lib/src/org/netbeans/modules/editor/lib/SettingsConversions.java +++ b/editor.lib/src/org/netbeans/modules/editor/lib/SettingsConversions.java @@ -52,6 +52,7 @@ import java.util.logging.Logger; import java.util.prefs.Preferences; import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.editor.Acceptor; import org.openide.util.Lookup; /** @@ -259,7 +260,7 @@ } } } - + private SettingsConversions() { } diff --git a/editor.lib2/manifest.mf b/editor.lib2/manifest.mf --- a/editor.lib2/manifest.mf +++ b/editor.lib2/manifest.mf @@ -3,3 +3,4 @@ OpenIDE-Module-Implementation-Version: 2 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/editor/lib2/Bundle.properties OpenIDE-Module-Layer: org/netbeans/modules/editor/lib2/resources/layer.xml +OpenIDE-Module-Needs: org.netbeans.modules.editor.actions \ No newline at end of file diff --git a/editor.lib2/nbproject/project.properties b/editor.lib2/nbproject/project.properties --- a/editor.lib2/nbproject/project.properties +++ b/editor.lib2/nbproject/project.properties @@ -40,7 +40,7 @@ is.autoload=true javac.source=1.5 javac.compilerargs=-Xlint:unchecked -spec.version.base=1.13.0 +spec.version.base=1.14.0 cp.extra=${nb_all}/apisupport.harness/external/openjdk-javac-6-b12.jar javadoc.arch=${basedir}/arch.xml diff --git a/editor.lib2/nbproject/project.xml b/editor.lib2/nbproject/project.xml --- a/editor.lib2/nbproject/project.xml +++ b/editor.lib2/nbproject/project.xml @@ -87,7 +87,7 @@ - 7.7 + 7.25
diff --git a/editor.lib2/src/org/netbeans/api/editor/EditorActionNames.java b/editor.lib2/src/org/netbeans/api/editor/EditorActionNames.java new file mode 100644 --- /dev/null +++ b/editor.lib2/src/org/netbeans/api/editor/EditorActionNames.java @@ -0,0 +1,69 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.netbeans.api.editor; + +/** + * Names of common editor actions. + * Clients can use the names constants e.g. for + * Ideally all editor actions' names should be declared here. + * + * @author Miloslav Metelka + * @since 1.13 + */ +public final class EditorActionNames { + + /** Toggle the visibility of the editor toolbar */ + public static final String toggleToolbar = "toggle-toolbar"; // NOI18N + + /** Toggle visibility of line numbers*/ + public static final String toggleLineNumbers = "toggle-line-numbers"; // NOI18N + + /** Goto declaration depending on the context under the caret */ + public static final String gotoDeclaration = "goto-declaration"; // NOI18N + +} diff --git a/editor.lib2/src/org/netbeans/api/editor/EditorActionRegistration.java b/editor.lib2/src/org/netbeans/api/editor/EditorActionRegistration.java --- a/editor.lib2/src/org/netbeans/api/editor/EditorActionRegistration.java +++ b/editor.lib2/src/org/netbeans/api/editor/EditorActionRegistration.java @@ -134,4 +134,45 @@ */ String popupText() default ""; + /** + * Path of this action in main menu e.g. "Edit". + */ + String menuPath() default ""; + + /** + * Integer position of the main menu item among the other menu items. + *
+ * The default Integer.MAX_VALUE value means no menu representation. + */ + int menuPosition() default Integer.MAX_VALUE; + + /** + * Path of this action in popup menu e.g. "" for appearance right in the context menu + * or a corresponding path for nested submenu appearance. + */ + String popupPath() default ""; + + /** + * Integer position of the popup menu item among the other popup menu (or submenu) items. + *
+ * The default Integer.MAX_VALUE value means no popup menu representation. + */ + int popupPosition() default Integer.MAX_VALUE; + + /** + * Integer position of this action in editor toolbar. + *
+ * The default Integer.MAX_VALUE value means no toolbar representation. + */ + int toolBarPosition() default Integer.MAX_VALUE; + + /** + * Boolean key in preferences that corresponds to action's selected state. + *
+ * If set to non-empty string the action will be represented by a check-box + * in menu and popup menu and the corresponding key will be set in + * global mime-lookup MimeLookup.getLookup(MimePath.EMPTY). + */ + String preferencesKey() default ""; + } diff --git a/editor.lib2/src/org/netbeans/api/editor/EditorUtilities.java b/editor.lib2/src/org/netbeans/api/editor/EditorUtilities.java new file mode 100644 --- /dev/null +++ b/editor.lib2/src/org/netbeans/api/editor/EditorUtilities.java @@ -0,0 +1,73 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ + +package org.netbeans.api.editor; + +import javax.swing.Action; +import javax.swing.text.EditorKit; +import org.netbeans.modules.editor.lib2.actions.EditorActionUtilities; + + +/** + * Various utility methods related to editor. + * + * @author Miloslav Metelka + * @since 1.13 + */ + +public final class EditorUtilities { + + private EditorUtilities() { + // No instances + } + + /** + * Find an action with the given name in the editor kit. + * + * @param editorKit non-null editor kit in which search is performed. + * @param actionName non-null action name to search for. + * @return action instance with the given name or null if action not found. + */ + public static Action getAction(EditorKit editorKit, String actionName) { + return EditorActionUtilities.getAction(editorKit, actionName); + } + +} diff --git a/editor.lib2/src/org/netbeans/modules/editor/lib2/EditorActionRegistrationProcessor.java b/editor.lib2/src/org/netbeans/modules/editor/lib2/EditorActionRegistrationProcessor.java --- a/editor.lib2/src/org/netbeans/modules/editor/lib2/EditorActionRegistrationProcessor.java +++ b/editor.lib2/src/org/netbeans/modules/editor/lib2/EditorActionRegistrationProcessor.java @@ -39,13 +39,10 @@ package org.netbeans.modules.editor.lib2; -import java.io.IOException; -import java.util.MissingResourceException; -import java.util.PropertyResourceBundle; -import java.util.ResourceBundle; +import java.util.List; +import org.netbeans.modules.editor.lib2.actions.EditorActionUtilities; +import org.netbeans.modules.editor.lib2.actions.PresenterEditorAction; import java.util.Set; -import javax.annotation.processing.Filer; -import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; @@ -55,10 +52,10 @@ import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.swing.Action; -import javax.tools.StandardLocation; import org.netbeans.api.editor.EditorActionRegistration; import org.netbeans.api.editor.EditorActionRegistrations; import org.openide.filesystems.annotations.LayerBuilder; @@ -67,7 +64,8 @@ import org.openide.util.lookup.ServiceProvider; /** - * Annotation processor for + * Annotation processor for {@link EditorActionRegistration} + * and {@link EditorActionRegistrations}. */ @ServiceProvider(service=Processor.class) @SupportedSourceVersion(SourceVersion.RELEASE_6) @@ -101,7 +99,9 @@ String methodName; TypeMirror swingActionType = processingEnv.getTypeUtils().getDeclaredType( processingEnv.getElementUtils().getTypeElement("javax.swing.Action")); - + TypeMirror utilMapType = processingEnv.getTypeUtils().getDeclaredType( + processingEnv.getElementUtils().getTypeElement("java.util.Map")); + boolean directActionCreation = false; // Whether construct AlwaysEnabledAction or annotated action directly switch (e.getKind()) { case CLASS: className = processingEnv.getElementUtils().getBinaryName((TypeElement)e).toString(); @@ -111,24 +111,35 @@ if (!e.getModifiers().contains(Modifier.PUBLIC)) { throw new LayerGenerationException(className + " is not public", e); } - boolean hasDefaultCtor = false; + ExecutableElement defaultCtor = null; + ExecutableElement mapCtor = null; for (ExecutableElement constructor : ElementFilter.constructorsIn(e.getEnclosedElements())) { - if (constructor.getParameters().isEmpty()) { - if (!constructor.getModifiers().contains(Modifier.PUBLIC)) { - throw new LayerGenerationException("Default constructor of " + className + " is not public", e); - } - hasDefaultCtor = true; - break; + List params = constructor.getParameters(); + if (params.isEmpty()) { + defaultCtor = constructor; + + } else if (params.size() == 1 && + processingEnv.getTypeUtils().isAssignable(params.get(0).asType(), utilMapType)) + { + mapCtor = constructor; } } - if (!hasDefaultCtor) { - throw new LayerGenerationException(className + " must have a no-argument constructor", e); + String msgBase = "No-argument (or single-argument \"Map attrs\") constructor"; + if (defaultCtor == null && mapCtor == null) { + throw new LayerGenerationException(msgBase + " not present in " + className, e); + } + boolean defaultCtorPublic = (defaultCtor != null && defaultCtor.getModifiers().contains(Modifier.PUBLIC)); + boolean mapCtorPublic = (mapCtor != null && mapCtor.getModifiers().contains(Modifier.PUBLIC)); + if (!defaultCtorPublic && !mapCtorPublic) { + throw new LayerGenerationException(msgBase + " not public in " + className, e); } if (!processingEnv.getTypeUtils().isAssignable(e.asType(), swingActionType)) { throw new LayerGenerationException(className + " is not assignable to javax.swing.Action", e); } - + if (mapCtorPublic) { + directActionCreation = true; + } methodName = null; break; @@ -136,18 +147,28 @@ className = processingEnv.getElementUtils().getBinaryName((TypeElement) e.getEnclosingElement()).toString(); methodName = e.getSimpleName().toString(); if (!e.getModifiers().contains(Modifier.STATIC)) { - throw new LayerGenerationException(className + "." + methodName + " must be static", e); + throw new LayerGenerationException(className + "." + methodName + " must be static", e); // NOI18N } // It appears that actually even non-public method registration works - so commented following // if (!e.getModifiers().contains(Modifier.PUBLIC)) { // throw new LayerGenerationException(className + "." + methodName + " must be public", e); // } - if (!((ExecutableElement) e).getParameters().isEmpty()) { - throw new LayerGenerationException(className + "." + methodName + " must not take arguments", e); + List params = ((ExecutableElement)e).getParameters(); + boolean emptyParams = params.isEmpty(); + boolean mapParam = (params.size() == 1 && processingEnv.getTypeUtils().isAssignable( + params.get(0).asType(), utilMapType)); + if (!emptyParams && !mapParam) + { + throw new LayerGenerationException(className + "." + methodName + + " must not take arguments (or have a single-argument \"Map attrs\")", e); // NOI18N } - if (swingActionType != null && !processingEnv.getTypeUtils().isAssignable(((ExecutableElement)e).getReturnType(), swingActionType)) { + TypeMirror returnType = ((ExecutableElement)e).getReturnType(); + if (swingActionType != null && !processingEnv.getTypeUtils().isAssignable(returnType, swingActionType)) { throw new LayerGenerationException(className + "." + methodName + " is not assignable to javax.swing.Action", e); } + if (mapParam) { + directActionCreation = true; + } break; default: @@ -157,14 +178,16 @@ String actionName = annotation.name(); StringBuilder filePath = new StringBuilder(50); + String mimeType = annotation.mimeType(); filePath.append("Editors"); - if (annotation.mimeType().length() > 0) { - filePath.append("/").append(annotation.mimeType()); + if (mimeType.length() > 0) { + filePath.append("/").append(mimeType); } filePath.append("/Actions/").append(actionName).append(".instance"); - LayerBuilder.File file = layer(e).file(filePath.toString()); - - file.stringvalue("displayName", actionName); + LayerBuilder layer = layer(e); + LayerBuilder.File file = layer.file(filePath.toString()); + String preferencesKey = annotation.preferencesKey(); + boolean checkBoxPresenter = (preferencesKey.length() > 0); // Resolve icon resource String iconResource = annotation.iconResource(); @@ -199,13 +222,117 @@ file.bundlevalue("popupText", popupText); } - file.methodvalue("instanceCreate", "org.openide.awt.Actions", "alwaysEnabled"); - if (methodName != null) { - file.methodvalue("delegate", className, methodName); - } else { - file.newvalue("delegate", className); + // Check presenters + String presenterActionName = null; + + // Check menu path + String menuPath = annotation.menuPath(); + int menuPosition = annotation.menuPosition(); + if (menuPosition != Integer.MAX_VALUE) { + StringBuilder presenterFilePath = new StringBuilder(50); + presenterFilePath.append("Menu/"); + if (menuPath.length() > 0) { + presenterFilePath.append(menuPath).append('/'); + } + presenterFilePath.append(actionName).append(".shadow"); + LayerBuilder.File presenterShadowFile = layer.file(presenterFilePath.toString()); + if (presenterActionName == null) { + if (checkBoxPresenter) { // Point directly to AlwaysEnabledAction + presenterActionName = "Editors/Actions/" + actionName + ".instance"; + } else { + presenterActionName = generatePresenterAction(layer, actionName); + } + } + presenterShadowFile.stringvalue("originalFile", presenterActionName); + presenterShadowFile.intvalue("position", menuPosition); + presenterShadowFile.write(); + } + + // Check popup path + String popupPath = annotation.popupPath(); + int popupPosition = annotation.popupPosition(); + if (popupPosition != Integer.MAX_VALUE) { + StringBuilder presenterFilePath = new StringBuilder(50); + presenterFilePath.append("Editors/Popup/"); + if (mimeType.length() > 0) { + presenterFilePath.append(mimeType).append("/"); + } + if (popupPath.length() > 0) { + presenterFilePath.append(popupPath).append('/'); + } + presenterFilePath.append(actionName).append(".shadow"); + LayerBuilder.File presenterShadowFile = layer.file(presenterFilePath.toString()); + if (presenterActionName == null) { + if (checkBoxPresenter) { // Point directly to AlwaysEnabledAction + presenterActionName = "Editors/Actions/" + actionName + ".instance"; + } else { + presenterActionName = generatePresenterAction(layer, actionName); + } + } + presenterShadowFile.stringvalue("originalFile", presenterActionName); + presenterShadowFile.intvalue("position", popupPosition); + presenterShadowFile.write(); + } + + int toolBarPosition = annotation.toolBarPosition(); + if (toolBarPosition != Integer.MAX_VALUE) { + StringBuilder presenterFilePath = new StringBuilder(50); + presenterFilePath.append("Editors/Toolbar/"); + if (mimeType.length() > 0) { + presenterFilePath.append(mimeType).append("/"); + } + presenterFilePath.append(actionName).append(".shadow"); + LayerBuilder.File presenterShadowFile = layer.file(presenterFilePath.toString()); + if (presenterActionName == null) { + presenterActionName = generatePresenterAction(layer, actionName); + } + presenterShadowFile.stringvalue("originalFile", presenterActionName); + presenterShadowFile.intvalue("position", toolBarPosition); + presenterShadowFile.write(); + } + + if (preferencesKey.length() > 0) { + file.stringvalue("PreferencesKey", preferencesKey); + file.methodvalue("PreferencesNode", EditorActionUtilities.class.getName(), "getGlobalPreferences"); + } + + // Deafult helpID is action's name + file.stringvalue("helpID", actionName); + + // Resolve accelerator through method + file.methodvalue(Action.ACCELERATOR_KEY, EditorActionUtilities.class.getName(), "getAccelerator"); + + // Always generate Action.NAME since although AlwaysEnabledAction tweaks its retrieval to "displayName" + // some tools may query FO's properties and expect it there. + file.stringvalue(Action.NAME, actionName); + + if (directActionCreation) { + if (methodName != null) { + file.methodvalue("instanceCreate", className, methodName); + } else { + file.newvalue("instanceCreate", className); + } + + } else { // Create always enabled action + file.methodvalue("instanceCreate", "org.openide.awt.Actions", "alwaysEnabled"); + file.stringvalue("displayName", actionName); + + if (methodName != null) { + file.methodvalue("delegate", className, methodName); + } else { + file.newvalue("delegate", className); + } } file.write(); } + private String generatePresenterAction(LayerBuilder layer, String actionName) { + String presenterActionName = "Editors/ActionPresenters/" + actionName + ".instance"; + LayerBuilder.File presenterActionFile = layer.file(presenterActionName); + presenterActionFile.methodvalue("instanceCreate", PresenterEditorAction.class.getName(), "create"); + presenterActionFile.stringvalue(Action.NAME, actionName); + presenterActionFile.write(); + return presenterActionName; + } + } diff --git a/editor.lib/src/org/netbeans/modules/editor/lib/EditorPreferencesDefaults.java b/editor.lib2/src/org/netbeans/modules/editor/lib2/EditorPreferencesDefaults.java rename from editor.lib/src/org/netbeans/modules/editor/lib/EditorPreferencesDefaults.java rename to editor.lib2/src/org/netbeans/modules/editor/lib2/EditorPreferencesDefaults.java --- a/editor.lib/src/org/netbeans/modules/editor/lib/EditorPreferencesDefaults.java +++ b/editor.lib2/src/org/netbeans/modules/editor/lib2/EditorPreferencesDefaults.java @@ -39,13 +39,10 @@ * made subject to such option by the copyright holder. */ -package org.netbeans.modules.editor.lib; +package org.netbeans.modules.editor.lib2; import java.awt.Insets; import java.awt.Dimension; -import org.netbeans.editor.Acceptor; -import org.netbeans.editor.AcceptorFactory; -import org.netbeans.editor.BaseCaret; /** * This class contains settings default values copied over from SettingsDefaults and ExtSettingsDefaults. @@ -57,9 +54,13 @@ private EditorPreferencesDefaults() { // no-op } + + public static final String LINE_CARET = "line-caret"; + public static final String BLOCK_CARET = "block-caret"; - // not in SettingsDefaults not ExtSettingsDefaults - public static final boolean defaultToolbarVisible = true; + public static final boolean defaultToolbarVisible = true; // Currently unused - see ToggleAction in editor.actions + public static final boolean defaultLineNumberVisible = false; // Currently unused - see ToggleAction in editor.actions + public static final boolean defaultPopupMenuEnabled = true; // ----------------------------------------------------------------------- @@ -98,8 +99,8 @@ public static final boolean defaultExpandTabs = true; - public static final String defaultCaretTypeInsertMode = BaseCaret.LINE_CARET; - public static final String defaultCaretTypeOverwriteMode = BaseCaret.BLOCK_CARET; + public static final String defaultCaretTypeInsertMode = LINE_CARET; + public static final String defaultCaretTypeOverwriteMode = BLOCK_CARET; public static final boolean defaultCaretItalicInsertMode = false; public static final boolean defaultCaretItalicOverwriteMode = false; /** @since 1.23 */ @@ -110,7 +111,6 @@ public static final boolean defaultStatusBarVisible = true; - public static final boolean defaultLineNumberVisible = false; public static final boolean defaultPrintLineNumberVisible = true; public static final boolean defaultTextLimitLineVisible = true; public static final boolean defaultHomeKeyColumnOne = false; diff --git a/editor.lib/src/org/netbeans/modules/editor/lib/EditorPreferencesKeys.java b/editor.lib2/src/org/netbeans/modules/editor/lib2/EditorPreferencesKeys.java rename from editor.lib/src/org/netbeans/modules/editor/lib/EditorPreferencesKeys.java rename to editor.lib2/src/org/netbeans/modules/editor/lib2/EditorPreferencesKeys.java --- a/editor.lib/src/org/netbeans/modules/editor/lib/EditorPreferencesKeys.java +++ b/editor.lib2/src/org/netbeans/modules/editor/lib2/EditorPreferencesKeys.java @@ -39,7 +39,7 @@ * made subject to such option by the copyright holder. */ -package org.netbeans.modules.editor.lib; +package org.netbeans.modules.editor.lib2; import org.netbeans.api.editor.settings.SimpleValueNames; diff --git a/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/EditorActionUtilities.java b/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/EditorActionUtilities.java new file mode 100644 --- /dev/null +++ b/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/EditorActionUtilities.java @@ -0,0 +1,308 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.editor.lib2.actions; + +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.prefs.Preferences; +import javax.swing.Action; +import javax.swing.KeyStroke; +import javax.swing.event.ChangeListener; +import javax.swing.text.EditorKit; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.api.editor.settings.KeyBindingSettings; +import org.netbeans.api.editor.settings.MultiKeyBinding; +import org.openide.filesystems.FileObject; +import org.openide.util.Lookup; +import org.openide.util.LookupEvent; +import org.openide.util.LookupListener; +import org.openide.util.lookup.Lookups; + + +/** + * Various utility methods for declarative editor action registrations. + */ +public final class EditorActionUtilities { + + // -J-Dorg.netbeans.modules.editor.lib2.actions.EditorActionUtilities.level=FINEST + private static final Logger LOG = Logger.getLogger(EditorActionUtilities.class.getName()); + + private static Map> mimeType2actionName2KeyStroke; + + private static Map mimeType2ListenerPresent = new HashMap(); + + private static Reference globalKitRef; + + private static LookupListener globalKitListener; + + private static final Map kit2searchable = new WeakHashMap(); + + private EditorActionUtilities() { + // No instances + } + + public static EditorKit getGlobalKit() { + synchronized (kit2searchable) { + EditorKit globalKit = (globalKitRef != null) ? globalKitRef.get() : null; + if (globalKit == null) { + Lookup.Result result = MimeLookup.getLookup("").lookupResult(EditorKit.class); + Iterator instancesIterator = result.allInstances().iterator(); + globalKit = instancesIterator.hasNext() ? instancesIterator.next() : null; + if (globalKit != null) { + globalKitRef = new WeakReference(globalKit); + } + if (globalKitListener == null) { + globalKitListener = new LookupListener() { + public void resultChanged(LookupEvent evt) { + synchronized (kit2searchable) { + globalKitRef = null; + } + } + }; + result.addLookupListener(globalKitListener); + } + } + return globalKit; + } + } + + public static EditorKit getKit(String mimeType) { + Lookup.Result result = MimeLookup.getLookup(mimeType).lookupResult(EditorKit.class); + Iterator instancesIterator = result.allInstances().iterator(); + EditorKit kit = instancesIterator.hasNext() ? instancesIterator.next() : null; + return kit; + } + + public static void registerSearchableKit(EditorKit kit, SearchableEditorKit searchableKit) { + synchronized (kit2searchable) { + kit2searchable.put(kit, searchableKit); + } + } + + /** + * Get an editor action in a constant time (wrap a kit with a SearchableEditorKit if necessary). + * + * @param kit non-null kit. + * @param actionName non-null action name. + * @return action's instance or null. + */ + public static Action getAction(EditorKit kit, String actionName) { + return getSearchableKit(kit).getAction(actionName); + } + + /** + * Get searchable editor kit for the given kit. + * @param kit non-null kit. + * @return non-null searchable kit. + */ + public static SearchableEditorKit getSearchableKit(EditorKit kit) { + SearchableEditorKit searchableKit; + if (kit instanceof SearchableEditorKit) { + searchableKit = ((SearchableEditorKit)kit); + } else { + synchronized (kit2searchable) { + searchableKit = kit2searchable.get(kit); + if (searchableKit == null) { + searchableKit = new DefaultSearchableKit(kit); + registerSearchableKit(kit, searchableKit); + } + } + } + return searchableKit; + } + + public static Lookup.Result createActionsLookupResult(String mimeType) { + if (!MimePath.validate(mimeType)) { + throw new IllegalArgumentException("Ïnvalid mimeType=\"" + mimeType + "\""); + } + Lookup lookup = Lookups.forPath(getPath(mimeType, "Actions")); + return lookup.lookupResult(Action.class); + } + + private static String getPath(String mimeType, String subFolder) { + StringBuilder path = new StringBuilder(50); + path.append("Editors/"); + if (mimeType.length() > 0) { + path.append('/').append(mimeType); + } + if (subFolder.length() > 0) { + path.append('/').append(subFolder); + } + return path.toString(); + } + + public static Preferences getGlobalPreferences() { + Lookup globalMimeLookup = MimeLookup.getLookup(MimePath.EMPTY); + return (globalMimeLookup != null) ? globalMimeLookup.lookup(Preferences.class) : null; + } + + /** + * Get single-key accelerator for a given declared action. + * Only a single-key accelerators are supported. + */ + public static KeyStroke getAccelerator(FileObject fo) { + if (fo == null) { + throw new IllegalArgumentException("Must be called with non-null fileObject"); // NOI18N + } + boolean fineLoggable = LOG.isLoggable(Level.FINE); + String path = fo.getParent().getPath(); + String actionName = (String) fo.getAttribute(Action.NAME); + KeyStroke ks = null; + if (path.startsWith("Editors/")) { + path = path.substring(7); // Leave ending '/' to support "Editors/Actions" + if (path.endsWith("/Actions")) { + path = path.substring(0, path.length() - 8); + if (path.startsWith("/")) { + path = path.substring(1); + } + String mimeType = path; + if (!MimePath.validate(mimeType)) { + LOG.info("Invalid mime-type='" + mimeType + "' of action's fileObject=" + fo); // NOI18N + } + ks = getAccelerator(mimeType, actionName); + } else if (fineLoggable) { + LOG.fine("No \"/Actions\" at end of mime-type='" + path + + "' of action's fileObject=" + fo); // NOI18N + } + } else if (fineLoggable) { + LOG.fine("No \"Editors/\" at begining of mime-type='" + path + // NOI18N + "' of action's fileObject=" + fo); // NOI18N + } + + if (LOG.isLoggable(Level.FINER)) { + LOG.finer("Accelerator for action \"" + actionName + "\" is " + ks); + } + return ks; + } + + /** + * Get single-key accelerator for a given declared action. + *
+ * Unfortunately currently there's no easy way to display multi-keybinding in menu-item + * (there's just JMenuItem.setAccelerator() and its impl is L&F-based) + * so just display single-keystroke accelerators. + */ + public static KeyStroke getAccelerator(String mimeType, String actionName) { + KeyStroke ks = null; + if (actionName != null) { + synchronized (EditorActionUtilities.class) { + if (mimeType2actionName2KeyStroke == null) { + mimeType2actionName2KeyStroke = new HashMap>(); + } + Map actionName2KeyStrokeList = mimeType2actionName2KeyStroke.get(mimeType); + if (actionName2KeyStrokeList == null) { + actionName2KeyStrokeList = new HashMap(); + Lookup.Result result = MimeLookup.getLookup(mimeType).lookupResult( + KeyBindingSettings.class); + Collection instances = result.allInstances(); + if (!instances.isEmpty()) { + KeyBindingSettings kbs = instances.iterator().next(); + for (MultiKeyBinding kb : kbs.getKeyBindings()) { + if (!actionName2KeyStrokeList.containsKey(kb.getActionName()) + && kb.getKeyStrokeCount() == 1) + { + actionName2KeyStrokeList.put(kb.getActionName(), kb.getKeyStroke(0)); + } + } + } + mimeType2actionName2KeyStroke.put(mimeType, actionName2KeyStrokeList); + // Ensure listening on changes in keybinding settings + if (!Boolean.TRUE.equals(mimeType2ListenerPresent.get(mimeType))) { + mimeType2ListenerPresent.put(mimeType, true); + result.addLookupListener(KeyBindingSettingsListener.INSTANCE); + } + } + ks = actionName2KeyStrokeList.get(actionName); + } + } + return ks; + } + + private static final class KeyBindingSettingsListener implements LookupListener { + + static final KeyBindingSettingsListener INSTANCE = new KeyBindingSettingsListener(); + + private KeyBindingSettingsListener() { + } + + public void resultChanged(LookupEvent ev) { + synchronized (EditorActionUtilities.class) { + mimeType2actionName2KeyStroke = null; + LOG.fine("mimeType2actionName2KeyStroke cleared."); // NOI18N + } + } + + } + + private static final class DefaultSearchableKit implements SearchableEditorKit { + + private final Map> name2actionRef = new WeakHashMap>(); + + DefaultSearchableKit(EditorKit kit) { + for (Action action : kit.getActions()) { + if (action != null) { + name2actionRef.put((String)action.getValue(Action.NAME), new WeakReference(action)); + } + } + } + + public Action getAction(String actionName) { + Reference actionRef = name2actionRef.get(actionName); + return (actionRef != null) ? actionRef.get() : null; + } + + public void addActionsChangeListener(ChangeListener listener) { + } + + public void removeActionsChangeListener(ChangeListener listener) { + } + + + } + +} diff --git a/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/EditorActionsProvider.java b/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/EditorActionsProvider.java new file mode 100644 --- /dev/null +++ b/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/EditorActionsProvider.java @@ -0,0 +1,59 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ + +package org.netbeans.modules.editor.lib2.actions; + +import java.util.List; +import javax.swing.Action; + +/** + * Abstract class implemented by editor module providing editor actions. + * + * @author Vita Stejskal + * @since 1.39 + */ +public interface EditorActionsProvider { + + List getActionsOnly(); + + List getAllInstances(); + +} diff --git a/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/MacroRecording.java b/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/MacroRecording.java new file mode 100644 --- /dev/null +++ b/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/MacroRecording.java @@ -0,0 +1,171 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.editor.lib2.actions; + +import java.awt.event.ActionEvent; +import javax.swing.Action; +import javax.swing.text.DefaultEditorKit; + +/** + * Class handling macro recording of editor actions. + * + * @author Miloslav Metelka + * @since 1.13 + */ +public final class MacroRecording { + + /** Action's property for disabling recording of the action as part of a macro. */ + public static final String NO_MACRO_RECORDING_PROPERTY = "NoMacroRecording"; + + private static final MacroRecording INSTANCE = new MacroRecording(); + + public static MacroRecording get() { + return INSTANCE; + } + + private StringBuilder macroBuffer; + + private StringBuilder textBuffer; + + private MacroRecording() { + } + + /** + * Start recording a macro. + * + * @return true if macro recording started successfully or false otherwise. + */ + public synchronized boolean startRecording() { + if (isRecording()) { + return false; + } + macroBuffer = new StringBuilder(100); + textBuffer = new StringBuilder(20); + return true; + } + + /** + * Stop macro recording. + * + * @return string describing the macro or null if no recording takes place currently. + */ + public synchronized String stopRecording() { + if (!isRecording()) { + return null; + } + if (textBuffer.length() > 0) { + if (macroBuffer.length() > 0) { + macroBuffer.append( ' ' ); + } + appendEncodedText(macroBuffer, textBuffer); + } + String completeMacroText = macroBuffer.toString(); + textBuffer = null; + macroBuffer = null; + return completeMacroText; + } + + /** + * Record given action into a macro buffer. + * + * @param action non-null action to record + * @param evt non-null evt used when recording typed text of default key-typed action. + */ + public synchronized void recordAction(Action action, ActionEvent evt) { + if (isRecording() && !Boolean.TRUE.equals(action.getValue(NO_MACRO_RECORDING_PROPERTY))) { + String actionName = actionName(action); + if (DefaultEditorKit.defaultKeyTypedAction.equals(actionName)) { + textBuffer.append(getFilteredActionCommand(evt.getActionCommand())); + } else { + if (textBuffer.length() > 0) { + if (macroBuffer.length() > 0) { + macroBuffer.append( ' ' ); + } + appendEncodedText(macroBuffer, textBuffer); + textBuffer.setLength(0); + } + if (macroBuffer.length() > 0) { + macroBuffer.append(' '); + } + // Append encoded action name + for (int i = 0; i < actionName.length(); i++) { + char c = actionName.charAt(i); + if (Character.isWhitespace(c) || c == '\\') { + macroBuffer.append('\\'); + } + macroBuffer.append(c); + } + } + } + } + + private boolean isRecording() { + return (macroBuffer != null); + } + + private static String actionName(Action action) { + return (String) action.getValue(Action.NAME); + } + + private static String getFilteredActionCommand(String cmd) { + if (cmd == null || cmd.length() == 0) { + return ""; + } + char ch = cmd.charAt(0); + if ((ch >= 0x20) && (ch != 0x7F)) { + return cmd; + } else { + return ""; + } + } + + private static void appendEncodedText(StringBuilder sb, StringBuilder text) { + sb.append('"'); + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + if (c == '"' || c == '\\') { + sb.append('\\'); + } + sb.append(c); + } + sb.append('"'); + } + +} diff --git a/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/PresenterEditorAction.java b/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/PresenterEditorAction.java new file mode 100644 --- /dev/null +++ b/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/PresenterEditorAction.java @@ -0,0 +1,365 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.editor.lib2.actions; + +import org.netbeans.spi.editor.AbstractEditorAction; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.Action; +import javax.swing.JButton; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JMenuItem; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.plaf.TextUI; +import javax.swing.text.EditorKit; +import javax.swing.text.JTextComponent; +import javax.swing.text.TextAction; +import org.netbeans.api.editor.EditorRegistry; +import org.netbeans.api.editor.EditorUtilities; +import org.openide.awt.Actions; +import org.openide.util.actions.Presenter; + +/** + * Action that represents a named editor action in main menu, popup menu + * and editor toolbar. + *
+ * The actions are registered into "Editors/ActionPresenters" regardless + * of the mime-type for which the actions get created. + */ +public final class PresenterEditorAction extends TextAction + implements Presenter.Menu, Presenter.Popup, Presenter.Toolbar, PropertyChangeListener, ChangeListener +{ + + /** + * Boolean action property displayed by the checkbox menu item. + */ + private static final String SELECTED_KEY = "SwingSelectedKey"; // [TODO] Replace with "Action.SELECTED_KEY" on 1.6 + + + // -J-Dorg.netbeans.modules.editor.lib2.actions.PresenterEditorAction.level=FINEST + private static final Logger LOG = Logger.getLogger(PresenterEditorAction.class.getName()); + + private static final Map presenterAction2Name + = new WeakHashMap(); + + /** + * Currently active editor component's editor kit reference. + */ + private static Reference activeEditorKitRef; + + private static ChangeListener kitChangeListener = new ChangeListener() { + public void stateChanged(ChangeEvent evt) { + updateActions(null); + } + }; + + private static SearchableEditorKit activeKit() { + synchronized (PresenterEditorAction.class) { + return (activeEditorKitRef != null) ? activeEditorKitRef.get() : null; + } + } + + private static void updateActions(SearchableEditorKit kit) { + boolean changed = (activeEditorKitRef == null || kit != activeEditorKitRef.get()); + if (changed) { + activeEditorKitRef = new WeakReference(kit); + for (Map.Entry actionAndName : presenterAction2Name.entrySet()) { + PresenterEditorAction presenterAction = actionAndName.getKey(); + String actionName = actionAndName.getValue(); + // Clear ref to old action + presenterAction.clearDelegateActionRef(); + // Update to current delegate action (by using the given kit) + presenterAction.delegateAction(kit, actionName); + } + } + } + + private static final Action NULL_ACTION = new TextAction("null") { + public void actionPerformed(ActionEvent evt) { + } + }; + + private static final Reference NULL_ACTION_REF = new WeakReference(NULL_ACTION); + + static { + EditorRegistry.addPropertyChangeListener(new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent evt) { + if (EditorRegistry.FOCUS_GAINED_PROPERTY.equals(evt.getPropertyName())) { + JTextComponent focusedTextComponent = (JTextComponent) evt.getNewValue(); + TextUI ui = (focusedTextComponent != null) ? focusedTextComponent.getUI() : null; + EditorKit kit = (ui != null) + ? ui.getEditorKit(focusedTextComponent) + : EditorActionUtilities.getGlobalKit(); + if (kit != null) { + SearchableEditorKit searchableKit = EditorActionUtilities.getSearchableKit(kit); + updateActions(searchableKit); + } + } + } + }); + } + + public static Action create(Map attrs) { + String actionName = (String)attrs.get(Action.NAME); + if (actionName == null) { + throw new IllegalArgumentException("Null Action.NAME attribute for attrs: " + attrs); // NOI18N + } + return new PresenterEditorAction(actionName, attrs); + } + + /** + * Corresponding action's reference. + */ + private Reference delegateActionRef; + + private JMenuItem menuPresenter; + + private JMenuItem popupPresenter; + + private Component toolBarPresenter; + + private Map attrs; + + public PresenterEditorAction(String actionName, Map attrs) { + super(actionName); + this.attrs = attrs; + presenterAction2Name.put(this, actionName); + } + + public void actionPerformed(ActionEvent evt) { + // Find the right action for the corresponding editor kit + JTextComponent component = getTextComponent(evt); + if (component != null) { + TextUI ui = component.getUI(); + if (ui != null) { + EditorKit kit = ui.getEditorKit(component); + if (kit != null) { + String actionName = actionName(); + Action action = EditorUtilities.getAction(kit, actionName); + if (action != null) { + action.actionPerformed(evt); + } else { + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Action '" + actionName + "' not found in editor kit " + kit + '\n'); // NOI18N + } + } + } + } + } + } + + public JMenuItem getMenuPresenter() { + if (menuPresenter == null) { + menuPresenter = createMenuItem(false); + } + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("getMenuPresenter() for action=" + actionName() + " returns " + menuPresenter); // NOI18N + } + return menuPresenter; + } + + public JMenuItem getPopupPresenter() { + if (popupPresenter == null) { + popupPresenter = createMenuItem(true); + } + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("getPopupPresenter() for action=" + actionName() + " returns " + popupPresenter); // NOI18N + } + return popupPresenter; + } + + public Component getToolbarPresenter() { + if (toolBarPresenter == null) { + toolBarPresenter = new JButton(this); + } + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("getToolbarPresenter() for action=" + actionName() + " returns " + toolBarPresenter); // NOI18N + } + return toolBarPresenter; + } + + @Override + public Object getValue(String key) { + Object value = super.getValue(key); + if (value == null) { + if (!"instanceCreate".equals(key)) { // Return null for this key + value = attrs.get(key); + if (value == null) { + Action delegateAction = delegateAction(); + if (delegateAction != null) { + value = delegateAction.getValue(key); + } + } + } + } + return value; + } + + public void propertyChange(PropertyChangeEvent evt) { + String propertyName = evt.getPropertyName(); + if (SELECTED_KEY.equals(propertyName)) { + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("propertyChange() of SELECTED_KEY for action " + actionName()); + } + updateSelected(); + } + } + + private void updateSelected() { + if (isCheckBox()) { + boolean selected = isSelected(); + if (menuPresenter instanceof JCheckBoxMenuItem) { + ((JCheckBoxMenuItem)menuPresenter).setSelected(selected); + } + if (popupPresenter instanceof JCheckBoxMenuItem) { + ((JCheckBoxMenuItem)popupPresenter).setSelected(selected); + } + } + } + + public void stateChanged(ChangeEvent evt) { + clearDelegateActionRef(); + } + + private boolean isSelected() { + Action action = delegateAction(); + boolean selected = (action != null) && Boolean.TRUE.equals(action.getValue(SELECTED_KEY)); + return selected; + } + + private JMenuItem createMenuItem(boolean isPopup) { + final JMenuItem menuItem; + if (isCheckBox()) { + menuItem = new JCheckBoxMenuItem(); + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Create checkbox menu item for action " + actionName() + ", selected=" + isSelected()); + } + menuItem.setSelected(isSelected()); + menuItem.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent evt) { + boolean checkboxSelected = ((JCheckBoxMenuItem)evt.getSource()).isSelected(); + boolean actionSelected = isSelected(); + if (checkboxSelected != actionSelected) { + Action delegateAction = delegateAction(); + if (delegateAction != null) { + delegateAction.putValue(SELECTED_KEY, checkboxSelected); + } + } + } + }); + + } else { // Regular menu item + menuItem = new JMenuItem(); + } + Actions.connect(menuItem, this, isPopup); + return menuItem; + } + + private boolean isCheckBox() { + String presenterType = (String) getValue("PresenterType"); + return "CheckBox".equals(presenterType); + } + + String actionName() { + return (String) getValue(Action.NAME); // should be non-null (check by constructor) + } + + Action delegateAction() { + return delegateAction(null, null); + } + + Action delegateAction(SearchableEditorKit searchableKit, String actionName) { + synchronized (this) { + if (delegateActionRef == null) { + if (actionName == null) { + actionName = actionName(); + } + if (searchableKit == null) { + EditorKit globalKit = EditorActionUtilities.getGlobalKit(); + searchableKit = (globalKit != null) ? EditorActionUtilities.getSearchableKit(globalKit) : null; + if (searchableKit == null) { + return null; + } + } + Action delegateAction = searchableKit.getAction(actionName); + if (delegateAction != null) { + delegateActionRef = new WeakReference(delegateAction); + delegateAction.addPropertyChangeListener(this); + setEnabled(delegateAction.isEnabled()); + } else { + delegateActionRef = NULL_ACTION_REF; + setEnabled(false); + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Action '" + actionName + "' not found in global editor kit " + searchableKit + '\n'); // NOI18N + } + } + updateSelected(); + } + return (delegateActionRef != NULL_ACTION_REF) + ? delegateActionRef.get() + : null; + } + } + + private void clearDelegateActionRef() { + synchronized (this) { + if (delegateActionRef != null && delegateActionRef != NULL_ACTION_REF) { + Action oldDelegateAction = delegateActionRef.get(); + if (oldDelegateAction != null) { + oldDelegateAction.removePropertyChangeListener(this); + } + } + delegateActionRef = null; + } + + } + +} diff --git a/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/SearchableEditorKit.java b/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/SearchableEditorKit.java new file mode 100644 --- /dev/null +++ b/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/SearchableEditorKit.java @@ -0,0 +1,73 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ + +package org.netbeans.modules.editor.lib2.actions; + +import javax.swing.Action; +import javax.swing.event.ChangeListener; + +/** + * This interface should be implemented by editor kits that hold their actions + * in a map. They may also notify + * + * @since 1.13 + */ +public interface SearchableEditorKit { + + /** + * Find action with the given name. + * + * @param actionName non-null action's name. + * @return action's instance or null if an action with the given name does not exist. + */ + Action getAction(String actionName); + + /** + * Add listener for notifications about any change in a set of actions + * maintained by this editor kit. + * + * @param listener non-null listener to be added. + */ + void addActionsChangeListener(ChangeListener listener); + + void removeActionsChangeListener(ChangeListener listener); + +} diff --git a/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/SearchableEditorKitImpl.java b/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/SearchableEditorKitImpl.java new file mode 100644 --- /dev/null +++ b/editor.lib2/src/org/netbeans/modules/editor/lib2/actions/SearchableEditorKitImpl.java @@ -0,0 +1,151 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ + +package org.netbeans.modules.editor.lib2.actions; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.Action; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.text.DefaultEditorKit; +import javax.swing.text.EditorKit; +import org.netbeans.lib.editor.util.ListenerList; +import org.openide.util.Lookup; +import org.openide.util.LookupEvent; +import org.openide.util.LookupListener; + +/** + * This interface should be implemented by editor kits that hold their actions + * in a map. They may also notify + * + * @since 1.13 + */ +public final class SearchableEditorKitImpl extends DefaultEditorKit implements SearchableEditorKit { + + // -J-Dorg.netbeans.modules.editor.lib2.actions.SearchableEditorKitImpl.level=FINEST + private static final Logger LOG = Logger.getLogger(SearchableEditorKitImpl.class.getName()); + + public static EditorKit createGlobalKit() { + return new SearchableEditorKitImpl(""); + } + + private final String mimeType; + + private final Map name2Action = new HashMap(); + + private Action[] actions; + + private LookupListener actionsListener; + + private ListenerList listenerList = new ListenerList(); + + private SearchableEditorKitImpl(String mimeType) { + this.mimeType = mimeType; + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("SearchableEditorKitImpl created for \"" + mimeType + "\"\n"); // NOI18N + } + updateActions(); + } + + public Action getAction(String actionName) { + synchronized (name2Action) { + return name2Action.get(actionName); + } + } + + private void updateActions() { + synchronized (name2Action) { + // Fill up the actions from layer + Lookup.Result actionsResult = EditorActionUtilities.createActionsLookupResult(mimeType); + Collection actionColl = actionsResult.allInstances(); + actions = new Action[actionColl.size()]; + actionColl.toArray(actions); + name2Action.clear(); + for (Action action : actions) { + String actionName; + if (action != null && (actionName = (String) action.getValue(Action.NAME)) != null) { + name2Action.put(actionName, action); + if (LOG.isLoggable(Level.FINER)) { + LOG.finer("Mime-type: \"" + mimeType + "\", registerAction(\"" + actionName + // NOI18N + "\", " + action + ")\n"); // NOI18N + } + } + } + + if (actionsListener == null) { + actionsListener = new LookupListener() { + public void resultChanged(LookupEvent ev) { + updateActions(); + } + }; + actionsResult.addLookupListener(actionsListener); + } + } + + // Fire change listeners + fireActionsChange(); + } + + @Override + public String getContentType() { + return mimeType; + } + + public void addActionsChangeListener(ChangeListener listener) { + listenerList.add(listener); + } + + public void removeActionsChangeListener(ChangeListener listener) { + listenerList.remove(listener); + } + + private void fireActionsChange() { + ChangeEvent evt = new ChangeEvent(this); + for (ChangeListener listener : listenerList.getListeners()) { + listener.stateChanged(evt); + } + } + +} diff --git a/editor.lib2/src/org/netbeans/modules/editor/lib2/resources/defaultglyph.gif b/editor.lib2/src/org/netbeans/modules/editor/lib2/resources/defaultglyph.gif new file mode 100644 index 0000000000000000000000000000000000000000..d7742546928aa53e8b4d835ef0600d4481879216 GIT binary patch literal 102 zc${ + + + + diff --git a/editor.lib2/src/org/netbeans/spi/editor/AbstractEditorAction.java b/editor.lib2/src/org/netbeans/spi/editor/AbstractEditorAction.java new file mode 100644 --- /dev/null +++ b/editor.lib2/src/org/netbeans/spi/editor/AbstractEditorAction.java @@ -0,0 +1,207 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.netbeans.spi.editor; + +import org.netbeans.modules.editor.lib2.actions.*; +import java.awt.event.ActionEvent; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import javax.swing.Action; +import javax.swing.text.Caret; +import javax.swing.text.JTextComponent; +import javax.swing.text.TextAction; +import org.openide.util.NbBundle; +import org.openide.util.RequestProcessor; + +/** + * Base class for editor actions. + * + * @author Miloslav Metelka + * @since 1.14 + */ +public abstract class AbstractEditorAction extends TextAction { + + /** Logger for reporting invoked actions */ + private static Logger UILOG = Logger.getLogger("org.netbeans.ui.actions.editor"); // NOI18N + + private static final long serialVersionUID = 1L; // Serialization no longer used (prevent warning) + + private final Map attrs; + + /** + * Constructor that should be used when a descendant requests a direct action's instantiation. + * When annotated with @EditorActionRegistration the infrastructure + * will pass action's properties from a generated layer to the action. + * + * @param attrs non-null attributes that hold action's properties. + */ + protected AbstractEditorAction(Map attrs) { + super(null); + this.attrs = attrs; + + if (attrs != null) { + String actionName = (String)attrs.get(Action.NAME); + if (actionName == null) { + throw new IllegalArgumentException("Null Action.NAME attribute for action " + this.getClass()); // NOI18N + } + putValue(Action.NAME, actionName); + } + } + + /** + * Constructor for a regular registration with @EditorActionRegistration + * or for an explicit instantiation when no extra arguments need to be passed + * to the action. + */ + protected AbstractEditorAction() { + this(null); + } + + /** + * Implementation of the action must be defined by descendants. + * + * @param evt non-null event + * @param component "active" text component obtained by {@link TextAction#getFocusedComponent()}. + */ + public abstract void actionPerformed(ActionEvent evt, JTextComponent component); + + /** + * Called by {@link #putValue(String,String)} when {@link Action#NAME} property + * is set to a non-null String value. This allows a "polymorphic" action (with + * Action.NAME-specific behavior) to update certain properties (e.g. an icon) + * according to the name that was set. + * + * @param actionName non-null action's name (value of Action.NAME property). + */ + protected void actionNameUpdate(String actionName) { + } + + /** + * Possibly allow asynchronous execution of the action by returning true. + * @return false (by default) or true to allow asynchronous execution. + */ + protected boolean asynchronous() { + return false; + } + + /** + * @return value of Action.NAME property. + */ + protected final String actionName() { + return (String) getValue(Action.NAME); + } + + + /** + * Reset caret's magic position. + * @param component target text component. + */ + protected final void resetCaretMagicPosition(JTextComponent component) { + Caret caret; + if (component != null && (caret = component.getCaret()) != null) { + caret.setMagicCaretPosition(null); + } + } + + @Override + public final void actionPerformed(final ActionEvent evt) { + final JTextComponent component = getTextComponent(evt); + MacroRecording.get().recordAction(this, evt); // Possibly record action in a currently recorded macro + + if (UILOG.isLoggable(Level.FINE)) { + // TODO [Mila] - Set action's property to disable UI logging + String actionNameLowerCase = actionName(); + if (actionNameLowerCase != null && + !"default-typed".equals(actionNameLowerCase) && //NOI18N + -1 == actionNameLowerCase.indexOf("caret") && //NOI18N + -1 == actionNameLowerCase.indexOf("delete") && //NOI18N + -1 == actionNameLowerCase.indexOf("selection") && //NOI18N + -1 == actionNameLowerCase.indexOf("build-tool-tip") &&//NOI18N + -1 == actionNameLowerCase.indexOf("build-popup-menu") &&//NOI18N + -1 == actionNameLowerCase.indexOf("page-up") &&//NOI18N + -1 == actionNameLowerCase.indexOf("page-down") &&//NOI18N + -1 == actionNameLowerCase.indexOf("-kit-install") //NOI18N + ) { + LogRecord r = new LogRecord(Level.FINE, "UI_ACTION_EDITOR"); // NOI18N + r.setResourceBundle(NbBundle.getBundle(AbstractEditorAction.class)); + if (evt != null) { + r.setParameters(new Object[] { evt, evt.toString(), this, toString(), getValue(NAME) }); + } else { + r.setParameters(new Object[] { "no-ActionEvent", "no-ActionEvent", this, toString(), getValue(NAME) }); //NOI18N + } + r.setLoggerName(UILOG.getName()); + UILOG.log(r); + } + } + + if (asynchronous()) { + RequestProcessor.getDefault().post(new Runnable () { + public void run() { + actionPerformed(evt, component); + } + }); + } else { + actionPerformed(evt, component); + } + } + + @Override + public Object getValue(String key) { + Object value = super.getValue(key); + if (value == null && attrs != null) { + if (!"instanceCreate".equals(key)) { // Return null for this key + value = attrs.get(key); + } + } + return value; + } + + @Override + public void putValue(String key, Object value) { + super.putValue(key, value); + if (Action.NAME.equals(key) && value instanceof String) { + actionNameUpdate((String)value); + } + } + + +} diff --git a/editor.lib2/src/org/netbeans/spi/editor/Bundle.properties b/editor.lib2/src/org/netbeans/spi/editor/Bundle.properties new file mode 100644 --- /dev/null +++ b/editor.lib2/src/org/netbeans/spi/editor/Bundle.properties @@ -0,0 +1,51 @@ +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. +# +# The contents of this file are subject to the terms of either the GNU +# General Public License Version 2 only ("GPL") or the Common +# Development and Distribution License("CDDL") (collectively, the +# "License"). You may not use this file except in compliance with the +# License. You can obtain a copy of the License at +# http://www.netbeans.org/cddl-gplv2.html +# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the +# specific language governing permissions and limitations under the +# License. When distributing the software, include this License Header +# Notice in each file and include the License file at +# nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the GPL Version 2 section of the License file that +# accompanied this code. If applicable, add the following below the +# License Header, with the fields enclosed by brackets [] replaced by +# your own identifying information: +# "Portions Copyrighted [year] [name of copyright owner]" +# +# Contributor(s): +# +# The Original Software is NetBeans. The Initial Developer of the Original +# Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun +# Microsystems, Inc. All Rights Reserved. +# +# If you wish your version of this file to be governed by only the CDDL +# or only the GPL Version 2, indicate your decision by adding +# "[Contributor] elects to include this software in this distribution +# under the [CDDL or GPL Version 2] license." If you do not indicate a +# single choice of license, a recipient has the option to distribute +# your version of this file under either the CDDL, the GPL Version 2 or +# to extend the choice of license to its licensees as provided above. +# However, if you add GPL Version 2 code and therefore, elected the GPL +# Version 2 license, then the option applies only if the new code is +# made subject to such option by the copyright holder. + +# +# UI Logging +# +# UI logging of button press +# {0} instance of the button +# {1} class of the button +# {2} instance of the action +# {3} class of the action +# {4} display name of the action +UI_ACTION_EDITOR=Invoking {4} implemented as {3} thru {1} +UI_ACTION_EDITOR_ICON_BASE=org/netbeans/modules/editor/lib2/resources/defaultglyph.gif + diff --git a/editor.lib2/test/unit/src/org/netbeans/api/editor/EditorUtilitiesTest.java b/editor.lib2/test/unit/src/org/netbeans/api/editor/EditorUtilitiesTest.java new file mode 100644 --- /dev/null +++ b/editor.lib2/test/unit/src/org/netbeans/api/editor/EditorUtilitiesTest.java @@ -0,0 +1,71 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2009 Sun Microsystems, Inc. + */ + +package org.netbeans.api.editor; + +import javax.swing.Action; +import javax.swing.text.DefaultEditorKit; +import javax.swing.text.EditorKit; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author mmetelka + */ +public class EditorUtilitiesTest { + + public EditorUtilitiesTest() { + } + + @Test + public void testGetAction() throws Exception { + EditorKit editorKit = new DefaultEditorKit(); + String actionName = DefaultEditorKit.backwardAction; + Action result = EditorUtilities.getAction(editorKit, actionName); + for (Action expected : editorKit.getActions()) { + if (actionName.equals(expected.getValue(Action.NAME))) { + assertEquals(expected, result); + return; + } + } + fail("Action " + actionName + " not found."); + } + +} \ No newline at end of file diff --git a/editor.lib2/test/unit/src/org/netbeans/modules/editor/lib2/actions/MacroRecordingTest.java b/editor.lib2/test/unit/src/org/netbeans/modules/editor/lib2/actions/MacroRecordingTest.java new file mode 100644 --- /dev/null +++ b/editor.lib2/test/unit/src/org/netbeans/modules/editor/lib2/actions/MacroRecordingTest.java @@ -0,0 +1,103 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2009 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.editor.lib2.actions; + +import java.awt.event.ActionEvent; +import javax.swing.Action; +import javax.swing.JEditorPane; +import javax.swing.text.TextAction; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author mmetelka + */ +public class MacroRecordingTest { + + static final String TEST_ACTION_NAME = "test-action"; + + public MacroRecordingTest() { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + @Test + public void testGet() { + MacroRecording macroRecording = MacroRecording.get(); + assertNotNull(macroRecording); + } + + @Test + public void testRecording() { + MacroRecording macroRecording = MacroRecording.get(); + assertNotNull(macroRecording.startRecording()); + macroRecording.recordAction(new MacroRecordingTestAction(), new ActionEvent(new JEditorPane(), 0, "")); + String text = macroRecording.stopRecording(); + assertEquals(TEST_ACTION_NAME, text); + } + + @Test + public void testStopRecording() { + MacroRecording macroRecording = MacroRecording.get(); + assertNull(macroRecording.stopRecording()); + } + + private static final class MacroRecordingTestAction extends TextAction { + + MacroRecordingTestAction() { + super(TEST_ACTION_NAME); + } + + public void actionPerformed(ActionEvent arg0) { + // do nothing + } + + } + +} \ No newline at end of file diff --git a/editor/src/org/netbeans/modules/editor/ExportHtmlAction.java b/editor/src/org/netbeans/modules/editor/ExportHtmlAction.java --- a/editor/src/org/netbeans/modules/editor/ExportHtmlAction.java +++ b/editor/src/org/netbeans/modules/editor/ExportHtmlAction.java @@ -73,7 +73,7 @@ import org.netbeans.api.editor.settings.FontColorNames; import org.netbeans.api.editor.settings.FontColorSettings; import org.netbeans.api.editor.settings.SimpleValueNames; -import org.netbeans.modules.editor.lib.EditorPreferencesDefaults; +import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; import org.openide.awt.Mnemonics; public class ExportHtmlAction extends CookieAction { diff --git a/editor/src/org/netbeans/modules/editor/MainMenuAction.java b/editor/src/org/netbeans/modules/editor/MainMenuAction.java --- a/editor/src/org/netbeans/modules/editor/MainMenuAction.java +++ b/editor/src/org/netbeans/modules/editor/MainMenuAction.java @@ -64,7 +64,7 @@ import org.netbeans.editor.Utilities; import org.netbeans.editor.ext.ExtKit; import org.netbeans.lib.editor.util.swing.DocumentUtilities; -import org.netbeans.modules.editor.lib.EditorPreferencesDefaults; +import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; import org.openide.awt.Mnemonics; import org.openide.util.HelpCtx; import org.openide.util.ImageUtilities; diff --git a/editor/src/org/netbeans/modules/editor/NbCodeFoldingAction.java b/editor/src/org/netbeans/modules/editor/NbCodeFoldingAction.java --- a/editor/src/org/netbeans/modules/editor/NbCodeFoldingAction.java +++ b/editor/src/org/netbeans/modules/editor/NbCodeFoldingAction.java @@ -58,7 +58,7 @@ import org.netbeans.editor.BaseKit; import org.netbeans.editor.Utilities; import org.netbeans.lib.editor.util.swing.DocumentUtilities; -import org.netbeans.modules.editor.lib.EditorPreferencesDefaults; +import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; import org.openide.awt.DynamicMenuContent; diff --git a/editor/src/org/netbeans/modules/editor/NbEditorKit.java b/editor/src/org/netbeans/modules/editor/NbEditorKit.java --- a/editor/src/org/netbeans/modules/editor/NbEditorKit.java +++ b/editor/src/org/netbeans/modules/editor/NbEditorKit.java @@ -105,8 +105,8 @@ import org.netbeans.modules.editor.impl.ToolbarActionsProvider; import org.netbeans.modules.editor.impl.actions.NavigationHistoryBackAction; import org.netbeans.modules.editor.impl.actions.NavigationHistoryForwardAction; +import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; import org.netbeans.modules.editor.lib.ColoringMap; -import org.netbeans.modules.editor.lib.EditorPreferencesDefaults; import org.netbeans.modules.editor.options.AnnotationTypesFolder; import org.openide.awt.Mnemonics; import org.openide.filesystems.FileObject; @@ -200,36 +200,22 @@ new NavigationHistoryForwardAction(), new SearchBar.IncrementalSearchForwardAction(), new SearchBar.IncrementalSearchBackwardAction(), - new ToggleToolbarAction(), - new NbToggleLineNumbersAction(), +// new ToggleToolbarAction(), +// new NbToggleLineNumbersAction(), new NbGenerateGoToPopupAction(), }; return TextAction.augmentList(super.createActions(), nbEditorActions); } - protected @Override Action[] getCustomActions() { - List actions = EditorActionsProvider.getEditorActions(getContentType()); - - if (LOG.isLoggable(Level.FINE)) { - LOG.fine("Custom layer actions for '" + getContentType() + "' {"); //NOI18N - for(Action a : actions) { - LOG.fine(" " + a); //NOI18N - } - LOG.fine("} End of custom layer actions for '" + getContentType() + "'"); //NOI18N - } - - if (!actions.isEmpty()) { - Action [] superActions = super.getCustomActions(); - if (superActions == null || superActions.length == 0) { - return actions.toArray(new Action[actions.size()]); - } else { - return TextAction.augmentList(superActions, actions.toArray(new Action[actions.size()])); - } - } else { - return super.getCustomActions(); - } + @Override + protected Action[] getDeclaredActions() { + List declaredActionList = EditorActionsProvider.getEditorActions(getContentType()); + Action[] declaredActions = new Action[declaredActionList.size()]; + declaredActionList.toArray(declaredActions); + return declaredActions; } - + + protected void addSystemActionMapping(String editorActionName, Class systemActionClass) { Action a = getActionByName(editorActionName); if (a != null) { @@ -283,7 +269,10 @@ } return bundle; } - + + /** + * @deprecated this action is no longer used. It is reimplemented in editor.actions module. + */ //@EditorActionRegistration(name = toggleToolbarAction) // Registration in createActions() due to getPopupMenuItem() public static class ToggleToolbarAction extends BaseAction { @@ -576,10 +565,13 @@ } - /** Switch visibility of line numbers in editor */ + /** + * Switch visibility of line numbers in editor. + * @deprecated this action is no longer used. It is reimplemented in editor.actions module. + */ //@EditorActionRegistration(name = BaseKit.toggleLineNumbersAction) // Registration in createActions() due to getPopupMenuItem() in predecessor - public static class NbToggleLineNumbersAction extends ActionFactory.ToggleLineNumbersAction { + public static final class NbToggleLineNumbersAction extends ActionFactory.ToggleLineNumbersAction { public NbToggleLineNumbersAction() { } diff --git a/editor/src/org/netbeans/modules/editor/NbEditorToolBar.java b/editor/src/org/netbeans/modules/editor/NbEditorToolBar.java --- a/editor/src/org/netbeans/modules/editor/NbEditorToolBar.java +++ b/editor/src/org/netbeans/modules/editor/NbEditorToolBar.java @@ -90,7 +90,7 @@ import org.netbeans.editor.Utilities; import org.netbeans.lib.editor.util.swing.DocumentUtilities; import org.netbeans.modules.editor.impl.ToolbarActionsProvider; -import org.netbeans.modules.editor.lib.EditorPreferencesDefaults; +import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; import org.openide.filesystems.FileChangeAdapter; import org.openide.filesystems.FileChangeListener; import org.openide.filesystems.FileEvent; diff --git a/editor/src/org/netbeans/modules/editor/NbEditorUI.java b/editor/src/org/netbeans/modules/editor/NbEditorUI.java --- a/editor/src/org/netbeans/modules/editor/NbEditorUI.java +++ b/editor/src/org/netbeans/modules/editor/NbEditorUI.java @@ -86,7 +86,7 @@ import org.netbeans.modules.editor.impl.SearchBar; import org.netbeans.modules.editor.impl.StatusLineFactories; import org.netbeans.modules.editor.indent.spi.CodeStylePreferences; -import org.netbeans.modules.editor.lib.EditorPreferencesDefaults; +import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; import org.openide.text.CloneableEditorSupport; import org.openide.util.ContextAwareAction; import org.openide.util.Lookup; diff --git a/editor/src/org/netbeans/modules/editor/resources/layer.xml b/editor/src/org/netbeans/modules/editor/resources/layer.xml --- a/editor/src/org/netbeans/modules/editor/resources/layer.xml +++ b/editor/src/org/netbeans/modules/editor/resources/layer.xml @@ -105,12 +105,6 @@ - - - - - - @@ -118,10 +112,6 @@ - - - - diff --git a/editor/test/qa-functional/src/org/netbeans/test/editor/popup/MainMenuTest.java b/editor/test/qa-functional/src/org/netbeans/test/editor/popup/MainMenuTest.java --- a/editor/test/qa-functional/src/org/netbeans/test/editor/popup/MainMenuTest.java +++ b/editor/test/qa-functional/src/org/netbeans/test/editor/popup/MainMenuTest.java @@ -53,7 +53,7 @@ import org.netbeans.jemmy.operators.JEditorPaneOperator; import org.netbeans.jemmy.operators.JTextComponentOperator; import org.netbeans.junit.NbModuleSuite; -import org.netbeans.modules.editor.lib.EditorPreferencesDefaults; +import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults; /** * Test behavior of main menus - Edit, View diff --git a/nbbuild/cluster.properties b/nbbuild/cluster.properties --- a/nbbuild/cluster.properties +++ b/nbbuild/cluster.properties @@ -291,6 +291,7 @@ defaults,\ diff,\ editor,\ + editor.actions,\ editor.bookmarks,\ editor.bracesmatching,\ editor.codetemplates,\ diff --git a/openide.awt/apichanges.xml b/openide.awt/apichanges.xml --- a/openide.awt/apichanges.xml +++ b/openide.awt/apichanges.xml @@ -47,6 +47,33 @@ AWT API + + + AwlaysEnabledAction can represent a boolean key in Preferences. + + + + + + + alwaysEnabledAction + was enhanced to understand + <attr name="PreferencesKey" stringvalue="boolean-key-name"/> + together with <attr name="PreferencesNode" stringvalue="prefstype:/nodepath"/> + where prefstype can be +
    +
  • "system" for Preferences.systemRoot()
  • +
  • "user" for Preferences.userRoot()
  • +
  • "nb" for NbPreferences.root()
  • +
+ or the property value can be Preferences instance or there can be lookup + with the Preferences instance in it. + The action will be represented by check box menu item in both menu and popup menu automatically. + +
+ + +
URLDisplayer can show URLs in an external browser even diff --git a/openide.awt/nbproject/project.properties b/openide.awt/nbproject/project.properties --- a/openide.awt/nbproject/project.properties +++ b/openide.awt/nbproject/project.properties @@ -44,4 +44,4 @@ javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml -spec.version.base=7.14.0 +spec.version.base=7.15.0 diff --git a/openide.awt/src/org/openide/awt/Actions.java b/openide.awt/src/org/openide/awt/Actions.java --- a/openide.awt/src/org/openide/awt/Actions.java +++ b/openide.awt/src/org/openide/awt/Actions.java @@ -367,7 +367,7 @@ } // for use from layers static Action alwaysEnabled(Map map) { - return new AlwaysEnabledAction(map); + return AlwaysEnabledAction.create(map); } /** Creates new "callback" action. Such action has an assigned key diff --git a/openide.awt/src/org/openide/awt/AlwaysEnabledAction.java b/openide.awt/src/org/openide/awt/AlwaysEnabledAction.java --- a/openide.awt/src/org/openide/awt/AlwaysEnabledAction.java +++ b/openide.awt/src/org/openide/awt/AlwaysEnabledAction.java @@ -8,32 +8,75 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.net.URL; +import java.util.Collection; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.prefs.BackingStoreException; +import java.util.prefs.PreferenceChangeEvent; +import java.util.prefs.PreferenceChangeListener; +import java.util.prefs.Preferences; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.Icon; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JMenuItem; +import javax.swing.KeyStroke; +import javax.swing.text.Keymap; import org.netbeans.modules.openide.util.ActionsBridge; import org.netbeans.modules.openide.util.ActionsBridge.ActionRunnable; import org.openide.util.ContextAwareAction; +import org.openide.util.Exceptions; import org.openide.util.ImageUtilities; import org.openide.util.Lookup; +import org.openide.util.LookupEvent; +import org.openide.util.LookupListener; +import org.openide.util.NbPreferences; +import org.openide.util.actions.Presenter; /** Lazily initialized always enabled action * * @author Jaroslav Tulach */ -final class AlwaysEnabledAction extends AbstractAction +class AlwaysEnabledAction extends AbstractAction implements PropertyChangeListener, ContextAwareAction { // -J-Dorg.openide.awt.AlwaysEnabledAction.level=FINE private static final Logger LOG = Logger.getLogger(AlwaysEnabledAction.class.getName()); - private final Map map; - private ActionListener delegate; - private final Lookup context; - private final Object equals; + /** + * Action property for key in {@link java.util.prefs.Preferences}. + * + * String key = (String) action.getValue("PreferencesKey"); + * boolean selected = preferencesNode.getBoolean(key, false); + * + */ + private static final String PREFERENCES_KEY = "PreferencesKey"; // NOI18N + + /** + * Action property for retrieving {@link java.util.prefs.Preferences} node. + * Its value can be a String: + *
    + *
  • "system:/path" for {@link java.util.prefs.Preferences#systemRoot() }.
  • + *
  • "user:/key" for {@link java.util.prefs.Preferences#userRoot() }.
  • + *
  • "nb:/key" for {@link org.openide.util.NbPreferences#root() }.
  • + *
+ * or a method value returning one of the following: + *
    + *
  • {@link java.util.prefs.Preferences } instance.
  • + *
  • {@link org.openide.util.Lookup }.
  • + *
+ */ + private static final String PREFERENCES_NODE = "PreferencesNode"; // NOI18N + + static AlwaysEnabledAction create(Map m) { + return (m.containsKey(PREFERENCES_KEY)) ? new CheckBox(m) : new AlwaysEnabledAction(m); + } + + final Map map; + ActionListener delegate; + final Lookup context; + final Object equals; public AlwaysEnabledAction(Map m) { super(); @@ -42,7 +85,7 @@ this.equals = this; } - private AlwaysEnabledAction(Map m, ActionListener delegate, Lookup context, Object equals) { + AlwaysEnabledAction(Map m, ActionListener delegate, Lookup context, Object equals) { super(); this.map = m; this.delegate = bindToContext(delegate, context); @@ -184,6 +227,17 @@ if ("noIconInMenu".equals(name)) { // NOI18N return fo == null ? null : fo.get("noIconInMenu"); // NOI18N } + if (Action.ACCELERATOR_KEY.equals(name)) { + Object value = fo.get(Action.ACCELERATOR_KEY); + if (value == null) { + Keymap map = Lookup.getDefault().lookup(Keymap.class); + if (map != null) { + KeyStroke[] arr = map.getKeyStrokesForAction(action); + value = arr.length > 0 ? arr[0] : null; + } + } + return value; + } // Delegate query to other properties to "fo" ignoring special properties if (!"delegate".equals(name) && !"instanceCreate".equals(name)) { return fo == null ? null : fo.get(name); @@ -233,4 +287,165 @@ public Action createContextAwareInstance(Lookup actionContext) { return new AlwaysEnabledAction(map, delegate, actionContext, equals); } + + static final class CheckBox extends AlwaysEnabledAction + implements Presenter.Menu, Presenter.Popup, PreferenceChangeListener, LookupListener + { + + private static final long serialVersionUID = 1L; + + private JCheckBoxMenuItem menuItem; + + private JCheckBoxMenuItem popupItem; + + private Preferences preferencesNode; + + private Lookup.Result preferencesNodeResult; + + private boolean prefsListening; + + CheckBox(Map m) { + super(m); + } + + CheckBox(Map m, ActionListener delegate, Lookup context, Object equals) { + super(m, delegate, context, equals); + } + + @Override + public void actionPerformed(ActionEvent e) { + // Toggle state in preferences + togglePreferncesSelected(); + + super.actionPerformed(e); + } + + public JMenuItem getMenuPresenter() { + if (menuItem == null) { + menuItem = new JCheckBoxMenuItem(); + menuItem.setSelected(isPreferencesSelected()); + Actions.connect(menuItem, this, false); + } + return menuItem; + } + + public JMenuItem getPopupPresenter() { + if (popupItem == null) { + popupItem = new JCheckBoxMenuItem(); + popupItem.setSelected(isPreferencesSelected()); + Actions.connect(popupItem, this, true); + } + return popupItem; + } + + public void preferenceChange(PreferenceChangeEvent pce) { + updateItemsSelected(); + } + + @Override + public Action createContextAwareInstance(Lookup actionContext) { + return new CheckBox(map, delegate, actionContext, equals); + } + + private boolean isPreferencesSelected() { + String key = (String) getValue(PREFERENCES_KEY); + Preferences prefs = prefs(); + boolean value; + if (key != null && prefs != null) { + value = prefs.getBoolean(key, false); + synchronized (this) { + if (!prefsListening) { + prefsListening = true; + prefs.addPreferenceChangeListener(this); + } + } + } else { + value = false; + } + return value; + } + + private void updateItemsSelected() { + boolean selected = isPreferencesSelected(); + if (menuItem != null) { + menuItem.setSelected(selected); + } + if (popupItem != null) { + popupItem.setSelected(selected); + } + } + + private synchronized Preferences prefs() { + if (preferencesNode == null) { + Object prefsNodeOrLookup = getValue(PREFERENCES_NODE); + if (prefsNodeOrLookup instanceof String) { + String nodeName = (String) prefsNodeOrLookup; + if (nodeName.startsWith("system:")) { + preferencesNode = Preferences.systemRoot(); + if (preferencesNode != null) { + nodeName = nodeName.substring("system:".length()); + try { + preferencesNode = preferencesNode.nodeExists(nodeName) ? preferencesNode.node(nodeName) : null; + } catch (BackingStoreException ex) { + preferencesNode = null; + } + } + } else if (nodeName.startsWith("user:")) { + preferencesNode = Preferences.userRoot(); + if (preferencesNode != null) { + nodeName = nodeName.substring("user:".length()); + try { + preferencesNode = preferencesNode.nodeExists(nodeName) ? preferencesNode.node(nodeName) : null; + } catch (BackingStoreException ex) { + preferencesNode = null; + } + } + } else if (nodeName.startsWith("nb:")) { + preferencesNode = NbPreferences.root(); + if (preferencesNode != null) { + nodeName = nodeName.substring("nb:".length());; + try { + preferencesNode = preferencesNode.nodeExists(nodeName) ? preferencesNode.node(nodeName) : null; + } catch (BackingStoreException ex) { + preferencesNode = null; + } + } + } else { + preferencesNode = null; + } + + } else if (prefsNodeOrLookup instanceof Preferences) { + preferencesNode = (Preferences) prefsNodeOrLookup; + } else if (prefsNodeOrLookup instanceof Lookup) { + Lookup prefsLookup = (Lookup) prefsNodeOrLookup; + preferencesNodeResult = prefsLookup.lookupResult(Preferences.class); + Collection instances = preferencesNodeResult.allInstances(); + if (instances.size() > 0) { + preferencesNode = instances.iterator().next(); + preferencesNodeResult.addLookupListener(this); + } + return prefsLookup.lookup(Preferences.class); + } else { + preferencesNode = null; + } + } + return preferencesNode; + } + + public void resultChanged(LookupEvent ev) { + preferencesNode = null; + preferencesNodeResult = null; + updateItemsSelected(); + } + + private void togglePreferncesSelected() { + String key = (String) getValue(PREFERENCES_KEY); + Preferences prefs = prefs(); + if (key != null && prefs != null) { + prefs.putBoolean(key, !prefs.getBoolean(key, false)); + } + } + + } + } diff --git a/openide.awt/test/unit/src/org/openide/awt/AlwaysEnabledActionTest.java b/openide.awt/test/unit/src/org/openide/awt/AlwaysEnabledActionTest.java --- a/openide.awt/test/unit/src/org/openide/awt/AlwaysEnabledActionTest.java +++ b/openide.awt/test/unit/src/org/openide/awt/AlwaysEnabledActionTest.java @@ -47,16 +47,25 @@ import java.beans.PropertyChangeListener; import java.net.URL; import java.util.logging.Level; +import java.util.prefs.BackingStoreException; +import java.util.prefs.PreferenceChangeEvent; +import java.util.prefs.PreferenceChangeListener; +import java.util.prefs.Preferences; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.Icon; import javax.swing.ImageIcon; +import javax.swing.JMenuItem; +import junit.framework.TestCase; import org.netbeans.junit.Log; import org.netbeans.junit.NbTestCase; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.util.ContextAwareAction; +import org.openide.util.Exceptions; import org.openide.util.Lookup; +import org.openide.util.NbPreferences; +import org.openide.util.actions.Presenter; import org.openide.util.lookup.AbstractLookup; import org.openide.util.lookup.InstanceContent; @@ -82,12 +91,12 @@ myListenerCounter = 0; MyAction.last = null; } - + @Override protected boolean runInEQ() { return true; } - + public void testIconIsCorrect() throws Exception { myListenerCounter = 0; myIconResourceCounter = 0; @@ -332,6 +341,44 @@ assertEquals("MyNamedAction", MyAction.last.getValue(Action.NAME)); } + public void testPreferencesAction() throws Exception { + checkPreferencesAction("testSystemPreferences.instance", Preferences.systemRoot()); + checkPreferencesAction("testUserPreferences.instance", Preferences.userRoot()); + checkPreferencesAction("testNbPreferences.instance", NbPreferences.root()); + checkPreferencesAction("testCustomPreferences.instance", Preferences.userRoot()); // customPreferences() uses "myNode" subnode + } + + private void checkPreferencesAction(String actionFileName, Preferences prefsRoot) throws Exception { + final int[] changeCount = new int[] { 0 }; + Action a = readAction(actionFileName); + Preferences prefsNode = prefsRoot.node("myNode"); + prefsNode.putBoolean("myKey", true); + prefsRoot.sync(); + int delay = 1; + Thread.sleep(delay); + // Verify value + assertTrue("Expected true as preference value", prefsNode.getBoolean("myKey", false)); + +// prefsNode.addPreferenceChangeListener(new PreferenceChangeListener() { +// public void preferenceChange(PreferenceChangeEvent pce) { +// changeCount[0]++; +// } +// }); + TestCase.assertTrue("Expected to be instance of Presenter.Menu", a instanceof Presenter.Menu); + JMenuItem item = ((Presenter.Menu) a).getMenuPresenter(); + TestCase.assertTrue("Expected to be selected", item.isSelected()); + prefsNode.putBoolean("myKey", false); + prefsRoot.sync(); + Thread.sleep(delay); + TestCase.assertFalse("Expected to not be selected", item.isSelected()); + a.actionPerformed(null); // new ActionEvent(null, 0, "")); + Thread.sleep(delay); + TestCase.assertTrue("Expected to be selected", item.isSelected()); + prefsNode.putBoolean("myKey", false); + prefsNode.sync(); + Thread.sleep(delay); + } + private static void assertPropertyPropagated(String propertyName, Object value, Action a, Action delegate) { assertEquals("Action's property \"" + propertyName + "\"", value, a.getValue(propertyName)); assertEquals("Delegate's property \"" + propertyName + "\"", value, delegate.getValue(propertyName)); @@ -417,4 +464,35 @@ } } + private static final class PreferencesAction extends AbstractAction { + static PreferencesAction last; + int performedCount; + + PreferencesAction() { + last = this; + } + + public void actionPerformed(ActionEvent e) { + performedCount++; + } + } + static Action preferencesAction() { + return new PreferencesAction(); + } + + private static final Preferences customPrefs; + + static { + customPrefs = Preferences.userRoot().node("/myNode"); + try { + customPrefs.sync(); + } catch (BackingStoreException ex) { + Exceptions.printStackTrace(ex); + } + } + + public static Preferences customPreferences() { + return customPrefs; + } + } diff --git a/openide.awt/test/unit/src/org/openide/awt/test-layer.xml b/openide.awt/test/unit/src/org/openide/awt/test-layer.xml --- a/openide.awt/test/unit/src/org/openide/awt/test-layer.xml +++ b/openide.awt/test/unit/src/org/openide/awt/test-layer.xml @@ -98,6 +98,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +