aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip/communicator
diff options
context:
space:
mode:
authorYana Stamcheva <yana@jitsi.org>2010-09-02 22:50:23 +0000
committerYana Stamcheva <yana@jitsi.org>2010-09-02 22:50:23 +0000
commit45cf66297884d51760202c3213fde1e909a8d482 (patch)
treea93fa8aab3fb949ae0c24ddd5c819da166cf96bf /src/net/java/sip/communicator
parentf6c822cc9e1486b59575fdc48a9b0e223e584a16 (diff)
downloadjitsi-45cf66297884d51760202c3213fde1e909a8d482.zip
jitsi-45cf66297884d51760202c3213fde1e909a8d482.tar.gz
jitsi-45cf66297884d51760202c3213fde1e909a8d482.tar.bz2
Skin implementation bases provided by Adam Netočný (work in progress).
Diffstat (limited to 'src/net/java/sip/communicator')
-rw-r--r--src/net/java/sip/communicator/impl/resources/ResourceManagementServiceImpl.java95
-rw-r--r--src/net/java/sip/communicator/impl/resources/util/SkinJarBuilder.java302
-rw-r--r--src/net/java/sip/communicator/plugin/pluginmanager/BundleComparator.java5
-rw-r--r--src/net/java/sip/communicator/plugin/pluginmanager/ManageButtonsPanel.java38
-rw-r--r--src/net/java/sip/communicator/plugin/pluginmanager/NewBundleDialog.java73
-rw-r--r--src/net/java/sip/communicator/plugin/pluginmanager/PluginListCellRenderer.java55
-rw-r--r--src/net/java/sip/communicator/plugin/pluginmanager/PluginTableModel.java31
-rw-r--r--src/net/java/sip/communicator/plugin/skinmanager/BundleComparator.java43
-rw-r--r--src/net/java/sip/communicator/plugin/skinmanager/ManageButtonsPanel.java245
-rw-r--r--src/net/java/sip/communicator/plugin/skinmanager/NewBundleDialog.java260
-rw-r--r--src/net/java/sip/communicator/plugin/skinmanager/Resources.java57
-rw-r--r--src/net/java/sip/communicator/plugin/skinmanager/SkinListCellRenderer.java311
-rw-r--r--src/net/java/sip/communicator/plugin/skinmanager/SkinManagerActivator.java86
-rw-r--r--src/net/java/sip/communicator/plugin/skinmanager/SkinManagerPanel.java131
-rw-r--r--src/net/java/sip/communicator/plugin/skinmanager/SkinTableModel.java171
-rw-r--r--src/net/java/sip/communicator/plugin/skinmanager/TitlePanel.java125
-rw-r--r--src/net/java/sip/communicator/plugin/skinmanager/skinmanager.manifest.mf27
-rw-r--r--src/net/java/sip/communicator/plugin/skinresourcepack/SkinResourcesPack.java383
-rw-r--r--src/net/java/sip/communicator/plugin/skinresourcepack/skinresourcepack.manifest.mf8
-rw-r--r--src/net/java/sip/communicator/service/resources/ResourceManagementService.java9
-rw-r--r--src/net/java/sip/communicator/service/resources/SkinPack.java49
21 files changed, 2473 insertions, 31 deletions
diff --git a/src/net/java/sip/communicator/impl/resources/ResourceManagementServiceImpl.java b/src/net/java/sip/communicator/impl/resources/ResourceManagementServiceImpl.java
index 45d27b5..31da767 100644
--- a/src/net/java/sip/communicator/impl/resources/ResourceManagementServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/resources/ResourceManagementServiceImpl.java
@@ -13,6 +13,7 @@ import java.util.*;
import javax.swing.*;
+import net.java.sip.communicator.impl.resources.util.*;
import net.java.sip.communicator.service.resources.*;
import net.java.sip.communicator.util.*;
@@ -24,6 +25,7 @@ import org.osgi.framework.*;
* @author Damian Minkov
* @author Yana Stamcheva
* @author Lubomir Marinov
+ * @author Adam Netocny, CircleTech, s.r.o.
*/
public class ResourceManagementServiceImpl
implements ResourceManagementService,
@@ -54,6 +56,9 @@ public class ResourceManagementServiceImpl
private Map<String, String> soundResources;
private ResourcePack soundPack = null;
+ private Map<String, String> skinResources;
+ private SkinPack skinPack = null;
+
/**
* Initializes already registered default resource packs.
*/
@@ -105,6 +110,16 @@ public class ResourceManagementServiceImpl
if (soundPack != null)
soundResources = getResources(soundPack);
+
+ skinPack = (SkinPack) getDefaultResourcePack(
+ SkinPack.class.getName(), SkinPack.RESOURCE_NAME_DEFAULT_VALUE);
+
+ if (skinPack != null)
+ {
+ skinResources = getResources(skinPack);
+ imageResources.putAll(skinPack.getImageResources());
+ colorResources.putAll(skinPack.getColorResources());
+ }
}
/**
@@ -207,6 +222,24 @@ public class ResourceManagementServiceImpl
soundPack = resourcePack;
soundResources = resources;
}
+ else if(resourcePack instanceof SkinPack && skinPack == null)
+ {
+ skinPack = (SkinPack) resourcePack;
+
+ if(imagePack!=null)
+ {
+ imageResources = getResources(imagePack);
+ }
+
+ if(colorPack!=null)
+ {
+ colorResources = getResources(colorPack);
+ }
+
+ skinResources = resources;
+ imageResources.putAll(skinPack.getImageResources());
+ colorResources.putAll(skinPack.getColorResources());
+ }
}
else if (event.getType() == ServiceEvent.UNREGISTERING)
{
@@ -258,6 +291,30 @@ public class ResourceManagementServiceImpl
if (soundPack != null)
soundResources = getResources(soundPack);
}
+ else if(resourcePack instanceof SkinPack
+ && skinPack.equals(resourcePack))
+ {
+ if(imagePack!=null)
+ {
+ imageResources = getResources(imagePack);
+ }
+
+ if(colorPack!=null)
+ {
+ colorResources = getResources(colorPack);
+ }
+
+ skinPack = (SkinPack) getDefaultResourcePack(
+ SkinPack.class.getName(),
+ SkinPack.RESOURCE_NAME_DEFAULT_VALUE);
+
+ if (skinPack != null)
+ {
+ skinResources = getResources(skinPack);
+ imageResources.putAll(skinPack.getImageResources());
+ colorResources.putAll(skinPack.getColorResources());
+ }
+ }
}
}
@@ -315,14 +372,24 @@ public class ResourceManagementServiceImpl
*/
public InputStream getImageInputStreamForPath(String path)
{
- return imagePack.getClass().getClassLoader().getResourceAsStream(path);
+ if(skinPack!=null)
+ {
+ if(skinPack.getClass().getClassLoader()
+ .getResourceAsStream(path)!=null)
+ {
+ return skinPack.getClass().getClassLoader()
+ .getResourceAsStream(path);
+ }
+ }
+
+ return imagePack.getClass().getClassLoader().getResourceAsStream(path);
}
/**
* Returns the <tt>InputStream</tt> of the image corresponding to the given
* key.
*
- * @param streamKey The identifier of the image in the resource properties
+ * @param streamKey The identifier of the image in the resource properties
* file.
* @return the <tt>InputStream</tt> of the image corresponding to the given
* key.
@@ -378,6 +445,14 @@ public class ResourceManagementServiceImpl
*/
public URL getImageURLForPath(String path)
{
+ if(skinPack!=null)
+ {
+ if(skinPack.getClass().getClassLoader().getResource(path)!=null)
+ {
+ return skinPack.getClass().getClassLoader().getResource(path);
+ }
+ }
+
return imagePack.getClass().getClassLoader().getResource(path);
}
@@ -419,6 +494,7 @@ public class ResourceManagementServiceImpl
* Returns an internationalized string corresponding to the given key.
*
* @param key The identifier of the string.
+ * @param params the parameters to pass to the localized string
* @return An internationalized string corresponding to the given key.
*/
public String getI18NString(String key, String[] params)
@@ -431,6 +507,7 @@ public class ResourceManagementServiceImpl
*
* @param key The identifier of the string in the resources properties
* file.
+ * @param params the parameters to pass to the localized string
* @param locale The locale.
* @return An internationalized string corresponding to the given key.
*/
@@ -616,6 +693,7 @@ public class ResourceManagementServiceImpl
/**
* Returns the <tt>URL</tt> of the sound corresponding to the given path.
*
+ * @param path the path, for which we're looking for a sound URL
* @return the <tt>URL</tt> of the sound corresponding to the given path.
*/
public URL getSoundURLForPath(String path)
@@ -627,6 +705,7 @@ public class ResourceManagementServiceImpl
* Returns the path of the sound corresponding to the given
* property key.
*
+ * @param soundKey the key, for the sound path
* @return the path of the sound corresponding to the given
* property key.
*/
@@ -678,4 +757,16 @@ public class ResourceManagementServiceImpl
}
return new ImageIcon(imageURL);
}
+
+ /**
+ * Builds a new skin bundle from the zip file content.
+ * @param zipFile Zip file with skin information.
+ * @return <tt>File</tt> for the bundle.
+ * @throws Exception When something goes wrong.
+ */
+ public File prepareSkinBundleFromZip(File zipFile)
+ throws Exception
+ {
+ return SkinJarBuilder.createBundleFromZip(zipFile);
+ }
}
diff --git a/src/net/java/sip/communicator/impl/resources/util/SkinJarBuilder.java b/src/net/java/sip/communicator/impl/resources/util/SkinJarBuilder.java
new file mode 100644
index 0000000..59d1ff7
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/resources/util/SkinJarBuilder.java
@@ -0,0 +1,302 @@
+/*
+ * 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.resources.util;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+
+/**
+ * Class for building of skin bundles from zip files.
+ * @author Adam Netocny, CircleTech, s.r.o.
+ */
+public class SkinJarBuilder
+{
+ /**
+ * Creates bundle from zip file.
+ * @param zip Zip file with skin contents.
+ * @return Jar <tt>File</tt>.
+ * @throws Exception When something goes wrong.
+ */
+ public static File createBundleFromZip(File zip)
+ throws Exception
+ {
+ File tmpDir = unzipIntoTmp(zip);
+ if (!test(tmpDir))
+ {
+ deleteDir(tmpDir);
+ throw new Exception(
+ "Zip file doesn't contain all necessary files and folders.");
+ }
+ File jar = cpTmp();
+ insertIntoZip(jar, tmpDir);
+ deleteDir(tmpDir);
+ return jar;
+ }
+
+ private static File cpTmp()
+ throws IOException
+ {
+ File jar = new File(System.getProperty("user.dir"),
+ "sc-bundles/skinresources.jar");
+
+ if (!jar.exists())
+ {
+ throw new IOException("Cannot find skinresources.jar file");
+ }
+
+ File tmp = File.createTempFile("skinresources", ".jar");
+
+ InputStream in = new FileInputStream(jar);
+
+ OutputStream out = new FileOutputStream(tmp);
+
+ byte[] buf = new byte[1024];
+ int len;
+
+ while ((len = in.read(buf)) > 0)
+ {
+ out.write(buf, 0, len);
+ }
+
+ in.close();
+ out.close();
+
+ return tmp;
+ }
+
+ private static File unzipIntoTmp(File zip)
+ throws Exception
+ {
+ File dest = File.createTempFile("zip", null);
+
+ if (!dest.delete())
+ {
+ throw new IOException("Cannot unzip given zip file");
+ }
+
+ if (!dest.mkdirs())
+ {
+ throw new IOException("Cannot unzip given zip file");
+ }
+
+ ZipFile archive = new ZipFile(zip);
+ Enumeration<? extends ZipEntry> e = archive.entries();
+ while (e.hasMoreElements())
+ {
+ ZipEntry entry = (ZipEntry) e.nextElement();
+ File file = new File(dest, entry.getName());
+ if (entry.isDirectory() && !file.exists())
+ {
+ file.mkdirs();
+ }
+ else
+ {
+ if (!file.getParentFile().exists())
+ {
+ file.getParentFile().mkdirs();
+ }
+ InputStream in = archive.getInputStream(entry);
+ BufferedOutputStream out
+ = new BufferedOutputStream(new FileOutputStream(file));
+ byte[] buffer = new byte[8192];
+ int read;
+ while (-1 != (read = in.read(buffer)))
+ {
+ out.write(buffer, 0, read);
+ }
+ in.close();
+ out.close();
+ }
+ }
+
+ return dest;
+ }
+
+ private static void insertIntoZip(File jar, File tmpDir)
+ throws IOException
+ {
+ File tempFile = File.createTempFile(jar.getName(), null);
+ tempFile.delete();
+
+ boolean renameOk = jar.renameTo(tempFile);
+ if (!renameOk)
+ {
+ throw new IOException("Error moving file " + jar.getAbsolutePath()
+ + " to " + tempFile.getAbsolutePath());
+ }
+
+ byte[] buf = new byte[8192];
+ ZipInputStream zin = new ZipInputStream(new FileInputStream(tempFile));
+ ZipOutputStream out = new ZipOutputStream(new FileOutputStream(jar));
+
+ ZipEntry entry = zin.getNextEntry();
+ while (entry != null)
+ {
+ String name = entry.getName();
+ out.putNextEntry(new ZipEntry(name));
+
+ int len;
+ while ((len = zin.read(buf)) > 0)
+ {
+ out.write(buf, 0, len);
+ }
+ entry = zin.getNextEntry();
+ }
+ zin.close();
+
+ tempFile.delete();
+
+ zipDir(tmpDir.getAbsolutePath(), out);
+
+ out.close();
+ }
+
+ private static void zipDir(String dir2zip, ZipOutputStream zos)
+ throws IOException
+ {
+ File directory = new File(dir2zip);
+ zip(directory, directory, zos);
+ }
+
+ private static final void zip(File directory, File base, ZipOutputStream zos)
+ throws IOException
+ {
+ File[] files = directory.listFiles();
+ byte[] buffer = new byte[8192];
+ int read = 0;
+ for (int i = 0, n = files.length; i < n; i++)
+ {
+ if (files[i].isDirectory())
+ {
+ zip(files[i], base, zos);
+ }
+ else
+ {
+ FileInputStream in = new FileInputStream(files[i]);
+ ZipEntry entry = new ZipEntry(files[i].getPath().substring(
+ base.getPath().length() + 1));
+ zos.putNextEntry(entry);
+ while (-1 != (read = in.read(buffer))) {
+ zos.write(buffer, 0, read);
+ }
+ in.close();
+ }
+ }
+ }
+
+ private static void deleteDir(File tmp)
+ {
+ if (tmp.exists())
+ {
+ File[] files = tmp.listFiles();
+ for (int i = 0; i < files.length; i++)
+ {
+ if (files[i].isDirectory())
+ {
+ deleteDir(files[i]);
+ }
+ else
+ {
+ files[i].delete();
+ }
+ }
+ tmp.delete();
+ }
+ }
+
+ private static boolean test(File tmpDir)
+ {
+ boolean colors = false;
+ boolean images = false;
+ boolean styles = false;
+
+ File[] list = tmpDir.listFiles();
+
+ if (list == null)
+ {
+ return false;
+ }
+
+ for (File f : list)
+ {
+ if (f.getName().equals("info.properties"))
+ {
+ if (!f.isFile())
+ {
+ return false;
+ }
+ }
+ else if (f.getName().equals("colors"))
+ {
+ if (f.isFile())
+ {
+ return false;
+ }
+ File[] ff = f.listFiles();
+ if (ff == null)
+ {
+ return false;
+ }
+
+ for (File x : ff)
+ {
+ if (x.getName().equals("colors.properties"))
+ {
+ colors = true;
+ }
+ }
+ }
+ else if (f.getName().equals("images"))
+ {
+ if (f.isFile())
+ {
+ return false;
+ }
+ File[] ff = f.listFiles();
+ if (ff == null)
+ {
+ return false;
+ }
+
+ for (File x : ff)
+ {
+ if (x.getName().equals("images.properties"))
+ {
+ images = true;
+ }
+ }
+ }
+ else if (f.getName().equals("styles"))
+ {
+ if (f.isFile())
+ {
+ return false;
+ }
+ File[] ff = f.listFiles();
+ if (ff == null)
+ {
+ return false;
+ }
+
+ for (File x : ff)
+ {
+ if (x.getName().equals("styles.properties"))
+ {
+ styles = true;
+ }
+ }
+
+ }
+ else
+ {
+ return false;
+ }
+ }
+ return styles && (colors && images);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/pluginmanager/BundleComparator.java b/src/net/java/sip/communicator/plugin/pluginmanager/BundleComparator.java
index a6313bc..87fce57 100644
--- a/src/net/java/sip/communicator/plugin/pluginmanager/BundleComparator.java
+++ b/src/net/java/sip/communicator/plugin/pluginmanager/BundleComparator.java
@@ -27,7 +27,7 @@ public class BundleComparator implements Comparator<Bundle>
{
String n1 = (String) arg0.getHeaders().get(Constants.BUNDLE_NAME);
String n2 = (String) arg1.getHeaders().get(Constants.BUNDLE_NAME);
-
+
if (n1 == null)
{
n1 = "unknown";
@@ -36,8 +36,7 @@ public class BundleComparator implements Comparator<Bundle>
{
n2 = "unknown";
}
-
+
return n1.compareTo(n2);
}
-
}
diff --git a/src/net/java/sip/communicator/plugin/pluginmanager/ManageButtonsPanel.java b/src/net/java/sip/communicator/plugin/pluginmanager/ManageButtonsPanel.java
index 1499d09..bd36dd2 100644
--- a/src/net/java/sip/communicator/plugin/pluginmanager/ManageButtonsPanel.java
+++ b/src/net/java/sip/communicator/plugin/pluginmanager/ManageButtonsPanel.java
@@ -10,7 +10,6 @@ import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
-import javax.swing.event.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.util.*;
@@ -29,26 +28,51 @@ public class ManageButtonsPanel
{
private Logger logger = Logger.getLogger(ManageButtonsPanel.class);
+ /**
+ * The deactivate skin button.
+ */
private JButton deactivateButton = new JButton(
Resources.getString("service.gui.DEACTIVATE"));
+ /**
+ * The activate skin button.
+ */
private JButton activateButton = new JButton(
Resources.getString("service.gui.ACTIVATE"));
+ /**
+ * The uninstall button.
+ */
private JButton uninstallButton = new JButton(
- Resources.getString("plugin.pluginmanager.UNINSTALL"));
+ Resources.getString("plugin.skinmanager.UNINSTALL"));
+ /**
+ * The update button.
+ */
private JButton updateButton = new JButton(
Resources.getString("plugin.pluginmanager.UPDATE"));
+ /**
+ * The new button.
+ */
private JButton newButton
- = new JButton(Resources.getString("plugin.pluginmanager.NEW"));
+ = new JButton(Resources.getString("plugin.skinmanager.NEW"));
- private JPanel buttonsPanel =
- new TransparentPanel(new GridLayout(0, 1, 8, 8));
+ /**
+ * The panel, containing all buttons.
+ */
+ private JPanel buttonsPanel
+ = new TransparentPanel(new GridLayout(0, 1, 8, 8));
+ /**
+ * The plugins table.
+ */
private JTable pluginTable;
+ /**
+ * Creates an instance of <tt>ManageButtonsPanel</tt>.
+ * @param pluginTable the table of bundles
+ */
public ManageButtonsPanel(JTable pluginTable)
{
this.pluginTable = pluginTable;
@@ -81,6 +105,10 @@ public class ManageButtonsPanel
defaultButtonState();
}
+ /**
+ * Performs corresponding operations when a button is pressed.
+ * @param e the <tt>ActionEvent</tt> that notified us
+ */
public void actionPerformed(ActionEvent e)
{
JButton sourceButton = (JButton) e.getSource();
diff --git a/src/net/java/sip/communicator/plugin/pluginmanager/NewBundleDialog.java b/src/net/java/sip/communicator/plugin/pluginmanager/NewBundleDialog.java
index f67d6f1..b2730c3 100644
--- a/src/net/java/sip/communicator/plugin/pluginmanager/NewBundleDialog.java
+++ b/src/net/java/sip/communicator/plugin/pluginmanager/NewBundleDialog.java
@@ -14,36 +14,73 @@ import java.net.*;
import javax.swing.*;
import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.swing.*;
import org.osgi.framework.*;
+/**
+ * @author Yana Stamcheva
+ */
public class NewBundleDialog
extends SIPCommDialog
implements ActionListener
-{private static final long serialVersionUID = 7638976584338100969L;
+{
+ /**
+ * The object used for logging.
+ */
+ private Logger logger = Logger.getLogger(NewBundleDialog.class);
+
+ private static final long serialVersionUID = 7638976584338100969L;
+ /**
+ * The install button.
+ */
private JButton installButton
= new JButton(Resources.getString("plugin.pluginmanager.INSTALL"));
-
+
+ /**
+ * The cancel button.
+ */
private JButton cancelButton
= new JButton(Resources.getString("service.gui.CANCEL"));
-
+
+ /**
+ * The bundle path field.
+ */
private JTextField bundlePathField = new JTextField();
-
+
+ /**
+ * The bundle path label.
+ */
private JLabel bundlePathLabel
= new JLabel(Resources.getString("plugin.pluginmanager.URL") + ": ");
-
+
+ /**
+ * The panel, containing all buttons.
+ */
private JPanel buttonsPanel
= new TransparentPanel(new FlowLayout(FlowLayout.CENTER));
-
+
+ /**
+ * The panel containing new bundle information.
+ */
private JPanel dataPanel = new TransparentPanel(new BorderLayout(5, 5));
-
+
+ /**
+ * The main panel, where all other panels are added.
+ */
private JPanel mainPanel = new TransparentPanel(new BorderLayout());
-
+
+ /**
+ * The button, from which to choose a file from the file system.
+ */
private JButton fileChooserButton = new JButton(
Resources.getString("plugin.pluginmanager.CHOOSE_FILE"));
-
+
+ /**
+ * Creates an instance of <tt>NewBundleDialog</tt>.
+ */
public NewBundleDialog ()
{
this.mainPanel.setPreferredSize(new Dimension(450, 150));
@@ -70,10 +107,14 @@ public class NewBundleDialog
this.dataPanel.add(fileChooserButton, BorderLayout.EAST);
}
+ /**
+ * Performs corresponding actions, when a buttons is pressed.
+ * @param e the <tt>ActionEvent</tt> that notified us
+ */
public void actionPerformed (ActionEvent e)
{
JButton sourceButton = (JButton) e.getSource();
-
+
if (sourceButton.equals(installButton))
{
if (bundlePathField.getText().length() > 0)
@@ -85,14 +126,14 @@ public class NewBundleDialog
}
catch (BundleException ex)
{
- ex.printStackTrace();
+ logger.info("Failed to install bundle.", ex);
PluginManagerActivator.getUIService().getPopupDialog()
.showMessagePopupDialog(ex.getMessage(), "Error",
PopupDialog.ERROR_MESSAGE);
}
catch (Throwable ex)
{
- ex.printStackTrace();
+ logger.info("Failed to install bundle.", ex);
}
finally
{
@@ -118,14 +159,18 @@ public class NewBundleDialog
}
catch (MalformedURLException ex)
{
- ex.printStackTrace();
+ logger.info("Failed parse URL.", ex);
}
}
- }
+ }
else
dispose();
}
+ /**
+ * Presses programatically the cancel button, when Esc key is pressed.
+ * @param isEscaped indicates if the Esc button was pressed on close
+ */
protected void close(boolean isEscaped)
{
cancelButton.doClick();
diff --git a/src/net/java/sip/communicator/plugin/pluginmanager/PluginListCellRenderer.java b/src/net/java/sip/communicator/plugin/pluginmanager/PluginListCellRenderer.java
index c917325..53d8a18 100644
--- a/src/net/java/sip/communicator/plugin/pluginmanager/PluginListCellRenderer.java
+++ b/src/net/java/sip/communicator/plugin/pluginmanager/PluginListCellRenderer.java
@@ -40,21 +40,46 @@ public class PluginListCellRenderer
private static final Color SELECTED_END_COLOR
= new Color(Resources.getColor("service.gui.GRADIENT_LIGHT_COLOR"));
+ /**
+ * The panel containing name and version information.
+ */
private JPanel nameVersionPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ /**
+ * The name label.
+ */
private JLabel nameLabel = new JLabel();
+ /**
+ * The version label.
+ */
private JLabel versionLabel = new JLabel();
+ /**
+ * The description label.
+ */
private JLabel descriptionLabel = new JLabel();
+ /**
+ * The state label.
+ */
private JLabel stateLabel = new JLabel();
+ /**
+ * The icon label.
+ */
private JLabel iconLabel = new JLabel();
+ /**
+ * The system label indicating that a bundle is system (i.e. not optional).
+ */
private JLabel systemLabel
- = new JLabel("( " + Resources.getString("plugin.pluginmanager.SYSTEM") + " )");
+ = new JLabel("( " + Resources.getString("plugin.pluginmanager.SYSTEM")
+ + " )");
+ /**
+ * Indicates if a skin is selected.
+ */
private boolean isSelected = false;
/**
@@ -105,16 +130,23 @@ public class PluginListCellRenderer
/**
* Implements the <tt>ListCellRenderer</tt> method.
- *
- * Returns this panel that has been configured to display the meta contact
- * and meta contact group cells.
+ * Returns this panel that has been configured to display bundle name,
+ * version and description.
+ * @param table the parent table
+ * @param value the value of the rendered cell
+ * @param isSelected indicates if the rendered cell is selected
+ * @param hasFocus indicates if the rendered cell has the focus
+ * @param rowIndex the row index of the rendered cell
+ * @param vColIndex the column index of the rendered cell
+ * @return the rendering component
*/
+ @SuppressWarnings("unchecked")
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int rowIndex, int vColIndex)
{
Bundle bundle = (Bundle) value;
- Dictionary headers = bundle.getHeaders();
+ Dictionary<Object, Object> headers = bundle.getHeaders();
Object bundleName = headers.get(Constants.BUNDLE_NAME);
Object bundleVersion = headers.get(Constants.BUNDLE_VERSION);
Object bundleDescription = headers.get(Constants.BUNDLE_DESCRIPTION);
@@ -149,6 +181,11 @@ public class PluginListCellRenderer
return this;
}
+ /**
+ * Returns an icon corresponding to the given <tt>state</tt>.
+ * @param state the state, for which we're looking for an icon
+ * @return the icon corresponding to the given state
+ */
private ImageIcon getStateIcon(int state)
{
int cacheIndex;
@@ -188,8 +225,10 @@ public class PluginListCellRenderer
/**
* Paint a background for all groups and a round blue border and background
* when a cell is selected.
+ * @param g the <tt>Graphics</tt> object used for painting
*/
- public void paintComponent(Graphics g) {
+ public void paintComponent(Graphics g)
+ {
super.paintComponent(g);
g = g.create();
@@ -203,6 +242,10 @@ public class PluginListCellRenderer
}
}
+ /**
+ * Paints a custom gradient background for selected cells.
+ * @param g the <tt>Graphics</tt> object used for painting
+ */
private void internalPaintComponent(Graphics g)
{
AntialiasingManager.activateAntialiasing(g);
diff --git a/src/net/java/sip/communicator/plugin/pluginmanager/PluginTableModel.java b/src/net/java/sip/communicator/plugin/pluginmanager/PluginTableModel.java
index 1569a75..7553d40 100644
--- a/src/net/java/sip/communicator/plugin/pluginmanager/PluginTableModel.java
+++ b/src/net/java/sip/communicator/plugin/pluginmanager/PluginTableModel.java
@@ -194,7 +194,36 @@ public class PluginTableModel
*/
private void refreshSortedBundlesList()
{
- this.bundles = this.bundleContext.getBundles();
+ Bundle[] list = this.bundleContext.getBundles();
+ ArrayList<Bundle> show = new ArrayList<Bundle>();
+ if(list != null)
+ {
+ for(Bundle b : list)
+ {
+ Dictionary headers = b.getHeaders();
+ if(headers.get(Constants.BUNDLE_ACTIVATOR)!=null)
+ {
+ if(!headers.get(Constants.BUNDLE_ACTIVATOR).toString()
+ .equals("net.java.sip.communicator.plugin." +
+ "skinresourcepack.SkinResourcesPack"))
+ {
+ show.add(b);
+ }
+ }
+ else
+ {
+ show.add(b);
+ }
+ }
+ }
+
+ this.bundles = new Bundle[show.size()];
+ int i = 0;
+ for(Bundle b : show)
+ {
+ this.bundles[i] = b;
+ i++;
+ }
Arrays.sort(this.bundles, bundleComparator);
}
}
diff --git a/src/net/java/sip/communicator/plugin/skinmanager/BundleComparator.java b/src/net/java/sip/communicator/plugin/skinmanager/BundleComparator.java
new file mode 100644
index 0000000..41e4382
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/skinmanager/BundleComparator.java
@@ -0,0 +1,43 @@
+/*
+ * 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.skinmanager;
+
+import java.util.*;
+import org.osgi.framework.*;
+
+/**
+ * Comparator for bundle array sort
+ *
+ * @author ROTH Damien
+ */
+public class BundleComparator
+ implements Comparator<Bundle>
+{
+ /**
+ * Compares the bundles using their "Bundle-Name"s.
+ * @param arg0 the first bundle to compare
+ * @param arg1 the second bundle to compare
+ * @return the result of the string comparison between the names of the two
+ * bundles
+ */
+ public int compare(Bundle arg0, Bundle arg1)
+ {
+ String n1 = (String) arg0.getHeaders().get(Constants.BUNDLE_NAME);
+ String n2 = (String) arg1.getHeaders().get(Constants.BUNDLE_NAME);
+
+ if (n1 == null)
+ {
+ n1 = "unknown";
+ }
+ if (n2 == null)
+ {
+ n2 = "unknown";
+ }
+
+ return n1.compareTo(n2);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/skinmanager/ManageButtonsPanel.java b/src/net/java/sip/communicator/plugin/skinmanager/ManageButtonsPanel.java
new file mode 100644
index 0000000..34eaac1
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/skinmanager/ManageButtonsPanel.java
@@ -0,0 +1,245 @@
+/*
+ * 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.skinmanager;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.swing.*;
+
+import org.osgi.framework.*;
+
+/**
+ * The panel containing all buttons for the <tt>PluginManagerConfigForm</tt>.
+ *
+ * @author Yana Stamcheva
+ * @author Adam Netocony, CircleTech, s.r.o.
+ */
+public class ManageButtonsPanel
+ extends TransparentPanel
+ implements ActionListener
+{
+ /**
+ * The object used for logging.
+ */
+ private Logger logger = Logger.getLogger(ManageButtonsPanel.class);
+
+ /**
+ * The deactivate skin button.
+ */
+ private JButton deactivateButton = new JButton(
+ Resources.getString("service.gui.DEACTIVATE"));
+
+ /**
+ * The activate skin button.
+ */
+ private JButton activateButton = new JButton(
+ Resources.getString("service.gui.ACTIVATE"));
+
+ /**
+ * The uninstall button.
+ */
+ private JButton uninstallButton = new JButton(
+ Resources.getString("plugin.skinmanager.UNINSTALL"));
+
+ /**
+ * The new button.
+ */
+ private JButton newButton
+ = new JButton(Resources.getString("plugin.skinmanager.NEW"));
+
+ /**
+ * The panel, containing all buttons.
+ */
+ private JPanel buttonsPanel
+ = new TransparentPanel(new GridLayout(0, 1, 8, 8));
+
+ /**
+ * The table of skins.
+ */
+ private JTable skinTable;
+
+ /**
+ * Creates an instance of <tt>ManageButtonsPanel</tt>.
+ * @param pluginTable the table of skins
+ */
+ public ManageButtonsPanel(JTable pluginTable)
+ {
+ this.skinTable = pluginTable;
+
+ this.setLayout(new BorderLayout());
+
+ this.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
+
+ this.newButton.setOpaque(false);
+ this.activateButton.setOpaque(false);
+ this.deactivateButton.setOpaque(false);
+ this.uninstallButton.setOpaque(false);
+
+ this.buttonsPanel.add(newButton);
+ this.buttonsPanel.add(activateButton);
+ this.buttonsPanel.add(deactivateButton);
+ this.buttonsPanel.add(uninstallButton);
+
+ this.add(buttonsPanel, BorderLayout.NORTH);
+
+ this.newButton.addActionListener(this);
+ this.activateButton.addActionListener(this);
+ this.deactivateButton.addActionListener(this);
+ this.uninstallButton.addActionListener(this);
+
+ //default as nothing is selected
+ defaultButtonState();
+ }
+
+ /**
+ * Performs corresponding operations when a button is pressed.
+ * @param e the <tt>ActionEvent</tt> that notified us
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ JButton sourceButton = (JButton) e.getSource();
+
+ if (sourceButton.equals(newButton))
+ {
+ NewBundleDialog dialog = new NewBundleDialog(skinTable);
+
+ dialog.pack();
+ dialog.setLocation(
+ Toolkit.getDefaultToolkit().getScreenSize().width / 2
+ - dialog.getWidth() / 2,
+ Toolkit.getDefaultToolkit().getScreenSize().height / 2
+ - dialog.getHeight() / 2);
+
+ dialog.setVisible(true);
+ }
+ else if (sourceButton.equals(activateButton))
+ {
+ int[] selectedRows = skinTable.getSelectedRows();
+
+ for (int i = 0; i < skinTable.getModel().getRowCount(); i++)
+ {
+ try
+ {
+ ((Bundle) skinTable.getModel().getValueAt(i, 0)).stop();
+ }
+ catch (BundleException ex) { }
+ }
+
+ for (int i = 0; i < selectedRows.length; i++)
+ {
+ try
+ {
+ ((Bundle) skinTable.getModel()
+ .getValueAt(selectedRows[i], 0)).start();
+ }
+ catch (BundleException ex)
+ {
+ logger.error("Failed to activate bundle.", ex);
+
+ SkinManagerActivator.getUIService().getPopupDialog()
+ .showMessagePopupDialog(ex.getMessage(), "Error",
+ PopupDialog.ERROR_MESSAGE);
+ }
+ }
+
+ defaultButtonState();
+ }
+ else if (sourceButton.equals(deactivateButton))
+ {
+ int[] selectedRows = skinTable.getSelectedRows();
+
+ for (int i = 0; i < selectedRows.length; i++)
+ {
+ try
+ {
+ ((Bundle) skinTable.getModel()
+ .getValueAt(selectedRows[i], 0)).stop();
+ }
+ catch (BundleException ex)
+ {
+ logger.error("Failed to desactivate bundle.", ex);
+
+ SkinManagerActivator.getUIService().getPopupDialog()
+ .showMessagePopupDialog(ex.getMessage(), "Error",
+ PopupDialog.ERROR_MESSAGE);
+ }
+ }
+
+ defaultButtonState();
+ }
+ else if (sourceButton.equals(uninstallButton))
+ {
+ int[] selectedRows = skinTable.getSelectedRows();
+
+ for (int i = selectedRows.length - 1; i >= 0; i--)
+ {
+ try
+ {
+ ((Bundle) skinTable.getModel()
+ .getValueAt(selectedRows[i], 0)).uninstall();
+ }
+ catch (BundleException ex)
+ {
+ logger.error("Failed to uninstall bundle.", ex);
+
+ SkinManagerActivator.getUIService().getPopupDialog()
+ .showMessagePopupDialog(ex.getMessage(), "Error",
+ PopupDialog.ERROR_MESSAGE);
+ }
+ }
+
+ defaultButtonState();
+ }
+ }
+
+ /**
+ * Default state of buttons, as nothing is selected
+ */
+ public void defaultButtonState()
+ {
+ enableActivateButton(false);
+ enableDeactivateButton(false);
+ enableUninstallButton(false);
+ }
+
+ /**
+ * Enable or disable the activate button.
+ *
+ * @param enable TRUE - to enable the activate button, FALSE - to disable it
+ */
+ public void enableActivateButton(boolean enable)
+ {
+ this.activateButton.setEnabled(enable);
+ }
+
+ /**
+ * Enable or disable the deactivate button.
+ *
+ * @param enable TRUE - to enable the deactivate button, FALSE - to
+ * disable it
+ */
+ public void enableDeactivateButton(boolean enable)
+ {
+ this.deactivateButton.setEnabled(enable);
+ }
+
+ /**
+ * Enable or disable the uninstall button.
+ *
+ * @param enable TRUE - to enable the uninstall button, FALSE - to
+ * disable it
+ */
+ public void enableUninstallButton(boolean enable)
+ {
+ this.uninstallButton.setEnabled(enable);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/skinmanager/NewBundleDialog.java b/src/net/java/sip/communicator/plugin/skinmanager/NewBundleDialog.java
new file mode 100644
index 0000000..217b15b
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/skinmanager/NewBundleDialog.java
@@ -0,0 +1,260 @@
+/*
+ * 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.skinmanager;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.net.*;
+import java.util.zip.ZipFile;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.swing.*;
+
+import org.osgi.framework.*;
+
+/**
+ *
+ * @author Yana Stamcheva
+ * @author Adam Netcony
+ */
+public class NewBundleDialog
+ extends SIPCommDialog
+ implements ActionListener
+{
+ private static final long serialVersionUID = 7638976584338100969L;
+
+ /**
+ * The object used for logging.
+ */
+ private Logger logger = Logger.getLogger(NewBundleDialog.class);
+
+ /**
+ * The install button.
+ */
+ private JButton installButton
+ = new JButton(Resources.getString("plugin.pluginmanager.INSTALL"));
+
+ /**
+ * The cancel button.
+ */
+ private JButton cancelButton
+ = new JButton(Resources.getString("service.gui.CANCEL"));
+
+ /**
+ * The bundle path field.
+ */
+ private JTextField bundlePathField = new JTextField();
+
+ /**
+ * The bundle path label.
+ */
+ private JLabel bundlePathLabel
+ = new JLabel(Resources.getString("plugin.pluginmanager.URL") + ": ");
+
+ /**
+ * The panel, containing all buttons.
+ */
+ private JPanel buttonsPanel
+ = new TransparentPanel(new FlowLayout(FlowLayout.CENTER));
+
+ /**
+ * The panel containing new bundle information.
+ */
+ private JPanel dataPanel = new TransparentPanel(new BorderLayout(5, 5));
+
+ /**
+ * The main panel, where all other panels are added.
+ */
+ private JPanel mainPanel = new TransparentPanel(new BorderLayout());
+
+ /**
+ * The button, from which to choose a file from the file system.
+ */
+ private JButton fileChooserButton = new JButton(
+ Resources.getString("plugin.pluginmanager.CHOOSE_FILE"));
+
+ private JTable skinTable;
+
+ /**
+ * Creates an instance of <tt>NewBundleDialog</tt>.
+ * @param table the skin table
+ */
+ public NewBundleDialog(JTable table)
+ {
+ skinTable = table;
+
+ this.mainPanel.setPreferredSize(new Dimension(450, 150));
+
+ this.getContentPane().add(mainPanel);
+
+ this.mainPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
+ this.mainPanel.add(dataPanel, BorderLayout.NORTH);
+
+ this.mainPanel.add(buttonsPanel, BorderLayout.SOUTH);
+
+ this.buttonsPanel.add(installButton);
+ this.buttonsPanel.add(cancelButton);
+
+ this.installButton.addActionListener(this);
+ this.cancelButton.addActionListener(this);
+ this.fileChooserButton.addActionListener(this);
+ this.fileChooserButton.setOpaque(false);
+
+ this.dataPanel.add(bundlePathLabel, BorderLayout.WEST);
+
+ this.dataPanel.add(bundlePathField, BorderLayout.CENTER);
+
+ this.dataPanel.add(fileChooserButton, BorderLayout.EAST);
+ }
+
+ /**
+ * Performs corresponding actions, when a buttons is pressed.
+ * @param e the <tt>ActionEvent</tt> that notified us
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ JButton sourceButton = (JButton) e.getSource();
+
+ if (sourceButton.equals(installButton))
+ {
+ if (bundlePathField.getText().length() > 0)
+ {
+ try
+ {
+ File jar = null;
+ try
+ {
+ jar = Resources.getResources()
+ .prepareSkinBundleFromZip(
+ new File(bundlePathField.getText()));
+ }
+ catch (Exception ex)
+ {
+ logger.info("Failed to load skin from zip.", ex);
+
+ SkinManagerActivator.getUIService().getPopupDialog()
+ .showMessagePopupDialog(ex.getMessage(), "Error",
+ PopupDialog.ERROR_MESSAGE);
+ }
+
+ if (jar != null)
+ {
+ try
+ {
+ Bundle newBundle = SkinManagerActivator
+ .bundleContext.installBundle(
+ jar.toURI().toURL().toString());
+
+ for (int i = 0;
+ i < skinTable.getModel().getRowCount(); i++)
+ {
+ try
+ {
+ ((Bundle) skinTable.getModel()
+ .getValueAt(i, 0)).stop();
+ }
+ catch (BundleException ex)
+ {
+ logger.info("Failed to stop bundle.", ex);
+ }
+ }
+ newBundle.start();
+ }
+ catch (MalformedURLException ex)
+ {
+ logger.info("Failed to load skin from zip.", ex);
+ }
+ }
+ }
+ catch (BundleException ex)
+ {
+ logger.info("Failed to install bundle.", ex);
+ SkinManagerActivator.getUIService().getPopupDialog()
+ .showMessagePopupDialog(ex.getMessage(), "Error",
+ PopupDialog.ERROR_MESSAGE);
+ }
+ catch (Throwable ex)
+ {
+ logger.info("Failed to install bundle.", ex);
+ }
+ finally
+ {
+ dispose();
+ }
+ }
+ }
+ else if (sourceButton.equals(fileChooserButton))
+ {
+ SipCommFileChooser chooser = GenericFileDialog.create(
+ null, "New bundle...",
+ SipCommFileChooser.LOAD_FILE_OPERATION);
+
+ chooser.addFilter(new SipCommFileFilter()
+ {
+ @Override
+ public boolean accept(File f)
+ {
+ if (f.isDirectory())
+ return true;
+
+ boolean good = true;
+ try
+ {
+ ZipFile zip = new ZipFile(f);
+ }
+ catch (IOException ex)
+ {
+ good = false;
+ }
+
+ if (!f.getName().toLowerCase().endsWith(".zip"))
+ {
+ good = false;
+ }
+ return good;
+ }
+
+ @Override
+ public String getDescription()
+ {
+ return "Zip files (*.zip)";
+ }
+ });
+
+ File newBundleFile = chooser.getFileFromDialog();
+
+ if (newBundleFile != null)
+ {
+ try
+ {
+ bundlePathField.setText(newBundleFile.getCanonicalPath());
+ }
+ catch (Exception ex)
+ {
+ bundlePathField.setText(newBundleFile.getAbsolutePath());
+ }
+ }
+ }
+ else
+ {
+ dispose();
+ }
+ }
+
+ /**
+ * Presses programatically the cancel button, when Esc key is pressed.
+ * @param isEscaped indicates if the Esc button was pressed on close
+ */
+ protected void close(boolean isEscaped)
+ {
+ cancelButton.doClick();
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/skinmanager/Resources.java b/src/net/java/sip/communicator/plugin/skinmanager/Resources.java
new file mode 100644
index 0000000..2f7fcf3
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/skinmanager/Resources.java
@@ -0,0 +1,57 @@
+/*
+ * 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.skinmanager;
+
+import net.java.sip.communicator.service.resources.*;
+
+/**
+ * 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 ResourceManagementService resourcesService;
+
+ /**
+ * 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 getResources().getI18NString(key);
+ }
+
+ /**
+ * Returns an int RGB color corresponding to the given key.
+ *
+ * @param key The key of the string.
+ *
+ * @return An internationalized string corresponding to the given key.
+ */
+ public static int getColor(String key)
+ {
+ return getResources().getColor(key);
+ }
+
+ /**
+ * Returns an instance of <tt>ResourceManagementService</tt> registered in
+ * the <tt>bundleContext</tt>.
+ * @return an instance of <tt>ResourceManagementService</tt>
+ */
+ public static ResourceManagementService getResources()
+ {
+ if (resourcesService == null)
+ resourcesService =
+ ResourceManagementServiceUtils
+ .getService(SkinManagerActivator.bundleContext);
+ return resourcesService;
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/skinmanager/SkinListCellRenderer.java b/src/net/java/sip/communicator/plugin/skinmanager/SkinListCellRenderer.java
new file mode 100644
index 0000000..0a3faf2
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/skinmanager/SkinListCellRenderer.java
@@ -0,0 +1,311 @@
+/*
+ * 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.skinmanager;
+
+import java.awt.*;
+import java.io.IOException;
+import java.net.*;
+import java.util.*;
+
+import javax.swing.*;
+import javax.swing.table.*;
+
+import net.java.sip.communicator.util.swing.*;
+
+import org.osgi.framework.*;
+
+/**
+ * The <tt>ContactListCellRenderer</tt> is the custom cell renderer used in the
+ * SIP-Communicator's <tt>ContactList</tt>. It extends JPanel instead of JLabel,
+ * which allows adding different buttons and icons to the contact cell.
+ * The cell border and background are repainted.
+ *
+ * @author Yana Stamcheva
+ * @author Adam Netocny, CircleTech, s.r.o.
+ */
+public class SkinListCellRenderer
+ extends JPanel
+ implements TableCellRenderer
+{
+ /**
+ * The end color used to paint a gradient selected background.
+ */
+ private static final Color SELECTED_START_COLOR
+ = new Color(Resources.getColor("service.gui.LIST_SELECTION_COLOR"));
+
+ /**
+ * The start color used to paint a gradient selected background.
+ */
+ private static final Color SELECTED_END_COLOR
+ = new Color(Resources.getColor("service.gui.GRADIENT_LIGHT_COLOR"));
+
+ /**
+ * The panel containing name and version information.
+ */
+ private JPanel nameVersionPanel
+ = new JPanel(new FlowLayout(FlowLayout.LEFT));
+
+ /**
+ * The name label.
+ */
+ private JLabel nameLabel = new JLabel();
+
+ /**
+ * The version label.
+ */
+ private JLabel versionLabel = new JLabel();
+
+ /**
+ * The description label.
+ */
+ private JLabel descriptionLabel = new JLabel();
+
+ /**
+ * The state label.
+ */
+ private JLabel stateLabel = new JLabel();
+
+ /**
+ * The icon label.
+ */
+ private JLabel iconLabel = new JLabel();
+
+ /**
+ * Indicates if a skin is selected.
+ */
+ private boolean isSelected = false;
+
+ /**
+ * The cache of the <code>ImageIcon</code> values returned by
+ * {@link #getStateIcon(int)} because the method in question is called
+ * whenever a table cell is painted and reading the image data out of a file
+ * and loading it into a new <code>ImageIcon</code> at such a time
+ * noticeably affects execution the speed.
+ */
+ private final ImageIcon[] stateIconCache = new ImageIcon[5];
+
+ /**
+ * Initialize the panel containing the node.
+ */
+ public SkinListCellRenderer()
+ {
+ super(new BorderLayout(8, 8));
+
+ JPanel mainPanel = new JPanel(new BorderLayout());
+
+ this.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+
+ this.setBackground(Color.WHITE);
+
+ this.setOpaque(true);
+
+ mainPanel.setOpaque(false);
+ this.nameVersionPanel.setOpaque(false);
+
+ this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+
+ this.nameLabel.setIconTextGap(2);
+
+ this.nameLabel.setFont(this.getFont().deriveFont(Font.BOLD));
+
+ this.nameVersionPanel.add(nameLabel);
+ this.nameVersionPanel.add(versionLabel);
+
+ mainPanel.add(nameVersionPanel, BorderLayout.NORTH);
+ mainPanel.add(descriptionLabel, BorderLayout.CENTER);
+
+ this.add(iconLabel, BorderLayout.WEST);
+
+ this.add(mainPanel, BorderLayout.CENTER);
+ this.add(stateLabel, BorderLayout.WEST);
+ }
+
+ /**
+ * Implements the <tt>ListCellRenderer</tt> method.
+ * Returns this panel that has been configured to display bundle name,
+ * version and description.
+ * @param table the parent table
+ * @param value the value of the rendered cell
+ * @param isSelected indicates if the rendered cell is selected
+ * @param hasFocus indicates if the rendered cell has the focus
+ * @param rowIndex the row index of the rendered cell
+ * @param vColIndex the column index of the rendered cell
+ * @return the rendering component
+ */
+ @SuppressWarnings("unchecked")
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected, boolean hasFocus, int rowIndex, int vColIndex)
+ {
+ Bundle bundle = (Bundle) value;
+ URL res = bundle.getResource("info.properties");
+ Dictionary<Object, Object> headers = bundle.getHeaders();
+ Object bundleName = headers.get(Constants.BUNDLE_NAME);
+ Object bundleVersion = headers.get(Constants.BUNDLE_VERSION);
+ Object bundleDescription = headers.get(Constants.BUNDLE_DESCRIPTION);
+
+ if (res != null) {
+ Properties props = new Properties();
+ try {
+ props.load(res.openStream());
+ String disp = props.getProperty("display_name");
+ if (disp != null) {
+ bundleName = disp;
+ }
+
+ disp = props.getProperty("version");
+ if (disp != null) {
+ bundleVersion = disp;
+ }
+
+ disp = props.getProperty("author");
+ String desc = props.getProperty("description");
+ String bundString = "";
+ if (disp != null) {
+ bundString = disp;
+ }
+ if (desc != null) {
+ if (disp != null) {
+ bundString += " - ";
+ }
+ bundString += desc;
+ }
+
+ if(!bundString.equals("")){
+ bundleDescription = bundString;
+ }
+ } catch (IOException ex) {
+ }
+ }
+
+ Icon stateIcon = getStateIcon(bundle.getState());
+
+ if (bundleName != null)
+ {
+ this.nameLabel.setText(bundleName.toString());
+ }
+ else
+ {
+ this.nameLabel.setText("unknown");
+ }
+
+ if (bundleVersion != null)
+ {
+ this.versionLabel.setText(bundleVersion.toString());
+ }
+ else
+ {
+ this.versionLabel.setText("");
+ }
+
+ if (bundleDescription != null)
+ {
+ this.descriptionLabel.setText(bundleDescription.toString());
+ }
+ else
+ {
+ this.descriptionLabel.setText("");
+ }
+
+ if (stateIcon != null)
+ {
+ this.stateLabel.setIcon(stateIcon);
+ }
+
+ this.isSelected = isSelected;
+
+ return this;
+ }
+
+ /**
+ * Returns an icon corresponding to the given <tt>state</tt>.
+ * @param state the state, for which we're looking for an icon
+ * @return the icon corresponding to the given state
+ */
+ private ImageIcon getStateIcon(int state)
+ {
+ int cacheIndex;
+ String imageID;
+ switch (state)
+ {
+ case Bundle.INSTALLED:
+ cacheIndex = 0;
+ imageID = "plugin.pluginmanager.INSTALLED_STATE";
+ break;
+ case Bundle.RESOLVED:
+ cacheIndex = 1;
+ imageID = "plugin.pluginmanager.DEACTIVATED_STATE";
+ break;
+ case Bundle.STARTING:
+ cacheIndex = 2;
+ imageID = "plugin.pluginmanager.STARTING_STATE";
+ break;
+ case Bundle.STOPPING:
+ cacheIndex = 3;
+ imageID = "plugin.pluginmanager.STOPPING_STATE";
+ break;
+ case Bundle.ACTIVE:
+ cacheIndex = 4;
+ imageID = "plugin.pluginmanager.ACTIVATE_STATE";
+ break;
+ default:
+ return null;
+ }
+
+ ImageIcon stateIcon = stateIconCache[cacheIndex];
+ if (stateIcon == null)
+ {
+ stateIconCache[cacheIndex]
+ = stateIcon = Resources.getResources().getImage(imageID);
+ }
+ return stateIcon;
+ }
+
+ /**
+ * Paints a custom background of the cell.
+ */
+ @Override
+ public void paintComponent(Graphics g)
+ {
+ super.paintComponent(g);
+
+ g = g.create();
+ try
+ {
+ internalPaintComponent(g);
+ }
+ finally
+ {
+ g.dispose();
+ }
+ }
+
+ /**
+ * Paints a custom gradient background for selected cells.
+ * @param g the <tt>Graphics</tt> object used for painting
+ */
+ private void internalPaintComponent(Graphics g)
+ {
+ AntialiasingManager.activateAntialiasing(g);
+
+ Graphics2D g2 = (Graphics2D) g;
+ int width = getWidth();
+ int height = getHeight();
+
+ if (this.isSelected)
+ {
+ GradientPaint p =
+ new GradientPaint(width / 2, 0, SELECTED_START_COLOR,
+ width / 2, height, SELECTED_END_COLOR);
+
+ g2.setPaint(p);
+ g2.fillRoundRect(1, 1, width, height - 1, 7, 7);
+ }
+
+ g2.setColor(SELECTED_START_COLOR);
+ g2.drawLine(0, height - 1, width, height - 1);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/skinmanager/SkinManagerActivator.java b/src/net/java/sip/communicator/plugin/skinmanager/SkinManagerActivator.java
new file mode 100644
index 0000000..e7f0c07
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/skinmanager/SkinManagerActivator.java
@@ -0,0 +1,86 @@
+/*
+ * 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.skinmanager;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.gui.*;
+
+import org.osgi.framework.*;
+
+/**
+ * The <tt>BundleActivator</tt> of the SkinManager plugin.
+ *
+ * @author Yana Stamcheva
+ * @author Adam Netocny, CircleTech, s.r.o.
+ */
+public class SkinManagerActivator
+ implements BundleActivator
+{
+ /**
+ * The bundle context.
+ */
+ public static BundleContext bundleContext;
+
+ /**
+ * The user interface service.
+ */
+ private static UIService uiService;
+
+ /**
+ * Starts this bundle and adds the
+ * <td>SkinManagerConfigForm</tt> contained in it to the configuration
+ * window obtained from the <tt>UIService</tt>.
+ * @param bc the <tt>BundleContext</tt>
+ * @throws Exception if one of the operation executed in the start method
+ * fails
+ */
+ public void start(BundleContext bc) throws Exception
+ {
+ bundleContext = bc;
+
+ Dictionary<String, String> properties = new Hashtable<String, String>();
+ properties.put( ConfigurationForm.FORM_TYPE,
+ ConfigurationForm.ADVANCED_TYPE);
+ bundleContext.registerService(
+ ConfigurationForm.class.getName(),
+ new LazyConfigurationForm(
+ "net.java.sip.communicator.plugin.skinmanager.SkinManagerPanel",
+ getClass().getClassLoader(),
+ "plugin.skinmanager.PLUGIN_ICON",
+ "plugin.skinmanager.SKINS",
+ 1001, true),
+ properties);
+ }
+
+ /**
+ * Stops this bundles.
+ * @param bc the <tt>BundleContext</tt>
+ * @throws Exception if one of the operation executed in the stop method
+ * fails
+ */
+ public void stop(BundleContext bc) throws Exception {}
+
+ /**
+ * Returns the <tt>UIService</tt> obtained from the bundle context.
+ *
+ * @return the <tt>UIService</tt> obtained from the bundle context
+ */
+ public static UIService getUIService()
+ {
+ if (uiService == null)
+ {
+ ServiceReference uiReference =
+ bundleContext.getServiceReference(UIService.class.getName());
+
+ uiService =
+ (UIService) bundleContext
+ .getService(uiReference);
+ }
+
+ return uiService;
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/skinmanager/SkinManagerPanel.java b/src/net/java/sip/communicator/plugin/skinmanager/SkinManagerPanel.java
new file mode 100644
index 0000000..f1b3efc
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/skinmanager/SkinManagerPanel.java
@@ -0,0 +1,131 @@
+/*
+ * 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.skinmanager;
+
+import java.awt.*;
+
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.table.*;
+
+import net.java.sip.communicator.util.swing.*;
+
+import org.osgi.framework.*;
+
+/**
+ * @author Yana Stamcheva
+ * @author Adam Netocny, CircleTech, s.r.o.
+ */
+public class SkinManagerPanel
+ extends TransparentPanel
+{
+ /**
+ * The table containing all skins.
+ */
+ private final JTable skinTable = new JTable();
+
+ /**
+ * The table model.
+ */
+ private final SkinTableModel tableModel = new SkinTableModel();
+
+ /**
+ * The panel containing manage buttons.
+ */
+ private final ManageButtonsPanel buttonsPanel;
+
+ /**
+ * Creates an instance of <tt>SkinManagerPanel</tt>.
+ */
+ public SkinManagerPanel()
+ {
+ super(new BorderLayout());
+ JScrollPane pluginListScrollPane = new JScrollPane();
+
+ skinTable.setModel(tableModel);
+
+ TableColumn col = skinTable.getColumnModel().getColumn(0);
+ col.setCellRenderer(new SkinListCellRenderer());
+
+ SkinListSelectionListener selectionListener =
+ new SkinListSelectionListener();
+
+ skinTable.getSelectionModel().addListSelectionListener(
+ selectionListener);
+ skinTable.getColumnModel().getSelectionModel()
+ .addListSelectionListener(selectionListener);
+
+ skinTable.setRowHeight(48);
+
+ skinTable.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
+
+ skinTable.setTableHeader(null);
+
+ buttonsPanel = new ManageButtonsPanel(skinTable);
+
+ this.add(pluginListScrollPane, BorderLayout.CENTER);
+
+ this.add(buttonsPanel, BorderLayout.EAST);
+
+ pluginListScrollPane.getViewport().add(skinTable);
+
+ pluginListScrollPane.getVerticalScrollBar().setUnitIncrement(30);
+
+ SkinManagerActivator.bundleContext
+ .addBundleListener(new SkinListBundleListener());
+ }
+
+ /**
+ * Listens for events triggered when a selection is made in the plugin list.
+ */
+ private class SkinListSelectionListener
+ implements ListSelectionListener
+ {
+ public void valueChanged(ListSelectionEvent e)
+ {
+ int selectedRow = skinTable.getSelectedRow();
+
+ if (selectedRow == -1)
+ return;
+
+ Bundle selectedBundle
+ = (Bundle) skinTable.getValueAt(selectedRow, 0);
+
+ buttonsPanel.enableUninstallButton(true);
+
+ if (selectedBundle.getState() != Bundle.ACTIVE)
+ {
+ buttonsPanel.enableActivateButton(true);
+ buttonsPanel.enableDeactivateButton(false);
+ }
+ else
+ {
+ buttonsPanel.enableActivateButton(false);
+ buttonsPanel.enableDeactivateButton(true);
+ }
+ }
+ }
+
+ /**
+ * Listens for <tt>BundleEvents</tt> triggered by the bundle context.
+ */
+ private class SkinListBundleListener
+ implements BundleListener
+ {
+ public void bundleChanged(BundleEvent event)
+ {
+ tableModel.update();
+
+ if (event.getType() == BundleEvent.INSTALLED)
+ {
+ skinTable.scrollRectToVisible(
+ new Rectangle( 0, skinTable.getHeight(),
+ 1, skinTable.getHeight()));
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/skinmanager/SkinTableModel.java b/src/net/java/sip/communicator/plugin/skinmanager/SkinTableModel.java
new file mode 100644
index 0000000..eacb2d3
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/skinmanager/SkinTableModel.java
@@ -0,0 +1,171 @@
+/*
+ * 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.skinmanager;
+
+import java.util.*;
+
+import javax.swing.table.*;
+
+import org.osgi.framework.*;
+
+/**
+ * The <tt>TableModel</tt> of the table containing all plug-ins.
+ *
+ * @author Yana Stamcheva
+ * @author Adam Netocny, CircleTech, s.r.o.
+ */
+public class SkinTableModel
+ extends AbstractTableModel
+{
+ /**
+ * The bundle context.
+ */
+ private BundleContext bundleContext = SkinManagerActivator.bundleContext;
+
+ /**
+ * The array of bundles.
+ */
+ private Bundle[] bundles = null;
+
+ /**
+ * A bundle comparator.
+ */
+ private final BundleComparator bundleComparator = new BundleComparator();
+
+ /**
+ * Create an instance of <tt>SkinTableModel</tt>
+ */
+ public SkinTableModel()
+ {
+ refreshSortedBundlesList();
+ }
+
+ /**
+ * Returns the count of table rows.
+ * @return int the count of table rows
+ */
+ public int getRowCount()
+ {
+ if (bundles == null)
+ {
+ return 0;
+ }
+ else
+ {
+ return bundles.length;
+ }
+ }
+
+ /**
+ * Returns TRUE if the given <tt>Bundle</tt> is contained in this table,
+ * FALSE - otherwise.
+ * @param bundle the <tt>Bundle</tt> to search for
+ * @return TRUE if the given <tt>Bundle</tt> is contained in this table,
+ * FALSE - otherwise.
+ */
+ public boolean contains(Bundle bundle)
+ {
+ for (int i = 0; i < bundles.length; i++)
+ {
+ Bundle b = bundles[i];
+
+ if (b.equals(bundle))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the count of table columns.
+ * @return int the count of table columns
+ */
+ public int getColumnCount()
+ {
+ return 1;
+ }
+
+ /**
+ * Returns FALSE for all cells in this table.
+ * @param row the row number to check
+ * @param column the column number to check
+ * @return false
+ */
+ public boolean isCellEditable(int row, int column)
+ {
+ return false;
+ }
+
+ /**
+ * Returns the value in the cell given by row and column.
+ * @param row the row number of the cell, which value we're looking for
+ * @param column the column of the cell, which value we're looking for
+ * @return the value of the cell given by <tt>row</tt> and <tt>column</tt>
+ */
+ public Object getValueAt(int row, int column)
+ {
+ int bundleCounter = 0;
+
+ for (int i = 0; i < bundles.length; i++)
+ {
+ if (bundleCounter == row)
+ {
+ return bundles[i];
+ }
+
+ bundleCounter++;
+ }
+ return null;
+ }
+
+ /**
+ * Updates the table content.
+ */
+ public void update()
+ {
+ refreshSortedBundlesList();
+ fireTableDataChanged();
+ }
+
+ /**
+ * Synchronizes the content of the bundle list with the bundles currently
+ * available in the bundle context and sorts it again.
+ */
+ private void refreshSortedBundlesList()
+ {
+ Bundle[] list = this.bundleContext.getBundles();
+ ArrayList<Bundle> show = new ArrayList<Bundle>();
+ if (list != null)
+ {
+ for (Bundle b : list)
+ {
+ Dictionary headers = b.getHeaders();
+ if (headers.get(Constants.BUNDLE_ACTIVATOR) != null)
+ {
+ if (headers.get(Constants.BUNDLE_ACTIVATOR).toString()
+ .equals("net.java.sip.communicator.plugin." +
+ "skinresourcepack.SkinResourcesPack"))
+ {
+ show.add(b);
+ }
+ }
+ }
+ }
+
+ this.bundles = new Bundle[show.size()];
+ int i = 0;
+ for (Bundle b : show)
+ {
+ this.bundles[i] = b;
+ i++;
+ }
+
+ Arrays.sort(this.bundles, bundleComparator);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/skinmanager/TitlePanel.java b/src/net/java/sip/communicator/plugin/skinmanager/TitlePanel.java
new file mode 100644
index 0000000..c2797c3
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/skinmanager/TitlePanel.java
@@ -0,0 +1,125 @@
+/*
+ * 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.skinmanager;
+
+import java.awt.*;
+
+import javax.swing.*;
+
+/**
+ * The <tt>TitlePanel</tt> is a decorated panel, that could be used for a
+ * header or a title area. This panel is used for example in the
+ * <tt>ConfigurationFrame</tt>.
+ *
+ * @author Yana Stamcheva
+ */
+public class TitlePanel
+ extends JPanel
+{
+ /**
+ * A color between blue and gray used to paint some borders.
+ */
+ public static final Color BORDER_COLOR
+ = new Color(Resources.getColor("service.gui.BORDER_COLOR"));
+
+ /**
+ * The size of the gradient used for painting the background.
+ */
+ private static final int GRADIENT_SIZE = 10;
+
+ /**
+ * The start color used to paint a gradient mouse over background.
+ */
+ private static final Color GRADIENT_DARK_COLOR
+ = new Color(Resources.getColor("service.gui.GRADIENT_DARK_COLOR"));
+
+ /**
+ * The end color used to paint a gradient mouse over background.
+ */
+ private static final Color GRADIENT_LIGHT_COLOR
+ = new Color(Resources.getColor("service.gui.GRADIENT_LIGHT_COLOR"));
+
+ private JLabel titleLabel = new JLabel();
+
+ /**
+ * Creates an instance of <tt>TitlePanel</tt>.
+ */
+ public TitlePanel()
+ {
+ super(new FlowLayout(FlowLayout.CENTER));
+
+ this.setPreferredSize(new Dimension(0, 30));
+
+ this.titleLabel.setFont(this.getFont().deriveFont(Font.BOLD, 14));
+ }
+
+ /**
+ * Creates an instance of <tt>TitlePanel</tt> by specifying the title
+ * String.
+ *
+ * @param title A String title.
+ */
+ public TitlePanel(String title)
+ {
+ super(new FlowLayout(FlowLayout.CENTER));
+
+ this.titleLabel.setFont(this.getFont().deriveFont(Font.BOLD, 14));
+
+ this.titleLabel.setText(title);
+
+ this.add(titleLabel);
+ }
+
+ /**
+ * Overrides the <code>paintComponent</code> method of <tt>JPanel</tt>
+ * to paint a gradient background of this panel.
+ * @param g the <tt>Graphics</tt> object used for painting
+ */
+ public void paintComponent(Graphics g)
+ {
+ super.paintComponent(g);
+
+ Graphics2D g2 = (Graphics2D) g;
+
+ GradientPaint p = new GradientPaint(this.getWidth() / 2, 0,
+ GRADIENT_DARK_COLOR, this.getWidth() / 2,
+ GRADIENT_SIZE,
+ GRADIENT_LIGHT_COLOR);
+
+ GradientPaint p1 = new GradientPaint(this.getWidth() / 2, this
+ .getHeight()
+ - GRADIENT_SIZE,
+ GRADIENT_LIGHT_COLOR, this.getWidth() / 2,
+ this.getHeight(), GRADIENT_DARK_COLOR);
+
+ g2.setPaint(p);
+ g2.fillRect(0, 0, this.getWidth(), GRADIENT_SIZE);
+
+ g2.setColor(GRADIENT_LIGHT_COLOR);
+ g2.fillRect(0, GRADIENT_SIZE, this.getWidth(),
+ this.getHeight() - GRADIENT_SIZE);
+
+ g2.setPaint(p1);
+ g2.fillRect(0, this.getHeight() - GRADIENT_SIZE
+ - 1, this.getWidth(), this.getHeight() - 1);
+
+ g2.setColor(BORDER_COLOR);
+ g2.drawRoundRect(0, 0, this.getWidth() - 1, this.getHeight() - 1, 5, 5);
+ }
+
+ /**
+ * Sets the title String.
+ * @param title The title String.
+ */
+ public void setTitleText(String title)
+ {
+ this.titleLabel.setText(title);
+
+ this.add(titleLabel);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/skinmanager/skinmanager.manifest.mf b/src/net/java/sip/communicator/plugin/skinmanager/skinmanager.manifest.mf
new file mode 100644
index 0000000..8919efa
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/skinmanager/skinmanager.manifest.mf
@@ -0,0 +1,27 @@
+Bundle-Activator: net.java.sip.communicator.plugin.skinmanager.SkinManagerActivator
+Bundle-Name: Skin Manager plugin
+Bundle-Description: Manage all SIP Communicator skins.
+Bundle-Vendor: sip-communicator.org, Adam Netocny, CircleTech, s.r.o.
+Bundle-Version: 0.0.1
+System-Bundle: yes
+Import-Package: org.osgi.framework,
+ net.java.sip.communicator.service.configuration,
+ net.java.sip.communicator.service.gui,
+ net.java.sip.communicator.service.gui.event,
+ net.java.sip.communicator.service.resources,
+ net.java.sip.communicator.util,
+ net.java.sip.communicator.util.swing,
+ javax.swing,
+ javax.swing.event,
+ javax.swing.table,
+ javax.swing.text,
+ javax.swing.text.html,
+ javax.accessibility,
+ javax.swing.plaf,
+ javax.swing.plaf.metal,
+ javax.swing.plaf.basic,
+ javax.imageio,
+ javax.swing.filechooser,
+ javax.swing.tree,
+ javax.swing.undo,
+ javax.swing.border
diff --git a/src/net/java/sip/communicator/plugin/skinresourcepack/SkinResourcesPack.java b/src/net/java/sip/communicator/plugin/skinresourcepack/SkinResourcesPack.java
new file mode 100644
index 0000000..bd47802
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/skinresourcepack/SkinResourcesPack.java
@@ -0,0 +1,383 @@
+/*
+ * 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.skinresourcepack;
+
+import java.net.*;
+import java.util.*;
+
+import org.osgi.framework.*;
+
+import net.java.sip.communicator.service.resources.*;
+
+/**
+ * The skin resource pack.
+ * @author Adam Netocny, CircleTech, s.r.o.
+ */
+public class SkinResourcesPack
+ implements BundleActivator, SkinPack
+{
+ /**
+ * The default resource path.
+ */
+ private static final String DEFAULT_RESOURCE_PATH = "info";
+
+ /**
+ * The resource path of skin images.
+ */
+ private static final String DEFAULT_IMAGE_RESOURCE_PATH = "images.images";
+
+ /**
+ * The resource path of skin colors.
+ */
+ private static final String DEFAULT_COLOR_RESOURCE_PATH = "colors.colors";
+
+ /**
+ * The resource path f skin styles.
+ */
+ private static final String DEFAULT_STYLE_RESOURCE_PATH = "styles.styles";
+
+ /**
+ * The bundle context.
+ */
+ private static BundleContext bundleContext;
+
+ /**
+ * Buffer for resource files found.
+ */
+ private static Hashtable<String, Iterator<String>> ressourcesFiles
+ = new Hashtable<String, Iterator<String>>();
+
+ /**
+ * A map of all skin image resources.
+ */
+ private Map<String, String> imageResources = null;
+
+ /**
+ * A map of all skin style resources.
+ */
+ private Map<String, String> styleResources = null;
+
+ /**
+ * A map of all skin color resources.
+ */
+ private Map<String, String> colorResources = null;
+
+ /**
+ * Starts the bundle.
+ * @param bc BundleContext
+ * @throws Exception -
+ */
+ public void start(BundleContext bc)
+ throws Exception
+ {
+ bundleContext = bc;
+
+ Hashtable<String, String> props = new Hashtable<String, String>();
+ props.put(ResourcePack.RESOURCE_NAME,
+ SkinPack.RESOURCE_NAME_DEFAULT_VALUE);
+
+ bundleContext.registerService(SkinPack.class.getName(),
+ this,
+ props);
+ }
+
+ /**
+ * Stops the bundle.
+ * @param bc BundleContext
+ * @throws Exception -
+ */
+ public void stop(BundleContext bc) throws Exception {}
+
+ /**
+ * Returns a <tt>Map</tt>, containing all [key, value] pairs for image
+ * resource pack.
+ *
+ * @return a <tt>Map</tt>, containing all [key, value] pairs for image
+ * resource pack.
+ */
+ public Map<String, String> getImageResources()
+ {
+ if(imageResources != null)
+ {
+ return imageResources;
+ }
+
+ ResourceBundle resourceBundle
+ = ResourceBundle.getBundle(DEFAULT_IMAGE_RESOURCE_PATH);
+
+ Map<String, String> resources = new TreeMap<String, String>();
+
+ this.initResources(resourceBundle, resources);
+
+ this.initImagePluginResources(resources);
+
+ imageResources = resources;
+
+ return resources;
+ }
+
+ /**
+ * Returns a <tt>Map</tt>, containing all [key, value] pairs for style
+ * resource pack.
+ *
+ * @return a <tt>Map</tt>, containing all [key, value] pairs for style
+ * resource pack.
+ */
+ public Map<String, String> getStyleResources()
+ {
+ if(styleResources != null)
+ {
+ return styleResources;
+ }
+
+ ResourceBundle resourceBundle
+ = ResourceBundle.getBundle(DEFAULT_STYLE_RESOURCE_PATH);
+
+ Map<String, String> resources = new TreeMap<String, String>();
+
+ this.initResources(resourceBundle, resources);
+
+ this.initStylePluginResources(resources);
+
+ styleResources = resources;
+
+ return resources;
+ }
+
+ /**
+ * Returns a <tt>Map</tt>, containing all [key, value] pairs for color
+ * resource pack.
+ *
+ * @return a <tt>Map</tt>, containing all [key, value] pairs for color
+ * resource pack.
+ */
+ public Map<String, String> getColorResources()
+ {
+ if(colorResources != null)
+ {
+ return colorResources;
+ }
+
+ ResourceBundle resourceBundle
+ = ResourceBundle.getBundle(DEFAULT_COLOR_RESOURCE_PATH);
+
+ Map<String, String> resources = new TreeMap<String, String>();
+
+ this.initResources(resourceBundle, resources);
+
+ this.initColorPluginResources(resources);
+
+ colorResources = resources;
+
+ return resources;
+ }
+
+ /**
+ * Returns a <tt>Map</tt>, containing all [key, value] pairs for this
+ * resource pack.
+ *
+ * @return a <tt>Map</tt>, containing all [key, value] pairs for this
+ * resource pack.
+ */
+ public Map<String, String> getResources()
+ {
+ ResourceBundle resourceBundle
+ = ResourceBundle.getBundle(DEFAULT_RESOURCE_PATH);
+
+ Map<String, String> resources = new TreeMap<String, String>();
+
+ this.initResources(resourceBundle, resources);
+
+ resources.putAll(getImageResources());
+
+ resources.putAll(getStyleResources());
+
+ resources.putAll(getColorResources());
+
+ return resources;
+ }
+
+ /**
+ * Returns the name of this resource pack.
+ *
+ * @return the name of this resource pack.
+ */
+ public String getName()
+ {
+ Map<String, String> resources = getResources();
+ String name = resources.get("display_name");
+ if(name != null)
+ {
+ return name + " Skin Resources";
+ }
+ else
+ {
+ return "Skin Resources";
+ }
+ }
+
+ /**
+ * Returns the description of this resource pack.
+ *
+ * @return the description of this resource pack.
+ */
+ public String getDescription()
+ {
+ Map<String, String> resources = getResources();
+ String name = resources.get("display_name");
+ if(name != null)
+ {
+ return "Provide SIP Communicator " + name + " skin resource pack.";
+ }
+ else
+ {
+ return "Provide SIP Communicator skin resource pack.";
+ }
+ }
+
+ /**
+ * Fills the given resource map with all (key,value) pairs obtained from the
+ * given <tt>ResourceBundle</tt>. This method will look in the properties
+ * files for references to other properties files and will include in the
+ * final map data from all referenced files.
+ *
+ * @param resourceBundle The initial <tt>ResourceBundle</tt>, corresponding
+ * to the "main" properties file.
+ * @param resources A <tt>Map</tt> that would store the data.
+ */
+ private void initResources( ResourceBundle resourceBundle,
+ Map<String, String> resources)
+ {
+ Enumeration<String> colorKeys = resourceBundle.getKeys();
+
+ while (colorKeys.hasMoreElements())
+ {
+ String key = colorKeys.nextElement();
+ String value = resourceBundle.getString(key);
+
+ resources.put(key, value);
+ }
+ }
+
+ /**
+ * Finds all plugin image resources, matching the "images-*.properties"
+ * pattern and adds them to this resource pack.
+ * @param resources the map of key, value image resource pairs
+ */
+ private void initImagePluginResources(Map<String, String> resources)
+ {
+ Iterator<String> pluginProperties
+ = findResourcePaths("images", "images-*.properties");
+
+ while (pluginProperties.hasNext())
+ {
+ String resourceBundleName = pluginProperties.next();
+
+ ResourceBundle resourceBundle
+ = ResourceBundle.getBundle(
+ resourceBundleName.substring(
+ 0, resourceBundleName.indexOf(".properties")));
+
+ initResources(resourceBundle, resources);
+ }
+ }
+
+ /**
+ * Finds all plugin style resources, matching the "styles-*.properties"
+ * pattern and adds them to this resource pack.
+ * @param resources the map of key, value stype resource pairs
+ */
+ private void initStylePluginResources(Map<String, String> resources)
+ {
+ Iterator<String> pluginProperties
+ = findResourcePaths("styles", "styles-*.properties");
+
+ while (pluginProperties.hasNext())
+ {
+ String resourceBundleName = pluginProperties.next();
+
+ ResourceBundle resourceBundle
+ = ResourceBundle.getBundle(
+ resourceBundleName.substring(
+ 0, resourceBundleName.indexOf(".properties")));
+
+ initResources(resourceBundle, resources);
+ }
+ }
+
+ /**
+ * Finds all plugin color resources, matching the "colors-*.properties"
+ * pattern and adds them to this resource pack.
+ * @param resources the map of key, value color resource pairs
+ */
+ private void initColorPluginResources(Map<String, String> resources)
+ {
+ Iterator<String> pluginProperties
+ = findResourcePaths("colors", "colors-*.properties");
+
+ while (pluginProperties.hasNext())
+ {
+ String resourceBundleName = pluginProperties.next();
+
+ ResourceBundle resourceBundle
+ = ResourceBundle.getBundle(
+ resourceBundleName.substring(
+ 0, resourceBundleName.indexOf(".properties")));
+
+ initResources(resourceBundle, resources);
+ }
+ }
+
+ /**
+ * Finds all properties files for the given path in this bundle.
+ *
+ * @param path the path pointing to the properties files.
+ * @param pattern the pattern for properties files
+ * (ex. "colors-*.properties")
+ * @return an <tt>Iterator</tt> over a list of all properties files found
+ * for the given path and pattern
+ */
+ protected static Iterator<String> findResourcePaths(String path,
+ String pattern)
+ {
+ Iterator<String> bufferedResult
+ = ressourcesFiles.get(path + "/" + pattern);
+ if (bufferedResult != null) {
+ return bufferedResult;
+ }
+
+ ArrayList<String> propertiesList = new ArrayList<String>();
+
+ @SuppressWarnings ("unchecked")
+ Enumeration<URL> propertiesUrls = bundleContext.getBundle()
+ .findEntries(path,
+ pattern,
+ false);
+
+ if (propertiesUrls != null)
+ {
+ while (propertiesUrls.hasMoreElements())
+ {
+ URL propertyUrl = propertiesUrls.nextElement();
+
+ // Remove the first slash.
+ String propertyFilePath
+ = propertyUrl.getPath().substring(1);
+
+ // Replace all slashes with dots.
+ propertyFilePath = propertyFilePath.replaceAll("/", ".");
+
+ propertiesList.add(propertyFilePath);
+ }
+ }
+
+ Iterator<String> result = propertiesList.iterator();
+ ressourcesFiles.put(path + pattern, result);
+
+ return result;
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/skinresourcepack/skinresourcepack.manifest.mf b/src/net/java/sip/communicator/plugin/skinresourcepack/skinresourcepack.manifest.mf
new file mode 100644
index 0000000..a00e4b4
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/skinresourcepack/skinresourcepack.manifest.mf
@@ -0,0 +1,8 @@
+Bundle-Activator: net.java.sip.communicator.plugin.skinresourcepack.SkinResourcesPack
+Bundle-Name: Skin Resource Pack
+Bundle-Description: The plugin offering skin related data.
+Bundle-Vendor: sip-communicator.org, Adam Netocny, CircleTech, s.r.o.
+Bundle-Version: 0.0.1
+System-Bundle: no
+Import-Package: org.osgi.framework,
+ net.java.sip.communicator.service.resources
diff --git a/src/net/java/sip/communicator/service/resources/ResourceManagementService.java b/src/net/java/sip/communicator/service/resources/ResourceManagementService.java
index 5400f9e..a89135b 100644
--- a/src/net/java/sip/communicator/service/resources/ResourceManagementService.java
+++ b/src/net/java/sip/communicator/service/resources/ResourceManagementService.java
@@ -18,6 +18,7 @@ import javax.swing.*;
* some configurations.
*
* @author Damian Minkov
+ * @author Adam Netocny, CircleTech, s.r.o.
*/
public interface ResourceManagementService
{
@@ -237,4 +238,12 @@ public interface ResourceManagementService
* @return A byte array containing the image with the given identifier.
*/
public byte[] getImageInBytes(String imageID);
+
+ /**
+ * Builds a new skin bundle from the zip file content.
+ * @param zipFile Zip file with skin information.
+ * @return <tt>File</tt> for the bundle.
+ * @throws Exception When something goes wrong.
+ */
+ public File prepareSkinBundleFromZip(File zipFile) throws Exception;
}
diff --git a/src/net/java/sip/communicator/service/resources/SkinPack.java b/src/net/java/sip/communicator/service/resources/SkinPack.java
new file mode 100644
index 0000000..56f033f
--- /dev/null
+++ b/src/net/java/sip/communicator/service/resources/SkinPack.java
@@ -0,0 +1,49 @@
+/*
+ * 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.resources;
+
+import java.util.*;
+
+/**
+ * Default Skin Pack interface.
+ * @author Adam Netocny, CircleTech, s.r.o.
+ */
+public interface SkinPack
+ extends ResourcePack
+{
+ /**
+ * Default resource name.
+ */
+ public String RESOURCE_NAME_DEFAULT_VALUE = "SkinPack";
+
+ /**
+ * Returns a <tt>Map</tt>, containing all [key, value] pairs for image
+ * resource pack.
+ *
+ * @return a <tt>Map</tt>, containing all [key, value] pairs for image
+ * resource pack.
+ */
+ public Map<String, String> getImageResources();
+
+ /**
+ * Returns a <tt>Map</tt>, containing all [key, value] pairs for style
+ * resource pack.
+ *
+ * @return a <tt>Map</tt>, containing all [key, value] pairs for style
+ * resource pack.
+ */
+ public Map<String, String> getStyleResources();
+
+ /**
+ * Returns a <tt>Map</tt>, containing all [key, value] pairs for color
+ * resource pack.
+ *
+ * @return a <tt>Map</tt>, containing all [key, value] pairs for color
+ * resource pack.
+ */
+ public Map<String, String> getColorResources();
+}