This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 58126
Collapse All | Expand All

(-)a/core.output2/nbproject/project.xml (+8 lines)
Lines 58-63 Link Here
58
                    </run-dependency>
58
                    </run-dependency>
59
                </dependency>
59
                </dependency>
60
                <dependency>
60
                <dependency>
61
                    <code-name-base>org.netbeans.modules.options.keymap</code-name-base>
62
                    <build-prerequisite/>
63
                    <compile-dependency/>
64
                    <run-dependency>
65
                        <specification-version>1.17</specification-version>
66
                    </run-dependency>
67
                </dependency>
68
                <dependency>
61
                    <code-name-base>org.openide.actions</code-name-base>
69
                    <code-name-base>org.openide.actions</code-name-base>
62
                    <build-prerequisite/>
70
                    <build-prerequisite/>
63
                    <compile-dependency/>
71
                    <compile-dependency/>
(-)a/core.output2/src/org/netbeans/core/output2/OutputTab.java (-9 / +66 lines)
Lines 61-66 Link Here
61
import java.io.File;
61
import java.io.File;
62
import java.io.IOException;
62
import java.io.IOException;
63
import java.util.ArrayList;
63
import java.util.ArrayList;
64
import java.util.Arrays;
64
import java.util.EnumMap;
65
import java.util.EnumMap;
65
import java.util.List;
66
import java.util.List;
66
import java.util.Map;
67
import java.util.Map;
Lines 81-87 Link Here
81
import javax.swing.UIManager;
82
import javax.swing.UIManager;
82
import javax.swing.event.PopupMenuEvent;
83
import javax.swing.event.PopupMenuEvent;
83
import javax.swing.event.PopupMenuListener;
84
import javax.swing.event.PopupMenuListener;
85
import javax.swing.text.DefaultEditorKit;
84
import javax.swing.text.Document;
86
import javax.swing.text.Document;
87
import org.netbeans.core.options.keymap.api.KeyStrokeUtils;
88
import org.netbeans.core.options.keymap.api.ShortcutAction;
89
import org.netbeans.core.options.keymap.api.ShortcutsFinder;
85
import org.netbeans.core.output2.Controller.ControllerOutputEvent;
90
import org.netbeans.core.output2.Controller.ControllerOutputEvent;
86
import org.netbeans.core.output2.ui.AbstractOutputPane;
91
import org.netbeans.core.output2.ui.AbstractOutputPane;
87
import org.netbeans.core.output2.ui.AbstractOutputTab;
92
import org.netbeans.core.output2.ui.AbstractOutputTab;
Lines 99-105 Link Here
99
import org.openide.xml.XMLUtil;
104
import org.openide.xml.XMLUtil;
100
105
101
import static org.netbeans.core.output2.OutputTab.ACTION.*;
106
import static org.netbeans.core.output2.OutputTab.ACTION.*;
102
import org.openide.windows.IOProvider;
107
import org.netbeans.core.output2.ui.OutputKeymapManager;
108
import org.openide.util.Lookup;
103
109
104
110
105
/**
111
/**
Lines 716-721 Link Here
716
    private final Map<ACTION, TabAction> actions = new EnumMap<ACTION, TabAction>(ACTION.class);;
722
    private final Map<ACTION, TabAction> actions = new EnumMap<ACTION, TabAction>(ACTION.class);;
717
723
718
    private void createActions() {
724
    private void createActions() {
725
        KeyStrokeUtils.refreshActionCache();
719
        for (ACTION a : ACTION.values()) {
726
        for (ACTION a : ACTION.values()) {
720
            TabAction action;
727
            TabAction action;
721
            switch(a) {
728
            switch(a) {
Lines 782-791 Link Here
782
        TabAction(ACTION action, String bundleKey) {
789
        TabAction(ACTION action, String bundleKey) {
783
            if (bundleKey != null) {
790
            if (bundleKey != null) {
784
                String name = NbBundle.getMessage(OutputTab.class, bundleKey);
791
                String name = NbBundle.getMessage(OutputTab.class, bundleKey);
785
                KeyStroke accelerator = getAcceleratorFor(bundleKey);
792
                KeyStroke[] accels = getAcceleratorFor(action);
786
                this.action = action;
793
                this.action = action;
787
                putValue(NAME, name);
794
                putValue(NAME, name);
788
                putValue(ACCELERATOR_KEY, accelerator);
795
                if (accels != null && accels.length > 0) {
796
                    putValue(ACCELERATORS_KEY, accels);
797
                    putValue(ACCELERATOR_KEY, accels[0]);
798
                }
789
            }
799
            }
790
        }
800
        }
791
801
Lines 814-828 Link Here
814
         * Get a keyboard accelerator from the resource bundle, with special handling
824
         * Get a keyboard accelerator from the resource bundle, with special handling
815
         * for the mac keyboard layout.
825
         * for the mac keyboard layout.
816
         *
826
         *
817
         * @param name The bundle key prefix
827
         * @param action Action to get accelerator for.
818
         * @return A keystroke
828
         * @return A keystroke
819
         */
829
         */
820
        private KeyStroke getAcceleratorFor(String name) {
830
        private KeyStroke[] getAcceleratorFor(ACTION action) {
821
            String key = name + ".accel"; //NOI18N
831
            switch (action) {
822
            if (Utilities.isMac()) {
832
                case COPY:
823
                key += ".mac"; //NOI18N
833
                    return KeyStrokeUtils.getKeyStrokesForAction(
834
                            "copy-to-clipboard", null);                 //NOI18N
835
                case PASTE:
836
                    return KeyStrokeUtils.getKeyStrokesForAction(
837
                            "paste-from-clipboard", null);              //NOI18N
838
                case SAVEAS:
839
                    return KeyStrokeUtils.getKeyStrokesForAction(
840
                            OutputKeymapManager.SAVE_AS_ACTION_ID, null);
841
                case CLOSE:
842
                    return KeyStrokeUtils.getKeyStrokesForAction(
843
                            OutputKeymapManager.CLOSE_ACTION_ID, null);
844
                case NEXT_ERROR:
845
                    return KeyStrokeUtils.getKeyStrokesForAction(
846
                            "next-error", null);                        //NOI18N
847
                case PREV_ERROR:
848
                    return KeyStrokeUtils.getKeyStrokesForAction(
849
                            "previous-error", null);                    //NOI18N
850
                case SELECT_ALL:
851
                    return KeyStrokeUtils.getKeyStrokesForAction(
852
                            "select-all", null);                        //NOI18N
853
                case FIND:
854
                    return KeyStrokeUtils.getKeyStrokesForAction(
855
                            "incremental-search-forward", null);        //NOI18N
856
                case FIND_NEXT:
857
                    return KeyStrokeUtils.getKeyStrokesForAction(
858
                            "find-next", null);                         //NOI18N
859
                case FIND_PREVIOUS:
860
                    return KeyStrokeUtils.getKeyStrokesForAction(
861
                            "find-previous", null);                     //NOI18N
862
                case FILTER:
863
                    return KeyStrokeUtils.getKeyStrokesForAction(
864
                            OutputKeymapManager.FILTER_ACTION_ID, null);
865
                case LARGER_FONT:
866
                    return KeyStrokeUtils.getKeyStrokesForAction(
867
                            OutputKeymapManager.LARGER_FONT_ACTION_ID, null);
868
                case SMALLER_FONT:
869
                    return KeyStrokeUtils.getKeyStrokesForAction(
870
                            OutputKeymapManager.SMALLER_FONT_ACTION_ID, null);
871
                case FONT_TYPE:
872
                    return KeyStrokeUtils.getKeyStrokesForAction(
873
                            OutputKeymapManager.FONT_TYPE_ACTION_ID, null);
874
                case CLEAR:
875
                    return KeyStrokeUtils.getKeyStrokesForAction(
876
                            OutputKeymapManager.CLEAR_ACTION_ID, null);
877
                case WRAP:
878
                    return KeyStrokeUtils.getKeyStrokesForAction(
879
                            OutputKeymapManager.WRAP_ACTION_ID, null);
880
                default:
881
                    return null;
824
            }
882
            }
825
            return Utilities.stringToKey(NbBundle.getMessage(OutputTab.class, key));
826
        }
883
        }
827
884
828
        public ACTION getAction() {
885
        public ACTION getAction() {
(-)a/core.output2/src/org/netbeans/core/output2/ui/AbstractOutputTab.java (-13 / +20 lines)
Lines 68-73 Link Here
68
    private boolean inputVisible = false;
68
    private boolean inputVisible = false;
69
    private AbstractOutputPane outputPane;
69
    private AbstractOutputPane outputPane;
70
    private Action[] actions = new Action[0];  
70
    private Action[] actions = new Action[0];  
71
    protected static final String ACCELERATORS_KEY = "ACCELERATORS_KEY";//NOI18N
71
72
72
    private Component toFocus;
73
    private Component toFocus;
73
    
74
    
Lines 191-212 Link Here
191
            //It is a Controller.ControllerAction - don't create a memory leak by listening to it
192
            //It is a Controller.ControllerAction - don't create a memory leak by listening to it
192
            a = new WeakAction(a);
193
            a = new WeakAction(a);
193
        }
194
        }
194
        KeyStroke accel = null;
195
        KeyStroke[] accels = null;
195
        String name;
196
        String name;
196
        Object o = a.getValue (Action.ACCELERATOR_KEY);
197
        Object o = a.getValue(ACCELERATORS_KEY);
197
        if (o instanceof KeyStroke) {
198
        if (o instanceof KeyStroke[]) {
198
            accel = (KeyStroke) o;
199
            accels = (KeyStroke[]) o;
199
        }
200
        }
200
        name = (String) a.getValue(Action.NAME);
201
        name = (String) a.getValue(Action.NAME);
201
        if (accel != null) {
202
        if (accels != null) {
202
            if (Controller.LOG) Controller.log ("Installed action " + name + " on " + accel);
203
            for (KeyStroke accel : accels) {
203
            // if the logic here changes, check the popup escaping hack in Controller
204
                if (Controller.LOG) {
204
            // it temporarily removes the VK_ESCAPE from input maps..
205
                    Controller.log("Installed action " //NOI18N
205
            JComponent c = getOutputPane().textView;
206
                            + name + " on " + accel);                   //NOI18N
206
            c.getInputMap().put(accel, name);
207
                }
207
            c.getActionMap().put(name, a);
208
                // if the logic here changes, check the popup escaping hack in
208
            getInputMap (WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put (accel, name);
209
                // Controller it temporarily removes the VK_ESCAPE from input
209
            getActionMap().put(name, a);
210
                // maps..
211
                JComponent c = getOutputPane().textView;
212
                c.getInputMap().put(accel, name);
213
                c.getActionMap().put(name, a);
214
                getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(accel, name);
215
                getActionMap().put(name, a);
216
            }
210
        }
217
        }
211
    }
218
    }
212
219
(-)a/core.output2/src/org/netbeans/core/output2/ui/OutputKeymapManager.java (+334 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.core.output2.ui;
43
44
import java.io.IOException;
45
import java.util.Collections;
46
import java.util.Enumeration;
47
import java.util.HashMap;
48
import java.util.HashSet;
49
import java.util.List;
50
import java.util.Map;
51
import java.util.Set;
52
import java.util.logging.Level;
53
import java.util.logging.Logger;
54
import org.netbeans.core.options.keymap.api.ShortcutAction;
55
import org.netbeans.core.options.keymap.spi.KeymapManager;
56
import org.netbeans.core.output2.NbIOProvider;
57
import org.openide.filesystems.FileObject;
58
import org.openide.filesystems.FileUtil;
59
import org.openide.util.NbBundle;
60
import org.openide.util.Utilities;
61
import org.openide.util.lookup.ServiceProvider;
62
63
/**
64
 *
65
 * @author jhavlin
66
 */
67
@ServiceProvider(service = KeymapManager.class)
68
public class OutputKeymapManager extends KeymapManager {
69
70
    private static final Logger LOG = Logger.getLogger(
71
            OutputKeymapManager.class.getName());
72
    private static final String CATEGORY_NAME = NbBundle.getMessage(
73
            NbIOProvider.class, "OpenIDE-Module-Name");                 //NOI18N
74
    /**
75
     * ID of actions in keymap settings panel.
76
     */
77
    public static final String CLEAR_ACTION_ID =
78
            "output-window-clear";                                      //NOI18N
79
    public static final String FILTER_ACTION_ID =
80
            "output-window-filter";                                     //NOI18N
81
    public static final String LARGER_FONT_ACTION_ID =
82
            "output-window-larger-font";                                //NOI18N
83
    public static final String SMALLER_FONT_ACTION_ID =
84
            "output-window-smaller-font";                               //NOI18N
85
    public static final String CLOSE_ACTION_ID =
86
            "output-window-close";                                      //NOI18N
87
    public static final String FONT_TYPE_ACTION_ID =
88
            "output-window-font-type";                                  //NOI18N
89
    public static final String SAVE_AS_ACTION_ID =
90
            "output-window-save-as";                                    //NOI18N
91
    public static final String WRAP_ACTION_ID =
92
            "output-window-wrap";                                       //NOI18N
93
    /**
94
     * Constants for persistence.
95
     */
96
    public static final String STORAGE_DIR =
97
            "org-netbeans-core-output2/actions/";                       //NOI18N
98
    public static final String SHORTCUT_PREFIX = "sc";                  //NOI18N
99
    /**
100
     * Actions
101
     */
102
    private final OutWinShortCutAction wrap = new OutWinShortCutAction(
103
            WRAP_ACTION_ID, "ACTION_WRAP");                             //NOI18N
104
    private final OutWinShortCutAction clear = new OutWinShortCutAction(
105
            CLEAR_ACTION_ID, "ACTION_CLEAR");                           //NOI18N
106
    private final OutWinShortCutAction filter = new OutWinShortCutAction(
107
            FILTER_ACTION_ID, "ACTION_FILTER");                         //NOI18N
108
    private final OutWinShortCutAction largerFont = new OutWinShortCutAction(
109
            LARGER_FONT_ACTION_ID, "ACTION_LARGER_FONT");               //NOI18N
110
    private final OutWinShortCutAction smallerFont = new OutWinShortCutAction(
111
            SMALLER_FONT_ACTION_ID, "ACTION_SMALLER_FONT");             //NOI18N
112
    private final OutWinShortCutAction closeWindow = new OutWinShortCutAction(
113
            CLOSE_ACTION_ID, "ACTION_CLOSE");                           //NOI18N
114
    private final OutWinShortCutAction fontType = new OutWinShortCutAction(
115
            FONT_TYPE_ACTION_ID, "ACTION_FONT_TYPE");                   //NOI18N
116
    private final OutWinShortCutAction saveAs = new OutWinShortCutAction(
117
            SAVE_AS_ACTION_ID, "ACTION_SAVEAS");                        //NOI18N
118
    /**
119
     * Map of keymaps. Keys are profile names.
120
     */
121
    Map<String, Map<ShortcutAction, Set<String>>> keymaps =
122
            new HashMap<String, Map<ShortcutAction, Set<String>>>();
123
    /**
124
     * The default keymap. Used if keys for a profile are not set.
125
     */
126
    Map<ShortcutAction, Set<String>> defaultKeymap =
127
            new HashMap<ShortcutAction, Set<String>>();
128
    /**
129
     * List of all actions.
130
     */
131
    private final Set<OutWinShortCutAction> allActions =
132
            new HashSet<OutWinShortCutAction>();
133
    /**
134
     * Map of actions of categories. There is only one category in this case.
135
     */
136
    Map<String, Set<ShortcutAction>> actions =
137
            new HashMap<String, Set<ShortcutAction>>();
138
139
    public OutputKeymapManager() {
140
        super("OutputWindowKeymapManager");                             //NOI18N
141
        actions = new HashMap<String, Set<ShortcutAction>>();
142
        Collections.addAll(allActions, wrap, clear, filter, largerFont,
143
                smallerFont, closeWindow, fontType, saveAs);
144
        Set<ShortcutAction> set = new HashSet<ShortcutAction>();
145
        set.addAll(allActions);
146
        actions.put(CATEGORY_NAME, set);
147
        fillDefaultKeyMap();
148
        loadShortCuts();
149
    }
150
151
    private void fillDefaultKeyMap() {
152
        for (OutWinShortCutAction a : allActions) {
153
            String dflt = a.getDefaultShortcut();
154
            defaultKeymap.put(a, (dflt != null && !dflt.isEmpty())
155
                    ? Collections.singleton(dflt)
156
                    : Collections.<String>emptySet());
157
        }
158
    }
159
160
    @Override
161
    public Map<String, Set<ShortcutAction>> getActions() {
162
        return actions;
163
    }
164
165
    @Override
166
    public void refreshActions() {
167
    }
168
169
    @Override
170
    public Map<ShortcutAction, Set<String>> getKeymap(String profileName) {
171
        Map<ShortcutAction, Set<String>> km = keymaps.get(profileName);
172
        if (km == null) {
173
            km = new HashMap<ShortcutAction, Set<String>>(defaultKeymap);
174
            keymaps.put(profileName, km);
175
        }
176
        return km;
177
    }
178
179
    @Override
180
    public Map<ShortcutAction, Set<String>> getDefaultKeymap(
181
            String profileName) {
182
        return defaultKeymap;
183
    }
184
185
    @Override
186
    public void saveKeymap(String profileName,
187
            Map<ShortcutAction, Set<String>> actionToShortcuts) {
188
189
        Map<ShortcutAction, Set<String>> newShortcuts =
190
                new HashMap<ShortcutAction, Set<String>>();
191
        keymaps.put(profileName, newShortcuts);
192
        for (OutWinShortCutAction owsa : allActions) {
193
            Set<String> shortcuts = actionToShortcuts.get(owsa);
194
            if (shortcuts == null) {
195
                shortcuts = Collections.<String>emptySet();
196
            }
197
            newShortcuts.put(owsa, shortcuts);
198
        }
199
        storeShortCuts(profileName);
200
    }
201
202
    @Override
203
    public List<String> getProfiles() {
204
        return null;
205
    }
206
207
    @Override
208
    public String getCurrentProfile() {
209
        return null;
210
    }
211
212
    @Override
213
    public void setCurrentProfile(String profileName) {
214
    }
215
216
    @Override
217
    public void deleteProfile(String profileName) {
218
    }
219
220
    @Override
221
    public boolean isCustomProfile(String profileName) {
222
        return false;
223
    }
224
225
    private class OutWinShortCutAction implements ShortcutAction {
226
227
        private String id;
228
        private String bundleKey;
229
        private String displayName;
230
        private String defaultShortcut;
231
232
        public OutWinShortCutAction(String id, String bundleKey) {
233
            this.id = id;
234
            this.bundleKey = bundleKey;
235
            this.displayName = NbBundle.getMessage(
236
                    NbIOProvider.class, bundleKey);
237
            String nbKeysBundleKey = Utilities.isMac()
238
                    ? bundleKey + ".accel.mac" //NOI18N
239
                    : bundleKey + ".accel";                             //NOI18N
240
            String nbKeys = NbBundle.getMessage(NbIOProvider.class,
241
                    nbKeysBundleKey);
242
            this.defaultShortcut = nbKeys;
243
        }
244
245
        public String getId() {
246
            return id;
247
        }
248
249
        public String getBundleKey() {
250
            return bundleKey;
251
        }
252
253
        public String getDisplayName() {
254
            return displayName;
255
        }
256
257
        public String getDefaultShortcut() {
258
            return defaultShortcut;
259
        }
260
261
        @Override
262
        public String getDelegatingActionId() {
263
            return null;
264
        }
265
266
        @Override
267
        public ShortcutAction getKeymapManagerInstance(
268
                String keymapManagerName) {
269
            return null;
270
        }
271
    }
272
273
    private void storeShortCuts(String profileName) {
274
        FileObject root = FileUtil.getConfigRoot();
275
        try {
276
            FileObject actionsDir = FileUtil.createFolder(
277
                    root, STORAGE_DIR + profileName);
278
            for (OutWinShortCutAction a : allActions) {
279
                FileObject data = actionsDir.getFileObject(a.getId());
280
                if (data == null) {
281
                    data = actionsDir.createData(a.getId());
282
                } else if (data.isFolder()) {
283
                    throw new IOException(data + " is a folder.");      //NOI18N
284
                }
285
                Enumeration<String> atts = data.getAttributes();
286
                while (atts.hasMoreElements()) {
287
                    String attName = atts.nextElement();
288
                    data.setAttribute(attName, null);
289
                }
290
                int index = 1;
291
                if (keymaps.get(profileName).get(a) == null) {
292
                    continue;
293
                }
294
                for (String shortCut : keymaps.get(profileName).get(a)) {
295
                    data.setAttribute(SHORTCUT_PREFIX + index++, shortCut);
296
                }
297
            }
298
        } catch (IOException e) {
299
            LOG.log(Level.WARNING, "Cannot create folder", e);          //NOI18N
300
        }
301
    }
302
303
    private void loadShortCuts() {
304
        FileObject root = FileUtil.getConfigRoot();
305
        FileObject actionsDir = root.getFileObject(STORAGE_DIR);
306
        if (actionsDir == null) {
307
            return;
308
        }
309
        for (FileObject profileDir : actionsDir.getChildren()) {
310
            if (!profileDir.isFolder()) {
311
                continue;
312
            }
313
            Map<ShortcutAction, Set<String>> keymap =
314
                    new HashMap<ShortcutAction, Set<String>>();
315
            keymaps.put(profileDir.getName(), keymap);
316
            for (OutWinShortCutAction a : allActions) {
317
                FileObject actionFile = profileDir.getFileObject(a.getId());
318
                if (actionFile == null || !actionFile.isData()) {
319
                    keymap.put(a, Collections.<String>emptySet());
320
                    continue;
321
                }
322
                Enumeration<String> atts = actionFile.getAttributes();
323
                Set<String> strokes = new HashSet<String>();
324
                while (atts.hasMoreElements()) {
325
                    String att = atts.nextElement();
326
                    if (att.startsWith(SHORTCUT_PREFIX)) {
327
                        strokes.add((String) actionFile.getAttribute(att));
328
                    }
329
                }
330
                keymap.put(a, strokes);
331
            }
332
        }
333
    }
334
}
(-)a/options.keymap/nbproject/project.xml (+1 lines)
Lines 152-157 Link Here
152
                </test-type>
152
                </test-type>
153
            </test-dependencies>
153
            </test-dependencies>
154
            <friend-packages>
154
            <friend-packages>
155
                <friend>org.netbeans.core.output2</friend>
155
                <friend>org.netbeans.modules.editor.macros</friend>
156
                <friend>org.netbeans.modules.editor.macros</friend>
156
                <friend>org.netbeans.modules.jumpto</friend>
157
                <friend>org.netbeans.modules.jumpto</friend>
157
                <friend>org.netbeans.modules.jvi</friend>
158
                <friend>org.netbeans.modules.jvi</friend>
(-)a/options.keymap/src/org/netbeans/modules/options/keymap/Utils.java (-7 / +149 lines)
Lines 42-51 Link Here
42
 * made subject to such option by the copyright holder.
42
 * made subject to such option by the copyright holder.
43
 */
43
 */
44
44
45
package org.netbeans.modules.options.keymap;
45
package org.netbeans.core.options.keymap.api;
46
import java.awt.event.InputEvent;
46
import java.awt.event.InputEvent;
47
import java.awt.event.KeyEvent;
47
import java.awt.event.KeyEvent;
48
import java.util.ArrayList;
49
import java.util.Arrays;
50
import java.util.List;
51
import java.util.StringTokenizer;
52
import java.util.logging.Level;
53
import java.util.logging.Logger;
48
import javax.swing.KeyStroke;
54
import javax.swing.KeyStroke;
55
import org.openide.util.Lookup;
49
import org.openide.util.Utilities;
56
import org.openide.util.Utilities;
50
57
51
58
Lines 53-62 Link Here
53
 *
60
 *
54
 * @author Jan Jancura
61
 * @author Jan Jancura
55
 */
62
 */
56
class Utils {
63
public class KeyStrokeUtils {
57
    
64
58
    
65
    private static final Logger LOG = Logger.getLogger(
59
    static String getKeyStrokesAsText (KeyStroke[] keyStrokes, String delim) {
66
            KeyStrokeUtils.class.getName());
67
68
    /**
69
     * Convert an array of {@link KeyStroke key stroke} to a string composed of
70
     * human-readable names of these key strokes, delimited by {@code delim}.
71
     */
72
    public static String getKeyStrokesAsText (KeyStroke[] keyStrokes, String delim) {
60
        if (keyStrokes == null) return "";
73
        if (keyStrokes == null) return "";
61
        if (keyStrokes.length == 0) return "";
74
        if (keyStrokes.length == 0) return "";
62
        StringBuffer sb = new StringBuffer (getKeyStrokeAsText (keyStrokes [0]));
75
        StringBuffer sb = new StringBuffer (getKeyStrokeAsText (keyStrokes [0]));
Lines 90-96 Link Here
90
        }
103
        }
91
    }
104
    }
92
    
105
    
93
    static KeyStroke getKeyStroke (String keyStroke) {
106
    /**
107
     * Convert human-readable keystroke name to {@link KeyStroke} object.
108
     */
109
    public static KeyStroke getKeyStroke (String keyStroke) {
94
        int modifiers = 0;
110
        int modifiers = 0;
95
        while (true) {
111
        while (true) {
96
            if (keyStroke.startsWith(EMACS_CTRL)) {
112
            if (keyStroke.startsWith(EMACS_CTRL)) {
Lines 124-130 Link Here
124
        }
140
        }
125
    }
141
    }
126
    
142
    
127
    static String getKeyStrokeAsText (KeyStroke keyStroke) {
143
    /**
144
     * Get human-readable name for a {@link KeyStroke}.
145
     */
146
    public static String getKeyStrokeAsText (KeyStroke keyStroke) {
128
        int modifiers = keyStroke.getModifiers ();
147
        int modifiers = keyStroke.getModifiers ();
129
        StringBuilder sb = new StringBuilder ();
148
        StringBuilder sb = new StringBuilder ();
130
        if ((modifiers & InputEvent.CTRL_DOWN_MASK) > 0) {
149
        if ((modifiers & InputEvent.CTRL_DOWN_MASK) > 0) {
Lines 150-153 Link Here
150
        }
169
        }
151
        return sb.toString ();
170
        return sb.toString ();
152
    }
171
    }
172
173
    /**
174
     * Converts a textual representation of key strokes to an array of <code>KeyStroke</code>
175
     * objects. Please see {@link #keyStrokesToString(Collection<KeyStroke>, boolean)}
176
     * ror details about the available formats.
177
     *
178
     * @param key The textual representation of keystorkes to convert. Its format
179
     *   depends on the value of <code>emacsStyle</code> parameter.
180
     *
181
     * @return The <code>KeyStroke</code>s that were represented by the <code>key</code>
182
     *   text or <code>null</code> if the textual representation was malformed.
183
     * @since 1.16
184
     */
185
    public static KeyStroke[] getKeyStrokes(String key) {
186
        assert key != null : "The parameter key must not be null"; //NOI18N
187
188
        List<KeyStroke> result = new ArrayList<KeyStroke>();
189
        String delimiter = " "; //NOI18N
190
191
        for(StringTokenizer st = new StringTokenizer(key, delimiter); st.hasMoreTokens();) { //NOI18N
192
            String ks = st.nextToken().trim();
193
            KeyStroke keyStroke = getKeyStroke(ks);
194
195
            if (keyStroke != null) {
196
                result.add(keyStroke);
197
            } else {
198
                if (LOG.isLoggable(Level.FINE)) {
199
                    LOG.log(Level.FINE,
200
                            "Invalid keystroke string: ''{0}''", ks);   //NOI18N
201
                }
202
                return null;
203
            }
204
        }
205
206
        return result.toArray(new KeyStroke[result.size()]);
207
    }
208
209
    /**
210
     * Find key strokes for an action. The currently selected profile is used.
211
     * If there are more than one key strokes, the most convenient one is the
212
     * first one in the array (index 0). If no key stroke is found and
213
     * {@code defaultKeyStroke} is not null, an array containing only
214
     * {@code defaultKeyStroke} is returned. If no key stroke is found and
215
     * {@code defaultKeyStroke} is null, an empty array is returned.
216
     *
217
     * @param actionId ID of action.
218
     * @param defaultKeyStroke Default key stroke, used in case no key stroke is
219
     * found for the action. Can be null.
220
     * @return Array of key strokes, or an empty array if no key stroke is
221
     * available.
222
     */
223
    public static KeyStroke[] getKeyStrokesForAction(String actionId,
224
            KeyStroke defaultKeyStroke) {
225
        for (ShortcutsFinder sf : Lookup.getDefault().lookupAll(
226
                ShortcutsFinder.class)) {
227
            ShortcutAction sa = sf.findActionForId(actionId);
228
            if (sa != null) {
229
                String[] shortcuts = sf.getShortcuts(sa);
230
                if (shortcuts != null && shortcuts.length > 0) {
231
                    KeyStroke[] ks = new KeyStroke[shortcuts.length];
232
                    int valid = 0;
233
                    for (int i = 0; i < shortcuts.length; i++) {
234
                        if (shortcuts[i] != null) {
235
                            KeyStroke s = getKeyStroke(shortcuts[i]);
236
                            if (s != null) {
237
                                ks[valid++] = s;
238
                            }
239
                        }
240
                    }
241
                    return sortKeyStrokesByPreference(
242
                            Arrays.copyOfRange(ks, 0, valid));
243
                }
244
            }
245
        }
246
        return defaultKeyStroke == null
247
                ? new KeyStroke[0]
248
                : new KeyStroke[] {defaultKeyStroke};
249
    }
250
251
    /**
252
     * Get the most appropriate accelerator. It can be used in pop-up menus.
253
     */
254
    private static KeyStroke[] sortKeyStrokesByPreference(
255
            KeyStroke[] keystrokes) {
256
        if (keystrokes.length < 2) {
257
            return keystrokes;
258
        }
259
        KeyStroke best = null;
260
        int bestIndex = -1;
261
        boolean isSolaris =
262
                Utilities.getOperatingSystem() == Utilities.OS_SOLARIS;
263
        for (int i = 0; i < keystrokes.length; i++) {
264
            KeyStroke ks = keystrokes[i];
265
            boolean solarisKey = ks.getKeyCode() >= KeyEvent.VK_STOP
266
                    && ks.getKeyCode() <= KeyEvent.VK_CUT;
267
            if (isSolaris == solarisKey
268
                    && (best == null
269
                    || best.getKeyCode() > ks.getKeyCode())) {
270
                //Solaris key on solaris OS or other key on other OS.
271
                best = ks;
272
                bestIndex = i;
273
            }
274
        }
275
        if (bestIndex > 0) {
276
            assert best != null;
277
            KeyStroke aux = keystrokes[0];
278
            keystrokes[0] = best;
279
            keystrokes[bestIndex] = aux;
280
        }
281
        return keystrokes;
282
    }
283
284
    /**
285
     * Force caches to be refreshed, so that
286
     * {@link #getKeyStrokesForAction(String, KeyStroke)} returns correct and
287
     * up-to-date results.
288
     */
289
    public static void refreshActionCache() {
290
        for (ShortcutsFinder sf :
291
                Lookup.getDefault().lookupAll(ShortcutsFinder.class)) {
292
            sf.refreshActions();
293
        }
294
    }
153
}
295
}
(-)a/options.keymap/src/org/netbeans/modules/options/keymap/KeymapViewModel.java (-3 / +6 lines)
Lines 68-78 Link Here
68
import javax.swing.SwingUtilities;
68
import javax.swing.SwingUtilities;
69
import javax.swing.event.TableModelEvent;
69
import javax.swing.event.TableModelEvent;
70
import javax.swing.table.DefaultTableModel;
70
import javax.swing.table.DefaultTableModel;
71
import org.netbeans.core.options.keymap.api.KeyStrokeUtils;
71
import org.netbeans.core.options.keymap.api.ShortcutAction;
72
import org.netbeans.core.options.keymap.api.ShortcutAction;
72
import org.netbeans.core.options.keymap.api.ShortcutsFinder;
73
import org.netbeans.core.options.keymap.api.ShortcutsFinder;
73
import org.openide.DialogDescriptor;
74
import org.openide.DialogDescriptor;
74
import org.openide.DialogDisplayer;
75
import org.openide.DialogDisplayer;
75
import org.openide.ErrorManager;
76
import org.openide.ErrorManager;
77
import org.openide.util.Exceptions;
76
import org.openide.util.NbBundle;
78
import org.openide.util.NbBundle;
77
import org.openide.util.RequestProcessor;
79
import org.openide.util.RequestProcessor;
78
import org.openide.util.Utilities;
80
import org.openide.util.Utilities;
Lines 625-630 Link Here
625
    
627
    
626
    public void refreshActions () {
628
    public void refreshActions () {
627
        categoryToActionsCache = new HashMap<String, List<Object>> ();
629
        categoryToActionsCache = new HashMap<String, List<Object>> ();
630
        shortcutsCache = new HashMap<String, Map<ShortcutAction, Set<String>>>();
628
        model.refreshActions ();
631
        model.refreshActions ();
629
    }
632
    }
630
    
633
    
Lines 658-664 Link Here
658
                shortcutsCache = new HashMap<String, Map<ShortcutAction, Set<String>>> ();
661
                shortcutsCache = new HashMap<String, Map<ShortcutAction, Set<String>>> ();
659
                model = new KeymapModel ();
662
                model = new KeymapModel ();
660
                applyInProgress = false;
663
                applyInProgress = false;
661
    }
664
            }
662
        });
665
        });
663
    }
666
    }
664
    
667
    
Lines 766-772 Link Here
766
            Set<String> shortcuts = new LinkedHashSet<String> ();
769
            Set<String> shortcuts = new LinkedHashSet<String> ();
767
            for (String emacsShortcut: entry.getValue()) {
770
            for (String emacsShortcut: entry.getValue()) {
768
                KeyStroke[] keyStroke = Utilities.stringToKeys (emacsShortcut);
771
                KeyStroke[] keyStroke = Utilities.stringToKeys (emacsShortcut);
769
                shortcuts.add (Utils.getKeyStrokesAsText (keyStroke, " "));
772
                shortcuts.add (KeyStrokeUtils.getKeyStrokesAsText (keyStroke, " "));
770
            }
773
            }
771
            result.put (action, shortcuts);
774
            result.put (action, shortcuts);
772
        }
775
        }
Lines 784-790 Link Here
784
        List<KeyStroke> result = new ArrayList<KeyStroke> ();
787
        List<KeyStroke> result = new ArrayList<KeyStroke> ();
785
        while (st.hasMoreTokens ()) {
788
        while (st.hasMoreTokens ()) {
786
            String ks = st.nextToken ().trim ();
789
            String ks = st.nextToken ().trim ();
787
            KeyStroke keyStroke = Utils.getKeyStroke (ks);
790
            KeyStroke keyStroke = KeyStrokeUtils.getKeyStroke (ks);
788
            if (keyStroke == null) return null; // text is not parsable 
791
            if (keyStroke == null) return null; // text is not parsable 
789
            result.add (keyStroke);
792
            result.add (keyStroke);
790
        }
793
        }
(-)a/options.keymap/src/org/netbeans/modules/options/keymap/ProfilesPanel.java (-2 / +3 lines)
Lines 56-61 Link Here
56
import javax.swing.AbstractListModel;
56
import javax.swing.AbstractListModel;
57
import javax.swing.JFileChooser;
57
import javax.swing.JFileChooser;
58
import javax.swing.KeyStroke;
58
import javax.swing.KeyStroke;
59
import org.netbeans.core.options.keymap.api.KeyStrokeUtils;
59
import org.netbeans.core.options.keymap.api.ShortcutAction;
60
import org.netbeans.core.options.keymap.api.ShortcutAction;
60
import org.openide.DialogDisplayer;
61
import org.openide.DialogDisplayer;
61
import org.openide.NotifyDescriptor;
62
import org.openide.NotifyDescriptor;
Lines 347-353 Link Here
347
        for(StringTokenizer st = new StringTokenizer(key, delimiter); st.hasMoreTokens();) { //NOI18N
348
        for(StringTokenizer st = new StringTokenizer(key, delimiter); st.hasMoreTokens();) { //NOI18N
348
            String ks = st.nextToken().trim();
349
            String ks = st.nextToken().trim();
349
350
350
            KeyStroke keyStroke = Utils.getKeyStroke(ks);
351
            KeyStroke keyStroke = KeyStrokeUtils.getKeyStroke(ks);
351
352
352
            if (keyStroke != null) {
353
            if (keyStroke != null) {
353
                buf.append(Utilities.keyToString(keyStroke, true));
354
                buf.append(Utilities.keyToString(keyStroke, true));
Lines 373-379 Link Here
373
            KeyStroke keyStroke = Utilities.stringToKey(ks);
374
            KeyStroke keyStroke = Utilities.stringToKey(ks);
374
375
375
            if (keyStroke != null) {
376
            if (keyStroke != null) {
376
                buf.append(Utils.getKeyStrokeAsText(keyStroke));
377
                buf.append(KeyStrokeUtils.getKeyStrokeAsText(keyStroke));
377
                if (st.hasMoreTokens())
378
                if (st.hasMoreTokens())
378
                    buf.append(' ');
379
                    buf.append(' ');
379
            } else {
380
            } else {
(-)a/options.keymap/src/org/netbeans/modules/options/keymap/ShortcutListener.java (-1 / +2 lines)
Lines 45-50 Link Here
45
import java.awt.event.KeyListener;
45
import java.awt.event.KeyListener;
46
import javax.swing.JTextField;
46
import javax.swing.JTextField;
47
import javax.swing.KeyStroke;
47
import javax.swing.KeyStroke;
48
import org.netbeans.core.options.keymap.api.KeyStrokeUtils;
48
49
49
/**
50
/**
50
 * KeyListener trasforming keystrokes to human-readable and displaying them
51
 * KeyListener trasforming keystrokes to human-readable and displaying them
Lines 125-131 Link Here
125
    }
126
    }
126
127
127
    private void addKeyStroke(KeyStroke keyStroke, boolean add) {
128
    private void addKeyStroke(KeyStroke keyStroke, boolean add) {
128
        String k = Utils.getKeyStrokeAsText(keyStroke);
129
        String k = KeyStrokeUtils.getKeyStrokeAsText(keyStroke);
129
        if (key.equals("")) { //NOI18N
130
        if (key.equals("")) { //NOI18N
130
            textField.setText(k);
131
            textField.setText(k);
131
            if (add)
132
            if (add)
(-)a/options.keymap/src/org/netbeans/modules/options/keymap/ShortcutProvider.java (-6 / +7 lines)
Lines 47-52 Link Here
47
import java.util.LinkedHashSet;
47
import java.util.LinkedHashSet;
48
import java.util.Set;
48
import java.util.Set;
49
import javax.swing.KeyStroke;
49
import javax.swing.KeyStroke;
50
import org.netbeans.core.options.keymap.api.KeyStrokeUtils;
50
import org.openide.util.Utilities;
51
import org.openide.util.Utilities;
51
52
52
/**
53
/**
Lines 129-163 Link Here
129
130
130
            //CTRL
131
            //CTRL
131
            for (int i = 0; i < letters.length; i++) {
132
            for (int i = 0; i < letters.length; i++) {
132
                shortcutSet.add(Utils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.CTRL_MASK)));
133
                shortcutSet.add(KeyStrokeUtils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.CTRL_MASK)));
133
            }
134
            }
134
135
135
            if (Utilities.isMac())
136
            if (Utilities.isMac())
136
                //META
137
                //META
137
                for (int i = 0; i < letters.length; i++) {
138
                for (int i = 0; i < letters.length; i++) {
138
                    shortcutSet.add(Utils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.META_MASK)));
139
                    shortcutSet.add(KeyStrokeUtils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.META_MASK)));
139
                }
140
                }
140
            else
141
            else
141
                //ALT
142
                //ALT
142
                for (int i = 0; i < letters.length; i++) {
143
                for (int i = 0; i < letters.length; i++) {
143
                    shortcutSet.add(Utils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.ALT_MASK)));
144
                    shortcutSet.add(KeyStrokeUtils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.ALT_MASK)));
144
                }
145
                }
145
146
146
            //CTRL+SHIFT
147
            //CTRL+SHIFT
147
            for (int i = 0; i < letters.length; i++) {
148
            for (int i = 0; i < letters.length; i++) {
148
                shortcutSet.add(Utils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK)));
149
                shortcutSet.add(KeyStrokeUtils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK)));
149
150
150
            }
151
            }
151
152
152
            if (Utilities.isMac())
153
            if (Utilities.isMac())
153
                //SHIFT+META
154
                //SHIFT+META
154
                for (int i = 0; i < letters.length; i++) {
155
                for (int i = 0; i < letters.length; i++) {
155
                    shortcutSet.add(Utils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.SHIFT_MASK | InputEvent.META_MASK)));
156
                    shortcutSet.add(KeyStrokeUtils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.SHIFT_MASK | InputEvent.META_MASK)));
156
                }
157
                }
157
            else
158
            else
158
                //SHIFT+ALT
159
                //SHIFT+ALT
159
                for (int i = 0; i < letters.length; i++) {
160
                for (int i = 0; i < letters.length; i++) {
160
                    shortcutSet.add(Utils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.SHIFT_MASK | InputEvent.ALT_MASK)));
161
                    shortcutSet.add(KeyStrokeUtils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.SHIFT_MASK | InputEvent.ALT_MASK)));
161
                }
162
                }
162
        }
163
        }
163
        return shortcutSet;
164
        return shortcutSet;
(-)a/options.keymap/src/org/netbeans/modules/options/keymap/ShortcutsDialog.form (-1 / +1 lines)
Lines 1-4 Link Here
1
<?xml version="1.1" encoding="UTF-8" ?>
1
<?xml version="1.0" encoding="UTF-8" ?>
2
2
3
<Form version="1.3" maxVersion="1.3" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
3
<Form version="1.3" maxVersion="1.3" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
4
  <AccessibilityProperties>
4
  <AccessibilityProperties>
(-)a/options.keymap/src/org/netbeans/modules/options/keymap/ShortcutsDialog.java (-1 / +2 lines)
Lines 58-63 Link Here
58
import javax.swing.JButton;
58
import javax.swing.JButton;
59
import javax.swing.JLabel;
59
import javax.swing.JLabel;
60
import javax.swing.KeyStroke;
60
import javax.swing.KeyStroke;
61
import org.netbeans.core.options.keymap.api.KeyStrokeUtils;
61
import org.netbeans.core.options.keymap.api.ShortcutAction;
62
import org.netbeans.core.options.keymap.api.ShortcutAction;
62
import org.netbeans.core.options.keymap.api.ShortcutsFinder;
63
import org.netbeans.core.options.keymap.api.ShortcutsFinder;
63
import org.openide.awt.Mnemonics;
64
import org.openide.awt.Mnemonics;
Lines 273-279 Link Here
273
            }
274
            }
274
            
275
            
275
            private void addKeyStroke (KeyStroke keyStroke, boolean add) {
276
            private void addKeyStroke (KeyStroke keyStroke, boolean add) {
276
                String k = Utils.getKeyStrokeAsText (keyStroke);
277
                String k = KeyStrokeUtils.getKeyStrokeAsText (keyStroke);
277
                if (key.equals ("")) { //NOI18N
278
                if (key.equals ("")) { //NOI18N
278
                    getTfShortcut().setText (k);
279
                    getTfShortcut().setText (k);
279
                    if (add) key = k;
280
                    if (add) key = k;

Return to bug 58126