KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > options > keymap > KeymapViewModel


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.options.keymap;
21
22
23 import java.awt.Component JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Arrays JavaDoc;
26 import java.util.Collections JavaDoc;
27 import java.util.Comparator JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.HashSet JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.Map JavaDoc;
33 import java.util.Set JavaDoc;
34 import java.util.StringTokenizer JavaDoc;
35 import java.util.TreeMap JavaDoc;
36 import java.util.Vector JavaDoc;
37 import javax.swing.AbstractButton JavaDoc;
38 import javax.swing.JLabel JavaDoc;
39 import javax.swing.KeyStroke JavaDoc;
40 import javax.swing.SwingUtilities JavaDoc;
41 import javax.swing.event.TreeModelEvent JavaDoc;
42 import javax.swing.event.TreeModelListener JavaDoc;
43 import javax.swing.tree.TreeModel JavaDoc;
44 import javax.swing.tree.TreePath JavaDoc;
45 import org.netbeans.modules.options.keymap.ShortcutsDialog.ShortcutsFinder;
46 import org.openide.ErrorManager;
47 import org.openide.awt.Mnemonics;
48 import org.openide.util.NbBundle;
49 import org.openide.util.RequestProcessor;
50 import org.openide.util.Utilities;
51
52
53 /**
54  *
55  * @author Jan Jancura
56  */

57 public class KeymapViewModel implements TreeModel JavaDoc, ShortcutsFinder {
58     
59     private Vector JavaDoc listeners = new Vector JavaDoc ();
60     private String JavaDoc currentProfile;
61     private KeymapModel model = new KeymapModel ();
62     // Map (String ("xx/yy") > List (Object (action)))
63
// tree of actions in folders
64
private Map JavaDoc categoryToActionsCache = new HashMap JavaDoc ();
65     // Map (String (keymapName) > Map (ActionImpl > Set (String (shortcut Ctrl+F)))).
66
// contains modified shortcuts only
67
private Map JavaDoc modifiedProfiles = new HashMap JavaDoc ();
68     // Set (String (profileName)).
69
private Set JavaDoc deletedProfiles = new HashSet JavaDoc ();
70     // Map (String (keymapName) > Map (ActionImpl > Set (String (shortcut Ctrl+F)))).
71
private Map JavaDoc shortcutsCache = new HashMap JavaDoc ();
72     
73     static final ActionsComparator actionsComparator = new ActionsComparator ();
74     
75     
76     /**
77      * Creates a new instance of KeymapModel
78      */

79     public KeymapViewModel () {
80         currentProfile = model.getCurrentProfile ();
81     }
82
83     
84     // TreeModel ...............................................................
85

86     public Object JavaDoc getRoot () {
87         return "";
88     }
89     
90     public Object JavaDoc getChild (Object JavaDoc parent, int index) {
91         return getItems ((String JavaDoc) parent).get (index);
92     }
93
94     public int getChildCount (Object JavaDoc parent) {
95         if (parent instanceof String JavaDoc)
96             return getItems ((String JavaDoc) parent).size ();
97         return 0;
98     }
99
100     public boolean isLeaf (Object JavaDoc node) {
101         return !(node instanceof String JavaDoc);
102     }
103
104     public void valueForPathChanged (TreePath JavaDoc path, Object JavaDoc newValue) {}
105
106     public int getIndexOfChild (Object JavaDoc parent, Object JavaDoc child) {
107         return getItems ((String JavaDoc) parent).indexOf (child);
108     }
109
110     public void addTreeModelListener (TreeModelListener JavaDoc l) {
111         listeners.add (l);
112     }
113
114     public void removeTreeModelListener (TreeModelListener JavaDoc l) {
115         listeners.remove (l);
116     }
117     
118     private void treeChanged () {
119         final Vector JavaDoc v = (Vector JavaDoc) listeners.clone ();
120         SwingUtilities.invokeLater (new Runnable JavaDoc () {
121             public void run () {
122                 TreeModelEvent JavaDoc tme = new TreeModelEvent JavaDoc (this, new Object JavaDoc[] {""});
123                 int i, k = v.size ();
124                 for (i = 0; i < k; i++)
125                     ((TreeModelListener JavaDoc) v.get (i)).treeNodesChanged (tme);
126             }
127         });
128     }
129     
130     private void nodeChanged (final TreePath JavaDoc path) {
131         final Vector JavaDoc v = (Vector JavaDoc) listeners.clone ();
132         SwingUtilities.invokeLater (new Runnable JavaDoc () {
133             public void run () {
134                 TreeModelEvent JavaDoc tme = new TreeModelEvent JavaDoc (this, path);
135                 int i, k = v.size ();
136                 for (i = 0; i < k; i++)
137                     ((TreeModelListener JavaDoc) v.get (i)).treeNodesChanged (tme);
138             }
139         });
140     }
141
142     
143     // ListModel ...............................................................
144

145     // Map (String ("xx/yy") > Map ...)
146
private Map JavaDoc categories;
147     
148     
149     /**
150      * Returns map of categories and subcategories.
151      * Root: getCategories ().get ("")
152      * Subcategories: getCategories ().get (category)
153      *
154      * Map (String (category name) > List (String (category name))).
155      */

156     public Map JavaDoc getCategories () {
157         if (categories == null) {
158             categories = new TreeMap JavaDoc ();
159             List JavaDoc c = new ArrayList JavaDoc (model.getActionCategories ());
160             Collections.sort (c);
161             Iterator JavaDoc it = c.iterator ();
162             while (it.hasNext ()) {
163                 String JavaDoc cn = (String JavaDoc) it.next ();
164                 String JavaDoc folderName = "";
165                 StringTokenizer JavaDoc st = new StringTokenizer JavaDoc (cn, "/");
166                 while (st.hasMoreTokens ()) {
167                     String JavaDoc name = st.nextToken ();
168                     List JavaDoc asd = (List JavaDoc) categories.get (folderName);
169                     if (asd == null) {
170                         asd = new ArrayList JavaDoc ();
171                         categories.put (folderName, asd);
172                     }
173                     folderName = folderName.length () == 0 ?
174                         name : folderName + '/' + name;
175                     if (asd.isEmpty () ||
176                         !asd.get (asd.size () - 1).equals (folderName)
177                     )
178                         asd.add (folderName);
179                 }
180             }
181         }
182         return categories;
183     }
184     
185     /**
186      * Returns list of subcategories (String) for given category merged
187      * together with actions for give category.
188      */

189     public List JavaDoc getItems (String JavaDoc category) {
190         List JavaDoc result = (List JavaDoc) categoryToActionsCache.get (category);
191         if (result == null) {
192             result = new ArrayList JavaDoc ();
193             List JavaDoc ll = (List JavaDoc) getCategories ().get (category);
194             if (ll != null)
195                 result.addAll (ll);
196             List JavaDoc l = new ArrayList JavaDoc (model.getActions (category));
197             Collections.sort (l, new ActionsComparator ());
198             result.addAll (l);
199             categoryToActionsCache.put (category, result);
200             //S ystem.out.println("getItems " + category + " : " + result);
201
}
202         return result;
203     }
204
205 // public ListCellRenderer getListCellRenderer () {
206
// return new KeymapListRenderer (this);
207
// }
208

209     
210     // other methods ...........................................................
211

212     List JavaDoc getProfiles () {
213         Set JavaDoc result = new HashSet JavaDoc (model.getProfiles ());
214         result.addAll (modifiedProfiles.keySet ());
215         List JavaDoc r = new ArrayList JavaDoc (result);
216         Collections.sort (r);
217         return r;
218     }
219     
220     boolean isCustomProfile (String JavaDoc profile) {
221         return model.isCustomProfile (profile);
222     }
223     
224     void deleteProfile (String JavaDoc profile) {
225         if (model.isCustomProfile (profile)) {
226             deletedProfiles.add (profile);
227             modifiedProfiles.remove (profile);
228         } else {
229             Map JavaDoc m = model.getKeymapDefaults (profile);
230             m = convertFromEmacs (m);
231             modifiedProfiles.put (profile, m);
232             treeChanged ();
233         }
234     }
235     
236     String JavaDoc getCurrentProfile () {
237         return currentProfile;
238     }
239     
240     void setCurrentProfile (String JavaDoc currentKeymap) {
241         this.currentProfile = currentKeymap;
242         treeChanged ();
243     }
244     
245     void cloneProfile (String JavaDoc newProfileName) {
246         Map JavaDoc result = new HashMap JavaDoc ();
247         cloneProfile ("", result);
248         modifiedProfiles.put (newProfileName, result);
249     }
250     
251     private void cloneProfile (
252         String JavaDoc category, // name of currently resolved category
253
Map JavaDoc result // Map (ActionImpl > Set (String (shortcut)))
254
) {
255         Iterator JavaDoc it = getItems (category).iterator ();
256         while (it.hasNext ()) {
257             Object JavaDoc o = it.next ();
258             if (o instanceof String JavaDoc)
259                 cloneProfile ((String JavaDoc) o, result);
260             else {
261                 String JavaDoc[] shortcuts = getShortcuts ((ActionImpl) o);
262                 result.put (o, new HashSet JavaDoc (Arrays.asList (shortcuts)));
263             }
264         }
265     }
266     
267     public ActionImpl findActionForShortcut (String JavaDoc shortcut) {
268         return findActionForShortcut (shortcut, "");
269     }
270     
271     private ActionImpl findActionForShortcut (String JavaDoc shortcut, String JavaDoc category) {
272         Iterator JavaDoc it = getItems (category).iterator ();
273         while (it.hasNext ()) {
274             Object JavaDoc o = it.next ();
275             if (o instanceof String JavaDoc) {
276                 ActionImpl result = findActionForShortcut (shortcut, (String JavaDoc) o);
277                 if (result != null) return result;
278                 continue;
279             }
280             ActionImpl action = (ActionImpl) o;
281             String JavaDoc[] shortcuts = getShortcuts (action);
282             int i, k = shortcuts.length;
283             for (i = 0; i < k; i++) {
284                 if (shortcuts [i].equals (shortcut)) return action;
285                 if (shortcuts [i].equals (shortcut + " ")) return action;
286             }
287         }
288         return null;
289     }
290
291     public ActionImpl findActionForId (final String JavaDoc actionId) {
292         if (SwingUtilities.isEventDispatchThread ())
293             return findActionForId (actionId, "");
294         
295         final ActionImpl[] result = new ActionImpl [1];
296         try {
297             SwingUtilities.invokeAndWait (new Runnable JavaDoc () {
298                 public void run () {
299                     result [0] = findActionForId (actionId, "");
300                 }
301             });
302         } catch (Exception JavaDoc ex) {
303             ErrorManager.getDefault ().notify (ex);
304         }
305         return result [0];
306     }
307     
308     private ActionImpl findActionForId (String JavaDoc actionId, String JavaDoc category) {
309         Iterator JavaDoc it = getItems (category).iterator ();
310         while (it.hasNext ()) {
311             Object JavaDoc o = it.next ();
312             if (o instanceof String JavaDoc) {
313                 ActionImpl result = findActionForId (actionId, (String JavaDoc) o);
314                 if (result != null) return result;
315                 continue;
316             }
317             String JavaDoc id = ((ActionImpl) o).getId ();
318             if (actionId.equals (id))
319                 return (ActionImpl) o;
320         }
321         return null;
322     }
323     
324     public String JavaDoc[] getShortcuts (ActionImpl action) {
325         if (modifiedProfiles.containsKey (currentProfile)) {
326             // find it in modified shortcuts
327
Map JavaDoc actionToShortcuts = (Map JavaDoc) modifiedProfiles.
328                 get (currentProfile);
329             if (actionToShortcuts.containsKey (action)) {
330                 Set JavaDoc s = (Set JavaDoc) actionToShortcuts.get (action);
331                 return (String JavaDoc[]) s.toArray (new String JavaDoc [s.size ()]);
332             }
333         }
334         
335         if (!shortcutsCache.containsKey (currentProfile)) {
336             // read profile and put it to cache
337
Map JavaDoc profileMap = convertFromEmacs (model.getKeymap (currentProfile));
338             shortcutsCache.put (
339                 currentProfile,
340                 profileMap
341              );
342         }
343         Map JavaDoc profileMap = (Map JavaDoc) shortcutsCache.get (currentProfile);
344         Set JavaDoc shortcuts = (Set JavaDoc) profileMap.get (action);
345         if (shortcuts == null) return new String JavaDoc [0];
346         return (String JavaDoc[]) shortcuts.toArray (new String JavaDoc [shortcuts.size ()]);
347     }
348     
349     void addShortcut (TreePath JavaDoc path, String JavaDoc shortcut) {
350         // delete old shortcut
351
ActionImpl action = findActionForShortcut (shortcut);
352         if (action != null)
353             removeShortcut (action, shortcut);
354         action = (ActionImpl) path.getLastPathComponent ();
355         Set JavaDoc s = new HashSet JavaDoc ();
356         s.add (shortcut);
357         s.addAll (Arrays.asList (getShortcuts (action)));
358         setShortcuts (action, s);
359         nodeChanged (path);
360     }
361     
362     public void setShortcuts (ActionImpl action, Set JavaDoc shortcuts) {
363         Map JavaDoc actionToShortcuts = (Map JavaDoc) modifiedProfiles.get (currentProfile);
364         if (actionToShortcuts == null) {
365             actionToShortcuts = new HashMap JavaDoc ();
366             modifiedProfiles.put (currentProfile, actionToShortcuts);
367         }
368         actionToShortcuts.put (action, shortcuts);
369     }
370     
371     void removeShortcut (TreePath JavaDoc path, String JavaDoc shortcut) {
372         ActionImpl action = (ActionImpl) path.getLastPathComponent ();
373         removeShortcut (action, shortcut);
374         nodeChanged (path);
375     }
376     
377     private void removeShortcut (ActionImpl action, String JavaDoc shortcut) {
378         Set JavaDoc s = new HashSet JavaDoc (Arrays.asList (getShortcuts (action)));
379         s.remove (shortcut);
380         Map JavaDoc actionToShortcuts = (Map JavaDoc) modifiedProfiles.get (currentProfile);
381         if (actionToShortcuts == null) {
382             actionToShortcuts = new HashMap JavaDoc ();
383             modifiedProfiles.put (currentProfile, actionToShortcuts);
384         }
385         actionToShortcuts.put (action, s);
386     }
387     
388     public void refreshActions () {
389         categoryToActionsCache = new HashMap JavaDoc ();
390         model.refreshActions ();
391     }
392     
393     public void apply () {
394         RequestProcessor.getDefault ().post (new Runnable JavaDoc () {
395             public void run () {
396                 Iterator JavaDoc it = modifiedProfiles.keySet ().iterator ();
397                 while (it.hasNext ()) {
398                     String JavaDoc profile = (String JavaDoc) it.next ();
399                     Map JavaDoc actionToShortcuts = (Map JavaDoc) modifiedProfiles.get (profile);
400                     actionToShortcuts = convertToEmacs (actionToShortcuts);
401                     model.changeKeymap (
402                         profile,
403                         actionToShortcuts
404                     );
405                 }
406                 it = deletedProfiles.iterator ();
407                 while (it.hasNext ()) {
408                     String JavaDoc profile = (String JavaDoc) it.next ();
409                     model.deleteProfile (profile);
410                 }
411                 model.setCurrentProfile (currentProfile);
412                 modifiedProfiles = new HashMap JavaDoc ();
413                 deletedProfiles = new HashSet JavaDoc ();
414                 shortcutsCache = new HashMap JavaDoc ();
415                 model = new KeymapModel ();
416             }
417         });
418     }
419     
420     public boolean isChanged () {
421         return (!modifiedProfiles.isEmpty ()) || !deletedProfiles.isEmpty ();
422     }
423     
424     public void cancel () {
425         modifiedProfiles = new HashMap JavaDoc ();
426         deletedProfiles = new HashSet JavaDoc ();
427         shortcutsCache = new HashMap JavaDoc ();
428         setCurrentProfile (model.getCurrentProfile ());
429         model = new KeymapModel ();
430     }
431     
432     /**
433      * Converts Map (ActionImpl > Set (String (shortcut Alt+Shift+P))) to
434      * Map (ActionImpl > Set (String (shortcut AS-P))).
435      */

436     private static Map JavaDoc convertToEmacs (Map JavaDoc shortcuts) {
437         Map JavaDoc result = new HashMap JavaDoc ();
438         Iterator JavaDoc it = shortcuts.keySet ().iterator ();
439         while (it.hasNext ()) {
440             Object JavaDoc action = it.next ();
441             Set JavaDoc sh = (Set JavaDoc) shortcuts.get (action);
442             Set JavaDoc newSet = new HashSet JavaDoc ();
443             Iterator JavaDoc it2 = sh.iterator ();
444             while (it2.hasNext ()) {
445                 String JavaDoc s = (String JavaDoc) it2.next ();
446                 if (s.length () == 0) continue;
447                 KeyStroke JavaDoc[] ks = getKeyStrokes (s, " ");
448                 if (ks == null)
449                     continue; // unparsable shortcuts ignorred
450
StringBuffer JavaDoc sb = new StringBuffer JavaDoc (
451                     Utilities.keyToString (ks [0])
452                 );
453                 int i, k = ks.length;
454                 for (i = 1; i < k; i++)
455                     sb.append (' ').append (Utilities.keyToString (ks [i]));
456                 newSet.add (sb.toString ());
457             }
458             result.put (action, newSet);
459         }
460         return result;
461     }
462     
463     /**
464      * Converts Map (ActionImpl > Set (String (shortcut AS-P))) to
465      * Map (ActionImpl > Set (String (shortcut Alt+Shift+P))).
466      */

467     private static Map JavaDoc convertFromEmacs (Map JavaDoc emacs) {
468         Map JavaDoc result = new HashMap JavaDoc ();
469         Iterator JavaDoc it = emacs.keySet ().iterator ();
470         while (it.hasNext ()) {
471             ActionImpl action = (ActionImpl) it.next ();
472             Set JavaDoc emacsShortcuts = (Set JavaDoc) emacs.get (action);
473             Iterator JavaDoc it2 = emacsShortcuts.iterator ();
474             Set JavaDoc shortcuts = new HashSet JavaDoc ();
475             while (it2.hasNext ()) {
476                 String JavaDoc emacsShortcut = (String JavaDoc) it2.next ();
477                 KeyStroke JavaDoc[] keyStroke = Utilities.stringToKeys (emacsShortcut);
478                 shortcuts.add (Utils.getKeyStrokesAsText (keyStroke, " "));
479             }
480             result.put (action, shortcuts);
481         }
482         return result;
483     }
484     
485     /**
486      * Returns multi keystroke for given text representation of shortcuts
487      * (like Alt+A B). Returns null if text is not parsable, and empty array
488      * for empty string.
489      */

490     private static KeyStroke JavaDoc[] getKeyStrokes (String JavaDoc keyStrokes, String JavaDoc delim) {
491         if (keyStrokes.length () == 0) return new KeyStroke JavaDoc [0];
492         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc (keyStrokes, delim);
493         List JavaDoc result = new ArrayList JavaDoc ();
494         while (st.hasMoreTokens ()) {
495             String JavaDoc ks = st.nextToken ().trim ();
496             KeyStroke JavaDoc keyStroke = Utils.getKeyStroke (ks);
497             if (keyStroke == null) return null; // text is not parsable
498
result.add (keyStroke);
499         }
500         return (KeyStroke JavaDoc[]) result.toArray (new KeyStroke JavaDoc [result.size ()]);
501     }
502     
503     private static String JavaDoc loc (String JavaDoc key) {
504         return NbBundle.getMessage (KeymapPanel.class, key);
505     }
506     
507     private static void loc (Component JavaDoc c, String JavaDoc key) {
508         if (c instanceof AbstractButton JavaDoc)
509             Mnemonics.setLocalizedText (
510                 (AbstractButton JavaDoc) c,
511                 loc (key)
512             );
513         else
514             Mnemonics.setLocalizedText (
515                 (JLabel JavaDoc) c,
516                 loc (key)
517             );
518     }
519     
520     
521     // innerclasses ............................................................
522

523     static class ActionsComparator implements Comparator JavaDoc {
524         
525         public int compare (Object JavaDoc o1, Object JavaDoc o2) {
526             if (o1 instanceof String JavaDoc)
527                 if (o2 instanceof String JavaDoc)
528                     return ((String JavaDoc) o1).compareTo ((String JavaDoc) o2);
529                 else
530                     return 1;
531             else
532                 if (o2 instanceof String JavaDoc)
533                     return -1;
534                 else
535                     return ((ActionImpl) o1).getDisplayName ().compareTo (
536                         ((ActionImpl) o2).getDisplayName ()
537                     );
538         }
539     }
540 }
541
Popular Tags