1 19 20 package org.netbeans.modules.options.keymap; 21 22 23 import java.awt.Component ; 24 import java.util.ArrayList ; 25 import java.util.Arrays ; 26 import java.util.Collections ; 27 import java.util.Comparator ; 28 import java.util.HashMap ; 29 import java.util.HashSet ; 30 import java.util.Iterator ; 31 import java.util.List ; 32 import java.util.Map ; 33 import java.util.Set ; 34 import java.util.StringTokenizer ; 35 import java.util.TreeMap ; 36 import java.util.Vector ; 37 import javax.swing.AbstractButton ; 38 import javax.swing.JLabel ; 39 import javax.swing.KeyStroke ; 40 import javax.swing.SwingUtilities ; 41 import javax.swing.event.TreeModelEvent ; 42 import javax.swing.event.TreeModelListener ; 43 import javax.swing.tree.TreeModel ; 44 import javax.swing.tree.TreePath ; 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 57 public class KeymapViewModel implements TreeModel , ShortcutsFinder { 58 59 private Vector listeners = new Vector (); 60 private String currentProfile; 61 private KeymapModel model = new KeymapModel (); 62 private Map categoryToActionsCache = new HashMap (); 65 private Map modifiedProfiles = new HashMap (); 68 private Set deletedProfiles = new HashSet (); 70 private Map shortcutsCache = new HashMap (); 72 73 static final ActionsComparator actionsComparator = new ActionsComparator (); 74 75 76 79 public KeymapViewModel () { 80 currentProfile = model.getCurrentProfile (); 81 } 82 83 84 86 public Object getRoot () { 87 return ""; 88 } 89 90 public Object getChild (Object parent, int index) { 91 return getItems ((String ) parent).get (index); 92 } 93 94 public int getChildCount (Object parent) { 95 if (parent instanceof String ) 96 return getItems ((String ) parent).size (); 97 return 0; 98 } 99 100 public boolean isLeaf (Object node) { 101 return !(node instanceof String ); 102 } 103 104 public void valueForPathChanged (TreePath path, Object newValue) {} 105 106 public int getIndexOfChild (Object parent, Object child) { 107 return getItems ((String ) parent).indexOf (child); 108 } 109 110 public void addTreeModelListener (TreeModelListener l) { 111 listeners.add (l); 112 } 113 114 public void removeTreeModelListener (TreeModelListener l) { 115 listeners.remove (l); 116 } 117 118 private void treeChanged () { 119 final Vector v = (Vector ) listeners.clone (); 120 SwingUtilities.invokeLater (new Runnable () { 121 public void run () { 122 TreeModelEvent tme = new TreeModelEvent (this, new Object [] {""}); 123 int i, k = v.size (); 124 for (i = 0; i < k; i++) 125 ((TreeModelListener ) v.get (i)).treeNodesChanged (tme); 126 } 127 }); 128 } 129 130 private void nodeChanged (final TreePath path) { 131 final Vector v = (Vector ) listeners.clone (); 132 SwingUtilities.invokeLater (new Runnable () { 133 public void run () { 134 TreeModelEvent tme = new TreeModelEvent (this, path); 135 int i, k = v.size (); 136 for (i = 0; i < k; i++) 137 ((TreeModelListener ) v.get (i)).treeNodesChanged (tme); 138 } 139 }); 140 } 141 142 143 145 private Map categories; 147 148 149 156 public Map getCategories () { 157 if (categories == null) { 158 categories = new TreeMap (); 159 List c = new ArrayList (model.getActionCategories ()); 160 Collections.sort (c); 161 Iterator it = c.iterator (); 162 while (it.hasNext ()) { 163 String cn = (String ) it.next (); 164 String folderName = ""; 165 StringTokenizer st = new StringTokenizer (cn, "/"); 166 while (st.hasMoreTokens ()) { 167 String name = st.nextToken (); 168 List asd = (List ) categories.get (folderName); 169 if (asd == null) { 170 asd = new ArrayList (); 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 189 public List getItems (String category) { 190 List result = (List ) categoryToActionsCache.get (category); 191 if (result == null) { 192 result = new ArrayList (); 193 List ll = (List ) getCategories ().get (category); 194 if (ll != null) 195 result.addAll (ll); 196 List l = new ArrayList (model.getActions (category)); 197 Collections.sort (l, new ActionsComparator ()); 198 result.addAll (l); 199 categoryToActionsCache.put (category, result); 200 } 202 return result; 203 } 204 205 209 210 212 List getProfiles () { 213 Set result = new HashSet (model.getProfiles ()); 214 result.addAll (modifiedProfiles.keySet ()); 215 List r = new ArrayList (result); 216 Collections.sort (r); 217 return r; 218 } 219 220 boolean isCustomProfile (String profile) { 221 return model.isCustomProfile (profile); 222 } 223 224 void deleteProfile (String profile) { 225 if (model.isCustomProfile (profile)) { 226 deletedProfiles.add (profile); 227 modifiedProfiles.remove (profile); 228 } else { 229 Map m = model.getKeymapDefaults (profile); 230 m = convertFromEmacs (m); 231 modifiedProfiles.put (profile, m); 232 treeChanged (); 233 } 234 } 235 236 String getCurrentProfile () { 237 return currentProfile; 238 } 239 240 void setCurrentProfile (String currentKeymap) { 241 this.currentProfile = currentKeymap; 242 treeChanged (); 243 } 244 245 void cloneProfile (String newProfileName) { 246 Map result = new HashMap (); 247 cloneProfile ("", result); 248 modifiedProfiles.put (newProfileName, result); 249 } 250 251 private void cloneProfile ( 252 String category, Map result ) { 255 Iterator it = getItems (category).iterator (); 256 while (it.hasNext ()) { 257 Object o = it.next (); 258 if (o instanceof String ) 259 cloneProfile ((String ) o, result); 260 else { 261 String [] shortcuts = getShortcuts ((ActionImpl) o); 262 result.put (o, new HashSet (Arrays.asList (shortcuts))); 263 } 264 } 265 } 266 267 public ActionImpl findActionForShortcut (String shortcut) { 268 return findActionForShortcut (shortcut, ""); 269 } 270 271 private ActionImpl findActionForShortcut (String shortcut, String category) { 272 Iterator it = getItems (category).iterator (); 273 while (it.hasNext ()) { 274 Object o = it.next (); 275 if (o instanceof String ) { 276 ActionImpl result = findActionForShortcut (shortcut, (String ) o); 277 if (result != null) return result; 278 continue; 279 } 280 ActionImpl action = (ActionImpl) o; 281 String [] 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 actionId) { 292 if (SwingUtilities.isEventDispatchThread ()) 293 return findActionForId (actionId, ""); 294 295 final ActionImpl[] result = new ActionImpl [1]; 296 try { 297 SwingUtilities.invokeAndWait (new Runnable () { 298 public void run () { 299 result [0] = findActionForId (actionId, ""); 300 } 301 }); 302 } catch (Exception ex) { 303 ErrorManager.getDefault ().notify (ex); 304 } 305 return result [0]; 306 } 307 308 private ActionImpl findActionForId (String actionId, String category) { 309 Iterator it = getItems (category).iterator (); 310 while (it.hasNext ()) { 311 Object o = it.next (); 312 if (o instanceof String ) { 313 ActionImpl result = findActionForId (actionId, (String ) o); 314 if (result != null) return result; 315 continue; 316 } 317 String id = ((ActionImpl) o).getId (); 318 if (actionId.equals (id)) 319 return (ActionImpl) o; 320 } 321 return null; 322 } 323 324 public String [] getShortcuts (ActionImpl action) { 325 if (modifiedProfiles.containsKey (currentProfile)) { 326 Map actionToShortcuts = (Map ) modifiedProfiles. 328 get (currentProfile); 329 if (actionToShortcuts.containsKey (action)) { 330 Set s = (Set ) actionToShortcuts.get (action); 331 return (String []) s.toArray (new String [s.size ()]); 332 } 333 } 334 335 if (!shortcutsCache.containsKey (currentProfile)) { 336 Map profileMap = convertFromEmacs (model.getKeymap (currentProfile)); 338 shortcutsCache.put ( 339 currentProfile, 340 profileMap 341 ); 342 } 343 Map profileMap = (Map ) shortcutsCache.get (currentProfile); 344 Set shortcuts = (Set ) profileMap.get (action); 345 if (shortcuts == null) return new String [0]; 346 return (String []) shortcuts.toArray (new String [shortcuts.size ()]); 347 } 348 349 void addShortcut (TreePath path, String shortcut) { 350 ActionImpl action = findActionForShortcut (shortcut); 352 if (action != null) 353 removeShortcut (action, shortcut); 354 action = (ActionImpl) path.getLastPathComponent (); 355 Set s = new HashSet (); 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 shortcuts) { 363 Map actionToShortcuts = (Map ) modifiedProfiles.get (currentProfile); 364 if (actionToShortcuts == null) { 365 actionToShortcuts = new HashMap (); 366 modifiedProfiles.put (currentProfile, actionToShortcuts); 367 } 368 actionToShortcuts.put (action, shortcuts); 369 } 370 371 void removeShortcut (TreePath path, String shortcut) { 372 ActionImpl action = (ActionImpl) path.getLastPathComponent (); 373 removeShortcut (action, shortcut); 374 nodeChanged (path); 375 } 376 377 private void removeShortcut (ActionImpl action, String shortcut) { 378 Set s = new HashSet (Arrays.asList (getShortcuts (action))); 379 s.remove (shortcut); 380 Map actionToShortcuts = (Map ) modifiedProfiles.get (currentProfile); 381 if (actionToShortcuts == null) { 382 actionToShortcuts = new HashMap (); 383 modifiedProfiles.put (currentProfile, actionToShortcuts); 384 } 385 actionToShortcuts.put (action, s); 386 } 387 388 public void refreshActions () { 389 categoryToActionsCache = new HashMap (); 390 model.refreshActions (); 391 } 392 393 public void apply () { 394 RequestProcessor.getDefault ().post (new Runnable () { 395 public void run () { 396 Iterator it = modifiedProfiles.keySet ().iterator (); 397 while (it.hasNext ()) { 398 String profile = (String ) it.next (); 399 Map actionToShortcuts = (Map ) 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 profile = (String ) it.next (); 409 model.deleteProfile (profile); 410 } 411 model.setCurrentProfile (currentProfile); 412 modifiedProfiles = new HashMap (); 413 deletedProfiles = new HashSet (); 414 shortcutsCache = new HashMap (); 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 (); 426 deletedProfiles = new HashSet (); 427 shortcutsCache = new HashMap (); 428 setCurrentProfile (model.getCurrentProfile ()); 429 model = new KeymapModel (); 430 } 431 432 436 private static Map convertToEmacs (Map shortcuts) { 437 Map result = new HashMap (); 438 Iterator it = shortcuts.keySet ().iterator (); 439 while (it.hasNext ()) { 440 Object action = it.next (); 441 Set sh = (Set ) shortcuts.get (action); 442 Set newSet = new HashSet (); 443 Iterator it2 = sh.iterator (); 444 while (it2.hasNext ()) { 445 String s = (String ) it2.next (); 446 if (s.length () == 0) continue; 447 KeyStroke [] ks = getKeyStrokes (s, " "); 448 if (ks == null) 449 continue; StringBuffer sb = new StringBuffer ( 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 467 private static Map convertFromEmacs (Map emacs) { 468 Map result = new HashMap (); 469 Iterator it = emacs.keySet ().iterator (); 470 while (it.hasNext ()) { 471 ActionImpl action = (ActionImpl) it.next (); 472 Set emacsShortcuts = (Set ) emacs.get (action); 473 Iterator it2 = emacsShortcuts.iterator (); 474 Set shortcuts = new HashSet (); 475 while (it2.hasNext ()) { 476 String emacsShortcut = (String ) it2.next (); 477 KeyStroke [] keyStroke = Utilities.stringToKeys (emacsShortcut); 478 shortcuts.add (Utils.getKeyStrokesAsText (keyStroke, " ")); 479 } 480 result.put (action, shortcuts); 481 } 482 return result; 483 } 484 485 490 private static KeyStroke [] getKeyStrokes (String keyStrokes, String delim) { 491 if (keyStrokes.length () == 0) return new KeyStroke [0]; 492 StringTokenizer st = new StringTokenizer (keyStrokes, delim); 493 List result = new ArrayList (); 494 while (st.hasMoreTokens ()) { 495 String ks = st.nextToken ().trim (); 496 KeyStroke keyStroke = Utils.getKeyStroke (ks); 497 if (keyStroke == null) return null; result.add (keyStroke); 499 } 500 return (KeyStroke []) result.toArray (new KeyStroke [result.size ()]); 501 } 502 503 private static String loc (String key) { 504 return NbBundle.getMessage (KeymapPanel.class, key); 505 } 506 507 private static void loc (Component c, String key) { 508 if (c instanceof AbstractButton ) 509 Mnemonics.setLocalizedText ( 510 (AbstractButton ) c, 511 loc (key) 512 ); 513 else 514 Mnemonics.setLocalizedText ( 515 (JLabel ) c, 516 loc (key) 517 ); 518 } 519 520 521 523 static class ActionsComparator implements Comparator { 524 525 public int compare (Object o1, Object o2) { 526 if (o1 instanceof String ) 527 if (o2 instanceof String ) 528 return ((String ) o1).compareTo ((String ) o2); 529 else 530 return 1; 531 else 532 if (o2 instanceof String ) 533 return -1; 534 else 535 return ((ActionImpl) o1).getDisplayName ().compareTo ( 536 ((ActionImpl) o2).getDisplayName () 537 ); 538 } 539 } 540 } 541 | Popular Tags |