KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > editor > options > KeyBindingsMIMEOptionFile


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.editor.options;
21
22 import java.awt.Toolkit JavaDoc;
23 import java.awt.event.KeyEvent JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import java.util.Collection JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.HashSet JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.Map JavaDoc;
31
32 import org.netbeans.editor.Settings;
33 import org.netbeans.editor.SettingsNames;
34 import org.openide.xml.XMLUtil;
35
36 import org.w3c.dom.Document JavaDoc;
37 import org.w3c.dom.Element JavaDoc;
38 import org.w3c.dom.Node JavaDoc;
39 import org.w3c.dom.NodeList JavaDoc;
40 import org.w3c.dom.Text JavaDoc;
41 import java.util.List JavaDoc;
42 import org.netbeans.editor.MultiKeyBinding;
43 import javax.swing.text.JTextComponent JavaDoc;
44
45 /** MIME Option XML file for KeyBindings settings.
46  * KeyBindings settings are loaded and saved in XML format
47  * according to EditorKeyBindings-1_0.dtd.
48  *
49  * @author Martin Roskanin
50  * @since 08/2001
51  */

52 public class KeyBindingsMIMEOptionFile extends MIMEOptionFile{
53     
54     /** Elements */
55     public static final String JavaDoc TAG_ROOT = "bindings"; //NOI18N
56
public static final String JavaDoc TAG_BIND = "bind"; //NOI18N
57

58     /** Attributes */
59     public static final String JavaDoc ATTR_KEY = "key"; //NOI18N
60
public static final String JavaDoc ATTR_ACTION_NAME = "actionName"; //NOI18N
61
public static final String JavaDoc ATTR_REMOVE = "remove"; //NOI18N
62

63     /** File name of this MIMEOptionFile */
64     static final String JavaDoc FILENAME = "keybindings"; //NOI18N
65

66     public KeyBindingsMIMEOptionFile(BaseOptions base, Object JavaDoc proc) {
67         super(base, proc);
68     }
69     
70     private List JavaDoc getKBList(){
71       Settings.KitAndValue[] kav = Settings.getValueHierarchy(base.getKitClass(), SettingsNames.KEY_BINDING_LIST);
72       List JavaDoc kbList = null;
73       for (int i = 0; i < kav.length; i++) {
74           if (kav[i].kitClass == base.getKitClass()) {
75               kbList = (List JavaDoc)kav[i].value;
76           }
77       }
78       if (kbList == null) {
79           kbList = new ArrayList JavaDoc();
80       }
81
82       // must convert all members to serializable MultiKeyBinding
83
int cnt = kbList.size();
84       for (int i = 0; i < cnt; i++) {
85           Object JavaDoc o = kbList.get(i);
86           if (!(o instanceof MultiKeyBinding) && o != null) {
87               JTextComponent.KeyBinding JavaDoc b = (JTextComponent.KeyBinding JavaDoc)o;
88               kbList.set(i, new MultiKeyBinding(b.key, b.actionName));
89           }
90       }
91       return new ArrayList JavaDoc( kbList );
92     }
93     
94     /** Loads settings from XML file.
95      * @param propagate if true - propagates the loaded settings to Editor UI */

96     protected void loadSettings(boolean propagate){
97         synchronized (Settings.class) {
98             if (base.usesNewOptionsDialog()){
99                 return;
100             }
101             Document JavaDoc doc = dom;
102             Element JavaDoc rootElement = doc.getDocumentElement();
103
104             if (!TAG_ROOT.equals(rootElement.getTagName())) {
105                 // Wrong root element
106
return;
107             }
108
109             // gets current keyBindings map
110
List JavaDoc keybs = getKBList();
111             Map JavaDoc mapa = OptionUtilities.makeKeyBindingsMap(keybs);
112             properties.clear();
113
114             NodeList JavaDoc bind = rootElement.getElementsByTagName(TAG_BIND);
115             int len = bind.getLength();
116             for (int i=0; i<len; i++){
117                 Node JavaDoc node = bind.item(i);
118                 Element JavaDoc bindElement = (Element JavaDoc)node;
119
120                 if (bindElement == null){
121                     continue;
122                 }
123
124                 String JavaDoc key = bindElement.getAttribute(ATTR_KEY);
125                 String JavaDoc delete = bindElement.getAttribute(ATTR_REMOVE);
126                 String JavaDoc actionName = bindElement.getAttribute(ATTR_ACTION_NAME);
127                 if (actionName==null) actionName="";
128
129
130                 if ((actionName.length() != 0) && (!Boolean.valueOf(delete).booleanValue())){
131                     if(key.indexOf('$') > 0){
132                         MultiKeyBinding mkb = new MultiKeyBinding( OptionUtilities.stringToKeys(key) , actionName );
133                         properties.put(key, mkb);
134                     }else{
135                         MultiKeyBinding mkb = new MultiKeyBinding( OptionUtilities.stringToKey(key) , actionName );
136                         properties.put(key, mkb );
137                     }
138                 }else{
139                     properties.put(key, "" );
140                 }
141
142             }
143
144             if (properties.size()>0){
145                 // create updated map
146
mapa.putAll(properties);
147
148                 // remove all deleted values
149
for( Iterator JavaDoc i = properties.keySet().iterator(); i.hasNext(); ) {
150                     String JavaDoc key = (String JavaDoc)i.next();
151                     if(properties.get(key) instanceof String JavaDoc){
152                         // remove all deleted props
153
mapa.remove(key);
154                     }
155                 }
156
157                 // setKeybMap without saving to XML
158
if (propagate){
159                     setLoaded(true);
160                     base.setKeyBindingList(new ArrayList JavaDoc(mapa.values()), false);
161                 }
162             }
163             if (propagate) setLoaded(true);
164         }
165     }
166     
167     /** Save settings to XML file
168      * @param changedProp the Map of settings to save */

169     protected void updateSettings(Map JavaDoc changedProp){
170         synchronized (Settings.class) {
171             if (base.usesNewOptionsDialog()){
172                 return;
173             }
174             // put changed properties to local map
175
properties.putAll(changedProp);
176
177             // now we can save local map to XML file
178
Document JavaDoc doc = XMLUtil.createDocument(TAG_ROOT, null, processor.getPublicID(), processor.getSystemID());
179             Element JavaDoc rootElem = doc.getDocumentElement();
180
181             ArrayList JavaDoc removed = new ArrayList JavaDoc();
182
183             Map JavaDoc defaultKeybs = base.getDefaultKeyBindingsMap();
184
185             // if default keybindings don't exist for appropriate kit, set them empty
186
if (defaultKeybs == null) defaultKeybs = new HashMap JavaDoc();
187
188             // save XML
189
for( Iterator JavaDoc i = properties.keySet().iterator(); i.hasNext(); ) {
190                 String JavaDoc key = (String JavaDoc)i.next();
191                 // Process deleted properties
192

193                 if (properties.get(key) instanceof String JavaDoc){
194                     String JavaDoc realKey = tryRemoveKeyFromMap(doc, properties, key, defaultKeybs, rootElem);
195                     if (realKey != null) {
196                         removed.add(realKey);
197                         key = realKey;
198                     }
199
200                     // if property is not in default set, it will not be written and will be deleted
201
continue;
202                 }
203
204                 if (properties.get(key) instanceof MultiKeyBinding){
205                     MultiKeyBinding mkb = (MultiKeyBinding) properties.get(key);
206                     String JavaDoc curActionName= mkb.actionName;
207                     if (curActionName == null) curActionName=""; //NOI18N
208

209                     boolean save = true;
210                     if (defaultKeybs.get(key) instanceof MultiKeyBinding){
211                         String JavaDoc defActionName = ((MultiKeyBinding)defaultKeybs.get(key)).actionName;
212
213                         boolean hasKey = defaultKeybs.containsKey(key);
214                         //Also look for permutations, i.e. CA-F5 may be DA-F5, AD-F5 or AC-F5
215
if (!hasKey) {
216                             String JavaDoc[] s = getPermutations (key);
217                             for (int j=0; j < s.length && !hasKey; j++) {
218                                 hasKey |= defaultKeybs.containsKey(s[j]);
219                                 if (hasKey) {
220                                     key = s[j];
221                                     break;
222                                 }
223                             }
224                         }
225
226                         // if property is in default set and the action names are the same we don't have to write it
227
if (hasKey && curActionName.equals(defActionName)) save = false;
228                     }
229
230                     if (save){
231                         Element JavaDoc keybElem = doc.createElement(TAG_BIND);
232                         keybElem.setAttribute(ATTR_KEY, key);
233                         keybElem.setAttribute(ATTR_ACTION_NAME, curActionName);
234                         rootElem.appendChild(keybElem);
235                     }
236                 }
237             }
238
239             // remove deleted properties from local Map
240
for (int i=0; i<removed.size(); i++){
241                 properties.remove(removed.get(i));
242             }
243
244             doc.getDocumentElement().normalize();
245
246             saveSettings(doc);
247         }
248     }
249     
250     private static String JavaDoc tryRemoveKeyFromMap (Document JavaDoc doc, Map JavaDoc props, String JavaDoc key, Map JavaDoc defaultKeybs, Element JavaDoc root) {
251         // if deleted property is in default set, mark it as deleted
252
if (defaultKeybs.containsKey(key)){
253             removeKeyFromMap (doc, props, key, root);
254             return key;
255         } else {
256             String JavaDoc[] s = getPermutations(key);
257             for (int i=0; i < s.length; i++) {
258                 if (defaultKeybs.containsKey(s[i])){
259                     removeKeyFromMap (doc, props, key, root);
260                     return s[i];
261                 }
262             }
263         }
264         return null;
265     }
266     
267     private static void removeKeyFromMap(Document JavaDoc doc, Map JavaDoc props, String JavaDoc key, Element JavaDoc root) {
268         Element JavaDoc keybElem = doc.createElement(TAG_BIND);
269         keybElem.setAttribute(ATTR_KEY, key);
270         keybElem.setAttribute(ATTR_REMOVE, Boolean.TRUE.toString());
271         root.appendChild(keybElem);
272     }
273     
274     /**
275      * There is no required ordering of key modifiers (C, M, S, A), and the
276      * D (default) wildcard character can map to either C or M depending on
277      * the platform. So when we need to delete a keybinding, the editor has
278      * given us one possible ordering, but not necessarily the correct one; it
279      * has also given us a hard keybinding, but the key may really be bound to
280      * D. So, for "MAS-F5" (meta-alt-shift F5) on the pc, we need to check
281      * MSA-F5, SMA-F5, SAM-F5, AMS-F5, ASM-F5; on the mac, we also need to check
282      * the same permutations of DAS-F5, since it may be registered with the
283      * wildcard character.
284      * <p>
285      * Finally, for each permutation, it is legal to separate characters with
286      * dashes - so for each permutation, we must also check for a hyphenated
287      * variant - i.e. for MAS-F5, we must check M-A-S-F5. Note that mixed
288      * hyphenation (M-AS-F5) is not supported. It either is or it isn't.
289      *
290      */

291     static String JavaDoc[] getPermutations (String JavaDoc name) {
292         //IMPORTANT: THERE IS A COPY OF THE SAME CODE IN
293
//org.netbeans.core.ShortcutsFolder (it has unit tests there)
294
//ANY CHANGES MADE HERE SHOULD ALSO BE MADE THERE!
295
String JavaDoc key = KeyEvent.META_MASK == Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() ?
296             "M" : "C"; //NOI18N
297

298         String JavaDoc ctrlWildcard = "D"; //NOI18N
299

300         String JavaDoc altKey = System.getProperty ("mrj.version") != null ?
301             "C" : "A"; //NOI18N
302

303         String JavaDoc altWildcard = "O"; //NOI18N
304

305         
306         int pos = name.lastIndexOf ("-"); //NOI18N
307
if (pos == -1) {
308             //#49590 - key binding like "F11" will not have modifiers
309
return new String JavaDoc[] { name };
310         }
311         String JavaDoc keyPart = name.substring (pos);
312         String JavaDoc modsPart = org.openide.util.Utilities.replaceString (name.substring (0, pos), "-", ""); //NOI18N
313
if (modsPart.length() > 1) {
314             Collection JavaDoc perms = new HashSet JavaDoc(modsPart.length() * modsPart.length());
315             int idx = name.indexOf(key);
316             if (idx != -1) {
317                 //First, try with the wildcard key. Remove all hyphens - we'll
318
//put them back later
319
StringBuffer JavaDoc sb = new StringBuffer JavaDoc(modsPart);
320                 sb.replace(idx, idx+1, ctrlWildcard);
321                 perms.add (sb.toString() + keyPart);
322                 getAllPossibleOrderings (sb.toString(), keyPart, perms);
323                 createHyphenatedPermutation(sb.toString().toCharArray(), perms, keyPart);
324                 idx = name.indexOf (altKey);
325                 if (idx != -1) {
326                     sb.replace (idx, idx+1, altWildcard);
327                     perms.add (sb.toString() + keyPart);
328                     getAllPossibleOrderings (sb.toString(), keyPart, perms);
329                     createHyphenatedPermutation(sb.toString().toCharArray(), perms, keyPart);
330                 } else {
331                     idx = name.indexOf(altWildcard);
332                     if (idx != -1) {
333                         sb.replace (idx, idx+1, altKey);
334                         perms.add (sb.toString() + keyPart);
335                         getAllPossibleOrderings (sb.toString(), keyPart, perms);
336                         createHyphenatedPermutation(sb.toString().toCharArray(), perms, keyPart);
337                     }
338                 }
339             } else {
340                 idx = name.indexOf (ctrlWildcard); //NOI18N
341
if (idx != -1) {
342                     StringBuffer JavaDoc sb = new StringBuffer JavaDoc(modsPart);
343                     sb.replace(idx, idx+1, key);
344                     perms.add (sb.toString() + keyPart);
345                     getAllPossibleOrderings (sb.toString(), keyPart, perms);
346                     createHyphenatedPermutation(sb.toString().toCharArray(), perms, keyPart);
347                     idx = name.indexOf (altKey);
348                     if (idx != -1) {
349                         sb.replace (idx, idx+1, altWildcard);
350                         perms.add (sb.toString() + keyPart);
351                         getAllPossibleOrderings (sb.toString(), keyPart, perms);
352                     } else {
353                         idx = name.indexOf(altWildcard);
354                         if (idx != -1) {
355                             sb.replace (idx, idx+1, altKey);
356                             perms.add (sb.toString() + keyPart);
357                             getAllPossibleOrderings (sb.toString(), keyPart, perms);
358                             createHyphenatedPermutation(sb.toString().toCharArray(), perms, keyPart);
359                         }
360                     }
361                 }
362             }
363             
364             idx = name.indexOf (altKey);
365             if (idx != -1) {
366                 StringBuffer JavaDoc sb = new StringBuffer JavaDoc(modsPart);
367                 sb.replace (idx, idx+1, altWildcard);
368                 perms.add (sb.toString() + keyPart);
369                 getAllPossibleOrderings (sb.toString(), keyPart, perms);
370                 createHyphenatedPermutation(sb.toString().toCharArray(), perms, keyPart);
371             } else {
372                 StringBuffer JavaDoc sb = new StringBuffer JavaDoc(modsPart);
373                 idx = name.indexOf(altWildcard);
374                 if (idx != -1) {
375                     sb.replace (idx, idx+1, altKey);
376                     perms.add (sb.toString() + keyPart);
377                     getAllPossibleOrderings (sb.toString(), keyPart, perms);
378                     createHyphenatedPermutation(sb.toString().toCharArray(), perms, keyPart);
379                 }
380             }
381             
382             getAllPossibleOrderings (modsPart, keyPart, perms);
383             createHyphenatedPermutation(modsPart.toCharArray(), perms, keyPart);
384             return (String JavaDoc[]) perms.toArray(new String JavaDoc[perms.size()]);
385         } else {
386             return key.equals (modsPart) ?
387                 new String JavaDoc[] {ctrlWildcard + keyPart} : altKey.equals(modsPart) ?
388                     new String JavaDoc[]{altWildcard + keyPart} : altWildcard.equals(modsPart) ?
389                     new String JavaDoc[] {altKey + keyPart} :
390                     new String JavaDoc[0];
391         }
392     }
393     
394     /**
395      * Retrieves all the possible orders for the passed in string, and puts them
396      * in the passed collection, appending <code>toAppend</code> to each.
397      */

398     static void getAllPossibleOrderings (String JavaDoc s, String JavaDoc toAppend, final Collection JavaDoc store) {
399         //IMPORTANT: THIS IS A COPY OF THE SAME CODE IN org.netbeans.core.ShortcutsFolder.
400
//ANY CHANGES MADE HERE SHOULD ALSO BE MADE THERE!
401
char[] c = s.toCharArray();
402         mutate (c, store, 0, toAppend);
403         String JavaDoc[] result = (String JavaDoc[]) store.toArray(new String JavaDoc[store.size()]);
404     }
405     
406     /**
407      * Recursively generates all possible orderings of the passed char array
408      */

409     private static void mutate(char[] c, Collection JavaDoc l, int n, String JavaDoc toAppend) {
410         //IMPORTANT: THIS IS A COPY OF THE SAME CODE IN org.netbeans.core.ShortcutsFolder.
411
//ANY CHANGES MADE HERE SHOULD ALSO BE MADE THERE!
412
if (n == c.length) {
413             l.add (new String JavaDoc(c) + toAppend);
414             createHyphenatedPermutation(c, l, toAppend);
415             return;
416         }
417         //XXX could be optimized to eliminate duplicates
418
for (int i=0; i < c.length; i++) {
419             char x = c[i];
420             c[i] = c[n];
421             c[n] = x;
422             if (n < c.length) {
423                 mutate (c, l, n+1, toAppend);
424             }
425         }
426     }
427     
428     /**
429      * Inserts "-" characters between each character in the char array and
430      * adds the result + toAppend to the collection.
431      */

432     static void createHyphenatedPermutation (char[] c, Collection JavaDoc l, String JavaDoc toAppend) {
433         //IMPORTANT: THIS IS A COPY OF THE SAME CODE IN org.netbeans.core.ShortcutsFolder.
434
//ANY CHANGES MADE HERE SHOULD ALSO BE MADE THERE!
435
if (c.length == 1) {
436             return;
437         }
438         StringBuffer JavaDoc sb = new StringBuffer JavaDoc (new String JavaDoc(c));
439         for (int i=c.length-1; i >= 1; i-=1) {
440             sb.insert (i, '-');
441         }
442         sb.append (toAppend);
443         l.add (sb.toString());
444     }
445 }
446
Popular Tags