1 19 20 package org.netbeans.modules.editor.settings.storage; 21 22 import java.beans.PropertyChangeEvent ; 23 import java.beans.PropertyChangeListener ; 24 import java.beans.PropertyChangeSupport ; 25 import java.lang.ref.WeakReference ; 26 import java.util.ArrayList ; 27 import java.util.Collection ; 28 import java.util.Collections ; 29 import java.util.HashMap ; 30 import java.util.HashSet ; 31 import java.util.Iterator ; 32 import java.util.List ; 33 import java.util.Map ; 34 import java.util.Set ; 35 import java.util.WeakHashMap ; 36 import java.util.logging.Level ; 37 import java.util.logging.Logger ; 38 import javax.swing.KeyStroke ; 39 import org.netbeans.api.editor.mimelookup.MimePath; 40 import org.netbeans.api.editor.settings.KeyBindingSettings; 41 import org.netbeans.api.editor.settings.MultiKeyBinding; 42 import org.netbeans.modules.editor.settings.storage.api.EditorSettings; 43 import org.netbeans.modules.editor.settings.storage.api.KeyBindingSettingsFactory; 44 import org.openide.filesystems.FileAttributeEvent; 45 import org.openide.filesystems.FileChangeListener; 46 import org.openide.filesystems.FileEvent; 47 import org.openide.filesystems.FileObject; 48 import org.openide.filesystems.FileRenameEvent; 49 import org.openide.util.Utilities; 50 51 62 public final class KeyBindingSettingsImpl extends KeyBindingSettingsFactory { 63 64 private static final Logger LOG = Logger.getLogger(KeyBindingSettingsImpl.class.getName()); 65 66 private static final Map <MimePath, WeakReference <KeyBindingSettingsImpl>> INSTANCES = 67 new WeakHashMap <MimePath, WeakReference <KeyBindingSettingsImpl>>(); 68 69 public static synchronized KeyBindingSettingsImpl get(MimePath mimePath) { 70 WeakReference <KeyBindingSettingsImpl> reference = INSTANCES.get(mimePath); 71 KeyBindingSettingsImpl result = reference == null ? null : reference.get(); 72 73 if (result == null) { 74 result = new KeyBindingSettingsImpl(mimePath); 75 INSTANCES.put(mimePath, new WeakReference <KeyBindingSettingsImpl>(result)); 76 } 77 78 return result; 79 } 80 81 private MimePath mimePath; 82 private PropertyChangeSupport pcs; 83 private Map <String , List <MultiKeyBinding>> keyMaps = new HashMap <String , List <MultiKeyBinding>>(); 84 private KeyBindingSettingsFactory baseKBS; 85 private Listener listener; 86 87 private String logActionName = null; 88 89 92 private KeyBindingSettingsImpl (MimePath mimePath) { 93 this.mimePath = mimePath; 94 pcs = new PropertyChangeSupport (this); 95 96 String myClassName = KeyBindingSettingsImpl.class.getName (); 98 String value = System.getProperty(myClassName); 99 if (value != null) { 100 if (!value.equals("true")) { 101 logActionName = System.getProperty(myClassName); 102 } 103 } else if (mimePath.size() == 1) { 104 logActionName = System.getProperty(myClassName + '.' + mimePath.getMimeType(0)); 105 } 106 } 107 108 private boolean init = false; 109 private void init () { 110 if (init) return; 111 init = true; 112 if (mimePath.size() != 1 || !mimePath.getMimeType(0).equals("text/base")) { baseKBS = EditorSettingsImpl.getInstance().getKeyBindingSettings(new String [] {"text/base"}); } 115 listener = new Listener (this, baseKBS); 116 } 117 118 123 public List <MultiKeyBinding> getKeyBindings() { 124 return getKeyBindings(EditorSettingsImpl.getInstance().getCurrentKeyMapProfile()); 125 } 126 127 132 public List <MultiKeyBinding> getKeyBindings(String profile) { 133 init (); 134 135 profile = EditorSettingsImpl.getInstance().getInternalKeymapProfile(profile); 137 138 List <MultiKeyBinding> result = new ArrayList <MultiKeyBinding>(); 139 if (!keyMaps.containsKey (profile)) { 140 synchronized (this) { 141 142 Map <Collection <KeyStroke >, MultiKeyBinding> defaults = 145 new HashMap <Collection <KeyStroke >, MultiKeyBinding>(getDefaults(profile)); 146 147 Object [] ret = KeyMapsStorage.loadKeyMaps(mimePath, profile, false); 149 150 @SuppressWarnings ("unchecked") 151 Map <Collection <KeyStroke >, MultiKeyBinding> shortcuts = (Map <Collection <KeyStroke >, MultiKeyBinding>) ret[0]; 152 @SuppressWarnings ("unchecked") 153 Set <Collection <KeyStroke >> removedShortcuts = (Set <Collection <KeyStroke >>) ret[1]; 154 155 for(Collection <KeyStroke > s : removedShortcuts) { 156 defaults.remove(s); 157 } 158 defaults.putAll(shortcuts); 159 160 List <MultiKeyBinding> localShortcuts = new ArrayList <MultiKeyBinding>(defaults.values()); 161 keyMaps.put(profile, localShortcuts); 162 result.addAll(localShortcuts); 163 } 164 } else { 165 result.addAll(keyMaps.get(profile)); 166 } 167 168 log ("getKeyBindings", result); 169 170 if (baseKBS != null) { 172 List <MultiKeyBinding> baseShortcuts = baseKBS.getKeyBindings(profile); 173 log ("", Collections.EMPTY_LIST); 174 result.addAll(baseShortcuts); 175 } 176 177 return Collections.unmodifiableList(result); 178 } 179 180 186 public List <MultiKeyBinding> getKeyBindingDefaults(String profile) { 187 profile = EditorSettingsImpl.getInstance().getInternalKeymapProfile(profile); 189 return Collections.unmodifiableList(new ArrayList <MultiKeyBinding>(getDefaults(profile).values())); 190 } 191 192 198 public synchronized void setKeyBindings ( 199 String profile, 200 List <MultiKeyBinding> keyBindings 201 ) { 202 log ("setKeyBindings", keyBindings); 203 204 profile = EditorSettingsImpl.getInstance().getInternalKeymapProfile(profile); 206 207 init (); 208 if (keyBindings == null) { 209 keyMaps.remove (profile); 211 KeyMapsStorage.deleteProfile(mimePath, profile, false); 212 return; 213 } 214 keyMaps.put(profile, keyBindings); 215 216 Map <Collection <KeyStroke >, MultiKeyBinding> m = new HashMap <Collection <KeyStroke >, MultiKeyBinding>(); 219 for(MultiKeyBinding mkb : keyBindings) { 220 m.put(mkb.getKeyStrokeList(), mkb); 221 } 222 223 Map <Collection <KeyStroke >, MultiKeyBinding> defaults = getDefaults(profile); 225 Set <Collection <KeyStroke >> removed = new HashSet <Collection <KeyStroke >>(); 226 227 for(Collection <KeyStroke > shortcut : defaults.keySet()) { 228 MultiKeyBinding mkb2 = defaults.get(shortcut); 229 if (!m.containsKey (shortcut)) { 230 removed.add (shortcut); 231 } else { 232 MultiKeyBinding mkb1 = (MultiKeyBinding) m.get (shortcut); 233 if (mkb1.getActionName ().equals (mkb2.getActionName ())) 234 m.remove (shortcut); 235 } 236 } 237 238 log (" changed:", m.values ()); 239 log (" removed:", removed); 240 log ("", Collections.EMPTY_LIST); 241 242 listener.removeListeners (); 244 KeyMapsStorage.saveKeyMaps(mimePath, profile, false, m.values(), removed); 245 246 listener.addListeners (); 247 pcs.firePropertyChange (null, null, null); 248 } 249 250 255 public void addPropertyChangeListener (PropertyChangeListener l) { 256 pcs.addPropertyChangeListener (l); 257 } 258 259 264 public void removePropertyChangeListener (PropertyChangeListener l) { 265 pcs.removePropertyChangeListener (l); 266 } 267 268 270 private Map <String , Map <Collection <KeyStroke >, MultiKeyBinding>> defaults = 272 new HashMap <String , Map <Collection <KeyStroke >, MultiKeyBinding>>(); 273 274 280 private Map <Collection <KeyStroke >, MultiKeyBinding> getDefaults(String profile) { 281 if (!defaults.containsKey (profile)) { 282 Object [] ret = KeyMapsStorage.loadKeyMaps(mimePath, profile, true); 283 @SuppressWarnings ("unchecked") 284 Map <Collection <KeyStroke >, MultiKeyBinding> keyMap = 285 (Map <Collection <KeyStroke >, MultiKeyBinding>) ret[0]; 286 287 defaults.put(profile, keyMap); 288 } 289 290 return defaults.get (profile); 291 } 292 293 296 private void refresh () { 297 keyMaps = new HashMap <String , List <MultiKeyBinding>>(); 298 log ("refresh", Collections.EMPTY_SET); 299 pcs.firePropertyChange (null, null, null); 300 } 301 302 private void log (String text, Collection keymap) { 303 if (!LOG.isLoggable(Level.FINE)) { 304 return; 305 } 306 if (text.length() != 0) { 307 if (mimePath.size() == 1) { 308 text += " " + mimePath.getMimeType(0); 309 } 310 text += " " + EditorSettingsImpl.getInstance().getCurrentKeyMapProfile(); 311 } 312 if (keymap == null) { 313 LOG.fine(text + " : null"); 314 return; 315 } 316 LOG.fine(text); 317 Iterator it = keymap.iterator (); 318 while (it.hasNext ()) { 319 Object mkb = it.next (); 320 if (logActionName == null || !(mkb instanceof MultiKeyBinding)) { 321 LOG.fine(" " + mkb); 322 } else if (mkb instanceof MultiKeyBinding && 323 logActionName.equals(((MultiKeyBinding) mkb).getActionName ())) 324 { 325 LOG.fine(" " + mkb); 326 } 327 } 328 } 329 330 public Object createInstanceForLookup() { 331 List <MultiKeyBinding> keyB = getKeyBindings(); 332 return new Immutable(new ArrayList <MultiKeyBinding>(keyB)); 333 } 334 335 336 private static class Listener extends WeakReference <KeyBindingSettingsImpl> 337 implements FileChangeListener, PropertyChangeListener , Runnable { 338 339 340 private FileObject fo; 341 private KeyBindingSettingsFactory baseKBS; 342 343 Listener ( 344 KeyBindingSettingsImpl kb, 345 KeyBindingSettingsFactory baseKBS 346 ) { 347 super(kb, Utilities.activeReferenceQueue()); 348 this.baseKBS = baseKBS; 349 addListeners (); 350 } 351 352 private KeyBindingSettingsImpl getSettings () { 353 KeyBindingSettingsImpl r = get (); 354 if (r != null) return r; 355 removeListeners (); 356 return null; 357 } 358 359 private void addListeners () { 360 EditorSettingsImpl.getInstance().addPropertyChangeListener( 361 EditorSettings.PROP_CURRENT_KEY_MAP_PROFILE, 362 this 363 ); 364 if (baseKBS != null) 365 baseKBS.addPropertyChangeListener (this); 366 setFolderListener (); 367 } 368 369 private void removeListeners () { 370 fo.removeFileChangeListener (this); 371 if (baseKBS != null) 372 baseKBS.removePropertyChangeListener (this); 373 EditorSettingsImpl.getInstance().removePropertyChangeListener( 374 EditorSettings.PROP_CURRENT_KEY_MAP_PROFILE, 375 this 376 ); 377 } 378 379 public void propertyChange (PropertyChangeEvent evt) { 380 KeyBindingSettingsImpl r = getSettings (); 381 if (r == null) return; 382 if (EditorSettings.PROP_CURRENT_KEY_MAP_PROFILE.equals ( 383 evt.getPropertyName () 384 )) 385 setFolderListener (); 386 r.log ("refresh2", Collections.EMPTY_SET); 387 r.pcs.firePropertyChange (null, null, null); 388 } 389 390 public void run() { 391 removeListeners(); 392 } 393 394 public void fileDataCreated (FileEvent fe) { 395 } 396 397 public void fileChanged (FileEvent fe) { 398 KeyBindingSettingsImpl r = getSettings (); 399 if (r == null) return; 400 if (fe.getFile().getNameExt().equals(KeyMapsStorage.KEYBINDING_FILE_NAME)) { 401 r.refresh(); 402 } 403 } 404 405 public void fileDeleted (FileEvent fe) { 406 KeyBindingSettingsImpl r = getSettings (); 407 if (r == null) return; 408 if (fe.getFile().getNameExt().equals(KeyMapsStorage.KEYBINDING_FILE_NAME)) { 409 r.refresh(); 410 } 411 } 412 413 public void fileFolderCreated(FileEvent fe) { 414 } 415 416 public void fileRenamed(FileRenameEvent fe) { 417 } 418 419 public void fileAttributeChanged(FileAttributeEvent fe) { 420 } 421 422 private void setFolderListener() { 423 if (fo != null) fo.removeFileChangeListener (this); 424 String profile = EditorSettingsImpl.getInstance().getCurrentKeyMapProfile(); 425 if (profile.equals(EditorSettingsImpl.DEFAULT_PROFILE)) profile = null; 426 427 KeyBindingSettingsImpl kbsi = getSettings(); 428 if (kbsi != null) { 429 fo = Utils.createFileObject(kbsi.mimePath, profile, null); 430 fo.addFileChangeListener (this); 431 } 432 } 433 } 434 435 static final class Immutable extends KeyBindingSettings { 436 private List <MultiKeyBinding> keyBindings; 437 438 public Immutable(List <MultiKeyBinding> keyBindings) { 439 this.keyBindings = keyBindings; 440 } 441 442 public List <MultiKeyBinding> getKeyBindings() { 443 return Collections.unmodifiableList(keyBindings); 444 } 445 } 446 447 } 448 | Popular Tags |