diff options
451 files changed, 17885 insertions, 0 deletions
diff --git a/doc/.cvsignore b/doc/.cvsignore new file mode 100644 index 0000000..9e5bfb4 --- /dev/null +++ b/doc/.cvsignore @@ -0,0 +1 @@ +api
\ No newline at end of file diff --git a/doc/AssigningAddressPreferences.sxi b/doc/AssigningAddressPreferences.sxi Binary files differnew file mode 100644 index 0000000..c8ac0aa --- /dev/null +++ b/doc/AssigningAddressPreferences.sxi diff --git a/lib/.project b/lib/.project new file mode 100644 index 0000000..14aa1aa --- /dev/null +++ b/lib/.project @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>lib</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + </buildSpec> + <natures> + </natures> +</projectDescription> diff --git a/lib/JainSipApi1.1.jar b/lib/JainSipApi1.1.jar Binary files differnew file mode 100644 index 0000000..fede379 --- /dev/null +++ b/lib/JainSipApi1.1.jar diff --git a/lib/Stun4J.jar b/lib/Stun4J.jar Binary files differnew file mode 100644 index 0000000..7e17300 --- /dev/null +++ b/lib/Stun4J.jar diff --git a/lib/architectureviewer1.1.jar b/lib/architectureviewer1.1.jar Binary files differnew file mode 100644 index 0000000..eacf55d --- /dev/null +++ b/lib/architectureviewer1.1.jar diff --git a/lib/bundle/.#junit.jar.1.8 b/lib/bundle/.#junit.jar.1.8 Binary files differnew file mode 100644 index 0000000..b3a1723 --- /dev/null +++ b/lib/bundle/.#junit.jar.1.8 diff --git a/lib/bundle/architectureviewer1.1.jar b/lib/bundle/architectureviewer1.1.jar Binary files differnew file mode 100644 index 0000000..6e63fc3 --- /dev/null +++ b/lib/bundle/architectureviewer1.1.jar diff --git a/lib/bundle/bundlerepository.jar b/lib/bundle/bundlerepository.jar Binary files differnew file mode 100644 index 0000000..9591907 --- /dev/null +++ b/lib/bundle/bundlerepository.jar diff --git a/lib/bundle/exportfilter.jar b/lib/bundle/exportfilter.jar Binary files differnew file mode 100644 index 0000000..2e02485 --- /dev/null +++ b/lib/bundle/exportfilter.jar diff --git a/lib/bundle/importfilter.jar b/lib/bundle/importfilter.jar Binary files differnew file mode 100644 index 0000000..5b4960f --- /dev/null +++ b/lib/bundle/importfilter.jar diff --git a/lib/bundle/importrange.jar b/lib/bundle/importrange.jar Binary files differnew file mode 100644 index 0000000..af1eaff --- /dev/null +++ b/lib/bundle/importrange.jar diff --git a/lib/bundle/junit.jar b/lib/bundle/junit.jar Binary files differnew file mode 100644 index 0000000..f2f60c8 --- /dev/null +++ b/lib/bundle/junit.jar diff --git a/lib/bundle/listener.jar b/lib/bundle/listener.jar Binary files differnew file mode 100644 index 0000000..f127e34 --- /dev/null +++ b/lib/bundle/listener.jar diff --git a/lib/bundle/servicebinder.jar b/lib/bundle/servicebinder.jar Binary files differnew file mode 100644 index 0000000..59d1ba3 --- /dev/null +++ b/lib/bundle/servicebinder.jar diff --git a/lib/bundle/shell.jar b/lib/bundle/shell.jar Binary files differnew file mode 100644 index 0000000..322c264 --- /dev/null +++ b/lib/bundle/shell.jar diff --git a/lib/bundle/shellgui.jar b/lib/bundle/shellgui.jar Binary files differnew file mode 100644 index 0000000..0376454 --- /dev/null +++ b/lib/bundle/shellgui.jar diff --git a/lib/bundle/shellplugin.jar b/lib/bundle/shellplugin.jar Binary files differnew file mode 100644 index 0000000..005b051 --- /dev/null +++ b/lib/bundle/shellplugin.jar diff --git a/lib/bundle/shelltui.jar b/lib/bundle/shelltui.jar Binary files differnew file mode 100644 index 0000000..8942385 --- /dev/null +++ b/lib/bundle/shelltui.jar diff --git a/lib/bundle/simple.jar b/lib/bundle/simple.jar Binary files differnew file mode 100644 index 0000000..8a4743f --- /dev/null +++ b/lib/bundle/simple.jar diff --git a/lib/bundle/tablelayout.jar b/lib/bundle/tablelayout.jar Binary files differnew file mode 100644 index 0000000..834fe9d --- /dev/null +++ b/lib/bundle/tablelayout.jar diff --git a/lib/jaxen-1.1-beta-8.jar b/lib/jaxen-1.1-beta-8.jar Binary files differnew file mode 100644 index 0000000..6b007d9 --- /dev/null +++ b/lib/jaxen-1.1-beta-8.jar diff --git a/lib/jmf-all/jmf.jar b/lib/jmf-all/jmf.jar Binary files differnew file mode 100644 index 0000000..0e39053 --- /dev/null +++ b/lib/jmf-all/jmf.jar diff --git a/lib/jmf-lin/jmf-native.jar b/lib/jmf-lin/jmf-native.jar Binary files differnew file mode 100644 index 0000000..45352ab --- /dev/null +++ b/lib/jmf-lin/jmf-native.jar diff --git a/lib/jmf-lin/jmf.jar b/lib/jmf-lin/jmf.jar Binary files differnew file mode 100644 index 0000000..049ef59 --- /dev/null +++ b/lib/jmf-lin/jmf.jar diff --git a/lib/jmf-lin/libjmutil.so.jar b/lib/jmf-lin/libjmutil.so.jar Binary files differnew file mode 100644 index 0000000..55eb9ec --- /dev/null +++ b/lib/jmf-lin/libjmutil.so.jar diff --git a/lib/jmf-sol/jmf-native.jar b/lib/jmf-sol/jmf-native.jar Binary files differnew file mode 100644 index 0000000..86862f7 --- /dev/null +++ b/lib/jmf-sol/jmf-native.jar diff --git a/lib/jmf-sol/jmf.jar b/lib/jmf-sol/jmf.jar Binary files differnew file mode 100644 index 0000000..cdca61b --- /dev/null +++ b/lib/jmf-sol/jmf.jar diff --git a/lib/jmf-sol/libjmutil.so.jar b/lib/jmf-sol/libjmutil.so.jar Binary files differnew file mode 100644 index 0000000..5c01ea5 --- /dev/null +++ b/lib/jmf-sol/libjmutil.so.jar diff --git a/lib/jmf-win/jmf-native.jar b/lib/jmf-win/jmf-native.jar Binary files differnew file mode 100644 index 0000000..2b0ea82 --- /dev/null +++ b/lib/jmf-win/jmf-native.jar diff --git a/lib/jmf-win/jmf.jar b/lib/jmf-win/jmf.jar Binary files differnew file mode 100644 index 0000000..556b508 --- /dev/null +++ b/lib/jmf-win/jmf.jar diff --git a/lib/jmf-win/sound.jar b/lib/jmf-win/sound.jar Binary files differnew file mode 100644 index 0000000..87625ec --- /dev/null +++ b/lib/jmf-win/sound.jar diff --git a/lib/junit.jar b/lib/junit.jar Binary files differnew file mode 100644 index 0000000..674d71e --- /dev/null +++ b/lib/junit.jar diff --git a/lib/logging.properties b/lib/logging.properties new file mode 100644 index 0000000..219e067 --- /dev/null +++ b/lib/logging.properties @@ -0,0 +1,54 @@ +############################################################ +# Default Logging Configuration File +# +# You can use a different file by specifying a filename +# with the java.util.logging.config.file system property. +# For example java -Djava.util.logging.config.file=myfile +############################################################ + +############################################################ +# Global properties +############################################################ + +# "handlers" specifies a comma separated list of log Handler +# classes. These handlers will be installed during VM startup. +# Note that these classes must be on the system classpath. +# By default we only configure a ConsoleHandler, which will only +# show messages at the INFO and above levels. +handlers= java.util.logging.ConsoleHandler + +# To also add the FileHandler, use the following line instead. +#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler + +# Default global logging level. +# This specifies which kinds of events are logged across +# all loggers. For any given facility this global level +# can be overriden by a facility specific level +# Note that the ConsoleHandler also has a separate level +# setting to limit messages printed to the console. +.level= FINE + +############################################################ +# Handler specific properties. +# Describes specific configuration info for Handlers. +############################################################ + +# default file output is in user's home directory. +java.util.logging.FileHandler.pattern = %h/java%u.log +java.util.logging.FileHandler.limit = 50000 +java.util.logging.FileHandler.count = 1 +java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter + +# Limit the message that are printed on the console to FINE and above. +java.util.logging.ConsoleHandler.level = FINEST +java.util.logging.ConsoleHandler.formatter = net.java.sip.communicator.util.ScLogFormatter + + +############################################################ +# Facility specific properties. +# Provides extra control for each logger. +############################################################ + +# For example, set the com.xyz.foo logger to only log SEVERE +# messages: +com.xyz.foo.level = SEVERE diff --git a/lib/moduleloader.jar b/lib/moduleloader.jar Binary files differnew file mode 100644 index 0000000..f6e6239 --- /dev/null +++ b/lib/moduleloader.jar diff --git a/lib/nist-sdp-1.0.jar b/lib/nist-sdp-1.0.jar Binary files differnew file mode 100644 index 0000000..d273d49 --- /dev/null +++ b/lib/nist-sdp-1.0.jar diff --git a/lib/nist-sip-1.2.jar b/lib/nist-sip-1.2.jar Binary files differnew file mode 100644 index 0000000..4d2552a --- /dev/null +++ b/lib/nist-sip-1.2.jar diff --git a/lib/oscar.client.run.properties b/lib/oscar.client.run.properties new file mode 100644 index 0000000..7e68add --- /dev/null +++ b/lib/oscar.client.run.properties @@ -0,0 +1,67 @@ +# +# Framework config properties. +# +org.osgi.framework.system.packages= org.osgi.framework; \ + javax.swing;\ + javax.swing.event;\ + javax.swing.table;\ + javax.swing.text; \ + javax.accessibility; \ + javax.swing.plaf; \ + javax.swing.tree; \ + javax.swing.undo; \ + javax.swing.event; \ + javax.swing.border; \ + org.w3c.dom;\ + org.xml.sax;\ + javax.xml.parsers;\ + org.jaxen; \ + org.jaxen.dom; \ + org.jaxen.saxpath; \ + org.apache.xml.serializer;\ + javax.xml.transform;\ + javax.xml.transform.dom;\ + javax.xml.transform.stream;\ + javax.media;\ + javax.media.format;\ + javax.media.protocol;\ + net.java.stun4j;\ + net.java.stun4j.client;\ + net.java.stun4j.StunAddress;\ + net.java.stun4j.StunException; + +oscar.auto.start.1= \ + file:lib/bundle/shell.jar \ + file:lib/bundle/bundlerepository.jar \ + file:lib/bundle/servicebinder.jar \ + file:lib/bundle/tablelayout.jar + +oscar.auto.start.2= \ + file:sc-bundles/util.jar + +oscar.auto.start.3= \ + file:sc-bundles/configuration.jar \ + file:sc-bundles/media.jar \ + file:sc-bundles/netaddr.jar + +oscar.auto.start.4= \ + file:sc-bundles/resources.jar \ + file:sc-bundles/history.jar + +# Uncomment the following lines if you want to run the architect viewer +# bundle. +#oscar.auto.start.100= \ +# file:lib/bundle/architectureviewer1.1.jar + +#Specify the directory where oscar should deploy its bundles +oscar.cache.profiledir=sip-communicator.bin + + +oscar.startlevel.framework=100 +oscar.startlevel.bundle=100 +# +# Bundle config properties. +# +#org.osgi.service.http.port=8080 +#osgi.shell.telnet=on +#oscar.repository.url=file:/home/rickhall/projects/noscar/repository.xml diff --git a/lib/oscar.jar b/lib/oscar.jar Binary files differnew file mode 100644 index 0000000..f7fb491 --- /dev/null +++ b/lib/oscar.jar diff --git a/lib/oscar.unit.test.properties b/lib/oscar.unit.test.properties new file mode 100644 index 0000000..c3d582e --- /dev/null +++ b/lib/oscar.unit.test.properties @@ -0,0 +1,79 @@ +# +# Oscar configuration properties. +# This file configures the OSCAR framework to run sip-communicator unit tests +# and Service Implementation Compatibility Kits +# +org.osgi.framework.system.packages= org.osgi.framework; \ + javax.swing; \ + javax.swing.event; \ + javax.swing.table; \ + org.w3c.dom; \ + org.xml.sax; \ + javax.xml.parsers;\ + org.jaxen; \ + org.jaxen.dom; \ + org.jaxen.saxpath; \ + org.apache.xml.serializer;\ + javax.xml.transform;\ + javax.xml.transform.dom;\ + javax.xml.transform.stream;\ + javax.media;\ + javax.media.format;\ + javax.media.protocol;\ + javax.sound.sampled;\ + com.sun.media.protocol.javasound;\ + com.sun.media.protocol.v4l;\ + net.java.stun4j; \ + net.java.stun4j.StunAddress;\ + net.java.stun4j.StunException;\ + net.java.stun4j.client;\ + org.apache.tools.ant.taskdefs.optional.junit; + + +# +# In case you want testing to run using oscar's graphical ui then uncomment +# +# the following and copy/paste them after the shell.jar bundle +# file:lib/bundle/shellgui.jar \ +# file:lib/bundle/shellplugin.jar \ +# file:lib/bundle/tablelayout.jar \ +# + +oscar.auto.start.1= \ + file:lib/bundle/shell.jar \ + file:lib/bundle/bundlerepository.jar \ + file:lib/bundle/servicebinder.jar \ + file:lib/bundle/junit.jar + +oscar.auto.start.2= \ + file:sc-bundles/util.jar + +oscar.auto.start.3= \ + file:sc-bundles/configuration.jar \ + file:sc-bundles/media.jar \ + file:sc-bundles/netaddr.jar + +oscar.auto.start.4= \ + file:sc-bundles/protocol-sip.jar \ + file:sc-bundles/resources.jar \ + file:sc-bundles/history.jar + +oscar.auto.start.5= \ + file:sc-bundles/slickless.jar \ + file:sc-bundles/configuration-slick.jar \ + file:sc-bundles/media-slick.jar \ + file:sc-bundles/netaddr-slick.jar \ + file:sc-bundles/resources-slick.jar \ + file:sc-bundles/history-slick.jar + + +oscar.auto.start.100= \ + file:sc-bundles/slick-runner.jar + +#Specify the directory where oscar should deploy its bundles +oscar.cache.profiledir=sip-communicator.utest.bin + +oscar.startlevel.framework=100 +oscar.startlevel.bundle=100 + +oscar.embedded.execution=true diff --git a/lib/osgi.jar b/lib/osgi.jar Binary files differnew file mode 100644 index 0000000..0f9ca8a --- /dev/null +++ b/lib/osgi.jar diff --git a/lib/servicebinder.jar b/lib/servicebinder.jar Binary files differnew file mode 100644 index 0000000..bfe2e40 --- /dev/null +++ b/lib/servicebinder.jar diff --git a/lib/skinlf.jar b/lib/skinlf.jar Binary files differnew file mode 100644 index 0000000..5486b0e --- /dev/null +++ b/lib/skinlf.jar diff --git a/src/net/java/sip/communicator/.cvsignore b/src/net/java/sip/communicator/.cvsignore new file mode 100644 index 0000000..843c2e7 --- /dev/null +++ b/src/net/java/sip/communicator/.cvsignore @@ -0,0 +1 @@ +sick
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/configuration/.cvsignore b/src/net/java/sip/communicator/impl/configuration/.cvsignore new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/net/java/sip/communicator/impl/configuration/.cvsignore diff --git a/src/net/java/sip/communicator/impl/configuration/Activator.java b/src/net/java/sip/communicator/impl/configuration/Activator.java new file mode 100644 index 0000000..0ec34e2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/configuration/Activator.java @@ -0,0 +1,74 @@ +/* + * 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.configuration; + +import org.osgi.framework.*; +import net.java.sip.communicator.service.configuration.*; +import net.java.sip.communicator.util.Logger; +import java.io.*; +import net.java.sip.communicator.util.xml.*; +import net.java.sip.communicator.impl.configuration.xml.*; + +/** + * + * @author Emil Ivov + */ +public class Activator + implements BundleActivator +{ + private Logger logger = Logger.getLogger(ConfigurationServiceImpl.class); + private ConfigurationServiceImpl impl = new ConfigurationServiceImpl(); + + /** + * Starts the configuration service + * + * @param bundleContext the BundleContext as provided from the osgi + * framework. + * @throws Exception if anything goes wrong + */ + public void start(BundleContext bundleContext) throws Exception + { + try + { + logger.logEntry(); + + bundleContext.registerService( ConfigurationService.class.getName(), + impl, + new java.util.Hashtable() ); + + logger.debug("Successfully registered " + getClass().getName()); + } + finally + { + logger.logExit(); + } + } + + /** + * Causes the configuration service to store the properties object and + * unregisters the configuration servcice. + * + * @param bundlecontext BundleContext + * @throws Exception + * @todo Implement this org.osgi.framework.BundleActivator method + */ + public void stop(BundleContext bundlecontext) throws Exception + { + try + { + logger.logEntry(); + + + + logger.info("The ConfigurationService stop method has been called."); + } + finally + { + logger.logEntry(); + } + } +} diff --git a/src/net/java/sip/communicator/impl/configuration/ChangeEventDispatcher.java b/src/net/java/sip/communicator/impl/configuration/ChangeEventDispatcher.java new file mode 100644 index 0000000..d0a116e --- /dev/null +++ b/src/net/java/sip/communicator/impl/configuration/ChangeEventDispatcher.java @@ -0,0 +1,433 @@ +/* + * 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.configuration; + +import net.java.sip.communicator.service.configuration.event.*; +import java.util.*; +import net.java.sip.communicator.service.configuration.*; +import net.java.sip.communicator.util.xml.*; +import net.java.sip.communicator.impl.configuration.xml.*; + +/** + * This is a utility class that can be used by objects that support constrained + * properties. You can use an instance of this class as a member field and + * delegate various work to it. + * + * @author Emil Ivov + */ +public class ChangeEventDispatcher +{ + + /** + * All property change listeners registered so far. + */ + private Vector propertyChangeListeners; + + /** + * All listeners registered for vetoable change events. + */ + private Vector vetoableChangeListeners; + + /** + * Hashtable for managing property change listeners registered for specific + * properties. Maps property names to PropertyChangeSupport objects. + */ + private Hashtable propertyChangeChildren; + + /** + * Hashtable for managing vetoable change listeners registered for specific + * properties. Maps property names to PropertyChangeSupport objects. + */ + private Hashtable vetoableChangeChildren; + + /** + * The object to be provided as the "source" for any generated events. + */ + private Object source; + + /** + * Constructs a <code>VetoableChangeSupport</code> object. + * + * @param sourceObject The object to be given as the source for any events. + */ + public ChangeEventDispatcher(Object sourceObject) + { + if (sourceObject == null) + { + throw new NullPointerException(); + } + source = sourceObject; + } + + /** + * Add a PropertyChangeListener to the listener list. + * The listener is registered for all properties. + * + * @param listener The PropertyChangeChangeListener to be added + */ + public synchronized void addPropertyChangeListener( + PropertyChangeListener listener) + { + if (propertyChangeListeners == null) + { + propertyChangeListeners = new Vector(); + } + + propertyChangeListeners.addElement(listener); + } + + /** + * Add a PropertyChangeListener for a specific property. The listener + * will be invoked only when a call on firePropertyChange names that + * specific property. + * + * @param propertyName The name of the property to listen on. + * @param listener The ConfigurationChangeListener to be added + */ + + public synchronized void addPropertyChangeListener( + String propertyName, + PropertyChangeListener listener) + { + if (propertyChangeChildren == null) + { + propertyChangeChildren = new Hashtable(); + } + ChangeEventDispatcher child = (ChangeEventDispatcher) propertyChangeChildren.get( + propertyName); + if (child == null) + { + child = new ChangeEventDispatcher(source); + propertyChangeChildren.put(propertyName, child); + } + child.addPropertyChangeListener(listener); + } + + /** + * Remove a PropertyChangeListener from the listener list. + * This removes a ConfigurationChangeListener that was registered + * for all properties. + * + * @param listener The PropertyChangeListener to be removed + */ + public synchronized void removePropertyChangeListener( + PropertyChangeListener listener) + { + + if (propertyChangeListeners == null) + { + return; + } + propertyChangeListeners.removeElement(listener); + } + + /** + * Remove a PropertyChangeListener for a specific property. + * + * @param propertyName The name of the property that was listened on. + * @param listener The VetoableChangeListener to be removed + */ + public synchronized void removePropertyChangeListener( + String propertyName, + PropertyChangeListener listener) + { + if (propertyChangeChildren == null) + { + return; + } + ChangeEventDispatcher child = (ChangeEventDispatcher) + propertyChangeChildren.get( propertyName ); + + if (child == null) + { + return; + } + child.removePropertyChangeListener(listener); + } + + /** + * Add a VetoableChangeListener to the listener list. + * The listener is registered for all properties. + * + * @param listener The VetoableChangeListener to be added + */ + public synchronized void addVetoableChangeListener( + VetoableChangeListener listener) + { + if (vetoableChangeListeners == null) + { + vetoableChangeListeners = new Vector(); + } + + vetoableChangeListeners.addElement(listener); + } + + /** + * Remove a VetoableChangeListener from the listener list. + * This removes a VetoableChangeListener that was registered + * for all properties. + * + * @param listener The VetoableChangeListener to be removed + */ + public synchronized void removeVetoableChangeListener( + VetoableChangeListener listener) + { + + if (vetoableChangeListeners == null) + { + return; + } + vetoableChangeListeners.removeElement(listener); + } + + /** + * Add a VetoableChangeListener for a specific property. The listener + * will be invoked only when a call on fireVetoableChange names that + * specific property. + * + * @param propertyName The name of the property to listen on. + * @param listener The ConfigurationChangeListener to be added + */ + + public synchronized void addVetoableChangeListener( + String propertyName, + VetoableChangeListener listener) + { + if (vetoableChangeChildren == null) + { + vetoableChangeChildren = new Hashtable(); + } + ChangeEventDispatcher child = (ChangeEventDispatcher) vetoableChangeChildren.get( + propertyName); + if (child == null) + { + child = new ChangeEventDispatcher(source); + vetoableChangeChildren.put(propertyName, child); + } + child.addVetoableChangeListener(listener); + } + + /** + * Remove a VetoableChangeListener for a specific property. + * + * @param propertyName The name of the property that was listened on. + * @param listener The VetoableChangeListener to be removed + */ + public synchronized void removeVetoableChangeListener( + String propertyName, + VetoableChangeListener listener) + { + if (vetoableChangeChildren == null) + { + return; + } + ChangeEventDispatcher child = (ChangeEventDispatcher) + vetoableChangeChildren.get( propertyName ); + + if (child == null) + { + return; + } + child.removeVetoableChangeListener(listener); + } + + /** + * Report a vetoable property update to any registered listeners. If + * no one vetos the change, then fire a new ConfigurationChangeEvent + * indicating that the change has been accepted. In the case of a + * PropertyVetoException, end eventdispatch and rethrow the eception + * <p> + * No event is fired if old and new are equal and non-null. + * + * @param propertyName The programmatic name of the property + * that is about to change.. + * @param oldValue The old value of the property. + * @param newValue The new value of the property. + * @exception PropertyVetoException if the recipient wishes the property + * change to be rolled back. + */ + public void fireVetoableChange(String propertyName, + Object oldValue, Object newValue) throws + PropertyVetoException + { + if (vetoableChangeListeners == null && vetoableChangeChildren == null) + { + return; + } + + PropertyChangeEvent evt = new PropertyChangeEvent(source, + propertyName, oldValue, newValue); + fireVetoableChange(evt); + } + + /** + * Fire a vetoable property update to any registered listeners. If + * anyone vetos the change, then the excption will be rethrown by this + * method. + * <p> + * No event is fired if old and new are equal and non-null. + * + * @param evt The PropertyChangeEvent to be fired. + * @exception PropertyVetoException if at least one of the recipients has + * vetoed the change. + */ + public void fireVetoableChange(PropertyChangeEvent evt) throws + PropertyVetoException + { + + Object oldValue = evt.getOldValue(); + Object newValue = evt.getNewValue(); + String propertyName = evt.getPropertyName(); + if (oldValue != null && newValue != null && oldValue.equals(newValue)) + { + return; + } + + Vector targets = null; + ChangeEventDispatcher child = null; + synchronized (this) + { + if (vetoableChangeListeners != null) + { + targets = (Vector) vetoableChangeListeners.clone(); + } + if (vetoableChangeChildren != null && propertyName != null) + { + child = (ChangeEventDispatcher)vetoableChangeChildren.get(propertyName); + } + } + + if (vetoableChangeListeners != null) + { + for (int i = 0; i < targets.size(); i++) + { + VetoableChangeListener target = + (VetoableChangeListener) targets.elementAt(i); + //don't catch the exception - let it bounce to the caller. + target.vetoableChange(evt); + } + } + + if (child != null) + { + child.fireVetoableChange(evt); + } + } + + + /** + * Report a bound property update to any registered listeners. + * No event is fired if old and new are equal and non-null. + * + * @param propertyName The programmatic name of the property + * that was changed. + * @param oldValue The old value of the property. + * @param newValue The new value of the property. + */ + public void firePropertyChange(String propertyName, + Object oldValue, Object newValue) + { + if (oldValue != null && newValue != null && oldValue.equals(newValue)) + { + return; + } + firePropertyChange(new PropertyChangeEvent(source, propertyName, + oldValue, newValue)); + } + + /** + * Fire an existing PropertyChangeEvent to any registered listeners. + * No event is fired if the given event's old and new values are + * equal and non-null. + * @param evt The PropertyChangeEvent object. + */ + public void firePropertyChange(PropertyChangeEvent evt) + { + Object oldValue = evt.getOldValue(); + Object newValue = evt.getNewValue(); + String propertyName = evt.getPropertyName(); + if (oldValue != null && newValue != null && oldValue.equals(newValue)) + { + return; + } + + if (propertyChangeListeners != null) + { + Iterator iterator = propertyChangeListeners.iterator(); + while (iterator.hasNext()) + { + PropertyChangeListener target = + (PropertyChangeListener) iterator.next(); + target.propertyChange(evt); + } + } + + if (propertyChangeChildren != null && propertyName != null) + { + ChangeEventDispatcher child = null; + child = (ChangeEventDispatcher) propertyChangeChildren.get(propertyName); + if (child != null) + { + child.firePropertyChange(evt); + } + } + } + + /** + * Check if there are any listeners for a specific property. (Generic + * listeners count as well) + * + * @param propertyName the property name. + * @return true if there are one or more listeners for the given property + */ + public synchronized boolean hasPropertyChangeListeners(String propertyName) + { + if(propertyChangeListeners != null && !propertyChangeListeners.isEmpty()) + { + // there is a generic listener + return true; + } + if (propertyChangeChildren != null) + { + ChangeEventDispatcher child = (ChangeEventDispatcher) propertyChangeChildren.get( + propertyName); + if (child != null && child.propertyChangeListeners != null) + { + return!child.propertyChangeListeners.isEmpty(); + } + } + return false; + } + + /** + * Check if there are any vetoable change listeners for a specific property. + * (Generic vetoable change listeners count as well) + * + * @param propertyName the property name. + * @return true if there are one or more listeners for the given property + */ + public synchronized boolean hasVetoableChangeListeners(String propertyName) + { + if(vetoableChangeListeners != null && !vetoableChangeListeners.isEmpty()) + { + // there is a generic listener + return true; + } + if (vetoableChangeChildren != null) + { + ChangeEventDispatcher child = (ChangeEventDispatcher) + vetoableChangeChildren.get(propertyName); + + if (child != null && child.vetoableChangeListeners != null) + { + return!child.vetoableChangeListeners.isEmpty(); + } + } + return false; + } + +} diff --git a/src/net/java/sip/communicator/impl/configuration/ConfigurationServiceImpl.java b/src/net/java/sip/communicator/impl/configuration/ConfigurationServiceImpl.java new file mode 100644 index 0000000..2707da1 --- /dev/null +++ b/src/net/java/sip/communicator/impl/configuration/ConfigurationServiceImpl.java @@ -0,0 +1,846 @@ +/* + * 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.configuration; + +import java.io.*; +import java.util.*; +import javax.xml.parsers.*; + +import org.w3c.dom.*; +import org.xml.sax.*; +import net.java.sip.communicator.impl.configuration.xml.*; +import net.java.sip.communicator.service.configuration.*; +import net.java.sip.communicator.service.configuration.event.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.xml.*; + + + +/** + * A straight forward implementation of the ConfigurationService using an xml + * file for storing properties. Currently only String properties are + * meaningfully saved (we should probably consider how and whether we should + * take care of the rest). + * + * @author Emil Ivov + * @author Damian Minkov + */ +public class ConfigurationServiceImpl + implements ConfigurationService +{ + private Logger logger = Logger.getLogger(ConfigurationServiceImpl.class); + + /** + * The XML Document containing the configuration file this service loaded. + */ + private Document propertiesDocument = null; + + /** Name of the xml attribute containing property values */ + private static final String ATTRIBUTE_VALUE = "value"; + + /** + * Name of the xml attribute indicating that a property is to be resolved + * in the system properties + */ + private static final String SYSTEM_ATTRIBUTE_NAME = "system"; + + /** The value of the Name of the xml attribute containing property values */ + private static final String SYSTEM_ATTRIBUTE_TRUE = "true"; + + /** + * The name of the system property that stores the name of the configuration + * file. + */ + private static final String FILE_NAME_PROPERTY = + "net.java.sip.communicator.PROPERTIES_FILE_NAME"; + + /** + * Our event dispatcher. + */ + private ChangeEventDispatcher changeEventDispatcher = + new ChangeEventDispatcher(this); + + /** + * The list of properties currently registered in the configuration service. + */ + private Map properties = new Hashtable(); + + /** + * Contains the properties that were initially loaded from the configuration + * file or (if the properties have been modified and saved since initially + * loaded) those that were last written to the file.We use the property so + * that we could determine which properties are new and do not have a + * corresponding node in the XMLDocument object. + */ + private Map fileExtractedProperties = new Hashtable(); + + /** + * Sets the property with the specified name to the specified value. Calling + * this method would first trigger a PropertyChangeEvent that will + * be dispatched to all VetoableChangeListeners. In case no complaints + * (PropertyVetoException) have been received, the property will be actually + * changed and a PropertyChangeEvent will be dispatched. + * <p> + * @param propertyName String + * @param property Object + * @throws PropertyVetoException in case the changed has been refused by + * at least one propertychange listener. + */ + public void setProperty(String propertyName, Object property) + throws PropertyVetoException + { + setProperty(propertyName, property, false); + } + + /** + * Sets the property with the specified name to the specified. Calling + * this method would first trigger a PropertyChangeEvent that will + * be dispatched to all VetoableChangeListeners. In case no complaints + * (PropertyVetoException) have been received, the property will be actually + * changed and a PropertyChangeEvent will be dispatched. This method also + * allows the caller to specify whether or not the specified property is a + * system one. + * <p> + * @param propertyName the name of the property to change. + * @param property the new value of the specified property. + * @param isSystem specifies whether or not the property being is a System + * property and should be resolved against the system + * property set. If the property has previously been + * specified as system then this value is inteernally forced + * to true. + * @throws PropertyVetoException in case the changed has been refused by + * at least one propertychange listener. + */ + public void setProperty(String propertyName, Object property, + boolean isSystem) throws PropertyVetoException + { + try{ + logger.logEntry(); + + Object oldValue = getProperty(propertyName); + //first check whether the change is ok with everyone + if (changeEventDispatcher.hasVetoableChangeListeners(propertyName)) + changeEventDispatcher.fireVetoableChange( + propertyName, oldValue, property); + + //no exception was thrown - lets change the property and fire a + //change event + + logger.trace(propertyName+"( oldValue="+oldValue + +", newValue=" + property+"."); + + //once set system, a property remains system event if the user + //specified sth else + + if( isSystem(propertyName) ) + isSystem = true; + + if (property == null){ + properties.remove(propertyName); + + if(isSystem){ + //we can't remove or nullset a sys prop so let's "empty" it. + System.setProperty(propertyName, ""); + } + } + else{ + if(isSystem){ + //in case this is a system property, we must only store it + //in the System property set and keep only a ref locally. + System.setProperty(propertyName, property.toString()); + properties.put(propertyName, + new PropertyReference(propertyName)); + } + else{ + properties.put(propertyName, property); + } + } + if (changeEventDispatcher.hasPropertyChangeListeners(propertyName)) + changeEventDispatcher.firePropertyChange( + propertyName, oldValue, property); + } + finally + { + logger.logExit(); + } + + } + + /** + * Returns the value of the property with the specified name or null if no + * such property exists. + * @param propertyName the name of the property that is being queried. + * @return the value of the property with the specified name. + */ + public Object getProperty(String propertyName) + { + Object value = properties.get(propertyName); + + //if this is a property reference make sure we return the referenced + //value and not the reference itself + if(value instanceof PropertyReference) + return ((PropertyReference)value).getValue(); + else + return value; + } + + /** + * Adds a PropertyChangeListener to the listener list. + * + * @param listener the PropertyChangeListener to be added + */ + public void addPropertyChangeListener(PropertyChangeListener listener) + { + changeEventDispatcher.addPropertyChangeListener(listener); + } + + /** + * Removes a PropertyChangeListener from the listener list. + * + * @param listener the PropertyChangeListener to be removed + */ + public void removePropertyChangeListener(PropertyChangeListener listener) + { + changeEventDispatcher.removePropertyChangeListener(listener); + } + + /** + * Adds a PropertyChangeListener to the listener list for a specific + * property. + * + * @param propertyName one of the property names listed above + * @param listener the PropertyChangeListener to be added + */ + public void addPropertyChangeListener(String propertyName, + PropertyChangeListener listener) + { + changeEventDispatcher. + addPropertyChangeListener(propertyName, listener); + } + + /** + * Removes a PropertyChangeListener from the listener list for a specific + * property. + * + * @param propertyName a valid property name + * @param listener the PropertyChangeListener to be removed + */ + public void removePropertyChangeListener(String propertyName, + PropertyChangeListener listener) + { + changeEventDispatcher. + removePropertyChangeListener(propertyName, listener); + } + + /** + * Adds a VetoableChangeListener to the listener list. + * + * @param listener the VetoableChangeListener to be added + */ + public void addVetoableChangeListener(VetoableChangeListener listener) + { + changeEventDispatcher.addVetoableChangeListener(listener); + } + + /** + * Removes a VetoableChangeListener from the listener list. + * + * @param listener the VetoableChangeListener to be removed + */ + public void removeVetoableChangeListener(VetoableChangeListener listener) + { + changeEventDispatcher.removeVetoableChangeListener(listener); + } + + /** + * Adds a VetoableChangeListener to the listener list for a specific + * property. + * + * @param propertyName one of the property names listed above + * @param listener the VetoableChangeListener to be added + */ + public void addVetoableChangeListener(String propertyName, + VetoableChangeListener listener) + { + changeEventDispatcher.addVetoableChangeListener(propertyName, listener); + } + + /** + * Removes a VetoableChangeListener from the listener list for a specific + * property. + * + * @param propertyName a valid property name + * @param listener the VetoableChangeListener to be removed + */ + public void removeVetoableChangeListener(String propertyName, + VetoableChangeListener listener) + { + changeEventDispatcher.removeVetoableChangeListener(propertyName, + listener); + } + + /** + * Initializes the configuration service impl and makes it load an initial + * configuration from the conf file. + */ + void start() + { + try + { + reloadConfiguration(); + } + catch (XMLException ex) + { + logger.error("Failed to parse the configuration file.", ex); + } + catch (IOException ex) + { + logger.error("Failed to load the configuration file", ex); + } + } + + public void reloadConfiguration() + throws IOException, XMLException + { + properties = new Hashtable(); + fileExtractedProperties = + loadConfiguration(getConfigurationFile()); + this.properties.putAll(fileExtractedProperties); + } + + /** + * Loads the contents of the specified configuration file into the local + * properties object. + * @param file a reference to the configuration file to load. + * @return a hashtable containing all properties extracted from the + * specified file. + * + * @throws IOException if the specified file does not exist + * @throws XMLException if there is a problem with the file syntax. + */ + Map loadConfiguration(File file) + throws IOException, XMLException + { + try + { + logger.logEntry(); + + DocumentBuilderFactory factory = + DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Map properties = new Hashtable(); + + propertiesDocument = builder.parse(file); + + Node root = propertiesDocument.getFirstChild(); + + Node currentNode = null; + NodeList children = root.getChildNodes(); + for(int i = 0; i < children.getLength(); i++) + { + currentNode = children.item(i); + + if(currentNode.getNodeType() == Node.ELEMENT_NODE) + { + StringBuffer propertyNameBuff = new StringBuffer(); + propertyNameBuff.append(currentNode.getNodeName()); + loadNode(currentNode, propertyNameBuff, properties); + } + } + + return properties; + } + catch(SAXException ex) + { + logger.error("Error parsing configuration file", ex); + throw new XMLException(ex.getMessage(), ex); + } + catch(ParserConfigurationException ex) + { + //it is not highly probable that this might happen - so lets just + //log it. + logger.error("Error finding configuration for default parsers", ex); + return new Hashtable(); + } + finally + { + logger.logExit(); + } + } + + public void storeConfiguration() + throws IOException + { + storeConfiguration(getConfigurationFile()); + + } + + /** + * Stores local properties in the specified configuration file. + * @param file a reference to the configuration file where properties should + * be stored. + * @throws IOException if there was a problem writing to the specified file. + */ + private void storeConfiguration(File file) + throws IOException + { + try + { + logger.logEntry(); + + DocumentBuilderFactory factory = + DocumentBuilderFactory.newInstance(); + + //resolve the properties that were initially in the file - back to + //the document. + + Node root = propertiesDocument.getFirstChild(); + + Node currentNode = null; + NodeList children = root.getChildNodes(); + for(int i = 0; i < children.getLength(); i++) + { + currentNode = children.item(i); + + if(currentNode.getNodeType() == Node.ELEMENT_NODE) + { + StringBuffer propertyNameBuff = new StringBuffer(); + propertyNameBuff.append(currentNode.getNodeName()); + updateNode(currentNode, propertyNameBuff, properties); + } + } + + //create in the document the properties that were added by other + //bundles after the initial property load. + + Map newlyAddedProperties = cloneProperties(); + + //remove those that were originally there; + Iterator propNames = fileExtractedProperties.keySet().iterator(); + while(propNames.hasNext()) + newlyAddedProperties.remove(propNames.next()); + + this.processNewProperties(propertiesDocument, + newlyAddedProperties); + + + //write the file. + XMLConfUtils.writeXML(propertiesDocument, getConfigurationFile()); + } + finally + { + logger.logExit(); + } + } + + /** + * Loads the contents of the specified node and its children into the local + * properties. Any nodes marked as "system" will also be resolved in the + * system properties. + * @param node the root node that we shold load together with its children + * @param propertyNameBuff a StringBuffer containing the prefix describing + * the route to the specified node including its one name + * @param properties the dictionary object where all properties extracted + * from this node and its children should be recorded. + */ + private void loadNode(Node node, + StringBuffer propertyNameBuff, + Map properties) + { + Node currentNode = null; + NodeList children = node.getChildNodes(); + for(int i = 0; i < children.getLength(); i++) + { + currentNode = children.item(i); + + if(currentNode.getNodeType() == Node.ELEMENT_NODE) + { + StringBuffer newPropBuff = + new StringBuffer(propertyNameBuff + + "." +currentNode.getNodeName()); + String value = XMLConfUtils.getAttribute( + currentNode, ATTRIBUTE_VALUE); + + String propertyType = + XMLConfUtils.getAttribute(currentNode, SYSTEM_ATTRIBUTE_NAME); + + // the value attr is present we must handle the desired property + if(value != null) + { + + //if the property is marked as "system", we should resolve + //it against the system properties and only store a + //reference locally. this is normally done for properties + //that are supposed to configure underlying libraries. + if(propertyType != null + && propertyType.equals(SYSTEM_ATTRIBUTE_TRUE)) + { + properties.put( + newPropBuff.toString(), + new PropertyReference(newPropBuff.toString())); + System.setProperty(newPropBuff.toString(), value); + } + else + { + properties.put(newPropBuff.toString(), value); + } + } + + //load child nodes + loadNode(currentNode, newPropBuff, properties); + + } + } + } + + /** + * Updates the value of the specified node and its children to reflect those + * in the properties file. Nodes marked as "system" will be updated from + * the specified properties object and not from the system properties since + * if any intentional change (through a configuration form) has occurred + * it will have been made there. + * + * @param node the root node that we shold update together with its children + * @param propertyNameBuff a StringBuffer containing the prefix describing + * the dot separated route to the specified node including its one name + * @param properties the dictionary object where the up to date values of + * the node should be queried. + */ + private void updateNode(Node node, + StringBuffer propertyNameBuff, + Map properties) + { + Node currentNode = null; + NodeList children = node.getChildNodes(); + for(int i = 0; i < children.getLength(); i++) + { + currentNode = children.item(i); + + if(currentNode.getNodeType() == Node.ELEMENT_NODE) + { + StringBuffer newPropBuff = + new StringBuffer(propertyNameBuff + + "." +currentNode.getNodeName()); + + Attr attr = + ((Element)currentNode).getAttributeNode(ATTRIBUTE_VALUE); + + if(attr != null) + { + //update the corresponding node + Object value = properties.get(newPropBuff.toString()); + boolean isSystem = value instanceof PropertyReference; + String prop = isSystem + ?((PropertyReference)value).getValue().toString() + :value.toString(); + + attr.setNodeValue(prop); + + //in case the property has changed to system since the last + //load - update the conf file accordingly. + if(isSystem) + ((Element)currentNode).setAttribute( + SYSTEM_ATTRIBUTE_NAME, SYSTEM_ATTRIBUTE_TRUE); + else + ((Element)currentNode).removeAttribute( + SYSTEM_ATTRIBUTE_NAME); + + } + + //update child nodes + updateNode(currentNode, newPropBuff, properties); + } + } + } + + /** + * Returns a reference to the configuration file that the service should + * load. The method would try to load a file with the name + * sip-communicator.xml unless a different one is specified in the system + * property net.java.sip.communicator.PROPERTIES_FILE_NAME . The method + * would first try to load the file from the current directory if it exists + * this is not the case a load would be attempted from the + * $HOME/.sip-communicator directory. In case it was not found there either + * we'll look for it in all locations currently present in the $CLASSPATH. + * In case we find it in there we will copy it to the + * $HOME/.sip-communicator directory in case it was in a jar archive and + * return the reference to the newly created file. In case the file is + * to be found noweher - a new empty file in the user home directory and + * returns a link to that one. + * + * + * @return the configuration the sip- + */ + File getConfigurationFile() + { + try + { + logger.logEntry(); + + //see whether we have a user specified name for the conf file + String pFileName = getSystemProperty( + FILE_NAME_PROPERTY); + if (pFileName == null) + { + pFileName = "sip-communicator.xml"; + } + + // try to open the file in current directory + File configFileInCurrentDir = new File(pFileName); + if (configFileInCurrentDir.exists()) + { + logger.debug("Using config file in current dir: " + + configFileInCurrentDir.getCanonicalPath()); + return configFileInCurrentDir; + } + + // we didn't find it in ".", try the user.home directory + File configDir = new File(getSystemProperty("user.home") + + File.separator + + ".sip-communicator"); + + File configFileInUserHomeDir = + new File(configDir, pFileName); + + if (configFileInUserHomeDir.exists()) + { + logger.debug("Using config file in $HOME/.sip-communicator: " + + configFileInCurrentDir.getCanonicalPath()); + return configFileInUserHomeDir; + } + + // If we are in a jar - copy config file from jar to user home. + logger.trace("Copying config file."); + + configDir.mkdirs(); + InputStream in = getClass().getClassLoader(). + getResourceAsStream(pFileName); + + //Return an empty file if there wasn't any in the jar + //null check report from John J. Barton - IBM + if (in == null) + { + configFileInUserHomeDir.createNewFile(); + logger.debug("Created an empty file in $HOME: " + + configFileInCurrentDir.getCanonicalPath()); + return configFileInUserHomeDir; + } + BufferedReader reader = + new BufferedReader(new InputStreamReader(in)); + + PrintWriter writer = new PrintWriter(new FileWriter( + configFileInUserHomeDir)); + + String line = null; + logger.debug("Copying properties file:"); + while ( (line = reader.readLine()) != null) + { + writer.println(line); + logger.debug(line); + } + writer.flush(); + return configFileInUserHomeDir; + } + catch (IOException ex) + { + logger.error("Error creating config file", ex); + return null; + } + finally + { + logger.logExit(); + } + } + + /** + * Creates new entries in the xml <code>doc</code> for every element in the + * <code>newProperties</code> table. + * + * @param doc the XML <code>Document</code> where the new entries should be + * created + * @param newProperties the table containing the properties that are to be + * in troduced in the document. + */ + private void processNewProperties(Document doc, + Map newProperties) + { + Iterator propNames = newProperties.keySet().iterator(); + while(propNames.hasNext()) + { + String key = (String)propNames.next(); + Object value = newProperties.get(key); + boolean isSystem = value instanceof PropertyReference; + value = isSystem + ?((PropertyReference)value).getValue() + :value; + processNewProperty(doc, key, value.toString(), isSystem); + } + } + + /** + * Creates an entry in the xml <code>doc</code> for the specified key value + * pair. + * @param doc the XML <code>document</code> to update. + * @param key the value of the <code>name</code> attribute for the new entry + * @param value the value of the <code>value</code> attribue for the new + * @param isSystem specifies whether this is a system property (system + * attribute will be set to true). + * entry. + */ + private void processNewProperty(Document doc, + String key, + String value, + boolean isSystem) + { + StringTokenizer tokenizer = new StringTokenizer(key, "."); + String[] toks = new String[tokenizer.countTokens()]; + int i = 0; + while(tokenizer.hasMoreTokens()) + toks[i++] = tokenizer.nextToken(); + + String[] chain = new String[toks.length - 1]; + for (int j = 0; j < chain.length; j++) + { + chain[j] = toks[j]; + } + + String nodeName = toks[toks.length - 1]; + + Element parent = XMLConfUtils.createLastPathComponent(doc, chain); + Element newNode = XMLConfUtils.findChild(parent, nodeName); + if (newNode == null) + { + newNode = doc.createElement(nodeName); + parent.appendChild(newNode); + } + newNode.setAttribute("value", value); + + if(isSystem) + newNode.setAttribute(SYSTEM_ATTRIBUTE_NAME, SYSTEM_ATTRIBUTE_TRUE); + + } + + + /** + * Returns the value of the specified java system property. In case the + * value was a zero length String or one that only contained whitespaces, + * null is returned. This method is for internal use only. Users of the + * configuration service are to use the getProperty() or getString() methods + * which would automatically determine whether a property is system or not. + * @param propertyName the name of the property whose value we need. + * @return the value of the property with name propertyName or null if + * the value had length 0 or only contained spaces tabs or new lines. + */ + private static String getSystemProperty(String propertyName) + { + String retval = System.getProperty(propertyName); + if (retval == null){ + return retval; + } + + if (retval.trim().length() == 0){ + return null; + } + return retval; + } + + /** + * Returns the String value of the specified property (minus all + * encompasssing whitespaces)and null in case no property value was mapped + * against the specified propertyName, or in case the returned property + * string had zero length or contained whitespaces only. + * + * @param propertyName the name of the property that is being queried. + * @return the result of calling the property's toString method and null in + * case there was no vlaue mapped against the specified + * <code>propertyName</code>, or the returned string had zero length or + * contained whitespaces only. + */ + public String getString(String propertyName) + { + Object propValue = getProperty(propertyName); + if (propValue == null) + return null; + + String propStrValue = propValue.toString().trim(); + + return propStrValue.length() > 0 + ? propStrValue + : null; + } + + /** + * We use property references when we'd like to store system properties. + * Simply storing System properties in our properties Map would not be + * enough since it will lead to mismatching values for the same property in + * the System property set and in our local set of properties. Storing them + * only in the System property set OTOH is a bit clumsy since it obliges + * bundles to use to different configuration property sources. For that + * reason, every time we get handed a property labeled as System, in stead + * of storing its actual value in the local property set we store a + * PropertyReference instance that will retrive it from the system + * properties when necessary. + */ + private class PropertyReference + { + private String propertyName = null; + + PropertyReference(String propertyName) + { + this.propertyName = propertyName; + } + + /** + * Return the actual value of the property as recorded in the System + * properties. + * @return the valued of the property as recorded in the System props. + */ + public Object getValue() + { + return System.getProperty(propertyName); + } + } + + /** + * Returns a copy of the Map containing all configuration properties + * @return a Map clone of the current configuration property set. + */ + private Map cloneProperties() + { + //at the time I'm writing this method we're implementing the + //configuraiton service through the use of a hashtable. this may very + //well change one day so let's not be overy assumptious (can you + //actually say that?) + if(properties instanceof Hashtable) + return (Map)((Hashtable)properties).clone(); + if(properties instanceof HashMap) + return (Map)((HashMap)properties).clone(); + if(properties instanceof TreeMap) + return (Map)((TreeMap)properties).clone(); + + //well you can't say that I didn't try!!! + + return new Hashtable(properties); + } + + /** + * Determines whether the property with the specified + * <code>propertyName</code> has been previously declared as System + * + * @param propertyName the name of the property to verify + * @return true if someone at some point specified that property to be + * system. (This could have been either through a call to + * setProperty(string, true)) or by setting the system attribute in the + * xml conf file to true. + */ + private boolean isSystem(String propertyName) + { + return properties.containsKey(propertyName) + && properties.get(propertyName) instanceof PropertyReference; + } + +} diff --git a/src/net/java/sip/communicator/impl/configuration/configuration.manifest.mf b/src/net/java/sip/communicator/impl/configuration/configuration.manifest.mf new file mode 100644 index 0000000..8d8be37 --- /dev/null +++ b/src/net/java/sip/communicator/impl/configuration/configuration.manifest.mf @@ -0,0 +1,18 @@ +Bundle-Activator: net.java.sip.communicator.impl.configuration.Activator +Bundle-Name: Configuration Service Implementation +Bundle-Description: A bundle that offers configuration utilities +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +Import-Package: org.osgi.framework, + org.apache.xml.serializer, + org.xml.sax, + org.w3c.dom, + javax.xml.transform, + javax.xml.transform.dom, + javax.xml.transform.stream, + javax.xml.parsers, + net.java.sip.communicator.util, + net.java.sip.communicator.util.xml, +Export-Package: net.java.sip.communicator.service.configuration, + net.java.sip.communicator.service.configuration.event, + net.java.sip.communicator.impl.configuration.xml, diff --git a/src/net/java/sip/communicator/impl/configuration/res/configuration.manifest.mf b/src/net/java/sip/communicator/impl/configuration/res/configuration.manifest.mf new file mode 100644 index 0000000..85f6b4a --- /dev/null +++ b/src/net/java/sip/communicator/impl/configuration/res/configuration.manifest.mf @@ -0,0 +1,7 @@ +Bundle-Activator: net.java.sip.communicator.impl.configuration.Activator +Export-Package: net.java.sip.communicator.service.configuration;net.java.sip.communicator.service.configuration.event; +Import-Package: net.java.sip.communicator.service.configuration;junit;junit.framework;junit.swingui;junit.textui;junit.awtui;junit.extensions;org.osgi.framework +Bundle-Name: Configuration Service Implementation +Bundle-Description: A bundle that offers configuration utilities +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/configuration/xml/XMLConfUtils.java b/src/net/java/sip/communicator/impl/configuration/xml/XMLConfUtils.java new file mode 100644 index 0000000..1d6d85a --- /dev/null +++ b/src/net/java/sip/communicator/impl/configuration/xml/XMLConfUtils.java @@ -0,0 +1,83 @@ +/* + * 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.configuration.xml; + +import org.w3c.dom.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.xml.*; + + + +/** + * Common XML Tasks. + * + * @author Damian Minkov + * @author Emil Ivov + */ +public class XMLConfUtils extends XMLUtils +{ + private static Logger logger = Logger.getLogger(XMLConfUtils.class); + + /** + * Returns the element which is at the end of the specified + * String chain. <great...grandparent>...<grandparent>.<parent>.<child> + * @param parent the xml element that is the parent of the root of this + * chain. + * @param chain a String array containing the names of all the child's + * parent nodes. + * @return the node represented by the specified chain + */ + public static Element getChildElementByChain(Element parent, + String[] chain, + boolean create) + { + if(chain == null) + return null; + Element e = parent; + for(int i=0; i<chain.length; i++) + { + if(e == null) + return null; + e = findChild(e, chain[i]); + } + return e; + } + + /** + * Creates (only if necessary) and returns the element which is at the end + * of the specified path. + * @param doc the target document where the specified path should be created + * @param path a dot separated string indicating the path to be created + * @return the component at the end of the newly created path. + */ + public static Element createLastPathComponent(Document doc, String[] path) + { + Element parent = (Element)doc.getFirstChild(); + if( path == null + || parent == null + || doc == null) + throw new IllegalArgumentException( + "Document parent and path must not be null"); + + Element e = parent; + for(int i=0; i < path.length; i++) + { + Element newEl = findChild(e, path[i]); + if(newEl == null) + { + newEl = doc.createElement(path[i]); + e.appendChild(newEl); + } + e = newEl; + } + return e; + } + + + + +} diff --git a/src/net/java/sip/communicator/impl/gui/Activator.java b/src/net/java/sip/communicator/impl/gui/Activator.java new file mode 100644 index 0000000..a47212c --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/Activator.java @@ -0,0 +1,45 @@ +package net.java.sip.communicator.impl.gui; + +import net.java.sip.communicator.service.gui.UIService; +import net.java.sip.communicator.util.Logger; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + + +public class Activator implements BundleActivator +{ + + private Logger logger = Logger.getLogger(Activator.class.getName()); + + private UIService uiService = null; + + public void start(BundleContext bundleContext) throws Exception + { + try + { + logger.logEntry(); + + //Create the ui service + this.uiService = + new UIServiceImpl(); + + logger.info("UI Service...[ STARTED ]"); + + bundleContext.registerService( + UIService.class.getName(), this.uiService, null); + + logger.info("UI Service ...[REGISTERED]"); + } + finally + { + logger.logExit(); + } + } + + public void stop(BundleContext bundleContext) throws Exception + { + logger.info("UI Service ...[STOPED]"); + } + +} diff --git a/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java b/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java new file mode 100644 index 0000000..c160070 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java @@ -0,0 +1,63 @@ +package net.java.sip.communicator.impl.gui; + +import net.java.sip.communicator.service.gui.UIService; +import net.java.sip.communicator.service.protocol.Call; +import net.java.sip.communicator.service.protocol.ProtocolProviderService; + +public class UIServiceImpl implements UIService { + + + public void registerProvider(ProtocolProviderService provider) { + // TODO Auto-generated method stub + + } + + public void setVisible(boolean visible) { + // TODO Auto-generated method stub + + } + + public Call[] getActiveCalls() { + // TODO Auto-generated method stub + return null; + } + + public String getUiLibName() { + // TODO Auto-generated method stub + return null; + } + + public String[] getSupportedUiLibNames() { + // TODO Auto-generated method stub + return null; + } + + public void addMenuItem(String parent, Object menuItem) + throws ClassCastException, IllegalArgumentException { + // TODO Auto-generated method stub + + } + + public void addComponent(Object component, String constraint) + throws ClassCastException, IllegalArgumentException { + // TODO Auto-generated method stub + + } + + public void addUserActionListener() { + // TODO Auto-generated method stub + + } + + public void requestAuthentication(String realm, String userName, + char[] password) { + // TODO Auto-generated method stub + + } + + public String getAuthenticationUserName() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/net/java/sip/communicator/impl/gui/gui.manifest.mf b/src/net/java/sip/communicator/impl/gui/gui.manifest.mf new file mode 100644 index 0000000..7d97775 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/gui.manifest.mf @@ -0,0 +1,6 @@ +Bundle-Activator: net.java.sip.communicator.impl.gui.Activator +Bundle-Name: Resources Management Service Provider +Bundle-Description: A bundle that implements the resource management package. +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +Export-Package: net.java.sip.communicator.gui, diff --git a/src/net/java/sip/communicator/impl/gui/main/CallList.java b/src/net/java/sip/communicator/impl/gui/main/CallList.java new file mode 100755 index 0000000..9ab53a4 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/CallList.java @@ -0,0 +1,19 @@ +package net.java.sip.communicator.impl.gui.main; + +import java.util.Vector; + +import javax.swing.JList; + +/** + * @author Yana Stamcheva + * + * The call list. + * TODO: to be removed. + */ + +public class CallList extends JList { + + public CallList(Vector data){ + super(data); + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/CallPanel.java b/src/net/java/sip/communicator/impl/gui/main/CallPanel.java new file mode 100644 index 0000000..e4cf405 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/CallPanel.java @@ -0,0 +1,54 @@ +package net.java.sip.communicator.impl.gui.main; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Image; + +import javax.swing.BorderFactory; +import javax.swing.JComboBox; +import javax.swing.JPanel; + +import net.java.sip.communicator.impl.gui.main.customcontrols.SIPCommButton; + +public class CallPanel extends JPanel{ + + private Image callButtonIcon = LookAndFeelConstants.CALL_BUTTON_ICON; + private Image hangupButtonIcon = LookAndFeelConstants.HANG_UP_BUTTON_ICON; + private Image callButtonBG = LookAndFeelConstants.CALL_BUTTON_BG; + private Image callButtonRolloverBG = LookAndFeelConstants.CALL_ROLLOVER_BUTTON_BG; + private Image hangupButtonBG = LookAndFeelConstants.HANGUP_BUTTON_BG; + private Image hangupButtonRolloverBG = LookAndFeelConstants.HANGUP_ROLLOVER_BUTTON_BG; + + private JComboBox phoneNumberCombo = new JComboBox(); + private JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + private SIPCommButton callButton; + private SIPCommButton hangupButton; + + public CallPanel(){ + + super(new BorderLayout()); + + callButton = new SIPCommButton(callButtonBG, + callButtonRolloverBG, + callButtonIcon); + hangupButton = new SIPCommButton(hangupButtonBG, + hangupButtonRolloverBG, + hangupButtonIcon); + + this.setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 5)); + this.init(); + } + + private void init() { + this.phoneNumberCombo.setEditable(true); + + this.add(phoneNumberCombo, BorderLayout.NORTH); + + this.buttonsPanel.add(callButton); + this.buttonsPanel.add(hangupButton); + + this.add(buttonsPanel, BorderLayout.CENTER); + } + +} diff --git a/src/net/java/sip/communicator/impl/gui/main/CommunicatorMain.java b/src/net/java/sip/communicator/impl/gui/main/CommunicatorMain.java new file mode 100755 index 0000000..d46c2d5 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/CommunicatorMain.java @@ -0,0 +1,96 @@ +package net.java.sip.communicator.impl.gui.main; + +import java.io.File; + +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.UIManager; +import javax.swing.plaf.metal.MetalLookAndFeel; +import javax.swing.plaf.metal.MetalTheme; + +import com.l2fprod.gui.plaf.skin.Skin; +import com.l2fprod.gui.plaf.skin.SkinLookAndFeel; +import com.l2fprod.util.OS; + +//import examples.demo; + +/** + * @author Yana Stamcheva + * + * Starts the GUI application using the SkinLookAndFeel of l2fprod. + */ +public class CommunicatorMain { + + public static void main(String[] args){ + + try { + //the theme could be passed as a parameter + if (args.length > 0) { + String themepack = args[0]; + if (themepack.endsWith(".xml")) { + SkinLookAndFeel.setSkin( + SkinLookAndFeel.loadThemePackDefinition(new File(args[0]).toURL())); + UIManager.setLookAndFeel("com.l2fprod.gui.plaf.skin.SkinLookAndFeel"); + } else if (themepack.startsWith("class:")) { + String classname = themepack.substring("class:".length()); + SkinLookAndFeel.setSkin((Skin)Class.forName(classname).newInstance()); + UIManager.setLookAndFeel("com.l2fprod.gui.plaf.skin.SkinLookAndFeel"); + } else if (themepack.startsWith("theme:")) { + String classname = themepack.substring("theme:".length()); + MetalTheme theme = (MetalTheme)Class.forName(classname).newInstance(); + MetalLookAndFeel metal = new MetalLookAndFeel(); + MetalLookAndFeel.setCurrentTheme(theme); + UIManager.setLookAndFeel(metal); + } else { + SkinLookAndFeel.setSkin(SkinLookAndFeel.loadThemePack(args[0])); + UIManager.setLookAndFeel("com.l2fprod.gui.plaf.skin.SkinLookAndFeel"); + } + } + //the default theme is set if no theme is specified + else{ + SkinLookAndFeel.setSkin( + SkinLookAndFeel.loadThemePackDefinition(new File("src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/skinlf-themepack.xml").toURL())); + UIManager.setLookAndFeel("com.l2fprod.gui.plaf.skin.SkinLookAndFeel"); + } + + //Decorates the frames and dialogs if we are running with jdk1.4 + + /* + if (OS.isOneDotFourOrMore()) { + java.lang.reflect.Method method = JFrame.class.getMethod( + "setDefaultLookAndFeelDecorated", + new Class[] { boolean.class }); + method.invoke(null, new Object[] { Boolean.TRUE }); + + method = JDialog.class.getMethod( + "setDefaultLookAndFeelDecorated", + new Class[] { boolean.class }); + method.invoke(null, new Object[] { Boolean.TRUE }); + }*/ + + } catch (Exception e) { } + + //Image frameIcon = + //new ImageIcon(demo.class.getResource("windowicon.gif")).getImage(); + // so option pane as same icon as us + //JOptionPane.getRootFrame().setIconImage(frameIcon); + + //TODO: To be removed when the contact list service is ready + ContactList clist = new ContactList(); + + clist.addContact(new ContactItem("user1")); + clist.addContact(new ContactItem("user2")); + clist.addContact(new ContactItem("user3")); + + User user = new User(); + + user.setProtocols(new String[]{"ICQ", "MSN"}); + + MainFrame mainFrame = new MainFrame(clist, user); + //mainFrame.setIconImage(frameIcon); + + mainFrame.setTitle("SIP Communicator"); + mainFrame.pack(); + + mainFrame.setVisible(true); + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/ContactItem.java b/src/net/java/sip/communicator/impl/gui/main/ContactItem.java new file mode 100755 index 0000000..598871a --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/ContactItem.java @@ -0,0 +1,54 @@ +package net.java.sip.communicator.impl.gui.main; + +import java.awt.Image; + +/** + * @author Yana Stamcheva + * + * The contact. + * TODO: To be removed when the contact list service is ready. + */ + +public class ContactItem { + + private String nickname; + private Image photo; + private String[] protocolList; + private String status; + + public ContactItem(String nickname){ + this.nickname = nickname; + } + + public String getNickName() { + return nickname; + } + + public void setNickName(String nickname) { + this.nickname = nickname; + } + + public Image getPhoto() { + return photo; + } + + public void setPhoto(Image photo) { + this.photo = photo; + } + + public String[] getProtocolList() { + return protocolList; + } + + public void setProtocolList(String[] protocolList) { + this.protocolList = protocolList; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/ContactList.java b/src/net/java/sip/communicator/impl/gui/main/ContactList.java new file mode 100644 index 0000000..70d92e3 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/ContactList.java @@ -0,0 +1,23 @@ +package net.java.sip.communicator.impl.gui.main; + +import java.util.Vector; + +/** + * @author Yana Stamcheva + * + * The contact list. + * TODO: to be removed when the contact list service is ready. + */ + +public class ContactList { + + private Vector contacts = new Vector(); + + public Vector getAllContacts(){ + return contacts; + } + + public void addContact(ContactItem contactItem){ + contacts.add(contactItem); + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/ContactListPanel.java b/src/net/java/sip/communicator/impl/gui/main/ContactListPanel.java new file mode 100755 index 0000000..5d3a37e --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/ContactListPanel.java @@ -0,0 +1,81 @@ +package net.java.sip.communicator.impl.gui.main; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Vector; + +import javax.swing.BoxLayout; +import javax.swing.JPanel; +import javax.swing.JScrollPane; + +/** + * @author Yana Stamcheva + * + * The ContactListPanel contains the contact list. + */ +public class ContactListPanel extends JScrollPane{ + + private ContactList clist; + private JPanel mainPanel = new JPanel(); + private JPanel contactsPanel = new JPanel(); + + public ContactListPanel(ContactList clist){ + + this.mainPanel.setLayout(new BorderLayout()); + this.contactsPanel.setLayout(new BoxLayout(this.contactsPanel, BoxLayout.Y_AXIS)); + + this.clist = clist; + this.init(); + } + + private void init(){ + + for (int i = 0; i < this.clist.getAllContacts().size(); i ++){ + + ContactPanel cpanel = new ContactPanel((ContactItem)this.clist.getAllContacts().get(i)); + cpanel.setPreferredSize(new Dimension(LookAndFeelConstants.CONTACTPANEL_WIDTH, LookAndFeelConstants.CONTACTPANEL_HEIGHT)); + + cpanel.addMouseListener(new MouseAdapter(){ + public void mouseEntered(MouseEvent e){ + ContactPanel cpanel = (ContactPanel)e.getSource(); + + cpanel.setMouseOver(true); + cpanel.repaint(); + } + + public void mouseExited(MouseEvent e){ + ContactPanel cpanel = (ContactPanel)e.getSource(); + + cpanel.setMouseOver(false); + cpanel.repaint(); + } + + public void mouseClicked(MouseEvent e){ + ContactPanel cpanel = (ContactPanel)e.getSource(); + + cpanel.setSelected(true); + refreshContactsStatus(cpanel); + } + }); + + this.contactsPanel.add(cpanel); + } + + this.mainPanel.add(contactsPanel, BorderLayout.NORTH); + this.getViewport().add(mainPanel); + } + + public void refreshContactsStatus(ContactPanel cpanelSelected){ + + for (int i = 0; i < this.contactsPanel.getComponentCount(); i ++){ + ContactPanel cpanel = (ContactPanel)this.contactsPanel.getComponent(i); + + if(!cpanel.equals(cpanelSelected)){ + cpanel.setSelected(false); + } + } + } + +} diff --git a/src/net/java/sip/communicator/impl/gui/main/ContactListTree.java b/src/net/java/sip/communicator/impl/gui/main/ContactListTree.java new file mode 100644 index 0000000..1c037e3 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/ContactListTree.java @@ -0,0 +1,7 @@ +package net.java.sip.communicator.impl.gui.main; + +import javax.swing.JTree; + +public class ContactListTree extends JTree { + +} diff --git a/src/net/java/sip/communicator/impl/gui/main/ContactPanel.java b/src/net/java/sip/communicator/impl/gui/main/ContactPanel.java new file mode 100755 index 0000000..70b5b1d --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/ContactPanel.java @@ -0,0 +1,138 @@ +package net.java.sip.communicator.impl.gui.main; + +import java.awt.BorderLayout; +import java.awt.Font; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Toolkit; + +import javax.swing.JLabel; +import javax.swing.JPanel; + +/** + * @author Yana Stamcheva + * + * The ContactPanel contains the contact item. + */ + +public class ContactPanel extends JPanel { + + private ContactItem contactItem; + + private boolean isMouseOver = false; + private boolean isSelected = false; + + private JLabel nicknameLabel = new JLabel(); + + public ContactPanel(ContactItem contactItem){ + super(new BorderLayout()); + + this.contactItem = contactItem; + + this.init(); + } + + private void init(){ + + nicknameLabel.setFont(this.getFont().deriveFont(Font.BOLD)); + + this.setUserData(); + + this.add(nicknameLabel, BorderLayout.WEST); + + } + + public void setUserData(){ + nicknameLabel.setText(this.contactItem.getNickName()); + } + + public void paintComponent(Graphics g){ + super.paintComponent(g); + + Graphics2D g2 = (Graphics2D)g; + + if(this.isSelected()){ + GradientPaint p = new GradientPaint(this.getWidth()/2, + 0, + LookAndFeelConstants.CONTACTPANEL_SELECTED_START_COLOR, + this.getWidth()/2, + LookAndFeelConstants.CONTACTPANEL_SELECTED_GRADIENT_SIZE, + LookAndFeelConstants.CONTACTPANEL_SELECTED_END_COLOR); + + GradientPaint p1 = new GradientPaint( this.getWidth()/2, + this.getHeight() - LookAndFeelConstants.CONTACTPANEL_SELECTED_GRADIENT_SIZE, + LookAndFeelConstants.CONTACTPANEL_SELECTED_END_COLOR, + this.getWidth()/2, + this.getHeight(), + LookAndFeelConstants.CONTACTPANEL_SELECTED_START_COLOR); + + g2.setPaint(p); + g2.fillRect(0, 0, this.getWidth(), LookAndFeelConstants.CONTACTPANEL_SELECTED_GRADIENT_SIZE); + + g2.setColor(LookAndFeelConstants.CONTACTPANEL_SELECTED_END_COLOR); + g2.fillRect(0, + LookAndFeelConstants.CONTACTPANEL_SELECTED_GRADIENT_SIZE, + this.getWidth(), + this.getHeight() - LookAndFeelConstants.CONTACTPANEL_SELECTED_GRADIENT_SIZE); + + g2.setPaint(p1); + g2.fillRect(0, this.getHeight() - LookAndFeelConstants.CONTACTPANEL_SELECTED_GRADIENT_SIZE, this.getWidth(), this.getHeight() - 1); + } + else if(this.isMouseOver()){ + GradientPaint p = new GradientPaint(this.getWidth()/2, + 0, + LookAndFeelConstants.CONTACTPANEL_MOVER_START_COLOR, + this.getWidth()/2, + LookAndFeelConstants.CONTACTPANEL_GRADIENT_SIZE, + LookAndFeelConstants.CONTACTPANEL_MOVER_END_COLOR); + + GradientPaint p1 = new GradientPaint( this.getWidth()/2, + this.getHeight() - LookAndFeelConstants.CONTACTPANEL_GRADIENT_SIZE, + LookAndFeelConstants.CONTACTPANEL_MOVER_END_COLOR, + this.getWidth()/2, + this.getHeight(), + LookAndFeelConstants.CONTACTPANEL_MOVER_START_COLOR); + + g2.setPaint(p); + g2.fillRect(0, 0, this.getWidth(), LookAndFeelConstants.CONTACTPANEL_GRADIENT_SIZE); + + g2.setColor(LookAndFeelConstants.CONTACTPANEL_MOVER_END_COLOR); + g2.fillRect(0, + LookAndFeelConstants.CONTACTPANEL_GRADIENT_SIZE, + this.getWidth(), + this.getHeight() - LookAndFeelConstants.CONTACTPANEL_GRADIENT_SIZE); + + g2.setPaint(p1); + g2.fillRect(0, this.getHeight() - LookAndFeelConstants.CONTACTPANEL_GRADIENT_SIZE - 1, this.getWidth(), this.getHeight() - 1); + } + + g2.setColor(LookAndFeelConstants.CONTACTPANEL_LINES_COLOR); + g2.drawLine(0, this.getHeight() - 1, this.getWidth(), this.getHeight() - 1); + } + + public boolean isMouseOver() { + return isMouseOver; + } + + public void setMouseOver(boolean isMouseOver) { + this.isMouseOver = isMouseOver; + } + + public boolean isSelected() { + return isSelected; + } + + public void setSelected(boolean isSelected) { + if(this.isSelected != isSelected) { + this.isSelected = isSelected; + /*if(isSelected) { + this.setSize(new Dimension(this.getWidth(), LookAndFeelConstants.CONTACTPANEL_SELECTED_HEIGHT)); + } else { + this.setSize(new Dimension(this.getWidth(), LookAndFeelConstants.CONTACTPANEL_HEIGHT)); + }*/ + this.repaint(); + } + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/DialPanel.java b/src/net/java/sip/communicator/impl/gui/main/DialPanel.java new file mode 100755 index 0000000..ce45b0e --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/DialPanel.java @@ -0,0 +1,71 @@ +package net.java.sip.communicator.impl.gui.main; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.GridLayout; + +import javax.swing.JButton; +import javax.swing.JPanel; + +/** + * @author Yana Stamcheva + * + * The DialPanel contains the dial buttons. + */ + +public class DialPanel extends JPanel { + private Font buttonTextFont = new Font("Verdana", Font.BOLD, 12); + + private JButton oneButton = new JButton("1"); + private JButton twoButton = new JButton("2"); + private JButton threeButton = new JButton("3"); + private JButton fourButton = new JButton("4"); + private JButton fiveButton = new JButton("5"); + private JButton sixButton = new JButton("6"); + private JButton sevenButton = new JButton("7"); + private JButton eightButton = new JButton("8"); + private JButton nineButton = new JButton("9"); + private JButton starButton = new JButton("*"); + private JButton zeroButton = new JButton("0+"); + private JButton diezButton = new JButton("#"); + + private JPanel dialPadPanel = new JPanel(new GridLayout(4, 3, 5, 5)); + + public DialPanel(){ + super(new FlowLayout(FlowLayout.CENTER)); + + this.init(); + } + + public void init(){ + + oneButton.setFont(this.buttonTextFont); + twoButton.setFont(this.buttonTextFont); + threeButton.setFont(this.buttonTextFont); + fourButton.setFont(this.buttonTextFont); + fiveButton.setFont(this.buttonTextFont); + sixButton.setFont(this.buttonTextFont); + sevenButton.setFont(this.buttonTextFont); + eightButton.setFont(this.buttonTextFont); + nineButton.setFont(this.buttonTextFont); + zeroButton.setFont(this.buttonTextFont); + diezButton.setFont(this.buttonTextFont); + starButton.setFont(this.buttonTextFont); + + dialPadPanel.add(oneButton); + dialPadPanel.add(twoButton); + dialPadPanel.add(threeButton); + dialPadPanel.add(fourButton); + dialPadPanel.add(fiveButton); + dialPadPanel.add(sixButton); + dialPadPanel.add(sevenButton); + dialPadPanel.add(eightButton); + dialPadPanel.add(nineButton); + dialPadPanel.add(starButton); + dialPadPanel.add(zeroButton); + dialPadPanel.add(diezButton); + + this.add(dialPadPanel, BorderLayout.CENTER); + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/LookAndFeelConstants.java b/src/net/java/sip/communicator/impl/gui/main/LookAndFeelConstants.java new file mode 100755 index 0000000..97f10ea --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/LookAndFeelConstants.java @@ -0,0 +1,284 @@ +package net.java.sip.communicator.impl.gui.main; + +import java.awt.Color; +import java.awt.Image; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Vector; + +import javax.imageio.ImageIO; +import javax.swing.ImageIcon; + +import net.java.sip.communicator.impl.gui.main.customcontrols.StatusIcon; +import net.java.sip.communicator.util.Logger; + +/** + * @author Yana Stamcheva + * + * All look and feel related constants are stored here. + */ + +public class LookAndFeelConstants { + private static Logger log = Logger.getLogger(LookAndFeelConstants.class); + + /*======================================================================== + * ------------------------ SIZE CONSTANTS -------------------------------- + ========================================================================*/ + + public static final int MAINFRAME_HEIGHT = 200; + + public static final int MAINFRAME_WIDTH = 30; + + public static final int CONTACTPANEL_HEIGHT = 25; + + public static final int CONTACTPANEL_WIDTH = 10; + + public static final int CONTACTPANEL_SELECTED_HEIGHT = 50; + + public static final int CONTACTPANEL_SELECTED_GRADIENT_SIZE = 10; + + public static final int CONTACTPANEL_GRADIENT_SIZE = 10; + + + /*======================================================================== + * ------------------------ COLOR CONSTANTS ------------------------------- + ========================================================================*/ + + public static final Color CONTACTPANEL_SELECTED_START_COLOR = + new Color(166, 207, 239); + + public static final Color CONTACTPANEL_SELECTED_END_COLOR = + new Color(255, 255, 255); + + public static final Color CONTACTPANEL_MOVER_START_COLOR = + new Color(210, 210, 210); + + // public static final Color CONTACTPANEL_MOVER_START_COLOR = + // new Color(244, 235, 143); + + public static final Color CONTACTPANEL_MOVER_END_COLOR = + new Color(255, 255, 255); + + public static final Color CONTACTPANEL_LINES_COLOR = + new Color(154, 154, 154); + + + /*========================================================================= + * ------------------------------ ICONS ---------------------------------- + ========================================================================*/ + + public static final Image QUICK_MENU_ADD_ICON = LookAndFeelConstants + .loadImage("../resources/buttons/addContactIcon.png"); + + public static final Image QUICK_MENU_CONFIGURE_ICON = LookAndFeelConstants + .loadImage("../resources/buttons/configureIcon.png"); + + public static final Image QUICK_MENU_BUTTON_BG = LookAndFeelConstants + .loadImage("../resources/buttons/quickMenuButtonBg.gif"); + + public static final Image QUICK_MENU_BUTTON_ROLLOVER_BG = LookAndFeelConstants + .loadImage("../resources/buttons/quickMenuButtonRolloverBg.gif"); + + public static final Image CALL_BUTTON_ICON = LookAndFeelConstants + .loadImage("../resources/buttons/callIcon.png"); + + public static final Image HANG_UP_BUTTON_ICON = LookAndFeelConstants + .loadImage("../resources/buttons/hangupIcon.png"); + + public static final Image CALL_BUTTON_BG = LookAndFeelConstants + .loadImage("../resources/buttons/call.gif"); + + public static final Image HANGUP_BUTTON_BG = LookAndFeelConstants + .loadImage("../resources/buttons/hangUp.gif"); + + public static final Image CALL_ROLLOVER_BUTTON_BG = LookAndFeelConstants + .loadImage("../resources/buttons/callRollover.gif"); + + public static final Image HANGUP_ROLLOVER_BUTTON_BG = LookAndFeelConstants + .loadImage("../resources/buttons/hangUpRollover.gif"); + + public static final Image STATUS_SELECTOR_BOX = LookAndFeelConstants + .loadImage("../resources/buttons/combobox.png"); + + + /*========================================================================= + * ------------------------ STATUS LABELS --------------------------------- + ========================================================================*/ + + public static final String ONLINE_STATUS = "Online"; + + public static final String OFFLINE_STATUS = "Offline"; + + public static final String OCCUPIED_STATUS = "Occupied"; + + public static final String CHAT_STATUS = "Free for chat"; + + public static final String AWAY_STATUS = "Away"; + + public static final String NA_STATUS = "Not available"; + + public static final String INVISIBLE_STATUS = "Invisible"; + + public static final String DND_STATUS = "Do not disturb"; + + /*========================================================================= + * ------------------------ PROTOCOL NAMES -------------------------------- + ========================================================================*/ + + public static final String ICQ = "ICQ"; + + public static final String MSN = "MSN"; + + public static final String AIM = "AIM"; + + public static final String YAHOO = "Yahoo"; + + public static final String JABBER = "Jabber"; + + public static final String SKYPE = "Skype"; + + /*========================================================================= + * --------------------- PROTOCOLS STATUS ICONS --------------------------- + ========================================================================*/ + + public static final Image ICQ_LOGO = LookAndFeelConstants + .loadImage("../resources/protocols/icq/Icq16.png"); + + public static final Image ICQ_FF_CHAT_ICON = LookAndFeelConstants + .loadImage("../resources/protocols/icq/cr16-action-icq_ffc.png"); + + public static final Image ICQ_AWAY_ICON = LookAndFeelConstants + .loadImage("../resources/protocols/icq/cr16-action-icq_away.png"); + + public static final Image ICQ_NA_ICON = LookAndFeelConstants + .loadImage("../resources/protocols/icq/cr16-action-icq_na.png"); + + public static final Image ICQ_DND_ICON = LookAndFeelConstants + .loadImage("../resources/protocols/icq/cr16-action-icq_dnd.png"); + + public static final Image ICQ_OCCUPIED_ICON = LookAndFeelConstants + .loadImage("../resources/protocols/icq/cr16-action-icq_occupied.png"); + + public static final Image ICQ_OFFLINE_ICON = LookAndFeelConstants + .loadImage("../resources/protocols/icq/cr16-action-icq_offline.png"); + + public static final Image ICQ_INVISIBLE_ICON = LookAndFeelConstants + .loadImage("../resources/protocols/icq/cr16-action-icq_invisible.png"); + + public static final Image MSN_LOGO = LookAndFeelConstants + .loadImage("../resources/protocols/msn/Msn16.png"); + + public static final Image AIM_LOGO = LookAndFeelConstants + .loadImage("../resources/protocols/aim/Aim16.png"); + + public static final Image YAHOO_LOGO = LookAndFeelConstants + .loadImage("../resources/protocols/yahoo/Yahoo16.png"); + + public static final Image JABBER_LOGO = LookAndFeelConstants + .loadImage("../resources/protocols/jabber/Jabber16.png"); + + public static final Image SKYPE_LOGO = LookAndFeelConstants + .loadImage("../resources/protocols/skype/Skype16.png"); + + + /** + * Gets all protocol statuses, including status and text. + * + * @param protocolName + * @return an ArrayList of all status Icons for the given protocol. + */ + + public static ArrayList getProtocolIcons (String protocolName) { + ArrayList protocolStatusList = new ArrayList (); + + if (protocolName.equals (LookAndFeelConstants.ICQ)) { + + protocolStatusList.add (new Status(ONLINE_STATUS, + new StatusIcon (LookAndFeelConstants.ICQ_LOGO))); + + protocolStatusList.add (new Status(CHAT_STATUS, + new StatusIcon (LookAndFeelConstants.ICQ_LOGO, + LookAndFeelConstants.ICQ_FF_CHAT_ICON))); + + protocolStatusList.add(new Status(AWAY_STATUS, + new StatusIcon (LookAndFeelConstants.ICQ_LOGO, + LookAndFeelConstants.ICQ_AWAY_ICON))); + + protocolStatusList.add(new Status(NA_STATUS, + new StatusIcon (LookAndFeelConstants.ICQ_LOGO, + LookAndFeelConstants.ICQ_NA_ICON))); + + protocolStatusList.add(new Status(DND_STATUS, + new StatusIcon (LookAndFeelConstants.ICQ_LOGO, + LookAndFeelConstants.ICQ_DND_ICON))); + + protocolStatusList.add(new Status(OCCUPIED_STATUS, + new StatusIcon (LookAndFeelConstants.ICQ_LOGO, + LookAndFeelConstants.ICQ_OCCUPIED_ICON))); + + protocolStatusList.add(new Status(OFFLINE_STATUS, + new StatusIcon (LookAndFeelConstants.ICQ_OFFLINE_ICON))); + + protocolStatusList.add(new Status(INVISIBLE_STATUS, + new StatusIcon (LookAndFeelConstants.ICQ_INVISIBLE_ICON))); + + } else if (protocolName.equals (LookAndFeelConstants.MSN)) { + + protocolStatusList.add (new Status(ONLINE_STATUS, + new StatusIcon (LookAndFeelConstants.MSN_LOGO))); + + } else if (protocolName.equals (LookAndFeelConstants.AIM)) { + + protocolStatusList.add (new Status(ONLINE_STATUS, + new StatusIcon (LookAndFeelConstants.AIM_LOGO))); + + } else if (protocolName.equals (LookAndFeelConstants.YAHOO)) { + + protocolStatusList.add (new Status(ONLINE_STATUS, + new StatusIcon (LookAndFeelConstants.YAHOO_LOGO))); + + } else if (protocolName.equals (LookAndFeelConstants.JABBER)) { + + protocolStatusList.add (new Status(ONLINE_STATUS, + new StatusIcon (LookAndFeelConstants.JABBER_LOGO))); + + } else if (protocolName.equals (LookAndFeelConstants.SKYPE)) { + + protocolStatusList.add (new Status(ONLINE_STATUS, + new StatusIcon (LookAndFeelConstants.SKYPE_LOGO))); + } + + return protocolStatusList; + } + + /** + * Loads an image from a given path. + */ + + private static Image loadImage(String path) { + Image image = null; + + try { + log.logEntry(); + + if (log.isTraceEnabled()) { + log.trace("Loading image : " + path + "..."); + } + + image = ImageIO.read(LookAndFeelConstants.class.getResource(path)); + + if (log.isTraceEnabled()) { + log.trace("Loading image : " + path + "... [ DONE ]"); + } + + } catch (IOException e) { + log.error("Failed to load image:" + path, e); + } finally { + log.logExit(); + } + + return image; + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/MainFrame.java b/src/net/java/sip/communicator/impl/gui/main/MainFrame.java new file mode 100755 index 0000000..2462764 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/MainFrame.java @@ -0,0 +1,58 @@ +package net.java.sip.communicator.impl.gui.main; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Toolkit; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +/** + * @author Yana Stamcheva + * + * The MainFrame of the application. + */ +public class MainFrame extends JFrame{ + + private JPanel mainPanel = new JPanel(new BorderLayout()); + private JPanel menusPanel = new JPanel(new BorderLayout()); + private Menu menu = new Menu(); + private QuickMenu quickMenu = new QuickMenu(); + private CallPanel callPanel = new CallPanel(); + private StatusPanel statusPanel; + private MainTabbedPane tabbedPane; + + + public MainFrame(ContactList clist, User user){ + tabbedPane = new MainTabbedPane(clist); + statusPanel = new StatusPanel(user.getProtocols()); + + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + this.setInitialBounds(); +// this.setIconImage(); + + this.init(); + } + + private void init(){ + this.menusPanel.add(menu, BorderLayout.NORTH); + this.menusPanel.add(quickMenu, BorderLayout.CENTER); + + this.mainPanel.add(tabbedPane, BorderLayout.CENTER); + this.mainPanel.add(callPanel, BorderLayout.SOUTH); + + this.getContentPane().add(menusPanel, BorderLayout.NORTH); + this.getContentPane().add(mainPanel, BorderLayout.CENTER); + this.getContentPane().add(statusPanel, BorderLayout.SOUTH); + } + + private void setInitialBounds(){ + this.setLocation(Toolkit.getDefaultToolkit().getScreenSize().width - MainFrame.WIDTH, 50); + + this.getContentPane().setSize( LookAndFeelConstants.MAINFRAME_WIDTH, + LookAndFeelConstants.MAINFRAME_HEIGHT); + + this.tabbedPane.setPreferredSize(new Dimension(LookAndFeelConstants.MAINFRAME_WIDTH, + LookAndFeelConstants.MAINFRAME_HEIGHT)); + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/MainTabbedPane.java b/src/net/java/sip/communicator/impl/gui/main/MainTabbedPane.java new file mode 100755 index 0000000..32fbc75 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/MainTabbedPane.java @@ -0,0 +1,30 @@ +package net.java.sip.communicator.impl.gui.main; + +import java.util.Vector; + +import javax.swing.JButton; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.ListModel; +import javax.swing.event.ListDataListener; + +/** + * @author Yana Stamcheva + * + * The main tabbed pane containing the contact list panel, the + * call list panel and the dial panel. + */ +public class MainTabbedPane extends JTabbedPane { + + private DialPanel dialPanel = new DialPanel(); + + public MainTabbedPane(ContactList clist){ + + ContactListPanel contactList = new ContactListPanel(clist); + + this.addTab("Contacts", contactList); + this.addTab("Call list", new JPanel()); + this.addTab("Dial", dialPanel); + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/Menu.java b/src/net/java/sip/communicator/impl/gui/main/Menu.java new file mode 100755 index 0000000..7ca520a --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/Menu.java @@ -0,0 +1,44 @@ +package net.java.sip.communicator.impl.gui.main; + +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JPanel; + +/** + * @author Yana Stamcheva + * + * The main menu. + */ +public class Menu extends JMenuBar { + private JMenu userMenu = new JMenu(); + private JMenu toolsMenu = new JMenu(); + private JMenu viewMenu = new JMenu(); + private JMenu helpMenu = new JMenu(); + + public Menu(){ + this.init(); + } + + private void init(){ + userMenu.setText("User"); + userMenu.setMnemonic('U'); + userMenu.setToolTipText("User"); + + toolsMenu.setText("Tools"); + toolsMenu.setMnemonic('T'); + toolsMenu.setToolTipText("Tools"); + + viewMenu.setText("View"); + viewMenu.setMnemonic('V'); + viewMenu.setToolTipText("View"); + + helpMenu.setText("Help"); + helpMenu.setMnemonic('H'); + helpMenu.setToolTipText("Help"); + + this.add(userMenu); + this.add(toolsMenu); + this.add(viewMenu); + this.add(helpMenu); + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/QuickMenu.java b/src/net/java/sip/communicator/impl/gui/main/QuickMenu.java new file mode 100755 index 0000000..33c91d4 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/QuickMenu.java @@ -0,0 +1,69 @@ +package net.java.sip.communicator.impl.gui.main; + +import java.awt.Dimension; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.RenderingHints; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JToolBar; + +import net.java.sip.communicator.impl.gui.main.customcontrols.SIPCommButton; +/** + * @author Yana Stamcheva + * + * The quick menu. + */ +public class QuickMenu extends JToolBar{ + + private Image addButtonIcon = LookAndFeelConstants.QUICK_MENU_ADD_ICON; + private Image configureButtonIcon = LookAndFeelConstants.QUICK_MENU_CONFIGURE_ICON; + + SIPCommButton infoButton; + SIPCommButton toolsButton; + SIPCommButton addButton; + SIPCommButton searchButton; + + public QuickMenu(){ + + this.setRollover(true); + this.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + infoButton = new SIPCommButton(); + toolsButton = new SIPCommButton(configureButtonIcon); + searchButton = new SIPCommButton(); + addButton = new SIPCommButton(addButtonIcon); + + this.init(); + } + + private void init() { + this.add(addButton); + this.add(toolsButton); + this.add(infoButton); + this.add(searchButton); + } + + public void paint(Graphics g){ + super.paint(g); + + Graphics2D g2 = (Graphics2D)g; + + GradientPaint p = new GradientPaint(this.getWidth()/2, + 0, + LookAndFeelConstants.CONTACTPANEL_SELECTED_START_COLOR, + this.getWidth()/2, + LookAndFeelConstants.CONTACTPANEL_SELECTED_GRADIENT_SIZE, + LookAndFeelConstants.CONTACTPANEL_SELECTED_END_COLOR); + + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + g2.setPaint(p); + + //g2.dra + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/Status.java b/src/net/java/sip/communicator/impl/gui/main/Status.java new file mode 100644 index 0000000..2928239 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/Status.java @@ -0,0 +1,29 @@ +package net.java.sip.communicator.impl.gui.main; + +import java.awt.Image; + +import javax.swing.ImageIcon; + +public class Status { + + private String text; + private Image icon; + + public Status(String text, Image icon){ + this.text = text; + this.icon = icon; + } + + public Image getIcon () { + return icon; + } + public void setIcon (Image icon) { + this.icon = icon; + } + public String getText () { + return text; + } + public void setText (String text) { + this.text = text; + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/StatusPanel.java b/src/net/java/sip/communicator/impl/gui/main/StatusPanel.java new file mode 100644 index 0000000..be2f5e3 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/StatusPanel.java @@ -0,0 +1,40 @@ +package net.java.sip.communicator.impl.gui.main; + +import java.awt.FlowLayout; +import java.util.ArrayList; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; + +import net.java.sip.communicator.impl.gui.main.customcontrols.SIPCommButton; +import net.java.sip.communicator.impl.gui.main.customcontrols.StatusSelectorBox; + +public class StatusPanel extends JPanel { + + private String[] userProtocols; + + public StatusPanel(String[] userProtocols) { + + this.setLayout(new FlowLayout(FlowLayout.LEFT)); + this.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, + LookAndFeelConstants.CONTACTPANEL_MOVER_START_COLOR)); + + this.userProtocols = userProtocols; + + this.init(); + } + + private void init() { + + for (int i = 0; i < userProtocols.length; i++) { + + ArrayList protocolStatusList = LookAndFeelConstants + .getProtocolIcons(userProtocols[i]); + + StatusSelectorBox protocolStatusCombo = new StatusSelectorBox( + protocolStatusList.toArray(), (Status)protocolStatusList.get(0)); + + this.add(protocolStatusCombo); + } + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/gui/main/User.java b/src/net/java/sip/communicator/impl/gui/main/User.java new file mode 100644 index 0000000..8510d6e --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/User.java @@ -0,0 +1,13 @@ +package net.java.sip.communicator.impl.gui.main; + +public class User { + private String[] userProtocols; + + public void setProtocols(String[] userProtocols){ + this.userProtocols = userProtocols; + } + + public String[] getProtocols(){ + return this.userProtocols; + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/customcontrols/SIPCommButton.java b/src/net/java/sip/communicator/impl/gui/main/customcontrols/SIPCommButton.java new file mode 100755 index 0000000..742c614 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/customcontrols/SIPCommButton.java @@ -0,0 +1,125 @@ +package net.java.sip.communicator.impl.gui.main.customcontrols; + +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.image.BufferedImage; + +import javax.swing.ImageIcon; +import javax.swing.JButton; + +import net.java.sip.communicator.impl.gui.main.LookAndFeelConstants; + +/** + * @author Yana Stamcheva + * + * The quick menu is composed of special buttons, which are specified here. + */ +public class SIPCommButton extends JButton { + + private Image bgImage; + + private Image bgRolloverImage; + + private Image iconImage; + + public SIPCommButton() { + super(); + + this.bgImage = LookAndFeelConstants.QUICK_MENU_BUTTON_BG; + this.bgRolloverImage = LookAndFeelConstants.QUICK_MENU_BUTTON_ROLLOVER_BG; + this.setIcon(new ImageIcon(this.bgImage)); + + this.setPreferredSize(new Dimension(this.bgImage.getWidth(null), + this.bgImage.getHeight(null))); + } + + public SIPCommButton(String text) { + super(text); + + this.bgImage = LookAndFeelConstants.QUICK_MENU_BUTTON_BG; + this.bgRolloverImage = LookAndFeelConstants.QUICK_MENU_BUTTON_ROLLOVER_BG; + + this.setPreferredSize(new Dimension(this.bgImage.getWidth(null), + this.bgImage.getHeight(null))); + } + + public SIPCommButton(Image iconImage) { + super(); + + this.iconImage = iconImage; + this.bgImage = LookAndFeelConstants.QUICK_MENU_BUTTON_BG; + this.bgRolloverImage = LookAndFeelConstants.QUICK_MENU_BUTTON_ROLLOVER_BG; + + this.setPreferredSize(new Dimension(this.bgImage.getWidth(null), + this.bgImage.getHeight(null))); + + this.setIcon(new ImageIcon(this.bgImage)); + } + + public SIPCommButton(Image bgImage, Image rolloverImage, Image iconImage) { + super(); + + this.iconImage = iconImage; + this.bgImage = bgImage; + this.bgRolloverImage = rolloverImage; + + this.setPreferredSize(new Dimension(this.bgImage.getWidth(null), + this.bgImage.getHeight(null))); + + this.setIcon(new ImageIcon(this.bgImage)); + } + + public void paint(Graphics g) { + + g.drawImage(this.bgImage, 0, 0, this); + + if (this.iconImage != null) { + + g.drawImage(this.iconImage, + (this.bgImage.getWidth(null) - + this.iconImage.getWidth(null)) / 2, + (this.bgImage.getHeight(null) - + this.iconImage.getHeight(null)) / 2, this); + } + + if (this.getModel().isRollover()) { + + g.setColor(LookAndFeelConstants.CONTACTPANEL_LINES_COLOR); + g.drawImage(this.bgRolloverImage, 0, 0, this); + + if (this.iconImage != null) { + + g.drawImage(this.iconImage, + (this.bgImage.getWidth(null) - + this.iconImage.getWidth(null)) / 2, + (this.bgImage.getHeight(null) - + this.iconImage.getHeight(null)) / 2, this); + } + } + } + + public Image getBgImage() { + return bgImage; + } + + public void setBgImage(Image bgImage) { + this.bgImage = bgImage; + } + + public Image getBgRolloverImage() { + return bgRolloverImage; + } + + public void setBgRolloverImage(Image bgRolloverImage) { + this.bgRolloverImage = bgRolloverImage; + } + + public Image getIconImage() { + return iconImage; + } + + public void setIconImage(Image iconImage) { + this.iconImage = iconImage; + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/customcontrols/StatusIcon.java b/src/net/java/sip/communicator/impl/gui/main/customcontrols/StatusIcon.java new file mode 100644 index 0000000..4445918 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/customcontrols/StatusIcon.java @@ -0,0 +1,40 @@ +package net.java.sip.communicator.impl.gui.main.customcontrols; + +import java.awt.Image; +import java.awt.image.BufferedImage; + +public class StatusIcon extends BufferedImage { + + private Image bgImage; + private Image iconImage; + + + public StatusIcon (Image bgImage, Image iconImage) { + super( bgImage.getWidth(null), + bgImage.getHeight(null), + BufferedImage.TYPE_4BYTE_ABGR); + + this.bgImage = bgImage; + this.iconImage = iconImage; + + this.getGraphics().drawImage (this.bgImage, 0, 0, null); + + if (this.iconImage != null) + this.getGraphics().drawImage (this.iconImage, + (this.bgImage.getWidth(null) - + this.iconImage.getWidth(null)) / 2, + (this.bgImage.getHeight(null) - + this.iconImage.getHeight(null)) / 2, null); + + } + + public StatusIcon (Image image) { + super( image.getWidth(null), + image.getHeight(null), + BufferedImage.TYPE_4BYTE_ABGR); + + this.bgImage = image; + + this.getGraphics().drawImage (this.bgImage, 0, 0, null); + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/gui/main/customcontrols/StatusSelectorBox.java b/src/net/java/sip/communicator/impl/gui/main/customcontrols/StatusSelectorBox.java new file mode 100644 index 0000000..5a45e22 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/customcontrols/StatusSelectorBox.java @@ -0,0 +1,117 @@ +package net.java.sip.communicator.impl.gui.main.customcontrols; + +import java.awt.Component; +import java.awt.Image; +import java.awt.MenuItem; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; + +import net.java.sip.communicator.impl.gui.main.LookAndFeelConstants; +import net.java.sip.communicator.impl.gui.main.Status; + +public class StatusSelectorBox extends SIPCommButton + implements ActionListener{ + + private SIPCommButton button; + + private JPopupMenu popup; + + private Object[] items; + + private final Image statusSelectorBg = LookAndFeelConstants.STATUS_SELECTOR_BOX; + + private final int width = statusSelectorBg.getWidth(null); + + private final int height = statusSelectorBg.getHeight(null); + + private final int iconX = (this.width - 24) / 2; + + private final int iconY = (this.height - 16) / 2; + + public StatusSelectorBox(Object[] items, Status currentStatus) { + + super( LookAndFeelConstants.STATUS_SELECTOR_BOX, + LookAndFeelConstants.STATUS_SELECTOR_BOX, + currentStatus.getIcon()); + + this.popup = new JPopupMenu(); + + this.items = items; + + this.init(); + } + + public void init() { + + for (int i = 0; i < items.length; i++) { + + if (items[i] instanceof Status) { + + Status status = (Status) items[i]; + JMenuItem item = new JMenuItem( status.getText(), + new ImageIcon(status.getIcon())); + + item.addActionListener(this); + + this.popup.add(item); + } + } + + this.popup.setInvoker(this); + this.addActionListener(this); + + } + + public void actionPerformed (ActionEvent e) { + + if (e.getSource() instanceof SIPCommButton){ + + if (!this.popup.isVisible()) { + this.popup.setLocation(this.calculatePopupLocation()); + this.popup.setVisible(true); + } + } + else if (e.getSource() instanceof JMenuItem){ + + JMenuItem menuItem = (JMenuItem) e.getSource(); + + this.setIconImage(((ImageIcon)menuItem.getIcon()).getImage()); + } + } + + public Point calculatePopupLocation(){ + + Component component = this; + Point point = new Point(); + int x = this.getX(); + int y = this.getY(); + + while(component.getParent() != null){ + + component = component.getParent(); + + x += component.getX(); + y += component.getY(); + } + + point.x = x; + point.y = y + this.getHeight(); + + return point; + } +} diff --git a/src/net/java/sip/communicator/impl/gui/resources/buttons/addContactIcon.png b/src/net/java/sip/communicator/impl/gui/resources/buttons/addContactIcon.png Binary files differnew file mode 100644 index 0000000..2665b18 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/buttons/addContactIcon.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/buttons/call.gif b/src/net/java/sip/communicator/impl/gui/resources/buttons/call.gif Binary files differnew file mode 100644 index 0000000..4aa7477 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/buttons/call.gif diff --git a/src/net/java/sip/communicator/impl/gui/resources/buttons/callIcon.png b/src/net/java/sip/communicator/impl/gui/resources/buttons/callIcon.png Binary files differnew file mode 100644 index 0000000..6a0e972 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/buttons/callIcon.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/buttons/callRollover.gif b/src/net/java/sip/communicator/impl/gui/resources/buttons/callRollover.gif Binary files differnew file mode 100644 index 0000000..7d00a0b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/buttons/callRollover.gif diff --git a/src/net/java/sip/communicator/impl/gui/resources/buttons/combobox.png b/src/net/java/sip/communicator/impl/gui/resources/buttons/combobox.png Binary files differnew file mode 100644 index 0000000..c50d8a0 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/buttons/combobox.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/buttons/configureIcon.png b/src/net/java/sip/communicator/impl/gui/resources/buttons/configureIcon.png Binary files differnew file mode 100644 index 0000000..8706529 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/buttons/configureIcon.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/buttons/hangUp.gif b/src/net/java/sip/communicator/impl/gui/resources/buttons/hangUp.gif Binary files differnew file mode 100644 index 0000000..8e743d9 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/buttons/hangUp.gif diff --git a/src/net/java/sip/communicator/impl/gui/resources/buttons/hangUpRollover.gif b/src/net/java/sip/communicator/impl/gui/resources/buttons/hangUpRollover.gif Binary files differnew file mode 100644 index 0000000..25e88de --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/buttons/hangUpRollover.gif diff --git a/src/net/java/sip/communicator/impl/gui/resources/buttons/hangupIcon.png b/src/net/java/sip/communicator/impl/gui/resources/buttons/hangupIcon.png Binary files differnew file mode 100644 index 0000000..aa529ed --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/buttons/hangupIcon.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/buttons/quickMenuButtonBg.gif b/src/net/java/sip/communicator/impl/gui/resources/buttons/quickMenuButtonBg.gif Binary files differnew file mode 100755 index 0000000..0b59456 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/buttons/quickMenuButtonBg.gif diff --git a/src/net/java/sip/communicator/impl/gui/resources/buttons/quickMenuButtonRolloverBg.gif b/src/net/java/sip/communicator/impl/gui/resources/buttons/quickMenuButtonRolloverBg.gif Binary files differnew file mode 100644 index 0000000..11e2363 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/buttons/quickMenuButtonRolloverBg.gif diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/aim/Aim.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/aim/Aim.png Binary files differnew file mode 100644 index 0000000..64ebb6e --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/aim/Aim.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/aim/Aim16.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/aim/Aim16.png Binary files differnew file mode 100644 index 0000000..a06975c --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/aim/Aim16.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/Icq.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/Icq.png Binary files differnew file mode 100644 index 0000000..6a3d154 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/Icq.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/Icq16.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/Icq16.png Binary files differnew file mode 100644 index 0000000..900ee44 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/Icq16.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_away.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_away.png Binary files differnew file mode 100644 index 0000000..81dfb45 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_away.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_dnd.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_dnd.png Binary files differnew file mode 100644 index 0000000..cf94ee0 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_dnd.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_ffc.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_ffc.png Binary files differnew file mode 100644 index 0000000..51f5162 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_ffc.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_invisible.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_invisible.png Binary files differnew file mode 100644 index 0000000..c7e37ce --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_invisible.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_na.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_na.png Binary files differnew file mode 100644 index 0000000..b1aa91a --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_na.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_occupied.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_occupied.png Binary files differnew file mode 100644 index 0000000..d468996 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_occupied.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_offline.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_offline.png Binary files differnew file mode 100644 index 0000000..a9d1103 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_offline.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/jabber/Jabber16.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/jabber/Jabber16.png Binary files differnew file mode 100644 index 0000000..e8e6148 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/jabber/Jabber16.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/jabber/Jabber2.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/jabber/Jabber2.png Binary files differnew file mode 100644 index 0000000..592c385 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/jabber/Jabber2.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/msn/Msn.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/msn/Msn.png Binary files differnew file mode 100644 index 0000000..47b72d4 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/msn/Msn.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/msn/Msn16.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/msn/Msn16.png Binary files differnew file mode 100644 index 0000000..6e340db --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/msn/Msn16.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/msn/Msn2-16.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/msn/Msn2-16.png Binary files differnew file mode 100644 index 0000000..9328c95 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/msn/Msn2-16.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/skype/Skype.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/skype/Skype.png Binary files differnew file mode 100644 index 0000000..8c622df --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/skype/Skype.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/skype/Skype16.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/skype/Skype16.png Binary files differnew file mode 100644 index 0000000..19a72b5 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/skype/Skype16.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/yahoo/Yahoo.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/yahoo/Yahoo.png Binary files differnew file mode 100644 index 0000000..f2e7339 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/yahoo/Yahoo.png diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/yahoo/Yahoo16.png b/src/net/java/sip/communicator/impl/gui/resources/protocols/yahoo/Yahoo16.png Binary files differnew file mode 100644 index 0000000..29d34fb --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/yahoo/Yahoo16.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_down1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_down1.png Binary files differnew file mode 100644 index 0000000..58be1f3 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_down1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_down2.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_down2.png Binary files differnew file mode 100644 index 0000000..d4c3914 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_down2.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_down3.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_down3.png Binary files differnew file mode 100644 index 0000000..d4c3914 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_down3.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_left1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_left1.png Binary files differnew file mode 100644 index 0000000..3bdc634 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_left1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_left2.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_left2.png Binary files differnew file mode 100644 index 0000000..3bdc634 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_left2.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_left3.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_left3.png Binary files differnew file mode 100644 index 0000000..3bdc634 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_left3.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_right1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_right1.png Binary files differnew file mode 100644 index 0000000..9d536c2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_right1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_right2.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_right2.png Binary files differnew file mode 100644 index 0000000..9d536c2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_right2.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_right3.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_right3.png Binary files differnew file mode 100644 index 0000000..9d536c2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_right3.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_up1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_up1.png Binary files differnew file mode 100644 index 0000000..8600612 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_up1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_up2.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_up2.png Binary files differnew file mode 100644 index 0000000..8600612 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_up2.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_up3.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_up3.png Binary files differnew file mode 100644 index 0000000..8600612 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/arrow_up3.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/background.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/background.png Binary files differnew file mode 100644 index 0000000..c64d4b2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/background.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button0.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button0.png Binary files differnew file mode 100644 index 0000000..a881e6c --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button0.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button1.png Binary files differnew file mode 100644 index 0000000..1baa1d2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button2.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button2.png Binary files differnew file mode 100644 index 0000000..f5e718f --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button2.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button3.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button3.png Binary files differnew file mode 100644 index 0000000..d253786 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button3.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button4.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button4.png Binary files differnew file mode 100644 index 0000000..1baa1d2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button4.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button5.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button5.png Binary files differnew file mode 100644 index 0000000..c3cbfd4 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button5.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button6.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button6.png Binary files differnew file mode 100644 index 0000000..38bbb12 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button6.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button7.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button7.png Binary files differnew file mode 100644 index 0000000..1baa1d2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button7.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button8.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button8.png Binary files differnew file mode 100644 index 0000000..1baa1d2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button8.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button9.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button9.png Binary files differnew file mode 100644 index 0000000..d387e49 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/button9.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/check1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/check1.png Binary files differnew file mode 100644 index 0000000..8ee89b6 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/check1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/check2.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/check2.png Binary files differnew file mode 100644 index 0000000..3641af2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/check2.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/empty.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/empty.png Binary files differnew file mode 100644 index 0000000..6539ac0 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/empty.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/entry1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/entry1.png Binary files differnew file mode 100644 index 0000000..f91777b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/entry1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/entry2.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/entry2.png Binary files differnew file mode 100644 index 0000000..f91777b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/entry2.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/extension_bottom1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/extension_bottom1.png Binary files differnew file mode 100644 index 0000000..f91777b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/extension_bottom1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/extension_left1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/extension_left1.png Binary files differnew file mode 100644 index 0000000..f91777b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/extension_left1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/extension_right1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/extension_right1.png Binary files differnew file mode 100644 index 0000000..f91777b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/extension_right1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/extension_top1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/extension_top1.png Binary files differnew file mode 100644 index 0000000..f91777b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/extension_top1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/frame_gap_top_end.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/frame_gap_top_end.png Binary files differnew file mode 100644 index 0000000..4b6dcc2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/frame_gap_top_end.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/frame_gap_top_start.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/frame_gap_top_start.png Binary files differnew file mode 100644 index 0000000..73e4763 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/frame_gap_top_start.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_bottom.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_bottom.png Binary files differnew file mode 100644 index 0000000..f91777b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_bottom.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_bottom_end.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_bottom_end.png Binary files differnew file mode 100644 index 0000000..f91777b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_bottom_end.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_bottom_start.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_bottom_start.png Binary files differnew file mode 100644 index 0000000..f91777b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_bottom_start.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_left.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_left.png Binary files differnew file mode 100644 index 0000000..8b0ad9b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_left.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_left_end.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_left_end.png Binary files differnew file mode 100644 index 0000000..d17c8c3 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_left_end.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_left_start.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_left_start.png Binary files differnew file mode 100644 index 0000000..d63a811 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_left_start.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_right.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_right.png Binary files differnew file mode 100644 index 0000000..31b476c --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_right.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_right_end.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_right_end.png Binary files differnew file mode 100644 index 0000000..5732bf1 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_right_end.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_right_start.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_right_start.png Binary files differnew file mode 100644 index 0000000..2d8ddca --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_right_start.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_top.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_top.png Binary files differnew file mode 100644 index 0000000..f91777b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_top.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_top_end.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_top_end.png Binary files differnew file mode 100644 index 0000000..f91777b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_top_end.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_top_start.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_top_start.png Binary files differnew file mode 100644 index 0000000..f91777b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gap_top_start.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gtkrc b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gtkrc new file mode 100644 index 0000000..2fe1281 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/gtkrc @@ -0,0 +1,1224 @@ +include "testgtkrc2" + +module_path ".:/home/raster/themes" + +style "eventbox" { + bg_pixmap[NORMAL] = "<parent>" + bg_pixmap[INSENSITIVE] = "<parent>" + bg_pixmap[PRELIGHT] = "<parent>" + bg_pixmap[SELECTED] = "<parent>" + bg_pixmap[ACTIVE] = "<parent>" +} + +class "GtkEventBox" style "eventbox" + +# +# Buttons +# + +style "checkradiobutton" { + engine "pixmap" { + image + { + function = FLAT_BOX + recolorable = TRUE + file = "button5.png" + border = { 10,5,5,10 } + stretch = TRUE + } + } +} + +class "GtkRadioButton" style "checkradiobutton" +class "GtkCheckButton" style "checkradiobutton" + +style "togglebutton" +{ + engine "pixmap" { + image + { + function = BOX + recolorable = TRUE + shadow = IN + file = "button2.png" + border = { 5,5,10,10 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + shadow = OUT + file = "button1.png" + border = { 10,10,10,10 } + stretch = TRUE + } + } +} + +class "GtkToggleButton" style "togglebutton" + +style "button" +{ + bg[NORMAL] = "#ffffff" + + engine "pixmap" + { + image + { + function = BOX + recolorable = TRUE + state = NORMAL + shadow = OUT + file = "button5.png" + border = { 10,5,5,10 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + state = PRELIGHT + shadow = OUT + file = "button9.png" + border = { 10,5,5,10 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + state = NORMAL + shadow = IN + file = "button4.png" + border = { 10,5,5,10 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + state = INSENSITIVE + shadow = IN + file = "button3.png" + border = { 10,5,5,10 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + state = INSENSITIVE + shadow = OUT + file = "button3.png" + border = { 10,5,5,10 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + state = SELECTED + shadow = IN + file = "button4.png" + border = { 10,5,5,10 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + state = ACTIVE + shadow = IN + file = "button0.png" + border = { 10,5,5,10 } + stretch = TRUE + } + } +} + +class "GtkButton" style "button" + +style "clist" +{ + bg[PRELIGHT] = "#eeeeee" + fg[PRELIGHT] = "#000000" + base[NORMAL] = "#eeeeee" +} + +class "GtkCList" style "clist" + +style "notebook" +{ + engine "pixmap" + { + image + { + function = EXTENSION + recolorable = TRUE + state = ACTIVE + file = "notebook2.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + gap_side = BOTTOM + } + image + { + function = EXTENSION + recolorable = TRUE + state = ACTIVE + file = "notebook5.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + gap_side = TOP + } + image + { + function = EXTENSION + recolorable = TRUE + state = ACTIVE + file = "notebook6.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + gap_side = RIGHT + } + image + { + function = EXTENSION + recolorable = TRUE + state = ACTIVE + file = "notebook6.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + gap_side = LEFT + } + image + { + function = EXTENSION + recolorable = TRUE + file = "notebook3.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + gap_side = BOTTOM + } + image + { + function = EXTENSION + recolorable = TRUE + file = "notebook4.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + gap_side = TOP + } + image + { + function = EXTENSION + recolorable = TRUE + file = "notebook7.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + gap_side = RIGHT + } + image + { + function = EXTENSION + recolorable = TRUE + file = "notebook7.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + gap_side = LEFT + } +# +# How to draw boxes with a gap on one side (ie the page of a notebook) +# + image + { + function = BOX_GAP + recolorable = TRUE + file = "notebook6.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + gap_file = "gap_top.png" + gap_border = { 0, 0, 0, 0 } + gap_start_file = "gap_top_start.png" + gap_start_border= { 2, 0, 0, 0 } + gap_end_file = "gap_top_end.png" + gap_end_border = { 0, 2, 0, 0 } + gap_side = TOP + } + image + { + function = BOX_GAP + recolorable = TRUE + file = "notebook6.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + gap_file = "gap_bottom.png" + gap_border = { 0, 0, 0, 0 } + gap_start_file = "gap_bottom_start.png" + gap_start_border= { 2, 0, 0, 0 } + gap_end_file = "gap_bottom_end.png" + gap_end_border = { 0, 2, 0, 0 } + gap_side = BOTTOM + } + image + { + function = BOX_GAP + recolorable = TRUE + file = "notebook6.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + gap_file = "gap_left.png" + gap_border = { 0, 0, 0, 0 } + gap_start_file = "gap_left_start.png" + gap_start_border= { 0, 0, 2, 0 } + gap_end_file = "gap_left_end.png" + gap_end_border = { 0, 0, 0, 2 } + gap_side = LEFT + } + image + { + function = BOX_GAP + recolorable = TRUE + file = "notebook6.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + gap_file = "gap_right.png" + gap_border = { 0, 0, 0, 0 } + gap_start_file = "gap_right_start.png" + gap_start_border= { 0, 0, 2, 0 } + gap_end_file = "gap_right_end.png" + gap_end_border = { 0, 0, 0, 2 } + gap_side = RIGHT + } +# +# How to draw the box of a notebook when it isnt attached to a tab +# + image + { + function = BOX + recolorable = TRUE + file = "notebook6.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + gap_side = TOP + } + } +} + +class "GtkNotebook" style "notebook" + +style "menu" +{ + engine "pixmap" { + image + { + function = BOX + recolorable = TRUE + file = "background.png" + border = { 3, 3, 3, 3 } + stretch = FALSE + overlay_file = "menu_shadow.png" + overlay_border = { 3, 3, 3, 3 } + overlay_stretch = TRUE + } + } +} + +class "GtkMenu" style "menu" + +style "menuitem" +{ + engine "pixmap" { + image + { + function = BOX + recolorable = TRUE + file = "menu_selected.png" + border = { 0, 0, 0, 0 } + stretch = TRUE + } + } +} + +class "GtkMenuItem" style "menuitem" + +style "menubar" +{ + engine "pixmap" { + image + { + function = BOX + recolorable = TRUE + file = "background.png" + border = { 3, 3, 3, 3 } + stretch = FALSE + } + } +} + +class "GtkMenuBar" style "menubar" + +style "optionmenu" +{ + engine "pixmap" { + image + { + function = BOX + recolorable = TRUE + file = "option_menu.png" + border = { 12, 32, 2, 3 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + state = INSENSITIVE + file = "option_menu_disabled.png" + border = { 12, 32, 2, 3 } + stretch = TRUE + } + } +} + +class "GtkOptionMenu" style "optionmenu" + +style "progressbar" +{ + engine "pixmap" + { + image + { + function = BOX + recolorable = TRUE + detail = "bar" + file = "button6.png" + border = { 5, 5, 5, 5 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + detail = "trough" + file = "button5.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + orientation = HORIZONTAL + } + } +} + +class "GtkProgressBar" style "progressbar" +class "GtkBar" style "progressbar" + +style "ruler" +{ + engine "pixmap" { + image + { + function = BOX + recolorable = TRUE + detail = "vruler" + file = "button5.png" + border = { 2, 2, 2, 2 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + detail = "hruler" + file = "button5.png" + border = { 2, 2, 2, 2 } + stretch = TRUE + } + } +} + +class "GtkRuler" style "ruler" + +style "item" +{ + engine "pixmap" { + image + { + function = FLAT_BOX + recolorable = TRUE + state = INSENSITIVE + file = "button3.png" + border = { 10, 5, 5, 10 } + stretch = TRUE + } + image + { + function = FLAT_BOX + recolorable = TRUE + file = "button6.png" + border = { 10, 5, 5, 10 } + stretch = TRUE + } + } +} + +class "GtkTreeItem" style "item" +class "GtkListItem" style "item" + +style "window" +{ + engine "pixmap" { + image + { + function = FLAT_BOX + recolorable = TRUE + file = "background.png" + stretch = TRUE + } + } +} + +class "GtkWindow" style "window" + +style "curve" +{ + engine "pixmap" { + image + { + function = FLAT_BOX + recolorable = TRUE + detail = "curve_bg" + file = "button1.png" + border = { 2, 2, 2, 2 } + stretch = TRUE + } + } +} + +class "GtkCurve" style "curve" + +style "scrollbar" +{ + engine "pixmap" + { + image + { + function = BOX + recolorable = TRUE + detail = "slider" + state = PRELIGHT + file = "scrollbar_horiz1.png" + border = { 15, 15, 2, 2 } + stretch = TRUE + orientation = HORIZONTAL + } + image + { + function = BOX + recolorable = TRUE + detail = "slider" + state = NORMAL + file = "scrollbar_horiz1.png" + border = { 13, 12, 2, 2 } + stretch = TRUE + orientation = HORIZONTAL + } + image + { + function = BOX + recolorable = TRUE + detail = "slider" + state = PRELIGHT + file = "scrollbar_vert1.png" + border = { 2, 2, 15, 15 } + stretch = TRUE + orientation = VERTICAL + } + image + { + function = BOX + recolorable = TRUE + detail = "slider" + file = "scrollbar_vert1.png" + border = { 2, 2, 15, 15 } + stretch = TRUE + orientation = VERTICAL + } + image + { + function = BOX + recolorable = TRUE + detail = "trough" + file = "vtrough.png" + border = { 3, 3, 23, 23 } + stretch = TRUE + orientation = VERTICAL + } + image + { + function = BOX + recolorable = TRUE + detail = "trough" + file = "htrough.png" + border = { 23, 23, 3, 3 } + stretch = TRUE + orientation = HORIZONTAL + } + } +} + +class "GtkScrollbar" style "scrollbar" + +style "pane" +{ + engine "pixmap" { + image + { + function = BOX + orientation = HORIZONTAL + file = "empty.png" + } + image + { + function = BOX + orientation = VERTICAL + file = "empty.png" + } + image + { + function = HANDLE + orientation = HORIZONTAL + file = "splitpane_hhandle.png" + } + image + { + function = HANDLE + orientation = VERTICAL + file = "splitpane_vhandle.png" + } + image + { + function = ARROW + arrow_direction = LEFT + file = "splitpane_leftarrow.png" + } + image + { + function = ARROW + arrow_direction = RIGHT + file = "splitpane_rightarrow.png" + } + image + { + function = ARROW + arrow_direction = DOWN + file = "splitpane_downarrow.png" + } + image + { + function = ARROW + arrow_direction = UP + file = "splitpane_uparrow.png" + } + + } +} + +class "GtkPaned" style "pane" + +style "range" +{ + engine "pixmap" { + image + { + function = SLIDER + recolorable = TRUE + file = "slider_hth2.png" + border = { 2, 2, 5, 5 } + stretch = FALSE + overlay_file = "slider_horiz1.png" + overlay_border = { 5, 5, 5, 5 } + overlay_stretch = FALSE + orientation = HORIZONTAL + } + image + { + function = SLIDER + recolorable = TRUE + file = "slider_vth2.png" + border = { 5, 5, 2, 2 } + stretch = FALSE + overlay_file = "slider_vert1.png" + overlay_border = { 5, 5, 5, 5 } + overlay_stretch = FALSE + orientation = VERTICAL + } + image + { + function = BOX + recolorable = TRUE + detail = "trough" + file = "slider_vth1.png" + border = { 5, 2, 2, 2 } + stretch = TRUE + orientation = VERTICAL + } + image + { + function = BOX + recolorable = TRUE + detail = "trough" + file = "slider_hth1.png" + border = { 2, 2, 5, 2 } + stretch = TRUE + orientation = HORIZONTAL + } + } +} +class "GtkRange" style "range" + +style "default" +{ + font = "-*-helvetica-medium-r-normal--10-*-*-*-*-*-*-*" + fg[NORMAL] = { 0.00, 0.00, 0.00 } + fg[PRELIGHT] = { 0.00, 0.00, 0.00 } + fg[ACTIVE] = { 0.00, 0.00, 0.00 } + fg[SELECTED] = { 0.00, 0.00, 0.00 } + fg[INSENSITIVE] = { 0.70, 0.70, 0.70 } + bg[NORMAL] = "#ffffff" + bg[PRELIGHT] = "#ffffff" + bg[ACTIVE] = "#ffffff" + bg[INSENSITIVE] = "#ffffff" + bg[SELECTED] = "#6ba5e7" + base[NORMAL] = "#ffffff" + engine "pixmap" + { + image + { + function = BOX + recolorable = TRUE + detail = "slider" + state = PRELIGHT + file = "scrollbar_horiz1.png" + border = { 15, 15, 2, 2 } + stretch = TRUE + orientation = HORIZONTAL + } + image + { + function = BOX + recolorable = TRUE + detail = "slider" + state = NORMAL + file = "scrollbar_horiz1.png" + border = { 13, 12, 2, 2 } + stretch = TRUE + orientation = HORIZONTAL + } + image + { + function = BOX + recolorable = TRUE + detail = "slider" + state = PRELIGHT + file = "scrollbar_vert1.png" + border = { 2, 2, 15, 15 } + stretch = TRUE + orientation = VERTICAL + } + image + { + function = BOX + recolorable = TRUE + detail = "slider" + file = "scrollbar_vert1.png" + border = { 2, 2, 15, 15 } + stretch = TRUE + orientation = VERTICAL + } +# +# Any trough.... +# + image + { + function = BOX + recolorable = TRUE + detail = "trough" + file = "vtrough.png" + border = { 3, 3, 23, 23 } + stretch = TRUE + orientation = VERTICAL + } + image + { + function = BOX + recolorable = TRUE + detail = "trough" +# file = "button5.png" +# border = { 10, 10, 10, 10 } + file = "htrough.png" + border = { 23, 23, 3, 3 } + stretch = TRUE + orientation = HORIZONTAL + } + image + { + function = BOX + recolorable = TRUE + detail = "bar" + file = "scrollbar_horiz2.png" + border = { 36, 36, 2, 2 } + stretch = TRUE + } +# +# Handlebox +# + image + { + function = BOX + recolorable = TRUE + detail = "handlebox_bin" + file = "button1.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + } +# +# Paned widget +# + image + { + function = BOX + recolorable = TRUE + detail = "paned" + file = "button5.png" + border = { 10, 5, 5, 10 } + stretch = TRUE + } +# +# Tooltips +# + image + { + function = FLAT_BOX + recolorable = TRUE + detail = "tooltip" + file = "button5.png" + border = { 5, 5, 5, 5 } + stretch = TRUE + } +# +# Selected text in entries, text widget, lists and trees +# + image + { + function = FLAT_BOX + recolorable = TRUE + state = INSENSITIVE + detail = "selected" + file = "button5.png" + border = { 10, 5, 5, 10 } + stretch = TRUE + } + image + { + function = FLAT_BOX + recolorable = TRUE + detail = "selected" + file = "button2.png" + border = { 10, 5, 5, 10 } + stretch = TRUE + } + image + { + function = FLAT_BOX + recolorable = TRUE + state = INSENSITIVE + detail = "text" + file = "textborder.png" + border = { 3, 3, 3, 3 } + stretch = TRUE + } + image + { + function = FLAT_BOX + recolorable = TRUE + detail = "text" + file = "textborder.png" + border = { 3, 3, 3, 3 } + stretch = TRUE + } + image + { + function = FLAT_BOX + recolorable = TRUE + detail = "viewportbin" + file = "background.png" + stretch = FALSE + } +# +# Arrows for all occasions +# + image + { + function = ARROW + recolorable = TRUE + state = NORMAL + file = "arrow_up1.png" + stretch = FALSE + arrow_direction = UP + } + image + { + function = ARROW + recolorable = TRUE + state = PRELIGHT + file = "arrow_up2.png" + stretch = FALSE + arrow_direction = UP + } + image + { + function = ARROW + recolorable = TRUE + shadow = IN + file = "arrow_up3.png" + stretch = FALSE + arrow_direction = UP + } + + image + { + function = ARROW + recolorable = TRUE + state = NORMAL + file = "arrow_down1.png" + stretch = FALSE + arrow_direction = DOWN + } + image + { + function = ARROW + recolorable = TRUE + state = PRELIGHT + file = "arrow_down2.png" + stretch = FALSE + arrow_direction = DOWN + } + image + { + function = ARROW + recolorable = TRUE + shadow = IN + file = "arrow_down3.png" + stretch = FALSE + arrow_direction = DOWN + } + + image + { + function = ARROW + recolorable = TRUE + state = NORMAL + file = "arrow_left1.png" + stretch = FALSE + arrow_direction = LEFT + } + image + { + function = ARROW + recolorable = TRUE + state = PRELIGHT + file = "arrow_left2.png" + stretch = FALSE + arrow_direction = LEFT + } + image + { + function = ARROW + recolorable = TRUE + shadow = IN + file = "arrow_left3.png" + stretch = FALSE + arrow_direction = LEFT + } + + image + { + function = ARROW + recolorable = TRUE + state = NORMAL + file = "arrow_right1.png" + stretch = FALSE + arrow_direction = RIGHT + } + image + { + function = ARROW + recolorable = TRUE + state = PRELIGHT + file = "arrow_right2.png" + stretch = FALSE + arrow_direction = RIGHT + } + image + { + function = ARROW + recolorable = TRUE + shadow = IN + file = "arrow_right3.png" + stretch = FALSE + arrow_direction = RIGHT + } +# +# Hline and Vline widgets +# + image + { + function = HLINE + recolorable = TRUE + file = "hline.png" + border = { 0, 0, 1, 1 } + stretch = TRUE + } + image + { + function = VLINE + recolorable = TRUE + file = "vline.png" + border = { 1, 1, 0, 0 } + stretch = TRUE + } +# +# Check marks +# + image + { + function = CHECK + recolorable = TRUE + shadow = OUT + overlay_file = "check1.png" + overlay_stretch = FALSE + } + image + { + function = CHECK + recolorable = TRUE + shadow = IN + overlay_file = "check2.png" + overlay_stretch = FALSE + } +# +# Option marks +# + image + { + function = OPTION + recolorable = TRUE + shadow = OUT + overlay_file = "option1.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + } + image + { + function = OPTION + recolorable = TRUE + shadow = IN + overlay_file = "option2.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + } +# +# The "tab" on the OptionMenu +# +# image +# { +# function = TAB +# recolorable = TRUE +# overlay_file = "button2.png" +# overlay_stretch = FALSE +# } +# +# Fun with Scrollbars +# + image + { + function = SLIDER + recolorable = TRUE + file = "scrollbar_horiz1.png" + border = { 13, 12, 2, 2 } + stretch = TRUE + orientation = HORIZONTAL + } + image + { + function = SLIDER + recolorable = TRUE + file = "scrollbar_vert1.png" + border = { 2, 2, 13, 12 } + stretch = TRUE + orientation = VERTICAL + } +# +# The handle on Handleboxes +# + image + { + function = HANDLE + recolorable = TRUE + file = "button5.png" + border = { 10, 5, 5, 10 } + stretch = TRUE + overlay_file = "handle_vert_thumb.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + orientation = VERTICAL + } + + image + { + function = HANDLE + recolorable = TRUE + file = "button5.png" + border = { 10, 5, 5, 10 } + stretch = TRUE + overlay_file = "handle_horiz_thumb.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + orientation = HORIZONTAL + } + +# +# The background for entry widgets +# + image + { + function = FLAT_BOX + recolorable = TRUE + state = INSENSITIVE + detail = "entry_bg" + file = "entry2.png" + border = { 3, 3, 3, 3 } + stretch = TRUE + } + image + { + function = FLAT_BOX + recolorable = TRUE + detail = "entry_bg" + file = "entry1.png" + border = { 3, 3, 3, 3 } + stretch = TRUE + } +# +# How to draw the focus around a widget +# + image + { + function = FOCUS + recolorable = TRUE + overlay_file = "focus.png" + overlay_border = { 4, 4, 4, 4 } + overlay_stretch = TRUE + } +# +# How to draw shadows in general +# + image + { + function = SHADOW + recolorable = TRUE + shadow = IN + file = "shadow2.png" + border = { 2, 2, 2, 2 } + stretch = TRUE + } + image + { + function = SHADOW + recolorable = TRUE + shadow = OUT + file = "shadow1.png" + border = { 2, 2, 2, 2 } + stretch = TRUE + } + image + { + function = SHADOW + recolorable = TRUE + shadow = ETCHED_IN + file = "frame1.png" + border = { 2, 2, 2, 2 } + stretch = TRUE + } + image + { + function = SHADOW + recolorable = TRUE + shadow = ETCHED_OUT + file = "frame2.png" + border = { 2, 2, 2, 2 } + stretch = TRUE + } +# +# How to draw shadows with a gap on one side +# + image + { + function = SHADOW_GAP + recolorable = TRUE + file = "frame_gap.png" + border = { 2, 2, 2, 2 } + stretch = FALSE + gap_start_file = "frame_gap_top_start.png" + gap_start_border= { 0, 0, 1, 1 } + gap_end_file = "frame_gap_top_end.png" + gap_end_border = { 0, 2, 1, 1 } + gap_side = TOP + } +# +# Some defaults as to how to draw boxes if they haven't already been covered +# + image + { + function = BOX + recolorable = TRUE + detail = "hruler" + file = "button5.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + detail = "vruler" + file = "button5.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + shadow = IN + file = "button2.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + file = "button7.png" + border = { 10, 10, 10, 10 } + stretch = TRUE + } + } +} + + +# common default +class "GtkWidget" style "default" + + diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/handle_horiz_thumb.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/handle_horiz_thumb.png Binary files differnew file mode 100644 index 0000000..410a2f8 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/handle_horiz_thumb.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/handle_vert_thumb.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/handle_vert_thumb.png Binary files differnew file mode 100644 index 0000000..65d2f1d --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/handle_vert_thumb.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/hline.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/hline.png Binary files differnew file mode 100644 index 0000000..f523ac9 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/hline.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/htrough.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/htrough.png Binary files differnew file mode 100644 index 0000000..b943509 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/htrough.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/mainbackground.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/mainbackground.png Binary files differnew file mode 100644 index 0000000..970413f --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/mainbackground.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/menu_selected.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/menu_selected.png Binary files differnew file mode 100644 index 0000000..1e51f96 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/menu_selected.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/menu_shadow.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/menu_shadow.png Binary files differnew file mode 100644 index 0000000..2b81419 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/menu_shadow.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/menubar.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/menubar.png Binary files differnew file mode 100644 index 0000000..f91777b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/menubar.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook1.png Binary files differnew file mode 100644 index 0000000..f91777b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook2.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook2.png Binary files differnew file mode 100644 index 0000000..c6c891a --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook2.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook3.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook3.png Binary files differnew file mode 100644 index 0000000..8ab49e2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook3.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook4.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook4.png Binary files differnew file mode 100644 index 0000000..09010a6 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook4.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook5.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook5.png Binary files differnew file mode 100644 index 0000000..53c5661 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook5.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook6.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook6.png Binary files differnew file mode 100644 index 0000000..5db655f --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook6.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook7.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook7.png Binary files differnew file mode 100644 index 0000000..38bbb12 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/notebook7.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/option1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/option1.png Binary files differnew file mode 100644 index 0000000..9b02817 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/option1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/option2.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/option2.png Binary files differnew file mode 100644 index 0000000..e9d5c28 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/option2.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/option_menu.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/option_menu.png Binary files differnew file mode 100644 index 0000000..009a23d --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/option_menu.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/option_menu_disabled.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/option_menu_disabled.png Binary files differnew file mode 100644 index 0000000..fa98440 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/option_menu_disabled.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/scrollbar_horiz1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/scrollbar_horiz1.png Binary files differnew file mode 100644 index 0000000..b8ae13c --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/scrollbar_horiz1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/scrollbar_horiz2.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/scrollbar_horiz2.png Binary files differnew file mode 100644 index 0000000..3a5703e --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/scrollbar_horiz2.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/scrollbar_vert1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/scrollbar_vert1.png Binary files differnew file mode 100644 index 0000000..07bb5d6 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/scrollbar_vert1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/shadow1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/shadow1.png Binary files differnew file mode 100644 index 0000000..5c96bc4 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/shadow1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/shadow2.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/shadow2.png Binary files differnew file mode 100644 index 0000000..6d77304 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/shadow2.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/slider_horiz1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/slider_horiz1.png Binary files differnew file mode 100644 index 0000000..79fa938 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/slider_horiz1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/slider_hth1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/slider_hth1.png Binary files differnew file mode 100644 index 0000000..ec40814 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/slider_hth1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/slider_hth2.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/slider_hth2.png Binary files differnew file mode 100644 index 0000000..099ef64 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/slider_hth2.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/slider_vert1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/slider_vert1.png Binary files differnew file mode 100644 index 0000000..9ef7671 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/slider_vert1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/slider_vth1.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/slider_vth1.png Binary files differnew file mode 100644 index 0000000..e6d2f36 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/slider_vth1.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/slider_vth2.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/slider_vth2.png Binary files differnew file mode 100644 index 0000000..1e41fa6 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/slider_vth2.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/splitpane_downarrow.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/splitpane_downarrow.png Binary files differnew file mode 100644 index 0000000..ac40dd9 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/splitpane_downarrow.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/splitpane_hhandle.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/splitpane_hhandle.png Binary files differnew file mode 100644 index 0000000..d503cc7 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/splitpane_hhandle.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/splitpane_leftarrow.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/splitpane_leftarrow.png Binary files differnew file mode 100644 index 0000000..4bf8a1a --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/splitpane_leftarrow.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/splitpane_rightarrow.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/splitpane_rightarrow.png Binary files differnew file mode 100644 index 0000000..9cd4b35 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/splitpane_rightarrow.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/splitpane_uparrow.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/splitpane_uparrow.png Binary files differnew file mode 100644 index 0000000..2dc2354 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/splitpane_uparrow.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/splitpane_vhandle.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/splitpane_vhandle.png Binary files differnew file mode 100644 index 0000000..85b6ab1 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/splitpane_vhandle.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/textborder.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/textborder.png Binary files differnew file mode 100644 index 0000000..12e4e4d --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/textborder.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/vline.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/vline.png Binary files differnew file mode 100644 index 0000000..10bbc9f --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/vline.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/vtrough.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/vtrough.png Binary files differnew file mode 100644 index 0000000..1459804 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/gtk/vtrough.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Computer16.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Computer16.gif Binary files differnew file mode 100644 index 0000000..824faff --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Computer16.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Default.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Default.gif Binary files differnew file mode 100644 index 0000000..9f215e6 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Default.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Error.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Error.gif Binary files differnew file mode 100644 index 0000000..0dbcc43 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Error.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/FileViewDetails18.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/FileViewDetails18.gif Binary files differnew file mode 100644 index 0000000..0e255e9 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/FileViewDetails18.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/FileViewList18.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/FileViewList18.gif Binary files differnew file mode 100644 index 0000000..4ea5b5e --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/FileViewList18.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Floppy16.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Floppy16.gif Binary files differnew file mode 100644 index 0000000..c9ab12f --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Floppy16.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Folder16.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Folder16.gif Binary files differnew file mode 100644 index 0000000..8d47dfe --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Folder16.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/FolderNew18.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/FolderNew18.gif Binary files differnew file mode 100644 index 0000000..902e442 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/FolderNew18.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/FolderUp18.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/FolderUp18.gif Binary files differnew file mode 100644 index 0000000..ddb5bcd --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/FolderUp18.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/FrameDefault.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/FrameDefault.gif Binary files differnew file mode 100644 index 0000000..2a19c3f --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/FrameDefault.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Harddisk16.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Harddisk16.gif Binary files differnew file mode 100644 index 0000000..49f9a81 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Harddisk16.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Home18.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Home18.gif Binary files differnew file mode 100644 index 0000000..a84fda1 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Home18.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Inform.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Inform.gif Binary files differnew file mode 100644 index 0000000..1a18a13 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Inform.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Information.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Information.gif Binary files differnew file mode 100644 index 0000000..ddfb94d --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Information.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Page16.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Page16.gif Binary files differnew file mode 100644 index 0000000..fc53866 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Page16.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Question.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Question.gif Binary files differnew file mode 100644 index 0000000..3eff0e8 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Question.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/TreeClosed.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/TreeClosed.gif Binary files differnew file mode 100644 index 0000000..fa4cf82 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/TreeClosed.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Warn.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Warn.gif Binary files differnew file mode 100644 index 0000000..2a09a87 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/icons/Warn.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/CosmicGelDj4.gif b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/CosmicGelDj4.gif Binary files differnew file mode 100644 index 0000000..2f0bbe1 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/CosmicGelDj4.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/activetitle.PNG b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/activetitle.PNG Binary files differnew file mode 100644 index 0000000..070d439 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/activetitle.PNG diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/aquabackground.PNG b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/aquabackground.PNG Binary files differnew file mode 100644 index 0000000..1cdcec7 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/aquabackground.PNG diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/bondiblue.PNG b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/bondiblue.PNG Binary files differnew file mode 100644 index 0000000..cb5caaf --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/bondiblue.PNG diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/close.PNG b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/close.PNG Binary files differnew file mode 100644 index 0000000..8de4516 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/close.PNG diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/close_disabled.PNG b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/close_disabled.PNG Binary files differnew file mode 100644 index 0000000..0f0ebb7 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/close_disabled.PNG diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/close_down.PNG b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/close_down.PNG Binary files differnew file mode 100644 index 0000000..ee54a90 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/close_down.PNG diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/iconify.PNG b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/iconify.PNG Binary files differnew file mode 100644 index 0000000..80a5e49 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/iconify.PNG diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/iconify_disabled.PNG b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/iconify_disabled.PNG Binary files differnew file mode 100644 index 0000000..422cfa2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/iconify_disabled.PNG diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/iconify_down.PNG b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/iconify_down.PNG Binary files differnew file mode 100644 index 0000000..9249d26 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/iconify_down.PNG diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/inactivetitle.PNG b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/inactivetitle.PNG Binary files differnew file mode 100644 index 0000000..bb21f15 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/inactivetitle.PNG diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/kde.themerc b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/kde.themerc new file mode 100644 index 0000000..f66994f --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/kde.themerc @@ -0,0 +1,132 @@ +[General] +name=kAqua +author=Jeff Marlow +email=jeff@bluegenesis.com +homepage=http://jeff.marlow.net +description=Apple's Aqua (Mac OS X)\nThanks to Dangeruss for the Graphics\nThanks to Steve Jobs for the Vision +version=1.03 + +[Panel] +background=panelx.PNG + +[File Manager] +# Kfm's common background image: +# backgroundImage=kfmbackground.PNG +# Kfm's colors: +htmlBackground=255,255,255 +htmlForeground=0,0,0 +htmlLinks=35,107,200 +htmlFollowedLinks=25,73,144 + +[Konsole] +schema=MacOSX.schema +background=bondiblue.PNG + +[Display] +CommonDesktop=True +Wallpaper0=aquabackground.PNG +WallpaperMode0=Scaled +Wallpaper1=aquabackground.PNG +WallpaperMode1=Scaled +Wallpaper2=aquabackground.PNG +WallpaperMode2=Scaled +Wallpaper3=aquabackground.PNG +WallpaperMode3=Scaled +Wallpaper4=aquabackground.PNG +WallpaperMode4=Scaled +Wallpaper5=aquabackground.PNG +WallpaperMode5=Scaled +Wallpaper6=aquabackground.PNG +WallpaperMode6=Scaled +Wallpaper7=aquabackground.PNG +WallpaperMode7=Scaled +RootIconTextStyle=1 +RootIconTextForeground=0,0,27 +RootIconTextBackground=240,247,255 + +[Colors] +foreground=0,0,0 +selectForeground=255,255,255 +activeBlend=53,99,144 +selectBackground=25,73,144 +inactiveBackground=128,128,128 +contrast=7 +background=233,237,247 +activeBackground=233,233,233 +inactiveBlend=192,192,192 +inactiveForeground=103,105,145 +activeForeground=0,0,0 +windowForeground=0,0,0 +windowBackground=255,255,255 +terminalForeground=black +terminalBackground=ghostwhite + + +[Window Titlebar] +#CloseButton=close.PNG +#CloseDownButton=close_down.PNG +#CloseInactiveButton=close_disabled.PNG + +CloseButton=CosmicGelDj4.gif +CloseDownButton=CosmicGelDj4.gif +CloseInactiveButton=CosmicGelDj4.gif + +MaximizeButton=maximize.PNG +MaximizeDownButton=maximizedown.PNG +MaximizeInactiveButton=maximize_disabled.PNG + +MenuButton=menu.PNG + +MinimizeButton=iconify.PNG +MinimizeDownButton=iconify_down.PNG +MinimizeInactiveButton=iconify_disabled.PNG + +StickyButton=sticky.PNG +StickyDownButton=stickydown.PNG + +TitlebarPixmapActive=titlebar_selected.png +TitlebarPixmapActiveBorder=80, 80, 0, 16 +TitlebarPixmapActiveLeft= +TitlebarPixmapActiveRight= +TitlebarPixmapActiveTop= +TitlebarPixmapActiveBottom= +TitlebarPixmapInactive=titlebar_unselected.png + +PixmapUnderTitleText=yes +TitleFrameShaded=no +TitleAlignment=middle +TitleAbsolutePosition=yes + +[Window Border] +shapePixmapTop= +shapePixmapBottom=window_bottom.png +shapePixmapLeft=window_left.png +shapePixmapRight=window_right.png +shapePixmapTopLeft=window_topleft.png +shapePixmapTopRight=window_topright.png +shapePixmapBottomLeft=window_bottomleft.png +shapePixmapBottomRight=window_bottomright.png + +[Window Button Layout] +ButtonA=Off +ButtonB=Maximize +ButtonC=Iconify +ButtonD=Off +ButtonE=Off +ButtonF=Close + +[Window Gimmick] + +[Sounds] +WindowShadeDown=RollUp.wav +WindowShadeUp=RollUp.wav +WindowClose=Close.wav + +[Icons] +PanelGo=go.PNG:mini-go.PNG +inode/directory=folder_blue.PNG:mini_folder_blue.PNG + +[Extra Icons] +Extra1=netscape.PNG:mini-netscape.PNG +Extra2=icq.PNG:mini-icq.PNG + diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/kfmbackground.PNG b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/kfmbackground.PNG Binary files differnew file mode 100644 index 0000000..2d6924b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/kfmbackground.PNG diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/maximize.PNG b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/maximize.PNG Binary files differnew file mode 100644 index 0000000..b28e5de --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/maximize.PNG diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/maximize_disabled.PNG b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/maximize_disabled.PNG Binary files differnew file mode 100644 index 0000000..ecea2a4 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/maximize_disabled.PNG diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/maximizedown.PNG b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/maximizedown.PNG Binary files differnew file mode 100644 index 0000000..cb365c8 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/maximizedown.PNG diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/menu.PNG b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/menu.PNG Binary files differnew file mode 100644 index 0000000..477b37e --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/menu.PNG diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/panelx.PNG b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/panelx.PNG Binary files differnew file mode 100644 index 0000000..660cf50 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/panelx.PNG diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/titlebar_selected.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/titlebar_selected.png Binary files differnew file mode 100644 index 0000000..2b61dd3 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/titlebar_selected.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/titlebar_unselected.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/titlebar_unselected.png Binary files differnew file mode 100644 index 0000000..cb8ac09 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/titlebar_unselected.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_bottom.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_bottom.png Binary files differnew file mode 100644 index 0000000..24ef2ed --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_bottom.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_bottomleft.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_bottomleft.png Binary files differnew file mode 100644 index 0000000..ade162b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_bottomleft.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_bottomright.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_bottomright.png Binary files differnew file mode 100644 index 0000000..1450e8e --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_bottomright.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_left.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_left.png Binary files differnew file mode 100644 index 0000000..95c4d09 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_left.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_right.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_right.png Binary files differnew file mode 100644 index 0000000..3092cdf --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_right.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_topleft.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_topleft.png Binary files differnew file mode 100644 index 0000000..2919eb2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_topleft.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_topright.png b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_topright.png Binary files differnew file mode 100644 index 0000000..c26cb32 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/kde/window_topright.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/skinlf-themepack.xml b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/skinlf-themepack.xml new file mode 100644 index 0000000..de368cc --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/aquathemepack/skinlf-themepack.xml @@ -0,0 +1,18 @@ +<?xml version="1.0"?> + +<skinlf-themepack require="1.2.4"> + + <property name="EnableBorders" value="true" /> + <property name="JDesktopPane.backgroundEnabled" value="false" /> + <property name="ScrollBar.alternateLayout" value="true" /> + <property name="PopupMenu.animation" value="false"/> + <property name="JSplitPane.alternateUI" value="true" /> + + <font name="Global" value="SansSerif,0,11"/> + + <skin> + <skin url="gtk/gtkrc"></skin> + <skin url="kde/kde.themerc"></skin> + </skin> + +</skinlf-themepack> diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_down.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_down.png Binary files differnew file mode 100644 index 0000000..40e9214 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_down.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_in_down.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_in_down.png Binary files differnew file mode 100644 index 0000000..f5da126 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_in_down.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_in_left.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_in_left.png Binary files differnew file mode 100644 index 0000000..03f5b3f --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_in_left.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_in_right.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_in_right.png Binary files differnew file mode 100644 index 0000000..68cdc12 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_in_right.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_in_up.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_in_up.png Binary files differnew file mode 100644 index 0000000..754797d --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_in_up.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_left.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_left.png Binary files differnew file mode 100644 index 0000000..848aee1 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_left.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_right.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_right.png Binary files differnew file mode 100644 index 0000000..50e8408 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_right.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_rollover_down.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_rollover_down.png Binary files differnew file mode 100644 index 0000000..3c27fef --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_rollover_down.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_rollover_left.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_rollover_left.png Binary files differnew file mode 100644 index 0000000..ae69546 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_rollover_left.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_rollover_right.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_rollover_right.png Binary files differnew file mode 100644 index 0000000..163305d --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_rollover_right.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_rollover_up.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_rollover_up.png Binary files differnew file mode 100644 index 0000000..c05a591 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_rollover_up.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_up.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_up.png Binary files differnew file mode 100644 index 0000000..c05a591 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/arrow_up.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_active.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_active.png Binary files differnew file mode 100644 index 0000000..6ba9e8e --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_active.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_disabled_in.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_disabled_in.png Binary files differnew file mode 100644 index 0000000..7ebf4a0 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_disabled_in.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_disabled_out.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_disabled_out.png Binary files differnew file mode 100644 index 0000000..3e6f324 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_disabled_out.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_in.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_in.png Binary files differnew file mode 100644 index 0000000..2eade1c --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_in.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_normal_in.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_normal_in.png Binary files differnew file mode 100644 index 0000000..85dee5e --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_normal_in.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_out.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_out.png Binary files differnew file mode 100644 index 0000000..cb51951 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_out.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_rollover_out.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_rollover_out.png Binary files differnew file mode 100644 index 0000000..de00b5f --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_rollover_out.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_selected.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_selected.png Binary files differnew file mode 100644 index 0000000..7c83858 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/button_selected.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/check_in.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/check_in.png Binary files differnew file mode 100644 index 0000000..4a8fd3a --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/check_in.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/check_out.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/check_out.png Binary files differnew file mode 100644 index 0000000..e176316 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/check_out.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/empty.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/empty.png Binary files differnew file mode 100644 index 0000000..6539ac0 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/empty.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/gtkrc b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/gtkrc new file mode 100644 index 0000000..5caf710 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/gtkrc @@ -0,0 +1,1079 @@ + +style "eventbox" { + bg_pixmap[NORMAL] = "<parent>" + bg_pixmap[INSENSITIVE] = "<parent>" + bg_pixmap[PRELIGHT] = "<parent>" + bg_pixmap[SELECTED] = "<parent>" + bg_pixmap[ACTIVE] = "<parent>" +} + +class "GtkEventBox" style "eventbox" + +style "pane" +{ + engine "pixmap" { + image + { + function = BOX + orientation = HORIZONTAL + file = "empty.png" + } + image + { + function = BOX + orientation = VERTICAL + file = "empty.png" + } + image + { + function = HANDLE + orientation = HORIZONTAL + file = "splitpane_hhandle.png" + } + image + { + function = HANDLE + orientation = VERTICAL + file = "splitpane_vhandle.png" + } + image + { + function = ARROW + arrow_direction = LEFT + file = "splitpane_leftarrow.png" + } + image + { + function = ARROW + arrow_direction = RIGHT + file = "splitpane_rightarrow.png" + } + image + { + function = ARROW + arrow_direction = DOWN + file = "splitpane_downarrow.png" + } + image + { + function = ARROW + arrow_direction = UP + file = "splitpane_uparrow.png" + } + + } +} + +class "GtkPaned" style "pane" + +style "checkradiobutton" { + engine "pixmap" { + image + { + function = FLAT_BOX + recolorable = TRUE + file = "highlight.png" + border = { 0, 0, 0, 1 } + stretch = TRUE + } + } +} + +class "GtkScrollbar" style "scrollbar" + +style "scrollbar" { + engine "pixmap" { + image + { + function = BOX + recolorable = TRUE + detail = "trough" + file = "scrollbar_htrough.png" + border = { 9, 2, 1, 1 } + stretch = TRUE + orientation = HORIZONTAL + } + image + { + function = BOX + recolorable = TRUE + detail = "trough" + file = "scrollbar_vtrough.png" + border = { 1, 1, 9, 2 } + stretch = TRUE + orientation = VERTICAL + } + } +} + +class "GtkRadioButton" style "checkradiobutton" +class "GtkCheckButton" style "checkradiobutton" + +style "togglebutton" +{ + engine "pixmap" { + image + { + function = BOX + recolorable = TRUE + shadow = IN + file = "toggle_in.png" + border = { 7, 7, 4, 5 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + state = PRELIGHT + shadow = OUT + file = "toggle_rollover_out.png" + border = { 7, 7, 4, 5 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + state = INSENSITIVE + shadow = IN + file = "toggle_disabled_in.png" + border = { 7, 7, 4, 5 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + state = INSENSITIVE + shadow = OUT + file = "toggle_disabled_out.png" + border = { 7, 7, 4, 5 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + shadow = OUT + file = "toggle_out.png" + border = { 7, 7, 4, 5 } + stretch = TRUE + } + } +} + +class "GtkToggleButton" style "togglebutton" + +style "button" +{ + engine "pixmap" + { + image + { + function = BOX + recolorable = TRUE + state = NORMAL + detail = "buttondefault" + shadow = IN + file = "button_in.png" + border = { 7, 7, 4, 5 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + state = NORMAL + shadow = OUT + file = "button_out.png" + border = { 7, 7, 4, 5 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + state = PRELIGHT + shadow = OUT + file = "button_rollover_out.png" + border = { 7, 7, 4, 5 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + state = NORMAL + shadow = IN + file = "button_normal_in.png" + border = { 7, 7, 4, 5 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + state = INSENSITIVE + shadow = IN + file = "button_disabled_in.png" + border = { 7, 7, 4, 5 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + state = INSENSITIVE + shadow = OUT + file = "button_disabled_out.png" + border = { 7, 7, 4, 5 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + state = SELECTED + shadow = IN + file = "button_selected.png" + border = { 7, 7, 4, 5 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + state = ACTIVE + shadow = IN + file = "button_active.png" + border = { 7, 7, 4, 5 } + stretch = TRUE + } + } +} + +class "GtkButton" style "button" + +style "clist" +{ + bg[PRELIGHT] = "#ffffff" + fg[PRELIGHT] = "#000000" + base[NORMAL] = "#ffffff" +} + +class "GtkCList" style "clist" + +style "notebook" +{ + engine "pixmap" + { + image + { + function = EXTENSION + recolorable = TRUE + state = ACTIVE + file = "tab_active.png" + border = { 11, 11, 5, 6 } + stretch = TRUE + } + image + { + function = EXTENSION + recolorable = TRUE + file = "tab_active_bottom.png" + border = { 11, 11, 5, 6 } + stretch = TRUE + gap_side = BOTTOM + } + image + { + function = EXTENSION + recolorable = TRUE + file = "tab_bottom.png" + border = { 3, 3, 3, 3 } + stretch = TRUE + gap_side = TOP + } + image + { + function = EXTENSION + recolorable = TRUE + file = "tab_left.png" + border = { 3, 3, 3, 3 } + stretch = TRUE + gap_side = RIGHT + } + image + { + function = EXTENSION + recolorable = TRUE + file = "tab_right.png" + border = { 3, 3, 3, 3 } + stretch = TRUE + gap_side = LEFT + } +# +# How to draw boxes with a gap on one side (ie the page of a notebook) +# + image + { + function = BOX_GAP + recolorable = TRUE + file = "tab_border.png" + border = { 17, 18, 10, 7 } + stretch = TRUE + gap_file = "gap_top.png" + gap_border = { 0, 0, 0, 0 } + gap_start_file = "gap_top_start.png" + gap_start_border= { 2, 0, 0, 0 } + gap_end_file = "gap_top_end.png" + gap_end_border = { 0, 2, 0, 0 } + gap_side = TOP + } + image + { + function = BOX_GAP + recolorable = TRUE + file = "notebook.png" + border = { 2, 2, 2, 2 } + stretch = TRUE + gap_file = "gap_bottom.png" + gap_border = { 0, 0, 0, 0 } + gap_start_file = "gap_bottom_start.png" + gap_start_border= { 2, 0, 0, 0 } + gap_end_file = "gap_bottom_end.png" + gap_end_border = { 0, 2, 0, 0 } + gap_side = BOTTOM + } + image + { + function = BOX_GAP + recolorable = TRUE + file = "notebook.png" + border = { 2, 2, 2, 2 } + stretch = TRUE + gap_file = "gap_left.png" + gap_border = { 0, 0, 0, 0 } + gap_start_file = "gap_left_start.png" + gap_start_border= { 0, 0, 2, 0 } + gap_end_file = "gap_left_end.png" + gap_end_border = { 0, 0, 0, 2 } + gap_side = LEFT + } + image + { + function = BOX_GAP + recolorable = TRUE + file = "notebook.png" + border = { 2, 2, 2, 2 } + stretch = TRUE + gap_file = "gap_right.png" + gap_border = { 0, 0, 0, 0 } + gap_start_file = "gap_right_start.png" + gap_start_border= { 0, 0, 2, 0 } + gap_end_file = "gap_right_end.png" + gap_end_border = { 0, 0, 0, 2 } + gap_side = RIGHT + } +# +# How to draw the box of a notebook when it isnt attached to a tab +# + image + { + function = BOX + recolorable = TRUE + file = "notebook.png" + border = { 2, 2, 2, 2 } + stretch = TRUE + gap_side = TOP + } + } +} + +class "GtkNotebook" style "notebook" + +style "menu" +{ + engine "pixmap" { + image + { + function = BOX + recolorable = TRUE + file = "menu_border.png" + border = { 82, 91, 9, 10 } + stretch = TRUE +# overlay_file = "UIS{Image:Personality/MenuBorders#0,1|menu_border_overlay.png}" +# overlay_border = { 1, 1, 1, 1 } + overlay_stretch = TRUE + } + } +} + +class "GtkMenu" style "menu" + +style "menuitem" +{ + engine "pixmap" { + image + { + function = BOX + recolorable = TRUE +# file = "menuitem.png" + file = "menuitem.png" + border = { 14, 13, 4, 4 } + stretch = TRUE + } + } +} + +class "GtkMenuItem" style "menuitem" + +style "optionmenu" +{ + engine "pixmap" { + image + { + function = BOX + recolorable = TRUE + state = PRELIGHT + file = "optionmenu_rollover.png" + border = { 4, 24, 2, 4 } + stretch = TRUE + arrow = "false" + } + image + { + function = BOX + recolorable = TRUE + state = INSENSITIVE + file = "optionmenu_disabled.png" + border = { 4, 24, 2, 4 } + stretch = TRUE + arrow = "false" + } + image + { + function = BOX + recolorable = TRUE + file = "optionmenu.png" + border = { 4, 24, 2, 4 } + stretch = TRUE + arrow = "false" + } + } +} + +class "GtkOptionMenu" style "optionmenu" + +style "progressbar" +{ + engine "pixmap" + { + image + { + function = BOX + recolorable = TRUE + detail = "bar" + file = "progress.png" + border = { 2, 2, 3, 4 } + stretch = TRUE + } + image + { + function = BOX + detail = "trough" + file = "progress_track.png" + border = { 1, 1, 1, 2 } + stretch = TRUE + } + } +} + +class "GtkProgressBar" style "progressbar" + +style "ruler" +{ + engine "pixmap" { + image + { + function = BOX + recolorable = TRUE + detail = "vruler" + file = "button_inactive.png" + border = { 5, 5, 5, 5 } + stretch = TRUE + } + image + { + function = BOX + recolorable = TRUE + detail = "hruler" + file = "button_inactive.png" + border = { 5, 5, 5, 5 } + stretch = TRUE + } + } +} + +class "GtkRuler" style "ruler" + +style "item" +{ + engine "pixmap" { + image + { + function = FLAT_BOX + recolorable = TRUE + state = INSENSITIVE + file = "slider_disabled.png" + border = { 4, 4, 3, 1 } + stretch = TRUE + } + image + { + function = FLAT_BOX + recolorable = TRUE + file = "slider.png" + border = { 4, 4, 3, 1 } + stretch = TRUE + } + } +} + +class "GtkTreeItem" style "item" +class "GtkListItem" style "item" + +style "window" +{ + engine "pixmap" { + image + { + function = FLAT_BOX + recolorable = TRUE + #file = "greybg.png" + border = { 5, 5, 5, 5 } + stretch = TRUE + } + } +} + +class "GtkWindow" style "window" + +style "curve" +{ + engine "pixmap" { + image + { + function = FLAT_BOX + recolorable = TRUE + detail = "curve_bg" + file = "greybg.png" + border = { 0, 0, 0, 0 } + stretch = TRUE + } + } +} + +class "GtkCurve" style "curve" + +style "default" +{ + engine "pixmap" + { + image + { + function = BOX + recolorable = TRUE + detail = "slider" + state = PRELIGHT + file = "sliderthumb_rollover_horizontal.png" + border = { 13, 13, 0, 0 } + stretch = TRUE + overlay_file = "sliderthumb_rollover_horizontal.png" + overlay_border = { 13, 13, 0, 0 } + overlay_stretch = FALSE + orientation = HORIZONTAL + } + image + { + function = BOX + recolorable = TRUE + detail = "slider" + state = NORMAL + file = "sliderthumb_horizontal.png" + border = { 13, 13, 0, 0 } + stretch = TRUE + file = "sliderthumb_horizontal.png" + overlay_border = { 13, 13, 0, 0 } + overlay_stretch = FALSE + orientation = HORIZONTAL + } + image + { + function = BOX + recolorable = TRUE + detail = "slider" + state = PRELIGHT + file = "sliderthumb_rollover_vertical.png" + border = { 0, 0, 13, 13 } + stretch = TRUE + overlay_file = "sliderthumb_rollover_vertical.png" + overlay_border = { 0, 0, 13, 13 } + overlay_stretch = FALSE + orientation = VERTICAL + } + image + { + function = BOX + recolorable = TRUE + detail = "slider" + state = NORMAL + file = "sliderthumb_vertical.png" + border = { 0, 0, 13, 13 } + stretch = TRUE + overlay_file = "sliderthumb_vertical.png" + overlay_border = { 0, 0, 13, 13 } + overlay_stretch = FALSE + orientation = VERTICAL + } +# +# Any trough.... +# + image + { + function = BOX + recolorable = TRUE + detail = "trough" + file = "trough.png" + border = { 4, 4, 3, 1 } + stretch = TRUE + } +# +# Selected text in entries, text widget, lists and trees +# +# image +# { +# function = FLAT_BOX +# recolorable = TRUE +# state = INSENSITIVE +# detail = "selected" +# file = "UIS{Image:SunkEdge/Image#1,4}" +# border = { 4, 4, 3, 1 } +# stretch = TRUE +# } +# image +# { +# function = FLAT_BOX +# recolorable = TRUE +# detail = "selected" +# file = "UIS{Image:SunkEdge/Image#1,4}" +# border = { 4, 4, 3, 1 } +# stretch = TRUE +# } +# image +# { +# function = FLAT_BOX +# recolorable = TRUE +# state = INSENSITIVE +# detail = "text" +# file = "UIS{Image:SunkEdge/Image#1,4}" +# border = { 4, 4, 3, 1 } +# stretch = TRUE +# } +# image +# { +# function = FLAT_BOX +# recolorable = TRUE +# detail = "text" +# file = "UIS{Image:SunkEdge/Image#1,4}" +# border = { 4, 4, 3, 1 } +# stretch = TRUE +# } + image + { + function = FLAT_BOX + recolorable = TRUE + detail = "viewportbin" + file = "greybg.png" + border = { 0, 0, 0, 0 } + stretch = TRUE + } +# +# Arrows for all occasions +# + image + { + function = ARROW + recolorable = TRUE + state = NORMAL + file = "arrow_up.png" + stretch = TRUE + border = { 5, 5, 5, 5 } + arrow_direction = UP + } + image + { + function = ARROW + recolorable = TRUE + state = PRELIGHT + file = "arrow_rollover_up.png" + stretch = TRUE + border = { 5, 5, 5, 5 } + arrow_direction = UP + } + image + { + function = ARROW + recolorable = TRUE + shadow = IN + file = "arrow_in_up.png" + stretch = TRUE + border = { 5, 5, 5, 5 } + arrow_direction = UP + } + + image + { + function = ARROW + recolorable = TRUE + state = NORMAL + file = "arrow_down.png" + stretch = TRUE + border = { 5, 5, 5, 5 } + arrow_direction = DOWN + } + image + { + function = ARROW + recolorable = TRUE + state = PRELIGHT + file = "arrow_rollover_down.png" + stretch = TRUE + border = { 5, 5, 5, 5 } + arrow_direction = DOWN + } + image + { + function = ARROW + recolorable = TRUE + shadow = IN + file = "arrow_in_down.png" + stretch = TRUE + border = { 5, 5, 5, 5 } + arrow_direction = DOWN + } + + image + { + function = ARROW + recolorable = TRUE + state = NORMAL + file = "arrow_left.png" + stretch = TRUE + border = { 5, 5, 5, 5 } + arrow_direction = LEFT + } + image + { + function = ARROW + recolorable = TRUE + state = PRELIGHT + file = "arrow_rollover_left.png" + stretch = TRUE + border = { 5, 5, 5, 5 } + arrow_direction = LEFT + } + image + { + function = ARROW + recolorable = TRUE + shadow = IN + file = "arrow_in_left.png" + stretch = TRUE + border = { 5, 5, 5, 5 } + arrow_direction = LEFT + } + + image + { + function = ARROW + recolorable = TRUE + state = NORMAL + file = "arrow_right.png" + stretch = TRUE + border = { 5, 5, 5, 5 } + arrow_direction = RIGHT + } + image + { + function = ARROW + recolorable = TRUE + state = PRELIGHT + file = "arrow_rollover_right.png" + stretch = TRUE + border = { 5, 5, 5, 5 } + + arrow_direction = RIGHT + } + image + { + function = ARROW + recolorable = TRUE + shadow = IN + file = "arrow_in_right.png" + stretch = TRUE + border = { 5, 5, 5, 5 } + arrow_direction = RIGHT + } + +# +# Hline and Vline widgets +# + image + { + function = HLINE + recolorable = TRUE + file = "hline.png" + stretch = TRUE + } + image + { + function = VLINE + recolorable = TRUE + file = "vline.png" + border = { 1, 1, 0, 0 } + stretch = TRUE + } +# +# Check marks +# + image + { + function = CHECK + recolorable = TRUE + shadow = OUT + overlay_file = "check_out.png" + overlay_stretch = FALSE + } + image + { + function = CHECK + recolorable = TRUE + shadow = IN + overlay_file = "check_in.png" + overlay_stretch = FALSE + } +# +# Option marks +# + image + { + function = OPTION + recolorable = TRUE + shadow = OUT + overlay_file = "radio_out.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + } + image + { + function = OPTION + recolorable = TRUE + shadow = IN + overlay_file = "radio_in.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + } +# +# The "tab" on the OptionMenu +# + image + { + function = TAB + recolorable = TRUE + overlay_file = "pulldown_indicator.png" + overlay_stretch = FALSE + } +# +# Fun with Scrollbars +# + image + { + function = SLIDER + recolorable = TRUE + file = "slider_horizontal.png" + border = { 1, 1, 1, 1 } + stretch = TRUE + orientation = HORIZONTAL + } + image + { + function = SLIDER + recolorable = TRUE + file = "slider_vertical.png" + border = { 1, 1, 1, 1 } + stretch = TRUE + orientation = VERTICAL + } +# +# The handle on Handleboxes +# + image + { + function = HANDLE + recolorable = TRUE + file = "menu.png" + border = { 3, 3, 3, 3 } + stretch = TRUE + overlay_file = "handle_vertical.png" + border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + orientation = VERTICAL + } + image + { + function = HANDLE + recolorable = TRUE + file = "menu.png" + border = { 2, 2, 2, 2 } + stretch = TRUE + overlay_file = "handle_horizontal.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + orientation = HORIZONTAL + } +# +# The background for entry widgets +# + image + { + function = FLAT_BOX + recolorable = TRUE + state = INSENSITIVE + detail = "entry_bg" + file = "entry.png" + stretch = TRUE + } + image + { + function = FLAT_BOX + recolorable = TRUE + detail = "entry_bg" + file = "entry.png" + stretch = TRUE + } +# +# How to draw the focus around a widget +# + #image + #{ + #function = FOCUS + #recolorable = TRUE + #overlay_file = "focus.png" + #overlay_border = { 4, 4, 4, 4 } + #overlay_stretch = TRUE + #} +# +# How to draw shadows in general +# + image + { + function = SHADOW + recolorable = TRUE + shadow = IN + file = "shadow_in.png" + border = { 2, 2, 2, 2 } + stretch = TRUE + } + image + { + function = SHADOW + recolorable = TRUE + shadow = OUT + file = "shadow_out.png" + border = { 2, 2, 2, 2 } + stretch = TRUE + } + image + { + function = SHADOW + recolorable = TRUE + shadow = ETCHED_IN + file = "frame_box_inset.png" + border = { 2, 2, 2, 2 } + stretch = TRUE + } + image + { + function = SHADOW + recolorable = TRUE + shadow = ETCHED_OUT + file = "frame_box_outset.png" + border = { 2, 2, 2, 2 } + stretch = TRUE + } +# +# How to draw shadows with a gap on one side +# + image + { + function = SHADOW_GAP + recolorable = TRUE + file = "frame_box_inset.png" + border = { 2, 2, 2, 2 } + stretch = TRUE + gap_start_file = "frame_gap_top_start.png" + gap_start_border= { 2, 0, 0, 0 } + gap_end_file = "frame_gap_top_end.png" + gap_end_border = { 0, 2, 0, 0 } + gap_side = TOP + } + image + { + function = BOX + recolorable = TRUE + file = "notebook.png" + border = { 3, 3, 3, 3 } + stretch = TRUE + } + } +} + +class "GtkWidget" style "default" + +class "GtkRange" style "range" + +style "range" +{ + engine "pixmap" { + image + { + function = SLIDER + orientation = HORIZONTAL + file = "slider_horizontal.png" + } + image + { + function = SLIDER + orientation = VERTICAL + file = "slider_vertical.png" + } + image + { + function = BOX + detail = "trough" + orientation = HORIZONTAL + file = "trough.png" + border = { 4, 4, 3, 1 } + } + image + { + function = BOX + detail = "trough" + orientation = HORIZONTAL + file = "trough.png" + border = { 4, 4, 3, 1 } + } + } +} diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/handle_horizontal.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/handle_horizontal.png Binary files differnew file mode 100644 index 0000000..b21b122 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/handle_horizontal.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/handle_vertical.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/handle_vertical.png Binary files differnew file mode 100644 index 0000000..b21b122 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/handle_vertical.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/menu_border.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/menu_border.png Binary files differnew file mode 100644 index 0000000..b1981f1 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/menu_border.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/menuitem.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/menuitem.png Binary files differnew file mode 100644 index 0000000..0a244b0 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/menuitem.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/optionmenu.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/optionmenu.png Binary files differnew file mode 100644 index 0000000..e50e24c --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/optionmenu.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/optionmenu_disabled.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/optionmenu_disabled.png Binary files differnew file mode 100644 index 0000000..9913d6d --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/optionmenu_disabled.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/optionmenu_rollover.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/optionmenu_rollover.png Binary files differnew file mode 100644 index 0000000..7c83aed --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/optionmenu_rollover.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/progress.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/progress.png Binary files differnew file mode 100644 index 0000000..34faed0 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/progress.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/progress_track.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/progress_track.png Binary files differnew file mode 100644 index 0000000..14a2757 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/progress_track.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/radio_in.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/radio_in.png Binary files differnew file mode 100644 index 0000000..b7f5f49 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/radio_in.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/radio_out.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/radio_out.png Binary files differnew file mode 100644 index 0000000..7f22605 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/radio_out.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/scrollbar_htrough.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/scrollbar_htrough.png Binary files differnew file mode 100644 index 0000000..57deb9a --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/scrollbar_htrough.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/scrollbar_vtrough.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/scrollbar_vtrough.png Binary files differnew file mode 100644 index 0000000..c095cfb --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/scrollbar_vtrough.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/slider.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/slider.png Binary files differnew file mode 100644 index 0000000..49c896d --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/slider.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/slider_disabled.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/slider_disabled.png Binary files differnew file mode 100644 index 0000000..49c896d --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/slider_disabled.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/slider_horizontal.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/slider_horizontal.png Binary files differnew file mode 100644 index 0000000..a5cc254 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/slider_horizontal.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/slider_vertical.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/slider_vertical.png Binary files differnew file mode 100644 index 0000000..5fc1f9e --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/slider_vertical.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/sliderthumb_horizontal.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/sliderthumb_horizontal.png Binary files differnew file mode 100644 index 0000000..e3537eb --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/sliderthumb_horizontal.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/sliderthumb_rollover_horizontal.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/sliderthumb_rollover_horizontal.png Binary files differnew file mode 100644 index 0000000..f23a594 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/sliderthumb_rollover_horizontal.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/sliderthumb_rollover_vertical.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/sliderthumb_rollover_vertical.png Binary files differnew file mode 100644 index 0000000..bc9d33c --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/sliderthumb_rollover_vertical.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/sliderthumb_vertical.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/sliderthumb_vertical.png Binary files differnew file mode 100644 index 0000000..bc9d33c --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/sliderthumb_vertical.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/splitpane_downarrow.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/splitpane_downarrow.png Binary files differnew file mode 100644 index 0000000..f787119 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/splitpane_downarrow.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/splitpane_hhandle.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/splitpane_hhandle.png Binary files differnew file mode 100644 index 0000000..1d6947f --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/splitpane_hhandle.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/splitpane_leftarrow.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/splitpane_leftarrow.png Binary files differnew file mode 100644 index 0000000..5fa371d --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/splitpane_leftarrow.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/splitpane_rightarrow.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/splitpane_rightarrow.png Binary files differnew file mode 100644 index 0000000..7462c08 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/splitpane_rightarrow.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/splitpane_uparrow.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/splitpane_uparrow.png Binary files differnew file mode 100644 index 0000000..bc41a5b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/splitpane_uparrow.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/splitpane_vhandle.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/splitpane_vhandle.png Binary files differnew file mode 100644 index 0000000..3599ab4 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/splitpane_vhandle.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/tab_active.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/tab_active.png Binary files differnew file mode 100644 index 0000000..a8b8d57 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/tab_active.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/tab_active_bottom.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/tab_active_bottom.png Binary files differnew file mode 100644 index 0000000..ce8487b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/tab_active_bottom.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/tab_border.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/tab_border.png Binary files differnew file mode 100644 index 0000000..5d99db8 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/tab_border.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/toggle_disabled_in.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/toggle_disabled_in.png Binary files differnew file mode 100644 index 0000000..9d5692a --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/toggle_disabled_in.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/toggle_disabled_out.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/toggle_disabled_out.png Binary files differnew file mode 100644 index 0000000..f99aabb --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/toggle_disabled_out.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/toggle_in.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/toggle_in.png Binary files differnew file mode 100644 index 0000000..53a7de6 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/toggle_in.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/toggle_out.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/toggle_out.png Binary files differnew file mode 100644 index 0000000..f2aa268 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/toggle_out.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/toggle_rollover_out.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/toggle_rollover_out.png Binary files differnew file mode 100644 index 0000000..84fd54f --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/toggle_rollover_out.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/trough.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/trough.png Binary files differnew file mode 100644 index 0000000..49c896d --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/gtk/trough.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Computer.gif b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Computer.gif Binary files differnew file mode 100644 index 0000000..0428956 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Computer.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Default.gif b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Default.gif Binary files differnew file mode 100644 index 0000000..6768828 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Default.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/DetailsView.gif b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/DetailsView.gif Binary files differnew file mode 100644 index 0000000..4011722 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/DetailsView.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Error.gif b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Error.gif Binary files differnew file mode 100644 index 0000000..f2e9de7 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Error.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/File.gif b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/File.gif Binary files differnew file mode 100644 index 0000000..0192fce --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/File.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/FloppyDrive.gif b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/FloppyDrive.gif Binary files differnew file mode 100644 index 0000000..49f8a15 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/FloppyDrive.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/HardDrive.gif b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/HardDrive.gif Binary files differnew file mode 100644 index 0000000..4539656 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/HardDrive.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/HomeFolder.gif b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/HomeFolder.gif Binary files differnew file mode 100644 index 0000000..c2d7401 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/HomeFolder.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Inform.gif b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Inform.gif Binary files differnew file mode 100644 index 0000000..648c957 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Inform.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/ListView.gif b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/ListView.gif Binary files differnew file mode 100644 index 0000000..c169842 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/ListView.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/NewFolder.gif b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/NewFolder.gif Binary files differnew file mode 100644 index 0000000..14ced04 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/NewFolder.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Question.gif b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Question.gif Binary files differnew file mode 100644 index 0000000..7db3f11 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Question.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/TreeClosed.gif b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/TreeClosed.gif Binary files differnew file mode 100644 index 0000000..f876988 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/TreeClosed.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/TreeOpen.gif b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/TreeOpen.gif Binary files differnew file mode 100644 index 0000000..dda4c83 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/TreeOpen.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/UpFolder.gif b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/UpFolder.gif Binary files differnew file mode 100644 index 0000000..babb6d1 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/UpFolder.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Warn.gif b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Warn.gif Binary files differnew file mode 100644 index 0000000..2388e0f --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Warn.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Window.gif b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Window.gif Binary files differnew file mode 100644 index 0000000..48161fe --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/icons/Window.gif diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/close.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/close.png Binary files differnew file mode 100644 index 0000000..b00390f --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/close.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/close_disabled.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/close_disabled.png Binary files differnew file mode 100644 index 0000000..5a9a411 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/close_disabled.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/close_down.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/close_down.png Binary files differnew file mode 100644 index 0000000..f4ac626 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/close_down.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/close_rollover.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/close_rollover.png Binary files differnew file mode 100644 index 0000000..468192a --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/close_rollover.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/kde.themerc b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/kde.themerc new file mode 100644 index 0000000..0d84caf --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/kde.themerc @@ -0,0 +1,90 @@ +[General] +name=Template +author=MsStylesToSkinLF +email=noone@isinnocent.com +homepage=http://www.noone.com +description=Template +version=1.0 + +[Panel] +background=background.bmp + +[Colors] +desktop=116 120 129 + +background=228 231 237 +foreground=0 0 0 + +activeBackground=228 231 237 +activeForeground=0 0 0 + +inactiveBackground=228 231 237 +inactiveForeground=116 120 129 + +activeBlend=53,99,144 +inactiveBlend=192,192,192 + +selectBackground=139 144 150 +selectForeground=255 255 255 + +contrast=7 + +windowBackground=255 255 255 +windowForeground=13 13 13 + +[Window Titlebar] +CloseButton=close.png +CloseRolloverButton=close_rollover.png +CloseDownButton=close_down.png +CloseInactiveButton=close_disabled.png + +MaximizeButton=maximize.png +MaximizeRolloverButton=maximize_rollover.png +MaximizeDownButton=maximize_down.png +MaximizeInactiveButton=maximize_disabled.png + +MenuButton=menu.xpm + +MinimizeButton=minimize.png +MinimizeRolloverButton=minimize_rollover.png +MinimizeDownButton=minimize_down.png +MinimizeInactiveButton=minimize_disabled.png + +StickyButton=sticky.xpm +StickyDownButton=stickydown.xpm + +TitlebarPixmapActive=titlebar_selected.png +TitlebarPixmapActiveBorder=80, 80, 0, 16 +TitlebarPixmapActiveLeft= +TitlebarPixmapActiveRight= +TitlebarPixmapActiveTop= +TitlebarPixmapActiveBottom= +TitlebarPixmapInactive=titlebar_unselected.png + +PixmapUnderTitleText=yes +TitleFrameShaded=no +TitleAlignment=middle +TitleAbsolutePosition=yes + +[Window Border] +shapePixmapTop= +shapePixmapBottom=window_bottom.png +shapePixmapLeft=window_left.png +shapePixmapRight=window_right.png +shapePixmapTopLeft=window_topleft.png +shapePixmapTopRight=window_topright.png +shapePixmapBottomLeft=window_bottomleft.png +shapePixmapBottomRight=window_bottomright.png + +[Window Button Layout] +ButtonA=Off +ButtonB=Off +ButtonC=Off +ButtonD=Close +ButtonE=Maximize +ButtonF=Iconify + +[Sounds] +WindowShadeDown=RollUp.wav +WindowShadeUp=RollUp.wav +WindowClose=Close.wav diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/maximize.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/maximize.png Binary files differnew file mode 100644 index 0000000..b00390f --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/maximize.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/maximize_disabled.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/maximize_disabled.png Binary files differnew file mode 100644 index 0000000..5a9a411 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/maximize_disabled.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/maximize_down.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/maximize_down.png Binary files differnew file mode 100644 index 0000000..54b4458 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/maximize_down.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/maximize_rollover.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/maximize_rollover.png Binary files differnew file mode 100644 index 0000000..d8e6e5e --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/maximize_rollover.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/minimize.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/minimize.png Binary files differnew file mode 100644 index 0000000..b00390f --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/minimize.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/minimize_disabled.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/minimize_disabled.png Binary files differnew file mode 100644 index 0000000..5a9a411 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/minimize_disabled.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/minimize_down.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/minimize_down.png Binary files differnew file mode 100644 index 0000000..d4773fd --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/minimize_down.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/minimize_rollover.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/minimize_rollover.png Binary files differnew file mode 100644 index 0000000..1dc1796 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/minimize_rollover.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/titlebar_selected.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/titlebar_selected.png Binary files differnew file mode 100644 index 0000000..2b61dd3 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/titlebar_selected.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/titlebar_unselected.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/titlebar_unselected.png Binary files differnew file mode 100644 index 0000000..cb8ac09 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/titlebar_unselected.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_bottom.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_bottom.png Binary files differnew file mode 100644 index 0000000..24ef2ed --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_bottom.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_bottomleft.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_bottomleft.png Binary files differnew file mode 100644 index 0000000..ade162b --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_bottomleft.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_bottomright.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_bottomright.png Binary files differnew file mode 100644 index 0000000..1450e8e --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_bottomright.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_left.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_left.png Binary files differnew file mode 100644 index 0000000..95c4d09 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_left.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_right.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_right.png Binary files differnew file mode 100644 index 0000000..3092cdf --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_right.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_topleft.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_topleft.png Binary files differnew file mode 100644 index 0000000..2919eb2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_topleft.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_topright.png b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_topright.png Binary files differnew file mode 100644 index 0000000..c26cb32 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/kde/window_topright.png diff --git a/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/skinlf-themepack.xml b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/skinlf-themepack.xml new file mode 100644 index 0000000..020b01e --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/themepacks/toxicthemepack/skinlf-themepack.xml @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<skinlf-themepack require="1.2.9"> + + <!-- + Information about this themepack + --> + <property name="skin.name" + value="Alluminium Alloy / Toxic"/> + <property name="skin.description" + value="Graphics and images taken from Alluminium Alloy Visual Style of Francisco Ocasio (KOL)"/> + <property name="skin.url" + value="http://www.studiotwentyeight.com"/> + + <property name="JDesktopPane.backgroundEnabled" value="false" /> + <property name="JSplitPane.alternateUI" value="true" /> + + <!-- + SkinLF custom properties + --> + <font name="Global" value="SansSerif,0,11"/> + + <!-- + UIManager properties + --> + <property name="PopupMenu.border" type="LineBorder" thickness="1" padding="2" color="#A6AAB3" /> + <property name="TextField.border" type="LineBorder" thickness="1" padding="2" color="#A6AAB3" /> + <property name="PasswordField.border" type="LineBorder" thickness="1" padding="2" color="#A6AAB3" /> + <icon name="InternalFrame.icon" value="icons/Window.gif"/> + + <icon name="FileView.directoryIcon" value="icons/TreeClosed.gif"/> + <icon name="FileView.fileIcon" value="icons/File.gif"/> + <icon name="FileView.computerIcon" value="icons/Computer.gif"/> + <icon name="FileView.hardDriveIcon" value="icons/HardDrive.gif"/> + <icon name="FileView.floppyDriveIcon" value="icons/FloppyDrive.gif"/> + + <icon name="FileChooser.newFolderIcon" value="icons/TreeClosed.gif"/> + <icon name="FileChooser.upFolderIcon" value="icons/UpFolder.gif"/> + <icon name="FileChooser.homeFolderIcon" value="icons/HomeFolder.gif"/> + + <icon name="Tree.openIcon" value="icons/TreeOpen.gif"/> + <icon name="Tree.closedIcon" value="icons/TreeClosed.gif"/> + <icon name="Tree.leafIcon" value="icons/Default.gif"/> + + <icon name="OptionPane.errorIcon" value="icons/Error.gif"/> + <icon name="OptionPane.informationIcon" value="icons/Inform.gif"/> + <icon name="OptionPane.warningIcon" value="icons/Warn.gif"/> + <icon name="OptionPane.questionIcon" value="icons/Question.gif"/> + + <!-- + Skin Definition + --> + <skin> + <skin url="gtk/gtkrc"></skin> + <skin url="kde/kde.themerc"></skin> + </skin> + +</skinlf-themepack> diff --git a/src/net/java/sip/communicator/impl/history/Activator.java b/src/net/java/sip/communicator/impl/history/Activator.java new file mode 100644 index 0000000..fab1c24 --- /dev/null +++ b/src/net/java/sip/communicator/impl/history/Activator.java @@ -0,0 +1,13 @@ +package net.java.sip.communicator.impl.history; + +import org.ungoverned.gravity.servicebinder.GenericActivator; + +/** + * Invoke "Service Binder" to parse the service XML and register + * all services. + * + * @author Alexander Pelov + */ +public class Activator extends GenericActivator { + +} diff --git a/src/net/java/sip/communicator/impl/history/DBStructSerializer.java b/src/net/java/sip/communicator/impl/history/DBStructSerializer.java new file mode 100644 index 0000000..adde8b9 --- /dev/null +++ b/src/net/java/sip/communicator/impl/history/DBStructSerializer.java @@ -0,0 +1,237 @@ +/* + * 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.history; + +import java.io.File; +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; + +import javax.xml.parsers.DocumentBuilder; + +import net.java.sip.communicator.service.history.History; +import net.java.sip.communicator.service.history.HistoryID; +import net.java.sip.communicator.service.history.records.HistoryRecordStructure; +import net.java.sip.communicator.service.history.records.TextType; +import net.java.sip.communicator.util.EnumerationBase; +import net.java.sip.communicator.util.xml.XMLUtils; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +/** + * + * @author Alexander Pelov + */ +public class DBStructSerializer { + + private HistoryServiceImpl historyService; + + public DBStructSerializer(HistoryServiceImpl historyService) { + this.historyService = historyService; + } + + public void writeHistory(File dbDatFile, History history) + throws IOException + { + DocumentBuilder builder = this.historyService.getDocumentBuilder(); + Document doc = builder.newDocument(); + + Element root = doc.createElement("dbstruct"); + root.setAttribute("version", "1.0"); + + Element structure = this.createStructureTag(doc, + history.getHistoryRecordsStructure()); + Element id = this.createIDTag(doc, history.getID()); + + root.appendChild(structure); + root.appendChild(id); + + doc.appendChild(root); + + XMLUtils.writeXML(doc, dbDatFile); + } + + private Element createIDTag(Document doc, HistoryID historyID) { + Element idroot = doc.createElement("id"); + Element current = idroot; + + String[] idelements = historyID.getID(); + for(int i = 0; i < idelements.length; i++) { + Element idnode = doc.createElement("component"); + idnode.setAttribute("value", idelements[i]); + + current.appendChild(idnode); + current = idnode; + } + + return idroot; + } + + private Element createStructureTag(Document doc, + HistoryRecordStructure recordStructure) + { + Element structure = doc.createElement("structure"); + String[] propertyNames = recordStructure.getPropertyNames(); + TextType[] propertyTypes = recordStructure.getValueTypes(); + int count = recordStructure.getPropertyCount(); + for(int i = 0; i < count; i++) { + Element property = doc.createElement("property"); + property.setAttribute("name", propertyNames[i]); + property.setAttribute("type", propertyTypes[i].toString()); + + structure.appendChild(property); + } + + return structure; + } + + /** + * This method parses an XML file, and returns a History object created + * with the information from it. The parsing is non-validating, so if a + * malformed XML is passed the results are undefined. + * The file should be with the following structure: + * + * <dbstruct version="1.0"> + * <id value="idcomponent1"> + * <id value="idcomponent2"> + * <id value="idcomponent3"/> + * </id> + * </id> + * + * <structure> + * <property name="propertyName" type="textType" /> + * <property name="propertyName" type="textType" /> + * <property name="propertyName" type="textType" /> + * </structure> + * </dbstruct> + * + * @param dbDatFile The file to be parsed. + * @return A History object corresponding to this dbstruct file. + * @throws SAXException Thrown if an error occurs during XML parsing. + * @throws IOException Thrown if an IO error occurs. + * @throws ParseException Thrown if there is error in the XML data format. + */ + public History loadHistory(File dbDatFile) + throws SAXException, IOException, ParseException + { + DocumentBuilder builder = historyService.getDocumentBuilder(); + Document doc = builder.parse(dbDatFile); + + Node root = doc.getFirstChild(); + HistoryID id = loadID(root); + HistoryRecordStructure structure = loadStructure(root); + + return new HistoryImpl(id, dbDatFile.getParentFile(), + structure, historyService); + } + + /** + * This method parses a "structure" tag and returns the corresponging + * HistoryRecordStructure. + * + * @throws ParseException Thrown if there is no structure tag. + */ + private HistoryRecordStructure loadStructure(Node root) + throws ParseException + { + Element structNode = findElement(root, "structure"); + if(structNode == null) { + throw new ParseException("There is not structure tag defined!", 0); + } + + NodeList nodes = structNode.getChildNodes(); + int count = nodes.getLength(); + + ArrayList propertyNames = new ArrayList(count); + ArrayList propertyTypes = new ArrayList(count); + + for(int i = 0; i < count; i++) { + Node node = nodes.item(i); + if(node.getNodeType() == Node.ELEMENT_NODE && + "property".equals(node.getNodeName())) + { + Element parameter = (Element) node; + String paramName = parameter.getAttribute("name"); + String paramType = parameter.getAttribute("type"); + + if(paramName == null) { + continue; + } + + TextType type = (TextType)EnumerationBase.fromString( + TextType.class, paramType); + if(type == null) { + type = HistoryRecordStructure.DEFAULT_TEXT_TYPE; + } + + propertyNames.add(paramName); + propertyTypes.add(type); + } + } + + String[] names = new String[propertyNames.size()]; + TextType[] types = new TextType[propertyTypes.size()]; + + propertyNames.toArray(names); + propertyTypes.toArray(types); + + return new HistoryRecordStructure(names, types); + } + + private HistoryID loadID(Node parent) throws ParseException { + Element idnode = findElement(parent, "id"); + ArrayList al = loadID(new ArrayList(), idnode); + + String[] id = new String[al.size()]; + al.toArray(id); + + return HistoryID.createFromID(id); + } + + private ArrayList loadID(ArrayList loadedIDs, Node parent) + throws ParseException + { + Element node = findElement(parent, "component"); + if(node != null) { + String idValue = node.getAttribute("value"); + if(idValue != null) { + loadedIDs.add(idValue); + } else { + throw new ParseException( + "There is an ID object without value.", 0); + } + } + + return loadID(loadedIDs, node); + } + + /** + * This method seraches through all children of a given node + * and returns the first with the name matching the given one. + * If no node is found, null is returned. + */ + private Element findElement(Node parent, String name) { + Element retVal = null; + NodeList nodes = parent.getChildNodes(); + int count = nodes.getLength(); + + for(int i = 0; i < count; i++) { + Node node = nodes.item(i); + if(node.getNodeType() == Node.ELEMENT_NODE && + name.equals(node.getNodeName())) + { + retVal = (Element)node; + break; + } + } + return retVal; + } +} diff --git a/src/net/java/sip/communicator/impl/history/HistoryImpl.java b/src/net/java/sip/communicator/impl/history/HistoryImpl.java new file mode 100644 index 0000000..1dea20d --- /dev/null +++ b/src/net/java/sip/communicator/impl/history/HistoryImpl.java @@ -0,0 +1,197 @@ +/* + * 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.history; + +import java.io.File; +import java.io.IOException; +import java.security.InvalidParameterException; +import java.util.Iterator; +import java.util.SortedMap; +import java.util.TreeMap; + +import javax.xml.parsers.DocumentBuilder; + +import net.java.sip.communicator.service.history.History; +import net.java.sip.communicator.service.history.HistoryID; +import net.java.sip.communicator.service.history.HistoryReader; +import net.java.sip.communicator.service.history.HistoryWriter; +import net.java.sip.communicator.service.history.records.HistoryRecordStructure; +import net.java.sip.communicator.util.Assert; +import net.java.sip.communicator.util.Logger; +import net.java.sip.communicator.util.xml.XMLUtils; + +import org.w3c.dom.Document; + +/** + * @author Alexander Pelov + */ +public class HistoryImpl implements History { + + private static Logger log = Logger.getLogger(HistoryImpl.class); + + private HistoryID id; + private HistoryRecordStructure historyRecordStructure; + private HistoryServiceImpl historyServiceImpl; + + private File directory; + private HistoryReader reader; + private HistoryWriter writer; + + private SortedMap historyDocuments = new TreeMap(); + + protected HistoryImpl( + HistoryID id, + File directory, + HistoryRecordStructure historyRecordStructure, + HistoryServiceImpl historyServiceImpl) + { + try { + log.logEntry(); + + Assert.assertNonNull(historyServiceImpl, "The historyServiceImpl should be non-null."); + Assert.assertNonNull(id, "The ID should be non-null."); + Assert.assertNonNull(historyRecordStructure, "The structure should be non-null."); + + this.id = id; + this.directory = directory; + this.historyServiceImpl = historyServiceImpl; + this.historyRecordStructure = historyRecordStructure; + this.reader = null; + this.writer = null; + + this.reloadDocumentList(); + } finally { + log.logExit(); + } + } + + public HistoryID getID() { + return this.id; + } + + public HistoryRecordStructure getHistoryRecordsStructure() { + return this.historyRecordStructure; + } + + public HistoryReader getReader() { + if(this.reader == null) { + this.reader = new HistoryReaderImpl(this); + } + + return this.reader; + } + + public HistoryWriter getWriter() { + if(this.writer == null) { + this.writer = new HistoryWriterImpl(this); + } + + return this.writer; + } + + protected HistoryServiceImpl getHistoryServiceImpl() { + return this.historyServiceImpl; + } + + private void reloadDocumentList() { + synchronized(this.historyDocuments) { + this.historyDocuments.clear(); + + File[] files = this.directory.listFiles(); + Assert.assertNonNull(files, "The list of files should be non-null."); + + for(int i = 0; i < files.length; i++) { + if(!files[i].isDirectory()) { + this.historyDocuments.put(files[i].getName(), files[i]); + } + } + } + } + + + protected Document createDocument(String filename) { + Document retVal = null; + + synchronized(this.historyDocuments) { + if(this.historyDocuments.containsKey(filename)) { + retVal = getDocumentForFile(filename); + } else { + retVal = this.historyServiceImpl.getDocumentBuilder().newDocument(); + retVal.appendChild(retVal.createElement("history")); + + this.historyDocuments.put(filename, retVal); + } + } + + return retVal; + } + + protected void writeFile(String filename) + throws InvalidParameterException, IOException + { + File file = new File(this.directory, filename); + + synchronized(this.historyDocuments) { + if(!this.historyDocuments.containsKey(filename)) { + throw new InvalidParameterException("The requested " + + "filename does not exist in the document list."); + } + + Object obj = this.historyDocuments.get(filename); + if(obj instanceof Document) { + Document doc = (Document)obj; + + synchronized(doc) { + XMLUtils.writeXML(doc, file); + } + } + } + } + + protected Iterator getFileList() { + return this.historyDocuments.keySet().iterator(); + } + + protected Document getDocumentForFile(String filename) + throws InvalidParameterException, RuntimeException + { + Document retVal = null; + + synchronized(this.historyDocuments) { + if(!this.historyDocuments.containsKey(filename)) { + throw new InvalidParameterException("The requested " + + "filename does not exist in the document list."); + } + + Object obj = this.historyDocuments.get(filename); + if(obj instanceof Document) { + // Document already loaded. Use it directly + retVal = (Document)obj; + } else if(obj instanceof File) { + File file = (File)obj; + + DocumentBuilder builder = this.historyServiceImpl.getDocumentBuilder(); + + try { + retVal = builder.parse(file); + } catch (Exception e) { + throw new RuntimeException("Error occured while " + + "parsing XML document.", e); + } + + // Cache the loaded document for reuse + this.historyDocuments.put(filename, retVal); + } else { + Assert.fail("Internal error - the data type " + + "should be either Document or File."); + } + } + + return retVal; + } + +} diff --git a/src/net/java/sip/communicator/impl/history/HistoryReaderImpl.java b/src/net/java/sip/communicator/impl/history/HistoryReaderImpl.java new file mode 100644 index 0000000..dc801d4 --- /dev/null +++ b/src/net/java/sip/communicator/impl/history/HistoryReaderImpl.java @@ -0,0 +1,163 @@ +/* + * 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.history; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; + +import net.java.sip.communicator.service.history.HistoryReader; +import net.java.sip.communicator.service.history.QueryResultSet; +import net.java.sip.communicator.service.history.DefaultQueryResultSet; +import net.java.sip.communicator.service.history.records.HistoryRecord; +import net.java.sip.communicator.util.BidirectionalIterator; + +import org.jaxen.JaxenException; +import org.jaxen.Navigator; +import org.jaxen.XPath; +import org.jaxen.dom.DocumentNavigator; +import org.jaxen.saxpath.SAXPathException; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * @author Alexander Pelov + */ +public class HistoryReaderImpl implements HistoryReader { + + private HistoryImpl historyImpl; + + protected HistoryReaderImpl(HistoryImpl historyImpl) { + this.historyImpl = historyImpl; + } + + public QueryResultSet findByStartDate(Date startDate) throws RuntimeException { + String expr = "/history/record[@timestamp>"+startDate.getTime()+"]"; + + return this.findByXpath(expr); + } + + public QueryResultSet findByEndDate(Date endDate) throws RuntimeException { + String expr = "/history/record[@timestamp<"+endDate.getTime()+"]"; + + return this.findByXpath(expr); + } + + public QueryResultSet findByPeriod(Date startDate, Date endDate) + throws RuntimeException { + String expr = "/history/record[@timestamp>"+startDate.getTime()+"]" + + "[@timestamp<"+endDate.getTime()+"]"; + + return this.findByXpath(expr); + } + + public QueryResultSet findByKeyword(String keyword) throws RuntimeException { + return findByKeywords(new String[] {keyword}); + } + + public QueryResultSet findByKeywords(String[] keywords) + throws RuntimeException { + + String expr = "/history/record"; + for(int i = 0; i < keywords.length; i++) { + expr += "[contains(*/text(),'"+keywords[i]+"')]"; + } + + return this.findByXpath(expr); + } + + public QueryResultSet findByText(Date startDate, Date endDate, + String[] keywords) throws UnsupportedOperationException + { + String expr = "/history/record[@timestamp>"+startDate.getTime()+"]" + + "[@timestamp<"+endDate.getTime()+"]"; + for(int i = 0; i < keywords.length; i++) { + expr += "[contains(*/text(),'"+keywords[i]+"')]"; + } + + return this.findByXpath(expr); + } + + public BidirectionalIterator bidirectionalIterator() { + String expr = "/history/record"; + + return this.findByXpath(expr); + } + + public Iterator iterator() { + return this.bidirectionalIterator(); + } + + + private QueryResultSet findByXpath(String xpathExpression) { + Vector vect = new Vector(); + + Iterator filelist = this.historyImpl.getFileList(); + + Navigator navigator = DocumentNavigator.getInstance(); + XPath xpath; + + try { + xpath = navigator.parseXPath(xpathExpression); + } catch (SAXPathException e) { + throw new RuntimeException(e); + } + + while(filelist.hasNext()) { + String filename = (String)filelist.next(); + Document doc = this.historyImpl.getDocumentForFile(filename); + + List nodes; + try { + nodes = xpath.selectNodes(doc); + } catch (JaxenException e) { + throw new RuntimeException(e); + } + + Iterator i = nodes.iterator(); + while(i.hasNext()) { + Node node = (Node)i.next(); + NodeList propertyNodes = node.getChildNodes(); + + String ts = node.getAttributes().getNamedItem("timestamp").getNodeValue(); + Date timestamp = new Date(Long.parseLong(ts)); + + ArrayList nameVals = new ArrayList(); + + int len = propertyNodes.getLength(); + for(int j = 0; j < len; j++) { + Node propertyNode = propertyNodes.item(j); + if(propertyNode.getNodeType() == Node.ELEMENT_NODE) { + nameVals.add(propertyNode.getNodeName()); + // Get nested TEXT node's value + nameVals.add(propertyNode.getFirstChild().getNodeValue()); + } + } + + String[] propertyNames = new String[nameVals.size()/2]; + String[] propertyValues = new String[propertyNames.length]; + for(int j = 0; j < propertyNames.length; j++) { + propertyNames[j] = (String)nameVals.get(j*2); + propertyValues[j] = (String)nameVals.get(j*2+1); + } + + HistoryRecord record = new HistoryRecord( + propertyNames, + propertyValues, + timestamp); + + vect.add(record); + } + } + + return new DefaultQueryResultSet(vect); + + } +} diff --git a/src/net/java/sip/communicator/impl/history/HistoryServiceImpl.java b/src/net/java/sip/communicator/impl/history/HistoryServiceImpl.java new file mode 100644 index 0000000..946aa9f --- /dev/null +++ b/src/net/java/sip/communicator/impl/history/HistoryServiceImpl.java @@ -0,0 +1,255 @@ +/* + * 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.history; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Vector; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import net.java.sip.communicator.service.configuration.ConfigurationService; +import net.java.sip.communicator.service.history.History; +import net.java.sip.communicator.service.history.HistoryID; +import net.java.sip.communicator.service.history.HistoryService; +import net.java.sip.communicator.service.history.records.HistoryRecordStructure; +import net.java.sip.communicator.service.resources.FileAccessService; +import net.java.sip.communicator.util.Logger; + +/** + * @author Alexander Pelov + */ +public class HistoryServiceImpl implements HistoryService { + + public static final String DATA_DIRECTORY = "history_ver1.0"; + + public static final String DATA_FILE = "dbstruct.dat"; + + /** + * The logger for this class. + */ + private static Logger log = Logger + .getLogger(HistoryServiceImpl.class); + + private Map histories = Collections.synchronizedMap(new HashMap()); + + private Collection loadedFiles = Collections.synchronizedCollection( + new Vector()); + + private ConfigurationService configurationService; + + private FileAccessService fileAccessService; + + private Object syncRoot_FileAccess = new Object(); + + private Object syncRoot_Config = new Object(); + + private DocumentBuilder builder; + + + public HistoryServiceImpl() + throws Exception + { + DocumentBuilderFactory factory = + DocumentBuilderFactory.newInstance(); + this.builder = factory.newDocumentBuilder(); + } + + public Iterator getExistingIDs() { + Vector vect = new Vector(); + File histDir = null; + try { + histDir = this.fileAccessService.getPrivatePersistentDirectory + (DATA_DIRECTORY); + findDatFiles(vect, histDir); + } catch (Exception e) { + log.error("Error opening directory", e); + } + + DBStructSerializer structParse = new DBStructSerializer(this); + int size = vect.size(); + for(int i = 0; i < size; i++) { + File f = (File)vect.get(i); + + synchronized(this.loadedFiles) { + if(!this.loadedFiles.contains(f)) { + synchronized(this.histories) { + try { + History hist = structParse.loadHistory(f); + if(!this.histories.containsKey(hist.getID())) { + this.histories.put(hist.getID(), hist); + } + } catch(Exception e) { + log.error("Could not load history from file: " + + f.getAbsolutePath(), e); + } + } + } + } + } + + synchronized(this.histories) { + return this.histories.keySet().iterator(); + } + } + + public boolean isHistoryExisting(HistoryID id) { + synchronized (this.histories) { + return this.histories.containsKey(id); + } + } + + public History getHistory(HistoryID id) throws IllegalArgumentException { + History retVal = null; + + synchronized(histories) { + if(histories.containsKey(id)) { + retVal = (History) histories.get(id); + } else { + throw new IllegalArgumentException( + "No history corresponds to the specified ID."); + } + } + + return retVal; + } + + public History createHistory(HistoryID id, + HistoryRecordStructure recordStructure) + throws IllegalArgumentException, IOException + { + History retVal = null; + + synchronized(this.histories) { + if(this.histories.containsKey(id)) { + throw new IllegalArgumentException( + "There is already a history with the specified ID."); + } else { + File dir = this.createHistoryDirectories(id); + HistoryImpl history = new HistoryImpl(id, dir, + recordStructure, this); + + File dbDatFile = new File(dir, HistoryServiceImpl.DATA_FILE); + DBStructSerializer dbss = new DBStructSerializer(this); + dbss.writeHistory(dbDatFile, history); + + this.histories.put(id, history); + retVal = history; + } + } + + return retVal; + } + + protected FileAccessService getFileAccessService() { + return this.fileAccessService; + } + + protected DocumentBuilder getDocumentBuilder() { + return builder; + } + + private void findDatFiles(Vector vect, File directory) { + File[] files = directory.listFiles(); + for(int i = 0; i < files.length; i++) { + if(files[i].isDirectory()) { + findDatFiles(vect, files[i]); + } else if(DATA_FILE.equalsIgnoreCase(files[i].getName())) { + vect.add(files[i]); + } + } + } + + private File createHistoryDirectories(HistoryID id) throws IOException { + String[] idComponents = id.getID(); + String[] dirs = new String[idComponents.length+1]; + dirs[0] = "history"; + System.arraycopy(idComponents, 0, dirs, 1, dirs.length-1); + + File directory = null; + try { + directory = this.fileAccessService + .getPrivatePersistentDirectory(dirs); + } catch (Exception e) { + throw (IOException)new IOException( + "Could not create history due to file system error") + .initCause(e); + } + + if(!directory.exists() && !directory.mkdirs()) { + throw new IOException( + "Could not create requested history service files:" + + directory.getAbsolutePath()); + } + + return directory; + } + + //////////////////////////////////////////////////////////////////////////// + /** + * Set the configuration service. + * + * @param configurationService + */ + public void setConfigurationService( + ConfigurationService configurationService) + { + synchronized(this.syncRoot_Config) { + this.configurationService = configurationService; + log.debug("New configuration service registered."); + } + } + + /** + * Remove a configuration service. + * + * @param configurationService + */ + public void unsetConfigurationService( + ConfigurationService configurationService) + { + synchronized(this.syncRoot_Config) { + if(this.configurationService == configurationService) { + this.configurationService = null; + log.debug("Configuration service unregistered."); + } + } + } + + /** + * Set the file access service. + * + * @param fileAccessService + */ + public void setFileAccessService(FileAccessService fileAccessService) { + synchronized(this.syncRoot_FileAccess) { + this.fileAccessService = fileAccessService; + log.debug("New file access service registered."); + } + } + + /** + * Remove the file access service. + * + * @param fileAccessService + */ + public void unsetFileAccessService(FileAccessService fileAccessService) { + synchronized(this.syncRoot_FileAccess) { + if(this.fileAccessService == fileAccessService) { + this.fileAccessService = null; + log.debug("File access service unregistered."); + } + } + } + +} diff --git a/src/net/java/sip/communicator/impl/history/HistoryWriterImpl.java b/src/net/java/sip/communicator/impl/history/HistoryWriterImpl.java new file mode 100644 index 0000000..37828d2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/history/HistoryWriterImpl.java @@ -0,0 +1,134 @@ +/* + * 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.history; + +import java.io.IOException; +import java.security.InvalidParameterException; +import java.util.Date; +import java.util.Iterator; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.Text; + +import net.java.sip.communicator.service.history.HistoryWriter; +import net.java.sip.communicator.service.history.records.HistoryRecord; +import net.java.sip.communicator.service.history.records.HistoryRecordStructure; +import net.java.sip.communicator.util.Assert; + +/** + * @author Alexander Pelov + */ +public class HistoryWriterImpl implements HistoryWriter { + + public static final int MAX_RECORDS_PER_FILE = 100; + + private Object docCreateLock = new Object(); + private Object docWriteLock = new Object(); + + private HistoryImpl historyImpl; + private String[] structPropertyNames; + private Document currentDoc = null; + private String currentFile = null; + private int currentDocElements = -1; + + protected HistoryWriterImpl(HistoryImpl historyImpl) { + this.historyImpl = historyImpl; + + HistoryRecordStructure struct = + this.historyImpl.getHistoryRecordsStructure(); + this.structPropertyNames = struct.getPropertyNames(); + } + + public void addRecord(HistoryRecord record) throws IOException { + this.addRecord(record.getPropertyNames(), record.getPropertyValues(), + record.getTimestamp()); + } + + public void addRecord(String[] propertyValues) throws IOException { + this.addRecord(structPropertyNames, propertyValues, new Date()); + } + + private void addRecord(String[] propertyNames, String[] propertyValues, Date date) + throws InvalidParameterException, IOException + { + // Synchronized to assure that two concurent threads can insert records safely. + synchronized(docCreateLock) { + if(this.currentDoc == null || this.currentDocElements > MAX_RECORDS_PER_FILE) { + this.createNewDoc(date, this.currentDoc == null); + } + } + + synchronized(this.currentDoc) { + Node root = this.currentDoc.getFirstChild(); + synchronized(root) { + Element elem = this.currentDoc.createElement("record"); + elem.setAttribute("timestamp", Long.toString(date.getTime())); + + for(int i = 0; i < propertyNames.length; i++) { + Element propertyElement = this.currentDoc.createElement(propertyNames[i]); + + Text value = this.currentDoc.createTextNode(propertyValues[i]); + propertyElement.appendChild(value); + + elem.appendChild(propertyElement); + } + + root.appendChild(elem); + this.currentDocElements++; + } + } + + // write changes + synchronized(docWriteLock) { + this.historyImpl.writeFile(this.currentFile); + } + } + + /** + * If no file is currently loaded loads the last opened file. If + * it does not exists or if the current file was set - create a + * new file. + * + * @param date + */ + private void createNewDoc(Date date, boolean loadLastFile) { + boolean loaded = false; + + if(loadLastFile) { + Iterator files = historyImpl.getFileList(); + + String file = null; + while(files.hasNext()) { + file = (String)files.next(); + } + + if(file != null) { + this.currentDoc = this.historyImpl.getDocumentForFile(file); + this.currentFile = file; + loaded = true; + } + } + + if(!loaded) { + this.currentFile = Long.toString(date.getTime()); + while(this.currentFile.length() < 8) { + this.currentFile = "0" + this.currentFile; + } + this.currentFile += ".xml"; + + this.currentDoc = this.historyImpl.createDocument(this.currentFile); + } + + Assert.assertNonNull(this.currentDoc, + "There should be a current document created."); + + this.currentDocElements = this.currentDoc.getFirstChild().getChildNodes().getLength(); + } + +} diff --git a/src/net/java/sip/communicator/impl/history/history.manifest.mf b/src/net/java/sip/communicator/impl/history/history.manifest.mf new file mode 100644 index 0000000..3b48970 --- /dev/null +++ b/src/net/java/sip/communicator/impl/history/history.manifest.mf @@ -0,0 +1,33 @@ +Bundle-Activator: net.java.sip.communicator.impl.history.Activator +Bundle-Name: History Service Provider +Bundle-Description: A bundle that implements the history package. +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +Import-Package: org.ungoverned.gravity.servicebinder, + org.osgi.framework, + net.java.sip.communicator.service.protocol, + net.java.sip.communicator.service.configuration, + net.java.sip.communicator.service.resources, + net.java.sip.communicator.service.history, + net.java.sip.communicator.impl.history, + net.java.sip.communicator.service.history.query, + net.java.sip.communicator.service.history.records, + net.java.sip.communicator.service.protocol, + org.w3c.dom, + org.xml.sax, + javax.xml.parsers, + org.apache.xml.serializer, + javax.xml.transform, + javax.xml.transform.dom, + javax.xml.transform.stream, + net.java.sip.communicator.util, + net.java.sip.communicator.util.xml, + org.jaxen, + org.jaxen.dom, + org.jaxen.saxpath, +Export-Package: net.java.sip.communicator.service.history, + net.java.sip.communicator.impl.history, + net.java.sip.communicator.service.history.query, + net.java.sip.communicator.service.history.records, + net.java.sip.communicator.service.protocol, +Metadata-Location: /net/java/sip/communicator/impl/history/history.metadata.xml
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/history/history.metadata.xml b/src/net/java/sip/communicator/impl/history/history.metadata.xml new file mode 100644 index 0000000..96f3d65 --- /dev/null +++ b/src/net/java/sip/communicator/impl/history/history.metadata.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<bundle> + <component class="net.java.sip.communicator.impl.history.HistoryServiceImpl"> + <provides service="net.java.sip.communicator.service.history.HistoryService"/> + + <requires service="net.java.sip.communicator.service.configuration.ConfigurationService" + filter="" + policy="static" + cardinality="1..1" + bind-method="setConfigurationService" + unbind-method="unsetConfigurationService" /> + + <requires service="net.java.sip.communicator.service.resources.FileAccessService" + filter="" + policy="static" + cardinality="1..1" + bind-method="setFileAccessService" + unbind-method="unsetFileAccessService" /> + + </component> +</bundle> diff --git a/src/net/java/sip/communicator/impl/media/Activator.java b/src/net/java/sip/communicator/impl/media/Activator.java new file mode 100644 index 0000000..7f56273 --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/Activator.java @@ -0,0 +1,19 @@ +/* + * 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.media; + +import org.ungoverned.gravity.servicebinder.GenericActivator; + +/** + * Invoke "Service Binder" to parse the service XML and register + * all services. + * + * @author Martin Andre + */ +public class Activator extends GenericActivator { + +} diff --git a/src/net/java/sip/communicator/impl/media/MediaControl.java b/src/net/java/sip/communicator/impl/media/MediaControl.java new file mode 100644 index 0000000..134b724 --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/MediaControl.java @@ -0,0 +1,144 @@ +/* + * 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.media; + +import java.io.IOException; + +import javax.media.CaptureDeviceInfo; +import javax.media.IncompatibleSourceException; +import javax.media.Manager; +import javax.media.MediaLocator; +import javax.media.NoDataSourceException; +import javax.media.protocol.CaptureDevice; +import javax.media.protocol.DataSource; + +import net.java.sip.communicator.impl.media.configuration.MediaConfiguration; +import net.java.sip.communicator.util.Logger; + + +/** + * This class is intended to provide a generic way to control media package. + * + * @author Martin Andre + */ +public class MediaControl +{ + private Logger logger = Logger.getLogger(MediaConfiguration.class); + + /** + * Our configuration helper. + */ + private MediaConfiguration mediaConfiguration = null; + + /** + * Capture devices + */ + private CaptureDevice audioCaptureDevice = null; + private CaptureDevice videoCaptureDevice = null; + private DataSource avDataSource = null; + + public MediaControl(MediaConfiguration mediaConfiguration) + { + this.mediaConfiguration = mediaConfiguration; + } + + protected void openCaptureDevices() + { + // Init Capture devices + DataSource audioDataSource = null; + DataSource videoDataSource = null; + CaptureDeviceInfo audioDeviceInfo = null; + CaptureDeviceInfo videoDeviceInfo = null; + + // audio device + audioDeviceInfo = mediaConfiguration.getAudioCaptureDevice(); + if (audioDeviceInfo != null) { + audioDataSource = createDataSource(audioDeviceInfo.getLocator()); + audioCaptureDevice = (CaptureDevice) audioDataSource; + } + + // video device + videoDeviceInfo = mediaConfiguration.getVideoCaptureDevice(); + if (videoDeviceInfo != null) { + videoDataSource = createDataSource(videoDeviceInfo.getLocator()); + videoCaptureDevice = (CaptureDevice) videoDataSource; + } + + // Create the av data source + if (audioDataSource != null && videoDataSource != null) { + DataSource[] allDS = new DataSource[] { + audioDataSource, + videoDataSource + }; + try { + avDataSource = Manager.createMergingDataSource(allDS); + } + catch (IncompatibleSourceException exc) { + System.out.println( + "Failed to create a media data source!" + + "Media transmission won't be enabled!"); + } + } + else { + if (audioDataSource != null) { + avDataSource = audioDataSource; + } + if (videoDataSource != null) { + avDataSource = videoDataSource; + } + } + + // avDataSource may be null + } + + protected void closeCaptureDevices() + { + try { + avDataSource.stop(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + protected DataSource getDataSource() { + return avDataSource; + } + + protected DataSource createDataSource(MediaLocator locator) + { + try { + logger.info("Creating datasource for:" + + locator != null + ? locator.toExternalForm() + : "null"); + return Manager.createDataSource(locator); + } + catch (NoDataSourceException ex) { + // The failure only concens us + logger.error("Could not create data source for " + + locator.toExternalForm()); + return null; + } + catch (IOException ex) { + // The failure only concens us + logger.error("Could not create data source for " + + locator.toExternalForm()); + return null; + } + } + + protected void startCapture(CaptureDevice captureDevice) + { + + } + + protected void stopCapture(CaptureDevice captureDevice) + { + + } +} diff --git a/src/net/java/sip/communicator/impl/media/MediaDispatcher.java b/src/net/java/sip/communicator/impl/media/MediaDispatcher.java new file mode 100644 index 0000000..1bf5489 --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/MediaDispatcher.java @@ -0,0 +1,119 @@ +/* + * 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.media; + +import java.util.*; + +import net.java.sip.communicator.impl.configuration.ChangeEventDispatcher; +import net.java.sip.communicator.service.configuration.event.VetoableChangeListener; +import net.java.sip.communicator.service.media.event.*; +import net.java.sip.communicator.service.media.*; + +/** + * This is a utility class that can be used by objects that support constrained + * properties. You can use an instance of this class as a member field + * of your bean and delegate various work to it. + * + * This class is serializable. When it is serialized it will save + * (and restore) any listeners that are themselves serializable. Any + * non-serializable listeners will be skipped during serialization. + * + * @author Martin Andre + */ +public class MediaDispatcher +{ + + /** + * All media listeners registered so far. + */ + private Vector mediaListeners; + + + public MediaDispatcher() {} + + /** + * Add a mediaListener to the listener list. + * + * @param listener The MediaListener to be added + */ + protected synchronized void addMediaListener(MediaListener listener) + { + if (mediaListeners == null) + { + mediaListeners = new Vector(); + } + + mediaListeners.addElement(listener); + } + + /** + * Remove a MediaListener from the listener list. + * + * @param listener The MediaListener to be removed + */ + protected synchronized void removeMediaListener(MediaListener listener) + { + if (mediaListeners == null) + { + return; + } + mediaListeners.removeElement(listener); + } + + /** + * Alert all media listeners that we're receiving a media stream. + * @param mediaEvent the source of the event + */ + protected void fireReceivedMediaStream(MediaEvent mediaEvent) + { + Vector targets = null; + synchronized (this) + { + if (mediaListeners != null) + { + targets = (Vector) mediaListeners.clone(); + } + } + + if (targets != null) + { + for (int i = 0; i < targets.size(); i++) + { + MediaListener target = + (MediaListener) targets.elementAt(i); + + target.receivedMediaStream(mediaEvent); + } + } + } + + /** + * Alert all media listeners that status has changed. + */ + protected void fireMediaServiceStatusChanged() + { + Vector targets = null; + synchronized (this) + { + if (mediaListeners != null) + { + targets = (Vector) mediaListeners.clone(); + } + } + + if (targets != null) + { + for (int i = 0; i < targets.size(); i++) + { + MediaListener target = + (MediaListener) targets.elementAt(i); + + target.mediaServiceStatusChanged(); + } + } + } +} diff --git a/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java b/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java new file mode 100644 index 0000000..d4a8e51 --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java @@ -0,0 +1,200 @@ +/* + * 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.media; + + +import javax.media.Player; +import javax.sdp.SdpFactory; +import javax.swing.JPanel; + +import com.sun.media.protocol.DataSource; + +import net.java.sip.communicator.impl.media.configuration.MediaConfiguration; +import net.java.sip.communicator.service.configuration.ConfigurationService; +import net.java.sip.communicator.service.media.MediaService; +import net.java.sip.communicator.service.media.event.MediaEvent; +import net.java.sip.communicator.service.media.event.MediaListener; +import net.java.sip.communicator.service.protocol.CallParticipant; +import net.java.sip.communicator.util.Logger; + + + +/** + * The service is meant to be a wrapper of media libraries such as JMF, + * (J)FFMPEG, JMFPAPI, and others. It takes care of all media play and capture + * as well as media transport (e.g. over RTP). + * + * Before being able to use this service calles would have to make sure that + * it is initialized (i.e. consult the isInitialized() method). + * + * @author Martin Andre + */ +public class MediaServiceImpl + implements MediaService +{ + private Logger logger = Logger.getLogger(MediaServiceImpl.class); + + private SdpFactory sdpFactory; + private boolean isInitialized = false; + + private Player player = null; + private JPanel videoPanel = null; + + /** + * Our event dispatcher. + */ + private MediaDispatcher mediaDispatcher = new MediaDispatcher(); + + /** + * Our configuration helper. + */ + private MediaConfiguration mediaConfiguration = new MediaConfiguration(); + + /** + * Our media control helper. + */ + private MediaControl mediaControl = new MediaControl(mediaConfiguration); + + /** + * Default constructor + */ + public MediaServiceImpl() { + } + + /** + * Set the configuration service. + * + * @param configurationService + */ + public void setConfigurationService(ConfigurationService configurationService) { + mediaConfiguration.setConfigurationService(configurationService); + } + + /** + * Remove a configuration service. + * + * @param configurationService + */ + public void unsetConfigurationService(ConfigurationService configurationService) { + mediaConfiguration.unsetConfigurationService(configurationService); + } + + /** + * The method is meant for use by protocol service implementations when + * willing to send an invitation to a remote callee. It is at that point + * that the media service would open a port where it would be waiting for + * data coming from the specified call participant. Subsequent sdpoffers + * requested for the call that the original call participant belonged to, + * would receive, the same IP/port couple as the first one in order to allow + * conferencing. The associated port will be released once the call has + * ended. See RFC3264 for details on Offer/Answer model with SDP. + * + * @param callParticipant the call participant meant to receive the offer + * @return a String containing an SDP offer. + */ + public String generateSdpOffer(CallParticipant callParticipant) + { + try + { + logger.logEntry(); + } + finally + { + logger.logExit(); + } + return null; + } + + /** + * The method is meant for use by protocol service implementations when + * willing to respond to an invitation received from a remote caller. It is + * at that point that the media service would open a port where it would + * wait for data coming from the specified call participant. Subsequent sdp + * offers/answers requested for the call that the original call participant + * belonged to will receive the same IP/port couple as the first one in + * order to allow conferencing. The associated port will be released once + * the call has ended. See RFC3264 for details on Offer/Answer model with SDP. + * + * @param callParticipant the call participant meant to receive the offer + * @return a String containing an SDP offer. + */ + public String generateSdpAnswer(CallParticipant callParticipant) + { + try + { + logger.logEntry(); + } + finally + { + logger.logExit(); + } + return null; + } + + /** + * Adds a listener that will be listening for incoming media and changes + * in the state of the media listener + * @param listener the listener to register + */ + public void addMediaListener(MediaListener listener) { + mediaDispatcher.addMediaListener(listener); + } + + /** + * Removes a listener that was listening for incoming media and changes + * in the state of the media listener + * @param listener the listener to remove + */ + public void removeMediaListener(MediaListener listener) { + mediaDispatcher.removeMediaListener(listener); + } + + /** + * Initializes the service implementation, and puts it in a state where it + * could interoperate with other services. + */ + public void initialize() { + openCaptureDevices(); + //createPlayer(); + + videoPanel = new JPanel(); + + // Now alert mediaListeners + MediaEvent mediaEvent = new MediaEvent(videoPanel); + mediaDispatcher.fireReceivedMediaStream(mediaEvent); + + isInitialized = true; + } + + /** + * Returns true if the media service implementation is initialized and ready + * for use by other services, and false otherwise. + */ + public boolean isInitialized() { + return isInitialized; + } + + /** + * Open capture devices specified by configuration service. + */ + private void openCaptureDevices() + { + mediaControl.openCaptureDevices(); +// javax.media.protocol.DataSource dataSource = mediaControl.getDataSource(); +// dataSource.disconnect(); + } + + /** + * Makes the service implementation close all release any devices or other + * resources that it might have allocated and prepare for shutdown/garbage + * collection. + */ + public void shutdown() { + isInitialized = false; + return; + } +} diff --git a/src/net/java/sip/communicator/impl/media/configuration/ConfigurationListener.java b/src/net/java/sip/communicator/impl/media/configuration/ConfigurationListener.java new file mode 100644 index 0000000..ca209bb --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/configuration/ConfigurationListener.java @@ -0,0 +1,28 @@ +/* + * 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.media.configuration; + +import net.java.sip.communicator.service.configuration.event.PropertyChangeEvent; +import net.java.sip.communicator.service.configuration.event.PropertyChangeListener; +import net.java.sip.communicator.util.*; + + +/** + * + * @author Martin Andre + */ +public class ConfigurationListener + implements PropertyChangeListener +{ + private Logger logger = Logger.getLogger(ConfigurationListener.class); + + public void propertyChange(PropertyChangeEvent evt) { + // TODO Auto-generated method stub + + } + +} diff --git a/src/net/java/sip/communicator/impl/media/configuration/DirectSoundAuto.java b/src/net/java/sip/communicator/impl/media/configuration/DirectSoundAuto.java new file mode 100644 index 0000000..c5f9624 --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/configuration/DirectSoundAuto.java @@ -0,0 +1,112 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + * + * File based on: + * @(#)DirectSoundAuto.java 1.3 01/03/13 + * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + */ +package net.java.sip.communicator.impl.media.configuration; + +import javax.media.*; +import javax.media.protocol.*; +import javax.media.format.AudioFormat; + +import net.java.sip.communicator.util.Logger; + +import java.util.Vector; +import java.util.Enumeration; +import com.sun.media.protocol.dsound.DirectSoundStream; + +public class DirectSoundAuto { + + private static final Logger logger = Logger.getLogger(DirectSoundAuto.class); + + private static final String detectClass = "com.sun.media.protocol.dsound.DSound"; + CaptureDeviceInfo[] devices = null; + + public static void main(String[] args) { + new DirectSoundAuto(); + System.exit(0); + } + + private boolean supports(AudioFormat af) { + try { + com.sun.media.protocol.dsound.DSound ds; + ds = new com.sun.media.protocol.dsound.DSound(af, 1024); + ds.open(); + ds.close(); + } catch (Exception e) { + logger.error(e); + return false; + } + return true; + } + + public DirectSoundAuto() { + boolean supported = false; + // instance JavaSoundDetector to check is javasound's capture is availabe + try { + Class cls = Class.forName(detectClass); + supported = true; + } catch (Throwable t) { + supported = false; + // t.printStackTrace(); + } + + logger.info("DirectSound Capture Supported = " + supported); + + if (supported) { + // It's there, start to register JavaSound with CaptureDeviceManager + Vector devices = (Vector) CaptureDeviceManager.getDeviceList(null).clone(); + + // remove the old direct sound capturers + String name; + Enumeration enumeration = devices.elements(); + while (enumeration.hasMoreElements()) { + CaptureDeviceInfo cdi = (CaptureDeviceInfo) enumeration.nextElement(); + name = cdi.getName(); + if (name.startsWith(com.sun.media.protocol.dsound.DataSource.NAME)) + CaptureDeviceManager.removeDevice(cdi); + } + int LE = AudioFormat.LITTLE_ENDIAN; + int SI = AudioFormat.SIGNED; + int US = AudioFormat.UNSIGNED; + int UN = AudioFormat.NOT_SPECIFIED; + float [] Rates = new float[] { + 48000, 44100, 32000, 22050, 16000, 11025, 8000 + }; + Vector formats = new Vector(4); + for (int rateIndex = 0; rateIndex < Rates.length; rateIndex++) { + float rate = Rates[rateIndex]; + AudioFormat af; + af = new AudioFormat(AudioFormat.LINEAR, rate, 16, 2, LE, SI); + if (supports(af)) formats.addElement(af); + af = new AudioFormat(AudioFormat.LINEAR, rate, 16, 1, LE, SI); + if (supports(af)) formats.addElement(af); + af = new AudioFormat(AudioFormat.LINEAR, rate, 8, 2, UN, US); + if (supports(af)) formats.addElement(af); + af = new AudioFormat(AudioFormat.LINEAR, rate, 8, 1, UN, US); + if (supports(af)) formats.addElement(af); + } + + AudioFormat [] formatArray = new AudioFormat[formats.size()]; + for (int fa = 0; fa < formatArray.length; fa++) + formatArray[fa] = (AudioFormat) formats.elementAt(fa); + + CaptureDeviceInfo cdi = new CaptureDeviceInfo( + com.sun.media.protocol.dsound.DataSource.NAME, + new MediaLocator("dsound://"), + formatArray); + CaptureDeviceManager.addDevice(cdi); + try { + CaptureDeviceManager.commit(); + logger.info("DirectSoundAuto: Committed ok"); + } catch (java.io.IOException ioe) { + logger.error("DirectSoundAuto: error committing cdm"); + } + } + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/media/configuration/JMFInit.java b/src/net/java/sip/communicator/impl/media/configuration/JMFInit.java new file mode 100644 index 0000000..09186e5 --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/configuration/JMFInit.java @@ -0,0 +1,395 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + * + * File based on: + * @(#)JMFInit.java 1.14 03/04/30 + * Copyright (c) 1996-2002 Sun Microsystems, Inc. All rights reserved. + */ +package net.java.sip.communicator.impl.media.configuration; + +import java.io.File; +import java.io.IOException; +import java.util.Vector; + +import javax.media.Format; +import javax.media.PlugInManager; +import javax.media.Renderer; +import javax.media.format.AudioFormat; + +import net.java.sip.communicator.util.Logger; + +import com.sun.media.ExclusiveUse; + + +public class JMFInit + implements Runnable { + + private static final Logger logger = Logger.getLogger(JMFInit.class); + + public JMFInit() { + +// try { +// Registry.commit(); +// } +// catch (Exception e) { +// logger.error("Failed to commit to JMFRegistry!", e ); +// } + + + Thread detectThread = new Thread(this); + detectThread.run(); + +/* + int slept = 0; + while (!done && slept < 60 * 1000 * 2) { + try { + Thread.currentThread().sleep(500); + } + catch (InterruptedException ie) { + } + slept += 500; + } + + if (!done) { + console.error("Detection is taking too long! Aborting!"); + message("Detection is taking too long! Aborting!"); + } + + try { + Thread.currentThread().sleep(2000); + } + catch (InterruptedException ie) { + } +*/ + } + + /** + * Detect all capture devices + */ + public void run() { + detectDirectAudio(); + detectS8DirectAudio(); + detectCaptureDevices(); + } + + private void detectCaptureDevices() { + // check if JavaSound capture is available + logger.info("Looking for Audio capturer"); + Class dsauto = null; + try { + dsauto = Class.forName( + "net.java.sip.communicator.impl.media.configuration.DirectSoundAuto"); + dsauto.newInstance(); + logger.info("Finished detecting DirectSound capturer"); + } + catch (ThreadDeath td) { + throw td; + } + catch (Throwable t) { + logger.warn("DirectSound capturer detection failed!", t); + } + + Class jsauto = null; + try { + jsauto = Class.forName( + "net.java.sip.communicator.impl.media.configuration.JavaSoundAuto"); + jsauto.newInstance(); + logger.info("Finished detecting JavaSound capturer"); + } + catch (ThreadDeath td) { + throw td; + } + catch (Throwable t) { + logger.warn("JavaSound capturer detection failed!", t); + } + + // Check if VFWAuto or SunVideoAuto is available + logger.info("Looking for video capture devices"); + Class auto = null; + Class autoPlus = null; +// try { +// auto = Class.forName( +// "net.java.sip.communicator.impl.media.configuration.VFWAuto"); +// } +// catch (Exception e) { +// logger.warn("VFWAuto capturer detection failed!", e); +// } +// if (auto == null) { +// try { +// auto = Class.forName( +// "net.java.sip.communicator.impl.media.configuration.SunVideoAuto"); +// } +// catch (Exception ee) { +// logger.warn("SunVideoAuto capturer detection failed!", ee); +// } +// try { +// autoPlus = Class.forName( +// "net.java.sip.communicator.impl.media.configuration.SunVideoPlusAuto"); +// } +// catch (Exception ee) { +// logger.warn("SunVideoPlusAuto capturer detection failed!", ee); +// } +// } + if (auto == null) { + try { + auto = Class.forName( + "net.java.sip.communicator.impl.media.configuration.V4LAuto"); + } + catch (Exception ee) { + logger.warn("V4lAuto capturer detection failed!", ee); + } + } + try { + Object instance = auto.newInstance(); + if (autoPlus != null) { + Object instancePlus = autoPlus.newInstance(); + } + logger.info("Finished detecting video capture devices"); + } + catch (ThreadDeath td) { + throw td; + } + catch (Throwable t) { + logger.error("Capture device detection failed!", t); + } + } + + private void detectDirectAudio() { + Class cls; + int plType = PlugInManager.RENDERER; + String dar = "com.sun.media.renderer.audio.DirectAudioRenderer"; + try { + // Check if this is the Windows Performance Pack - hack + cls = Class.forName( + "net.java.sip.communicator.impl.media.configuration.VFWAuto"); + // Check if DS capture is supported, otherwise fail DS renderer + // since NT doesn't have capture + cls = Class.forName("com.sun.media.protocol.dsound.DSound"); + // Find the renderer class and instantiate it. + cls = Class.forName(dar); + + Renderer rend = (Renderer) cls.newInstance(); + try { + // Set the format and open the device + AudioFormat af = new AudioFormat(AudioFormat.LINEAR, + 44100, 16, 2); + rend.setInputFormat(af); + rend.open(); + Format[] inputFormats = rend.getSupportedInputFormats(); + // Register the device + PlugInManager.addPlugIn(dar, inputFormats, new Format[0], + plType); + // Move it to the top of the list + Vector rendList = + PlugInManager.getPlugInList(null, null, plType); + int listSize = rendList.size(); + if (rendList.elementAt(listSize - 1).equals(dar)) { + rendList.removeElementAt(listSize - 1); + rendList.insertElementAt(dar, 0); + PlugInManager.setPlugInList(rendList, plType); + PlugInManager.commit(); + //System.err.println("registered"); + } + rend.close(); + } + catch (Throwable t) { + //System.err.println("Error " + t); + } + } + catch (Throwable tt) { + } + } + + private void detectS8DirectAudio() { + Class cls; + int plType = PlugInManager.RENDERER; + String dar = "com.sun.media.renderer.audio.DirectAudioRenderer"; + try { + // Check if this is the solaris Performance Pack - hack + cls = Class.forName( + "net.java.sip.communicator.impl.media.configuration.SunVideoAuto"); + + // Find the renderer class and instantiate it. + cls = Class.forName(dar); + + Renderer rend = (Renderer) cls.newInstance(); + + if (rend instanceof ExclusiveUse && + ! ( (ExclusiveUse) rend).isExclusive()) { + // sol8+, DAR supports mixing + Vector rendList = PlugInManager.getPlugInList(null, null, + plType); + int listSize = rendList.size(); + boolean found = false; + String rname = null; + + for (int i = 0; i < listSize; i++) { + rname = (String) (rendList.elementAt(i)); + if (rname.equals(dar)) { // DAR is in the registry + found = true; + rendList.removeElementAt(i); + break; + } + } + + if (found) { + rendList.insertElementAt(dar, 0); + PlugInManager.setPlugInList(rendList, plType); + PlugInManager.commit(); + } + } + } + catch (Throwable tt) { + } + } + + /** + * Runs JMFInit the first time the application is started so that capture + * devices are properly detected and initialized by JMF. + */ + public static void setupJMF() + { + try + { + logger.logEntry(); + + // .jmf is the place where we store the jmf.properties file used + // by JMF. if the directory does not exist or it does not contain + // a jmf.properties file, or if the jmf.properties file has 0 length + // then this is the first time we're running and should detect capture + // devices + String homeDir = System.getProperty("user.home"); + File jmfDir = new File(homeDir, ".jmf"); + String classpath = System.getProperty("java.class.path"); + classpath += System.getProperty("path.separator") + + jmfDir.getAbsolutePath(); + System.setProperty("java.class.path", classpath); + + if (!jmfDir.exists()) + jmfDir.mkdir(); + + File jmfProperties = new File(jmfDir, "jmf.properties"); + + if (!jmfProperties.exists()) { + try { + jmfProperties.createNewFile(); + } + catch (IOException ex) { + logger.error( + "Failed to create jmf.properties - " + + jmfProperties.getAbsolutePath()); + } + } + + //if we're running on linux checkout that libjmutil.so is where it + //should be and put it there. +// runLinuxPreInstall(); + + if (jmfProperties.length() == 0) { + JMFInit init = new JMFInit(); + } + } + finally + { + logger.logExit(); + } + + } + + +// private static void runLinuxPreInstall() +// { +// try { +// logger.logEntry(); +// +// if (Utils.getProperty("os.name") == null +// || !Utils.getProperty("os.name").equalsIgnoreCase("Linux")) +// return; +// +// try { +// System.loadLibrary("jmv4l"); +// console.debug("Successfully loaded libjmv4l.so"); +// } +// catch (UnsatisfiedLinkError err) { +// console.debug("Failed to load libjmv4l.so. Will try and copy libjmutil.so", err); +// +// String destinationPathStr = Utils.getProperty("java.home") +// + File.separator + "lib" +// + File.separator + "i386"; +// String libjmutilFileStr = "libjmutil.so"; +// +// try { +// InputStream libIS = +// MediaManager.class.getClassLoader(). +// getResourceAsStream(libjmutilFileStr); +// File outFile = new File(destinationPathStr +// +File.separator + libjmutilFileStr); +// +// //Check if file is already there - Ben Asselstine +// if (outFile.exists()) { +// //if we're here then libjmutil is already where it should be +// // but yet we failed to load libjmv4l. +// //so notify log and bail out +// console.error( +// "An error occurred while trying to load JMF. This " +// +"error is probably due to a JMF installation problem. " +// +"Please copy libjmutil.so to a location contained by " +// + "$LD_LIBRARY_PATH and try again!", +// err); +// return; +// +// } +// +// outFile.createNewFile(); +// +// console.debug("jmutil"); +// +// FileOutputStream fileOS = new FileOutputStream(outFile); +// int available = libIS.available(); +// byte[] bytes = new byte[libIS.available()]; +// int read = 0; +// int i = 0; +// for (i = 0; i<available ; i++) +// { +// bytes[i] = (byte)libIS.read(); +// } +// +// console.debug("Read " + i + " bytes out of " + available ); +// +// fileOS.write(bytes, 0, bytes.length); +// console.debug("Wrote " + available + " bytes."); +// bytes = null; +// libIS.close(); +// fileOS.close(); +// } +// catch (IOException exc) { +// if( exc.getMessage() != null +// && exc.getMessage().toLowerCase().indexOf("permission denied") != -1) +// console.showError("Permission denied!", +// "Because of insufficient permissions SIP Communicator has failed " +// + "to copy a required library to\n\n\t" +// + destinationPathStr + "!\n\nPlease run the application as root or " +// + "manually copy the " +libjmutilFileStr +// + " file to the above location!\n"); +// exc.printStackTrace(); +// } +// } +// /** @todo check whether we have a permissions problem and alert the +// * user that they should be running as root */ +// catch(Throwable t) +// { +// console.debug("Error while loading"); +// } +// } +// finally { +// console.logExit(); +// } +// } + + public static void start() { + setupJMF(); + } +} diff --git a/src/net/java/sip/communicator/impl/media/configuration/JavaSoundAuto.java b/src/net/java/sip/communicator/impl/media/configuration/JavaSoundAuto.java new file mode 100644 index 0000000..3a72e58 --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/configuration/JavaSoundAuto.java @@ -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. + * + * File based on: + * @(#)JavaSoundAuto.java 1.2 01/03/13 + * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + */ +package net.java.sip.communicator.impl.media.configuration; + +import java.util.Enumeration; +import java.util.Vector; + +import javax.media.CaptureDeviceInfo; +import javax.media.CaptureDeviceManager; + +import net.java.sip.communicator.util.Logger; + +public class JavaSoundAuto { + + private static final Logger logger = Logger.getLogger(JavaSoundAuto.class); + + private static final String detectClass = + "net.java.sip.communicator.impl.media.configuration.JavaSoundDetector"; + CaptureDeviceInfo[] devices = null; + + public static void main(String[] args) { + new JavaSoundAuto(); + System.exit(0); + } + + public JavaSoundAuto() { + boolean supported = false; + // instance JavaSoundDetector to check is javasound's capture is availabe + try { + Class cls = Class.forName(detectClass); + JavaSoundDetector detect = (JavaSoundDetector)cls.newInstance(); + supported = detect.isSupported(); + } catch (Throwable t) { + supported = false; + t.printStackTrace(); + } + + logger.info("JavaSound Capture Supported = " + supported); + + if (supported) { + // It's there, start to register JavaSound with CaptureDeviceManager + Vector devices = (Vector) CaptureDeviceManager.getDeviceList(null).clone(); + + // remove the old javasound capturers + String name; + Enumeration enumeration = devices.elements(); + while (enumeration.hasMoreElements()) { + CaptureDeviceInfo cdi = (CaptureDeviceInfo) enumeration.nextElement(); + name = cdi.getName(); + if (name.startsWith("JavaSound")) + CaptureDeviceManager.removeDevice(cdi); + } + + // collect javasound capture device info from JavaSoundSourceStream + // and register them with CaptureDeviceManager + CaptureDeviceInfo[] cdi = com.sun.media.protocol.javasound.JavaSoundSourceStream.listCaptureDeviceInfo(); + if ( cdi != null ){ + for (int i = 0; i < cdi.length; i++) + CaptureDeviceManager.addDevice(cdi[i]); + try { + CaptureDeviceManager.commit(); + logger.info("JavaSoundAuto: Committed ok"); + } catch (java.io.IOException ioe) { + logger.error("JavaSoundAuto: error committing cdm"); + } + } + + } + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/media/configuration/JavaSoundDetector.java b/src/net/java/sip/communicator/impl/media/configuration/JavaSoundDetector.java new file mode 100644 index 0000000..a3ad124 --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/configuration/JavaSoundDetector.java @@ -0,0 +1,37 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + * + * File based on: + * @(#)JavaSoundDetector.java 1.2 01/03/13 + * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + */ +package net.java.sip.communicator.impl.media.configuration; + +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.DataLine; +import javax.sound.sampled.TargetDataLine; + + +public class JavaSoundDetector { + + boolean supported = false; + + public JavaSoundDetector() { + try { + DataLine.Info info = new DataLine.Info(TargetDataLine.class, + null, + AudioSystem.NOT_SPECIFIED); + supported = AudioSystem.isLineSupported(info); + } catch (Exception ex) { + supported = false; + } + } + + public boolean isSupported() { + return supported; + } +} + diff --git a/src/net/java/sip/communicator/impl/media/configuration/MediaConfiguration.java b/src/net/java/sip/communicator/impl/media/configuration/MediaConfiguration.java new file mode 100644 index 0000000..a919526 --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/configuration/MediaConfiguration.java @@ -0,0 +1,189 @@ +/* + * 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.media.configuration; + +import java.util.*; + +import javax.media.CaptureDeviceInfo; +import javax.media.CaptureDeviceManager; +import javax.media.format.AudioFormat; +import javax.media.format.VideoFormat; +import javax.media.protocol.CaptureDevice; +import javax.media.protocol.DataSource; + +import net.java.sip.communicator.service.configuration.ConfigurationService; +import net.java.sip.communicator.util.*; + + + +/** + * This class aims to provide a simple configuration interface for JMF. + * It retrieves stored configuration when started or listens to + * ConfigurationEvent for property changes and configures the JMF accordingly. + * + * @author Martin Andre + */ +public class MediaConfiguration +{ + private Logger logger = Logger.getLogger(MediaConfiguration.class); + + private Object syncRoot_Config = new Object(); + + /** + * The configuration service to use when retrieving conf property values + */ + private ConfigurationService configurationService = null; + + /** + * Our configuration listener. + */ + private ConfigurationListener configurationListener = + new ConfigurationListener(); + + /** + * Audio and Video transmission + */ + private boolean audioTransmission = true; + private boolean videoTransmission = true; + private boolean audioReception = true; + private boolean videoReception = true; + + /** + * Capture devices + */ + private CaptureDeviceInfo audioCaptureDevice = null; + private CaptureDeviceInfo videoCaptureDevice = null; +// private DataSource avDataSource = null; + + /** + * Default constructor. + */ + public MediaConfiguration() { + JMFInit.start(); + detectConfiguredCaptureDevices(); + } + + /** + * Set the configuration service. + * + * @param configurationService + */ + public void setConfigurationService(ConfigurationService configurationService) { + synchronized(this.syncRoot_Config) { + this.configurationService = configurationService; + logger.debug("New configuration service registered."); + } + // TODO add a list of proporties to listen to + this.configurationService.addPropertyChangeListener(configurationListener); + } + + /** + * Remove a configuration service. + * + * @param configurationService + */ + public void unsetConfigurationService(ConfigurationService configurationService) { + synchronized(this.syncRoot_Config) { + if (this.configurationService == configurationService) { + this.configurationService = null; + logger.debug("Configuration service unregistered."); + } + } + } + + /** + * Detects capture devices configured through JMF and disable audio + * and/or video transmission if none were found. + * Stores found devices in audioCaptureDevice and videoCaptureDevice. + */ + private void detectConfiguredCaptureDevices() + { + logger.info("Scanning for configured Audio Devices."); + Vector audioCaptureDevices = CaptureDeviceManager.getDeviceList(new + AudioFormat(AudioFormat.LINEAR, 44100, 16, 1)); + if (audioCaptureDevices.size() < 1) { + logger.error("No Audio Device was found."); + audioCaptureDevice = null; + setAudioTransmission(false); + } + else { + audioCaptureDevice = (CaptureDeviceInfo) audioCaptureDevices.get(0); + logger.info("Found " + audioCaptureDevice.getName() +" as an audio capture device."); + } + + logger.info("Scanning for configured Video Devices."); + Vector videoCaptureDevices = CaptureDeviceManager.getDeviceList(new + VideoFormat(VideoFormat.RGB)); + if (videoCaptureDevices.size() > 0) { + videoCaptureDevice = (CaptureDeviceInfo) videoCaptureDevices.get(0); + logger.info("Found " + videoCaptureDevice.getName() + " as an RGB Video Device."); + } + // no RGB camera found. And what about YUV ? + else + { + videoCaptureDevices = CaptureDeviceManager.getDeviceList(new + VideoFormat(VideoFormat.YUV)); + if (videoCaptureDevices.size() > 0) { + videoCaptureDevice = (CaptureDeviceInfo) videoCaptureDevices.get(0); + logger.info("Found " + videoCaptureDevice.getName() + " as an YUV Video Device."); + } + else { + logger.error("No Video Device was found."); + videoCaptureDevice = null; + setVideoTransmission(false); + } + } + } + + public CaptureDeviceInfo getAudioCaptureDevice() { + return audioCaptureDevice; + } + + public CaptureDeviceInfo getVideoCaptureDevice() { + return videoCaptureDevice; + } + + /** + * Enable or disable Audio stream transmission. + * @param enable whereas Audio stream transmission must be enabled or disabled + */ + protected void setAudioTransmission(boolean enable) { + logger.info(enable? "Enabling":"Disabling" + + " Audio transmission."); + this.audioTransmission = enable; + } + + /** + * Enable or disable Video stream transmission. + * @param enable whereas Video stream transmission must be enabled or disabled + */ + protected void setVideoTransmission(boolean enable) { + logger.info(enable? "Enabling":"Disabling" + + " Video transmission."); + this.videoTransmission = enable; + } + + /** + * Enable or disable Audio stream reception. + * @param enable whereas Audio stream reception must be enabled or disabled + */ + protected void setAudioReception(boolean enable) { + logger.info(enable? "Enabling":"Disabling" + + " Audio reception."); + this.audioReception = enable; + } + + /** + * Enable or disable Video stream reception. + * @param enable whereas Video stream reception must be enabled or disabled + */ + protected void setVideoReception(boolean enable) { + logger.info(enable? "Enabling":"Disabling" + + " Video reception."); + this.videoReception = enable; + } +} diff --git a/src/net/java/sip/communicator/impl/media/configuration/SunVideoAuto.java b/src/net/java/sip/communicator/impl/media/configuration/SunVideoAuto.java new file mode 100644 index 0000000..8507b4a --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/configuration/SunVideoAuto.java @@ -0,0 +1,183 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + * + * File based on: + * @(#)SunVideoAuto.java 1.8 01/03/13 + * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + */ +package net.java.sip.communicator.impl.media.configuration; + +import javax.media.*; +import javax.media.Format; +import javax.media.format.VideoFormat; +import javax.media.format.RGBFormat; + +import net.java.sip.communicator.util.Logger; + +import java.util.*; +import java.awt.*; +import java.awt.event.*; +import java.io.File; +import com.sun.media.protocol.sunvideo.*; + +public class SunVideoAuto { + + private static final Logger logger = Logger.getLogger(SunVideoAuto.class); + + private static String DEVICE_PREFIX = "/dev/rtvc"; + private static String PROTOCOL = "sunvideo"; + private static String LOCATOR_PREFIX = PROTOCOL + "://"; + CaptureDeviceInfo [] devices = null; + int currentID = -1; + + XILCapture xilCap; + + Vector formats = null; + + int [] ports = { 1, 2, 0 }; // most likely ports for a device + + int [] scales = { 2, 4, 1 }; // supported scales / sizes + + + public SunVideoAuto() { + Vector devices = (Vector) CaptureDeviceManager.getDeviceList(null).clone(); + Enumeration enumeration = devices.elements(); + while (enumeration.hasMoreElements()) { + CaptureDeviceInfo cdi = (CaptureDeviceInfo) enumeration.nextElement(); + String devName = cdi.getLocator().getProtocol(); + if (devName.equals(PROTOCOL)) + CaptureDeviceManager.removeDevice(cdi); + } + + int nDevices = 0; + for (int i = 0; i < 7; i++) { + File fl = new File(DEVICE_PREFIX + i); + if (fl.exists()) { + doDevice(i); + nDevices++; + } + } + try { + CaptureDeviceManager.commit(); + logger.info("SunVideoAuto: Committed ok"); + } catch (java.io.IOException ioe) { + logger.error("SunVideoAuto: error committing cdm"); + } + } + + private void addFormat(Format fin) { + Enumeration enumeration = formats.elements(); + while (enumeration.hasMoreElements()) { + Format f = (Format) enumeration.nextElement(); + if (f.equals(fin)) + return; + } + + //System.err.println("New format = " + fin); + formats.addElement(fin); + } + + private void doDevice(int index) { + + xilCap = new XILCapture(null); + VideoFormat vf; + formats = new Vector(); + boolean gotPort = false; + + if (!xilCap.connect(index)) { + dummyDevice(index); + return; + } + + + for (int i = 0; i < ports.length; i++) { + if (xilCap.setPort(ports[i])) { + getJpegFormats(i); + getRGBFormats(i); + } + } + xilCap.disconnect(); + + if (formats.size() > 0) + addDevice(index); + else + dummyDevice(index); + } + + + private void getRGBFormats(int index) { + if (!xilCap.setCompress("RGB")) + return; + for (int i = 0; i < scales.length; i++) { + xilCap.setScale(scales[i]); + // To get the real values, start the device + if (xilCap.start()) { + Dimension size = new Dimension(xilCap.getWidth(), + xilCap.getHeight()); + int stride = xilCap.getLineStride(); + int maxbuf = stride * size.width; + addFormat(new RGBFormat(size, maxbuf, byte[].class, + 15f, + 24, + 3, 2, 1, 3, stride, + Format.FALSE, + Format.NOT_SPECIFIED)); + } + xilCap.stop(); + } + } + + private void getJpegFormats(int index) { + if (!xilCap.setCompress("Jpeg")) + return; + for (int i = 0; i < scales.length; i++) { + xilCap.setScale(scales[i]); + // To get the real values, start the device + if (xilCap.start()) { + Dimension size = new Dimension(xilCap.getWidth(), + xilCap.getHeight()); + // approximate the max for high quality + int maxbuf = 3 * size.width * size.height; + addFormat(new VideoFormat(VideoFormat.JPEG, size, maxbuf, + byte[].class, 15f)); + } + xilCap.stop(); + } + } + + private void dummyDevice(int index) { + // Can't get to the device, use the barest formats + addFormat(new VideoFormat(VideoFormat.JPEG)); + addFormat(new RGBFormat()); + addDevice(index); + } + + + private void addDevice(int index) { + + String name = "SunVideo device " + index; + String locator = LOCATOR_PREFIX + index; + + Format [] farray = new Format[formats.size()]; + Enumeration enumeration = formats.elements(); + + int i = 0; + while (enumeration.hasMoreElements()) { + Format f = (Format) enumeration.nextElement(); + farray[i++] = f; + } + + CaptureDeviceInfo cdi = new CaptureDeviceInfo(name, + new MediaLocator(locator), farray); + CaptureDeviceManager.addDevice(cdi); + } + + public static void main(String [] args) { + SunVideoAuto a = new SunVideoAuto(); + System.exit(0); + } +} + diff --git a/src/net/java/sip/communicator/impl/media/configuration/SunVideoPlusAuto.java b/src/net/java/sip/communicator/impl/media/configuration/SunVideoPlusAuto.java new file mode 100644 index 0000000..f0faa5f --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/configuration/SunVideoPlusAuto.java @@ -0,0 +1,393 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + * + * File based on: + * @(#)SunVideoPlusAuto.java 1.6 01/03/13 + * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + */ +package net.java.sip.communicator.impl.media.configuration; + +import java.awt.Dimension; +import java.awt.Toolkit; +import java.io.File; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import javax.media.CaptureDeviceInfo; +import javax.media.CaptureDeviceManager; +import javax.media.Format; +import javax.media.Manager; +import javax.media.MediaLocator; +import javax.media.format.RGBFormat; +import javax.media.format.VideoFormat; +import javax.media.format.YUVFormat; +import javax.media.protocol.CaptureDevice; + +import com.sun.media.protocol.sunvideoplus.*; + +public class SunVideoPlusAuto { + + private static String DEVICE_PREFIX = "/dev/o1k"; + private static String PROTOCOL = "sunvideoplus"; + private static String LOCATOR_PREFIX = PROTOCOL + "://"; + + private static boolean DO_PAL = false; + + int currentID = -1; + + public SunVideoPlusAuto() { + /* + * First remove any old entries + */ + Vector devices = (Vector) CaptureDeviceManager. + getDeviceList(null).clone(); + Enumeration enumeration = devices.elements(); + while (enumeration.hasMoreElements()) { + CaptureDeviceInfo cdi = (CaptureDeviceInfo) enumeration.nextElement(); + String devName = cdi.getLocator().getProtocol(); + if (devName.equals(PROTOCOL)) + CaptureDeviceManager.removeDevice(cdi); + } + + int nDevices = 0; + for (int i = 0; i < 10; i++) { + File fl = new File(DEVICE_PREFIX + i); + if (fl.exists()) { + if (DO_PAL) { + generalDevice(i, "PAL"); + // If generating PAL, do both + // Garbage collect to release the PAL datasource + // otherwise it sometimes hangs before completing NTSC + System.gc(); + generalDevice(i, "NTSC"); + } else { + generalDevice(i, null); + } + // No longer generate specific configurations, + // let capture preview handle selection. + // doDevice(i); + nDevices++; + } + } + + try { + CaptureDeviceManager.commit(); + System.err.println("SunVideoPlusAuto: Committed ok"); + } catch (java.io.IOException ioe) { + System.err.println("SunVideoPlusAuto: error committing cdm"); + } + } + + protected void generalDevice(int id, String signal) { + // Add the general device + javax.media.protocol.DataSource dsource = null; + String url = LOCATOR_PREFIX + id; + if (signal != null) + url += "////" + signal.toLowerCase(); + try { + dsource = Manager.createDataSource(new MediaLocator(url)); + } catch (Exception ex) { + } + if (dsource != null && dsource instanceof + com.sun.media.protocol.sunvideoplus.DataSource) { + CaptureDeviceInfo cdi = ((CaptureDevice)dsource). + getCaptureDeviceInfo(); + if (cdi != null) { + String name = cdi.getName(); + if (signal == null) { + CaptureDeviceManager.addDevice(cdi); + } else { + name = cdi.getName() + " (" + signal + ")"; + CaptureDeviceManager.addDevice(new CaptureDeviceInfo(name, + cdi.getLocator(), cdi.getFormats())); + } + System.err.println("CaptureDeviceInfo = " + + name + " " + + cdi.getLocator()); + } + dsource.disconnect(); + } + } + + protected void doDevice(int id) { + currentID = id; + FormatSetup fd = new FormatSetup(currentID); + Vector cdiv = fd.getDeviceInfo(); + if (cdiv != null && cdiv.size() > 0) { + for (int i = 0; i < cdiv.size(); i++) { + CaptureDeviceInfo cdi = + (CaptureDeviceInfo) cdiv.elementAt(i); + // At the moment, the name and locator are identical + System.err.println("CaptureDeviceInfo = " + + cdi.getName()); +// System.err.println("CaptureDeviceInfo = " +// + cdi.getName() + " " +// + cdi.getLocator()); + } + } + } + + class FormatSetup { + + int id; + + boolean fullVideo = false; + boolean anyVideo = true; + + String sAnalog, sPort, sVideoFormat, sSize; + + Hashtable videoFormats = new Hashtable(); + + OPICapture opiVidCap = null; + + public FormatSetup(int id) { + this.id = id; + opiVidCap = new OPICapture(null); + if (!opiVidCap.connect(id)) { + throw new Error("Unable to connect to device"); + } + + } + + private void addVideoFormat(Format fin) { + String sVideo = sPort + "/" + sVideoFormat + "/" + + sSize + "/" + + sAnalog; + System.err.println("New format " + sVideo + " = " + fin); + videoFormats.put(sVideo, fin); + } + + public void mydispose() { + opiVidCap.disconnect(); + System.err.println("Disconnected driver"); + } + + public void doFormat() { + if (anyVideo) { + doVideoFormats(); + } + } + + public void doVideoFormats() { + if (!anyVideo) { + // add a dummy format entry + videoFormats.put("off", new VideoFormat(VideoFormat.RGB)); + } + + sAnalog = "ntsc"; + if (DO_PAL) + sAnalog = "pal"; + if (!opiVidCap.setSignal(sAnalog)) { + System.err.println("Video analog signal not recognized"); + return; + } + int port = 1; + if (!opiVidCap.setPort(port)) { + System.err.println("Video source not recognized on port"); + return; + } + sPort = "" + port; + opiVidCap.setScale(2); + sSize = "cif"; + getVideoFormats(); + } + + private void getVideoFormats() { + sVideoFormat = "h261"; + getH261Format(); + sVideoFormat = "h263"; + getH263Format(); + sVideoFormat = "jpeg"; + getJpegFormat(); + sVideoFormat = "rgb"; + getRGBFormat(); + sVideoFormat = "yuv"; + getYUVFormat(); + } + + private void getRGBFormat() { + if (!opiVidCap.setCompress("RGB")) + return; + /* + * If sizes are wanted, the only valid sizes are + * NTSC + * fcif (640 x 480) + * cif (320 x 240) + * qcif (160 x 120) + * PAL + * fcif (768 x 576) + * cif (384 x 288) + * qcif (192 x 144) + */ + Dimension size = new Dimension(opiVidCap.getWidth(), + opiVidCap.getHeight()); + addVideoFormat(new RGBFormat(size, Format.NOT_SPECIFIED, + Format.byteArray, + Format.NOT_SPECIFIED, + 16, + 0xF800, 0x7E0, 0x1F, 2, + Format.NOT_SPECIFIED, + Format.FALSE, + Format.NOT_SPECIFIED)); + } + + private void getYUVFormat() { + if (!opiVidCap.setCompress("YUV")) + return; + /* + * If sizes are wanted, the only valid sizes are + * NTSC + * fcif (640 x 480) + * cif (320 x 240) + * qcif (160 x 120) + * PAL + * fcif (768 x 576) + * cif (384 x 288) + * qcif (192 x 144) + * + * The capture stream is actually interleaved YVYU format. + * This is defined in the offset values below. + */ + Dimension size = new Dimension(opiVidCap.getWidth(), + opiVidCap.getHeight()); + addVideoFormat(new YUVFormat(size, Format.NOT_SPECIFIED, + Format.byteArray, + Format.NOT_SPECIFIED, + YUVFormat.YUV_YUYV, + Format.NOT_SPECIFIED, + Format.NOT_SPECIFIED, + 0, 3, 1)); + } + + private void getJpegFormat() { + if (!opiVidCap.setCompress("Jpeg")) + return; + /* + * If sizes are wanted, the only valid sizes are + * NTSC + * cif (320 x 240) + * qcif (160 x 120) + * PAL + * cif (384 x 288) + * qcif (192 x 144) + */ + Dimension size = new Dimension(opiVidCap.getWidth(), + opiVidCap.getHeight()); + addVideoFormat(new VideoFormat(VideoFormat.JPEG, size, + Format.NOT_SPECIFIED, + Format.byteArray, + Format.NOT_SPECIFIED)); + } + + private void getH261Format() { + if (!opiVidCap.setCompress("H261")) + return; + /* + * If sizes are wanted, the only valid sizes are + * cif (352 x 288) + * qcif (176 x 144) + */ + Dimension size = new Dimension(opiVidCap.getWidth(), + opiVidCap.getHeight()); + addVideoFormat(new VideoFormat(VideoFormat.H261, size, + Format.NOT_SPECIFIED, + Format.byteArray, + Format.NOT_SPECIFIED)); + } + + private void getH263Format() { + if (!opiVidCap.setCompress("H263")) + return; + /* + * If sizes are wanted, the only valid sizes are + * cif (352 x 288) + * qcif (176 x 144) + */ + Dimension size = new Dimension(opiVidCap.getWidth(), + opiVidCap.getHeight()); + addVideoFormat(new VideoFormat(VideoFormat.H263, size, + Format.NOT_SPECIFIED, + Format.byteArray, + Format.NOT_SPECIFIED)); + } + + + public void issueError(String err) { + System.err.println(err); + Toolkit.getDefaultToolkit().beep(); + } + + public Enumeration sortedFormats(Hashtable formats) { + Vector sorted = new Vector(); + keyloop: for (Enumeration en = formats.keys(); + en.hasMoreElements(); ) { + String key = (String) en.nextElement(); + for (int i = 0; i < sorted.size(); i++) { + if (key.compareTo((String)sorted.elementAt(i)) < 0) { + sorted.insertElementAt(key, i); + continue keyloop; + } + } + sorted.addElement(key); + } + return sorted.elements(); + } + + + public Vector getDeviceInfo() { + doFormat(); + mydispose(); + + String locatorPrefix = LOCATOR_PREFIX + id; + Vector devices = new Vector(); + if (anyVideo) { + + for (Enumeration ve = sortedFormats(videoFormats); + ve.hasMoreElements(); ) { + String vKey = (String) ve.nextElement(); + Format vForm = (VideoFormat)videoFormats.get(vKey); + Format[] farray = null; + farray = new Format[1]; + farray[0] = vForm; + String name = locatorPrefix + "/" + vKey; + CaptureDeviceInfo cdi = new CaptureDeviceInfo(name, + new MediaLocator(name), farray); + CaptureDeviceManager.addDevice(cdi); + devices.addElement(cdi); + } + } + return devices; + } + + } + + public static void setPALSignal(boolean pal) { + DO_PAL = pal; + } + + public static void main(String [] args) { + if (args.length > 0) { + if (args.length > 1) { + System.err.println( + "Usage: java SunVideoPlusAuto [ ntsc | pal ]"); + System.exit(1); + } + if (args[0].equalsIgnoreCase("ntsc")) { + SunVideoPlusAuto.setPALSignal(false); + } else if (args[0].equalsIgnoreCase("pal")) { + SunVideoPlusAuto.setPALSignal(true); + } else { + System.err.println( + "Usage: java SunVideoPlusAuto [ ntsc | pal ]"); + System.exit(1); + } + } + SunVideoPlusAuto m = new SunVideoPlusAuto(); + System.exit(0); + } +} + diff --git a/src/net/java/sip/communicator/impl/media/configuration/V4LAuto.java b/src/net/java/sip/communicator/impl/media/configuration/V4LAuto.java new file mode 100644 index 0000000..0bbe382 --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/configuration/V4LAuto.java @@ -0,0 +1,70 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + * + * File based on: + * @(#)V4LAuto.java 1.2 01/03/13 + * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + */ +package net.java.sip.communicator.impl.media.configuration; + +import java.util.Enumeration; +import java.util.Vector; + +import javax.media.CaptureDeviceInfo; +import javax.media.CaptureDeviceManager; + +import net.java.sip.communicator.util.Logger; + +import com.sun.media.protocol.v4l.V4LDeviceQuery; + +public class V4LAuto { + + private static final Logger logger = Logger.getLogger(V4LAuto.class); + + public V4LAuto() { + Vector devices = (Vector) CaptureDeviceManager.getDeviceList(null).clone(); + Enumeration enumeration = devices.elements(); + while (enumeration.hasMoreElements()) { + CaptureDeviceInfo cdi = (CaptureDeviceInfo) enumeration.nextElement(); + String name = cdi.getName(); + if (name.startsWith("v4l:")) + CaptureDeviceManager.removeDevice(cdi); + } + + autoDetect(0); +// for (int i = 0; i < 10; i++) { +// autoDetect(i); +// } + } + + protected CaptureDeviceInfo autoDetect(int cardNo) { + CaptureDeviceInfo cdi = null; + try { + cdi = new V4LDeviceQuery(cardNo); + if ( cdi != null && cdi.getFormats() != null && + cdi.getFormats().length > 0) { + // Commit it to disk. Its a new device + if (CaptureDeviceManager.addDevice(cdi)) { + logger.info("Added device " + cdi); + CaptureDeviceManager.commit(); + } + + } + } catch (Throwable t) { + logger.error("Could not add device!", t); + if (t instanceof ThreadDeath) + throw (ThreadDeath)t; + } + + return cdi; + } + + public static void main(String [] args) { + V4LAuto a = new V4LAuto(); + System.exit(0); + } +} + diff --git a/src/net/java/sip/communicator/impl/media/configuration/VFWAuto.java b/src/net/java/sip/communicator/impl/media/configuration/VFWAuto.java new file mode 100644 index 0000000..4e4bce1 --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/configuration/VFWAuto.java @@ -0,0 +1,52 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + * + * File based on: + * @(#)VFWAuto.java 1.2 01/03/13 + * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + */ +package net.java.sip.communicator.impl.media.configuration; + +import java.util.Enumeration; +import java.util.Vector; + +import javax.media.CaptureDeviceInfo; +import javax.media.CaptureDeviceManager; + +import com.sun.media.vfw.*; +import com.sun.media.util.WindowUtil; + +public class VFWAuto { + + public VFWAuto() { + Vector devices = (Vector) CaptureDeviceManager.getDeviceList(null).clone(); + Enumeration enumeration = devices.elements(); + + while (enumeration.hasMoreElements()) { + CaptureDeviceInfo cdi = (CaptureDeviceInfo) enumeration.nextElement(); + String name = cdi.getName(); + if (name.startsWith("vfw:")) + CaptureDeviceManager.removeDevice(cdi); + } + + int nDevices = 0; +// for (int i = 0; i < 10; i++) { +// String name = VFWCapture.capGetDriverDescriptionName(i); +// if (name != null && name.length() > 1) { +// System.err.println("Found device " + name); +// System.err.println("Querying device. Please wait..."); +// com.sun.media.protocol.vfw.VFWSourceStream.autoDetect(i); +// nDevices++; +// } +// } + } + + public static void main(String [] args) { + VFWAuto a = new VFWAuto(); + System.exit(0); + } +} + diff --git a/src/net/java/sip/communicator/impl/media/media.manifest.mf b/src/net/java/sip/communicator/impl/media/media.manifest.mf new file mode 100644 index 0000000..947b202 --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/media.manifest.mf @@ -0,0 +1,23 @@ +Bundle-Activator: net.java.sip.communicator.impl.media.Activator +Bundle-Name: Media Service Implementation +Bundle-Description: A bundle that offers Media capture and presentation capabilities. +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +Import-Package: org.ungoverned.gravity.servicebinder, + org.osgi.framework, + net.java.sip.communicator.service.configuration, + net.java.sip.communicator.service.configuration.event, + net.java.sip.communicator.service.protocol, + net.java.sip.communicator.util, + javax.media, + javax.media.format, + javax.media.protocol, + javax.sound.sampled, + javax.swing, + com.sun.media.protocol.javasound, + com.sun.media.protocol.v4l, +Export-Package: net.java.sip.communicator.service.media, + net.java.sip.communicator.service.media.event, + net.java.sip.communicator.impl.media, + net.java.sip.communicator.impl.media.event, +Metadata-Location: /net/java/sip/communicator/impl/media/media.metadata.xml diff --git a/src/net/java/sip/communicator/impl/media/media.metadata.xml b/src/net/java/sip/communicator/impl/media/media.metadata.xml new file mode 100644 index 0000000..58bdd13 --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/media.metadata.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<bundle> + <component class="net.java.sip.communicator.impl.media.MediaServiceImpl"> + <provides service="net.java.sip.communicator.service.media.MediaService"/> + + <requires + service="net.java.sip.communicator.service.configuration.ConfigurationService" + filter="" + policy="static" + cardinality="1..1" + bind-method="setConfigurationService" + unbind-method="unsetConfigurationService" + /> + </component> + +</bundle> diff --git a/src/net/java/sip/communicator/impl/netaddr/.#NetworkAddressManagerServiceImpl.java.1.6 b/src/net/java/sip/communicator/impl/netaddr/.#NetworkAddressManagerServiceImpl.java.1.6 new file mode 100644 index 0000000..7ef72a9 --- /dev/null +++ b/src/net/java/sip/communicator/impl/netaddr/.#NetworkAddressManagerServiceImpl.java.1.6 @@ -0,0 +1,562 @@ +/* + * 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.netaddr; + +import java.net.*; +import java.util.Enumeration; + +import net.java.sip.communicator.util.*; +import net.java.stun4j.client.SimpleAddressDetector; +import net.java.stun4j.StunAddress; +import net.java.stun4j.StunException; +import net.java.sip.communicator.service.netaddr.*; +import net.java.sip.communicator.service.configuration.event.*; +import net.java.sip.communicator.service.configuration.*; + + + + + +/** + * + * @author Emil Ivov + */ +public class NetworkAddressManagerServiceImpl + implements NetworkAddressManagerService, PropertyChangeListener +{ + private static Logger logger = Logger.getLogger(NetworkAddressManagerServiceImpl.class); + + // General properties for the Network Address Manager + private static final String PROP_STUN_SERVER_ADDRESS + = "net.java.sip.communicator.STUN_SERVER_ADDRESS"; + private static final String PROP_STUN_SERVER_PORT + = "net.java.sip.communicator.STUN_SERVER_PORT"; + private static final String PROP_PREF_IPV6 + = "java.net.preferIPv6Addresses"; + private static final String PROP_PREF_IPV4 + = "java.net.preferIPv4Stack"; + private static final String PROP_PREFERRED_NET_ADDRESS + = "net.java.sip.communicator.common.PREFERRED_NETWORK_ADDRESS"; + private static final String PROP_PREFERRED_NET_IFACE + = "net.java.sip.communicator.common.PREFERRED_NETWORK_INTERFACE"; + + //reference to the bundles used in this class + private ConfigurationService configurationService = null; + private NetworkAddressManagerService networkAddressManagerService= null; + + + private SimpleAddressDetector detector = null; + private boolean useStun = true; + private static final int RANDOM_PORT = 55055; + private static final String WINDOWS_AUTO_CONFIGURED_ADDRESS_PREFIX = "169"; + + + + /** + * When + * + */ + public void propertyChange(PropertyChangeEvent event) + { + try + { + logger.logEntry(); + if(event.getPropertyName()==PROP_STUN_SERVER_ADDRESS) + { + logger.debug(PROP_STUN_SERVER_ADDRESS + "Change state"); + //Do an action + start(); + + } + else if(event.getPropertyName()==PROP_STUN_SERVER_PORT) + { + logger.debug(PROP_STUN_SERVER_PORT + "Change state"); + //Do an action + start(); + + } + else + { + logger.debug("Something Change state but it is " + +"not determine what !"); + start(); + } + } + finally + { + logger.logExit(); + } + } + + + /** + * The constructor nothing to do now, because we don't have access to the + * Configuration service yet + */ + public NetworkAddressManagerServiceImpl() + { + + // put a listener on propertyStunServerAddress and + //propertyStunServerPort properties + // set them to null il tey doesn't exist + + } + + /** + * get a reference to the configuration service + * and add listeners on propertyStunServerAddress and propertyStunServerPort + * @param configuration ConfigurationService reference to the bundle + * configuration service + */ + protected void setConfigurationService(ConfigurationService configuration) + { + configurationService = configuration; + + try + { + logger.logEntry(); + configurationService.addPropertyChangeListener( + PROP_STUN_SERVER_ADDRESS,this); + configurationService.addPropertyChangeListener( + PROP_STUN_SERVER_PORT,this); + configurationService.addPropertyChangeListener( + PROP_PREF_IPV6,this); + configurationService.addPropertyChangeListener( + PROP_PREF_IPV4,this); + } + catch(Exception e) + { + logger.error("NetworkAddressManagerServiceImpl " + +"problem on listem properties : "+ e); + } + + + + + finally + { + logger.logExit(); + } + } + + /** + * Initializes the address manager and any underlying libs. + * + */ + public void start() + { + try + { + logger.logEntry(); + // init stun + //InetAddress stunAddressStr = null; + String stunAddressStr = null; + int port = -1; + + try + { + stunAddressStr =(String) configurationService.getProperty( + PROP_STUN_SERVER_ADDRESS); + Integer portItr =(Integer) configurationService.getProperty( + PROP_STUN_SERVER_PORT); + + // in case the user prefers ipv6 addresses we don't want + // to use stun + boolean preferIPv6Addresses ; + if(configurationService.getProperty(PROP_PREF_IPV6) == null + || (((Boolean)configurationService.getProperty( + PROP_PREF_IPV6)).booleanValue())==false) + { + preferIPv6Addresses = false; + } + else + { + preferIPv6Addresses = true; + } + + if (stunAddressStr == null + || portItr == null + || preferIPv6Addresses) + { + useStun = false; + + //don't throw an exception as this is most probably the user + //that doesn't want stun + //throw new Exception("STUN address or port were null"); + return; + } + else + useStun = true; + + port = portItr.intValue(); + + } + catch (Throwable ex) + { + logger.error("Failed to init STUN service and it will " + +"stay disabled. Error was:",ex); + useStun = false; + } + try + { + detector = new SimpleAddressDetector( + new StunAddress(stunAddressStr,port)); + } + catch(Exception e) + { + logger.debug("can't create the StunDetector ! "+e); + } + + if (logger.isDebugEnabled()) + logger.debug("Created a STUN Address detector for the " + +"following STUN server: " + + stunAddressStr + ":" + port); + try { + detector.start(); + logger.debug("STUN server started;"); + } + catch (StunException ex) { + logger.error( + "Failed to start the STUN Address Detector at address:" + +stunAddressStr + ":" + port, ex); + detector = null; + useStun = false; + } + } + finally + { + logger.logExit(); + } + } + + + + /** + * + * @return an InetAddress instance representing the local host or null + * if no IP address for the host could be found + */ + public InetAddress getLocalHost() + { + return getLocalHost(true); + } + + + /** + * Returns a localhostAddress. + * + * @param anyAddressIsAccepted is 0.0.0.0 accepted as a return value. + * @return the address that was detected the address of the localhost. + */ + public InetAddress getLocalHost(boolean anyAddressIsAccepted) + { + try + { + logger.logEntry(); + // (though InetAddress itself is not really mobile but in case it + //gets fixed). + InetAddress localHost = null; + InetAddress mappedAddress = null; + InetAddress linkLocalAddress = null; + InetAddress publicAddress = null; + String selectedInterface = null; + + boolean preferIPv4Stack ; + if(configurationService.getProperty(PROP_PREF_IPV4) == null + || (((Boolean)configurationService. + getProperty(PROP_PREF_IPV4)).booleanValue())==false) + { + preferIPv4Stack = false; + } + else + { + preferIPv4Stack = true; + } + + + try + { + if (logger.isDebugEnabled()) + { + logger.debug("------------- NAMImpl.getLocalHost-----"); + logger.debug("propertyIPv4Stack=" + + configurationService.getProperty(PROP_PREF_IPV4)); + logger.debug("propertyStunServerAddress = " + + configurationService.getProperty( + PROP_STUN_SERVER_ADDRESS)); + logger.debug("propertyStunServerPort = " + + configurationService.getProperty( + PROP_STUN_SERVER_PORT)); + logger.debug("propertIpV6Pref = " + + configurationService.getProperty(PROP_PREF_IPV6)); + logger.debug("Use Stun :: "+useStun); + logger.debug("------------------------------"); + } + + + //check whether we have a public address that matches one of + //the local interfaces if not - return the first one that + //is not the loopback + try + { + //retrieve a STUN binding if possible + if (useStun) + { + StunAddress stunMappedAddress = + queryStunServer(RANDOM_PORT); + + if(stunMappedAddress == null) + { + mappedAddress = null; + } + else + { + mappedAddress = queryStunServer(RANDOM_PORT). + getSocketAddress().getAddress(); + } + return mappedAddress; + } + } + catch (Exception ex) + { + logger.error("manager.useStun error .. " + +"continuing without", ex); + } + + Enumeration localIfaces = + NetworkInterface.getNetworkInterfaces(); + + //interfaces loop + interfaces_loop: + while (localIfaces.hasMoreElements()) + { + NetworkInterface iFace = + (NetworkInterface) localIfaces.nextElement(); + Enumeration addresses = iFace.getInetAddresses(); + + //addresses loop + while (addresses.hasMoreElements()) { + InetAddress address = + (InetAddress) addresses.nextElement(); + //ignore link local addresses + if (!address.isAnyLocalAddress() + && !address.isLinkLocalAddress() + && !address.isLoopbackAddress() + && !isWindowsAutoConfiguredIPv4Address(address)) { + if (mappedAddress != null + && mappedAddress.equals(address)) { + if (logger.isDebugEnabled()) + logger.debug("Returninng localhost: Mapped " + + "address = Public address = " + + address); + //the address matches the one seen by the STUN + //server no doubt that it's a working public + //address. + + return address; + } + else if (isLinkLocalIPv4Address(address)) + { + if (logger.isDebugEnabled()) + logger.debug("Found Linklocal ipv4 address " + + address); + linkLocalAddress = address; + } + else { + if (logger.isDebugEnabled()) + logger.debug("Found public address " + + address); + + String preferredAddr = + (String)configurationService.getProperty( + PROP_PREFERRED_NET_ADDRESS); + String preferredIface = + (String)configurationService.getProperty( + PROP_PREFERRED_NET_IFACE); + + + if (// bail out if we already have the address + // chosen by the user + ( publicAddress != null + && preferredAddr!= null + && preferredAddr.equals( + publicAddress.getHostAddress())) + //bail out if we already have an address on + //an interface chosen by the user + || (publicAddress != null + && selectedInterface != null + && preferredIface != null + && preferredIface.equals( + selectedInterface)) + //in case we have an ipv4 addr and don't + //want to change it for an ipv6 + || (publicAddress != null + && publicAddress instanceof Inet4Address + && address instanceof Inet6Address + && preferIPv4Stack) + //in case we have an ipv6 addr and don't + //want to change it for an ipv4 + || (publicAddress != null + && publicAddress instanceof Inet6Address + && address instanceof Inet4Address + && !preferIPv4Stack) + ) + { + continue; + } + publicAddress = address; + selectedInterface = iFace.getDisplayName(); + } + } + }//addresses loop + }//interfaces loop + if (publicAddress != null) { + logger.debug("Returning public address");//debug + return publicAddress; + } + if (linkLocalAddress != null) { + logger.debug("Returning link local address");//debug + return linkLocalAddress; + } + if (anyAddressIsAccepted) + localHost = new InetSocketAddress(RANDOM_PORT).getAddress(); + else + localHost = InetAddress.getLocalHost(); + } + catch (Exception ex) { + logger.error("Failed to create localhost address, returning " + + "the any address (0.0.0.0)", ex); + //get the address part of an InetSocketAddress for a random port. + localHost = new InetSocketAddress(RANDOM_PORT).getAddress(); + } + if (logger.isDebugEnabled()) + logger.debug("Returning localhost address=" + localHost); + return localHost; + } + finally + { + logger.logExit(); + } + } + + + /** + * Determines whether the address is the result of windows auto configuration. + * (i.e. One that is in the 169.254.0.0 network) + * @param add the address to inspect + * @return true if the address is autoconfigured by windows, false otherwise. + */ + private static boolean isWindowsAutoConfiguredIPv4Address(InetAddress add) + { + return (add.getAddress()[0] & 0xFF) == 169 + && (add.getAddress()[1] & 0xFF) == 254; + } + + /** + * The method quesries a Stun server for a binding for the specified port. + * @param port the port from which a STUN message + * @return StunAddress + */ + private StunAddress queryStunServer(int port) + { + + + try{ + logger.logEntry(); + StunAddress mappedAddress = null; + if (detector != null && useStun) { + try { + mappedAddress = detector.getMappingFor(port); + if (logger.isDebugEnabled()) + logger.debug("For port:" + + port + "a Stun server returned the " + +"following mapping [" + mappedAddress); + } + catch (StunException ex) { + logger.error( + "Failed to retrive mapped address port:" +port, ex); + } + } + return mappedAddress; + } + finally{ + logger.logExit(); + } + } + + + + /** + * Determines whether the address is an IPv4 link local address. IPv4 link + * local addresses are those in the following networks: + * + * 10.0.0.0 to 10.255.255.255 + * 172.16.0.0 to 172.31.255.255 + * 192.168.0.0 to 192.168.255.255 + * + * @param add the address to inspect + * @return true if add is a link local ipv4 address and false if not. + */ + private static boolean isLinkLocalIPv4Address(InetAddress add) + { + if(add instanceof Inet4Address) + { + byte address[] = add.getAddress(); + if ( (address[0] & 0xFF) == 10) + return true; + if ( (address[0] & 0xFF) == 172 + && (address[1] & 0xFF) >= 16 && address[1] <= 31) + return true; + if ( (address[0] & 0xFF) == 192 + && (address[1] & 0xFF) == 168) + return true; + return false; + } + return false; + } + + + + /** + * Tries to obtain a mapped/public address for the specified port. + * + * @param port the port whose mapping we are interested in. + * @return a public address corresponding to the specified port or null + * if all attempts to retrieve such an address have failed. + */ + public InetSocketAddress getPublicAddressFor(int port) + { + try { + logger.logEntry(); + if (!useStun) { + logger.debug( + "Stun is disabled, skipping mapped address recovery."); + return new InetSocketAddress(getLocalHost(), port); + } + StunAddress mappedAddress = queryStunServer(port); + InetSocketAddress result = null; + if (mappedAddress != null) + result = mappedAddress.getSocketAddress(); + else { + //Apparently STUN failed. Let's try to temporarily disble it + //and use algorithms in getLocalHost(). ... We should probably + //eveng think about completely disabling stun, and not only + //temporarily. + //Bug report - John J. Barton - IBM + InetAddress localHost = getLocalHost(false); + result = new InetSocketAddress(localHost, port); + } + if (logger.isDebugEnabled()) + logger.debug("Returning mapping for port:" + + port +" as follows: " + result); + return result; + } + finally { + logger.logExit(); + } + } +} diff --git a/src/net/java/sip/communicator/impl/netaddr/.cvsignore b/src/net/java/sip/communicator/impl/netaddr/.cvsignore new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/net/java/sip/communicator/impl/netaddr/.cvsignore diff --git a/src/net/java/sip/communicator/impl/netaddr/Activator.java b/src/net/java/sip/communicator/impl/netaddr/Activator.java new file mode 100644 index 0000000..ebc2978 --- /dev/null +++ b/src/net/java/sip/communicator/impl/netaddr/Activator.java @@ -0,0 +1,80 @@ +/* + * 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.netaddr; + +import org.osgi.framework.*; +import net.java.sip.communicator.service.configuration.ConfigurationService; +import net.java.sip.communicator.service.netaddr.*; +import net.java.sip.communicator.util.*; + +/** + * The activator manage the the bundles between OSGi framework and the + * Network address manager + * + * @author Emil Ivov + * @author Pierre Floury + */ +public class Activator + implements BundleActivator +{ + private static Logger logger = + Logger.getLogger(NetworkAddressManagerServiceImpl.class); + + private NetworkAddressManagerServiceImpl networkAMS = null; + + /** + * Creates a NetworkAddressManager, starts it, and registers it as a + * NetworkAddressManagerService. + * + * @param bundleContext OSGI bundle context + * @throws Exception if starting the NetworkAddressManagerFails. + */ + public void start(BundleContext bundleContext) throws Exception + { + try{ + + logger.logEntry(); + // get the config service + ServiceReference refConfig = bundleContext.getServiceReference( + ConfigurationService.class.getName()); + + ConfigurationService configurationService = (ConfigurationService) + bundleContext.getService(refConfig); + + //Create and start the network address manager. + networkAMS = + new NetworkAddressManagerServiceImpl(configurationService); + + // give references to the NetworkAddressManager implementation + networkAMS.start(); + + logger.info("Network Address Manager ...[ STARTED ]"); + + bundleContext.registerService( + NetworkAddressManagerService.class.getName(), networkAMS, null); + + logger.info("Network Address Manager Service ...[REGISTERED]"); + } + finally + { + logger.logExit(); + } + } + + /** + * Stops the Networ Address Manager bundle + * + * @param bundleContext the OSGI bundle context + * + */ + public void stop(BundleContext bundleContext) + { + if(networkAMS != null) + networkAMS.stop(); + logger.info("Network Address Manager Service ...[STOPED]"); + } +} diff --git a/src/net/java/sip/communicator/impl/netaddr/AddressDiagnosticsKit.java b/src/net/java/sip/communicator/impl/netaddr/AddressDiagnosticsKit.java new file mode 100644 index 0000000..4ed1a72 --- /dev/null +++ b/src/net/java/sip/communicator/impl/netaddr/AddressDiagnosticsKit.java @@ -0,0 +1,384 @@ +package net.java.sip.communicator.impl.netaddr; + +import net.java.sip.communicator.util.*; +import java.net.*; +import net.java.stun4j.*; +import net.java.stun4j.stack.*; +import net.java.stun4j.message.*; +import net.java.stun4j.attribute.*; + +/** + * Runs a separate thread of diagnostics for a given network address. The + * diagnostics thread would discover NAT bindings through stun, update bindings + * lifetime test connectivity and etc. + * + * @author Emil Ivov + */ +public class AddressDiagnosticsKit + extends Thread +{ + private static final Logger logger = + Logger.getLogger(AddressDiagnosticsKit.class); + + public static final int DIAGNOSTICS_STATUS_OFF = 1; + public static final int DIAGNOSTICS_STATUS_DISOVERING_CONFIG = 2; + public static final int DIAGNOSTICS_STATUS_RESOLVING = 3; + public static final int DIAGNOSTICS_STATUS_COMPLETED = 4; + public static final int DIAGNOSTICS_STATUS_DISOVERING_BIND_LIFETIME = 5; + public static final int DIAGNOSTICS_STATUS_TERMINATED = 6; + + private int diagnosticsStatus = DIAGNOSTICS_STATUS_OFF; + + /** + * These are used by (to my knowledge) mac and windows boxes when dhcp + * fails and are only usable with other boxes using the same address + * in the same net segment. That's why they get their low preference. + */ + private static final AddressPreference ADDR_PREF_LOCAL_IPV4_AUTOCONF + = new AddressPreference(40); + + /** + * Local IPv6 addresses are assigned by default to any network iface running + * an ipv6 stack. Theya are one of our last resorts since an internet + * connected node would have generally configured sth else as well. + */ + private static final AddressPreference ADDR_PREF_LOCAL_IPV6 + = new AddressPreference(40); + + /** + * Local IPv4 addresses are either assigned by DHCP or manually configured + * which means that even if they're unresolved to a globally routable + * address they're still there for a reason (let the reason be ...) and this + * reason might very well be purposeful so they should get a preference + * higher than local IPv6 (even though I'm an IPv6 fan :) ) + */ + private static final AddressPreference ADDR_PREF_PRIVATE_IPV4 + = new AddressPreference(50); + + /** + * Global IPv4 Addresses are a good think when they work. We are therefore + * setting a high preference that will then be corrected by. + */ + private static final AddressPreference ADDR_PREF_GLOBAL_IPV4 + = new AddressPreference(60); + + /** + * There are many reasons why global IPv6 addresses should have the highest + * preference. A global IPv6 address is most often delivered through + * stateless address autoconfiguration which means an active router and + * might also mean an active net connection. + */ + private static final AddressPreference ADDR_PREF_GLOBAL_IPV6 + = new AddressPreference(70); + + /** + * The address of the stun server to query + */ + private StunAddress primaryStunServerAddress = + new StunAddress("stun01.sipphone.com", 3478); + + /** + * The address pool entry that this kit is diagnosing. + */ + private AddressPoolEntry addressEntry = null; + + /** + * Specifies whether stun should be used or not. + * This field is updated during runtime to conform to the configuration. + */ + private boolean useStun = true; + + private StunClient stunClient = null; + + /** + * The port to be used locally for sending generic stun queries. + */ + static final int LOCAL_STUN_PORT = 55126; + private int bindRetries = 10; + + public AddressDiagnosticsKit(AddressPoolEntry addressEntry) + { + this.addressEntry = addressEntry; + setDiagnosticsStatus(DIAGNOSTICS_STATUS_OFF); + } + + /** + * Sets the current status of the address diagnostics process + * @param status int + */ + private void setDiagnosticsStatus(int status) + { + this.diagnosticsStatus = status; + } + + /** + * Returns the current status of this diagnosics process. + * @return int + */ + public int getDiagnosticsStatus() + { + return this.diagnosticsStatus; + } + + /** + * The diagnostics code itself. + */ + public void run() + { + logger.debug("Started a diag kit for entry: " + addressEntry); + + //implements the algorithm from AssigningAddressPreferences.png + + setDiagnosticsStatus(this.DIAGNOSTICS_STATUS_DISOVERING_CONFIG); + + InetAddress address = addressEntry.getInetAddress(); + + //is this an ipv6 address + if (addressEntry.isIPv6()) + { + if (addressEntry.isLinkLocal()) + { + addressEntry.setAddressPreference(ADDR_PREF_LOCAL_IPV6); + setDiagnosticsStatus(DIAGNOSTICS_STATUS_TERMINATED); + return; + } + + if (addressEntry.is6to4()) + { + //right now we don't support these. we should though ... one day + addressEntry.setAddressPreference(AddressPreference.MIN); + setDiagnosticsStatus(DIAGNOSTICS_STATUS_TERMINATED); + return; + } + + //if we get here then we are a globally routable ipv6 addr + addressEntry.setAddressPreference(ADDR_PREF_GLOBAL_IPV6); + setDiagnosticsStatus(DIAGNOSTICS_STATUS_COMPLETED); + //should do some connectivity testing here and proceed with firewall + //discovery but since stun4j does not support ipv6 yet, this too + //will happen another day. + return; + } + + //from now on we're only dealing with IPv4 + if (addressEntry.isIPv4LinkLocalAutoconf()) + { + //not sure whether these are used for anything. + addressEntry.setAddressPreference(AddressPreference.MIN); + setDiagnosticsStatus(DIAGNOSTICS_STATUS_TERMINATED); + return; + } + + //first try and see what we can infer from just looking at the + //address + if (addressEntry.isLinkLocalIPv4Address()) + { + addressEntry.setAddressPreference(ADDR_PREF_PRIVATE_IPV4); + } + else + { + //public address + addressEntry.setAddressPreference(ADDR_PREF_GLOBAL_IPV4); + } + + if (!useStun) + { + //if we're configured not to run stun - we're done. + setDiagnosticsStatus(DIAGNOSTICS_STATUS_TERMINATED); + return; + } + + //start stunning + for(int i = 0; i < bindRetries; i++){ + StunAddress localStunAddress = new StunAddress( + address, 1024 + (int) (Math.random() * 64512)); + try + { + + stunClient = new StunClient(localStunAddress); + stunClient.start(); + logger.debug("Successfully started StunClient for " + + localStunAddress + "."); + break; + } + catch (StunException ex) + { + if (ex.getCause() instanceof SocketException + && i < bindRetries) + { + logger.debug("Failed to bind to " + + localStunAddress + ". Retrying ..."); + logger.debug("Exception was ", ex); + continue; + } + logger.error("Failed to start a stun client for address entry [" + + addressEntry.toString()+"]:" + +localStunAddress.getPort() + ". Ceasing attempts", + ex); + setDiagnosticsStatus(DIAGNOSTICS_STATUS_TERMINATED); + return; + } + } + //De Stun Test I + StunMessageEvent event = null; + try + { + event = stunClient.doStunTestI( + primaryStunServerAddress); + } + catch (StunException ex) + { + logger.error("Failed to perform STUN Test I for address entry" + + addressEntry.toString(), ex); + setDiagnosticsStatus(DIAGNOSTICS_STATUS_TERMINATED); + stunClient.shutDown(); + return; + } + + if(event == null) + { + //didn't get a response - we either don't have connectivity or the + //server is down + /** @todo if possible try another stun server here. we should + * support multiple stun servers*/ + logger.debug("There seems to be no inet connectivity for " + + addressEntry); + setDiagnosticsStatus(DIAGNOSTICS_STATUS_TERMINATED); + stunClient.shutDown(); + logger.debug("stun test 1 failed"); + return; + } + + //the moment of the truth - are we behind a NAT? + boolean isPublic; + Message stunResponse = event.getMessage(); + + Attribute mappedAttr = stunResponse.getAttribute(Attribute.MAPPED_ADDRESS); + + StunAddress mappedAddrFromTestI = ((MappedAddressAttribute)mappedAttr).getAddress(); + Attribute changedAddressAttributeFromTestI + = stunResponse.getAttribute(Attribute.CHANGED_ADDRESS); + StunAddress secondaryStunServerAddress = + ((ChangedAddressAttribute)changedAddressAttributeFromTestI). + getAddress(); + + /** @todo verify whether the stun server returned the same address for + * the primary and secondary server and act accordingly + * */ + + if(mappedAddrFromTestI == null){ + logger.error( + "Stun Server did not return a mapped address for entry " + + addressEntry.toString()); + setDiagnosticsStatus(DIAGNOSTICS_STATUS_TERMINATED); + return; + } + + if(mappedAddrFromTestI.equals(event.getSourceAccessPoint().getAddress())) + { + isPublic = true; + } + else + { + isPublic = false; + } + + //do STUN Test II + try + { + event = stunClient.doStunTestII(primaryStunServerAddress); + } + catch (StunException ex) + { + logger.error("Failed to perform STUN Test II for address entry" + + addressEntry.toString(), ex); + setDiagnosticsStatus(DIAGNOSTICS_STATUS_TERMINATED); + stunClient.shutDown(); + logger.debug("stun test 2 failed"); + return; + } + + if(event != null){ + logger.error("Secondary STUN server is down" + + addressEntry.toString()); + setDiagnosticsStatus(DIAGNOSTICS_STATUS_TERMINATED); + stunClient.shutDown(); + return; + } + + //might mean that either the secondary stun server is down + //or that we are behind a restrictive firewall. Let's find out + //which. + try + { + event = stunClient.doStunTestI(secondaryStunServerAddress); + logger.debug("stun test 1 succeeded with s server 2"); + } + catch (StunException ex) + { + logger.error("Failed to perform STUN Test I for address entry" + + addressEntry.toString(), ex); + setDiagnosticsStatus(DIAGNOSTICS_STATUS_TERMINATED); + stunClient.shutDown(); + return; + } + + if (event == null) + { + //secondary stun server is down + logger.error("Secondary STUN server is down" + + addressEntry.toString()); + setDiagnosticsStatus(DIAGNOSTICS_STATUS_TERMINATED); + stunClient.shutDown(); + return; + } + + //we are at least behind a port restricted nat + + stunResponse = event.getMessage(); + mappedAttr = stunResponse.getAttribute(Attribute.MAPPED_ADDRESS); + StunAddress mappedAddrFromSecServer = + ((MappedAddressAttribute)mappedAttr).getAddress(); + + if(!mappedAddrFromTestI.equals(mappedAddrFromSecServer)) + { + //secondary stun server is down + logger.debug("We are behind a symmetric nat" + + addressEntry.toString()); + setDiagnosticsStatus(DIAGNOSTICS_STATUS_TERMINATED); + stunClient.shutDown(); + return; + } + + //now let's run test III so that we could guess whether or not we're + //behind a port restricted nat/fw or simply a restricted one. + try + { + event = stunClient.doStunTestIII(primaryStunServerAddress); + logger.debug("stun test 3 succeeded with s server 1"); + } + catch (StunException ex) + { + logger.error("Failed to perform STUN Test III for address entry" + + addressEntry.toString(), ex); + setDiagnosticsStatus(DIAGNOSTICS_STATUS_TERMINATED); + stunClient.shutDown(); + return; + } + + if (event == null) + { + logger.debug("We are behind a port restricted NAT or fw" + + addressEntry.toString()); + setDiagnosticsStatus(DIAGNOSTICS_STATUS_TERMINATED); + stunClient.shutDown(); + return; + } + + logger.debug("We are behind a restricted NAT or fw" + + addressEntry.toString()); + setDiagnosticsStatus(DIAGNOSTICS_STATUS_TERMINATED); + stunClient.shutDown(); + } +} + diff --git a/src/net/java/sip/communicator/impl/netaddr/AddressPool.java b/src/net/java/sip/communicator/impl/netaddr/AddressPool.java new file mode 100644 index 0000000..e5b0941 --- /dev/null +++ b/src/net/java/sip/communicator/impl/netaddr/AddressPool.java @@ -0,0 +1,80 @@ +package net.java.sip.communicator.impl.netaddr; + +import java.net.NetworkInterface; +import java.net.InetAddress; +import java.util.Enumeration; +import net.java.sip.communicator.util.*; +import java.net.*; +import java.util.*; + +/** + * The class scans all local interfaces discovering all addresses, and starts + * constantly testing them one by one in order to verify their properties. It + * also orders the addresses, putting first those that it has determined to be + * more easily usable and that offer better chances of connection success. + * + * @author Emil Ivov + */ +public class AddressPool +{ + private static Logger logger = + Logger.getLogger(AddressPool.class); + + private Map diagnosticsKits = new Hashtable(); + private ArrayList addressEntries = new ArrayList(); + + public AddressPool() + { + super(); + } + + private void initPool() + { + Enumeration localIfaces = null; + try + { + localIfaces = NetworkInterface.getNetworkInterfaces(); + } + catch (SocketException ex) + { + throw new RuntimeException( + "Failed to retrieve local interfaces!"); + } + + //loop over all local network interfaces + while (localIfaces.hasMoreElements()) + { + NetworkInterface iFace = + (NetworkInterface) localIfaces.nextElement(); + + Enumeration addresses = iFace.getInetAddresses(); + + //addresses loop + while (addresses.hasMoreElements()) + { + InetAddress address = (InetAddress) addresses.nextElement(); + + //we don't care about loopback addresses + if(address.isLoopbackAddress()) + continue; + + AddressPoolEntry addrEntry = + new AddressPoolEntry(address, iFace); + AddressDiagnosticsKit diagKit = + new AddressDiagnosticsKit(addrEntry); + + addressEntries.add(addrEntry); + diagnosticsKits.put(addrEntry, diagKit); + diagKit.start(); + } //addresses loop + } //interfaces loop + } + + public static void main(String[] args) + { + AddressPool pool = new AddressPool(); + pool.initPool(); + } + + +} diff --git a/src/net/java/sip/communicator/impl/netaddr/AddressPoolEntry.java b/src/net/java/sip/communicator/impl/netaddr/AddressPoolEntry.java new file mode 100644 index 0000000..897a725 --- /dev/null +++ b/src/net/java/sip/communicator/impl/netaddr/AddressPoolEntry.java @@ -0,0 +1,275 @@ +package net.java.sip.communicator.impl.netaddr; + +import java.net.*; + +/** + * An entry in the Address Pool. An addresspool entry contains an address + * belonging to any of the local network interfaces together with properties + * that characterize them, such as - whether or not the address is publicly + * routable or not, It's corresponding NAT entry obtained by stun. The type of + * NAT that the address is located behind (if any). The lifetime of the bindings + * behind this NAT. Any TURN bindings. Whether or not TCP connections seem to be + * supported. Whether or not UDP connections seem to be supported, and etc. etc. + * + * Concerning the address, this is an immutable object, in other words, the + * address of a single entry is not supposed to change once it has been created. + * In case this address is modified while the application is running we'll + * rather have a new address entry instead of modifying this one. The only thing + * that can change about an address pool entry is its properties. + * + * @author Emil Ivov + */ +public class AddressPoolEntry +{ + private InetAddress address = null; + + /** + * The AddressPreference qualifyer is used to indicate preference of this + * address compared to others available on the current host. + */ + private AddressPreference preference; + + /** + * Only used for ipv4 addresses. Actually it would have been better to use + * the isLinkLocal method of InetAddress but since it only works for ipv6 + * we're using this one. in the case of an ip address + */ + private boolean isLinkLocal = false; + + private NetworkInterface ownerInterface = null; + + private FirewallDescriptor firewallDescriptor = null; + + private InetAddress turnAddress = null; + + public AddressPoolEntry(InetAddress address, NetworkInterface ownerIface) + { + if(address == null) + throw new NullPointerException("Address param cannot be null"); + this.address = address; + this.ownerInterface = ownerIface; + } + + /** + * Returns the ip address that this address pool entry represents. + * @return InetAddress + */ + public InetAddress getInetAddress() + { + return address; + } + + /** + * Determines whether or not the address pool entry represents an IPv6 + * address. + * @return true if the address is ipv6 and false in case of ipv4. + */ + public boolean isIPv6() + { + return (address instanceof Inet6Address); + } + + /** + * Determines whether this is a link local or publicly routable address. + * Works for both IPv4 and IPv6 addresses. + * @return true if the address is link local and thus not globally routable + * and false if otherwise. + */ + public boolean isLinkLocal() + { + if( address instanceof Inet6Address){ + if(address.isLinkLocalAddress()){ + return true; + } + else{ + return false; + } + } + + return isLinkLocal; + } + + /** + * Specifies whether or not the address is an IPv4 link local address. + * @param linkLocal true if t + */ + void setLinkLocal(boolean linkLocal) + { + this.isLinkLocal = linkLocal; + } + + + /** + * Determines whether the address is the result of windows auto configuration. + * (i.e. One that is in the 169.254.0.0 network) + * @param add the address to inspect + * @return true if the address is autoconfigured by windows, false otherwise. + */ + /** + * Determines whether the address is the result of windows auto configuration. + * (i.e. One that is in the 169.254.0.0 network) + * @param add the address to inspect + * @return true if the address is autoconfigured by windows, false otherwise. + */ + public static boolean isIPv4LinkLocalAutoconf(InetAddress add) + { + return (add.getAddress()[0] & 0xFF) == 169 + && (add.getAddress()[1] & 0xFF) == 254; + } + + /** + * Determines whether the address encapsulated by this entry is the result of + * windows auto configuration (i.e. One that is in the 169.254.0.0 network) + * @return true if the address is autoconfigured by windows, false otherwise. + */ + public boolean isIPv4LinkLocalAutoconf() + { + return isIPv4LinkLocalAutoconf(address); + } + + /** + * Determines whether the address is an IPv4 link local address. IPv4 link + * local addresses are those in the following networks: + * + * 10.0.0.0 to 10.255.255.255 + * 172.16.0.0 to 172.31.255.255 + * 192.168.0.0 to 192.168.255.255 + * + * @param add the address to inspect + * @return true if add is a link local ipv4 address and false if not. + */ + public static boolean isLinkLocalIPv4Address(InetAddress add) + { + if(add instanceof Inet4Address) + { + byte address[] = add.getAddress(); + if ( (address[0] & 0xFF) == 10) + return true; + if ( (address[0] & 0xFF) == 172 + && (address[1] & 0xFF) >= 16 && address[1] <= 31) + return true; + if ( (address[0] & 0xFF) == 192 + && (address[1] & 0xFF) == 168) + return true; + return false; + } + return false; + } + + + /** + * Determines whether the address encapsulated by this entry is an IPv4 link + * local address. IPv4 link local addresses are those in the following + * networks: + * + * 10.0.0.0 to 10.255.255.255 + * 172.16.0.0 to 172.31.255.255 + * 192.168.0.0 to 192.168.255.255 + * + * @return true if add is a link local ipv4 address and false if not. + */ + public boolean isLinkLocalIPv4Address() + { + return isLinkLocalIPv4Address(this.address); + } + + /** + * Determines whether the address encapsulated by this entry is an IPv6 link + * local address. IPv6 link local addresses are briefly those that start + * with fe80. + * + * @return true if add is a link local ipv6 address and false if not. + */ + public boolean isLinkLocalIPv6Address() + { + return address instanceof Inet6Address && address.isLinkLocalAddress(); + } + + /** + * Determines whether this is the localhost address (127.0.0.1 or ::1). + * This is a simple wrapper of the InetAddress.isLoopbackAddress() method. + * @return true if this is and IPv6 or IPv4 loopback address and false + * otherwise. + */ + public boolean isLoopback() + { + return address.isLoopbackAddress(); + } + + /** + * Determines whether the adderss encapsulated by this address entry is + * a globally routable inet address, or in other words is it possible + * (at least in theory) to directly send packets to it from any point of + * the internet. + * @return true if this is a public ip addr and false otherwise. + */ + public boolean isGloballyRoutable() + { + return !isLinkLocalIPv6Address() + && !isIPv4LinkLocalAutoconf() + && !isLinkLocalIPv4Address() + && !isLoopback(); + } + + /** + * Determines whether the adderss encapsulated by this address entry is + * a 6to4 translation address (2002::/16) + * + * @return true if this is a 6to4 ip addr (belongs to 2002::/16) and false + * otherwise. + */ + public boolean is6to4() + { + return address instanceof Inet6Address + && (address.getAddress()[0] & 0xff) == 0x20 + && (address.getAddress()[0] & 0xc0) == 0x02; + } + + /** + * Returns the interface that this address belongs to. + * @return a reference to the interface that this address belongs to. + */ + public NetworkInterface getOwnerInterface() + { + return ownerInterface; + } + + /** + * Returns a string representation of this address entry + * @return a String containing key characteristics of this address pool + * entry + */ + public String toString() + { + return "AddressPoolEntry:" + + address.getHostAddress() + + "@"+getOwnerInterface().getDisplayName() + " " + + "isIPv6=" + isIPv6() + ", " + + "isLoopback=" + isLoopback() + ", " + + "isGloballyRoutable=" + isGloballyRoutable() + ", " + + "isLinkLocal=" + isLinkLocal() + + "AddressPreference=["+preference+"]"; + } + + /** + * Sets the AddressPreference that address diagnostics have calculated for + * the address corresponding to this entry. + * @param preference the preference to assign to this entry + */ + void setAddressPreference(AddressPreference preference) + { + this.preference = preference; + } + + /** + * Returns the AddressPreference assigned to this AddressEntry. The + * AddressPreference is what establishes an order in the "usability" of the + * addresses on the local machine. + * @return AddressPreference the AddressPreference assigned to this entry. + */ + public AddressPreference getAddressPreference() + { + return this.preference; + } + +} diff --git a/src/net/java/sip/communicator/impl/netaddr/AddressPreference.java b/src/net/java/sip/communicator/impl/netaddr/AddressPreference.java new file mode 100644 index 0000000..612e019 --- /dev/null +++ b/src/net/java/sip/communicator/impl/netaddr/AddressPreference.java @@ -0,0 +1,109 @@ +package net.java.sip.communicator.impl.netaddr; + +/** + * The AddressPreference class is used to assign preference to the various + * network addresses on a host. Preferencies are in essence integers that + * may vary between MAX_PREF and MIN_PREF, where MIN_PREF indicates low or + * inexisting connectivity through the specified address and MAX_PREF - + * complete, flowless open internet access (the perfect connection ;-) ) + * + * @author Emil Ivov + */ +public class AddressPreference + implements Comparable +{ + public static final int MAX_PREF = 100; + public static final int MIN_PREF = 0; + + public static final AddressPreference MAX = new AddressPreference(MAX_PREF); + public static final AddressPreference MIN = new AddressPreference(MIN_PREF); + + /** + * The numerical value of this AddressPreference instance. + */ + private int preference = (MAX_PREF - MIN_PREF)/2; + + /** + * Creates an AddressPreference instance with the specified preference. + * @param preference the preference integer corresponding to this + * AddressPreference + */ + AddressPreference(int preference) + { + this.preference = preference; + } + + /** + * Creates an AddressPreference object with a default preference value. + */ + AddressPreference() + { + } + + /** + * Sets the preference value of this AddressPreference instance to be + * @param preference int + */ + void setPreference(int preference) + { + this.preference = preference; + } + + + /** + * Returns the exact preference value of this AddressPreference instance. + * @return the exact preference value (an ineteger between MAX_PREF and + * MIN_PREF) representing this AddressPreference instance. + */ + public int getPreference() + { + return preference; + } + + /** + * Compares this address preference with the specified object for order. + * Returns a negative integer, zero, or a positive integer as this + * AddressPreference is less than, equal to, or greater than the specified + * object.<p> + * + * @param o the Object to be compared. + * @return a negative integer, zero, or a positive integer as this object + * is less than, equal to, or greater than the specified object. + * + * @throws ClassCastException if the specified object's type is not an + * instance or a descendant of AddressPreference. + */ + public int compareTo(Object o) + { + AddressPreference other = (AddressPreference)o; + + return this.preference - other.preference; + } + + /** + * Returns true if <code>obj</code> is the same object as this + * AddressPreference or is at least an instance of AddressPreference and + * has the same numerical value. In all other cases the method returns + * false. + * @param obj the object to compare with + * @return true if both objects represent the same preference value and + * false otherwise. + */ + public boolean equals(Object obj) + { + if (! (obj instanceof AddressPreference) + || obj == null) + return false; + + if (obj == this + || ((AddressPreference)obj).preference == preference ) + return true; + + return false; + } + + public String toString() + { + return "preference="+getPreference(); + } +} diff --git a/src/net/java/sip/communicator/impl/netaddr/BlockingRequestSender.java b/src/net/java/sip/communicator/impl/netaddr/BlockingRequestSender.java new file mode 100644 index 0000000..430b44a --- /dev/null +++ b/src/net/java/sip/communicator/impl/netaddr/BlockingRequestSender.java @@ -0,0 +1,96 @@ +package net.java.sip.communicator.impl.netaddr; + +import net.java.stun4j.*; +import net.java.stun4j.message.*; +import net.java.stun4j.stack.*; + +/** + * A utility used to flatten the multithreaded architecture of the Stack + * and execute the discovery process in a synchronized manner. Roughly what + * happens here is: + * + * ApplicationThread: + * sendMessage() + * wait(); + * + * StackThread: + * processMessage/Timeout() + * { + * saveMessage(); + * notify(); + * } + * + * + * <p>Organisation: <p> Louis Pasteur University, Strasbourg, France</p> + * <p>Network Research Team (http://www-r2.u-strasbg.fr)</p></p> + * @author Emil Ivov + * @version 0.1 + */ +class BlockingRequestSender + implements ResponseCollector +{ + private StunProvider stunProvider = null; + private NetAccessPointDescriptor apDescriptor = null; + + StunMessageEvent responseEvent = null; + + BlockingRequestSender(StunProvider stunProvider, + NetAccessPointDescriptor apDescriptor) + { + this.stunProvider = stunProvider; + this.apDescriptor = apDescriptor; + } + + /** + * Saves the message event and notifies the discoverer thread so that + * it may resume. + * @param evt the newly arrived message event. + */ + public synchronized void processResponse(StunMessageEvent evt) + { + this.responseEvent = evt; + notifyAll(); + } + + /** + * Notifies the discoverer thread when a message has timeouted so that + * it may resume and consider it as unanswered. + */ + public synchronized void processTimeout() + { + notifyAll(); + } + + /** + * Sends the specified request and blocks until a response has been + * received or the request transaction has timed out. + * @param request the reuqest to send + * @param serverAddress the request destination address + * @return the event encapsulating the response or null if no response + * has been received. + * @throws StunException NETWORK_ERROR or other if we fail to send + * the message + */ + public synchronized StunMessageEvent sendRequestAndWaitForResponse( + Request request, + StunAddress serverAddress) + throws StunException + { + stunProvider.sendRequest(request, serverAddress, apDescriptor, + BlockingRequestSender.this); + + try + { + wait(); + } + catch (InterruptedException ex) + { /** @todo log */ + ex.printStackTrace(); + } + + StunMessageEvent res = responseEvent; + responseEvent = null; //prepare for next message + + return res; + } +} diff --git a/src/net/java/sip/communicator/impl/netaddr/FirewallDescriptor.java b/src/net/java/sip/communicator/impl/netaddr/FirewallDescriptor.java new file mode 100644 index 0000000..4173929 --- /dev/null +++ b/src/net/java/sip/communicator/impl/netaddr/FirewallDescriptor.java @@ -0,0 +1,204 @@ +package net.java.sip.communicator.impl.netaddr; + +import java.net.*; +import net.java.sip.communicator.util.*; + +/** + * todo: add a type for the open internet + * + * + * @author Emil Ivov + */ +public class FirewallDescriptor +{ + private static final Logger logger = + Logger.getLogger(FirewallDescriptor.class); + + /** + * The amount of preference points that a firewall corresponding to this + * descriptor should remove from the address preference. + */ + private AddressPreference preferenceSubtrahend = + new AddressPreference(AddressPreference.MIN_PREF); + + + /** + * Means no firewall or nat. In the case of a privvate IPv4 address this + * would meand no internet connectivity through this address. The same + * applies to IPv6 link local addresses. In the case of a public (IPv4 or + * IPv6) address this means that there is unlimited connectivity. + */ + public static final int TYPE_OPEN_INTERNET = 1; + + /** + * Full cone NAT or firewall. + */ + public static final int TYPE_FULL_CONE = 2; + + /** + * Restricted cone NAT or firewall. + */ + public static final int TYPE_RESTRICTED_CONE = 3; + + /** + * Port restricted cone NAT or firewall. + */ + public static final int TYPE_PORT_RESTRICTED_CONE = 4; + + /** + * Symmetric NAT or firewall. + */ + public static final int TYPE_SYMMETRIC = 5; + + /** + * Determines firewall type. Could be one of symmetric, full cone, + * restricted cone, port restricted cone. + */ + private int type = -1; + + /** + * The time (in seconds) that address port bindings remain active on this + * firewall without the node sendind any packets. Bindings may and most + * probably will change during runtime. They are initially set to be equal + * to net.java.sip.communicator.service.netaddr.INITIAL_BINDINGS_LIFETIME. + */ + private int bindingsLifetime = 30; + + /** + * Indicates whether or not the firewall corresponding to this descriptor + * is also acting as NAT (are we using a globally routable ip address or + * not). + */ + private boolean isTranslatingAddresses = false; + + /** + * The public IP address of the firewall. In the case of a NAT this is + * the address that other would have to use to contact us. + */ + private InetAddress publicAddress = null; + + /** + * Constructs an empty firewall descirptor + */ + FirewallDescriptor() + { + super(); + } + + /** + * Set the type of this firewall. + * @param type the type of the firewall referenced by this descriptor. + */ + void setType(int type) + { + this.type = type; + } + + /** + * Returns the type of the firewall referenced by this desciptor + * @return the type of the firewall referenced by this desciptor. One of the + * TYPE_XXX fields of this class. + */ + public int getType() + { + return this.type; + } + + /** + * Sets the address preference subtrahend corresponding to the firewall + * referenced by this descriptor. The subtrahend is a value that is + * subtracted from AddressPreference-s of addresses behind this firewall. + * + * @param subtrahend the amount of address preference points that need to + * be subtracted from address preferences of oaddresses behind this + * firewall. + */ + void setPreferenceSubtrahend(AddressPreference subtrahend) + { + this.preferenceSubtrahend = subtrahend; + } + + /** + * Returns the address preference subtrahend corresponding to the firewall + * referenced by this descriptor. The subtrahend is a value that is + * subtracted from AddressPreference-s of addresses behind this firewall. + * + * @return the amount of address preference points that need to + * be subtracted from address preferences of oaddresses behind this + * firewall. + */ + public AddressPreference getAddressPreferenceSubtrahend() + { + return this.preferenceSubtrahend; + } + + /** + * Sets the public IP address of the firewall. In the case of a NAT this is + * the address that others would have to use to contact us. + * + * @param address the ip address of the nat + */ + void setPublicAddress(InetAddress address) + { + this.publicAddress = address; + } + + /** + * Returns the public IP address of the firewall. In the case of a NAT this + * is the address that others would have to use to contact us. + * + * @return the ip address of the nat + */ + public InetAddress getPublicAddress() + { + return this.publicAddress; + } + + /** + * Sets the time (in seconds) that address port bindings remain active on + * this firewall without the node sendind any packets. Bindings may and most + * probably will change during runtime. They are initially set to be equal + * to net.java.sip.communicator.service.netaddr.INITIAL_BINDINGS_LIFETIME. + * + * @param lifetime the number of seconds that bindings on this firewall + * remain active + */ + void setBindingsLifetime(int lifetime) + { + this.bindingsLifetime = lifetime; + } + + /** + * Returns the time (in seconds) that address port bindings remain active on + * this firewall without the node sendind any packets. Bindings may and most + * probably will change during runtime. They are initially set to be equal + * to net.java.sip.communicator.service.netaddr.INITIAL_BINDINGS_LIFETIME. + * + * @return lifetime the number of seconds that bindings on this firewall + * remain active + */ + public int getBindingsLifetime() + { + return this.bindingsLifetime; + } + + /** + * Specifies whether this is an address translating firewall (NAT) or not. + * @param isNAT a boolean specifying whether the firewall referenced by this + * descriptor is a NAT. + */ + void setTranslatingAddresses(boolean isNAT) + { + this.isTranslatingAddresses = isNAT; + } + + /** + * Determines whether this is an address translating firewall (NAT) or not. + * @return true if the firewall referenced by this descriptor is a NAT and + * false otherwise. + */ + public boolean isTranslatingAddresses() + { + return isTranslatingAddresses; + } +} diff --git a/src/net/java/sip/communicator/impl/netaddr/NetworkAddressManagerServiceImpl.java b/src/net/java/sip/communicator/impl/netaddr/NetworkAddressManagerServiceImpl.java new file mode 100644 index 0000000..8409033 --- /dev/null +++ b/src/net/java/sip/communicator/impl/netaddr/NetworkAddressManagerServiceImpl.java @@ -0,0 +1,634 @@ +/* + * 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.netaddr; + +import java.net.*; +import java.util.Enumeration; + +import net.java.sip.communicator.util.*; +import net.java.stun4j.client.SimpleAddressDetector; +import net.java.stun4j.*; +import net.java.sip.communicator.service.netaddr.*; +import net.java.sip.communicator.service.configuration.*; +import net.java.sip.communicator.service.configuration.event.*; +import java.text.*; + + +/** + * This implementation of the Network Address Manager allows you to + * intelligently retrieve the address of your localhost according to preferences + * specified in a number of properties like: + * <br> + * net.java.sip.communicator.STUN_SERVER_ADDRESS - the address of the stun + * server to use for NAT traversal + * <br> + * net.java.sip.communicator.STUN_SERVER_PORT - the port of the stun server + * to use for NAT traversal + * <br> + * java.net.preferIPv6Addresses - a system property specifying weather ipv6 + * addresses are to be preferred in address resolution (default is false for + * backward compatibility) + * <br> + * net.java.sip.communicator.common.PREFERRED_NETWORK_ADDRESS - the address + * that the user would like to use. (If this is a valid address it will be + * returned in getLocalhost() calls) + * <br> + * net.java.sip.communicator.common.PREFERRED_NETWORK_INTERFACE - the network + * interface that the user would like to use for fommunication (addresses + * belonging to that interface will be prefered when selecting a localhost + * address) + * + * @todo further explain the way the service works. explain address selection + * algorithms and priorities. + * + * @author Emil Ivov + * @author Pierre Floury + */ +public class NetworkAddressManagerServiceImpl + implements NetworkAddressManagerService, VetoableChangeListener +{ + private static Logger logger = + Logger.getLogger(NetworkAddressManagerServiceImpl.class); + + /** + * The name of the property containing the stun server address. + */ + private static final String PROP_STUN_SERVER_ADDRESS + = "net.java.sip.communicator.STUN_SERVER_ADDRESS"; + /** + * the port number of the stun server to use for NAT traversal + */ + private static final String PROP_STUN_SERVER_PORT + = "net.java.sip.communicator.STUN_SERVER_PORT"; + /** + * a system property specifying weather ipv6 + * addresses are to be preferred in address resolution (default is false for + * backward compatibility) + */ + private static final String PROP_PREF_IPV6_ADDRS + = "java.net.preferIPv6Addresses"; + + /** + * If an application has a preference to only use IPv4 sockets then this + * property can be set to true. + */ + private static final String PROP_PREF_IPV4_STACK + = "java.net.preferIPv4Stack"; + + /** + * the address that the user would like to use. (If this is a valid address + * it will be returned in getLocalhost() calls) + */ + private static final String PROP_PREFERRED_NET_ADDRESS + = "net.java.sip.communicator.common.PREFERRED_NETWORK_ADDRESS"; + + /** + * the network interface that the user would like to use for fommunication + * (addresses belonging to that interface will be prefered when selecting a + * localhost address) + */ + private static final String PROP_PREFERRED_NET_IFACE + = "net.java.sip.communicator.common.PREFERRED_NETWORK_INTERFACE"; + + /** The configuration service to use when retrieving conf property values*/ + private ConfigurationService configurationService = null; + + /** A stun4j address resolver */ + private SimpleAddressDetector detector = null; + + /** Specifies whether or not STUN should be used for NAT traversal */ + private boolean useStun = true; + private static final int RANDOM_PORT = 55055; + private static final String WINDOWS_AUTO_CONFIGURED_ADDRESS_PREFIX = "169"; + + /** + * A default constructor. + * + * @param configurationService the configruation service that this address + * manager should use for retrieving configuration properties. + */ + NetworkAddressManagerServiceImpl(ConfigurationService configurationService) + { + this.configurationService = configurationService; + } + + /** + * Initializes this network address manager service implementation and + * starts all processes/threads associated with this address manager, such + * as a stun firewall/nat detector, keep alive threads, binding lifetime + * discovery threads and etc. The method may also be used after a call to + * stop() as a reinitialization technique. + */ + public void start() + { + try + { + logger.logEntry(); + + // init stun + String stunAddressStr = null; + int port = -1; + stunAddressStr = configurationService.getString( + PROP_STUN_SERVER_ADDRESS); + String portStr = configurationService.getString( + PROP_STUN_SERVER_PORT); + + //in case the user prefers ipv6 addresses we don't want to use + //stun + boolean preferIPv6Addresses = Boolean.getBoolean( + PROP_PREF_IPV6_ADDRS); + + if (stunAddressStr == null + || portStr == null + || preferIPv6Addresses) + { + useStun = false; + //user doesn't want stun - bail out + return; + } + + port = Integer.valueOf(portStr).intValue(); + + detector = new SimpleAddressDetector( + new StunAddress(stunAddressStr, port)); + + if (logger.isDebugEnabled()) + logger.debug( + "Created a STUN Address detector for the following " + + "STUN server: " + + stunAddressStr + ":" + port); + + + try + { + detector.start(); + logger.debug("STUN server started;"); + } + catch (StunException ex) + { + logger.error( + "Failed to start the STUN Address Detector. " + + detector.toString()); + logger.debug("Disabling stun and continuing bravely!"); + detector = null; + useStun = false; + } + + //make sure that someone doesn't set invalid stun address and port + configurationService.addVetoableChangeListener( + PROP_STUN_SERVER_ADDRESS, this); + configurationService.addVetoableChangeListener( + PROP_STUN_SERVER_PORT, this); + + //don't register a property listener. reinitialization is supposed + //to only happen after a stop(), start() call seq + } + finally + { + logger.logExit(); + } + } + + /** + * Kills all threads/processes lauched by this thread and prepares it for + * shutdown. You may use this method as a reinitialization technique ( + * you'll have to call start afterwards) + */ + public void stop() + { + try + { + try{ + detector.shutDown(); + }catch (Exception ex){ + logger.debug("Failed to properly shutdown a stun detector: " + +ex.getMessage()); + + } + detector = null; + useStun = false; + + //remove the listeners + configurationService.removeVetoableChangeListener( + PROP_STUN_SERVER_ADDRESS, this); + + configurationService.removeVetoableChangeListener( + PROP_STUN_SERVER_PORT, this); + + } + finally + { + logger.logExit(); + } + + } + + /** + * Returns an InetAddress instance that represents the localhost, and that + * a socket can bind upon. + * + * @return an InetAddress instance representing the local host. The returned + * value may also contain the "any" inet address (i.e. 0.0.0.0 or ::0) + */ + public InetAddress getLocalHost() + { + return getLocalHost(true); + } + + + /** + * Returns an InetAddress instance that represents the localhost, and that + * a socket can bind upon. + * + * @param anyAddressIsAccepted are (0.0.0.0 / ::0) addresses accepted as a + * return value. + * @return the address that was detected the address of the localhost. + */ + public InetAddress getLocalHost(boolean anyAddressIsAccepted) + { + try + { + logger.logEntry(); + InetAddress localHost = null; + InetAddress mappedAddress = null; + InetAddress stunConfirmedAddress = null; + InetAddress linkLocalAddress = null; + InetAddress publicAddress = null; + String selectedInterface = null; + + //let's check whether the user has any preferences concerning addrs + String preferredAddr = + configurationService.getString(PROP_PREFERRED_NET_ADDRESS); + String preferredIface = + configurationService.getString(PROP_PREFERRED_NET_IFACE); + + boolean preferIPv4Stack = Boolean.getBoolean(PROP_PREF_IPV4_STACK); + boolean preferIPv6Addrs = Boolean.getBoolean(PROP_PREF_IPV6_ADDRS); + + try + { + //check whether we have a public address that matches one of + //the local interfaces if not - return the first one that + //is not the loopback + + //retrieve and store a STUN binding if possible + if (useStun) + { + StunAddress stunMappedAddress = + queryStunServer(RANDOM_PORT); + + mappedAddress = (stunMappedAddress == null) + ? null + : stunMappedAddress.getSocketAddress().getAddress(); + } + + Enumeration localIfaces = + NetworkInterface.getNetworkInterfaces(); + + //do a loop over all addresses of all interfaces and return + //the first that we judge correct. + + //interfaces loop + interfaces_loop: + while (localIfaces.hasMoreElements()) + { + NetworkInterface iFace = + (NetworkInterface) localIfaces.nextElement(); + + Enumeration addresses = iFace.getInetAddresses(); + + //addresses loop + addresses_loop: + while (addresses.hasMoreElements()) { + InetAddress address = + (InetAddress) addresses.nextElement(); + //ignore link local addresses + if (address.isAnyLocalAddress() + || address.isLinkLocalAddress() + || address.isLoopbackAddress() + || isWindowsAutoConfiguredIPv4Address(address)) + { + //address is phony - go on to the next one + continue addresses_loop; + } + //see whether this is the address used in STUN communic. + if (mappedAddress != null + && mappedAddress.equals(address)) { + if (logger.isDebugEnabled()) + logger.debug("Returninng localhost: Mapped " + + "address = Public address = " + + address); + //the addr matches the one seen by the STUN server + //no doubt that it's a working public + //address. + + stunConfirmedAddress = address; + } + //link local addr + else if (isLinkLocalIPv4Address(address)) + { + if (logger.isDebugEnabled()) + logger.debug("Found Linklocal ipv4 address " + + address); + linkLocalAddress = address; + } + //publicly routable addr + else { + if (logger.isDebugEnabled()) + logger.debug("Found a public address " + + address); + + //now befo we store this address, make sure we don't + //already have one that suits us better and bail out + //if this is the case + + if (//we already have the address prefferred by user + ( publicAddress != null + && preferredAddr!= null + && preferredAddr.equals(publicAddress. + getHostAddress())) + //we already have an address on an iface + //preferred by the user + ||(publicAddress != null + && selectedInterface != null + && preferredIface != null + && preferredIface.equals(selectedInterface)) + //in case we have an ipv4 addr and don't + //want to change it for an ipv6 + ||(publicAddress != null + && publicAddress instanceof Inet4Address + && address instanceof Inet6Address + && preferIPv4Stack) + //in case we have an ipv6 addr and don't + //want to change it for an ipv4 + ||(publicAddress != null + && publicAddress instanceof Inet6Address + && address instanceof Inet4Address + && !preferIPv4Stack) + ) + { + continue; + } + publicAddress = address; + selectedInterface = iFace.getDisplayName(); + } + }//addresses loop + }//interfaces loop + + //if we have an address confirmed by STUN msg exchanges - we'll + //return it unless the user had really insisted on IPv6 addresses. + if(stunConfirmedAddress != null + && ! preferIPv6Addrs){ + logger.debug("Returning stun confirmed address"); + return stunConfirmedAddress; + } + //return the address that was selected during the loop above. + if (publicAddress != null) { + logger.debug("Returning public address"); + return publicAddress; + } + if (linkLocalAddress != null) { + logger.debug("Returning link local address"); + return linkLocalAddress; + } + if (anyAddressIsAccepted) + localHost = new InetSocketAddress(RANDOM_PORT).getAddress(); + else + localHost = InetAddress.getLocalHost(); + } + catch (Exception ex) { + logger.error("Failed to determine the localhost address, " + +"returning the any address (0.0.0.0/::0)", ex); + //get the address part of an InetSocketAddress for a random port. + localHost = new InetSocketAddress(RANDOM_PORT).getAddress(); + } + if (logger.isDebugEnabled()) + logger.debug("Returning localhost address=" + localHost); + return localHost; + } + finally + { + logger.logExit(); + } + } + + /** + * The method queries a Stun server for a binding for the specified port. + * @param port the port to resolve (the stun message gets sent trhough that + * port) + * @return StunAddress the address returned by the stun server or null + * if an error occurred or no address was returned + */ + private StunAddress queryStunServer(int port) + { + + try{ + logger.logEntry(); + StunAddress mappedAddress = null; + if (detector != null && useStun) { + try { + mappedAddress = detector.getMappingFor(port); + if (logger.isDebugEnabled()) + logger.debug("For port:" + + port + "a Stun server returned the " + +"following mapping [" + mappedAddress); + } + catch (StunException ex) { + logger.error( + "Failed to retrive mapped address port:" +port, ex); + mappedAddress = null; + } + } + return mappedAddress; + } + finally{ + logger.logExit(); + } + } + + /** + * Determines whether the address is the result of windows auto configuration. + * (i.e. One that is in the 169.254.0.0 network) + * @param add the address to inspect + * @return true if the address is autoconfigured by windows, false otherwise. + */ + private static boolean isWindowsAutoConfiguredIPv4Address(InetAddress add) + { + return (add.getAddress()[0] & 0xFF) == 169 + && (add.getAddress()[1] & 0xFF) == 254; + } + + /** + * Determines whether the address is an IPv4 link local address. IPv4 link + * local addresses are those in the following networks: + * + * 10.0.0.0 to 10.255.255.255 + * 172.16.0.0 to 172.31.255.255 + * 192.168.0.0 to 192.168.255.255 + * + * @param add the address to inspect + * @return true if add is a link local ipv4 address and false if not. + */ + private static boolean isLinkLocalIPv4Address(InetAddress add) + { + if(add instanceof Inet4Address) + { + byte address[] = add.getAddress(); + if ( (address[0] & 0xFF) == 10) + return true; + if ( (address[0] & 0xFF) == 172 + && (address[1] & 0xFF) >= 16 && address[1] <= 31) + return true; + if ( (address[0] & 0xFF) == 192 + && (address[1] & 0xFF) == 168) + return true; + return false; + } + return false; + } + + /** + * Tries to obtain a mapped/public address for the specified port (possibly + * by executing a STUN query). + * + * @param port the port whose mapping we are interested in. + * @return a public address corresponding to the specified port or null + * if all attempts to retrieve such an address have failed. + */ + public InetSocketAddress getPublicAddressFor(int port) + { + try { + logger.logEntry(); + if (!useStun) { + logger.debug( + "Stun is disabled, skipping mapped address recovery."); + return new InetSocketAddress(getLocalHost(), port); + } + StunAddress mappedAddress = queryStunServer(port); + InetSocketAddress result = null; + if (mappedAddress != null) + result = mappedAddress.getSocketAddress(); + else { + //Apparently STUN failed. Let's try to temporarily disble it + //and use algorithms in getLocalHost(). ... We should probably + //eveng think about completely disabling stun, and not only + //temporarily. + //Bug report - John J. Barton - IBM + InetAddress localHost = getLocalHost(false); + result = new InetSocketAddress(localHost, port); + } + if (logger.isDebugEnabled()) + logger.debug("Returning mapping for port:" + + port +" as follows: " + result); + return result; + } + finally { + logger.logExit(); + } + } + + /** + * This method gets called when a bound property is changed. + * @param evt A PropertyChangeEvent object describing the event source + * and the property that has changed. + */ + public void propertyChange(PropertyChangeEvent evt) + { + //there's no point in implementing this method as we have no way of + //knowing whether the current property change event is the only event + //we're going to get or whether another one is going to follow.. + + //in the case of a STUN_SERVER_ADDRESS property change for example + //there's no way of knowing whether a STUN_SERVER_PORT property change + //will follow or not. + + //Reinitializaion will therefore only happen if the reinitialize() + //method is called. + } + + /** + * This method gets called when a property we're interested in is about to + * change. In case we don't like the new value we throw a + * PropertyVetoException to prevent the actual change from happening. + * + * @param evt a <code>PropertyChangeEvent</code> object describing the + * event source and the property that will change. + * @exception PropertyVetoException if we don't want the change to happen. + */ + public void vetoableChange(PropertyChangeEvent evt) throws + PropertyVetoException + { + if (evt.getPropertyName().equals(PROP_STUN_SERVER_ADDRESS)) + { + //make sure that we have a valid fqdn or ip address. + + //null or empty port is ok since it implies turning STUN off. + if (evt.getNewValue() == null) + return; + + String host = evt.getNewValue().toString(); + if (host.trim().length() == 0) + return; + + boolean ipv6Expected = false; + if (host.charAt(0) == '[') + { + // This is supposed to be an IPv6 litteral + if (host.length() > 2 && + host.charAt(host.length() - 1) == ']') + { + host = host.substring(1, host.length() - 1); + ipv6Expected = true; + } + else + { + // This was supposed to be a IPv6 address, but it's not! + throw new PropertyVetoException( + "Invalid address string" + host, evt); + } + } + + for(int i = 0; i < host.length(); i++) + { + char c = host.charAt(i); + if( Character.isLetterOrDigit(c)) + continue; + + if( (c != '.' && c!= ':') + ||( c == '.' && ipv6Expected) + ||( c == ':' && !ipv6Expected)) + throw new PropertyVetoException( + host + " is not a valid address nor host name", + evt); + } + + }//is prop_stun_server_address + else if (evt.getPropertyName().equals(PROP_STUN_SERVER_PORT)){ + + //null or empty port is ok since it implies turning STUN off. + if (evt.getNewValue() == null) + return; + + String port = evt.getNewValue().toString(); + if (port.trim().length() == 0) + return; + + try + { + Integer.valueOf(evt.getNewValue().toString()); + } + catch (NumberFormatException ex) + { + throw new PropertyVetoException( + port + " is not a valid port! " + ex.getMessage(), evt); + } + + + } + + } +} diff --git a/src/net/java/sip/communicator/impl/netaddr/StunClient.java b/src/net/java/sip/communicator/impl/netaddr/StunClient.java new file mode 100644 index 0000000..a89cc95 --- /dev/null +++ b/src/net/java/sip/communicator/impl/netaddr/StunClient.java @@ -0,0 +1,291 @@ +package net.java.sip.communicator.impl.netaddr; + +import java.net.*; + +import net.java.stun4j.*; +import net.java.stun4j.attribute.*; +import net.java.stun4j.message.*; +import net.java.stun4j.stack.*; +import net.java.sip.communicator.util.*; + +/** + * <p> + * This class implements the STUN Discovery Process as described by section 10.1 + * of rfc 3489. + * </p><p> + * The flow makes use of three tests. In test I, the client sends a + * STUN Binding Request to a server, without any flags set in the + * CHANGE-REQUEST attribute, and without the RESPONSE-ADDRESS attribute. + * This causes the server to send the response back to the address and + * port that the request came from. In test II, the client sends a + * Binding Request with both the "change IP" and "change port" flags + * from the CHANGE-REQUEST attribute set. In test III, the client sends + * a Binding Request with only the "change port" flag set. + * </p><p> + * The client begins by initiating test I. If this test yields no + * response, the client knows right away that it is not capable of UDP + * connectivity. If the test produces a response, the client examines + * the MAPPED-ADDRESS attribute. If this address and port are the same + * as the local IP address and port of the socket used to send the + * request, the client knows that it is not natted. It executes test + * II. + * </p><p> + * If a response is received, the client knows that it has open access + * to the Internet (or, at least, its behind a firewall that behaves + * like a full-cone NAT, but without the translation). If no response + * is received, the client knows its behind a symmetric UDP firewall. + * </p><p> + * In the event that the IP address and port of the socket did not match + * the MAPPED-ADDRESS attribute in the response to test I, the client + * knows that it is behind a NAT. It performs test II. If a response + * is received, the client knows that it is behind a full-cone NAT. If + * no response is received, it performs test I again, but this time, + * does so to the address and port from the CHANGED-ADDRESS attribute + * from the response to test I. If the IP address and port returned in + * the MAPPED-ADDRESS attribute are not the same as the ones from the + * first test I, the client knows its behind a symmetric NAT. If the + * address and port are the same, the client is either behind a + * restricted or port restricted NAT. To make a determination about + * which one it is behind, the client initiates test III. If a response + * is received, its behind a restricted NAT, and if no response is + * received, its behind a port restricted NAT. + * </p><p> + * This procedure yields substantial information about the operating + * condition of the client application. In the event of multiple NATs + * between the client and the Internet, the type that is discovered will + * be the type of the most restrictive NAT between the client and the + * Internet. The types of NAT, in order of restrictiveness, from most + * to least, are symmetric, port restricted cone, restricted cone, and + * full cone. + * </p><p> + * Typically, a client will re-do this discovery process periodically to + * detect changes, or look for inconsistent results. It is important to + * note that when the discovery process is redone, it should not + * generally be done from the same local address and port used in the + * previous discovery process. If the same local address and port are + * reused, bindings from the previous test may still be in existence, + * and these will invalidate the results of the test. Using a different + * local address and port for subsequent tests resolves this problem. + * An alternative is to wait sufficiently long to be confident that the + * old bindings have expired (half an hour should more than suffice). + * </p><p> + * <p>Organisation: <p> Louis Pasteur University, Strasbourg, France</p> + * <p>Network Research Team (http://www-r2.u-strasbg.fr)</p></p> + * @author Emil Ivov + * @version 0.1 + */ + +public class StunClient +{ + private static final Logger logger = + Logger.getLogger(StunClient.class); + /** + * Indicates whether the underlying stack has been initialized and started + * and that the discoverer is operational. + */ + private boolean started = false; + + /** + * The stack to use for STUN communication. + */ + private StunStack stunStack = null; + + /** + * The provider to send our messages through + */ + private StunProvider stunProvider = null; + + /** + * The point where we'll be listening. + */ + private NetAccessPointDescriptor apDescriptor = null; + + /** + * The address of the stun server + */ + private StunAddress serverAddress = null; + + /** + * A utility used to flatten the multithreaded architecture of the Stack + * and execute the discovery process in a synchronized manner + */ + private BlockingRequestSender requestSender = null; + + /** + * Creates a StunAddressDiscoverer. In order to use it one must start the + * discoverer. + * @param localAddress the address where the stach should bind. + */ + public StunClient(StunAddress localAddress) + { + apDescriptor = new NetAccessPointDescriptor(localAddress); + } + + /** + * Creates a StunAddressDiscoverer. In order to use it one must start the + * discoverer. + * @param apDescriptor the address where the stach should bind. + * @param serverAddress the address of the server to interrogate. + */ + public StunClient(NetAccessPointDescriptor apDescriptor, + StunAddress serverAddress) + { + this.apDescriptor = apDescriptor; + this.serverAddress = serverAddress; + + } + + /** + * Shuts down the underlying stack and prepares the object for garbage + * collection. + */ + public void shutDown() + { + try + { + stunStack.removeNetAccessPoint(apDescriptor); + } + catch (StunException ex) + { + logger.warn("Failed to remove NetAP: "+apDescriptor.toString(), ex); + } + stunStack = null; + stunProvider = null; + apDescriptor = null; + requestSender = null; + + this.started = false; + + } + + /** + * Puts the discoverer into an operational state. + * @throws StunException if we fail to bind or some other error occurs. + */ + public void start() throws StunException + { + stunStack = StunStack.getInstance(); + stunStack.start(); + + stunStack.installNetAccessPoint(apDescriptor); + + stunProvider = stunStack.getProvider(); + + requestSender = new BlockingRequestSender(stunProvider, apDescriptor); + + started = true; + } + + /** + * Sends a binding request to the specified server address. Both change IP + * and change port flags are set to false. + * @param serverAddress the address where to send the bindingRequest. + * @return The returned message encapsulating event or null if no message + * was received. + * @throws StunException if an exception occurs while sending the messge + */ + public StunMessageEvent doStunTestI(StunAddress serverAddress) throws + StunException + { + Request request = MessageFactory.createBindingRequest(); + + ChangeRequestAttribute changeRequest = + (ChangeRequestAttribute) request.getAttribute(Attribute. + CHANGE_REQUEST); + changeRequest.setChangeIpFlag(false); + changeRequest.setChangePortFlag(false); + StunMessageEvent evt = + requestSender.sendRequestAndWaitForResponse(request, serverAddress); + if (evt != null) + System.out.println("TEST I res=" + evt.getRemoteAddress().toString() + + " - " + evt.getRemoteAddress().getHostName()); + else + System.out.println("NO RESPONSE received to TEST I."); + return evt; + } + + /** + * Sends a binding request to the specified server address with both change + * IP and change port flags are set to true. + * @param serverAddress the address where to send the bindingRequest. + * @return The returned message encapsulating event or null if no message + * was received. + * @throws StunException if an exception occurs while sending the messge + */ + public StunMessageEvent doStunTestII(StunAddress serverAddress) throws + StunException + { + Request request = MessageFactory.createBindingRequest(); + + ChangeRequestAttribute changeRequest = + (ChangeRequestAttribute) request.getAttribute(Attribute. + CHANGE_REQUEST); + changeRequest.setChangeIpFlag(true); + changeRequest.setChangePortFlag(true); + + StunMessageEvent evt = + requestSender.sendRequestAndWaitForResponse(request, serverAddress); + if (evt != null) + System.out.println("Test II res=" + evt.getRemoteAddress().toString() + + " - " + evt.getRemoteAddress().getHostName()); + else + System.out.println("NO RESPONSE received to Test II."); + + return evt; + } + + /** + * Sends a binding request to the specified server address with only change + * port flag set to true and change IP flag - to false. + * @param serverAddress the address where to send the bindingRequest. + * @return The returned message encapsulating event or null if no message + * was received. + * @throws StunException if an exception occurs while sending the messge + */ + public StunMessageEvent doStunTestIII(StunAddress serverAddress) throws + StunException + { + Request request = MessageFactory.createBindingRequest(); + + ChangeRequestAttribute changeRequest = (ChangeRequestAttribute) request. + getAttribute(Attribute.CHANGE_REQUEST); + changeRequest.setChangeIpFlag(false); + changeRequest.setChangePortFlag(true); + + StunMessageEvent evt = + requestSender.sendRequestAndWaitForResponse(request, serverAddress); + if (evt != null) + System.out.println("Test III res=" + + evt.getRemoteAddress().toString() + + " - " + evt.getRemoteAddress().getHostName()); + else + System.out.println("NO RESPONSE received to Test III."); + + return evt; + } + + /** + * Makes shure the discoverer is operational and throws an + * StunException.ILLEGAL_STATE if that is not the case. + * @throws StunException ILLEGAL_STATE if the discoverer is not operational. + */ + private void checkStarted() throws StunException + { + if (!started) + throw new StunException(StunException.ILLEGAL_STATE, + "The Discoverer must be started before " + + "launching the discovery process!"); + } +} +/** + * Sample run results. + * + * TEST I res=/69.0.209.22:3478 - stun01bak.sipphone.com + * mapped address is=193.108.24.226./193.108.24.226:5678, name=193.108.24.226. + * backup server address is=69.0.208.27./69.0.208.27:3478, name=69.0.208.27. + * NO RESPONSE received to Test II. + * TEST I res=/69.0.208.27:3478 - stun01.sipphone.com + * NO RESPONSE received to Test III. + * The detected network configuration is: Port Restricted Cone NAT + * Your mapped public address is: 193.108.24.226./193. + */ diff --git a/src/net/java/sip/communicator/impl/netaddr/StunDiscoveryReport.java b/src/net/java/sip/communicator/impl/netaddr/StunDiscoveryReport.java new file mode 100644 index 0000000..af34bdc --- /dev/null +++ b/src/net/java/sip/communicator/impl/netaddr/StunDiscoveryReport.java @@ -0,0 +1,143 @@ +package net.java.sip.communicator.impl.netaddr; + +import net.java.stun4j.StunAddress; + +/** + * The class is used to deliver results from a STUN Discovery Process. It + * contains information about the NAT Server (or firewall )this client is behind, + * and a mapped address value (if discovered) + * + * + * <p>Organisation: <p> Louis Pasteur University, Strasbourg, France</p> + * <p>Network Research Team (http://www-r2.u-strasbg.fr)</p></p> + * @author Emil Ivov + * @version 0.1 + */ + +public class StunDiscoveryReport +{ + /** + * Indicates that NAT detection has failed or not yet initiated. + */ + public static final String UNKNOWN = "Unknown Network Configuration"; + + /** + * Means, there's no NAT or firewall. + */ + public static final String OPEN_INTERNET = "Open Internet Configuration"; + + /** + * Indicates that UDP communication is not possible. + */ + public static final String UDP_BLOCKING_FIREWALL = "UDP Blocking Firewall"; + + /** + * Means we are behind a symmetric udp firewall. + */ + public static final String SYMMETRIC_UDP_FIREWALL= "Symmetric UDP Firewall"; + + /** + * NAT type is full cone. + */ + public static final String FULL_CONE_NAT = "Full Cone NAT"; + + /** + * We are behind a symmetric nat. + */ + public static final String SYMMETRIC_NAT = "Symmetric NAT"; + + /** + * NAT type is Restricted Cone. + */ + public static final String RESTRICTED_CONE_NAT = "Restricted Cone NAT"; + + /** + * NAT type is port restricted cone. + */ + public static final String PORT_RESTRICTED_CONE_NAT= "Port Restricted Cone NAT"; + + private String natType = UNKNOWN; + + + private StunAddress publicAddress = null; + + /** + * Creates a discovery report with natType = UNKNOWN and a null public + * address. + */ + StunDiscoveryReport() + { + } + + /** + * Returns the type of the NAT described in the report. + * @return the type of the NAT that this report is about. + */ + public String getNatType() + { + return natType; + } + + /** + * Sets the type of the NAT indicated by the report. + * @param natType the type of the NAT. + */ + void setNatType(String natType) + { + this.natType = natType; + } + + /** + * Returns the public addressed discovered by a discovery process. + * @return an Inetner address for public use. + */ + public StunAddress getPublicAddress() + { + return publicAddress; + } + + /** + * Sets a public address. + * @param stunAddress An address that's accesible from everywhere. + */ + void setPublicAddress(StunAddress stunAddress) + { + this.publicAddress = stunAddress; + } + + + /** + * Compares this object with obj. Two reports are considered equal if and + * only if both have the same nat type and their public addresses are + * equal or are both null. + * @param obj the object to compare against. + * @return true if the two objects are equal and false otherwise. + */ + public boolean equals(Object obj) + { + if(obj == null + || !(obj instanceof StunDiscoveryReport)) + return false; + + if(obj == this) + return true; + + StunDiscoveryReport target = (StunDiscoveryReport)obj; + + return ( target.getNatType() == getNatType() + && ( getPublicAddress() == null && target.getPublicAddress() == null + || target.getPublicAddress().equals(getPublicAddress()))); + } + + /** + * Returns a readable representation of the report. + * @return a readable representation of the report. + */ + public String toString() + { + return "The detected network configuration is: " + getNatType() + "\n" + + "Your mapped public address is: " + getPublicAddress(); + } + + +} diff --git a/src/net/java/sip/communicator/impl/netaddr/netaddr.manifest.mf b/src/net/java/sip/communicator/impl/netaddr/netaddr.manifest.mf new file mode 100644 index 0000000..e8528f9 --- /dev/null +++ b/src/net/java/sip/communicator/impl/netaddr/netaddr.manifest.mf @@ -0,0 +1,14 @@ +Bundle-Activator: net.java.sip.communicator.impl.netaddr.Activator +Bundle-Name: Network address managment service +Bundle-Description: A bundle that provide a gesture of the ip addresses. prospere est le toi du pain d'epice +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +Import-Package: net.java.stun4j, + net.java.stun4j.client, + net.java.stun4j.StunAddress, + net.java.stun4j.StunException, + net.java.sip.communicator.service.configuration, + net.java.sip.communicator.service.configuration.event, + net.java.sip.communicator.util, + org.osgi.framework, +Export-Package: net.java.sip.communicator.service.netaddr, diff --git a/src/net/java/sip/communicator/impl/netaddr/res/netaddr.manifest.mf b/src/net/java/sip/communicator/impl/netaddr/res/netaddr.manifest.mf new file mode 100644 index 0000000..71dd4cb --- /dev/null +++ b/src/net/java/sip/communicator/impl/netaddr/res/netaddr.manifest.mf @@ -0,0 +1,7 @@ +Bundle-Name: Network address managment service +Bundle-Activator: net.java.sip.communicator.impl.netaddr.Activator +Export-Package: net.java.sip.communicator.service.netaddr; +Import-Package: net.java.sip.communicator.service.configuration,net.java.sip.communicator.service.configuration.event, net.java.sip.communicator.util,net.java.stun4j, net.java.sip.communicator.util;junit;junit.framework, junit.swingui;junit.textui;junit.awtui, junit.extensions;org.osgi.framework +Bundle-Description: A bundle that provide a gesture of the ip addresses. prospere est le toi du pain d'epice +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/sip/Activator.java b/src/net/java/sip/communicator/impl/protocol/sip/Activator.java new file mode 100644 index 0000000..ea9fae9 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/Activator.java @@ -0,0 +1,44 @@ +package net.java.sip.communicator.impl.protocol.sip; + +import org.osgi.framework.*; +import net.java.sip.communicator.util.*; + +/** + * Activates the SIP package + * @author Emil Ivov + */ +public class Activator + implements BundleActivator +{ + private Logger logger = Logger.getLogger(Activator.class.getName()); + + /** + * Called when this bundle is started so the Framework can perform the + * bundle-specific activities necessary to start this bundle. + * + * @param context The execution context of the bundle being started. + * @throws Exception If this method throws an exception, this bundle is + * marked as stopped and the Framework will remove this bundle's + * listeners, unregister all services registered by this bundle, and + * release all services used by this bundle. + */ + public void start(BundleContext context) throws Exception + { + logger.debug("Started."); + } + + /** + * 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. + * @throws Exception If this method throws an exception, the bundle is + * still marked as stopped, and the Framework will remove the bundle's + * listeners, unregister all services registered by the bundle, and + * release all services used by the bundle. + */ + public void stop(BundleContext context) throws Exception + { + logger.debug("Stopped."); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/SipProtocolProviderServiceImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/SipProtocolProviderServiceImpl.java new file mode 100644 index 0000000..0a859bf --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/SipProtocolProviderServiceImpl.java @@ -0,0 +1,230 @@ +/* + * 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.protocol.sip; + +import java.util.*; + +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; + +/** + * + * @author Emil Ivov + */ +public class SipProtocolProviderServiceImpl + implements ProtocolProviderService +{ + public static final String SIP_PROTOCOL_NAME = "SIP"; + /** + * Returns the short name of the protocol that the implementation of this + * provider is based upon, or in other words - SIP. + * + * @return a String containing the name of our protocol - SIP. + */ + public String getProtocolName() + { + return SIP_PROTOCOL_NAME; + } + + /** + * Registers the specified listener with this provider so that it would + * receive notifications on changes of its state or other properties such + * as its local address and display name. + * + * @param listener the listener to register. + * @todo Implement this + * net.java.sip.communicator.service.protocol.ProtocolProviderService + * method + */ + public void addProviderChangeListener(ProviderChangeListener listener) + { + } + + /** + * Requests the provider to enter into a status corresponding to the + * specified paramters. + * + * @param status the PresenceStatus as returned by + * getRequestableStatusSet + * @param statusMessage the message that should be set as the reason to + * enter that status + * @throws IllegalArgumentException if the status requested is not a + * valid PresenceStatus supported by this provider. + * @todo Implement this + * net.java.sip.communicator.service.protocol.ProtocolProviderService + * method + */ + public void enterStatus(PresenceStatus status, String statusMessage) throws + IllegalArgumentException + { + } + + /** + * Returns the protocol specific contact instance representing the local + * user. + * + * @return the Contact (address, phone number, or uin) that the Provider + * implementation is communicating on behalf of. + * @todo Implement this + * net.java.sip.communicator.service.protocol.ProtocolProviderService + * method + */ + public Contact getLocalContact() + { + return null; + } + + /** + * Many communications protocols have well known logos that users are + * familiar with. + * + * @return byte[] a 32x32 protocol logo or representative image. + * @todo Implement this + * net.java.sip.communicator.service.protocol.ProtocolProviderService + * method + */ + public byte[] getProviderImage() + { + return null; + } + + /** + * Returns a String containing a human readable string representation of + * the provider. + * + * @return a String representation of this provider. + * @todo Implement this + * net.java.sip.communicator.service.protocol.ProtocolProviderService + * method + */ + public String getProviderName() + { + return ""; + } + + /** + * Returns a string representation of the registration service that is + * used by this provider or null if none is used. + * + * @return a string representing (the address of) the service being used. + * @todo Implement this + * net.java.sip.communicator.service.protocol.ProtocolProviderService + * method + */ + public String getRegistrationServer() + { + return ""; + } + + /** + * Returns the set of PresenceStatus objects that a user of this service + * may request the provider to enter. + * + * @return Iterator a PresenceStatus array containing "enterable" status + * instances. + * @todo Implement this + * net.java.sip.communicator.service.protocol.ProtocolProviderService + * method + */ + public Iterator getRequestableStatusSet() + { + return null; + } + + /** + * Returns a PresenceStatus instance representing the state this provider + * is currently in. + * + * @return PresenceStatus + * @todo Implement this + * net.java.sip.communicator.service.protocol.ProtocolProviderService + * method + */ + public PresenceStatus getStatus() + { + return null; + } + + /** + * Returns an array containing all operation sets supported by the + * current implementation. + * + * @return an array of OperationSet-s supported by this protocol + * provider implementation. + * @todo Implement this + * net.java.sip.communicator.service.protocol.ProtocolProviderService + * method + */ + public OperationSet[] getSupportedOperationSets() + { + return null; + } + + /** + * Initialized the service implementation, and puts it in a sate where it + * could interoperate with other services. + * + * @todo Implement this + * net.java.sip.communicator.service.protocol.ProtocolProviderService + * method + */ + public void initialize() + { + } + + /** + * Returns true if the provider service implementation is initialized and + * ready for use by other services, and false otherwise. + * + * @return boolean + * @todo Implement this + * net.java.sip.communicator.service.protocol.ProtocolProviderService + * method + */ + public boolean isInitialized() + { + return false; + } + + /** + * Removes the specified listener. + * + * @param listener the listener to remove. + * @todo Implement this + * net.java.sip.communicator.service.protocol.ProtocolProviderService + * method + */ + public void removeProviderChangeListener(ProviderChangeListener listener) + { + } + + /** + * Allows the user interface to plugin an object that would handle + * incoming authentication challenges. + * + * @param authority SecurityAuthority + * @todo Implement this + * net.java.sip.communicator.service.protocol.ProtocolProviderService + * method + */ + public void setSecurityAuthority(SecurityAuthority authority) + { + } + + /** + * Makes the service implementation close all open sockets and release + * any resources that it might have taken and prepare for + * shutdown/garbage collection. + * + * @todo Implement this + * net.java.sip.communicator.service.protocol.ProtocolProviderService + * method + */ + public void shutdown() + { + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf new file mode 100644 index 0000000..85893e5 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf @@ -0,0 +1,7 @@ +Bundle-Activator: net.java.sip.communicator.impl.protocol.sip.Activator +Bundle-Name: SIP Communicator SIP Protocol Provider +Bundle-Description: A bundle that implements the Protocol Provider package over SIP. +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +Import-Package: net.java.sip.communicator.util, +Export-Package: net.java.sip.communicator.protocol, diff --git a/src/net/java/sip/communicator/impl/resources/Activator.java b/src/net/java/sip/communicator/impl/resources/Activator.java new file mode 100644 index 0000000..152811b --- /dev/null +++ b/src/net/java/sip/communicator/impl/resources/Activator.java @@ -0,0 +1,14 @@ +package net.java.sip.communicator.impl.resources; + +import org.ungoverned.gravity.servicebinder.GenericActivator; + +/** + * Invoke "Service Binder" to parse the service XML and register + * all services. + * + * @author Emil Ivov + * @author Alexander Pelov + */ +public class Activator extends GenericActivator { + +} diff --git a/src/net/java/sip/communicator/impl/resources/FileAccessServiceImpl.java b/src/net/java/sip/communicator/impl/resources/FileAccessServiceImpl.java new file mode 100644 index 0000000..1dc551a --- /dev/null +++ b/src/net/java/sip/communicator/impl/resources/FileAccessServiceImpl.java @@ -0,0 +1,267 @@ +/* + * 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; + +import java.io.File; +import java.io.IOException; + +import net.java.sip.communicator.service.configuration.ConfigurationService; +import net.java.sip.communicator.service.resources.FileAccessService; +import net.java.sip.communicator.util.Assert; +import net.java.sip.communicator.util.Logger; + +/** + * Default FileAccessService implementation. + * + * @author Alexander Pelov + */ +public class FileAccessServiceImpl implements FileAccessService { + + /** + * The logger for this class. + */ + private static Logger logger = Logger + .getLogger(FileAccessServiceImpl.class); + + /** + * The file prefix for all temp files. + */ + public static final String TEMP_FILE_PREFIX = "SIPCOMM"; + + /** + * The file suffix for all temp files. + */ + public static final String TEMP_FILE_SUFFIX = "TEMP"; + + /** + * List of available configuration services. + */ + private ConfigurationService configurationService = null; + + /** + * An synchronization object. + * + * A lock should be obtained whenever the configuration service is + * accessed. + */ + private Object syncRoot = new Object(); + + /** + * Set the configuration service. + * + * @param configurationService + */ + public void setConfigurationService( + ConfigurationService configurationService) + { + synchronized(this.syncRoot) { + this.configurationService = configurationService; + logger.debug("New configuration service registered."); + } + } + + /** + * Remove a configuration service. + * + * @param configurationService + */ + public void unsetConfigurationService( + ConfigurationService configurationService) + { + synchronized(this.syncRoot) { + if(this.configurationService == configurationService) { + this.configurationService = null; + logger.debug("Configuration service unregistered."); + } + } + } + + public File getTemporaryFile() throws IOException { + File retVal = null; + + try { + logger.logEntry(); + + retVal = TempFileManager.createTempFile(TEMP_FILE_PREFIX, + TEMP_FILE_SUFFIX); + } finally { + logger.logExit(); + } + + return retVal; + } + + public File getTemporaryDirectory() throws IOException { + File file = getTemporaryFile(); + + if(!file.delete()) { + throw new IOException("Could not create temporary directory, " + + "because: could not delete temporary file."); + } + if(!file.mkdirs()) { + throw new IOException("Could not create temporary directory"); + } + + return file; + } + + /** + * @throws IllegalStateException + * Thrown if the configuration service is not set + */ + public File getPrivatePersistentFile(String fileName) throws Exception { + Assert.assertNonNull(fileName, "Parameter fileName should be non-null"); + + File file = null; + + try { + logger.logEntry(); + + + String fullPath = getFullPath(fileName); + file = this.accessibleFile(fullPath, fileName); + + if(file == null) { + throw new SecurityException("Insufficient rights to access " + + "this file in current user's home directory: " + + file.getAbsolutePath()); + } + } finally { + logger.logExit(); + } + + return file; + } + + public File getPrivatePersistentDirectory(String dirName) + throws Exception + { + Assert.assertNonNull(dirName, "Parameter dirName should be non-null"); + + String fullPath = getFullPath(dirName); + File dir = new File(fullPath, dirName); + + if(dir.exists()) { + if(!dir.isDirectory()) { + throw new RuntimeException("Could not create directory " + + "because: A file exists with this name:" + + dir.getAbsolutePath()); + } + } else { + if(!dir.mkdirs()) { + throw new IOException("Could not create directory"); + } + } + + return dir; + } + + public File getPrivatePersistentDirectory(String[] dirNames) throws Exception { + Assert.assertNonNull(dirNames, "Parameter dirNames should be non-null"); + Assert.assertTrue(dirNames.length > 0, "dirNames.length should be > 0"); + + StringBuffer dirName = new StringBuffer(); + for(int i = 0; i < dirNames.length; i++) { + if(i > 0) { + dirName.append(File.separatorChar); + } + dirName.append(dirNames[i]); + } + + return getPrivatePersistentDirectory(dirName.toString()); + } + + private String getFullPath(String fileName) { + Assert.assertNonNull(fileName, "The filename should be non-null."); + + String userhome = null; + String sipSubdir = null; + + // Obtain configuration service lock + synchronized(this.syncRoot) { + Assert.assertNonNull(this.configurationService, + "The configurationService should be non-null."); + + userhome = this.configurationService.getString(FileAccessService.CONFPROPERTYKEY_USER_HOME); + sipSubdir = this.configurationService.getString(FileAccessService.CONFPROPERTYKEY_SIP_DIRECTORY); + } + + + if(userhome == null) { + userhome = System.getProperty(FileAccessService.SYSPROPERTYKEY_USER_HOME); + if(userhome == null) { + throw new IllegalStateException("No user home directory specified in system's environment"); + } + } + if(sipSubdir == null) { + sipSubdir = FileAccessService.DEFAULT_SIP_DIRECTORY; + } + + if(!userhome.endsWith(File.separator)) { + userhome += File.separator; + } + if(!sipSubdir.endsWith(File.separator)) { + sipSubdir += File.separator; + } + + return userhome + sipSubdir; + } + + + /** + * Checks if a file exists and if it is writable or readable. If not - checks + * if the user has a write privileges to the containing directory. + * + * If those conditions are met it returns a File in the directory with a + * fileName. If not - returns null. + * + * @param homedir + * @param fileName + * @return Returns null if the file does not exist and cannot be created. + * Otherwise - an object to this file + * @throws IOException Thrown if the home directory cannot be created + */ + private File accessibleFile(String homedir, String fileName) throws IOException { + File file = null; + + try { + logger.logEntry(); + + homedir = homedir.trim(); + if(!homedir.endsWith(File.separator)) { + homedir += File.separator; + } + + file = new File(homedir + fileName); + if (file.canRead() || file.canWrite()) { + return file; + } + + File homedirFile = new File(homedir); + + if(!homedirFile.exists()) { + logger.debug("Creating home directory : " + homedirFile.getAbsolutePath()); + if(!homedirFile.mkdirs()) { + String message = "Could not create the home directory : " + + homedirFile.getAbsolutePath(); + + logger.debug(message); + throw new IOException(message); + } + logger.debug("Home directory created : " + homedirFile.getAbsolutePath()); + } else if (!homedirFile.canWrite()) { + file = null; + } + + } finally { + logger.logExit(); + } + + return file; + } + +} diff --git a/src/net/java/sip/communicator/impl/resources/TempFileManager.java b/src/net/java/sip/communicator/impl/resources/TempFileManager.java new file mode 100644 index 0000000..a365125 --- /dev/null +++ b/src/net/java/sip/communicator/impl/resources/TempFileManager.java @@ -0,0 +1,212 @@ +/* + * 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; + +import java.io.*; +import java.util.logging.Level; +import java.util.logging.Logger; + + +/** + * Generates and properly cleans up temporary files. Similar to {@link + * File#createTempFile(java.lang.String, java.lang.String)}, this class + * provides a static method to create temporary files. The temporary files will + * be created in a special directory to be cleaned up the next time this class + * is loaded by the JVM. This functionality is required because Win32 platforms + * will not allow the JVM to delete files that are open. This causes problems + * with items such as JARs that get opened by a URLClassLoader and can + * therefore not be deleted by the JVM (including deleteOnExit). + * + * The caller should not need to create an instance of this class, although it + * is possible. Simply use the static methods to perform the required + * operations. Note that all files created by this class should be + * considered as deleted at JVM exit (although the actual deletion may be + * delayed). If persistent temporary files are required, use {@link + * java.io.File} instead. + * + * Refer to Sun bugs 4171239 and 4950148 for more details. + */ +public class TempFileManager +{ + + /** + * Creates a temporary file in the proper directory to allow for cleanup + * after execution. This method delegates to {@link + * File#createTempFile(java.lang.String, java.lang.String, java.io.File)} so + * refer to it for more documentation. Any file created using this method + * should be considered as deleted at JVM exit; therefore, do not use this + * method to create files that need to be persistent between application + * runs. + * + * @param prefix the prefix string used in generating the file name; + * must be at least three characters long + * @param suffix the suffix string to be used in generating the file's + * name; may be null, in which case the suffix ".tmp" will be used + * @return an abstract pathname denoting a newly created empty + * file + * @throws IOException if a file could not be created + */ + public static File createTempFile(String prefix, String suffix) + throws IOException + { + // Check to see if you have already initialized a temp directory + // for this class. + if (sTmpDir == null) + { + // Initialize your temp directory. You use the java temp directory + // property, so you are sure to find the files on the next run. + String tmpDirName = System.getProperty("java.io.tmpdir"); + File tmpDir = File.createTempFile(TEMP_DIR_PREFIX, ".tmp", + new File(tmpDirName)); + + // Delete the file if one was automatically created by the JVM. + // You are going to use the name of the file as a directory name, + // so you do not want the file laying around. + tmpDir.delete(); + + // Create a lock before creating the directory so + // there is no race condition with another application trying + // to clean your temp dir. + File lockFile = new File(tmpDirName, tmpDir.getName() + ".lck"); + lockFile.createNewFile(); + + // Set the lock file to delete on exit so it is properly cleaned + // by the JVM. This will allow the TempFileManager to clean + // the overall temp directory next time. + lockFile.deleteOnExit(); + + // Make a temp directory that you will use for all future requests. + if (!tmpDir.mkdirs()) + { + throw new IOException("Unable to create temporary directory:" + + tmpDir.getAbsolutePath()); + } + + sTmpDir = tmpDir; + } + + // Generate a temp file for the user in your temp directory + // and return it. + return File.createTempFile(prefix, suffix, sTmpDir); + } + + + /** + * Deletes all of the files in the given directory, recursing into any sub + * directories found. Also deletes the root directory. + * + * @param rootDir the root directory to be recursively deleted + * @throws IOException if any file or directory could not be deleted + */ + private static void recursiveDelete(File rootDir) + throws IOException + { + // Select all the files + File[] files = rootDir.listFiles(); + for (int i = 0; i < files.length; i++) + { + // If the file is a directory, we will + // recursively call delete on it. + if (files[i].isDirectory()) + { + recursiveDelete(files[i]); + } + else + { + // It is just a file so we are safe to + // delete it + if (!files[i].delete()) + { + throw new IOException("Could not delete: " + files[i].getAbsolutePath()); + } + } + } + + // Finally, delete the root directory now + // that all of the files in the directory have + // been properly deleted. + if (!rootDir.delete()) + { + throw new IOException("Could not delete: " + rootDir.getAbsolutePath()); + } + } + + + /** + * The prefix for the temp directory in the system temp directory + */ + private final static String TEMP_DIR_PREFIX = "tmp-mgr-"; + + /** + * The temp directory to generate all files in + */ + private static File sTmpDir = null; + + /** + * Static block used to clean up any old temp directories found -- the JVM + * will run this block when a class loader loads the class. + */ + static + { + // Clean up any old temp directories by listing + // all of the files, using a filter that will + // return only directories that start with your + // prefix. + FileFilter tmpDirFilter = + new FileFilter() + { + public boolean accept(File pathname) + { + return (pathname.isDirectory() && + pathname.getName().startsWith(TEMP_DIR_PREFIX)); + } + }; + + // Get the system temp directory and filter the files. + String tmpDirName = System.getProperty("java.io.tmpdir"); + File tmpDir = new File(tmpDirName); + File[] tmpFiles = tmpDir.listFiles(tmpDirFilter); + + // Find all the files that do not have a lock by + // checking if the lock file exists. + for (int i = 0; i < tmpFiles.length; i++) + { + File tmpFile = tmpFiles[i]; + + // Create a file to represent the lock and test. + File lockFile = new File(tmpFile.getParent(), tmpFile.getName() + ".lck"); + if (!lockFile.exists()) + { + // Delete the contents of the directory since + // it is no longer locked. + Logger.getLogger("default").log(Level.FINE, + "TempFileManager::deleting old temp directory " + tmpFile); + + try + { + recursiveDelete(tmpFile); + } + catch (IOException ex) + { + // You log at a fine level since not being able to delete + // the temp directory should not stop the application + // from performing correctly. However, if the application + // generates a lot of temp files, this could become + // a disk space problem and the level should be raised. + Logger.getLogger("default").log(Level.INFO, + "TempFileManager::unable to delete " + tmpFile.getAbsolutePath()); + + // Print the exception. + ByteArrayOutputStream ostream = new ByteArrayOutputStream(); + ex.printStackTrace(new PrintStream(ostream)); + + Logger.getLogger("default").log(Level.FINE, ostream.toString()); + } + } + } + } +} diff --git a/src/net/java/sip/communicator/impl/resources/resources.manifest.mf b/src/net/java/sip/communicator/impl/resources/resources.manifest.mf new file mode 100644 index 0000000..f73bbc6 --- /dev/null +++ b/src/net/java/sip/communicator/impl/resources/resources.manifest.mf @@ -0,0 +1,22 @@ +Bundle-Activator: net.java.sip.communicator.impl.resources.Activator +Bundle-Name: Resources Management Service Provider +Bundle-Description: A bundle that implements the resource management package. +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +Import-Package: org.ungoverned.gravity.servicebinder, + org.osgi.framework, + net.java.sip.communicator.service.configuration, + net.java.sip.communicator.service.resources, + net.java.sip.communicator.impl.resources, + org.w3c.dom, + org.xml.sax, + javax.xml.parsers, + org.apache.xml.serializer, + javax.xml.transform, + javax.xml.transform.dom, + javax.xml.transform.stream, + net.java.sip.communicator.util, + net.java.sip.communicator.util.xml, +Export-Package: net.java.sip.communicator.service.resources, + net.java.sip.communicator.impl.resources, +Metadata-Location: /net/java/sip/communicator/impl/resources/resources.metadata.xml
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/resources/resources.metadata.xml b/src/net/java/sip/communicator/impl/resources/resources.metadata.xml new file mode 100644 index 0000000..aa37573 --- /dev/null +++ b/src/net/java/sip/communicator/impl/resources/resources.metadata.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<bundle> + <component class="net.java.sip.communicator.impl.resources.FileAccessServiceImpl"> + <provides service="net.java.sip.communicator.service.resources.FileAccessService"/> + + <!-- + One and only one reference to a configuration service is needed. + If this service is stopped, the file access service is also uninstalled + --> + <requires service="net.java.sip.communicator.service.configuration.ConfigurationService" + filter="" + policy="static" + cardinality="1..1" + bind-method="setConfigurationService" + unbind-method="unsetConfigurationService" /> + </component> +</bundle> diff --git a/src/net/java/sip/communicator/launcher/Main.java b/src/net/java/sip/communicator/launcher/Main.java new file mode 100644 index 0000000..ca296be --- /dev/null +++ b/src/net/java/sip/communicator/launcher/Main.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.launcher; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import net.java.sip.communicator.util.Logger; + +import org.ungoverned.oscar.Noscar; +import org.ungoverned.oscar.cache.DefaultBundleCache; +import org.ungoverned.oscar.util.MutablePropertyResolver; +import org.ungoverned.oscar.util.MutablePropertyResolverImpl; + + +/** + * This is the main entry point of the program. It launches the Oscar OSGI + * Framework. + * + * @author Alexander Pelov + */ +public class Main { + + public static final String CLIENT_RUN_PROPERTIES_FILE = "lib/oscar.client.run.properties"; + + /** + * The logger for this class. + */ + private static Logger log = Logger.getLogger(Main.class); + + private static Noscar oscar; + + public static void main(String[] args) { + + int errorStatus = -1; + + try { + log.logEntry(); + + String oscarSystemPropertiesFile = System.getProperty("oscar.system.properties"); + if(oscarSystemPropertiesFile == null) { + oscarSystemPropertiesFile = Main.CLIENT_RUN_PROPERTIES_FILE; + } + + Properties props = Main.loadProperties(oscarSystemPropertiesFile); + + // See if the profile name property was specified. + String profileName = System.getProperty(DefaultBundleCache.CACHE_PROFILE_PROP); + + // See if the profile directory property was specified. + String profileDirName = System.getProperty(DefaultBundleCache.CACHE_PROFILE_DIR_PROP); + + // If no profile or profile directory is specified in the + // properties, then ask for a profile name. + if ((profileName == null) && (profileDirName == null)) + { + boolean cacheProfileSet = props.containsKey(DefaultBundleCache.CACHE_PROFILE_PROP); + boolean cacheProfileDirSet = props.containsKey(DefaultBundleCache.CACHE_PROFILE_DIR_PROP); + + if(!cacheProfileSet && !cacheProfileDirSet) { + props.put(DefaultBundleCache.CACHE_PROFILE_PROP, ".sip-communicator"); + } + } + + MutablePropertyResolver propertyResolver = + new MutablePropertyResolverImpl(props); + + Main.oscar = new Noscar(); + Main.oscar.start(propertyResolver, null); + + errorStatus = 0; + } catch(Exception e) { + log.error("Error occured while starting Oscar OSGI", e); + + errorStatus = -1; + } finally { + log.logExit(); + } + + System.exit(errorStatus); + } + + private static Properties loadProperties(String file) throws IOException { + int pos = file.indexOf(':'); + + // Test if the protocol is defined. + // It is set if ':' exists in the filename and if + // there is no path separator before it. + // + // Several characters are tested for file separator candidates: + // Window's '\', Unix's '/', and the separator of the current + // system.. Who knows.. maybe some day someone will use @ for file + // separator on @ix.. ;) + if(pos >= 0 && + Main.isPosBeforeChar(file, pos, File.separatorChar) && + Main.isPosBeforeChar(file, pos, '\\') && + Main.isPosBeforeChar(file, pos, '/')) + { + file = file.substring(pos+1); + } + + InputStream in = new FileInputStream(file); + + Properties props = null; + + try { + props = new Properties(); + props.load(in); + } finally { + try { + in.close(); + } catch(Exception e) {} + } + + return props; + } + + private static boolean isPosBeforeChar(String text, int pos, char c) { + int charPos = text.indexOf(c); + + return charPos < 0 || pos < charPos; + } + +} diff --git a/src/net/java/sip/communicator/service/callhistory/CallHistoryService.java b/src/net/java/sip/communicator/service/callhistory/CallHistoryService.java new file mode 100644 index 0000000..911e050 --- /dev/null +++ b/src/net/java/sip/communicator/service/callhistory/CallHistoryService.java @@ -0,0 +1,15 @@ +/* + * 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.callhistory; + +import net.java.sip.communicator.service.history.HistoryReader; + +/** + * @author Alexander Pelov + */ +public interface CallHistoryService extends HistoryReader { +} diff --git a/src/net/java/sip/communicator/service/configuration/ConfigurationService.java b/src/net/java/sip/communicator/service/configuration/ConfigurationService.java new file mode 100644 index 0000000..ee15197 --- /dev/null +++ b/src/net/java/sip/communicator/service/configuration/ConfigurationService.java @@ -0,0 +1,201 @@ +/* + * 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.configuration; + +import net.java.sip.communicator.service.configuration.event.*; +import java.io.*; + +import net.java.sip.communicator.util.xml.*; + +/** + * The configuration services provides a centralized approach of storing + * persistent configuration data. + * + * @author Emil Ivov + */ +public interface ConfigurationService +{ + /** + * Sets the property with the specified name to the specified value. Calling + * this method would first trigger a PropertyChangeEvent that will + * be dispatched to all VetoableChangeListeners. In case no complaints + * (PropertyVetoException) have been received, the property will be actually + * changed and a PropertyChangeEvent will be dispatched. + * <p> + * @param propertyName the name of the property to change. + * @param property the new value of the specified property. + * @throws PropertyVetoException in case the changed has been refused by + * at least one propertychange listener. + */ + public void setProperty(String propertyName, Object property) + throws PropertyVetoException; + + /** + * Sets the property with the specified name to the specified. Calling + * this method would first trigger a PropertyChangeEvent that will + * be dispatched to all VetoableChangeListeners. In case no complaints + * (PropertyVetoException) have been received, the property will be actually + * changed and a PropertyChangeEvent will be dispatched. This method also + * allows the caller to specify whether or not the specified property is a + * system one. + * <p> + * @param propertyName the name of the property to change. + * @param property the new value of the specified property. + * @param isSystem specifies whether or not the property being is a System + * property and should be resolved against the system + * property set + * @throws PropertyVetoException in case the changed has been refused by + * at least one propertychange listener. + */ + public void setProperty(String propertyName, + Object property, + boolean isSystem) + throws PropertyVetoException; + + /** + * Returns the value of the property with the specified name or null if no + * such property exists. + * @param propertyName the name of the property that is being queried. + * @return the value of the property with the specified name. + */ + public Object getProperty(String propertyName); + + /** + * Returns the String value of the specified property and null in case no + * property value was mapped against the specified propertyName, or in + * case the returned property string had zero length or contained + * whitespaces only. + * + * @param propertyName the name of the property that is being queried. + * @return the result of calling the property's toString method and null in + * case there was no vlaue mapped against the specified + * <code>propertyName</code>, or the returned string had zero length or + * contained whitespaces only. + */ + public String getString(String propertyName); + + /** + * Adds a PropertyChangeListener to the listener list. The listener is + * registered for all properties in the current configuration. + * <p> + * @param listener the PropertyChangeListener to be added + */ + public void addPropertyChangeListener(PropertyChangeListener listener); + + /** + * Removes a PropertyChangeListener from the listener list. + * <p> + * @param listener the PropertyChangeListener to be removed + */ + public void removePropertyChangeListener(PropertyChangeListener listener); + + /** + * Adds a PropertyChangeListener to the listener list for a specific + * property. In case a property with the specified name does not exist the + * listener is still added and would only be taken into account from the + * moment such a property is set by someone. + * <p> + * @param propertyName one of the property names listed above + * @param listener the PropertyChangeListener to be added + */ + public void addPropertyChangeListener(String propertyName, + PropertyChangeListener listener); + + /** + * Removes a PropertyChangeListener from the listener list for a specific + * property. This method should be used to remove PropertyChangeListeners + * that were registered for a specific property. The method has no effect + * when called for a listener that was not registered for that specifiec + * property. + * <p> + * + * @param propertyName a valid property name + * @param listener the PropertyChangeListener to be removed + */ + public void removePropertyChangeListener(String propertyName, + PropertyChangeListener listener); + + /** + * Adds a VetoableChangeListener to the listener list. The listener is + * registered for all properties in the configuration. + * <p> + * @param listener the VetoableChangeListener to be added + */ + public void addVetoableChangeListener(VetoableChangeListener listener); + + /** + * Removes a VetoableChangeListener from the listener list. + * <p> + * + * @param listener the VetoableChangeListener to be removed + */ + public void removeVetoableChangeListener(VetoableChangeListener listener); + + /** + * Adds a VetoableChangeListener to the listener list for a specific + * property. + * <p> + * + * @param propertyName one of the property names listed above + * @param listener the VetoableChangeListener to be added + */ + public void addVetoableChangeListener(String propertyName, + VetoableChangeListener listener); + + /** + * Removes a VetoableChangeListener from the listener list for a specific + * property. + * <p> + * + * @param propertyName a valid property name + * @param listener the VetoableChangeListener to be removed + */ + public void removeVetoableChangeListener(String propertyName, + VetoableChangeListener listener); + + /** + * Store the current set of properties back to the configuration file. The + * name of the configuration file is queried from the system property + * net.java.sip.communicator.PROPERTIES_FILE_NAME, and is set to + * sip-communicator.xml in case the property does not contain a valid file + * name. The location might be one of three possibile, checked in the + * following order: <br> + * 1. The current directory. <br> + * 2. The sip-communicator directory in the user.home + * ($HOME/.sip-communicator) + * 3. A location in the classpath (such as the sip-communicator jar file). + * <p> + * In the last case the file is copied to the sip-communicator configuration + * directory right after being extracted from the classpath location. + * + * @throws IOException in case storing the configuration failed. + */ + public void storeConfiguration() + throws IOException; + + /** + * Deletes the current configuration and reloads it from the configuration + * file. The + * name of the configuration file is queried from the system property + * net.java.sip.communicator.PROPERTIES_FILE_NAME, and is set to + * sip-communicator.xml in case the property does not contain a valid file + * name. The location might be one of three possibile, checked in the + * following order: <br> + * 1. The current directory. <br> + * 2. The sip-communicator directory in the user.home + * ($HOME/.sip-communicator) + * 3. A location in the classpath (such as the sip-communicator jar file). + * <p> + * In the last case the file is copied to the sip-communicator configuration + * directory right after being extracted from the classpath location. + * @throws IOException in case reading the configuration failes + * @throws XMLException in case parsing the configuration file has failed + */ + public void reloadConfiguration() + throws IOException, XMLException; + +} diff --git a/src/net/java/sip/communicator/service/configuration/PropertyVetoException.java b/src/net/java/sip/communicator/service/configuration/PropertyVetoException.java new file mode 100644 index 0000000..dd57f95 --- /dev/null +++ b/src/net/java/sip/communicator/service/configuration/PropertyVetoException.java @@ -0,0 +1,50 @@ +/* + * 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.configuration; + +import net.java.sip.communicator.service.configuration.event.*; +import net.java.sip.communicator.util.xml.*; +import net.java.sip.communicator.impl.configuration.xml.*; + +/** + * A PropertyVetoException is thrown when a proposed change to a + * property represents an unacceptable value. + * + * @author Emil Ivov + */ +public class PropertyVetoException + extends Exception +{ + /** + * A PropertyChangeEvent describing the vetoed change. + * @serial + */ + private PropertyChangeEvent evt; + + /** + * Constructs a <code>PropertyVetoException</code> with a + * detailed message. + * + * @param mess Descriptive message + * @param evt A PropertyChangeEvent describing the vetoed change. + */ + public PropertyVetoException(String mess, PropertyChangeEvent evt) + { + super(mess); + this.evt = evt; + } + + /** + * Gets the vetoed <code>PropertyChangeEvent</code>. + * + * @return A PropertyChangeEvent describing the vetoed change. + */ + public PropertyChangeEvent getPropertyChangeEvent() + { + return evt; + } +} diff --git a/src/net/java/sip/communicator/service/configuration/event/PropertyChangeEvent.java b/src/net/java/sip/communicator/service/configuration/event/PropertyChangeEvent.java new file mode 100644 index 0000000..641a8c5 --- /dev/null +++ b/src/net/java/sip/communicator/service/configuration/event/PropertyChangeEvent.java @@ -0,0 +1,109 @@ +/* + * 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.configuration.event; + +import net.java.sip.communicator.service.configuration.*; +import net.java.sip.communicator.util.xml.*; +import net.java.sip.communicator.impl.configuration.xml.*; + +/** + * A "ConfigurationChange" event gets delivered whenever a someone changes a + * configuration property. A ConfigurationEvent object is sent as an + * argument to the ConfigurationChangeListener methods. + * <P> + * Normally ConfigurationChangeEvents are accompanied by the name and the old + * and new values of the changed property. If the new value is a primitive + * type (such as int or boolean) it must be wrapped as the + * corresponding java.lang.* Object type (such as Integer or Boolean). + * <P> + * Null values may be provided for the old and the new values if their + * true values are not known. + * <P> + * An event source may send a null object as the name to indicate that an + * arbitrary set of if its properties have changed. In this case the + * old and new values should also be null. + * <P> + * In the case where the event reflects the change of a constrained property, + * it will first be dispatched to all propertyWillChange methods and only in + * case that none of them has objected (no ChangeVetoException has been thrown) + * the propertyChange method is called. + * + * @author Emil Ivov + */ +public class PropertyChangeEvent + extends java.util.EventObject +{ + + /** + * name of the property that changed. May be null, if not known. + * @serial + */ + private String propertyName; + + /** + * New value for property. May be null if not known. + * @serial + */ + private Object newValue; + + /** + * Previous value for property. May be null if not known. + * @serial + */ + private Object oldValue; + + /** + * Constructs a new <code>PropertyChangeEvent</code>. + * + * @param source The bean that fired the event. + * @param propertyName The programmatic name of the property + * that was changed. + * @param oldValue The old value of the property. + * @param newValue The new value of the property. + */ + public PropertyChangeEvent(Object source, String propertyName, + Object oldValue, Object newValue) + { + super(source); + this.propertyName = propertyName; + this.newValue = newValue; + this.oldValue = oldValue; + } + + /** + * Gets the programmatic name of the property that was changed. + * + * @return The programmatic name of the property that was changed. + * May be null if multiple properties have changed. + */ + public String getPropertyName() + { + return propertyName; + } + + /** + * Sets the new value for the property, expressed as an Object. + * + * @return The new value for the property, expressed as an Object. + * May be null if multiple properties have changed. + */ + public Object getNewValue() + { + return newValue; + } + + /** + * Gets the old value for the property, expressed as an Object. + * + * @return The old value for the property, expressed as an Object. + * May be null if multiple properties have changed. + */ + public Object getOldValue() + { + return oldValue; + } +} diff --git a/src/net/java/sip/communicator/service/configuration/event/PropertyChangeListener.java b/src/net/java/sip/communicator/service/configuration/event/PropertyChangeListener.java new file mode 100644 index 0000000..fe9b05f --- /dev/null +++ b/src/net/java/sip/communicator/service/configuration/event/PropertyChangeListener.java @@ -0,0 +1,29 @@ +/* + * 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.configuration.event; + +import net.java.sip.communicator.service.configuration.*; +import net.java.sip.communicator.util.xml.*; +import net.java.sip.communicator.impl.configuration.xml.*; + +/** + * A "ConfigurationChange" event gets fired whenever a configuration property + * changes. Depending on whether the property was constrained or not, the + * propertyChange or vetoableChange methods get called. + * + * @author Emil Ivov + */ +public interface PropertyChangeListener +{ + /** + * This method gets called when a bound property is changed. + * @param evt A PropertyChangeEvent object describing the event source + * and the property that has changed. + */ + void propertyChange(PropertyChangeEvent evt); + +} diff --git a/src/net/java/sip/communicator/service/configuration/event/VetoableChangeListener.java b/src/net/java/sip/communicator/service/configuration/event/VetoableChangeListener.java new file mode 100644 index 0000000..2f09263 --- /dev/null +++ b/src/net/java/sip/communicator/service/configuration/event/VetoableChangeListener.java @@ -0,0 +1,46 @@ +/* + * 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.configuration.event; + +import java.util.*; +import net.java.sip.communicator.service.configuration.*; +import net.java.sip.communicator.util.xml.*; +import net.java.sip.communicator.impl.configuration.xml.*; + +/** + * A VetoableChange event gets fired whenever a property is about to change. + * One can register a VetoableChangeListener with the ConfigurationService so as + * to be notified in advance of any property updates. The purpose of a + * VetoableChaneListener is that it allows registered instances to veto or in + * other words cancel events by throwing a PropertyVetoException. In case + * none of the registered listeners has thrown an exception, the property is + * changed and a propertyChange event is dispatched to all registered + * PropertyChangeListener-s + * + * @author Emil Ivov + */ +public interface VetoableChangeListener + extends EventListener +{ + + /** + * This method gets called when a constrained property is about to change. + * Note that the method only warns about the change and in case none of + * the interested listeners vetos it (i.e. no PropertyVetoException + * is thrown) the propedrtyChange method will be called next to indicate + * that the change has taken place. In case you don't want to be notified + * for pending changes over constained properties you should provide + * an empty implementation of the method. + * + * @param evt a <code>PropertyChangeEvent</code> object describing the + * event source and the property that has changed. + * @exception PropertyVetoException if the recipient wishes the property + * change to be rolled back. + */ + void vetoableChange(PropertyChangeEvent evt) + throws PropertyVetoException; +} diff --git a/src/net/java/sip/communicator/service/configuration/gravity-metadata.xml b/src/net/java/sip/communicator/service/configuration/gravity-metadata.xml new file mode 100644 index 0000000..4827372 --- /dev/null +++ b/src/net/java/sip/communicator/service/configuration/gravity-metadata.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<bundle> + <!-- + This metadata file instructs the Gravity Service Binder to + create one instance of "SpellCheckServiceImpl". It also + tells the generic activator that this instance implements the + "SpellCheckService" service interface and that it has an + aggregate dependency on "DictionaryService" services. Since + the service dependency on dictionary services has a lower + cardinality of one, the generic activator will create the instance + and offer its spell check service only when there is at least + one dictionary service available. The service dependency is + "dynamic", which means that dictionary service availability + will be monitored dynamically at runtime and it also tells the + generic activator which methods to call when adding and removing + dictionary services. + --> + <instance class="tutorial.example7.SpellCheckServiceImpl"> + <service interface="net.java.sip.communicator.service.configuration.ConfigurationService"/> + <requires + service="tutorial.example2.service.DictionaryService" + filter="(Language=*)" + cardinality="1..n" + policy="dynamic" + bind-method="addDictionary" + unbind-method="removeDictionary" + /> + </instance> +</bundle> diff --git a/src/net/java/sip/communicator/service/gui/UIService.java b/src/net/java/sip/communicator/service/gui/UIService.java new file mode 100644 index 0000000..05127b1 --- /dev/null +++ b/src/net/java/sip/communicator/service/gui/UIService.java @@ -0,0 +1,155 @@ +/* + * 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.gui; + +import net.java.sip.communicator.service.protocol.*; + +/** + * The UIService provides an interface towards towards that part of the + * SIP Communicator that interacts with users and offers them a way to make, + * accept, manage or handup calls. + * + * In order for the UIService to support a communications protocol, a + * Provider implementation of the specified protocol needs to be registered with + * the service. + * + * Plugins using the service in order to add featuress to the user interface + * may do that using one of the addXxx() methods. + * + * + * @author Emil Ivov + */ +public interface UIService +{ + public static final String COMPONENT_CONSTRAINT_MENU_FILE = "File"; + public static final String COMPONENT_CONSTRAINT_MENU_VIEW = "View"; + public static final String COMPONENT_CONSTRAINT_MENU_TOOLS = "Tools"; + public static final String COMPONENT_CONSTRAINT_MENU_SETTINGS = "Settings"; + public static final String COMPONENT_CONSTRAINT_MENU_HELP = "Help"; + public static final String COMPONENT_CONSTRAINT_MENU_CALL = "Call"; + public static final String COMPONENT_CONSTRAINT_MENU_ACCOUNT = "Account"; + public static final String COMPONENT_CONSTRAINT_MENU_CONTACT = "Contact"; + public static final String COMPONENT_CONSTRAINT_MENU_BAR = "Menu Bar"; + + public static final String UI_LIB_SWING = "Swing"; + public static final String UI_LIB_SWT = "SWT"; + public static final String UI_LIB_AWT = "AWT"; + + /** + * Registers the specified telephony provider with the user interface. The + * PhoneUIService implementation will automatically add itself as a state + * listener of the specified provider and react in accordance with states + * coming from that listener. It will also deliver all relevant user + * call control requests to that provider. + * @param provider the provider to register. + */ + public void registerProvider(ProtocolProviderService provider); + + /** + * Specifies whether or not the phone ui should be visible (In case for + * example we'd only like a sys tray icon or a contact list to show). + * @param visible a boolean specifying whether the phone ui should be visible + */ + public void setVisible(boolean visible); + + //services offered to bundles/plugins that would like to interact with the + //gui. + /** + * Returns an array of Call objects containing the current set of ongoing + * calls. One could obtain specific call participants through by querying + * Call methods + * @return an array of Call objects reflecting on-going calls. + */ + public Call[] getActiveCalls(); + + /** + * Returns the name of the library used to implement the service. In case + * the implementation is using Swing, SWT, or pure AWT it MUST return one of + * the UI_LIB_XXX constants. The method may be used by plugins that would + * like to retrieve ui components + * @return String + */ + public String getUiLibName(); + + /** + * Returns an array of UI lib names indicating that the implementation is + * able to handle components registered by external plugins/bungles in case + * they are implemented using one of the returned lib names. The lib names + * returned by this method should be one or more of the UI_LIB_XXX constants, + * but callers of this method must properly handle unknown Strings which might + * be returned by implementations of future versions of this service. + * + * @return an array containing one or more UI_LIB_XXX constants. + */ + public String[] getSupportedUiLibNames(); + + /** + * Adds the specified menuItem to the specified parent menu. The parent String + * MUST be one of the MENU_XXX constants. It is up to the service + * implementation to verify that "menuItem" is an instance of a class + * compatible with the gui library used by it. If this is not the case and + * adding the requested object would not be possible the implementation + * MUST through an exception. + * + * @param parent one of the MENU_XXX string constants indicating the parent + * menu that this menuItem should be added to. + * @param menuItem the item to add. + * @throws ClassCastException if the menuItem is an + * instance of a class not supported by the service implementation. + * @throws IllegalArgumentException if the specified parent is not + * recognized by the implementation (note that implementations MUST properly + * handle all MENU_XXX strings as eventual parents even if they do not + * correspond to a menu with the same title and may be organized at the will + * of the implementor). + */ + public void addMenuItem(String parent, Object menuItem) + throws ClassCastException, IllegalArgumentException; + + /** + * Adds the specified UI component to the user interface according to the + * provided string constraint. The method is meant to be used by plugins or + * bundles that would like to add components to the user interface. The + * <code>constraint</code> string is used by the implementation to determine + * the place where the component should be added. The <code>constraint</code> + * String SHOULD be one of the COMPONENT_CONSTRAINT_XXX constants. It is up + * to the service implementation to verify that <code>component</code> is an + * instance of a class compatible with the gui library used by it. If this + * is not the case and adding the requested object would not be possible the + * implementation MUST through a ClassCastException exception. + * Implementations of this service MUST understant and know how to handle + * all COMPONENT_CONSTRAINT_XXX Strings defined by this interface, they + * MAY also define additional constraints. In case the addComponent method + * is called with a <code>constraint</code> that the implementation does + * not understand it MUST through a java.lang.IllegalArgumentException <br> + * <br> + * @param component the component we'd like to add + * @param constraint a String (possibly one of the COMPONENT_CONSTRAINT_XXX + * strings) indicating the place where the component should be added. + * @throws ClassCastException if <code>component</code> is not an + * instance of a class supported by the service implementation. An SWT impl + * would, for example through a ClassCastException if handed a + * java.awt.Component + * @throws IllegalArgumentException if the specified <code>constraint</code> + * is not recognized by the implementation (note that implementations + * MUST properly handle all COMPONENT_CONSTRAINT_XXX strings. + */ + public void addComponent(Object component, String constraint) + throws ClassCastException, IllegalArgumentException; + + public void addUserActionListener(); + + //========================== CONFIG ====================================== + //maybe add a method to show the config dialog + + //========================== AuthenticationService ======================= + //these should probably go to a different service + public void requestAuthentication(String realm, String userName, + char[] password); + public String getAuthenticationUserName(); + + //get main frame (for dialogs) +} diff --git a/src/net/java/sip/communicator/service/gui/event/UserActionListener.java b/src/net/java/sip/communicator/service/gui/event/UserActionListener.java new file mode 100644 index 0000000..8a4fa1e --- /dev/null +++ b/src/net/java/sip/communicator/service/gui/event/UserActionListener.java @@ -0,0 +1,44 @@ +/* + * 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.gui.event; + +import net.java.sip.communicator.service.protocol.event.*; + +/** + * The UserActionListener allows interested parties (such as a telephony + * protocol implementation) to register for notifications upon user requests + * relating to conversation management (such as establishing or ending a call + * with a specified call participant). The reason that we use such an + * EventListener rather that putting all these methods in a Provider and + * letting the Provider implementation handle them is that the user interface + * is not supposed to show any intelligence concerning telephony. It is not, + * for example, supposed to know that sip:emcho@sipphone.com is a sip URI and + * not a phone number. Listening telephony providers on the other hand would be + * able to recognize URIs they know how to handle and handle the event as well + * as call its consume() method so that it is not dispatched to other + * listeners. + * @author Emil Ivov + */ +public interface UserActionListener + extends java.util.EventListener +{ + /** + * + * @param evt CalleeInvitationEvent + */ + public void handleHangupRequest(CallParticipantControlEvent evt); + + public void handleAnswerRequest(CallParticipantControlEvent evt); + + /** + * @todo add some a target url to the call control listener + * @param evt UserCallControlEvent + */ + public void handleTransferRequest(CallParticipantControlEvent evt); + + public void handleExitRequest(); +} diff --git a/src/net/java/sip/communicator/service/history/DefaultQueryResultSet.java b/src/net/java/sip/communicator/service/history/DefaultQueryResultSet.java new file mode 100644 index 0000000..2dc21bb --- /dev/null +++ b/src/net/java/sip/communicator/service/history/DefaultQueryResultSet.java @@ -0,0 +1,65 @@ +/* + * 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.history; + +import java.util.NoSuchElementException; +import java.util.Vector; + +import net.java.sip.communicator.service.history.records.HistoryRecord; + +/** + * @author Alexander Pelov + */ +public class DefaultQueryResultSet implements QueryResultSet { + + private Vector records = new Vector(); + private int currentPos = -1; + + public DefaultQueryResultSet(Vector records) { + this.records = records; + } + + public HistoryRecord nextRecord() throws NoSuchElementException { + return (HistoryRecord)this.next(); + } + + public HistoryRecord prevRecord() throws NoSuchElementException { + return (HistoryRecord)this.prev(); + } + + public boolean hasPrev() { + return this.currentPos-1 >= 0; + } + + public Object prev() throws NoSuchElementException { + this.currentPos--; + + if(this.currentPos < 0) { + throw new NoSuchElementException(); + } + + return records.get(this.currentPos); + } + + public boolean hasNext() { + return this.currentPos+1 < this.records.size(); + } + + public Object next() { + this.currentPos++; + + if(this.currentPos >= this.records.size()) { + throw new NoSuchElementException(); + } + + return records.get(this.currentPos); + } + + public void remove() throws UnsupportedOperationException { + throw new UnsupportedOperationException("Cannot remove elements " + + "from underlaying collection."); + } +} diff --git a/src/net/java/sip/communicator/service/history/History.java b/src/net/java/sip/communicator/service/history/History.java new file mode 100644 index 0000000..b71a1e5 --- /dev/null +++ b/src/net/java/sip/communicator/service/history/History.java @@ -0,0 +1,38 @@ +/* + * 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.history; + +import net.java.sip.communicator.service.history.records.HistoryRecordStructure; + +/** + * @author Alexander Pelov + */ +public interface History { + + /** + * @return Returns an object which can be used to read and query + * this history. + */ + HistoryReader getReader(); + + /** + * @return Returns an object which can be used to append records to + * this history. + */ + HistoryWriter getWriter(); + + /** + * @return Returns the ID of this history. + */ + HistoryID getID(); + + /** + * @return Returns the structure of the history records in this history. + */ + HistoryRecordStructure getHistoryRecordsStructure(); + +} diff --git a/src/net/java/sip/communicator/service/history/HistoryID.java b/src/net/java/sip/communicator/service/history/HistoryID.java new file mode 100644 index 0000000..f1b4714 --- /dev/null +++ b/src/net/java/sip/communicator/service/history/HistoryID.java @@ -0,0 +1,216 @@ +/* + * 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.history; + +import net.java.sip.communicator.util.Assert; + +/** + * Object used to uniquely identify a group of history records. + * + * @author Alexander Pelov + */ +public class HistoryID { + + private String[] id; + + private String stringRepresentation; + + private int hashCode; + + private HistoryID(String[] id) { + this.id = id; + + StringBuffer buff = new StringBuffer(); + for(int i = 0; i < id.length; i++) { + if(i > 0) buff.append(' '); + buff.append(this.id[i]); + } + + this.stringRepresentation = buff.toString(); + this.hashCode = this.stringRepresentation.hashCode(); + } + + /** + * Create a HistoryID from a raw ID. You can pass any kind of + * strings and they will be safely converted to valid IDs. + */ + public static HistoryID createFromRawID(String[] rawid) { + Assert.assertNonNull(rawid, "Parameter RAWID should be non-null"); + Assert.assertTrue(rawid.length > 0, "RAWID.length should be > 0"); + + String[] id = new String[rawid.length]; + for(int i = 0; i < rawid.length; i++) { + id[i] = HistoryID.readableHash(rawid[i]); + } + + return new HistoryID(id); + } + + /** + * Create a HistoryID from a valid ID. You should pass only + * valid IDs (ones produced from readableHash). + * + * @throws IllegalArgumentException Thrown if a string from the ID is not + * valid an exception. + */ + public static HistoryID createFromID(String[] id) + throws IllegalArgumentException + { + Assert.assertNonNull(id, "Parameter ID should be non-null"); + Assert.assertTrue(id.length > 0, "ID.length should be > 0"); + + for(int i = 0; i < id.length; i++) { + if(!HistoryID.isIDValid(id[i])) { + throw new IllegalArgumentException("Not a valid ID: " + id[i]); + } + } + + String[] newID = new String[id.length]; + System.arraycopy(id, 0, newID, 0, id.length); + return new HistoryID(newID); + } + + public String[] getID() { + return this.id; + } + + public String toString() { + return this.stringRepresentation; + } + + public int hashCode() { + return this.hashCode; + } + + public boolean equals(Object obj) { + boolean eq = false; + + if(obj instanceof HistoryID) { + String[] id = ((HistoryID)obj).id; + + if(this.id.length == id.length) { + for(int i = 0; i < id.length; i++) { + String s1 = id[i]; + String s2 = this.id[i]; + + if(!((s1 == s2) || (s1 != null && s1.equals(s2)))) { + eq = false; + break; + } + } + } + } + + return eq; + } + + /** + * An one-way function returning a "human readable" containing no special + * characters. All characters _, a-z, A-Z, 0-9 are kept unchainged. + * All other are replaced with _ and the word is postfixed with + * %HASHCODE, where HASHCODE is the hexadecimal hash value of the + * original string. If there are no special characters the word is + * not postfixed. + * + * Note: This method does not use URLEncoder, because in url-encoding + * the * sign is considered as "safe". + * + * @param rawString The string to be hashed. + * @return The human-readable hash. + */ + public static String readableHash(String rawString) { + StringBuffer encodedString = new StringBuffer(rawString); + boolean addHash = false; + + for(int i = 0; i < encodedString.length(); i++) { + if(HistoryID.isSpecialChar(encodedString.charAt(i))) { + addHash = true; + encodedString.setCharAt(i, '_'); + } + } + + if(addHash) { + encodedString.append('%'); + encodedString.append(Integer.toHexString(rawString.hashCode())); + } + + return encodedString.toString(); + } + + /** + * Tests if an ID is valid. + */ + private static boolean isIDValid(String id) { + boolean isValid = true; + + int pos = id.indexOf('%'); + if(pos < 0) { + // There is no % in the id. In order to be valid all characters + // should be non-special + isValid = !hasSpecialChar(id); + } else { + // There is a % sign in the id. In order to be valid it has + // to be in the form X..X%Y..Y, where there should be no + // special characters in X..X, and Y..Y should be a hexadecimal + // number + if(pos+1 < id.length()) { + String start = id.substring(0, pos); + String end = id.substring(pos+1); + + // Check X..X + isValid = !hasSpecialChar(start); + if(isValid) { + // OK; Check Y..Y + try { + Integer.parseInt(end, 16); + // OK + isValid = true; + } catch(Exception e) { + // Not OK + isValid = false; + } + } + } else { + // The % sign is in the beginning - bad ID. + isValid = false; + } + } + + return isValid; + } + + /** + * Tests if a character is a special one. A character is special + * if it is not in the range _, a-z, A-Z, 0-9. + * + * @param c The character to test. + * @return Returns true if the character is special. False otherwise. + */ + private static boolean isSpecialChar(char c) { + return (c != '_') && + (c < 'A' || c > 'Z') && + (c < 'a' || c > 'z') && + (c < '0' || c > '9'); + } + + /** + * Tests there is a special character in a string. + */ + private static boolean hasSpecialChar(String str) { + boolean hasSpecialChar = false; + + for(int i = 0; i < str.length(); i++) { + if(isSpecialChar(str.charAt(i))) { + hasSpecialChar = false; + break; + } + } + + return hasSpecialChar; + } + +} diff --git a/src/net/java/sip/communicator/service/history/HistoryReader.java b/src/net/java/sip/communicator/service/history/HistoryReader.java new file mode 100644 index 0000000..8ae3f3d --- /dev/null +++ b/src/net/java/sip/communicator/service/history/HistoryReader.java @@ -0,0 +1,74 @@ +/* + * 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.history; + +import java.util.Date; + +/** + * @author Alexander Pelov + */ +public interface HistoryReader { + + /** + * Searches the history for all records with timestamp + * after <code>startDate</code>. + * + * @throws RuntimeException Thrown if an exception occurs during + * the execution of the query, such as internal IO error. + */ + QueryResultSet findByStartDate(Date startDate) + throws RuntimeException; + + /** + * Searches the history for all records with timestamp + * before <code>endDate</code>. + * + * @throws RuntimeException Thrown if an exception occurs during + * the execution of the query, such as internal IO error. + */ + QueryResultSet findByEndDate(Date endDate) + throws RuntimeException; + + /** + * Searches the history for all records with timestamp + * between <code>startDate</code> and <code>endDate</code>. + * + * @throws RuntimeException Thrown if an exception occurs during + * the execution of the query, such as internal IO error. + */ + QueryResultSet findByPeriod(Date startDate, Date endDate) + throws RuntimeException; + + /** + * Searches the history for all records containing the <code>keyword</code>. + * + * @throws RuntimeException Thrown if an exception occurs during + * the execution of the query, such as internal IO error. + */ + QueryResultSet findByKeyword(String keyword) + throws RuntimeException; + + /** + * Searches the history for all records containing all <code>keywords</code>. + * + * @throws RuntimeException Thrown if an exception occurs during + * the execution of the query, such as internal IO error. + */ + QueryResultSet findByKeywords(String[] keywords) + throws RuntimeException; + + /** + * Searches for all history records containing all <code>keywords</code>, + * with timestamp between <code>startDate</code> and <code>endDate</code>. + * + * @throws RuntimeException Thrown if an exception occurs during + * the execution of the query, such as internal IO error. + */ + QueryResultSet findByText(Date startDate, Date endDate, String[] keywords) + throws UnsupportedOperationException; + +} diff --git a/src/net/java/sip/communicator/service/history/HistoryService.java b/src/net/java/sip/communicator/service/history/HistoryService.java new file mode 100644 index 0000000..1d8cc5e --- /dev/null +++ b/src/net/java/sip/communicator/service/history/HistoryService.java @@ -0,0 +1,62 @@ +/* + * 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.history; + +import java.io.IOException; +import java.util.Iterator; + +import net.java.sip.communicator.service.history.records.HistoryRecordStructure; + +/** + * This service provides the functionality to store history records. The + * records are called <code>HistoryRecord</code>s and are grouped by ID. + * + * The ID may be used to set hierarchical structure. In a typical usage one + * may set the first string to be the userID, and the second - the service name. + * + * @author Alexander Pelov + */ +public interface HistoryService { + + /** + * Returns the IDs of all existing histories. + * + * @return An iterator to a list of IDs. + */ + Iterator getExistingIDs(); + + /** + * Returns the history associated with this ID. + * + * @param id The ID of the history. + * @return Returns the history with this ID. + * @throws IllegalArgumentException Thrown if there is no such history. + */ + History getHistory(HistoryID id) + throws IllegalArgumentException; + + /** + * Tests if a history with the given ID exists. + * + * @param id The ID to test. + * @return True if a history with this ID exists. False otherwise. + */ + boolean isHistoryExisting(HistoryID id); + + /** + * Creates a new history for this ID. + * + * @param id The ID of the history to be created. + * @param recordStructure The structure of the data. + * @return Returns the history with this ID. + * @throws IllegalArgumentException Thrown if such history already exists. + * @throws IOException Thrown if the history could not be created due to + * a IO error. + */ + History createHistory(HistoryID id, HistoryRecordStructure recordStructure) + throws IllegalArgumentException, IOException; +} diff --git a/src/net/java/sip/communicator/service/history/HistoryWriter.java b/src/net/java/sip/communicator/service/history/HistoryWriter.java new file mode 100644 index 0000000..cba2bd1 --- /dev/null +++ b/src/net/java/sip/communicator/service/history/HistoryWriter.java @@ -0,0 +1,36 @@ +/* + * 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.history; + +import java.io.IOException; + +import net.java.sip.communicator.service.history.records.HistoryRecord; + +/** + * @author Alexander Pelov + */ +public interface HistoryWriter { + + /** + * Stores the passed record complying with the historyRecordStructure. + * + * @param record The record to be added. + * + * @throws IOException + */ + void addRecord(HistoryRecord record) throws IOException; + + /** + * Stores the passed propertyValues complying with the historyRecordStructure. + * + * @param propertyValues The values of the record. + * + * @throws IOException + */ + void addRecord(String[] propertyValues) throws IOException; + +} diff --git a/src/net/java/sip/communicator/service/history/QueryResultSet.java b/src/net/java/sip/communicator/service/history/QueryResultSet.java new file mode 100644 index 0000000..968c0c6 --- /dev/null +++ b/src/net/java/sip/communicator/service/history/QueryResultSet.java @@ -0,0 +1,39 @@ +/* + * 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.history; + +import java.util.NoSuchElementException; + +import net.java.sip.communicator.service.history.records.HistoryRecord; +import net.java.sip.communicator.util.BidirectionalIterator; + + +/** +* +* @author Alexander Pelov +*/ +public interface QueryResultSet extends BidirectionalIterator { + + /** + * A strongly-typed variant of <code>next()</code>. + * + * @return the next history record. + * + * @throws NoSuchElementException iteration has no more elements. + */ + HistoryRecord nextRecord() throws NoSuchElementException; + + /** + * A strongly-typed variant of <code>prev()</code>. + * + * @return the previous history record. + * + * @throws NoSuchElementException iteration has no more elements. + */ + HistoryRecord prevRecord() throws NoSuchElementException; + +} diff --git a/src/net/java/sip/communicator/service/history/records/HistoryRecord.java b/src/net/java/sip/communicator/service/history/records/HistoryRecord.java new file mode 100644 index 0000000..7572892 --- /dev/null +++ b/src/net/java/sip/communicator/service/history/records/HistoryRecord.java @@ -0,0 +1,90 @@ +/* + * 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.history.records; + +import java.util.Date; + +import net.java.sip.communicator.util.Assert; + +/** + * @author Alexander Pelov + */ +public class HistoryRecord { + + private Date timestamp; + private String[] propertyNames; + private String[] propertyValues; + + /** + * Constructs an entry containing multiple name-value pairs, where the names + * are taken from the defined structure. The timestamp is set to the time this + * object is created. + * + * @param entryStructure + * @param propertyValues + */ + public HistoryRecord(HistoryRecordStructure entryStructure, String[] propertyValues) { + this(entryStructure.getPropertyNames(), propertyValues, new Date()); + } + + /** + * Constructs an entry containing multiple name-value pairs, where the name is not + * unique. The timestamp is set to the time this object is created. + * + * @param propertyNames + * @param propertyValues + */ + public HistoryRecord(String[] propertyNames, String[] propertyValues) { + this(propertyNames, propertyValues, new Date()); + } + + /** + * Constructs an entry containing multiple name-value pairs, where the names + * are taken from the defined structure. + * + * @param entryStructure + * @param propertyValues + * @param timestamp + */ + public HistoryRecord(HistoryRecordStructure entryStructure, String[] propertyValues, Date timestamp) { + this(entryStructure.getPropertyNames(), propertyValues, timestamp); + } + + /** + * Constructs an entry containing multiple name-value pairs, where the name is not + * unique. + * + * @param propertyNames + * @param propertyValues + * @param timestamp + */ + public HistoryRecord(String[] propertyNames, String[] propertyValues, Date timestamp) { + Assert.assertNonNull(propertyNames, "The property names should be non-null."); + Assert.assertNonNull(propertyValues, "The property values should be non-null."); + Assert.assertNonNull(timestamp, "The timestamp should be non-null."); + + Assert.assertTrue(propertyNames.length == propertyValues.length, + "The length of the property names and property values should be equal."); + + this.propertyNames = propertyNames; + this.propertyValues = propertyValues; + this.timestamp = timestamp; + } + + public String[] getPropertyNames() { + return this.propertyNames; + } + + public String[] getPropertyValues() { + return this.propertyValues; + } + + public Date getTimestamp() { + return this.timestamp; + } + +} diff --git a/src/net/java/sip/communicator/service/history/records/HistoryRecordStructure.java b/src/net/java/sip/communicator/service/history/records/HistoryRecordStructure.java new file mode 100644 index 0000000..f115d04 --- /dev/null +++ b/src/net/java/sip/communicator/service/history/records/HistoryRecordStructure.java @@ -0,0 +1,76 @@ +/* + * 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.history.records; + +import net.java.sip.communicator.util.Assert; + +/** + * @author Alexander Pelov + */ +public class HistoryRecordStructure { + + public static final TextType DEFAULT_TEXT_TYPE = TextType.LONG; + + private String[] propertyNames; + private TextType[] valueTypes; + + /** + * Creates an entry structure object used to define the shape of the data + * stored in the history. All valueTypes are set to TextType.LONG. + * + * Note that the property values are not unique, i.e. a single property + * may have 0, 1 or more values. + * + * @param propertyNames + */ + public HistoryRecordStructure(String[] propertyNames) { + Assert.assertNonNull(propertyNames, "Parameter propertyNames should be non-null."); + this.propertyNames = new String[propertyNames.length]; + System.arraycopy(propertyNames, 0, this.propertyNames, 0, this.propertyNames.length); + + this.valueTypes = new TextType[this.propertyNames.length]; + for(int i = 0; i < this.valueTypes.length; i++) { + this.valueTypes[i] = HistoryRecordStructure.DEFAULT_TEXT_TYPE; + } + } + + /** + * Creates an entry structure object used to define the shape of the data + * stored in the history. + * + * Note that the property values are not unique, i.e. a single property + * may have 0, 1 or more values. + * + * @param propertyNames + * @param valueTypes + */ + public HistoryRecordStructure(String[] propertyNames, TextType[] valueTypes) { + Assert.assertNonNull(propertyNames, "Parameter propertyNames should be non-null."); + Assert.assertNonNull(valueTypes, "Parameter valueTypes should be non-null."); + Assert.assertTrue(propertyNames.length == valueTypes.length, + "The length of the propertyNames and valueTypes should be equal."); + + this.propertyNames = new String[propertyNames.length]; + this.valueTypes = new TextType[valueTypes.length]; + + System.arraycopy(propertyNames, 0, this.propertyNames, 0, this.propertyNames.length); + System.arraycopy(valueTypes, 0, this.valueTypes, 0, this.valueTypes.length); + } + + public String[] getPropertyNames() { + return this.propertyNames; + } + + public TextType[] getValueTypes() { + return this.valueTypes; + } + + public int getPropertyCount() { + return this.propertyNames.length; + } + +} diff --git a/src/net/java/sip/communicator/service/history/records/TextType.java b/src/net/java/sip/communicator/service/history/records/TextType.java new file mode 100644 index 0000000..1cefb6d --- /dev/null +++ b/src/net/java/sip/communicator/service/history/records/TextType.java @@ -0,0 +1,23 @@ +/* + * 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.history.records; + +import net.java.sip.communicator.util.EnumerationBase; + +/** + * @author Alexander Pelov + */ +public final class TextType extends EnumerationBase { + + public static final TextType SHORT = new TextType("SHORT"); + public static final TextType LONG = new TextType("LONG"); + + protected TextType(String description) { + super(description); + } + +} diff --git a/src/net/java/sip/communicator/service/media/MediaService.java b/src/net/java/sip/communicator/service/media/MediaService.java new file mode 100644 index 0000000..6ee1acb --- /dev/null +++ b/src/net/java/sip/communicator/service/media/MediaService.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.service.media; + +import net.java.sip.communicator.service.media.event.MediaListener; +import net.java.sip.communicator.service.protocol.*; + +/** + * The service is meant to be a wrapper of media libraries such as JMF, + * (J)FFMPEG, JMFPAPI, and others. It takes care of all media play and capture + * as well as media transport (e.g. over RTP). + * + * Before being able to use this service calles would have to make sure that + * it is initialized (i.e. consult the isInitialized() method). + * @author Emil Ivov + * @author Martin Andre + */ +public interface MediaService +{ + /** + * The method is meant for use by protocol service implementations when + * willing to send an invitation to a remote callee. It is at that point + * that the media service would open a port where it would be waiting for + * data coming from the specified call participant. Subsequent sdpoffers + * requested for the call that the original call participant belonged to, + * would receive, the same IP/port couple as the first one in order to allow + * conferencing. The associated port will be released once the call has + * ended. + * + * @param callParticipant the call participant meant to receive the offer + * @return a String containing an SDP offer. + */ + public String generateSdpOffer(CallParticipant callParticipant); + + /** + * The method is meant for use by protocol service implementations when + * willing to respond to an invitation received from a remote caller. It is + * at that point that the media service would open a port where it would + * wait for data coming from the specified call participant. Subsequent sdp + * offers/answers requested for the call that the original call participant + * belonged to will receive the same IP/port couple as the first one in + * order to allow conferencing. The associated port will be released once + * the call has ended. + * + * @param callParticipant the call participant meant to receive the answer + * @return a String containing an SDP answer. + */ + public String generateSdpAnswer(CallParticipant callParticipant); + + /** + * Adds a listener that will be listening for incoming media and changes + * in the state of the media listener + * @param listener the listener to register + */ + public void addMediaListener(MediaListener listener); + + /** + * Removes a listener that was listening for incoming media and changes + * in the state of the media listener + * @param listener the listener to remove + */ + public void removeMediaListener(MediaListener listener); + + /** + * Initializes the service implementation, and puts it in a state where it + * could interoperate with other services. + */ + public void initialize(); + + /** + * Returns true if the media service implementation is initialized and ready + * for use by other services, and false otherwise. + */ + public boolean isInitialized(); + + /** + * Makes the service implementation close all release any devices or other + * resources that it might have allocated and prepare for shutdown/garbage + * collection. + */ + public void shutdown(); +} diff --git a/src/net/java/sip/communicator/service/media/event/MediaEvent.java b/src/net/java/sip/communicator/service/media/event/MediaEvent.java new file mode 100644 index 0000000..2993a46 --- /dev/null +++ b/src/net/java/sip/communicator/service/media/event/MediaEvent.java @@ -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.media.event; + +import net.java.sip.communicator.service.protocol.CallParticipant; + +/** + * + * @author Martin Andre + */ +public class MediaEvent + extends java.util.EventObject +{ + CallParticipant callParticipant; + + public MediaEvent(Object source) + { + super(source); + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/service/media/event/MediaListener.java b/src/net/java/sip/communicator/service/media/event/MediaListener.java new file mode 100644 index 0000000..b1d8705 --- /dev/null +++ b/src/net/java/sip/communicator/service/media/event/MediaListener.java @@ -0,0 +1,19 @@ +/* + * 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.media.event; + +/** + * Allows you to register for media events. + * + * @author Emil Ivov + */ +public interface MediaListener +{ + public void receivedMediaStream(MediaEvent evt); + + public void mediaServiceStatusChanged(); +} diff --git a/src/net/java/sip/communicator/service/msghistory/MessageHistoryService.java b/src/net/java/sip/communicator/service/msghistory/MessageHistoryService.java new file mode 100644 index 0000000..9fe4c5a --- /dev/null +++ b/src/net/java/sip/communicator/service/msghistory/MessageHistoryService.java @@ -0,0 +1,19 @@ +/* + * 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.msghistory; + +import net.java.sip.communicator.service.history.HistoryReader; +import net.java.sip.communicator.service.protocol.Contact; + +/** + * @author Alexander Pelov + */ +public interface MessageHistoryService extends HistoryReader { + + + +} diff --git a/src/net/java/sip/communicator/service/netaddr/NetworkAddressManagerService.java b/src/net/java/sip/communicator/service/netaddr/NetworkAddressManagerService.java new file mode 100644 index 0000000..11813ef --- /dev/null +++ b/src/net/java/sip/communicator/service/netaddr/NetworkAddressManagerService.java @@ -0,0 +1,75 @@ +/* + * 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.netaddr; + +import java.net.InetAddress; +import java.net.InetSocketAddress; + +/** + * The NetworkAddressManagerService takes care of problems such as + * @author Emil Ivov + */ +public interface NetworkAddressManagerService +{ + /** + * Returns an InetAddress instance representing the local host or null if no + * IP address for the host could be found + * @return an InetAddress instance representing the local host or null if no + * IP address for the host could be found + */ + public InetAddress getLocalHost(); + + /** + * Returns a localhostAddress. The method uses the following algorithm to + * choose among multiple addresses: + * if stun is enabled - queries STUN server and saves returned address + * Scans addresses for all network interfaces<br> + * if an address that matches the one returned by the STUN server is found - it is returned<br> + * else<br> + * if a non link local (starting with 172.16-31, 10, or 192.168) address is found it is returned<br> + * else<br> + * if a link local address is found it is returned<br> + * else<br> + * if the any address is accepted - it is returned<br> + * else<br> + * returns the InetAddress.getLocalHost()<br> + * if the InetAddress.getLocalHost() fails returns<br> + * the "any" local address - 0.0.0.0<br> + * + * @param anyAddressIsAccepted is 0.0.0.0 accepted as a return value. + * @return the address that was detected the address of the localhost. + */ + public InetAddress getLocalHost(boolean anyAddressIsAccepted); + + /** + * Tries to obtain a mapped/public address for the specified port. If the + * STUN lib fails, tries to retrieve localhost, if that fails too, returns + * null. + * + * @param port the port whose mapping we are interested in. + * @return a public address corresponding to the specified port or null if + * all attempts to retrieve such an address have failed. + */ + public InetSocketAddress getPublicAddressFor(int port); + + /** + * Initializes the network address manager service implementation and + * starts all processes/threads associated with this address manager, such + * as a stun firewall/nat detector, keep alive threads, binding lifetime + * discovery threads and etc. The method may also be used after a call to + * stop() as a reinitialization technique. + */ + public void start(); + + /** + * Kills all threads/processes lauched by this thread and prepares it for + * shutdown. You may use this method as a reinitialization technique ( + * you'll have to call start afterwards) + */ + public void stop(); + +} diff --git a/src/net/java/sip/communicator/service/protocol/AuthorizationHandler.java b/src/net/java/sip/communicator/service/protocol/AuthorizationHandler.java new file mode 100644 index 0000000..384d5d7 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/AuthorizationHandler.java @@ -0,0 +1,18 @@ +/* + * 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.protocol; + +/** + * @todo say that this is not a listener because methods have to have a return + * value + * + * @author Emil Ivov + */ +public interface AuthorizationHandler +{ + public void handleAuthorisationRequest(); +} diff --git a/src/net/java/sip/communicator/service/protocol/Call.java b/src/net/java/sip/communicator/service/protocol/Call.java new file mode 100644 index 0000000..5bf1916 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/Call.java @@ -0,0 +1,96 @@ +/* + * 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.protocol; + +import java.util.*; +import net.java.sip.communicator.service.protocol.event.CallChangeListener; + + +/** + * A represenation of a Call. The Call class must obly be created by users (i.e. + * telephony protocols) of the PhoneUIService such as a SIP protocol + * implemenation. Extensions of this class might have names like SipCall + * or H323Call or AnyOtherTelephonyProtocolCall + * + * @author Emil Ivov + */ +public abstract class Call +{ + private String callID = null; + private CallParticipant callCreator = null; + private Vector callParticipants = new Vector(); + private Vector callListeners = new Vector(); + + /** + * Creates a new call with the specified id. + * @param callID the id of the call to create. + */ + protected Call(String callID, CallParticipant callCreator) + { + this.callID = callID; + } + + /** + * Returns the id of the specified Call. + * @return String + */ + public String getCallID() + { + return callID; + } + + /** + * Compares the specified object with this call and returns true if it the + * specified object is an instance of a Call object and if the + * extending telephony protocol considers the calls represented by both + * objects to be the same. + * + * @param obj the call to compare this one with. + * @return true in case both objects are pertaining to the same call and + * false otherwise. + */ + public boolean equals(Object obj) + { + if(obj == null + || !(obj instanceof Call)) + return false; + if (obj == this + || ((Call)obj).getCallID().equals( getCallID() )) + return true; + + return false; + } + + /** + * Returns the call participant that created the call. + * @return a CallParticipant instance containing the call participant that + * created the call. + */ + public CallParticipant getCallCreator() + { + return callCreator; + } + + /** + * Returns an iterator over all call participants. + * @return an Iterator over all participants currently involved in the call. + */ + public Iterator getCallParticipants() + { + return callParticipants.iterator(); + } + + /** + * Adds a call change listener to this call so that it could receive events + * on new call participants, theme changes and others. + * @param listener the listener to register + */ + public void addCallChangeListener(CallChangeListener listener) + { + + } +} diff --git a/src/net/java/sip/communicator/service/protocol/CallParticipant.java b/src/net/java/sip/communicator/service/protocol/CallParticipant.java new file mode 100644 index 0000000..5d5e827 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/CallParticipant.java @@ -0,0 +1,118 @@ +/* + * 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.protocol; + +import java.util.*; +import net.java.sip.communicator.service.protocol.event.CallParticipantListener; + +import net.java.sip.communicator.service.gui.*; + +/** + * The CallParticipant is an interface that represents participants in a call. + * Users of the PhoneUIService need to implement this interface (or one of its + * default implementations such DefaultCallParticipant) in order to be able to + * register call participant in the user interface. + * + * <p>For SIP calls for example, it would be necessary to create a + * SipCallParticipant class that would provide sip specific implementations of + * various methods (getAddress() for example would return the participant's sip + * URI). + * + * @author Emil Ivov + */ +public interface CallParticipant +{ + + /** + * Returns a unique identifier representing this participant. Identifiers + * returned by this method should remain unique across calls. In other + * words, if it returned the value of "A" for a given participant it should + * not return that same value for any other participant and return a + * different value even if the same person (address) is participating in + * another call. Values need not remain unique after restarting the program. + * + * @return an identifier representing this call participant. + */ + public String getParticipantID(); + + /** + * Returns a reference to the call that this participant belongs to. + * @return a reference to the call containing this participant. + */ + public Call getCall(); + + /** + * Returns a human readable name representing this participant. + * @return a String containing a name for that participant. + */ + public String getDisplayName(); + + /** + * Returns a String locator for that participant. A locator might be a SIP + * URI, an IP address or a telephone number. + * @return the participant's address or phone number. + */ + public String getAddress(); + + /** + * Returns an object representing the current state of that participant. + * CallParticipantState may vary among CONNECTING, RINGING, CALLING, BISY, + * CONNECTED, and others, and it reflects the state of the connection between + * us and that participant. + * @return a CallParticipantState instance representin the participant's + * state. + */ + public CallParticipantState getState(); + + /** + * Determines whether or not this is the participant that originated the + * call (as opposed to the one that was called). + * + * @return true if this is the participant that calls us and falls if + * otherwise. + */ + public boolean isCaller(); + + /** + * Allows the user interface to register a listener interested in changes + * @param listener a listener instance to register with this participant. + */ + public void addCallParticipantListener(CallParticipantListener listener); + + /** + * Unregisters the specified listener. + * @param listener the listener to unregister. + */ + public void removeCallParticipantListener(CallParticipantListener listener); + + /** + * Returns the date (time) when this call participant acquired its current + * status. This method is to be used by the phone ui interface in order + * to show the duration of a call. + * @return a java.util.Date object containing the date when this call + * participant entered its current state. + */ + public Date getCurrentStateStartDate(); + + /** + * Returns a string representation of the participant in the form of + * <br> + * Display Name <address>;status=CallParticipantStatus + * @return a string representation of the participant and its state. + */ + public String toString(); + + /** + * The method returns an image representation of the call participant (e.g. + * a photo). Generally, the image representation is acquired from the + * underlying telephony protocol and is transferred over the network during + * call negotiation. + * @return byte[] a byte array containing the image or null if no image is + * available. + */ + public byte[] getImage(); +} diff --git a/src/net/java/sip/communicator/service/protocol/CallParticipantState.java b/src/net/java/sip/communicator/service/protocol/CallParticipantState.java new file mode 100644 index 0000000..b63a342 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/CallParticipantState.java @@ -0,0 +1,234 @@ +/* + * 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.protocol; + +/** + * The CallParticipantState class reflects the current state of a call + * participant. In other words when you start calling your grand mother she will + * be in a INITIATING_CALL state, when her phone rings her state will change to + * ALERTING_REMOTE_SIDE, and when she replies she will enter a CONNCECTED state. + * + * <p>Though not mandatory CallParticipantState would generally have one of the + * following life cycles + * + * <p> In the case with your grand mother that we just described we have: + * <br>INITIATING_CALL -> CONNECTING -> ALERTING_REMOTE_USER -> CONNECTED -> DISCONNECTED + * + * <p> If your granny was already on the phone we have: + * <br>INITIATING_CALL -> CONNECTING -> BUSY -> DISCONNECTED + * + * <p>Whenever someone tries to reach you: + * <br>INCOMING_CALL -> CONNECTED -> DISCONNECTED + * + * <p>A FAILED state is prone to appear at any place in the above diagram and is + * generally followed by a disconnected state. + * + * <p>Information on call participant is shonw in the phone user interface until + * they enter the DISCONNECTED state. At that point call participant information + * is automatically removed from the user interface and the call is considered + * terminated. + * + * @author Emil Ivov + */ +public class CallParticipantState +{ + /** + * This constant value indicates a String representation of the UNKNOWN + * call state. + * <br>This constant has the String value "Unknown". + */ + public static final String _UNKNOWN = "Unknown"; + + /** + * This constant value indicates that the state of the call participant is + * is UNKNOWN - which means that there is no information on the state for + * the time being (this constant should be used as a default value for + * newly created call participant that don't yet have an attributed call + * state. + */ + public static final CallParticipantState UNKNOWN = + new CallParticipantState(_UNKNOWN); + + /** + * This constant value indicates a String representation of the + * INITIATING_CALL call state. + * <br>This constant has the String value "Initiating Call". + */ + public static final String _INITIATING_CALL = "Initiating Call"; + + /** + * This constant value indicates that the state of the call participant is + * is INITIATING_CALL - which means that we're currently trying to open a + * socket and send our request. In the case of SIP for example we will leave + * this state the moment we receive a "100 Trying" request from a proxy or + * the remote side. + */ + public static final CallParticipantState INITIATING_CALL = + new CallParticipantState(_INITIATING_CALL); + + /** + * This constant value indicates a String representation of the CONNECTING + * call state. + * <br>This constant has the String value "Connecting". + */ + public static final String _CONNECTING = "Connecting"; + + /** + * This constant value indicates that the state of the call participant is + * is CONNECTING - which means that a network connection to that participant + * is currently being established. + */ + public static final CallParticipantState CONNECTING = + new CallParticipantState(_CONNECTING); + + /** + * This constant value indicates a String representation of the + * ALERTING_REMOTE_SIDE call state. + * <br>This constant has the String value "Alerting Remote User". + */ + public static final String _ALERTING_REMOTE_SIDE + = "Alerting Remote User (Ringing)"; + + /** + * This constant value indicates that the state of the call participant is + * is ALERTING_REMOTE_SIDE - which means that a network connection to that participant + * has been established and participant's phone is currently alerting the + * remote user of the current call. + */ + public static final CallParticipantState ALERTING_REMOTE_SIDE = + new CallParticipantState(_ALERTING_REMOTE_SIDE); + + /** + * This constant value indicates a String representation of the + * INCOMING_CALL call state. + * <br>This constant has the String value "Incoming Call". + */ + public static final String _INCOMING_CALL = "Incoming Call"; + + /** + * This constant value indicates that the state of the call participant is + * is INCOMING_CALL - which means that the participant is willing to start + * a call with us. At that point local side should be playing a sound or a + * graphical alert (the phone is ringing). + */ + public static final CallParticipantState INCOMING_CALL + = new CallParticipantState(_INCOMING_CALL); + + /** + * This constant value indicates a String representation of the CONNECTED + * call state. + * <br>This constant has the String value "Connected". + */ + public static final String _CONNECTED = "Connected"; + + /** + * This constant value indicates that the state of the call participant is + * is CONNECTED - which means that there is an ongoing call with that + * participant. + */ + public static final CallParticipantState CONNECTED + = new CallParticipantState(_CONNECTED); + + /** + * This constant value indicates a String representation of the DISCONNECTED + * call state. + * <br>This constant has the String value "Disconnected". + */ + public static final String _DISCONNECTED = "Disconnected"; + + /** + * This constant value indicates that the state of the call participant is + * is DISCONNECTET - which means that this participant is not participating :) + * in the call any more. + */ + public static final CallParticipantState DISCONNECTED = + new CallParticipantState(_DISCONNECTED); + + /** + * This constant value indicates a String representation of the BUSY + * call state. + * <br>This constant has the String value "Busy". + */ + public static final String _BUSY = "Busy"; + + /** + * This constant value indicates that the state of the call participant is + * is BUSY - which means that an attempt to establish a call with that + * participant has been made and that it has been turned down by them (e.g. + * because they were already in a call). + */ + public static final CallParticipantState BUSY + = new CallParticipantState(_BUSY); + + /** + * This constant value indicates a String representation of the FAILED + * call state. + * <br>This constant has the String value "Failed". + */ + public static final String _FAILED = "Failed"; + /** + * This constant value indicates that the state of the call participant is + * is ON_HOLD - which means that an attempt to establish a call with that + * participant has failed for an unexpected reason. + */ + public static final CallParticipantState FAILED + = new CallParticipantState(_FAILED); + + /** + * This constant value indicates a String representation of the ON_HOLD + * call state. + * <br>This constant has the String value "On Hold". + */ + public static final String _ON_HOLD = "On Hold"; + /** + * This constant value indicates that the state of the call participant is + * is ON_HOLD - which means that an attempt to establish a call with that + * participant has failed for an unexpected reason. + */ + public static final CallParticipantState ON_HOLD + = new CallParticipantState(_ON_HOLD); + + /** + * A string representationf this Participant Call State. Could be + * _CONNECTED, _FAILED, _CALLING and etc. + */ + private String callStateStr; + + /** + * Create a participant call state object with a value corresponding to the + * specified string. + * @param callParticipantState a string representation of the state. + */ + private CallParticipantState(String callParticipantState) + { + this.callStateStr = callParticipantState; + } + + /** + * Returns a String representation of tha CallParticipantSte. + * + * @return A string value (one of the _BUSY, _CALLING, _CONNECTED, + * _CONNECTING, _DISCONNECTED, _FAILED, _RINGING constants) representing + * this call participant state). + */ + public String getStateString() + { + return callStateStr; + } + + /** + * Returns a string represenation of this call state. Strings returned + * by this method have the following format: + * CallParticipantState:<STATE_STRING> + * and are meant to be used for loggin/debugging purposes. + * @return a string representation of this object. + */ + public String toString() + { + return getClass().getName()+":"+getStateString(); + } +} diff --git a/src/net/java/sip/communicator/service/protocol/Contact.java b/src/net/java/sip/communicator/service/protocol/Contact.java new file mode 100644 index 0000000..10da17f --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/Contact.java @@ -0,0 +1,72 @@ +/* + * 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.protocol; + +import java.util.*; + +/** + * This class represents the notion of a Contact or Buddy, that is widely used + * in instant messaging today. From a protocol point of view, a contact is + * generally considered to be another user of the service that proposes the + * protocol + * + * + * @author Emil Ivov + */ +public class Contact +{ + private String address = null; + private byte[] image = null; + private Hashtable contactProperties = new Hashtable(); + private boolean isLocal = false; + + public Contact(String address, boolean isLocal) + { + this.address = address; + this.isLocal = isLocal; + } + + //address + + public String getAddress() + { + return address; + } + + public void setAddres(String address) + { + this.address = address; + } + + //image + public byte[] getImage() + { + return image; + } + + public void setImage(byte[] image) + { + this.image = image; + } + + //properties + public void setProperty(String name, Object property) + { + contactProperties.put(name, property); + } + + public Object getProperty(String name) + { + return contactProperties.get(name); + } + + //islocal + public boolean isLocal() + { + return isLocal; + } +} diff --git a/src/net/java/sip/communicator/service/protocol/ContactList.java b/src/net/java/sip/communicator/service/protocol/ContactList.java new file mode 100644 index 0000000..c2964b9 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/ContactList.java @@ -0,0 +1,22 @@ +/* + * 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.protocol; + +/** + *Contact lists would have to be modified by both a the user and the implementation + * of the service as the user interface might have to ask for the status of + * some particular users + * + * @author Emil Ivov + */ +public class ContactList +{ + public ContactList() + { + super(); + } +} diff --git a/src/net/java/sip/communicator/service/protocol/DefaultCallParticipant.java b/src/net/java/sip/communicator/service/protocol/DefaultCallParticipant.java new file mode 100644 index 0000000..dd2e2a0 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/DefaultCallParticipant.java @@ -0,0 +1,342 @@ +/* + * 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.protocol; + +import java.util.*; + +import net.java.sip.communicator.service.protocol.event.*; + + +/** + * The DefaultCallParticipant provides a default implementation for most of the + * CallParticpant methods with the purpose of only leaving custom protocol + * development to clients using the PhoneUI service. + * <p> </p> + * + * @author Emil Ivov + */ +public class DefaultCallParticipant + implements CallParticipant +{ + + /** + * All the CallParticipant listeners registered with this CallParticipant. + */ + protected ArrayList callParticipantListeners = new ArrayList(); + + /** + * The address (sip address, phone number or other protocol specific + * identifier) of this call participant. + */ + protected String address = null; + + /** + * The state of the call participant. + */ + protected CallParticipantState callParticipantState = + CallParticipantState.UNKNOWN; + /** + * Indicates the date when this call participant passed into its current state. + */ + protected Date currentStateStartDate = new Date(); + + /** + * A human readable name corresponding to the call participant. + * (e.g. John Travolta) + */ + private String displayName = null; + + /** + * A byte array containing the image/photo representing the call participant. + */ + protected byte[] image; + + /** + * A string provided by the underlying implementationm uniquely identifying + * the participant. + */ + protected String participantID; + + /** + * Specifies whether or not the participant is the one that initiated the + * call. + */ + protected boolean isCaller = false; + + /** + * Returns a String identifying the call that this participant belongs to. + * We have been thinking of returning an instance of a Call interface here + * but this would mean too much to implement for users of this service. + */ + protected Call call; + + /** + * @param listener a listener instance to register with this participant. + * + * @todo Implement this + * net.java.sip.communicator.service.phoneui.CallParticipant method + */ + public void addCallParticipantListener(CallParticipantListener listener) + { + this.callParticipantListeners.add(listener); + } + + /** + * Unregisters the specified listener. + * @param listener the listener to unregister. + */ + public void removeCallParticipantListener(CallParticipantListener listener) + { + if(listener == null) + return; + callParticipantListeners.remove(listener); + } + + /** + * Returns a String locator for that participant. + * + * @return the participant's address or phone number. + * @todo Implement this + * net.java.sip.communicator.service.phoneui.CallParticipant method + */ + public String getAddress() + { + return address; + } + + /** + * Specifies the address, phone number, or other protocol specific + * identifier that represents this call participant. This method is to be + * used by service users and MUST NOT be called by the implementation. + * + * @param address The address of this call participant. + */ + public void setAddress(String address) + { + String oldAddress = getAddress(); + this.address = address; + //Fire the Event + fireCallParticipantChangeEvent( + new CallParticipantChangeEvent( + this, + CallParticipantChangeEvent.CALL_PARTICIPANT_ADDRESS_CHANGE, + oldAddress, + address + )); + } + + /** + * Returns an object representing the current state of that participant. + * + * @return a CallParticipantState instance representin the participant's + * state. + * @todo Implement this + * net.java.sip.communicator.service.phoneui.CallParticipant method + */ + public CallParticipantState getState() + { + return callParticipantState; + } + + /** + * Causes this CallParticipant to enter the specified state. The method also + * sets the currentStateStartDate field and fires a + * CallParticipantChangeEvent. + * + * @param newState the state this call participant should enter. + */ + public void enterState(CallParticipantState newState) + { + CallParticipantState oldState = getState(); + this.callParticipantState = newState; + this.currentStateStartDate = new Date(); + fireCallParticipantChangeEvent( + new CallParticipantChangeEvent( + this, + CallParticipantChangeEvent.CALL_PARTICIPANT_STATUS_CHANGE, + oldState, + newState)); + } + + /** + * Notifies all registered CallParticipantListener-s of the specified + * change event. + * + * @param evt the event to dispatch. + */ + protected void fireCallParticipantChangeEvent(CallParticipantChangeEvent + evt) + { + for (int i = 0; i < callParticipantListeners.size(); i++) + { + ((CallParticipantListener)callParticipantListeners.get(i)) + .participantChange(evt); + } + } + + /** + * Returns the date (time) when this call participant acquired its + * current status. + * + * @return a java.util.Date object containing the date when this call + * participant entered its current state. + * @todo Implement this + * net.java.sip.communicator.service.phoneui.CallParticipant method + */ + public Date getCurrentStateStartDate() + { + return currentStateStartDate; + } + + /** + * Returns a human readable name representing this participant. + * + * @return a String containing a name for that participant. + * @todo Implement this + * net.java.sip.communicator.service.phoneui.CallParticipant method + */ + public String getDisplayName() + { + return displayName; + } + + /** + * Sets a human readable name representing this participant. + * + * @param displayName the participant's display name + */ + public void setDisplayName(String displayName) + { + String oldName = getDisplayName(); + this.displayName = displayName; + + //Fire the Event + fireCallParticipantChangeEvent( + new CallParticipantChangeEvent( + this, + CallParticipantChangeEvent.CALL_PARTICIPANT_DISPLAY_NAME_CHANGE, + oldName, + displayName + )); + } + + /** + * The method returns an image representation of the call participant + * (e.g. + * + * @return byte[] a byte array containing the image or null if no image + * is available. + * @todo Implement this + * net.java.sip.communicator.service.phoneui.CallParticipant method + */ + public byte[] getImage() + { + return image; + } + + /** + * Sets the byte array containing an image representation (photo or picture) + * of the call participant. + * + * @param image a byte array containing the image + */ + public void setImage(byte[] image) + { + byte[] oldImage = getImage(); + this.image = image; + + //Fire the Event + fireCallParticipantChangeEvent( + new CallParticipantChangeEvent( + this, + CallParticipantChangeEvent.CALL_PARTICIPANT_IMAGE_CHANGE, + oldImage, + image + )); + } + + /** + * Returns a unique identifier representing this participant. + * + * @return an identifier representing this call participant. + * @todo Implement this + * net.java.sip.communicator.service.phoneui.CallParticipant method + */ + public String getParticipantID() + { + return participantID; + } + + /** + * Sets the String that serves as a unique identifier of this + * CallParticipant. + * @param participantID the ID of this call participant. + */ + public void setParticipantID(String participantID) + { + this.participantID = participantID; + } + + /** + * Sets this call participant to be (or not) the one that initiated the + * current call. + * @param isCaller a bool specifying whether or not the participant is a + * caller. + */ + public void setIsCaller(boolean isCaller) + { + this.isCaller = isCaller; + } + + /** + * Determines whether or not this is the participant that originated the + * call (as opposed to the one that was called). + * + * @return true if this is the participant that calls us and falls if + * otherwise. + * @todo Implement this + * net.java.sip.communicator.service.phoneui.CallParticipant method + */ + public boolean isCaller() + { + return isCaller; + } + + /** + * Returns a reference to the call that this participant belongs to. Calls + * are created by underlying telephony protocol implementations. + * + * @return a reference to the call containing this participant. + */ + public Call getCall() + { + return call; + } + + /** + * Sets the call containing this participant. + * @param call the call that this call participant is + * partdicipating in. + */ + public void setCall(Call call) + { + this.call = call; + } + + /** + * Returns a string representation of the participant in the form of + * <br> + * Display Name <address>;status=CallParticipantStatus + * @return a string representation of the participant and its state. + */ + public String toString() + { + return getDisplayName() + " <" + getAddress() + + ">;status=" + getState().getStateString(); + } +} diff --git a/src/net/java/sip/communicator/service/protocol/OperationNotSupportedException.java b/src/net/java/sip/communicator/service/protocol/OperationNotSupportedException.java new file mode 100644 index 0000000..135d61a --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/OperationNotSupportedException.java @@ -0,0 +1,30 @@ +/* + * 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.protocol; + +/** + * The OperationNotSupportedException is used by telephony providers as an + * indication that a requested operation is not supported or implemented. + * + * @author Emil Ivov + */ +public class OperationNotSupportedException + extends Exception +{ + /** + * Creates an OperationNotSupportedException instance with the specified + * reason phrase. + * @param message a detailed message explaining any particular details as + * to why is not the specified operation supported or null if no particular + * details exist. + */ + public OperationNotSupportedException(String message) + { + super(message); + } + +} diff --git a/src/net/java/sip/communicator/service/protocol/OperationSet.java b/src/net/java/sip/communicator/service/protocol/OperationSet.java new file mode 100644 index 0000000..7f5cb5a --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/OperationSet.java @@ -0,0 +1,15 @@ +/* + * 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.protocol; + +/** + * @author Emil Ivov + */ +public interface OperationSet +{ + public String getOperationSetName(); +} diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetBasicInstantMessaging.java b/src/net/java/sip/communicator/service/protocol/OperationSetBasicInstantMessaging.java new file mode 100644 index 0000000..6f26752 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/OperationSetBasicInstantMessaging.java @@ -0,0 +1,22 @@ +/* + * 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.protocol; + +/** + * + * @author Emil Ivov + */ +public interface OperationSetBasicInstantMessaging + extends OperationSet +{ + public void createMessageFactory(); + + public void sendInstantMessage(); + + public void addMessageListener(); + +} diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java b/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java new file mode 100644 index 0000000..3560e52 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java @@ -0,0 +1,87 @@ +/* + * 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.protocol; + +import net.java.sip.communicator.service.protocol.event.CallListener; + + + +/** + * An Operation Set defining all basic telephony operations such as conducting + * simple calls and etc. Note that video is not considered as a part of a + * supplementary operation set and if included in the service should be available + * behind the basic telephoy set. + * + * @author Emil Ivov + */ +public interface OperationSetBasicTelephony + extends OperationSet +{ + /** + * Registers the specified CallListener with this provider so that it could + * be notified when incoming calls are received. This method is called + * by the implementation of the PhoneUI service. + * @param listener the listener to register with this provider. + */ + public void addCallListener(CallListener listener); + + /** + * Removes the specified listener from the list of call listeners. + * @param listener the listener to unregister. + */ + public void removeCallListener(CallListener listener); + + + /** + * Create a new call and invite the specified CallParticipant to it. + * @param uri the address of the callee that we should invite to a new + * call. + * @return CallParticipant the CallParticipant that will represented by the + * specified uri. All following state change events will be delivered + * through that call participant. The Call that this participant is a member + * of could be retrieved from the CallParticipatn instance with the use + * of the corresponding method. + */ + public Call createCall(String uri); + + /** + * Indicates a user request to answer an incoming call from the specified + * CallParticipant. + * @param participant the call participant that we'd like to anwer. + */ + public void answerCallParticipant(CallParticipant participant); + + /** + * Puts the specified CallParticipant "on hold". In other words incoming + * media flows are not played and outgoing media flows are either muted or + * stopped, without actually interrupting the session. + * @param participant the participant that we'd like to put on hold. + */ + public void putOnHold(CallParticipant participant); + + /** + * Resumes communication with a call participant previously put on hold. If + * the specified participant is not "On Hold" at the time putOffHold is + * called, the method has no effect. + * @param participant the call participant to put on hold. + */ + public void putOffHold(CallParticipant participant); + + /** + * Indicates a user request to end a call with the specified call + * particiapnt. + * @param participant the participant that we'd like to hang up on. + */ + public void hangupCallParticipant(CallParticipant participant); + + /** + * Returns an iterator over all currently active calls. + * @return Iterator + */ + public java.util.Iterator getActiveCalls(); + +} diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetCallTransfer.java b/src/net/java/sip/communicator/service/protocol/OperationSetCallTransfer.java new file mode 100644 index 0000000..fcba14d --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/OperationSetCallTransfer.java @@ -0,0 +1,27 @@ +/* + * 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.protocol; + +/** + * An Operation Set defining operations that allow transfering calls to a new + * location. + * + * @author Emil Ivov + */ +public interface OperationSetCallTransfer + extends OperationSet +{ + /** + * Indicates a user request to transfer the specified call particiapant to a + * new (target) uri. + * @param participant the call participant we'd like to transfer + * @param targetURI the uri that we'd like this call participant to be + * transferred to. + */ + public void transferCallParticipant(CallParticipant participant, + String targetURI); +} diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetFileTransfer.java b/src/net/java/sip/communicator/service/protocol/OperationSetFileTransfer.java new file mode 100644 index 0000000..8d06a87 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/OperationSetFileTransfer.java @@ -0,0 +1,25 @@ +/* + * 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.protocol; + +import net.java.sip.communicator.service.protocol.event.FileListener; + +/** + * The File Transfer Operation Set provides an interface towards those functions + * of a given protocl, that allow transferring files among users. + * + * @todo say that meta contacts must be implemented by the user interface + * + * @author Emil Ivov + */ +public interface OperationSetFileTransfer + extends OperationSet +{ + public void sendFile(); + + public void addFileListener(FileListener listener); +} diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetPresence.java b/src/net/java/sip/communicator/service/protocol/OperationSetPresence.java new file mode 100644 index 0000000..b46228f --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/OperationSetPresence.java @@ -0,0 +1,37 @@ +/* + * 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.protocol; + +import net.java.sip.communicator.service.protocol.event.PresenceStatusListener; + +/** + * @todo Say that ContactList management and Presence functions + * @todo Say that presence implementations should not contain any persistant + * data on contactlists and who they should be keeping records of + * + * @author Emil Ivov + */ +public interface OperationSetPresence + extends OperationSet +{ + public ContactList retrieveContactList(); + + public void addPresenceStatusListener(PresenceStatusListener listener); + + public PresenceStatus getStatusForContact(); + + /** + * Some protos support batch status requests. We should therefore give them + * the possibility to execute them instead of demanding status one by one + * @param list ContactList + */ + public void addPresenceStatusSubscriptions(ContactList list); + + + public void setAuthorizationHandler(AuthorizationHandler handler); + +} diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetTelephonyConferencing.java b/src/net/java/sip/communicator/service/protocol/OperationSetTelephonyConferencing.java new file mode 100644 index 0000000..130bf80 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/OperationSetTelephonyConferencing.java @@ -0,0 +1,47 @@ +/* + * 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.protocol; + +/** + * Provides operations necessary to create and handle conferencing calls. This + * interface does + * + * @author Emil Ivov + */ +public interface OperationSetTelephonyConferencing + extends OperationSet +{ + /** + * Creates a conference call with the specified callees as call + * participants. + * + * @param callees the list of addresses that we should call + * @return the newly created conference call containing all CallParticipants + * @throws OperationNotSupportedException if the provider does not have any + * conferencing features. + */ + public Call createConfCall(String[] callees) + throws OperationNotSupportedException; + + /** + * Invitites the callee represented by the specified uri to an already + * existing call. The difference between this method and createConfCall is + * that inviteCalleeToCall allows a user to transform an existing 1 to 1 + * call into a conference call, or add new participants to an already + * established conference. + * + * @param uri the callee to invite to an existing conf call. + * @param existingCall the call that we should invite the callee to. + * @return the CallParticipant object corresponding to the callee + * represented by the specified uri. + * @throws OperationNotSupportedException if allowing additional callees to + * a pre-established call is not supported. + */ + public CallParticipant inviteCalleeToCall(String uri, Call existingCall) + throws OperationNotSupportedException; + +} diff --git a/src/net/java/sip/communicator/service/protocol/PresenceStatus.java b/src/net/java/sip/communicator/service/protocol/PresenceStatus.java new file mode 100644 index 0000000..d20305a --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/PresenceStatus.java @@ -0,0 +1,135 @@ +/* + * 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.protocol; + +/** + * The class is used to represent the state of the connection + * of a given ProtocolProvider or Contact. It is up to the implementation to + * determine the exact states that an object might go through. An IM provider + * for example might go through states like, CONNECTING, ON-LINE, AWAY, etc, A + * status instance is represented by an integer varying from 0 to + * 100, a Status Name and a Status Description. + * + * The integer status variable is used so that the users of the service get the + * notion of whether or not a given Status instance represents a state that + * allows communication (above 20) and so that it could compare instances + * between themselves (e.g. for sorting a ContactList for example). + * + * A state may not be created by the user. User may request a status change + * giving parameters requested by the ProtocolProvider. Once a statue is + * successfully entered by the provider, a ConnectivityStatus instacne is + * conveyed to the user through a notification event. + * + * @author Emil Ivov + */ +public class PresenceStatus + implements Comparable +{ + + /** + * Represents the connectivity status on a scale from + * 0 to 100 with 0 indicating complete disabiilty for communication and 100 + * maximum ability and user willingness. Implementors of this service should + * respect the following indications for status values. + * 0 - complete disability + * 1:10 - initializing. + * 1:20 - trying to enter a state where communication is possible (Connecting ..) + * 20:50 - communication is possible but might be unwanted, inefficient or delayed(e.g. Away state in IM clients) + * 50:80 - communication is possible (On - line) + * 80:100 - communication is possible and user is eager to communicate. (Free for chat! Talk to me, etc.) + */ + protected short status = 0; + + /** + * The name of this status instance (e.g. Away, On-line, Invisible, etc.) + */ + protected String statusName = null; + + /** + * A message describing this status instance (e.g. I am busy right now, and + * I'll get back to you later). + */ + protected String statusMessage = null; + + /** + * Creates an instance of this class using the specified parameters. + * @param status the status variable representing the new instance + * @param statusName the name of this PresenceStatus + * @param statusMessage a message describing the user's status. + */ + protected PresenceStatus(short status, String statusName, String statusMessage) + { + this.status = status; + this.statusName = statusName; + this.statusMessage = statusMessage; + } + + + /** + * Returns an integer representing the presence status on a scale from + * 0 to 100. + * @return a short indicating the level of availability corresponding to + * this status object. + */ + public short getStatus() + { + return status; + } + + /** + * Returns the name of this status (such as Away, On-line, Invisible, etc). + * @return a String variable containing the name of this status instance. + */ + public String getStatusName() + { + return statusName; + } + + /** + * Returns a description of the status (like for example a note giving + * details on that status like "out for a piss" for example). + * @return a String variable detailing the status. + */ + public String getStatusMessage() + { + return statusMessage; + } + + /** + * Returns a string represenation of this provider status. Strings returned + * by this method have the following format: PresenceStatus:<STATUS_STRING>: + * <STATUS_MESSAGE> and are meant to be used for loggin/debugging purposes. + * @return a string representation of this object. + */ + public String toString() + { + return getClass().getName() + + ":" + getStatusName() + + ":" + getStatusMessage(); + } + + /** + * Compares this inatance with the specified object for order. Returns a + * negative integer, zero, or a positive integer as this status instance is + * considered to represent less, as much, or more availabilite than the one + * specified by the parameter.<p> + * + * @param o the Object to be compared. + * @return a negative integer, zero, or a positive integer as this object + * is less than, equal to, or greater than the specified object. + * + * @throws ClassCastException if the specified object's type prevents it + * from being compared to this Object. + * @throws NullPointerException if o is null + */ + public int compareTo(Object o) + throws ClassCastException, NullPointerException + { + PresenceStatus target = (PresenceStatus)o; + return (getStatus() - target.getStatus()); + } +} diff --git a/src/net/java/sip/communicator/service/protocol/ProtocolProviderService.java b/src/net/java/sip/communicator/service/protocol/ProtocolProviderService.java new file mode 100644 index 0000000..69b6759 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/ProtocolProviderService.java @@ -0,0 +1,178 @@ +/* + * 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.protocol; + +import net.java.sip.communicator.service.protocol.event.*; +import java.util.*; + + +/** + * The ProtocolProvider interface should be implemented by bundles that wrap telephony + * protocol stacks. It gives the user interface a way to plug into those stacks + * and receive notifications on status change and incoming calls, as well as + * deliver user requests for establishing or ending calls, putting participants + * on hold and etc. + * + * @author Emil Ivov + */ +public interface ProtocolProviderService +{ + + + /** + * Returns a String containing a human readable string representation of the + * provider. Such names would be shown by a telephony user interface so that + * users may chose the protocol they'd like to use to make a specific call. + * Most often this would be the name of the protocol the provider + * implements, and/or some combination of the server it is attached to and + * the user name used. It is up to providers to make sure that th. + * @return a String representation of this provider. + */ + public String getProviderName(); + + /** + * Returns the short name of the protocol that the implementation of this + * provider is based upon (like SIP, Jabber, ICQ/AIM, or others for + * example). + * @return a String containing the short name of the protocol this service + * is taking care of. + */ + public String getProtocolName(); + + /** + * Many communications protocols have well known logos that users are + * familiar with. The image returned by this method should have a 32x32 size + * and if this is not the case the user interface will try to resize it + * (results are not guaranteed). In case the Provider does not wish to use + * this feature, this method should return null. In that case the user + * interface may try to show an image representation that it finds suitable + * (or just a common protocol logo). + * @return byte[] a 32x32 protocol logo or representative image. + */ + public byte[] getProviderImage(); + + /** + * Registers the specified listener with this provider so that it would + * receive notifications on changes of its state or other properties such + * as its local address and display name. + * @param listener the listener to register. + */ + public void addProviderChangeListener(ProviderChangeListener listener); + + /** + * Removes the specified listener. + * @param listener the listener to remove. + */ + public void removeProviderChangeListener(ProviderChangeListener listener); + + /** + * Returns the protocol specific contact instance representing the local + * user. In the case of SIP this would be your local sip address or in the + * case of an IM protocol such as ICQ - your own uin. No set method should + * be provided in implementations of this class. The getLocalContact() + * method is only used for giving information to the user on their currently + * used addressed a different service (ConfigurationService) should be used + * for changing that kind of settings. + * @return the Contact (address, phone number, or uin) that the Provider + * implementation is communicating on behalf of. + */ + public Contact getLocalContact(); + + /** + * Returns a PresenceStatus instance representing the state this provider is + * currently in. Note that PresenceStatus instances returned by this method + * MUST adequately represent all possible states that a provider might + * enter duruing its lifecycle, includindg those that would not be visible + * to others (e.g. Initializing, Connecting, etc ..) and those that will be + * sent to contacts/buddies (On-Line, Eager to chat, etc.). + * @return PresenceStatus + */ + public PresenceStatus getStatus(); + + /** + * Requests the provider to enter into a status corresponding to the + * specified paramters. Note that calling this method does not necessarily + * imply that the requested status would be entered. This method would + * return right after being called and the caller should add itself as + * a listener to this class in order to get notified when the state has + * actually changed. + * + * @param status the PresenceStatus as returned by getRequestableStatusSet + * @param statusMessage the message that should be set as the reason to + * enter that status + * @throws IllegalArgumentException if the status requested is not a valid + * PresenceStatus supported by this provider. + */ + public void enterStatus(PresenceStatus status, String statusMessage) + throws IllegalArgumentException; + + /** + * Returns the set of PresenceStatus objects that a user of this service + * may request the provider to enter. Note that the provider would most + * probaby enter more states than those returned by this method as they + * only depict instances that users may request to enter. (e.g. a user + * may not request a "Connecting..." state - it is a temporary state + * that the provider enters while trying to enter the "Connected" state). + * + * @return Iterator a PresenceStatus array containing "enterable" + * status instances. + */ + public Iterator getRequestableStatusSet(); + + /** + * Returns a string representation of the registration service that is + * used by this provider or null if none is used. The string returned by + * this method is used by the user interface so that it could give (if + * necessary) information to the user on its point of registration. It is + * therefore not necessary to return a valid URL but rather a human readable + * descriptive string. + * @return a string representing (the address of) the service being used. + */ + public String getRegistrationServer(); + + /** + * Allows the user interface to plugin an object that would handle incoming + * authentication challenges. + * @param authority SecurityAuthority + */ + public void setSecurityAuthority(SecurityAuthority authority); + + /** + * Returns an array containing all operation sets supported by the current + * implementation. When querying this method users must be prepared to + * receive any sybset of the OperationSet-s defined by this service. They + * MUST ignore any OperationSet-s that they are not aware of and that may be + * defined by future version of this service. Such "unknown" OperationSet-s + * though not encouraged, may also be defined by service implementors. + * + * @return an array of OperationSet-s supported by this protocol provider + * implementation. + */ + public OperationSet[] getSupportedOperationSets(); + + /** + * Initialized the service implementation, and puts it in a sate where it + * could interoperate with other services. + */ + public void initialize(); + + /** + * Returns true if the provider service implementation is initialized and ready + * for use by other services, and false otherwise. Note that this should + * remain as a separate method and not be one of the presence statuses + * since, in theorry, an implementation might very well be unaware of + * its presence status while uninitialized. + */ + public boolean isInitialized(); + + /** + * Makes the service implementation close all open sockets and release + * any resources that it might have taken and prepare for shutdown/garbage + * collection. + */ + public void shutdown(); +} diff --git a/src/net/java/sip/communicator/service/protocol/SecurityAuthority.java b/src/net/java/sip/communicator/service/protocol/SecurityAuthority.java new file mode 100644 index 0000000..488e1bf --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/SecurityAuthority.java @@ -0,0 +1,17 @@ +/* + * 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.protocol; + +/** + * Allows the user interface + * + * @author Emil Ivov + */ +public interface SecurityAuthority +{ + +} diff --git a/src/net/java/sip/communicator/service/protocol/event/CallChangeListener.java b/src/net/java/sip/communicator/service/protocol/event/CallChangeListener.java new file mode 100644 index 0000000..cb5cf2b --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/event/CallChangeListener.java @@ -0,0 +1,19 @@ +/* + * 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.protocol.event; + +import java.util.*; + +/** + * Allows notification for new call participants, theme changes and + * other call state events. + * @author Emil Ivov + */ +public class CallChangeListener + implements EventListener +{ +} diff --git a/src/net/java/sip/communicator/service/protocol/event/CallListener.java b/src/net/java/sip/communicator/service/protocol/event/CallListener.java new file mode 100644 index 0000000..84a18d6 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/event/CallListener.java @@ -0,0 +1,29 @@ +/* + * 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.protocol.event; + +import java.util.*; + + +/** + * Instances of this class are used for listening for notifications coming out + * of a telephony Provider - such as an incoming Call for example. Whenever + * a telephony Provider receives an invitation to a call from a particular + * + * @author Emil Ivov + */ +public interface CallListener extends EventListener +{ + /** + * This method is called by a protocol provider whenever an incoming call + * is received. + * @param event a CallReceivedEvent instance describing the new incoming + * call + */ + public void incomingCallReceived(CallReceivedEvent event); +} diff --git a/src/net/java/sip/communicator/service/protocol/event/CallParticipantChangeEvent.java b/src/net/java/sip/communicator/service/protocol/event/CallParticipantChangeEvent.java new file mode 100644 index 0000000..5817d56 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/event/CallParticipantChangeEvent.java @@ -0,0 +1,84 @@ +/* + * 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.protocol.event; + +import net.java.sip.communicator.service.protocol.CallParticipant; + +/** + * CallParticipantChangeEvent-s are triggerred whenever a change occurs in a + * CallParticipant. Dispatched events may be of one of the following types. + * <p> + * CALL_PARTICIPANT_STATUS_CHANGE - indicates a change in the status of the + * participant. + * <p> + * CALL_PARTICIPANT_DISPLAY_NAME_CHANGE - means that participant's displayName + * has changed + * <p> + * CALL_PARTICIPANT_ADDRESS_CHANGE - means that participant's address has changed. + * <p> + * CALL_PARTICIPANT_IMAGE_CHANGE - participant update photo. + * <p> + * + * @author Emil Ivov + */ +public class CallParticipantChangeEvent + extends java.beans.PropertyChangeEvent +{ + /** + * An event type indicating that the corresponding event is caused by a + * change of the CallParticipant's status. + */ + public static final String CALL_PARTICIPANT_STATUS_CHANGE = + "CallParticipantStatusChange"; + + /** + * An event type indicating that the corresponding event is caused by a + * change of the participant's display name. + */ + public static final String CALL_PARTICIPANT_DISPLAY_NAME_CHANGE = + "CallParticipantDisplayNameChange"; + + /** + * An event type indicating that the corresponding event is caused by a + * change of the participant's address. + */ + public static final String CALL_PARTICIPANT_ADDRESS_CHANGE = + "CallParticipantAddressChange"; + + /** + * An event type indicating that the corresponding event is caused by a + * change of the participant's photo/picture. + */ + public static final String CALL_PARTICIPANT_IMAGE_CHANGE = + "CallParticipantImageChange"; + + /** + * Creates a CallParticipantChangeEvent with the specified source, type, + * oldValue and newValue. + * @param source the participant that produced the event. + * @param type the type of the event (i.e. address change, state change etc.). + * @param oldValue the value of the changed property before the event occurred + * @param newValue current value of the changed property. + */ + public CallParticipantChangeEvent(CallParticipant source, String type, + Object oldValue, Object newValue) + { + super(source, type, oldValue, newValue); + } + + /** + * Returns the type of this event. + * @return a string containing one of the following values: + * CALL_PARTICIPANT_STATUS_CHANGE, CALL_PARTICIPANT_DISPLAY_NAME_CHANGE, + * CALL_PARTICIPANT_ADDRESS_CHANGE, CALL_PARTICIPANT_IMAGE_CHANGE + */ + public String getEventType() + { + return getPropertyName(); + } +} + diff --git a/src/net/java/sip/communicator/service/protocol/event/CallParticipantControlEvent.java b/src/net/java/sip/communicator/service/protocol/event/CallParticipantControlEvent.java new file mode 100644 index 0000000..86f0aa3 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/event/CallParticipantControlEvent.java @@ -0,0 +1,62 @@ +/* + * 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.protocol.event; + +import java.util.*; +import net.java.sip.communicator.service.protocol.*; + +/** + * The CallParticipantControlEvent is issued by the PhoneUIService as a result + * of a user request to modify the way a CallParticipant is associated with a + * call, or in other words "Answer" the incoming call of a CallParticipant or + * "Hangup" and thus and the participation of a CallParticipant in a call. The + * source of the event is considered to be the CallParticipant that is being + * controlled. As the event might also be used to indicate a user request to + * transfer a given participant to a different number, the calss also contains + * a targetURI field, containing the adress that a client is being redirected to + * (the target uri might also have slightly different meanings depending on the + * method dispatching the event). + * @author Emil Ivov + * + */ +public class CallParticipantControlEvent + extends java.util.EventObject +{ + private String targetURI = null; + + /** + * Creates a new event instance with the specifieed source CallParticipant + * and targetURI, if any. + * @param source the CallParticipant that this event is pertaining to. + * @param targetURI the URI to transfer to if this is a "Transfer" event + * or null otherwise. + */ + public CallParticipantControlEvent(CallParticipant source, String targetURI) + { + super(source); + this.targetURI = targetURI; + } + + /** + * Returns the CallParticipant that this event is pertaining to. + * @return the CallParticipant that this event is pertaining to. + */ + public CallParticipant getAssociatedCallparticipant() + { + return (CallParticipant) source; + } + + /** + * Returns the target URI if this is event is triggered by a transfer + * request or null if not. + * @return null or a tranfer URI. + */ + public String getTargetURI(){ + return targetURI; + } +} diff --git a/src/net/java/sip/communicator/service/protocol/event/CallParticipantListener.java b/src/net/java/sip/communicator/service/protocol/event/CallParticipantListener.java new file mode 100644 index 0000000..ceb90b1 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/event/CallParticipantListener.java @@ -0,0 +1,19 @@ +/* + * 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.protocol.event; + +import java.util.*; + + +/** + * @author Emil Ivov + */ +public interface CallParticipantListener + extends EventListener +{ + public void participantChange(CallParticipantChangeEvent evt); +} diff --git a/src/net/java/sip/communicator/service/protocol/event/CallReceivedEvent.java b/src/net/java/sip/communicator/service/protocol/event/CallReceivedEvent.java new file mode 100644 index 0000000..8cae0b3 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/event/CallReceivedEvent.java @@ -0,0 +1,37 @@ +/* + * 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.protocol.event; + +import java.util.*; +import net.java.sip.communicator.service.protocol.*; + +/** + * A class representing the event of a call reception. + * @author Emil Ivov + */ +public class CallReceivedEvent + extends EventObject +{ + private Call call = null; + + + public CallReceivedEvent(Call call) + { + super(call); + } + + public CallParticipant getCallParticipant() + { + return (CallParticipant)getSource(); + } + + public Call getCall() + { + return (Call)getSource(); + } +} diff --git a/src/net/java/sip/communicator/service/protocol/event/FileListener.java b/src/net/java/sip/communicator/service/protocol/event/FileListener.java new file mode 100644 index 0000000..cc19ba3 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/event/FileListener.java @@ -0,0 +1,20 @@ +/* + * 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.protocol.event; + +import java.util.*; + +/** + * + * + * @author Emil Ivov + */ +public interface FileListener + extends EventListener +{ + +} diff --git a/src/net/java/sip/communicator/service/protocol/event/PresenceStatusListener.java b/src/net/java/sip/communicator/service/protocol/event/PresenceStatusListener.java new file mode 100644 index 0000000..488fcfd --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/event/PresenceStatusListener.java @@ -0,0 +1,20 @@ +/* + * 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.protocol.event; + +import java.util.*; + +/** + * + * @author Emil Ivov + */ +public interface PresenceStatusListener + extends EventListener +{ + public void contactPresenceStatusChanged(); + +} diff --git a/src/net/java/sip/communicator/service/protocol/event/ProviderChangeListener.java b/src/net/java/sip/communicator/service/protocol/event/ProviderChangeListener.java new file mode 100644 index 0000000..f947f02 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/event/ProviderChangeListener.java @@ -0,0 +1,25 @@ +/* + * 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.protocol.event; + +/** + * An event listener that should be implemented by parties interested in changes + * that occur in the state of a ProtocolProvider (e.g. PresenceStatusChanges) + * @author Emil Ivov + */ +public interface ProviderChangeListener +{ + /** + * The method is called by a ProtocolProvider implementation whenver + * a change in the presence status of the corresponding provider had + * occurred. + * @param evt ProviderStatusChangeEvent the event describing the status + * change. + */ + public void providerStatusChanged(ProviderStatusChangeEvent evt); +} diff --git a/src/net/java/sip/communicator/service/protocol/event/ProviderStatusChangeEvent.java b/src/net/java/sip/communicator/service/protocol/event/ProviderStatusChangeEvent.java new file mode 100644 index 0000000..2618285 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/event/ProviderStatusChangeEvent.java @@ -0,0 +1,67 @@ +/* + * 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.protocol.event; + +import java.beans.PropertyChangeEvent; +import net.java.sip.communicator.service.protocol.PresenceStatus; +import net.java.sip.communicator.service.protocol.ProtocolProviderService; + +/** + * Instances of this class represent a change in the status of the provider + * that triggerred them. + * @author Emil Ivov + */ +public class ProviderStatusChangeEvent extends PropertyChangeEvent +{ + + /** + * Creates an event instance indicating a change of the property + * specified by <code>eventType</code> from <code>oldValue</code> to + * <code>newValue</code>. + * @param source the provider that generated the event + * @param eventType the type of the newly created event. + * @param oldValue the status the source provider was int before enetering + * the new state. + * @param newValue the status the source provider is currently in. + */ + public ProviderStatusChangeEvent(ProtocolProviderService source, String eventType, + PresenceStatus oldValue, PresenceStatus newValue) + { + super(source, eventType, oldValue, newValue); + } + + /** + * Returns the provider that has genereted this event + * @return the provider that generated the event. + */ + public ProtocolProviderService getProvider() + { + return (ProtocolProviderService)getSource(); + } + + /** + * Returns the status of the provider before this event took place. + * @return a PresenceStatus instance indicating the event the source + * provider was in before it entered its new state. + */ + public PresenceStatus getOldStatusValue() + { + return (PresenceStatus)super.getOldValue(); + } + + /** + * Returns the status of the provider after this event took place. + * (i.e. at the time the event is being dispatched). + * @return a PresenceStatus instance indicating the event the source + * provider is in after the status change occurred. + */ + public PresenceStatus getNewStatusValue() + { + return (PresenceStatus)super.getNewValue(); + } + +} diff --git a/src/net/java/sip/communicator/service/resources/FileAccessService.java b/src/net/java/sip/communicator/service/resources/FileAccessService.java new file mode 100644 index 0000000..580d307 --- /dev/null +++ b/src/net/java/sip/communicator/service/resources/FileAccessService.java @@ -0,0 +1,124 @@ +/* + * 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.io.File; +import java.io.IOException; + +/** + * A service used to provide the basic functionality required to access the + * undelaying file system. + * + * Note: Never store unencrypted sensitive information, such as passwords, + * personal data, credit card numbers, etc.. + * + * @author Alexander Pelov + */ +public interface FileAccessService { + + /** + * The key of the system property containing the user home dir + */ + public static final String SYSPROPERTYKEY_USER_HOME = "user.home"; + + /** + * The key of the configuration property containing the user home dir - if + * it is not defined the system property is used + */ + public static final String CONFPROPERTYKEY_USER_HOME = "sipcommunicator.user.home"; + + /** + * The subdirectory of USER_HOME in which all user files will be stored + */ + public static final String CONFPROPERTYKEY_SIP_DIRECTORY = "sipcommunicator.user.home.sipdir"; + + /** + * The default subdirectory + */ + public static final String DEFAULT_SIP_DIRECTORY = ".sipcommunicator"; + + /** + * This method returns a created temporary file. After you close this file + * it is not guaranteed that you will be able to open it again nor that it + * will contain any information. + * + * Note: DO NOT store unencrypted sensitive information in this file + * + * @return The created temporary file + * @throws IOException + * If the file cannot be created + */ + File getTemporaryFile() throws IOException; + + /** + * This method returns a created temporary directory. Any file you create + * in it will be a temporary file. + * + * Note: If there is no opened file in this directory it may be deleted + * at any time. + * Note: DO NOT store unencrypted sensitive information in this directory + * + * @return The created directory + * @throws IOException + * If the directory cannot be created + */ + File getTemporaryDirectory() throws IOException; + + /** + * This method returns a file specific to the current user. It may not + * exist, but it is guaranteed that you will have the sufficient rights to + * create it. + * + * This file should not be considered secure because the implementor may + * return a file accesible to everyone. Generaly it will reside in + * current user's homedir, but it may as well reside in a shared directory. + * + * Note: DO NOT store unencrypted sensitive information in this file + * + * @param fileName + * The name of the private file you wish to access + * @return The file + * @throws Exception + * Thrown if there is no suitable location for the persistent + * file + */ + File getPrivatePersistentFile(String fileName) throws Exception; + + /** + * This method creates a directory specific to the current user. + * + * This directory should not be considered secure because the implementor may + * return a directory accesible to everyone. Generaly it will reside in + * current user's homedir, but it may as well reside in a shared directory. + * + * It is guaranteed that you will be able to create files in it. + * + * Note: DO NOT store unencrypted sensitive information in this file + * + * @param dirName + * The name of the private directory you wish to access. + * @return The created directory. + * @throws Exception + * Thrown if there is no suitable location for the persistent + * directory. + */ + File getPrivatePersistentDirectory(String dirName) throws Exception; + + /** + * This method creates a directory specific to the current user. + * + * {@link #getPrivatePersistentDirectory(String)} + * + * @param dirNames + * The name of the private directory you wish to access. + * @return The created directory. + * @throws Exception + * Thrown if there is no suitable location for the persistent + * directory. + */ + File getPrivatePersistentDirectory(String[] dirNames) throws Exception; +} diff --git a/src/net/java/sip/communicator/sick/netaddr/.cvsignore b/src/net/java/sip/communicator/sick/netaddr/.cvsignore new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/net/java/sip/communicator/sick/netaddr/.cvsignore diff --git a/src/net/java/sip/communicator/slick/.cvsignore b/src/net/java/sip/communicator/slick/.cvsignore new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/net/java/sip/communicator/slick/.cvsignore diff --git a/src/net/java/sip/communicator/slick/netaddr/.cvsignore b/src/net/java/sip/communicator/slick/netaddr/.cvsignore new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/net/java/sip/communicator/slick/netaddr/.cvsignore diff --git a/src/net/java/sip/communicator/slick/netaddr/TestAddressPool.java b/src/net/java/sip/communicator/slick/netaddr/TestAddressPool.java new file mode 100644 index 0000000..303f5c0 --- /dev/null +++ b/src/net/java/sip/communicator/slick/netaddr/TestAddressPool.java @@ -0,0 +1,21 @@ +package net.java.sip.communicator.slick.netaddr; + +import junit.framework.*; + +/** + * + * @author Emil Ivov + */ +public class TestAddressPool + extends TestCase +{ + public TestAddressPool() + { + super(); + } + + public TestAddressPool(String name) + { + super(name); + } +} diff --git a/src/net/java/sip/communicator/util/Activator.java b/src/net/java/sip/communicator/util/Activator.java new file mode 100644 index 0000000..cef2221 --- /dev/null +++ b/src/net/java/sip/communicator/util/Activator.java @@ -0,0 +1,35 @@ +package net.java.sip.communicator.util; + +import org.osgi.framework.*; + +/** + * + * @author Emil Ivov + */ +public class Activator + implements BundleActivator +{ + private Logger logger = Logger.getLogger(getClass().getName()); + /** + * start + * + * @param bundleContext BundleContext + * @throws Exception + * @todo Implement this org.osgi.framework.BundleActivator method + */ + public void start(BundleContext bundleContext) throws Exception + { + logger.debug("Successfully activated!"); + } + + /** + * stop + * + * @param bundlecontext BundleContext + * @throws Exception + * @todo Implement this org.osgi.framework.BundleActivator method + */ + public void stop(BundleContext bundlecontext) throws Exception + { + } +} diff --git a/src/net/java/sip/communicator/util/Assert.java b/src/net/java/sip/communicator/util/Assert.java new file mode 100644 index 0000000..593e4e5 --- /dev/null +++ b/src/net/java/sip/communicator/util/Assert.java @@ -0,0 +1,80 @@ +/* + * 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.util; + +/** + * @author Alexander Pelov + */ +public class Assert { + /** + * The logger for this class. + */ + private static final Logger log = Logger.getLogger(Assert.class.getName()); + + /** + * Enables or disables assertion checks. + */ + public static boolean EnableAssertions = true; + + /** + * Throws an exception if the passed condition is false and + * <code>EnableAssertions == true</code>. + * + * @param condition The condition to be met. + * @param message The message to be displayed as explanation on assertion fail. + * @throws IllegalArgumentException Thrown if condition is false and + * assertions are enabled. + */ + public static final void assertTrue(boolean condition, String message) throws AssertionError { + if (EnableAssertions && !condition) { + StackTraceElement caller = new Throwable().getStackTrace()[1]; + + String fullText = "Assertion failed at " + caller.getMethodName() + ", line " + + caller.getLineNumber() + + "\n\tMessage: " + message; + + log.error(fullText); + + throw new AssertionError(fullText); + } + } + + /** + * Throws an exception if <code>EnableAssertions == true</code>. + * + * @param message The message to be displayed as explanation on assertion fail. + * @throws IllegalArgumentException Thrown if assertions are enabled. + */ + public static final void fail(String message) throws AssertionError { + Assert.assertTrue(false, message); + } + + /** + * Throws an exception if the passed object is null and + * <code>EnableAssertions == true</code>. + * + * @param obj The object to be checked. + * @param message The message to be displayed as explanation on assertion fail. + * @throws IllegalArgumentException Thrown if obj is null + * and assertions are enabled. + */ + public static final void assertNonNull(Object obj, String message) throws AssertionError { + Assert.assertTrue(obj != null, message); + } + + /** + * Throws an exception if the passed object is not null and + * <code>EnableAssertions == true</code>. + * + * @param obj The object to be checked. + * @param message The message to be displayed as explanation on assertion fail. + * @throws IllegalArgumentException Thrown if obj is not null and + * assertions are enabled. + */ + public static final void assertNull(Object obj, String message) throws AssertionError { + Assert.assertTrue(obj == null, message); + } +} diff --git a/src/net/java/sip/communicator/util/BidirectionalIterator.java b/src/net/java/sip/communicator/util/BidirectionalIterator.java new file mode 100644 index 0000000..19675e5 --- /dev/null +++ b/src/net/java/sip/communicator/util/BidirectionalIterator.java @@ -0,0 +1,38 @@ +/* + * 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.util; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * The standard Java Iterator is uni-directional, allowing the user to explore + * the contents of a collection in one way only. This interface defines a + * bi-directional iterator, permiting the user to go forwards and backwards in + * a collection. + * + * @author Alexander Pelov + */ +public interface BidirectionalIterator extends Iterator { + /** + * Returns true if the iteration has elements preceeding the current one. + * (In other words, returns true if <code>prev</code> would return an element rather + * than throwing an exception.) + * + * @return true if the iterator has preceeding elements. + */ + boolean hasPrev(); + + /** + * Returns the previous element in the iteration. + * + * @return the previous element in the iteration. + * + * @throws NoSuchElementException iteration has no more elements. + */ + Object prev() throws NoSuchElementException; +} diff --git a/src/net/java/sip/communicator/util/EnumerationBase.java b/src/net/java/sip/communicator/util/EnumerationBase.java new file mode 100644 index 0000000..22d14ff --- /dev/null +++ b/src/net/java/sip/communicator/util/EnumerationBase.java @@ -0,0 +1,124 @@ +/* + * 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.util; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +/** + * The base class for all enumerated types. One uses this class by extending it + * and defining "static final" constants, like this: + * + * <code> + * public class Color extends EnumerationBase { + * + * public static final Color Red = new Color("Red"); + * public static final Color Blue = new Color("Blue"); + * public static final Color Green = new Color("Green"); + * + * protected Color(String description) { + * super(description); + * } + * + * } + * </code> + * + * + * Note: always compare the enum constants with <code>equals</code>, because + * serialization and multiple class loaders can cause alot of hard to trace bugs + * + * For more information on the issue see + * + * @link http://www.javaworld.com/javaworld/javatips/jw-javatip122.html and + * @link http://www.javaworld.com/javaworld/javatips/jw-javatip133.html + * + * @author Alexander Pelov + */ +public abstract class EnumerationBase { + + private String description; + + /** + * Constructs an enumeration element + * + * @param description + * The description of this element. Should be unique + */ + protected EnumerationBase(String description) { + Assert.assertNonNull(description, "Enumeration description should be non-null"); + + this.description = description; + } + + public final String toString() { + return this.description; + } + + /** + * This method searches through all static fields defined in a class + * which are of type derived from EnumerationBase and returns the one whose + * description matches the desired one. It is the "inverse" of toString. + * + * Typical usage: + * WeekdaysEnumeration monday = (WeekdaysEnumeration) + * EnumerationBase.fromString(WeekdaysEnumeration.class, "Monday"); + * + * Note: You should test to see the exact type of the object. A "blind" cast + * may cause an exception if the object is of a type superior to the one + * you are testing. + * + * @param clazz The class whose static fields will be tested. The + * static fields defined in it and its parents will be tested + * for description match. + * @param description The desired description to be matched. + * @return The field matching the description. Null if no field matches the + * description. + */ + public static EnumerationBase fromString(Class clazz, + String description) + { + if(clazz == null || description == null) { + return null; + } + + EnumerationBase retVal = null; + + Field[] fields = clazz.getFields(); + + for(int i = 0; i < fields.length; i++) { + if((Modifier.STATIC & fields[i].getModifiers()) != 0 && + EnumerationBase.class.isAssignableFrom(fields[i].getType())) + { + try { + EnumerationBase e = (EnumerationBase)fields[i].get(null); + if(e.descriptionEquals(description)) { + retVal = e; + break; + } + } catch (Exception e) {} + } + } + + return retVal; + } + + public boolean equals(Object obj) { + if (obj instanceof EnumerationBase) { + return this.description.equals(((EnumerationBase) obj).description); + } + return false; + } + + public boolean descriptionEquals(String str) { + return this.description.equals(str); + } + + public boolean descriptionEqualsIgnoreCase(String str) { + return this.description.equalsIgnoreCase(str); + } + +} diff --git a/src/net/java/sip/communicator/util/Logger.java b/src/net/java/sip/communicator/util/Logger.java new file mode 100644 index 0000000..b95829c --- /dev/null +++ b/src/net/java/sip/communicator/util/Logger.java @@ -0,0 +1,353 @@ +package net.java.sip.communicator.util; + +import java.util.logging.*; + +/** + * Standard logging methods. + * + * @author Emil Ivov + */ +public class Logger +{ + private java.util.logging.Logger loggerDelegate = null; + + /** + * Base constructor + * + * @param logger the implementation specific logger delegate that this + * Logger instance should be created around. + */ + private Logger(java.util.logging.Logger logger) + { + this.loggerDelegate = logger; + } + + + /** + * Find or create a logger for the specified class. If a logger has + * already been created for that class it is returned. Otherwise + * a new logger is created. + * <p> + * If a new logger is created its log level will be configured + * based on the logging configuration and it will be configured + * to also send logging output to its parent's handlers. + * <p> + * @param clazz The creating class. + * <p> + * @return a suitable Logger + * @throws NullPointerException if the name is null. + */ + public static Logger getLogger(Class clazz) + throws NullPointerException + { + return getLogger(clazz.getName()); + } + + /** + * Find or create a logger for a named subsystem. If a logger has + * already been created with the given name it is returned. Otherwise + * a new logger is created. + * <p> + * If a new logger is created its log level will be configured + * based on the logging configuration and it will be configured + * to also send logging output to its parent's handlers. + * <p> + * @param name A name for the logger. This should be a dot-separated name + * and should normally be based on the class name of the creator, such as + * "net.java.sip.communicator.MyFunnyClass" + * <p> + * @return a suitable Logger + * @throws NullPointerException if the name is null. + */ + public static Logger getLogger(String name) + throws NullPointerException + { + return new Logger(java.util.logging.Logger.getLogger(name)); + + } + + + /** + * Logs an entry in the calling method. + */ + public void logEntry() + { + if (loggerDelegate.isLoggable(Level.FINEST)) { + StackTraceElement caller = new Throwable().getStackTrace()[1]; + loggerDelegate.log(Level.FINEST, "[entry] " + caller.getMethodName()); + } + } + + /** + * Logs exiting the calling method + */ + public void logExit() + { + if (loggerDelegate.isLoggable(Level.FINEST)) { + StackTraceElement caller = new Throwable().getStackTrace()[1]; + loggerDelegate.log(Level.FINEST, "[exit] " + caller.getMethodName()); + } + } + + /** + * Check if a message with a TRACE level would actually be logged by this + * logger. + * <p> + * @return true if the TRACE level is currently being logged + */ + public boolean isTraceEnabled() + { + return loggerDelegate.isLoggable(Level.FINER); + } + + /** + * Log a TRACE message. + * <p> + * If the logger is currently enabled for the TRACE message + * level then the given message is forwarded to all the + * registered output Handler objects. + * <p> + * @param msg The message to log + */ + public void trace(Object msg) + { + loggerDelegate.finer(msg!=null?msg.toString():"null"); + } + + /** + * Log a message, with associated Throwable information. + * <p> + * @param msg The message to log + * @param t Throwable associated with log message. + */ + public void trace(Object msg, Throwable t) + { + loggerDelegate.log(Level.FINER, msg!=null?msg.toString():"null", t); + } + + /** + * Check if a message with a DEBUG level would actually be logged by this + * logger. + * <p> + * @return true if the DEBUG level is currently being logged + */ + public boolean isDebugEnabled() + { + return loggerDelegate.isLoggable(Level.FINE); + } + + /** + * Log a DEBUG message. + * <p> + * If the logger is currently enabled for the DEBUG message + * level then the given message is forwarded to all the + * registered output Handler objects. + * <p> + * @param msg The message to log + */ + public void debug(Object msg) + { + loggerDelegate.fine(msg!=null?msg.toString():"null"); + } + + /** + * Log a message, with associated Throwable information. + * <p> + * @param msg The message to log + * @param t Throwable associated with log message. + */ + public void debug(Object msg, Throwable t) + { + loggerDelegate.log(Level.FINE, msg!=null?msg.toString():"null", t); + } + + /** + * Check if a message with an INFO level would actually be logged by this + * logger. + * + * @return true if the INFO level is currently being logged + */ + public boolean isInfoEnabled() + { + return loggerDelegate.isLoggable(Level.INFO); + } + + /** + * Log a INFO message. + * <p> + * If the logger is currently enabled for the INFO message + * level then the given message is forwarded to all the + * registered output Handler objects. + * <p> + * @param msg The message to log + */ + public void info(Object msg) + { + loggerDelegate.info(msg!=null?msg.toString():"null"); + } + + /** + * Log a message, with associated Throwable information. + * <p> + * @param msg The message to log + * @param t Throwable associated with log message. + */ + public void info(Object msg, Throwable t) + { + loggerDelegate.log(Level.INFO, msg!=null?msg.toString():"null", t); + } + + /** + * Log a WARN message. + * <p> + * If the logger is currently enabled for the WARN message + * level then the given message is forwarded to all the + * registered output Handler objects. + * <p> + * @param msg The message to log + */ + public void warn(Object msg) + { + loggerDelegate.warning(msg!=null?msg.toString():"null"); + } + + /** + * Log a message, with associated Throwable information. + * <p> + * @param msg The message to log + * @param t Throwable associated with log message. + */ + public void warn(Object msg, Throwable t) + { + loggerDelegate.log(Level.WARNING, msg!=null?msg.toString():"null", t); + } + + /** + * Log a ERROR message. + * <p> + * If the logger is currently enabled for the ERROR message + * level then the given message is forwarded to all the + * registered output Handler objects. + * <p> + * @param msg The message to log + */ + public void error(Object msg) + { + loggerDelegate.severe(msg!=null?msg.toString():"null"); + } + + /** + * Log a message, with associated Throwable information. + * <p> + * @param msg The message to log + * @param t Throwable associated with log message. + */ + public void error(Object msg, Throwable t) + { + loggerDelegate.log(Level.SEVERE, msg!=null?msg.toString():"null", t); + } + + /** + * Log a FATAL message. + * <p> + * If the logger is currently enabled for the FATAL message + * level then the given message is forwarded to all the + * registered output Handler objects. + * <p> + * @param msg The message to log + */ + public void fatal(Object msg) + { + loggerDelegate.severe(msg!=null?msg.toString():"null"); + } + + /** + * Log a message, with associated Throwable information. + * <p> + * @param msg The message to log + * @param t Throwable associated with log message. + */ + public void fatal(Object msg, Throwable t) + { + loggerDelegate.log(Level.SEVERE, msg!=null?msg.toString():"null", t); + } + + /** + * Set logging level for all handlers to FATAL + */ + public void setLevelFatal() + { + setLevel(Level.SEVERE); + } + + /** + * Set logging level for all handlers to ERROR + */ + public void setLevelError() + { + setLevel(Level.SEVERE); + } + + /** + * Set logging level for all handlers to WARNING + */ + public void setLevelWarn() + { + setLevel(Level.WARNING); + } + + /** + * Set logging level for all handlers to INFO + */ + public void setLevelInfo() + { + setLevel(Level.INFO); + } + + /** + * Set logging level for all handlers to DEBUG + */ + public void setLevelDebug() + { + setLevel(Level.FINE); + } + + /** + * Set logging level for all handlers to TRACE + */ + public void setLevelTrace() + { + setLevel(Level.FINER); + } + + /** + * Set logging level for all handlers to ALL (allow all log messages) + */ + public void setLevelAll() + { + setLevel(Level.ALL); + } + + /** + * Set logging level for all handlers to OFF (allow no log messages) + */ + public void setLevelOff() + { + setLevel(Level.OFF); + } + + /** + * Set logging level for all handlers to <code>level</code> + * + * @param level the level to set for all logger handlers + */ + private void setLevel(java.util.logging.Level level) + { + Handler[] handlers = loggerDelegate.getHandlers(); + for (int i = 0; i < handlers.length; i++) + { + handlers[i].setLevel(level); + } + loggerDelegate.setLevel(level); + } +} diff --git a/src/net/java/sip/communicator/util/QuoteTokenizer.java b/src/net/java/sip/communicator/util/QuoteTokenizer.java new file mode 100644 index 0000000..83a52b3 --- /dev/null +++ b/src/net/java/sip/communicator/util/QuoteTokenizer.java @@ -0,0 +1,288 @@ +/* + * 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.util; + +import java.util.Enumeration; +import java.util.NoSuchElementException; + +/** + * A tokenizer that takes in account the existence of " and ' and + * does skips all delimeters enclosed in those characters. + * + * Thus the following string: + * + * <code>This is a "stupid 'hot' dog", boy!</code> + * + * Parsed created with the default constructor will produce the + * following tokens: + * + * This is a + * "stupid 'hot' dog", + * boy! + * + * And the string: + * <code>This is a 'smart "hot" dog', boy!</code> + * + * This is a + * 'stupid "hot" dog', + * boy! + * + * @author Alexander Pelov + * + * TODO: TEST + */ +public class QuoteTokenizer implements Enumeration { + + /** + * Delimeters defined for internal purposes. + */ + private static final String DELIMS = "'\""; + + /** + * The text to be tokenized. + */ + private String text; + + /** + * Delimeters requested by the user. + */ + private String delims; + + /** + * The number of tokens in the text. + */ + private int tokens = -1; + + /** + * Pointer to the current tokenizer position. + */ + private int currentTokenEnd = -1; + + /** + * Return the delimeters? + */ + private boolean returnDelim; + + /** + * Constructs a tokenizer using spaces and tabs as delimeters, + * not returning the delimeters as tokens. + * + * @param text The text to be tokenized + */ + public QuoteTokenizer(String text) { + this(text, " \t"); + } + + /** + * Constructs a tokenizer using the characters specified in delims + * as delimeters, not returning the delimeters as tokens. + * + * @param text The text to be tokenized + * @param delims The delimeters in a string + */ + public QuoteTokenizer(String text, String delims) { + this(text, delims, false); + } + + /** + * Constructs a tokenizer using the specified delimeters, specifying + * if the delimeters should be returned as tokens. + * + * @param text The text to be tokenized + * @param delims The delimeters in a string + * @param returnDelim Flag specifying if the delimeters should be returned + */ + public QuoteTokenizer(String text, String delims, boolean returnDelim) { + this.text = text; + this.delims = delims; + this.returnDelim = returnDelim; + } + + /** + * Calculates the number of times that this tokenizer's + * <code>nextToken</code> method can be called before it + * generates an exception. The current position is not advanced. + * + * @return Returns the number of tokens remaining in the + * string using the current delimiter set + */ + public int countTokens() { + // See if the tokens were already count + if(this.tokens == -1) { + this.tokens = performTokenCount(); + } + + return this.tokens; + } + + /** + * Returns the same value as the <code>hasMoreTokens</code> method. + * It exists so that this class can implement the Enumeration interface. + */ + public boolean hasMoreElements() { + return this.hasMoreTokens(); + } + + /** + * Tests if there are more tokens available from this + * tokenizer's string. If this method returns true, then a + * subsequent call to nextToken with no argument will + * successfully return a token. + * + * @return Returns true if and only if there is at + * least one token in the string after the current position; + * false otherwise. + */ + public boolean hasMoreTokens() { + return this.currentTokenEnd < this.text.length(); + } + + /** + * Returns the same value as the nextToken method, except that + * its declared return value is Object rather than String. + * It exists so that this class can implement the Enumeration interface. + * + * @return Returns the next token in the string. + * + * @throws Throws NoSuchElementException - if there are no more + * tokens in this tokenizer's string. + */ + public Object nextElement() { + return this.nextToken(); + } + + /** + * Returns the next token from this string tokenizer. + * + * @return Returns the next token from this string tokenizer. + * @throws Throws NoSuchElementException - if there are no more + * tokens in this tokenizer's string. + */ + public String nextToken() throws NoSuchElementException { + if(!this.hasMoreTokens()) { + throw new NoSuchElementException(); + } + + int tokStart = this.getTokenStart(this.currentTokenEnd); + int tokEnd = this.getTokenEnd(tokStart); + + this.currentTokenEnd = tokEnd; + return this.text.substring(tokStart, tokEnd); + } + + /** + * Returns the next token in this string tokenizer's string. + * First, the set of characters considered to be delimiters + * by this StringTokenizer object is changed to be the characters + * in the string delim. Then the next token in the string after + * the current position is returned. The current position is + * advanced beyond the recognized token. The new delimiter set + * remains the default after this call. + * + * @param delims - the new delimeters. + * @return Returns the next token, after switching to the + * new delimiter set. + * + * @throws Throws NoSuchElementException - if there are no more + * tokens in this tokenizer's string. + */ + public String nextToken(String delims) { + this.delims = delims; + this.tokens = -1; + + return this.nextToken(); + } + + /** + * Restarts the tokenizer. + */ + public void reset() { + this.currentTokenEnd = -1; + } + + /** + * Restarts the tokenizer and sets new parameters. + */ + public void reset(String delims, boolean returnDelim) { + this.reset(); + this.delims = delims; + this.returnDelim = returnDelim; + this.tokens = -1; + } + + + private int performTokenCount() { + // No; Count them + int count = 1; + + int tokStart = this.getTokenStart(0); + int tokEnd = this.getTokenEnd(tokStart); + + int textLen = this.text.length(); + while(tokEnd < textLen) { + tokStart = this.getTokenStart(tokEnd); + tokEnd = this.getTokenEnd(tokStart); + + count++; + } + + return count; + } + + private int getTokenStart(int prevTokenEnd) { + int tokenStart = prevTokenEnd; + + if(!this.returnDelim) { + int textLen = this.text.length(); + while(tokenStart < textLen + && + this.delims.indexOf(this.text.charAt(tokenStart)) >= 0 + ) { + tokenStart++; + } + } + + return tokenStart; + } + + private int getTokenEnd(int tokenStart) { + return findNextDelim(tokenStart)+1; + } + + private int findNextDelim(int start) { + // The end of the token + int nextDelim; + + // Mark if a " or ' was met + char stringChar = 0; + + int textLen = this.text.length(); + for(nextDelim = start; nextDelim < textLen; nextDelim++) { + char c = this.text.charAt(nextDelim); + + // Have we met a " or ' ? + if(stringChar == 0) { + // No; + if(this.delims.indexOf(c) >= 0) { + // Found Delim!! Quit! + break; + } else if(DELIMS.indexOf(c) >= 0) { + stringChar = c; + } + } else { + // Yes; + if(c == stringChar) { + stringChar = 0; + } + } + } + + return nextDelim; + } + + +} diff --git a/src/net/java/sip/communicator/util/ScLogFormatter.java b/src/net/java/sip/communicator/util/ScLogFormatter.java new file mode 100644 index 0000000..a697fdf --- /dev/null +++ b/src/net/java/sip/communicator/util/ScLogFormatter.java @@ -0,0 +1,116 @@ +/* + * 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.util; + +import java.io.*; +import java.util.logging.*; + +/** + * Print a brief summary of the LogRecord in a human readable. The summary will + * typically be on a single line (unless it's too long :) ... what I meant to + * say is that we don't add any line breaks). + * + * @author Emil Ivov + */ + +public class ScLogFormatter + extends Formatter +{ + static long startTime = System.currentTimeMillis(); + + private static String lineSeparator = System.getProperty("line.separator"); + + /** + * Format the given LogRecord. + * @param record the log record to be formatted. + * @return a formatted log record + */ + public synchronized String format(LogRecord record) + { + StringBuffer sb = new StringBuffer(); + + //time since the program started + sb.append(System.currentTimeMillis() - startTime); + sb.append(" "); + + //log level + sb.append(record.getLevel().getLocalizedName()); + sb.append(": "); + + //caller method + inferCaller(record); + String loggerName = record.getLoggerName(); + if(loggerName.startsWith("net.java.sip.communicator.")) + sb.append(loggerName.substring("net.java.sip.communicator.".length())); + else + sb.append(record.getLoggerName()); + + if (record.getSourceMethodName() != null) + { + sb.append("."); + sb.append(record.getSourceMethodName()); + sb.append("()"); + } + sb.append(" "); + sb.append(record.getMessage()); + sb.append(lineSeparator); + if (record.getThrown() != null) + { + try + { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + record.getThrown().printStackTrace(pw); + pw.close(); + sb.append(sw.toString()); + } + catch (Exception ex) + { + } + } + return sb.toString(); + } + + /** + * Try to extract the name of the class and method that called the current + * log statement. + * + * @param record the logrecord where class and method name should be stored. + */ + private void inferCaller(LogRecord record) + { + // Get the stack trace. + StackTraceElement stack[] = (new Throwable()).getStackTrace(); + + // First, search back to a method in the SIP Communicator Logger class. + int ix = 0; + while (ix < stack.length) + { + StackTraceElement frame = stack[ix]; + String cname = frame.getClassName(); + if (cname.equals("net.java.sip.communicator.util.Logger")) + { + break; + } + ix++; + } + // Now search for the first frame before the SIP Communicator Logger class. + while (ix < stack.length) + { + StackTraceElement frame = stack[ix]; + String cname = frame.getClassName(); + if (!cname.equals("net.java.sip.communicator.util.Logger")) + { + // We've found the relevant frame. + record.setSourceClassName(cname); + record.setSourceMethodName(frame.getMethodName()); + break; + } + ix++; + } + } +} diff --git a/src/net/java/sip/communicator/util/util.manifest.mf b/src/net/java/sip/communicator/util/util.manifest.mf new file mode 100644 index 0000000..30fb569 --- /dev/null +++ b/src/net/java/sip/communicator/util/util.manifest.mf @@ -0,0 +1,17 @@ +Bundle-Activator: net.java.sip.communicator.util.Activator +Bundle-Name: SIP Communicator Utility Packages +Bundle-Description: A bundle that export packages with utility classes. +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +Import-Package: org.w3c.dom, + org.xml.sax, + javax.xml.parsers, + org.apache.xml.serializer, + javax.xml.transform, + javax.xml.transform.dom, + javax.xml.transform.stream, + net.java.sip.communicator.util.xml, + net.java.sip.communicator.util, +Export-Package: net.java.sip.communicator.util.xml, + net.java.sip.communicator.util, + diff --git a/src/net/java/sip/communicator/util/xml/XMLException.java b/src/net/java/sip/communicator/util/xml/XMLException.java new file mode 100644 index 0000000..88e093c --- /dev/null +++ b/src/net/java/sip/communicator/util/xml/XMLException.java @@ -0,0 +1,45 @@ +/* + * 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.util.xml; + +/** + * The class is used to mask any XML specific exceptions thrown during parsing + * various descriptors. + * + * @author Emil Ivov + * @version 1.0 + */ + +public class XMLException extends Exception +{ + /** + * Constructs a new XMLException with the specified detail message and cause. + * + * @param message a message specifying the reason that caused the + * exception. + * + * @param cause the cause (which is saved + * for later retrieval by the Throwable.getCause() method). (A null value is + * permitted, and indicates that the cause is nonexistent or unknown.) + */ + public XMLException(String message, Throwable cause) + { + super (message, cause); + } + + /** + * Constructs a new XMLException with the specified detail message. + * + * @param message a message specifying the reason that caused the + * exception. + */ + public XMLException(String message) + { + super(message); + } + +} diff --git a/src/net/java/sip/communicator/util/xml/XMLUtils.java b/src/net/java/sip/communicator/util/xml/XMLUtils.java new file mode 100644 index 0000000..01a279a --- /dev/null +++ b/src/net/java/sip/communicator/util/xml/XMLUtils.java @@ -0,0 +1,403 @@ +package net.java.sip.communicator.util.xml; + + +import java.io.*; +import org.w3c.dom.*; +import javax.xml.transform.*; +import javax.xml.transform.dom.*; +import javax.xml.transform.stream.*; +import net.java.sip.communicator.util.*; + +/** + * Common XML Tasks + * + * @author Emil Ivov + * @author Damian Minkov + */ +public class XMLUtils +{ + private static Logger logger = Logger.getLogger(XMLUtils.class); + /** + * Extracts from node the attribute with the specified name. + * @param node the node whose attribute we'd like to extract. + * @param name the name of the attribute to extract. + * @return a String containing the trimmed value of the attribute or null + * if no such attribute exists + */ + public static String getAttribute(Node node, String name) + { + try + { + logger.logEntry(); + + if(node == null) + return null; + + Node attribute = node.getAttributes().getNamedItem(name); + return (attribute==null)? null : attribute.getNodeValue().trim(); + } + finally + { + logger.logExit(); + } + } + + /** + * Extracts the String content of a TXT element. + * + * @param parentNode the node containing the data that we'd like to get. + * @return the string contained by the node or null if none existed. + */ + public static String getText(Element parentNode) + { + try + { + logger.logEntry(); + + Text text = getTextNode(parentNode); + + if (text == null){ + return null; + } + else{ + return text.getData(); + } + } + finally + { + logger.logExit(); + } + } + + /** + * Sets data to be the TEXT content of element + * + * @param parentNode the parent element. + * @param data the data to set. + */ + public static void setText(Element parentNode, String data) + { + try + { + logger.logEntry(); + + Text txt = getTextNode(parentNode); + + if(txt != null) + txt.setData(data); + else + { + txt = parentNode.getOwnerDocument().createTextNode(data); + parentNode.appendChild(txt); + } + } + finally + { + logger.logExit(); + } + } + + /** + * Sets data to be the CDATA content of element + * + * @param element the parent element. + * @param data the data to set. + */ + public static void setCData(Element element, String data) + { + try + { + logger.logEntry(); + + CDATASection txt = getCDataNode(element); + if(txt != null) + txt.setData(data); + else + { + txt = element.getOwnerDocument().createCDATASection(data); + element.appendChild(txt); + } + } + finally + { + logger.logExit(); + } + } + + /** + * Extract the CDATA content of the specified element. + * @param element the element whose data we need + * @return a String containing the CDATA value of element. + */ + public static String getCData(Element element) + { + try + { + logger.logEntry(); + + CDATASection text = getCDataNode(element); + if(text != null) + return text.getData().trim(); + else + return null; + } + finally + { + logger.logExit(); + } + } + + + /** + * Returns element's CDATA child node (if it has one). + * @param element the element whose CDATA we need to get. + * @return a CDATASection object containing the specified element's CDATA + * content + */ + public static CDATASection getCDataNode(Element element) + { + try + { + logger.logEntry(); + + return (CDATASection)getChildByType(element, + Node.CDATA_SECTION_NODE); + } + finally + { + logger.logExit(); + } + } + + /** + * Returns element's TEXXT child node (if it has one). + * @param element the element whose TEXT we need to get. + * @return a <code>Text</code> object containing the specified element's + * text content. + */ + public static Text getTextNode(Element element) + { + try + { + logger.logEntry(); + + return (Text)getChildByType(element, Node.TEXT_NODE); + } + finally + { + logger.logExit(); + } + } + + /** + * Returns first of the <code>element</code>'s child nodes that is of type + * <code>nodeType</code>. + * @param element the element whose child we need. + * @param nodeType the type of the child we need. + * @return a child of the specified <code>nodeType</code> or null if none + * was found. + */ + public static Node getChildByType(Element element, short nodeType) + { + try{ + logger.logEntry(); + + if (element == null) + return null; + + NodeList nodes = element.getChildNodes(); + if (nodes == null || nodes.getLength() < 1) + return null; + + Node node; + String data; + for (int i = 0; i < nodes.getLength(); i++) + { + node = nodes.item(i); + short type = node.getNodeType(); + if (type == nodeType) + { + if (type == Node.TEXT_NODE || + type == Node.CDATA_SECTION_NODE) + { + data = ( (Text) node).getData(); + if (data == null || data.trim().length() < 1) + continue; + } + + return node; + } + } + + return null; + }finally{ + logger.logExit(); + } + } + + /** + * Writes the specified document to the given file adding indentatation. + * The default encoding is UTF-8. + * + * @param out the output File + * @param document the document to write + * + * @throws java.io.IOException in case a TransformerException is thrown by + * the underlying Transformer. + */ + public static void writeXML(Document document, File out) + throws java.io.IOException + { + writeXML(document, new StreamResult(out), null, null); + } + + /** + * Writes the specified document to the given file adding indentatation. + * The default encoding is UTF-8. + * + * @param writer the writer to use when writing the File + * @param document the document to write + * + * @throws java.io.IOException in case a TransformerException is thrown by + * the underlying Transformer. + */ + public static void writeXML(Document document, + Writer writer) + throws java.io.IOException + { + writeXML(document, new StreamResult(writer), null, null); + } + + /** + * Writes the specified document to the given file adding indentatation. + * The default encoding is UTF-8. + * + * @param streamResult the streamResult object where the document should be + * written + * @param document the document to write + * @param doctypeSystem the doctype system of the xml document that we should + * record in the file or null if none is specified. + * @param doctypePublic the public identifier to be used in the document + * type declaration. + * + * @throws java.io.IOException in case a TransformerException is thrown by + * the underlying Transformer. + */ + public static void writeXML(Document document, + StreamResult streamResult, + String doctypeSystem, + String doctypePublic) + throws java.io.IOException + { + try { + logger.logEntry(); + + DOMSource domSource = new DOMSource(document); + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer serializer = tf.newTransformer(); + if(doctypeSystem != null) + serializer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, + doctypeSystem); + if(doctypePublic != null) + serializer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, + doctypePublic); + serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + serializer.setOutputProperty(OutputKeys.INDENT, "yes"); + serializer.transform(domSource, streamResult); + } + catch (TransformerException ex) { + logger.error("Error saving configuration file", ex); + throw new java.io.IOException( + "Failed to write the configuration file: " + + ex.getMessageAndLocation()); + } + catch (IllegalArgumentException ex) { + //this one is thrown by the setOutputProperty or in other words - + //shoudln't happen. so let's just log it down in case ... + logger.error("Error saving configuration file", ex); + } + finally { + logger.logExit(); + } + } + + /** + * Whenever you'd need to print a configuration node and/or its children. + * + * @param root the root node to print. + * @param out the print stream that should be used to outpu + * @param recurse boolean + * @param prefix String + */ + public static void printChildElements(Element root, + PrintStream out, + boolean recurse, + String prefix) + { + out.print(prefix + "<" + root.getNodeName()); + NamedNodeMap attrs = root.getAttributes(); + Node node; + for(int i = 0; i < attrs.getLength(); i++) + { + node = attrs.item(i); + out.print(" " + node.getNodeName() + "=\"" + + node.getNodeValue() + "\""); + } + out.println(">"); + + String data = getText(root); + if(data != null && data.trim().length() > 0) + out.println(prefix + "\t" + data); + + data = getCData(root); + if(data != null && data.trim().length() > 0) + out.println(prefix + "\t<![CDATA[" + data + "]]>"); + + NodeList nodes = root.getChildNodes(); + for(int i = 0; i < nodes.getLength(); i++) + { + node = nodes.item(i); + if(node.getNodeType() == Node.ELEMENT_NODE) + { + if(recurse) + printChildElements((Element)node, out, recurse, prefix + + "\t"); + else + out.println(prefix + node.getNodeName()); + } + } + + out.println(prefix + "</" + root.getNodeName() + ">"); + } + + /** + * Returns the child element with the specified tagName for the specified + * parent element. + * @param parent The parent whose child we're looking for. + * @param tagName the name of the child to find + * @return The child with the specified name or null if no such child was + * found. + * @throws NullPointerException if parent or tagName are null + */ + public static Element findChild(Element parent, String tagName) + { + if(parent == null || tagName == null) + throw new NullPointerException("Parent or tagname were null! " + + "parent = " + parent + "; tagName = " + tagName); + + NodeList nodes = parent.getChildNodes(); + Node node; + int len = nodes.getLength(); + for(int i = 0; i < len; i++) + { + node = nodes.item(i); + if(node.getNodeType() == Node.ELEMENT_NODE + && ((Element)node).getNodeName().equals(tagName)) + return (Element)node; + } + + return null; + } + + +} |