mirror of https://github.com/sipwise/jitsi.git
parent
89ea2776f0
commit
4460f1b636
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,5 @@
|
||||
Image Sources:
|
||||
pluginIcon.png -
|
||||
NuoveXT by Alexandre Moore (http://nuovext.pwsp.net/)
|
||||
Available under the GPLv2
|
||||
Originally 'gnome-settings-keybindings.png'
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license. See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.keybindings;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import net.java.sip.communicator.service.keybindings.KeybindingSet;
|
||||
|
||||
/**
|
||||
* Default implementation for the wrapper of keybinding sets.
|
||||
*
|
||||
* @author Damian Johnson
|
||||
*/
|
||||
class KeybindingSetImpl
|
||||
extends KeybindingSet
|
||||
{
|
||||
private LinkedHashMap<KeyStroke, String> bindings;
|
||||
|
||||
private Category category;
|
||||
|
||||
// Destination where custom bindings are saved, null if it couldn't be
|
||||
// secured
|
||||
private File customFile;
|
||||
|
||||
// Flag indicating that the associated service has been stopped
|
||||
private boolean isInvalidated = false;
|
||||
|
||||
KeybindingSetImpl(Map<KeyStroke, String> initial, Category category,
|
||||
File saveDst)
|
||||
{
|
||||
this.bindings = new LinkedHashMap<KeyStroke, String>(initial);
|
||||
this.category = category;
|
||||
this.customFile = saveDst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides current keybinding mappings.
|
||||
*
|
||||
* @return mapping of keystrokes to the string representation of the actions
|
||||
* they perform
|
||||
*/
|
||||
public LinkedHashMap<KeyStroke, String> getBindings()
|
||||
{
|
||||
return new LinkedHashMap<KeyStroke, String>(this.bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the bindings and notifies the observer's listeners if they've
|
||||
* changed. If the bindings can be written then they will be.
|
||||
*
|
||||
* @param newBindings new keybindings to be held
|
||||
*/
|
||||
public void setBindings(Map<KeyStroke, String> newBindings)
|
||||
{
|
||||
if (!this.bindings.equals(newBindings))
|
||||
{
|
||||
this.bindings = new LinkedHashMap<KeyStroke, String>(newBindings);
|
||||
setChanged();
|
||||
notifyObservers(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the portion of the UI to which the bindings belong.
|
||||
*
|
||||
* @return binding category
|
||||
*/
|
||||
public Category getCategory()
|
||||
{
|
||||
return this.category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides if the keybindings can be written when changed or not.
|
||||
*
|
||||
* @return true if bindings can be written when changed, false otherwise
|
||||
*/
|
||||
boolean isWritable()
|
||||
{
|
||||
return !this.isInvalidated && this.customFile != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the file where custom bindings are to be saved.
|
||||
*
|
||||
* @return custom bindings save destination
|
||||
*/
|
||||
File getCustomFile()
|
||||
{
|
||||
return this.customFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates reference to custom output, preventing further writes.
|
||||
*/
|
||||
void invalidate()
|
||||
{
|
||||
this.isInvalidated = true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.keybindings;
|
||||
|
||||
import net.java.sip.communicator.service.keybindings.KeybindingsService;
|
||||
import net.java.sip.communicator.util.Logger;
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
if (this.keybindingsService == null)
|
||||
{
|
||||
logger.debug("Service Impl: " + getClass().getName()
|
||||
+ " [ STARTED ]");
|
||||
this.keybindingsService = new KeybindingsServiceImpl();
|
||||
this.keybindingsService.start(context);
|
||||
context.registerService(KeybindingsService.class.getName(),
|
||||
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)
|
||||
{
|
||||
if (this.keybindingsService != null)
|
||||
{
|
||||
this.keybindingsService.stop();
|
||||
this.keybindingsService = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,278 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.keybindings;
|
||||
|
||||
import java.io.*;
|
||||
import java.text.ParseException;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import org.osgi.framework.*;
|
||||
|
||||
import chooser.Persistence;
|
||||
|
||||
import net.java.sip.communicator.service.fileaccess.FileAccessService;
|
||||
import net.java.sip.communicator.service.keybindings.*;
|
||||
import net.java.sip.communicator.util.Logger;
|
||||
|
||||
/**
|
||||
* Service that concerns keybinding mappings used by various parts of the UI.
|
||||
* Persistence is handled as follows when started:
|
||||
* <ol>
|
||||
* <li>Load default bindings from relative directory</li>
|
||||
* <li>Attempt to load custom bindings and resolve any duplicates</li>
|
||||
* <li>If merged bindings differ from the custom bindings then this attempts to
|
||||
* save the merged version</li>
|
||||
* </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);
|
||||
|
||||
// Name of the relative directory that holds default bindings
|
||||
private static final String DEFAULT_KEYBINDING_DIR =
|
||||
"/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>();
|
||||
|
||||
/**
|
||||
* 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;
|
||||
for (KeybindingSet.Category category : KeybindingSet.Category.values())
|
||||
{
|
||||
// Retrieves default bindings
|
||||
Persistence format = category.getFormat();
|
||||
LinkedHashMap <KeyStroke, String> defaultBindings;
|
||||
try
|
||||
{
|
||||
String defaultPath =
|
||||
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);
|
||||
this.bindings.put(category, null);
|
||||
continue;
|
||||
}
|
||||
catch (ParseException exc)
|
||||
{
|
||||
logger.error("unable to parse default bindings set: "
|
||||
+ category.getResource(), exc);
|
||||
this.bindings.put(category, null);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Attempts to retrieve custom bindings
|
||||
String customPath =
|
||||
CUSTOM_KEYBINDING_DIR + "/" + category.getResource();
|
||||
File customFile;
|
||||
try
|
||||
{
|
||||
ServiceReference faServiceReference =
|
||||
bc.getServiceReference(FileAccessService.class
|
||||
.getName());
|
||||
FileAccessService faService =
|
||||
(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();
|
||||
|
||||
// Gets file access service to reference persistent storage
|
||||
// of the user
|
||||
customFile = faService.getPrivatePersistentFile(customPath);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
String msg =
|
||||
"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);
|
||||
this.bindings.put(category, newSet);
|
||||
newSet.addObserver(this);
|
||||
continue;
|
||||
}
|
||||
|
||||
LinkedHashMap <KeyStroke, String> customBindings = null;
|
||||
if (customFile.exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
FileInputStream in = new FileInputStream(customFile);
|
||||
customBindings = format.load(in);
|
||||
in.close();
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
// If either an IO or ParseException occur then we skip
|
||||
// loading custom bindings
|
||||
}
|
||||
}
|
||||
|
||||
// Merges custom bindings
|
||||
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())
|
||||
{
|
||||
String action = defaultBindings.get(shortcut);
|
||||
|
||||
if (customTmp.containsValue(action))
|
||||
{
|
||||
KeyStroke custom = null;
|
||||
for (KeyStroke customShortcut : customTmp.keySet())
|
||||
{
|
||||
if (customTmp.get(customShortcut).equals(action))
|
||||
{
|
||||
custom = customShortcut;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert custom != null;
|
||||
customTmp.remove(custom);
|
||||
merged.put(custom, action);
|
||||
}
|
||||
else
|
||||
{
|
||||
merged.put(shortcut, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
merged = defaultBindings;
|
||||
}
|
||||
|
||||
// Writes merged result
|
||||
if (!merged.equals(customBindings))
|
||||
{
|
||||
try
|
||||
{
|
||||
FileOutputStream out = new FileOutputStream(customFile);
|
||||
format.save(out, merged);
|
||||
out.close();
|
||||
}
|
||||
catch (IOException exc)
|
||||
{
|
||||
logger.error("unable to write to: "
|
||||
+ customFile.getAbsolutePath(), exc);
|
||||
}
|
||||
}
|
||||
|
||||
KeybindingSetImpl newSet =
|
||||
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;
|
||||
for (KeybindingSetImpl bindingSet : this.bindings.values())
|
||||
{
|
||||
bindingSet.invalidate();
|
||||
}
|
||||
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)
|
||||
{
|
||||
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())
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (changedBindings.isWritable())
|
||||
{
|
||||
// Writes new bindings to custom file
|
||||
File customFile = changedBindings.getCustomFile();
|
||||
|
||||
try
|
||||
{
|
||||
FileOutputStream out =
|
||||
new FileOutputStream(customFile);
|
||||
Persistence format =
|
||||
changedBindings.getCategory().getFormat();
|
||||
format.save(out, changedBindings.getBindings());
|
||||
out.close();
|
||||
}
|
||||
catch (IOException exc)
|
||||
{
|
||||
logger.error("unable to write to: "
|
||||
+ customFile.getAbsolutePath(), exc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
Bundle-Activator: net.java.sip.communicator.impl.keybindings.KeybindingsActivator
|
||||
Bundle-Name: Keybindings
|
||||
Bundle-Description: Provides management and persistence of keyboard shortcuts.
|
||||
Bundle-Vendor: sip-communicator.org
|
||||
Bundle-Version: 0.0.1
|
||||
Import-Package: org.osgi.framework,
|
||||
net.java.sip.communicator.util,
|
||||
net.java.sip.communicator.service.fileaccess,
|
||||
net.java.sip.communicator.service.gui,
|
||||
javax.swing
|
||||
Export-Package: net.java.sip.communicator.service.keybindings
|
||||
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.plugin.keybindingchooser;
|
||||
|
||||
import net.java.sip.communicator.service.gui.*;
|
||||
import net.java.sip.communicator.service.keybindings.KeybindingsService;
|
||||
import net.java.sip.communicator.service.resources.*;
|
||||
import net.java.sip.communicator.util.Logger;
|
||||
|
||||
import org.osgi.framework.*;
|
||||
|
||||
/**
|
||||
* Enabling and disabling osgi functionality for the keybinding chooser.
|
||||
*
|
||||
* @author Damian Johnson
|
||||
*/
|
||||
public class KeybindingChooserActivator
|
||||
implements BundleActivator
|
||||
{
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(KeybindingChooserActivator.class);
|
||||
|
||||
private static BundleContext bundleContext;
|
||||
|
||||
public static ResourceManagementService resourcesService;
|
||||
|
||||
/**
|
||||
* Called when this bundle is started.
|
||||
* @param context The execution context of the bundle being started.
|
||||
*/
|
||||
public void start(BundleContext context)
|
||||
{
|
||||
bundleContext = context;
|
||||
|
||||
logger.debug("Service Impl: " + getClass().getName() + " [ STARTED ]");
|
||||
|
||||
ServiceReference keybindingRef
|
||||
= context.getServiceReference(KeybindingsService.class.getName());
|
||||
|
||||
KeybindingsService keybingingsService
|
||||
= (KeybindingsService) context.getService(keybindingRef);
|
||||
|
||||
KeybindingsConfigForm keybindingsManager =
|
||||
new KeybindingsConfigForm(keybingingsService);
|
||||
|
||||
context.registerService(ConfigurationForm.class.getName(),
|
||||
keybindingsManager,
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops this bundles.
|
||||
*/
|
||||
public void stop(BundleContext arg0) throws Exception
|
||||
{}
|
||||
|
||||
public static ResourceManagementService getResources()
|
||||
{
|
||||
if (resourcesService == null)
|
||||
{
|
||||
ServiceReference serviceReference =
|
||||
bundleContext.getServiceReference(
|
||||
ResourceManagementService.class.getName());
|
||||
|
||||
if(serviceReference == null)
|
||||
return null;
|
||||
|
||||
resourcesService = (ResourceManagementService)
|
||||
bundleContext.getService(serviceReference);
|
||||
}
|
||||
|
||||
return resourcesService;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.plugin.keybindingchooser;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import chooser.*;
|
||||
|
||||
import net.java.sip.communicator.service.gui.ConfigurationForm;
|
||||
import net.java.sip.communicator.service.keybindings.*;
|
||||
|
||||
/**
|
||||
* The <tt>ConfigurationForm</tt> that would be added to the settings
|
||||
* configuration to configure the application keybindings.
|
||||
*
|
||||
* @author Damian Johnson
|
||||
*/
|
||||
public class KeybindingsConfigForm
|
||||
extends JPanel
|
||||
implements ConfigurationForm
|
||||
{
|
||||
private static final long serialVersionUID = 0;
|
||||
private HashMap <KeybindingSet, SIPChooser> choosers =
|
||||
new HashMap <KeybindingSet, SIPChooser>();
|
||||
|
||||
public KeybindingsConfigForm(KeybindingsService service)
|
||||
{
|
||||
super(new BorderLayout());
|
||||
|
||||
setFocusable(true);
|
||||
JTabbedPane chooserPanes = new JTabbedPane(JTabbedPane.LEFT);
|
||||
|
||||
// deselects entries awaiting input when focus is lost
|
||||
this.addFocusListener(new FocusAdapter()
|
||||
{
|
||||
public void focusLost(FocusEvent event)
|
||||
{
|
||||
for (SIPChooser chooser : choosers.values())
|
||||
{
|
||||
chooser.setSelected(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (KeybindingSet.Category category : KeybindingSet.Category.values())
|
||||
{
|
||||
KeybindingSet bindingSet = service.getBindings(category);
|
||||
if (bindingSet == null) continue; // defaults failed to load
|
||||
|
||||
SIPChooser newChooser = new SIPChooser();
|
||||
newChooser.putAllBindings(bindingSet.getBindings());
|
||||
|
||||
JPanel chooserWrapper = new JPanel(new BorderLayout());
|
||||
chooserWrapper.add(newChooser, BorderLayout.NORTH);
|
||||
JScrollPane scroller = new JScrollPane(chooserWrapper);
|
||||
|
||||
// adds listener that receives events to set bindings
|
||||
this.addKeyListener(newChooser.makeAdaptor());
|
||||
|
||||
chooserPanes.addTab(getReadableConstant(category.toString()),
|
||||
scroller);
|
||||
this.choosers.put(bindingSet, newChooser);
|
||||
}
|
||||
|
||||
add(chooserPanes);
|
||||
|
||||
JButton apply = new JButton("Apply");
|
||||
apply.addActionListener(new ActionListener()
|
||||
{
|
||||
public void actionPerformed(ActionEvent event)
|
||||
{
|
||||
for (KeybindingSet set : choosers.keySet())
|
||||
set.setBindings(choosers.get(set).getBindingMap());
|
||||
}
|
||||
});
|
||||
|
||||
JPanel bottomWrapper = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||
bottomWrapper.add(apply);
|
||||
add(bottomWrapper, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the <tt>ConfigurationForm.getTitle()</tt> method. Returns
|
||||
* the title of this configuration form.
|
||||
*/
|
||||
public String getTitle()
|
||||
{
|
||||
return Resources.getString("keybindings");
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the <tt>ConfigurationForm.getIcon()</tt> method. Returns the
|
||||
* icon of this configuration form.
|
||||
*/
|
||||
public byte[] getIcon()
|
||||
{
|
||||
return Resources.getImageInBytes("keybindingPluginIcon");
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the <tt>ConfigurationForm.getForm()</tt> method. Returns the
|
||||
* component corresponding to this configuration form.
|
||||
*/
|
||||
public Object getForm()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a more readable version of constant names. Spaces replace
|
||||
* underscores and this changes the input to lowercase except the first
|
||||
* letter of each word. For instance, "RARE_CARDS" would become "Rare
|
||||
* Cards".
|
||||
* @param input string to be converted
|
||||
* @return reader friendly variant of constant name
|
||||
*/
|
||||
private static String getReadableConstant(String input)
|
||||
{
|
||||
char[] name = input.toCharArray();
|
||||
|
||||
boolean isStartOfWord = true;
|
||||
for (int i = 0; i < name.length; ++i)
|
||||
{
|
||||
char chr = name[i];
|
||||
if (chr == '_') name[i] = ' ';
|
||||
else if (isStartOfWord) name[i] = Character.toUpperCase(chr);
|
||||
else name[i] = Character.toLowerCase(chr);
|
||||
isStartOfWord = chr == '_';
|
||||
}
|
||||
|
||||
return new String(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keybinding chooser with customized appearance and functionality for the
|
||||
* SIP Communicator.
|
||||
*/
|
||||
private class SIPChooser
|
||||
extends BindingChooser
|
||||
{
|
||||
private static final long serialVersionUID = 0;
|
||||
|
||||
// Provides mapping of UI labels to internal action names
|
||||
private HashMap <String, String> actionLabels =
|
||||
new HashMap <String, String>();
|
||||
|
||||
// Calls focus to the form so keyboard events are received
|
||||
protected void onClick(MouseEvent event, BindingEntry entry,
|
||||
BindingEntry.Field field)
|
||||
{
|
||||
super.onClick(event, entry, field);
|
||||
KeybindingsConfigForm.this.requestFocus();
|
||||
}
|
||||
|
||||
public boolean putBinding(BindingEntry newEntry, int index)
|
||||
{
|
||||
// Converts to I18N strings for UI
|
||||
String actionInternal = newEntry.getAction();
|
||||
String actionLabel = Resources.getString(actionInternal);
|
||||
this.actionLabels.put(actionLabel, actionInternal);
|
||||
newEntry.setAction(actionLabel);
|
||||
|
||||
// Overwrites the default entry layout to stretch shortcut field
|
||||
newEntry.removeAll();
|
||||
newEntry.setLayout(new BorderLayout());
|
||||
|
||||
JPanel left = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
|
||||
left.add(newEntry.getField(BindingEntry.Field.INDENT));
|
||||
left.add(newEntry.getField(BindingEntry.Field.ACTION));
|
||||
newEntry.add(left, BorderLayout.WEST);
|
||||
newEntry.add(newEntry.getField(BindingEntry.Field.SHORTCUT));
|
||||
|
||||
return super.putBinding(newEntry, index);
|
||||
}
|
||||
|
||||
public LinkedHashMap <KeyStroke, String> getBindingMap()
|
||||
{
|
||||
// Translates I18N strings back to internal action labels
|
||||
LinkedHashMap <KeyStroke, String> bindings =
|
||||
new LinkedHashMap <KeyStroke, String>();
|
||||
for (BindingEntry entry : super.getBindings())
|
||||
{
|
||||
bindings.put(entry.getShortcut(), this.actionLabels.get(entry
|
||||
.getAction()));
|
||||
}
|
||||
|
||||
return bindings;
|
||||
}
|
||||
}
|
||||
|
||||
public int getIndex()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
|
||||
package net.java.sip.communicator.plugin.keybindingchooser;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* The <tt>Resources</tt> class manages the access to the internationalization
|
||||
* properties files and the image resources used in this plugin.
|
||||
*
|
||||
* @author Yana Stamcheva
|
||||
*/
|
||||
public class Resources
|
||||
{
|
||||
private static Logger log = Logger.getLogger(Resources.class);
|
||||
|
||||
/**
|
||||
* Returns an internationalized string corresponding to the given key.
|
||||
*
|
||||
* @param key The key of the string.
|
||||
* @return An internationalized string corresponding to the given key.
|
||||
*/
|
||||
public static String getString(String key)
|
||||
{
|
||||
return KeybindingChooserActivator.getResources().getI18NString(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an image from a given image identifier.
|
||||
*
|
||||
* @param imageID The identifier of the image.
|
||||
* @return The image for the given identifier.
|
||||
*/
|
||||
public static byte[] getImageInBytes(String imageID)
|
||||
{
|
||||
InputStream in = KeybindingChooserActivator.
|
||||
getResources().getImageInputStream(imageID);
|
||||
|
||||
if(in == null)
|
||||
return null;
|
||||
|
||||
byte[] image = null;
|
||||
|
||||
try
|
||||
{
|
||||
image = new byte[in.available()];
|
||||
in.read(image);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
log.error("Failed to load image:" + imageID, e);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
Bundle-Activator: net.java.sip.communicator.plugin.keybindingchooser.KeybindingChooserActivator
|
||||
Bundle-Name: Keybinding Chooser
|
||||
Bundle-Description: Provides configuring UI for keyboard shortcuts.
|
||||
Bundle-Vendor: sip-communicator.org
|
||||
Bundle-Version: 0.0.1
|
||||
Import-Package: org.osgi.framework,
|
||||
net.java.sip.communicator.util,
|
||||
net.java.sip.communicator.service.gui,
|
||||
net.java.sip.communicator.service.keybindings,
|
||||
net.java.sip.communicator.service.resources,
|
||||
javax.swing,
|
||||
javax.swing.event
|
||||
@ -0,0 +1 @@
|
||||
pluginIcon=resources/images/plugin/keybindingchooser/pluginIcon.png
|
||||
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.service.keybindings;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import chooser.Persistence;
|
||||
|
||||
/**
|
||||
* Wrapper for keybinding sets. Observers are notified when there's a change.
|
||||
* @author Damian Johnson
|
||||
*/
|
||||
public abstract class KeybindingSet
|
||||
extends Observable
|
||||
{
|
||||
/**
|
||||
* Provides current keybinding mappings.
|
||||
* @return mapping of keystrokes to the string representation of the actions
|
||||
* they perform
|
||||
*/
|
||||
public abstract LinkedHashMap <KeyStroke, String> getBindings();
|
||||
|
||||
/**
|
||||
* Resets the bindings and notifies the observer's listeners if they've
|
||||
* changed.
|
||||
* @param newBindings new keybindings to be held
|
||||
*/
|
||||
public abstract void setBindings(Map <KeyStroke, String> newBindings);
|
||||
|
||||
/**
|
||||
* Provides the portion of the UI to which the bindings belong.
|
||||
* @return binding category
|
||||
*/
|
||||
public abstract Category getCategory();
|
||||
|
||||
/**
|
||||
* Keybinding sets available in the Sip Communicator.
|
||||
*/
|
||||
public enum Category
|
||||
{
|
||||
CHAT("keybindings-chat", Persistence.SERIAL_HASH),
|
||||
MAIN("keybindings-main", Persistence.SERIAL_HASH);
|
||||
|
||||
private final String resource;
|
||||
private final Persistence persistenceFormat;
|
||||
|
||||
Category(String resource, Persistence format)
|
||||
{
|
||||
this.resource = resource;
|
||||
this.persistenceFormat = format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the name keybindings are saved and loaded with.
|
||||
* @return filename used for keybindings
|
||||
*/
|
||||
public String getResource()
|
||||
{
|
||||
return this.resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the format used to save and load keybinding resources.
|
||||
* @return style of persistence used by keybindings
|
||||
*/
|
||||
public Persistence getFormat()
|
||||
{
|
||||
return this.persistenceFormat;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.service.keybindings;
|
||||
|
||||
/**
|
||||
* The <tt>KeybindingService</tt> handles the distribution of configurable and
|
||||
* persistent keybinding sets.
|
||||
* @author Damian Johnson
|
||||
*/
|
||||
public interface KeybindingsService
|
||||
{
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
KeybindingSet getBindings(KeybindingSet.Category category);
|
||||
}
|
||||
Loading…
Reference in new issue