Provides optimizations to the keybindings with respect to execution time and garbage creation no matter how minor because the keybindings are used in MainFrame and ChatWindow which are in turn always in use.

cusax-fix
Lyubomir Marinov 18 years ago
parent 6f04d3bf48
commit a7dfe3619f

@ -8,15 +8,15 @@
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.keybindings.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.service.keybindings.*;
import java.util.*;
/**
* A custom frame that remembers its size and location and could have a
@ -79,6 +79,7 @@ public void actionPerformed(ActionEvent e)
* Sets the input map to utilize a given category of keybindings. The frame
* is updated to reflect the new bindings when they change. This replaces
* any previous bindings that have been added.
*
* @param category set of keybindings to be utilized
*/
protected void setKeybindingInput(KeybindingSet.Category category)
@ -91,17 +92,16 @@ protected void setKeybindingInput(KeybindingSet.Category category)
}
// Adds new bindings to input map
KeybindingsService service = GuiActivator.getKeybindingsService();
this.bindings = service.getBindings(category);
this.bindings =
GuiActivator.getKeybindingsService().getBindings(category);
for (KeyStroke key : this.bindings.getBindings().keySet())
for (Map.Entry<KeyStroke, String> key2action : this.bindings
.getBindings().entrySet())
{
String action = this.bindings.getBindings().get(key);
imap.put(key, action);
imap.put(key2action.getKey(), key2action.getValue());
}
this.bindings.addObserver(this);
}
/**
@ -382,18 +382,21 @@ private void resetInputMap()
imap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "close");
}
// Listens for changes in binding sets so they can be reflected in the input
// map
/**
* Listens for changes in binding sets so they can be reflected in the input
* map
*/
public void update(Observable obs, Object arg)
{
if (obs instanceof KeybindingSet)
{
KeybindingSet changedBindings = (KeybindingSet) obs;
resetInputMap();
for (KeyStroke binding : changedBindings.getBindings().keySet())
for (Map.Entry<KeyStroke, String> key2action : changedBindings
.getBindings().entrySet())
{
String action = changedBindings.getBindings().get(binding);
imap.put(binding, action);
imap.put(key2action.getKey(), key2action.getValue());
}
}
}

@ -6,25 +6,27 @@
*/
package net.java.sip.communicator.impl.keybindings;
import net.java.sip.communicator.service.keybindings.KeybindingsService;
import net.java.sip.communicator.util.Logger;
import net.java.sip.communicator.service.keybindings.*;
import net.java.sip.communicator.util.*;
import org.osgi.framework.*;
/**
* Enabling and disabling osgi functionality for keybindings.
*
* @author Damian Johnson
*/
public class KeybindingsActivator
implements BundleActivator
{
private static final Logger logger =
Logger.getLogger(KeybindingsActivator.class);
Logger.getLogger(KeybindingsActivator.class);
private KeybindingsServiceImpl keybindingsService = null;
/**
* Called when this bundle is started.
*
* @param context The execution context of the bundle being started.
*/
public void start(BundleContext context)
@ -32,17 +34,18 @@ public void start(BundleContext context)
if (this.keybindingsService == null)
{
logger.debug("Service Impl: " + getClass().getName()
+ " [ STARTED ]");
+ " [ STARTED ]");
this.keybindingsService = new KeybindingsServiceImpl();
this.keybindingsService.start(context);
context.registerService(KeybindingsService.class.getName(),
this.keybindingsService, null);
this.keybindingsService, null);
}
}
/**
* Called when this bundle is stopped so the Framework can perform the
* bundle-specific activities necessary to stop the bundle.
*
* @param context The execution context of the bundle being stopped.
*/
public void stop(BundleContext context)
@ -53,4 +56,4 @@ public void stop(BundleContext context)
this.keybindingsService = null;
}
}
}
}

@ -7,18 +7,18 @@
package net.java.sip.communicator.impl.keybindings;
import java.io.*;
import java.text.ParseException;
import java.text.*;
import java.util.*;
import javax.swing.KeyStroke;
import javax.swing.*;
import org.osgi.framework.*;
import chooser.Persistence;
import chooser.*;
import net.java.sip.communicator.service.fileaccess.FileAccessService;
import net.java.sip.communicator.service.fileaccess.*;
import net.java.sip.communicator.service.keybindings.*;
import net.java.sip.communicator.util.Logger;
import net.java.sip.communicator.util.*;
/**
* Service that concerns keybinding mappings used by various parts of the UI.
@ -31,83 +31,86 @@
* </ol>
* Custom bindings attempt to be written again whenever they're changed if the
* service is running. Each category of keybindings are stored in its own file.
*
* @author Damian Johnson
*/
class KeybindingsServiceImpl
implements KeybindingsService, Observer
{
private static final Logger logger =
Logger.getLogger(KeybindingsServiceImpl.class);
Logger.getLogger(KeybindingsServiceImpl.class);
// Name of the relative directory that holds default bindings
private static final String DEFAULT_KEYBINDING_DIR =
"/resources/config/defaultkeybindings";
"/resources/config/defaultkeybindings";
// Name of the directory that holds custom bindings
private static final String CUSTOM_KEYBINDING_DIR = "keybindings";
// Flag indicating if service is running
private boolean isRunning = false;
// Loaded keybinding mappings, maps to null if defaults failed to be loaded
private final HashMap <KeybindingSet.Category, KeybindingSetImpl> bindings =
new HashMap <KeybindingSet.Category, KeybindingSetImpl>();
private final HashMap<KeybindingSet.Category, KeybindingSetImpl> bindings =
new HashMap<KeybindingSet.Category, KeybindingSetImpl>();
/**
* Starts the KeybindingService, for each keybinding category retrieving the
* default bindings then overwriting them with any custom bindings that can
* be retrieved. This writes the merged copy back if it differs from the
* custom bindings. This is a no-op if the service has already been started.
*
* @param bc the currently valid OSGI bundle context.
*/
synchronized void start(BundleContext bc)
{
if (this.isRunning) return;
if (this.isRunning)
return;
for (KeybindingSet.Category category : KeybindingSet.Category.values())
{
// Retrieves default bindings
Persistence format = category.getFormat();
LinkedHashMap <KeyStroke, String> defaultBindings;
LinkedHashMap<KeyStroke, String> defaultBindings;
try
{
String defaultPath =
DEFAULT_KEYBINDING_DIR + "/" + category.getResource();
DEFAULT_KEYBINDING_DIR + "/" + category.getResource();
InputStream in = getClass().getResourceAsStream(defaultPath);
defaultBindings = format.load(in);
}
catch (IOException exc)
{
logger.error("default bindings set missing: "
+ category.getResource(), exc);
+ category.getResource(), exc);
this.bindings.put(category, null);
continue;
}
catch (ParseException exc)
{
logger.error("unable to parse default bindings set: "
+ category.getResource(), exc);
+ category.getResource(), exc);
this.bindings.put(category, null);
continue;
}
// Attempts to retrieve custom bindings
String customPath =
CUSTOM_KEYBINDING_DIR + "/" + category.getResource();
CUSTOM_KEYBINDING_DIR + "/" + category.getResource();
File customFile;
try
{
ServiceReference faServiceReference =
bc.getServiceReference(FileAccessService.class
.getName());
bc.getServiceReference(FileAccessService.class.getName());
FileAccessService faService =
(FileAccessService) bc.getService(faServiceReference);
(FileAccessService) bc.getService(faServiceReference);
// Makes directory for custom bindings if it doesn't exist
File customDir =
faService
.getPrivatePersistentDirectory(CUSTOM_KEYBINDING_DIR);
if (!customDir.exists()) customDir.mkdir();
faService
.getPrivatePersistentDirectory(CUSTOM_KEYBINDING_DIR);
if (!customDir.exists())
customDir.mkdir();
// Gets file access service to reference persistent storage
// of the user
customFile = faService.getPrivatePersistentFile(customPath);
@ -115,18 +118,17 @@ synchronized void start(BundleContext bc)
catch (Exception exc)
{
String msg =
"unable to secure file for custom bindings ("
+ customPath
+ "), using defaults but won't be able to save changes";
"unable to secure file for custom bindings (" + customPath
+ "), using defaults but won't be able to save changes";
logger.error(msg, exc);
KeybindingSetImpl newSet =
new KeybindingSetImpl(defaultBindings, category, null);
new KeybindingSetImpl(defaultBindings, category, null);
this.bindings.put(category, newSet);
newSet.addObserver(this);
continue;
}
LinkedHashMap <KeyStroke, String> customBindings = null;
LinkedHashMap<KeyStroke, String> customBindings = null;
if (customFile.exists())
{
try
@ -141,38 +143,40 @@ synchronized void start(BundleContext bc)
// loading custom bindings
}
}
// Merges custom bindings
LinkedHashMap <KeyStroke, String> merged =
new LinkedHashMap <KeyStroke, String>();
LinkedHashMap<KeyStroke, String> merged =
new LinkedHashMap<KeyStroke, String>();
if (customBindings != null)
{
LinkedHashMap <KeyStroke, String> customTmp =
new LinkedHashMap <KeyStroke, String>(customBindings);
for (KeyStroke shortcut : defaultBindings.keySet())
Map<KeyStroke, String> customTmp =
new LinkedHashMap<KeyStroke, String>(customBindings);
for (Map.Entry<KeyStroke, String> shortcut2action : defaultBindings
.entrySet())
{
String action = defaultBindings.get(shortcut);
String action = shortcut2action.getValue();
if (customTmp.containsValue(action))
{
KeyStroke custom = null;
for (KeyStroke customShortcut : customTmp.keySet())
for (Map.Entry<KeyStroke, String> customShortcut2action : customTmp
.entrySet())
{
if (customTmp.get(customShortcut).equals(action))
if (customShortcut2action.getValue().equals(action))
{
custom = customShortcut;
custom = customShortcut2action.getKey();
break;
}
}
assert custom != null;
customTmp.remove(custom);
merged.put(custom, action);
}
else
{
merged.put(shortcut, action);
merged.put(shortcut2action.getKey(), action);
}
}
}
@ -180,7 +184,7 @@ synchronized void start(BundleContext bc)
{
merged = defaultBindings;
}
// Writes merged result
if (!merged.equals(customBindings))
{
@ -193,25 +197,26 @@ synchronized void start(BundleContext bc)
catch (IOException exc)
{
logger.error("unable to write to: "
+ customFile.getAbsolutePath(), exc);
+ customFile.getAbsolutePath(), exc);
}
}
KeybindingSetImpl newSet =
new KeybindingSetImpl(merged, category, customFile);
new KeybindingSetImpl(merged, category, customFile);
this.bindings.put(category, newSet);
newSet.addObserver(this);
}
this.isRunning = true;
}
/**
* Invalidates references to custom bindings, preventing further writes.
*/
synchronized void stop()
{
if (!this.isRunning) return;
if (!this.isRunning)
return;
for (KeybindingSetImpl bindingSet : this.bindings.values())
{
bindingSet.invalidate();
@ -219,32 +224,34 @@ synchronized void stop()
this.bindings.clear();
this.isRunning = false;
}
/**
* Provides the bindings associated with a given category. This may be null
* if the default bindings failed to be loaded.
*
* @param category segment of the UI for which bindings should be retrieved
* @return mappings of keystrokes to the string representation of their
* actions
* @throws UnsupportedOperationException if the service isn't running
*/
public synchronized KeybindingSet getBindings(
KeybindingSet.Category category)
KeybindingSet.Category category)
{
if (!this.isRunning) throw new UnsupportedOperationException();
if (!this.isRunning)
throw new UnsupportedOperationException();
// Started services should have all categories
assert this.bindings.containsKey(category);
return this.bindings.get(category);
}
// Listens for changes in binding sets so changes can be written
public void update(Observable obs, Object arg)
{
if (obs instanceof KeybindingSetImpl)
{
KeybindingSetImpl changedBindings = (KeybindingSetImpl) obs;
// Attempts to avoid lock if unwritable (this works since bindings
// can't become re-writable)
if (changedBindings.isWritable())
@ -255,24 +262,24 @@ public void update(Observable obs, Object arg)
{
// Writes new bindings to custom file
File customFile = changedBindings.getCustomFile();
try
{
FileOutputStream out =
new FileOutputStream(customFile);
new FileOutputStream(customFile);
Persistence format =
changedBindings.getCategory().getFormat();
changedBindings.getCategory().getFormat();
format.save(out, changedBindings.getBindings());
out.close();
}
catch (IOException exc)
{
logger.error("unable to write to: "
+ customFile.getAbsolutePath(), exc);
+ customFile.getAbsolutePath(), exc);
}
}
}
}
}
}
}
}

@ -8,9 +8,9 @@
import java.util.*;
import javax.swing.KeyStroke;
import javax.swing.*;
import chooser.Persistence;
import chooser.*;
/**
* Wrapper for keybinding sets. Observers are notified when there's a change.

Loading…
Cancel
Save