001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.options.keymap;
043:
044: import java.util.ArrayList;
045: import java.util.Collection;
046: import java.util.Collections;
047: import java.util.HashMap;
048: import java.util.HashSet;
049: import java.util.Iterator;
050: import java.util.List;
051: import java.util.Map;
052: import java.util.Set;
053: import java.util.logging.Level;
054: import java.util.logging.LogRecord;
055: import java.util.logging.Logger;
056: import org.netbeans.core.options.keymap.api.ShortcutAction;
057: import org.netbeans.core.options.keymap.spi.KeymapManager;
058: import org.openide.util.Lookup;
059: import org.openide.util.NbBundle;
060:
061: /**
062: *
063: * @author Jan Jancura
064: */
065: public class KeymapModel {
066:
067: private static final Logger LOG = Logger
068: .getLogger(KeymapModel.class.getName());
069: private static final Logger UI_LOG = Logger
070: .getLogger("org.netbeans.ui.options"); // NOI18N
071:
072: private static ArrayList<KeymapManager> al = new ArrayList<KeymapManager>();
073:
074: /**
075: * @return All the registered implementations.
076: */
077: public static Collection<? extends KeymapManager> getKeymapManagerInstances() {
078: if (!al.isEmpty()) {
079: return al;
080: }
081: al.addAll(Lookup.getDefault().lookupAll(KeymapManager.class));
082: return al;
083: }
084:
085: // actions .................................................................
086:
087: public Set<String> getActionCategories() {
088: Set<String> result = new HashSet<String>();
089: for (KeymapManager m : getKeymapManagerInstances()) {
090: result.addAll(m.getActions().keySet());
091: }
092: return Collections.unmodifiableSet(result);
093: }
094:
095: /**
096: * Map (String (category name) > Set (ShortcutAction)).
097: */
098: private Map<String, Set<ShortcutAction>> categoryToActions = new HashMap<String, Set<ShortcutAction>>();
099:
100: /**
101: * Returns List (ShortcutAction) of all global and editor actions.
102: */
103: public Set<ShortcutAction> getActions(String category) {
104: if (!categoryToActions.containsKey(category)) {
105: Set<ShortcutAction> actions = new HashSet<ShortcutAction>();
106: for (KeymapManager m : getKeymapManagerInstances()) {
107: Set<ShortcutAction> s = m.getActions().get(category);
108: if (s != null) {
109: actions = mergeActions(actions, s, m.getName());
110: }
111: }
112: categoryToActions.put(category, actions);
113: }
114: return categoryToActions.get(category);
115: }
116:
117: /**
118: * Clear action caches.
119: */
120: public void refreshActions() {
121: categoryToActions = new HashMap<String, Set<ShortcutAction>>();
122: for (KeymapManager m : getKeymapManagerInstances()) {
123: m.refreshActions();
124: }
125: }
126:
127: // keymaps .................................................................
128:
129: public String getCurrentProfile() {
130: String profileName = null;
131: for (KeymapManager m : getKeymapManagerInstances()) {
132: String res = m.getCurrentProfile();
133: if (res != null) {
134: profileName = res;
135: break;
136: }
137: }
138:
139: if (profileName == null) {
140: profileName = "NetBeans"; //NOI18N
141: }
142:
143: Map<String, String> map = getProfilesMap();
144: for (Map.Entry<String, String> entry : map.entrySet()) {
145: if (entry.getValue().equals(profileName)) {
146: return entry.getKey();
147: }
148: }
149:
150: return profileName;
151: }
152:
153: public void setCurrentProfile(String profile) {
154: String prev = getCurrentProfile();
155: if (!prev.equals(profile)) {
156: LogRecord rec = new LogRecord(Level.CONFIG,
157: "KEYMAP_SET_PROFILE"); // NOI18N
158: rec.setParameters(new Object[] { profile, prev });
159: rec
160: .setResourceBundle(NbBundle
161: .getBundle(KeymapModel.class));
162: rec.setResourceBundleName(KeymapModel.class.getPackage()
163: .getName()
164: + ".Bundle");
165: rec.setLoggerName(UI_LOG.getName());
166: UI_LOG.log(rec);
167: }
168:
169: profile = displayNameToName(profile);
170: for (KeymapManager m : getKeymapManagerInstances()) {
171: m.setCurrentProfile(profile);
172: }
173: }
174:
175: public List<String> getProfiles() {
176: return new ArrayList<String>(getProfilesMap().keySet());
177: }
178:
179: public boolean isCustomProfile(String profile) {
180: profile = displayNameToName(profile);
181: for (KeymapManager m : getKeymapManagerInstances()) {
182: boolean res = m.isCustomProfile(profile);
183: if (res) {
184: return true;
185: }
186: }
187: return false;
188: }
189:
190: /**
191: * Map (String (profile) > Map (ShortcutAction > Set (String (shortcut AS-M)))).
192: */
193: private Map<String, Map<ShortcutAction, Set<String>>> keyMaps = new HashMap<String, Map<ShortcutAction, Set<String>>>();
194:
195: /**
196: * Returns Map (ShortcutAction > Set (String (shortcut))).
197: */
198: public Map<ShortcutAction, Set<String>> getKeymap(String profile) {
199: profile = displayNameToName(profile);
200: if (!keyMaps.containsKey(profile)) {
201: Map<ShortcutAction, Set<String>> res = new HashMap<ShortcutAction, Set<String>>();
202: for (KeymapManager m : getKeymapManagerInstances()) {
203: Map<ShortcutAction, Set<String>> mm = m
204: .getKeymap(profile);
205: res = mergeShortcuts(res, mm);
206: }
207: keyMaps.put(profile, res);
208: }
209: return keyMaps.get(profile);
210: }
211:
212: /**
213: * Map (String (keymap name) > Map (ShortcutAction > Set (String (shortcut AS-M)))).
214: */
215: private Map<String, Map<ShortcutAction, Set<String>>> keyMapDefaults = new HashMap<String, Map<ShortcutAction, Set<String>>>();
216:
217: /**
218: * Returns Map (ShortcutAction > Set (String (shortcut))).
219: */
220: public Map<ShortcutAction, Set<String>> getKeymapDefaults(
221: String profile) {
222: profile = displayNameToName(profile);
223: if (!keyMapDefaults.containsKey(profile)) {
224: Map<ShortcutAction, Set<String>> res = new HashMap<ShortcutAction, Set<String>>();
225: for (KeymapManager m : getKeymapManagerInstances()) {
226: Map<ShortcutAction, Set<String>> mm = m
227: .getDefaultKeymap(profile);
228: res = mergeShortcuts(res, mm);
229: }
230: keyMapDefaults.put(profile, res);
231: }
232: return keyMapDefaults.get(profile);
233: }
234:
235: public void deleteProfile(String profile) {
236: profile = displayNameToName(profile);
237: for (KeymapManager m : getKeymapManagerInstances()) {
238: m.deleteProfile(profile);
239: }
240: }
241:
242: /**
243: * Defines new shortcuts for some actions in given keymap.
244: * Map (ShortcutAction > Set (String (shortcut AS-M P)).
245: */
246: public void changeKeymap(String profile,
247: Map<ShortcutAction, Set<String>> actionToShortcuts) {
248: profile = displayNameToName(profile);
249:
250: log("changeKeymap.actionToShortcuts", actionToShortcuts
251: .entrySet());
252:
253: // 1) mix changes with current keymap and put them to cached current shortcuts
254: Map<ShortcutAction, Set<String>> m = new HashMap<ShortcutAction, Set<String>>(
255: getKeymap(profile));
256: m.putAll(actionToShortcuts);
257: keyMaps.put(profile, m);
258: log("changeKeymap.m", m.entrySet());
259: for (KeymapManager km : getKeymapManagerInstances()) {
260: km.saveKeymap(profile, m);
261: }
262: }
263:
264: // private methods .........................................................
265:
266: private void log(String name, Collection items) {
267: if (!LOG.isLoggable(Level.FINE))
268: return;
269:
270: LOG.fine(name);
271: for (Iterator i = items.iterator(); i.hasNext();) {
272: Object item = i.next();
273: LOG.fine(" " + item);
274: }
275: }
276:
277: private Map<ShortcutAction, CompoundAction> sharedActions = new HashMap<ShortcutAction, CompoundAction>();
278:
279: /**
280: * Merges editor actions and layers actions. Creates CompoundAction for
281: * actions like Copy, registerred to both contexts.
282: */
283: /* package */Set<ShortcutAction> mergeActions(
284: Collection<ShortcutAction> res,
285: Collection<ShortcutAction> adding, String name) {
286:
287: Set<ShortcutAction> result = new HashSet<ShortcutAction>();
288: Map<String, ShortcutAction> idToAction = new HashMap<String, ShortcutAction>();
289: Map<String, ShortcutAction> delegateIdToAction = new HashMap<String, ShortcutAction>();
290: for (ShortcutAction action : res) {
291: String id = action.getId();
292: idToAction.put(id, action);
293: String delegate = action.getDelegatingActionId();
294: if (delegate != null) {
295: delegateIdToAction.put(delegate, action);
296: }
297: }
298:
299: for (ShortcutAction action : adding) {
300: String id = action.getId();
301:
302: if (delegateIdToAction.containsKey(id)) {
303: ShortcutAction origAction = delegateIdToAction
304: .remove(id);
305: idToAction.remove(origAction.getId());
306: KeymapManager origActionKeymapManager = findOriginator(origAction);
307: Map<String, ShortcutAction> ss = new HashMap<String, ShortcutAction>();
308: ss.put(origActionKeymapManager.getName(), origAction);
309: ss.put(name, action);
310: CompoundAction compoundAction = new CompoundAction(ss);
311: result.add(compoundAction);
312: sharedActions.put(origAction, compoundAction);
313: sharedActions.put(action, compoundAction);
314: result.add(compoundAction);
315: }
316: String delegatingId = action.getDelegatingActionId();
317: if (idToAction.containsKey(delegatingId)) {
318: ShortcutAction origAction = idToAction
319: .remove(delegatingId);
320: KeymapManager origActionKeymapManager = findOriginator(origAction);
321: Map<String, ShortcutAction> ss = new HashMap<String, ShortcutAction>();
322: ss.put(origActionKeymapManager.getName(), origAction);
323: ss.put(name, action);
324: CompoundAction compoundAction = new CompoundAction(ss);
325: result.add(compoundAction);
326: sharedActions.put(origAction, compoundAction);
327: sharedActions.put(action, compoundAction);
328: result.add(compoundAction);
329: }
330: if (!sharedActions.containsKey(action)) {
331: result.add(action);
332: }
333: }
334: result.addAll(idToAction.values());
335: return result;
336: }
337:
338: /**
339: * Tries to determince where the action originates.
340: */
341: private KeymapManager findOriginator(ShortcutAction a) {
342: for (KeymapManager km : getKeymapManagerInstances()) {
343: if (a.getKeymapManagerInstance(km.getName()) != null) {
344: return km;
345: }
346: }
347: return null;
348: }
349:
350: private Map<ShortcutAction, Set<String>> mergeShortcuts(
351: Map<ShortcutAction, Set<String>> res,
352: Map<ShortcutAction, Set<String>> adding) {
353:
354: for (ShortcutAction action : adding.keySet()) {
355: Set<String> shortcuts = adding.get(action);
356: if (shortcuts.isEmpty()) {
357: continue;
358: }
359: if (sharedActions.containsKey(action)) {
360: action = sharedActions.get(action);
361: }
362: res.put(action, shortcuts);
363: }
364: return res;
365: }
366:
367: private String displayNameToName(String keymapDisplayName) {
368: String name = getProfilesMap().get(keymapDisplayName);
369: return name == null ? keymapDisplayName : name;
370: }
371:
372: private Map<String, String> profilesMap;
373:
374: private Map<String, String> getProfilesMap() {
375: if (profilesMap == null) {
376: for (KeymapManager m : getKeymapManagerInstances()) {
377: List<String> l = m.getProfiles();
378: if (l != null) {
379: profilesMap = new HashMap<String, String>();
380: for (String name : l) {
381: profilesMap.put(m.getProfileDisplayName(name),
382: name);
383: }
384: break;
385: }
386: }
387: }
388: return profilesMap;
389: }
390:
391: {
392: // HACK - loads all actions. othervise during second open of Options
393: // Dialog (after cancel) map of sharedActions is not initialized.
394: Iterator it = getActionCategories().iterator();
395: while (it.hasNext())
396: getActions((String) it.next());
397: }
398: }
|