diff options
Diffstat (limited to 'src/net/java/sip/communicator/plugin')
228 files changed, 20913 insertions, 171 deletions
diff --git a/src/net/java/sip/communicator/plugin/accountinfo/AccountDetailsPanel.java b/src/net/java/sip/communicator/plugin/accountinfo/AccountDetailsPanel.java index 4425f92..b5eaa11 100644 --- a/src/net/java/sip/communicator/plugin/accountinfo/AccountDetailsPanel.java +++ b/src/net/java/sip/communicator/plugin/accountinfo/AccountDetailsPanel.java @@ -14,6 +14,7 @@ import java.util.*; import javax.imageio.*; import javax.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.ServerStoredDetails.BinaryDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.BirthDateDetail; @@ -26,7 +27,6 @@ import net.java.sip.communicator.service.protocol.ServerStoredDetails.MiddleName import net.java.sip.communicator.service.protocol.ServerStoredDetails.PhoneNumberDetail; import net.java.sip.communicator.util.*; import net.java.sip.communicator.util.skin.*; -import net.java.sip.communicator.util.swing.*; /** * The right side panel of AccountDetailsDialog. Shows one tab of a summary of diff --git a/src/net/java/sip/communicator/plugin/accountinfo/AccountInfoPanel.java b/src/net/java/sip/communicator/plugin/accountinfo/AccountInfoPanel.java index 700a45b..99d8c40 100644 --- a/src/net/java/sip/communicator/plugin/accountinfo/AccountInfoPanel.java +++ b/src/net/java/sip/communicator/plugin/accountinfo/AccountInfoPanel.java @@ -10,9 +10,9 @@ import java.util.*; import javax.swing.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.util.swing.*;
import org.osgi.framework.*;
diff --git a/src/net/java/sip/communicator/plugin/accountinfo/accountinfo.manifest.mf b/src/net/java/sip/communicator/plugin/accountinfo/accountinfo.manifest.mf index e329076..3426abd 100644 --- a/src/net/java/sip/communicator/plugin/accountinfo/accountinfo.manifest.mf +++ b/src/net/java/sip/communicator/plugin/accountinfo/accountinfo.manifest.mf @@ -14,7 +14,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.event,
org.jitsi.service.resources, net.java.sip.communicator.service.resources,
net.java.sip.communicator.util,
- net.java.sip.communicator.util.swing,
+ net.java.sip.communicator.plugin.desktoputil,
javax.swing,
javax.swing.event,
javax.swing.table,
diff --git a/src/net/java/sip/communicator/plugin/addrbook/AdvancedConfigForm.java b/src/net/java/sip/communicator/plugin/addrbook/AdvancedConfigForm.java index 7872636..5bd25c1 100644 --- a/src/net/java/sip/communicator/plugin/addrbook/AdvancedConfigForm.java +++ b/src/net/java/sip/communicator/plugin/addrbook/AdvancedConfigForm.java @@ -12,7 +12,7 @@ import javax.swing.*; import net.java.sip.communicator.plugin.addrbook.macosx.*; import net.java.sip.communicator.plugin.addrbook.msoutlook.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.jitsi.util.*; diff --git a/src/net/java/sip/communicator/plugin/addrbook/addrbook.manifest.mf b/src/net/java/sip/communicator/plugin/addrbook/addrbook.manifest.mf index ee9e92d..47b6cc1 100644 --- a/src/net/java/sip/communicator/plugin/addrbook/addrbook.manifest.mf +++ b/src/net/java/sip/communicator/plugin/addrbook/addrbook.manifest.mf @@ -11,6 +11,6 @@ Import-Package: javax.swing, net.java.sip.communicator.service.protocol,
org.jitsi.service.resources, net.java.sip.communicator.service.resources,
net.java.sip.communicator.util, - net.java.sip.communicator.util.swing,
+ net.java.sip.communicator.plugin.desktoputil,
org.osgi.framework
System-Bundle: yes
diff --git a/src/net/java/sip/communicator/plugin/advancedconfig/AdvancedConfigurationPanel.java b/src/net/java/sip/communicator/plugin/advancedconfig/AdvancedConfigurationPanel.java index f209d39..ae79138 100644 --- a/src/net/java/sip/communicator/plugin/advancedconfig/AdvancedConfigurationPanel.java +++ b/src/net/java/sip/communicator/plugin/advancedconfig/AdvancedConfigurationPanel.java @@ -11,9 +11,9 @@ import java.awt.*; import javax.swing.*; import javax.swing.event.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; import org.osgi.framework.*; diff --git a/src/net/java/sip/communicator/plugin/advancedconfig/advancedconfig.manifest.mf b/src/net/java/sip/communicator/plugin/advancedconfig/advancedconfig.manifest.mf index b0065cf..b6e84bb 100644 --- a/src/net/java/sip/communicator/plugin/advancedconfig/advancedconfig.manifest.mf +++ b/src/net/java/sip/communicator/plugin/advancedconfig/advancedconfig.manifest.mf @@ -14,7 +14,7 @@ Import-Package: org.osgi.framework, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.service.systray, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.event, javax.swing.table, diff --git a/src/net/java/sip/communicator/plugin/aimaccregwizz/FirstWizardPage.java b/src/net/java/sip/communicator/plugin/aimaccregwizz/FirstWizardPage.java index 92ce0f4..3ae8f22 100644 --- a/src/net/java/sip/communicator/plugin/aimaccregwizz/FirstWizardPage.java +++ b/src/net/java/sip/communicator/plugin/aimaccregwizz/FirstWizardPage.java @@ -11,9 +11,9 @@ import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; /** * The <tt>FirstWizardPage</tt> is the page, where user could enter the uin diff --git a/src/net/java/sip/communicator/plugin/aimaccregwizz/aimaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/aimaccregwizz/aimaccregwizz.manifest.mf index d71dd91..956d6c1 100644 --- a/src/net/java/sip/communicator/plugin/aimaccregwizz/aimaccregwizz.manifest.mf +++ b/src/net/java/sip/communicator/plugin/aimaccregwizz/aimaccregwizz.manifest.mf @@ -17,7 +17,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.icqconstants, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.event, javax.swing.table, diff --git a/src/net/java/sip/communicator/plugin/branding/AboutWindow.java b/src/net/java/sip/communicator/plugin/branding/AboutWindow.java index 47ba575..3b8958f 100644 --- a/src/net/java/sip/communicator/plugin/branding/AboutWindow.java +++ b/src/net/java/sip/communicator/plugin/branding/AboutWindow.java @@ -14,13 +14,12 @@ import javax.imageio.*; import javax.swing.*;
import javax.swing.event.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
+import net.java.sip.communicator.plugin.desktoputil.plaf.*;
import net.java.sip.communicator.service.browserlauncher.*;
import net.java.sip.communicator.service.gui.*;
-import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.Logger;
import net.java.sip.communicator.util.skin.*;
-import net.java.sip.communicator.util.swing.*;
-import net.java.sip.communicator.util.swing.plaf.*;
import org.jitsi.service.resources.*;
import org.jitsi.util.*;
@@ -266,7 +265,7 @@ public class AboutWindow "close");
}
- GuiUtils.addWindow(this);
+ WindowUtils.addWindow(this);
}
/**
diff --git a/src/net/java/sip/communicator/plugin/branding/JitsiWarningWindow.java b/src/net/java/sip/communicator/plugin/branding/JitsiWarningWindow.java index b072869..c94457d 100644 --- a/src/net/java/sip/communicator/plugin/branding/JitsiWarningWindow.java +++ b/src/net/java/sip/communicator/plugin/branding/JitsiWarningWindow.java @@ -11,8 +11,8 @@ import java.awt.event.*; import javax.swing.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.browserlauncher.*;
-import net.java.sip.communicator.util.swing.*;
import org.jitsi.service.resources.*;
import org.jitsi.util.*;
diff --git a/src/net/java/sip/communicator/plugin/branding/WelcomeWindow.java b/src/net/java/sip/communicator/plugin/branding/WelcomeWindow.java index 345546b..2692799 100644 --- a/src/net/java/sip/communicator/plugin/branding/WelcomeWindow.java +++ b/src/net/java/sip/communicator/plugin/branding/WelcomeWindow.java @@ -14,7 +14,7 @@ import java.io.*; import javax.imageio.*; import javax.swing.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.jitsi.service.resources.*; diff --git a/src/net/java/sip/communicator/plugin/branding/branding.manifest.mf b/src/net/java/sip/communicator/plugin/branding/branding.manifest.mf index 659f89d..73f314f 100755 --- a/src/net/java/sip/communicator/plugin/branding/branding.manifest.mf +++ b/src/net/java/sip/communicator/plugin/branding/branding.manifest.mf @@ -13,8 +13,8 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol, org.jitsi.util, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, - net.java.sip.communicator.util.swing.plaf, + net.java.sip.communicator.plugin.desktoputil, + net.java.sip.communicator.plugin.desktoputil.plaf, net.java.sip.communicator.util.skin, javax.swing, javax.swing.event, diff --git a/src/net/java/sip/communicator/plugin/certconfig/CertConfigEntryDialog.java b/src/net/java/sip/communicator/plugin/certconfig/CertConfigEntryDialog.java index c2f9e4c..aa9e9dd 100644 --- a/src/net/java/sip/communicator/plugin/certconfig/CertConfigEntryDialog.java +++ b/src/net/java/sip/communicator/plugin/certconfig/CertConfigEntryDialog.java @@ -18,9 +18,9 @@ import javax.security.auth.callback.*; import javax.swing.*;
import javax.swing.event.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.certificate.*;
import net.java.sip.communicator.util.Logger;
-import net.java.sip.communicator.util.swing.*;
import org.jitsi.service.resources.*;
import org.jitsi.util.*;
diff --git a/src/net/java/sip/communicator/plugin/certconfig/CertConfigPanel.java b/src/net/java/sip/communicator/plugin/certconfig/CertConfigPanel.java index c6e12c7..4e631c2 100644 --- a/src/net/java/sip/communicator/plugin/certconfig/CertConfigPanel.java +++ b/src/net/java/sip/communicator/plugin/certconfig/CertConfigPanel.java @@ -14,9 +14,9 @@ import javax.swing.*; import javax.swing.border.*;
import javax.swing.event.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.certificate.*;
import net.java.sip.communicator.service.gui.*;
-import net.java.sip.communicator.util.swing.*;
import org.jitsi.service.resources.*;
import org.jitsi.util.*;
diff --git a/src/net/java/sip/communicator/plugin/certconfig/certconfig.manifest.mf b/src/net/java/sip/communicator/plugin/certconfig/certconfig.manifest.mf index e58c0bd..58ca8f4 100644 --- a/src/net/java/sip/communicator/plugin/certconfig/certconfig.manifest.mf +++ b/src/net/java/sip/communicator/plugin/certconfig/certconfig.manifest.mf @@ -13,7 +13,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.resources, org.jitsi.util, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.border, javax.swing.event, diff --git a/src/net/java/sip/communicator/plugin/chatconfig/ChatConfigActivator.java b/src/net/java/sip/communicator/plugin/chatconfig/ChatConfigActivator.java index 16f7db7..7e19374 100644 --- a/src/net/java/sip/communicator/plugin/chatconfig/ChatConfigActivator.java +++ b/src/net/java/sip/communicator/plugin/chatconfig/ChatConfigActivator.java @@ -10,11 +10,11 @@ import java.util.*; import javax.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.replacement.*; import net.java.sip.communicator.service.resources.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; import org.jitsi.service.configuration.*; import org.jitsi.service.resources.*; diff --git a/src/net/java/sip/communicator/plugin/chatconfig/ChatConfigPanel.java b/src/net/java/sip/communicator/plugin/chatconfig/ChatConfigPanel.java index faf91c3..4656c7d 100644 --- a/src/net/java/sip/communicator/plugin/chatconfig/ChatConfigPanel.java +++ b/src/net/java/sip/communicator/plugin/chatconfig/ChatConfigPanel.java @@ -10,7 +10,7 @@ import java.awt.*; import javax.swing.*; import net.java.sip.communicator.plugin.chatconfig.replacement.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * The chat configuration panel. diff --git a/src/net/java/sip/communicator/plugin/chatconfig/chatconfig.manifest.mf b/src/net/java/sip/communicator/plugin/chatconfig/chatconfig.manifest.mf index f332b74..a503b1c 100644 --- a/src/net/java/sip/communicator/plugin/chatconfig/chatconfig.manifest.mf +++ b/src/net/java/sip/communicator/plugin/chatconfig/chatconfig.manifest.mf @@ -10,7 +10,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.gui.event, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, net.java.sip.communicator.service.replacement, javax.swing, javax.swing.event, diff --git a/src/net/java/sip/communicator/plugin/chatconfig/replacement/ReplacementConfigPanel.java b/src/net/java/sip/communicator/plugin/chatconfig/replacement/ReplacementConfigPanel.java index 7ea8e9d..e4c1d3e 100644 --- a/src/net/java/sip/communicator/plugin/chatconfig/replacement/ReplacementConfigPanel.java +++ b/src/net/java/sip/communicator/plugin/chatconfig/replacement/ReplacementConfigPanel.java @@ -15,8 +15,8 @@ import javax.swing.table.*; import net.java.sip.communicator.impl.replacement.smiley.*; import net.java.sip.communicator.plugin.chatconfig.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.replacement.*; -import net.java.sip.communicator.util.swing.*; import org.jitsi.service.configuration.*; diff --git a/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoContactPanel.java b/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoContactPanel.java index b570b77..3783346 100644 --- a/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoContactPanel.java +++ b/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoContactPanel.java @@ -11,8 +11,8 @@ import java.util.*; import javax.swing.*;
import javax.swing.event.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.swing.*;
/**
* The left side panel of ContactInfoDialog. Display all associated subcontacts
diff --git a/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoDetailsPanel.java b/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoDetailsPanel.java index e0bdf90..1e2ba22 100644 --- a/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoDetailsPanel.java +++ b/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoDetailsPanel.java @@ -15,6 +15,7 @@ import javax.swing.*; import javax.swing.event.*;
import javax.swing.text.html.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.ServerStoredDetails.BinaryDetail;
import net.java.sip.communicator.service.protocol.ServerStoredDetails.BirthDateDetail;
@@ -29,7 +30,6 @@ import net.java.sip.communicator.service.protocol.ServerStoredDetails.MiddleName import net.java.sip.communicator.service.protocol.ServerStoredDetails.PhoneNumberDetail;
import net.java.sip.communicator.service.protocol.ServerStoredDetails.TimeZoneDetail;
import net.java.sip.communicator.util.*;
-import net.java.sip.communicator.util.swing.*;
/**
* The right side panel of ContactInfoDialog. Shows one tab of a summary of
diff --git a/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoDialog.java b/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoDialog.java index 0e8075d..69ed2eb 100644 --- a/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoDialog.java +++ b/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoDialog.java @@ -9,9 +9,9 @@ package net.java.sip.communicator.plugin.contactinfo; import java.awt.*;
import java.util.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.swing.*;
/**
* A GUI plug-in for SIP Communicator that will allow cross protocol contact
diff --git a/src/net/java/sip/communicator/plugin/contactinfo/contactinfo.manifest.mf b/src/net/java/sip/communicator/plugin/contactinfo/contactinfo.manifest.mf index 09bf6c1..b1ec3ca 100644 --- a/src/net/java/sip/communicator/plugin/contactinfo/contactinfo.manifest.mf +++ b/src/net/java/sip/communicator/plugin/contactinfo/contactinfo.manifest.mf @@ -13,7 +13,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol,
org.jitsi.service.resources, net.java.sip.communicator.service.resources,
net.java.sip.communicator.util,
- net.java.sip.communicator.util.swing,
+ net.java.sip.communicator.plugin.desktoputil,
javax.swing,
javax.swing.event,
javax.swing.table,
diff --git a/src/net/java/sip/communicator/plugin/contactsourceconfig/ContactSourceConfigForm.java b/src/net/java/sip/communicator/plugin/contactsourceconfig/ContactSourceConfigForm.java index 77984f7..8fb25e4 100644 --- a/src/net/java/sip/communicator/plugin/contactsourceconfig/ContactSourceConfigForm.java +++ b/src/net/java/sip/communicator/plugin/contactsourceconfig/ContactSourceConfigForm.java @@ -10,8 +10,8 @@ import java.awt.event.*; import javax.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.gui.*; -import net.java.sip.communicator.util.swing.*; import org.osgi.framework.*; diff --git a/src/net/java/sip/communicator/plugin/contactsourceconfig/contactsourceconfig.manifest.mf b/src/net/java/sip/communicator/plugin/contactsourceconfig/contactsourceconfig.manifest.mf index 0814977..e60f6bd 100644 --- a/src/net/java/sip/communicator/plugin/contactsourceconfig/contactsourceconfig.manifest.mf +++ b/src/net/java/sip/communicator/plugin/contactsourceconfig/contactsourceconfig.manifest.mf @@ -7,7 +7,7 @@ Import-Package: net.java.sip.communicator.service.contactsource, net.java.sip.communicator.service.protocol,
net.java.sip.communicator.util, net.java.sip.communicator.service.gui, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, org.jitsi.service.resources, net.java.sip.communicator.service.resources, org.jitsi.service.configuration,
org.osgi.framework, diff --git a/src/net/java/sip/communicator/plugin/desktoputil/AntialiasingManager.java b/src/net/java/sip/communicator/plugin/desktoputil/AntialiasingManager.java new file mode 100644 index 0000000..bc0b2fd --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/AntialiasingManager.java @@ -0,0 +1,32 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; + +/** + * Through the <tt>AntialiasingManager</tt> the developer could activate the + * antialiasing mechanism when painting. The method that do the job is + * the <code>activateAntialiasing</code> method. It takes a <tt>Graphics</tt> + * object and activates the antialiasing for it. + * + * @author Yana Stamcheva + */ +public class AntialiasingManager +{ + + /** + * Activates the antialiasing mechanism for the given <tt>Graphics</tt> + * object. + * @param g The <tt>Graphics</tt> object. + */ + public static void activateAntialiasing(Graphics g) + { + ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/AuthenticationWindow.java b/src/net/java/sip/communicator/plugin/desktoputil/AuthenticationWindow.java new file mode 100644 index 0000000..07eefc0 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/AuthenticationWindow.java @@ -0,0 +1,742 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.Logger; + +import org.jitsi.util.*; + +/** + * The <tt>AuthenticationWindow</tt> is the window where the user should type + * his user identifier and password to login. + * + * @author Yana Stamcheva + */ +public class AuthenticationWindow + extends SIPCommDialog + implements ActionListener +{ + private static final long serialVersionUID = 1L; + + /** + * Used for logging. + */ + private static Logger logger = Logger.getLogger(AuthenticationWindow.class); + + /** + * Info text area. + */ + private final JTextArea infoTextArea = new JTextArea(); + + /** + * The uin component. + */ + private JComponent uinValue; + + /** + * The password field. + */ + private final JPasswordField passwdField = new JPasswordField(15); + + /** + * The login button. + */ + private final JButton loginButton = new JButton( + DesktopUtilActivator.getResources().getI18NString("service.gui.OK")); + + /** + * The cancel button. + */ + private final JButton cancelButton = new JButton( + DesktopUtilActivator.getResources().getI18NString("service.gui.CANCEL")); + + /** + * The check box indicating if the password should be remembered. + */ + private final JCheckBox rememberPassCheckBox = new SIPCommCheckBox( + DesktopUtilActivator.getResources() + .getI18NString("service.gui.REMEMBER_PASSWORD"), + DesktopUtilActivator.getConfigurationService() + .getBoolean(PNAME_SAVE_PASSWORD_TICKED, false)); + + /** + * Property to disable/enable allow save password option + * in authentication window. By default it is enabled. + */ + private static final String PNAME_ALLOW_SAVE_PASSWORD = + "net.java.sip.communicator.util.swing.auth.ALLOW_SAVE_PASSWORD"; + + /** + * Property to set whether the save password option in + * the authentication window is ticked by default or not. + * By default it is not ticked + */ + private static final String PNAME_SAVE_PASSWORD_TICKED = + "net.java.sip.communicator.util.swing.auth.SAVE_PASSWORD_TICKED"; + + /** + * The name of the server, for which this authentication window is about. + */ + private String server; + + /** + * The user name. + */ + private String userName; + + /** + * The password. + */ + private char[] password; + + /** + * Indicates if the password should be remembered. + */ + private boolean isRememberPassword = false; + + /** + * Indicates if the window has been canceled. + */ + private boolean isCanceled = false; + + /** + * A lock used to synchronize data setting. + */ + private final Object lock = new Object(); + + /** + * The condition that decides whether to continue waiting for data. + */ + private boolean buttonClicked = false; + + /** + * Used to override default Authentication window title. + */ + private String windowTitle = null; + + /** + * Used to override default window text. + */ + private String windowText = null; + + /** + * Used to override username label text. + */ + private String usernameLabelText = null; + + /** + * Used to override password label text. + */ + private String passwordLabelText = null; + + /** + * The sign up link if specified. + */ + private String signupLink = null; + + /** + * Creates an instance of the <tt>LoginWindow</tt>. + * + * @param server the server name + * @param isUserNameEditable indicates if the user name is editable + * @param icon the icon to display on the left of the authentication window + */ + public AuthenticationWindow(String server, + boolean isUserNameEditable, + ImageIcon icon) + { + this(null, null, server, isUserNameEditable, false, + icon, null, null, null, null, null, null); + } + + /** + * Creates an instance of the <tt>LoginWindow</tt>. + * + * @param server the server name + * @param isUserNameEditable indicates if the user name is editable + * @param icon the icon to display on the left of the authentication window + * @param windowTitle customized window title + * @param windowText customized window text + * @param usernameLabelText customized username field label text + * @param passwordLabelText customized password field label text + * @param errorMessage an error message if this dialog is shown to indicate + * the user that something went wrong + * @param signupLink an URL that allows the user to sign up + */ + private AuthenticationWindow(String userName, + char[] password, + String server, + boolean isUserNameEditable, + boolean isRememberPassword, + ImageIcon icon, + String windowTitle, + String windowText, + String usernameLabelText, + String passwordLabelText, + String errorMessage, + String signupLink) + { + super(false); + + this.windowTitle = windowTitle; + this.windowText = windowText; + this.usernameLabelText = usernameLabelText; + this.passwordLabelText = passwordLabelText; + this.isRememberPassword = isRememberPassword; + this.signupLink = signupLink; + + init(userName, password, server, isUserNameEditable, icon, errorMessage); + } + + /** + * Initializes this authentication window. + * + * @param server the server + * @param isUserNameEditable indicates if the user name is editable + * @param icon the icon to show on the authentication window + */ + private void init( String userName, + char[] password, + String server, + boolean isUserNameEditable, + ImageIcon icon, + String errorMessage) + { + this.server = server; + + initIcon(icon); + + if(!isUserNameEditable) + this.uinValue = new JLabel(); + else + this.uinValue = new JTextField(); + + this.init(); + + this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + + this.enableKeyActions(); + + this.setResizable(false); + + /* + * Workaround for the following bug: + * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4446522 + * Need to pack() the window after it's opened in order to obtain the + * correct size of our infoTextArea, otherwise window size is wrong and + * buttons on the south are cut. + */ + this.addWindowListener(new WindowAdapter() + { + public void windowOpened(WindowEvent e) + { + pack(); + removeWindowListener(this); + } + }); + + if (OSUtils.IS_MAC) + getRootPane() + .putClientProperty("apple.awt.brushMetalLook", Boolean.TRUE); + + if (userName != null) + { + if (uinValue instanceof JLabel) + ((JLabel) uinValue).setText(userName); + else if (uinValue instanceof JTextField) + ((JTextField) uinValue).setText(userName); + } + + if (password != null) + passwdField.setText(new String(password)); + + if(errorMessage != null) + { + this.infoTextArea.setForeground(Color.RED); + this.infoTextArea.setText(errorMessage); + } + } + + /** + * Creates an instance of the <tt>LoginWindow</tt>. + * + * @param userName the user name to set by default + * @param password the password to set by default + * @param server the server name this authentication window is about + * @param isUserNameEditable indicates if the user name should be editable + * by the user or not + * @param icon the icon displayed on the left of the authentication window + * @param errorMessage an error message explaining a reason for opening + * the authentication dialog (when a wrong password was provided, etc.) + */ + public AuthenticationWindow(String userName, + char[] password, + String server, + boolean isUserNameEditable, + ImageIcon icon, + String errorMessage) + { + this(userName, password, server, + isUserNameEditable, + false, + icon, null, null, null, null, errorMessage, null); + } + + /** + * Creates an instance of the <tt>LoginWindow</tt>. + * + * @param userName the user name to set by default + * @param password the password to set by default + * @param server the server name this authentication window is about + * @param isUserNameEditable indicates if the user name should be editable + * by the user or not + * @param icon the icon displayed on the left of the authentication window + * @param errorMessage an error message explaining a reason for opening + * the authentication dialog (when a wrong password was provided, etc.) + * @param signupLink an URL that allows the user to sign up + */ + public AuthenticationWindow(String userName, + char[] password, + String server, + boolean isUserNameEditable, + ImageIcon icon, + String errorMessage, + String signupLink) + { + this(userName, password, server, + isUserNameEditable, + false, + icon, null, null, null, null, errorMessage, signupLink); + } + + /** + * Creates an instance of the <tt>LoginWindow</tt>. + * + * @param userName the user name to set by default + * @param password the password to set by default + * @param server the server name this authentication window is about + * @param isUserNameEditable indicates if the user name should be editable + * by the user or not + * @param icon the icon displayed on the left of the authentication window + */ + public AuthenticationWindow( + String userName, + char[] password, + String server, + boolean isUserNameEditable, + ImageIcon icon) + { + this(userName, password, server, isUserNameEditable, icon, null, null); + } + + /** + * Creates an instance of the <tt>LoginWindow</tt>. + * + * @param owner the owner of this dialog + * @param userName the user name to set by default + * @param password the password to set by default + * @param server the server name this authentication window is about + * @param isUserNameEditable indicates if the user name should be editable + * by the user or not + * @param icon the icon displayed on the left of the authentication window + */ + public AuthenticationWindow( + Dialog owner, + String userName, + char[] password, + String server, + boolean isUserNameEditable, + ImageIcon icon) + { + super(owner, false); + + init(userName, password, server, isUserNameEditable, icon, null); + } + + /** + * Shows or hides the "save password" checkbox. + * @param allow the checkbox is shown when allow is <tt>true</tt> + */ + public void setAllowSavePassword(boolean allow) + { + rememberPassCheckBox.setVisible(allow); + } + + /** + * Initializes the icon image. + * + * @param icon the icon to show on the left of the window + */ + private void initIcon(ImageIcon icon) + { + // If an icon isn't provided set the application logo icon by default. + if (icon == null) + icon = DesktopUtilActivator.getResources() + .getImage("service.gui.SIP_COMMUNICATOR_LOGO_64x64"); + + JLabel iconLabel = new JLabel(icon); + + iconLabel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + + iconLabel.setAlignmentY(Component.TOP_ALIGNMENT); + + JPanel iconPanel = new TransparentPanel(new BorderLayout()); + iconPanel.add(iconLabel, BorderLayout.NORTH); + + getContentPane().add(iconPanel, BorderLayout.WEST); + } + + /** + * Constructs the <tt>LoginWindow</tt>. + */ + private void init() + { + String title; + + if(windowTitle != null) + title = windowTitle; + else + title = DesktopUtilActivator.getResources().getI18NString( + "service.gui.AUTHENTICATION_WINDOW_TITLE", new String[]{server}); + + String text; + if(windowText != null) + text = windowText; + else + text = DesktopUtilActivator.getResources().getI18NString( + "service.gui.AUTHENTICATION_REQUESTED_SERVER", + new String[]{server}); + + String uinText; + if(usernameLabelText != null) + uinText = usernameLabelText; + else + uinText = DesktopUtilActivator.getResources().getI18NString( + "service.gui.IDENTIFIER"); + + String passText; + if(passwordLabelText != null) + passText = passwordLabelText; + else + passText = DesktopUtilActivator.getResources().getI18NString( + "service.gui.PASSWORD"); + + setTitle(title); + + infoTextArea.setEditable(false); + infoTextArea.setOpaque(false); + infoTextArea.setLineWrap(true); + infoTextArea.setWrapStyleWord(true); + infoTextArea.setFont( + infoTextArea.getFont().deriveFont(Font.BOLD)); + infoTextArea.setText(text); + infoTextArea.setAlignmentX(0.5f); + + JLabel uinLabel = new JLabel(uinText); + uinLabel.setFont(uinLabel.getFont().deriveFont(Font.BOLD)); + + JLabel passwdLabel = new JLabel(passText); + passwdLabel.setFont(passwdLabel.getFont().deriveFont(Font.BOLD)); + + TransparentPanel labelsPanel + = new TransparentPanel(new GridLayout(0, 1, 8, 8)); + + labelsPanel.add(uinLabel); + labelsPanel.add(passwdLabel); + + TransparentPanel textFieldsPanel + = new TransparentPanel(new GridLayout(0, 1, 8, 8)); + + textFieldsPanel.add(uinValue); + textFieldsPanel.add(passwdField); + + JPanel southFieldsPanel = new TransparentPanel(new GridLayout(1, 2)); + + this.rememberPassCheckBox.setOpaque(false); + this.rememberPassCheckBox.setBorder(null); + + southFieldsPanel.add(rememberPassCheckBox); + if (signupLink != null && signupLink.length() > 0) + southFieldsPanel.add(createWebSignupLabel( + DesktopUtilActivator.getResources().getI18NString( + "plugin.simpleaccregwizz.SIGNUP"), signupLink)); + else + southFieldsPanel.add(new JLabel()); + + boolean allowRememberPassword = true; + + String allowRemPassStr = DesktopUtilActivator.getResources().getSettingsString( + PNAME_ALLOW_SAVE_PASSWORD); + if(allowRemPassStr != null) + { + allowRememberPassword = Boolean.parseBoolean(allowRemPassStr); + } + allowRememberPassword = DesktopUtilActivator.getConfigurationService() + .getBoolean(PNAME_ALLOW_SAVE_PASSWORD, allowRememberPassword); + + setAllowSavePassword(allowRememberPassword); + + JPanel buttonPanel + = new TransparentPanel(new FlowLayout(FlowLayout.CENTER)); + + buttonPanel.add(loginButton); + buttonPanel.add(cancelButton); + + JPanel southEastPanel = new TransparentPanel(new BorderLayout()); + southEastPanel.add(buttonPanel, BorderLayout.EAST); + + TransparentPanel mainPanel + = new TransparentPanel(new BorderLayout(10, 10)); + + mainPanel.setBorder( + BorderFactory.createEmptyBorder(20, 0, 20, 20)); + + JPanel mainFieldsPanel = new TransparentPanel(new BorderLayout(0, 10)); + mainFieldsPanel.add(labelsPanel, BorderLayout.WEST); + mainFieldsPanel.add(textFieldsPanel, BorderLayout.CENTER); + mainFieldsPanel.add(southFieldsPanel, BorderLayout.SOUTH); + + mainPanel.add(infoTextArea, BorderLayout.NORTH); + mainPanel.add(mainFieldsPanel, BorderLayout.CENTER); + mainPanel.add(southEastPanel, BorderLayout.SOUTH); + + this.getContentPane().add(mainPanel, BorderLayout.EAST); + + this.loginButton.setName("ok"); + this.cancelButton.setName("cancel"); + if(loginButton.getPreferredSize().width + > cancelButton.getPreferredSize().width) + cancelButton.setPreferredSize(loginButton.getPreferredSize()); + else + loginButton.setPreferredSize(cancelButton.getPreferredSize()); + + this.loginButton.setMnemonic( + DesktopUtilActivator.getResources().getI18nMnemonic("service.gui.OK")); + this.cancelButton.setMnemonic( + DesktopUtilActivator.getResources().getI18nMnemonic("service.gui.CANCEL")); + + this.loginButton.addActionListener(this); + this.cancelButton.addActionListener(this); + + this.getRootPane().setDefaultButton(loginButton); + } + + /** + * Handles the <tt>ActionEvent</tt> triggered when one of the buttons is + * clicked. When "Login" button is chosen installs a new account from + * the user input and logs in. + * + * @param evt the action event that has just occurred. + */ + public void actionPerformed(ActionEvent evt) + { + JButton button = (JButton) evt.getSource(); + String buttonName = button.getName(); + + if ("ok".equals(buttonName)) + { + if(uinValue instanceof JLabel) + userName = ((JLabel) uinValue).getText(); + else if(uinValue instanceof JTextField) + userName = ((JTextField) uinValue).getText(); + + password = passwdField.getPassword(); + isRememberPassword = rememberPassCheckBox.isSelected(); + } + else + { + isCanceled = true; + } + + // release the caller that opened the window + buttonClicked = true; + synchronized (lock) + { + lock.notify(); + } + + this.dispose(); + } + + /** + * Enables the actions when a key is pressed, for now + * closes the window when esc is pressed. + */ + private void enableKeyActions() + { + @SuppressWarnings("serial") + UIAction act = new UIAction() + { + public void actionPerformed(ActionEvent e) + { + close(true); + } + }; + + getRootPane().getActionMap().put("close", act); + + InputMap imap = this.getRootPane().getInputMap( + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + imap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "close"); + } + + /** + * Automatically clicks the cancel button, when this window is closed. + * + * @param isEscaped indicates if the window has been closed by pressing the + * Esc key + */ + @Override + protected void close(boolean isEscaped) + { + this.cancelButton.doClick(); + } + + /** + * Shows this modal dialog. + * + * @param isVisible specifies whether we should be showing or hiding the + * window. + */ + @Override + public void setVisible(final boolean isVisible) + { + this.setName("AUTHENTICATION"); + + if(getOwner() != null) + setModal(true); + + if(isVisible) + { + addWindowFocusListener(new WindowAdapter() { + public void windowGainedFocus(WindowEvent e) + { + removeWindowFocusListener(this); + + if (uinValue instanceof JTextField && + "".equals(((JTextField) uinValue).getText())) + { + uinValue.requestFocusInWindow(); + } + else + passwdField.requestFocusInWindow(); + } + }); + } + + super.setVisible(isVisible); + + if(isVisible) + { + if(getOwner() != null) + return; + + synchronized (lock) + { + while(!buttonClicked) + { + try + { + lock.wait(); + } + catch (InterruptedException e) + {} // we don't care, just retry + } + } + } + } + + /** + * Indicates if this window has been canceled. + * + * @return <tt>true</tt> if this window has been canceled, <tt>false</tt> - + * otherwise + */ + public boolean isCanceled() + { + return isCanceled; + } + + /** + * Returns the user name entered by the user or previously set if the + * user name is not editable. + * + * @return the user name + */ + public String getUserName() + { + return userName; + } + + /** + * Returns the password entered by the user. + * + * @return the password + */ + public char[] getPassword() + { + return password; + } + + /** + * Indicates if the password should be remembered. + * + * @return <tt>true</tt> if the password should be remembered, + * <tt>false</tt> - otherwise + */ + public boolean isRememberPassword() + { + return isRememberPassword; + } + + /** + * Creates the subscribe label. + * @param linkName the link name + * @return the newly created subscribe label + */ + private Component createWebSignupLabel( String linkName, + final String linkURL) + { + JLabel subscribeLabel = + new JLabel("<html><a href=''>" + + linkName + + "</a></html>", + JLabel.RIGHT); + + subscribeLabel.setCursor(new Cursor(Cursor.HAND_CURSOR)); + subscribeLabel.setToolTipText( + DesktopUtilActivator.getResources().getI18NString( + "plugin.simpleaccregwizz.SPECIAL_SIGNUP")); + subscribeLabel.addMouseListener(new MouseAdapter() + { + public void mousePressed(MouseEvent e) + { + try + { + DesktopUtilActivator.getBrowserLauncher() + .openURL(linkURL); + } + catch (UnsupportedOperationException ex) + { + // This should not happen, because we check if the + // operation is supported, before adding the sign + // up. + logger.error("The web sign up is not supported.", + ex); + } + } + }); + return subscribeLabel; + } + +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/ComponentMover.java b/src/net/java/sip/communicator/plugin/desktoputil/ComponentMover.java new file mode 100644 index 0000000..5a0c5b6 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/ComponentMover.java @@ -0,0 +1,108 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.event.*; + +/** + * + * @author Yana Stamcheva + */ +public class ComponentMover +{ + /** + * Registers the given component for component dragging/moving functionality. + * + * @param c the component, which should be moved on drag + */ + public static void registerComponent(Component c) + { + MoverMouseListener l = new MoverMouseListener(); + + c.addMouseListener(l); + c.addMouseMotionListener(l); + } + + /** + * The Mouse listener for local video. It is responsible for dragging local + * video. + */ + private static class MoverMouseListener + implements MouseListener, + MouseMotionListener + { + /** + * Indicates if we're currently during a drag operation. + */ + private boolean inDrag = false; + + /** + * The previous x coordinate of the drag. + */ + private int previousX = 0; + + /** + * The previous y coordinate of the drag. + */ + private int previousY = 0; + + /** + * Indicates that the mouse has been dragged. + * + * @param event the <tt>MouseEvent</tt> that notified us + */ + public void mouseDragged(MouseEvent event) + { + Point p = event.getPoint(); + + if (inDrag) + { + Component c = (Component) event.getSource(); + + int newX = c.getX() + p.x - previousX; + int newY = c.getY() + p.y - previousY; + + c.setLocation(newX, newY); + } + } + + public void mouseMoved(MouseEvent event) {} + + public void mouseClicked(MouseEvent event) {} + + public void mouseEntered(MouseEvent event) {} + + public void mouseExited(MouseEvent event) {} + + /** + * Indicates that the mouse has been pressed. + * + * @param event the <tt>MouseEvent</tt> that notified us + */ + public void mousePressed(MouseEvent event) + { + Point p = event.getPoint(); + + previousX = p.x; + previousY = p.y; + inDrag = true; + } + + /** + * Indicates that the mouse has been released. + * + * @param event the <tt>MouseEvent</tt> that notified us + */ + public void mouseReleased(MouseEvent event) + { + inDrag = false; + previousX = 0; + previousY = 0; + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/ComponentUtils.java b/src/net/java/sip/communicator/plugin/desktoputil/ComponentUtils.java new file mode 100644 index 0000000..8cb945d --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/ComponentUtils.java @@ -0,0 +1,139 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.font.*; +import java.awt.geom.*; + +import javax.swing.*; + +/** + * Utility class for component related operations. For example: calculating size + * of strings depending on the component, obtaining component index, updating + * component tree UI, etc. + * + * @author Yana Stamcheva + */ +public class ComponentUtils +{ + /** + * Returns the width in pixels of a text. + * + * @param c the component where the text is contained + * @param text the text to measure + * @return the width in pixels of a text. + */ + public static int getStringWidth(Component c, String text) + { + return SwingUtilities.computeStringWidth(c + .getFontMetrics(c.getFont()), text); + } + + /** + * Returns the size of the given text computed towards to the given + * component. + * + * @param c the component where the text is contained + * @param text the text to measure + * @return the dimensions of the text + */ + public static Dimension getStringSize(Component c, String text) + { + // get metrics from the graphics + FontMetrics metrics = c.getFontMetrics(c.getFont()); + // get the height of a line of text in this font and render context + int hgt = metrics.getHeight(); + // get the advance of my text in this font and render context + int adv = metrics.stringWidth(text); + // calculate the size of a box to hold the text with some padding. + return new Dimension(adv+2, hgt+2); + } + + /** + * Returns the bounds of the given string. + * + * @param text the string to measure + * @return the bounds of the given string + */ + public static Rectangle2D getDefaultStringSize(String text) + { + Font font = UIManager.getFont("Label.font"); + FontRenderContext frc = new FontRenderContext(null, true, false); + TextLayout layout = new TextLayout(text, font, frc); + + return layout.getBounds(); + } + + /** + * A simple minded look and feel change: ask each node in the tree + * to <code>updateUI()</code> -- that is, to initialize its UI property + * with the current look and feel. + * + * @param c UI component. + */ + public static void updateComponentTreeUI(Component c) + { + updateComponentTreeUI0(c); + c.invalidate(); + c.validate(); + c.repaint(); + } + + /** + * Returns the index of the given component in the given container. + * + * @param c the Component to look for + * @param container the parent container, where this component is added + * @return the index of the component in the container or -1 if no such + * component is contained in the container + */ + public static int getComponentIndex(Component c, Container container) + { + for (int i = 0, count = container.getComponentCount(); i < count; i++) + { + if (container.getComponent(i).equals(c)) + return i; + } + return -1; + } + + /** + * Repaints UI tree recursively. + * + * @param c UI component. + */ + private static void updateComponentTreeUI0(Component c) + { + if (c instanceof JComponent) + { + JComponent jc = (JComponent) c; + jc.invalidate(); + jc.validate(); + jc.repaint(); + JPopupMenu jpm =jc.getComponentPopupMenu(); + if(jpm != null && jpm.isVisible() && jpm.getInvoker() == jc) + { + updateComponentTreeUI(jpm); + } + } + Component[] children = null; + if (c instanceof JMenu) + { + children = ((JMenu)c).getMenuComponents(); + } + else if (c instanceof java.awt.Container) + { + children = ((java.awt.Container)c).getComponents(); + } + if (children != null) + { + for(int i = 0; i < children.length; i++) + updateComponentTreeUI0(children[i]); + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/DesktopUtilActivator.java b/src/net/java/sip/communicator/plugin/desktoputil/DesktopUtilActivator.java new file mode 100644 index 0000000..dc23458 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/DesktopUtilActivator.java @@ -0,0 +1,177 @@ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.image.*; +import java.net.*; + +import javax.imageio.*; + +import net.java.sip.communicator.service.browserlauncher.*; +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.keybindings.*; +import net.java.sip.communicator.service.resources.*; +import net.java.sip.communicator.util.*; + +import org.jitsi.service.configuration.*; +import org.jitsi.service.resources.*; +import org.osgi.framework.*; + +public class DesktopUtilActivator + implements BundleActivator +{ + /** + * The <tt>Logger</tt> used by the <tt>SwingUtilActivator</tt> class and its + * instances for logging output. + */ + private static final Logger logger + = Logger.getLogger(DesktopUtilActivator.class); + + private static ConfigurationService configurationService; + + private static ResourceManagementService resourceService; + + private static KeybindingsService keybindingsService; + + private static BrowserLauncherService browserLauncherService; + + private static UIService uiService; + + static BundleContext bundleContext; + + /** + * Calls <tt>Thread.setUncaughtExceptionHandler()</tt> + * + * @param context The execution context of the bundle being started + * (unused). + * @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 + { + bundleContext = context; + } + + /** + * Doesn't do anything. + * + * @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 + { + } + + /** + * Returns the <tt>ConfigurationService</tt> currently registered. + * + * @return the <tt>ConfigurationService</tt> + */ + public static ConfigurationService getConfigurationService() + { + if (configurationService == null) + { + configurationService + = ServiceUtils.getService( + bundleContext, + ConfigurationService.class); + } + return configurationService; + } + + /** + * Returns the service giving access to all application resources. + * + * @return the service giving access to all application resources. + */ + public static ResourceManagementService getResources() + { + if (resourceService == null) + { + resourceService + = ResourceManagementServiceUtils.getService(bundleContext); + } + return resourceService; + } + + /** + * Returns the image corresponding to the given <tt>imageID</tt>. + * + * @param imageID the identifier of the image + * @return the image corresponding to the given <tt>imageID</tt> + */ + public static BufferedImage getImage(String imageID) + { + BufferedImage image = null; + + URL path = getResources().getImageURL(imageID); + + if (path == null) + return null; + + try + { + image = ImageIO.read(path); + } + catch (Exception exc) + { + logger.error("Failed to load image:" + path, exc); + } + + return image; + } + + /** + * Returns the <tt>KeybindingsService</tt> currently registered. + * + * @return the <tt>KeybindingsService</tt> + */ + public static KeybindingsService getKeybindingsService() + { + if (keybindingsService == null) + { + keybindingsService + = ServiceUtils.getService( + bundleContext, + KeybindingsService.class); + } + return keybindingsService; + } + + + /** + * Returns the <tt>BrowserLauncherService</tt> obtained from the bundle + * context. + * @return the <tt>BrowserLauncherService</tt> obtained from the bundle + * context + */ + public static BrowserLauncherService getBrowserLauncher() + { + if (browserLauncherService == null) + { + browserLauncherService + = ServiceUtils.getService( + bundleContext, + BrowserLauncherService.class); + } + return browserLauncherService; + } + + /** + * Gets the <tt>UIService</tt> instance registered in the + * <tt>BundleContext</tt> of the <tt>UtilActivator</tt>. + * + * @return the <tt>UIService</tt> instance registered in the + * <tt>BundleContext</tt> of the <tt>UtilActivator</tt> + */ + public static UIService getUIService() + { + if (uiService == null) + uiService = ServiceUtils.getService(bundleContext, UIService.class); + return uiService; + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/plugin/desktoputil/EmphasizedLabel.java b/src/net/java/sip/communicator/plugin/desktoputil/EmphasizedLabel.java new file mode 100644 index 0000000..488030f --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/EmphasizedLabel.java @@ -0,0 +1,104 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; + +import javax.swing.*; + +/** + * A label with white shadow. + * Based on code published on http://explodingpixels.wordpress.com. + * + * @author Yana Stamcheva + */ +public class EmphasizedLabel + extends JLabel +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + private boolean fUseEmphasisColor; + + /** + * The color used to paint the shadow. + */ + public static final Color EMPHASIZED_FONT_COLOR = + new Color(255, 255, 255, 110); + + /** + * The color used to paint focused view. + */ + public static final Color EMPHASIZED_FOCUSED_FONT_COLOR = + new Color(0x000000); + + /** + * The color used to paint unfocused view. + */ + public static final Color EMPHASIZED_UNFOCUSED_FONT_COLOR = + new Color(0x3f3f3f); + + /** + * Creates an instance of <tt>EmphasizedLabel</tt>. + * + * @param text the text to show in this label + */ + public EmphasizedLabel(String text) + { + super(text); + } + + /** + * Overrides the <tt>getPreferredSize()</tt> method in order to enlarge the + * height of this label, which should welcome the lightening shadow. + */ + @Override + public Dimension getPreferredSize() + { + Dimension d = super.getPreferredSize(); + d.height += 1; + return d; + } + + /** + * Overrides the <tt>getForeground()</tt> method in order to provide + * different foreground color, depending on whether the label is focused. + */ + @Override + public Color getForeground() + { + Color retVal; + Window window = SwingUtilities.getWindowAncestor(this); + boolean hasFoucs = window != null && window.isFocused(); + + if (fUseEmphasisColor) + retVal = EMPHASIZED_FONT_COLOR; + else if (hasFoucs) + retVal = EMPHASIZED_FOCUSED_FONT_COLOR; + else + retVal = EMPHASIZED_UNFOCUSED_FONT_COLOR; + + return retVal; + } + + /** + * Paints this label. + * + * @param g the <tt>Graphics</tt> object used for painting + */ + @Override + protected void paintComponent(Graphics g) + { + fUseEmphasisColor = true; + g.translate(0,1); + super.paintComponent(g); + g.translate(0,-1); + fUseEmphasisColor = false; + super.paintComponent(g); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/ErrorDialog.java b/src/net/java/sip/communicator/plugin/desktoputil/ErrorDialog.java new file mode 100644 index 0000000..91ad614 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/ErrorDialog.java @@ -0,0 +1,381 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.event.*; +import java.io.*; + +import javax.swing.*; +import javax.swing.event.*; + +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.skin.*; + +/** + * Implements a <tt>JDialog</tt> which displays an error message and, + * optionally, a <tt>Throwable</tt> stack trace. <tt>ErrorDialog</tt> has an OK + * button which dismisses the message and a link to display the + * <tt>Throwable</tt> stack trace upon request if available. + * + * @author Yana Stamcheva + * @author Adam Netocny + * @author Lyubomir Marinov + */ +public class ErrorDialog + extends SIPCommDialog + implements ActionListener, + HyperlinkListener, + Skinnable +{ + private static final long serialVersionUID = 1L; + + /** + * The <tt>Logger</tt> used by the <tt>ErrorDialog</tt> class and its + * instances for logging output. + */ + private static final Logger logger = Logger.getLogger(ErrorDialog.class); + + private JButton okButton + = new JButton( + DesktopUtilActivator.getResources().getI18NString("service.gui.OK")); + + private JLabel iconLabel + = new JLabel(new ImageIcon( + DesktopUtilActivator.getImage("service.gui.icons.ERROR_ICON"))); + + private StyledHTMLEditorPane htmlMsgEditorPane = new StyledHTMLEditorPane(); + + private JTextArea stackTraceTextArea = new JTextArea(); + + private JScrollPane stackTraceScrollPane = new JScrollPane(); + + private TransparentPanel buttonsPanel + = new TransparentPanel(new FlowLayout(FlowLayout.CENTER)); + + private TransparentPanel infoMessagePanel = new TransparentPanel(); + + private TransparentPanel messagePanel + = new TransparentPanel(new BorderLayout()); + + private TransparentPanel mainPanel + = new TransparentPanel(new BorderLayout(10, 10)); + + /** + * Load the "net.java.sip.communicator.SHOW_STACK_TRACE" property to + * determine whether we should show stack trace in error dialogs. + * Default is show. + */ + private static String showStackTraceDefaultProp + = DesktopUtilActivator.getResources().getSettingsString( + "net.java.sip.communicator.SHOW_STACK_TRACE"); + + /** + * Should we show stack trace. + */ + private final static boolean showStackTrace = + (showStackTraceDefaultProp != null) ? + Boolean.parseBoolean(showStackTraceDefaultProp) : true; + + /** + * The indicator which determines whether the details of the error are + * currently shown. + * <p> + * The indicator is initially set to <tt>true</tt> because the constructor + * {@link #ErrorDialog(Frame, String, String, Throwable)} calls + * {@link #showOrHideDetails()} and thus <tt>ErrorDialog</tt> defaults to + * not showing the details of the error. + * </p> + */ + private boolean detailsShown = true; + + /** + * The type of <tt>ErrorDialog</tt> which displays a warning instead of an + * error. + */ + public static final int WARNING = 1; + + /** + * The type of this <tt>ErrorDialog</tt> (e.g. {@link #WARNING}). The + * default <tt>ErrorDialog</tt> displays an error. + */ + private int type = 0; + + /** + * The maximum width that we allow message dialogs to have. + */ + private static final int MAX_MSG_PANE_WIDTH = 340; + + /** + * The maximum height that we allow message dialogs to have. + */ + private static final int MAX_MSG_PANE_HEIGHT = 800; + + /** + * Initializes a new <tt>ErrorDialog</tt> with a specific owner + * <tt>Frame</tt>, title and message to be displayed. + * + * @param owner the dialog owner + * @param title the title of the dialog + * @param message the message to be displayed + */ + public ErrorDialog( Frame owner, + String title, + String message) + { + super(owner, false); + + this.mainPanel.setBorder( + BorderFactory.createEmptyBorder(20, 20, 10, 20)); + + if (showStackTrace) + { + this.stackTraceScrollPane.setBorder(BorderFactory.createLineBorder( + iconLabel.getForeground())); + + this.stackTraceScrollPane.setHorizontalScrollBarPolicy( + JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + } + + this.setTitle(title); + + this.infoMessagePanel.setLayout(new BorderLayout()); + + JEditorPane messageArea = new JEditorPane(); + + /* + * Make JEditorPane respect our default font because we will be using it + * to just display text. + */ + messageArea.putClientProperty( + JEditorPane.HONOR_DISPLAY_PROPERTIES, + true); + + messageArea.setOpaque(false); + messageArea.setEditable(false); + messageArea.setContentType("text/html"); + messageArea.setText( + "<html><body><p align=\"left\" >"+message+"</p></body></html>"); + //try to reevaluate the preferred size of the message pane. + //(this is definitely not a neat way to do it ... but it works). + messageArea.setSize( + new Dimension(MAX_MSG_PANE_WIDTH, MAX_MSG_PANE_HEIGHT)); + messageArea.setPreferredSize( + new Dimension( + MAX_MSG_PANE_WIDTH, + messageArea.getPreferredSize().height)); + + this.infoMessagePanel.add(messageArea, BorderLayout.CENTER); + + this.init(); + } + + /** + * Initializes a new <tt>ErrorDialog</tt> with a specific owner + * <tt>Frame</tt>, title, error message to be displayed and the + * <tt>Throwable</tt> associated with the error. + * + * @param owner the dialog owner + * @param title the title of the dialog + * @param message the message to be displayed + * @param e the exception corresponding to the error + */ + public ErrorDialog( Frame owner, + String title, + String message, + Throwable e) + { + this(owner, title, message); + + if (showStackTrace) + { + this.setTitle(title); + + this.htmlMsgEditorPane.setEditable(false); + this.htmlMsgEditorPane.setOpaque(false); + + this.htmlMsgEditorPane.addHyperlinkListener(this); + + showOrHideDetails(); + + this.infoMessagePanel.add(htmlMsgEditorPane, BorderLayout.SOUTH); + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + pw.close(); + + String stackTrace = sw.toString(); + + try + { + sw.close(); + } + catch (IOException ex) + { + //really shouldn't happen. but log anyway + logger.error("Failed to close a StringWriter. ", ex); + } + + this.stackTraceTextArea.setText(stackTrace); + } + } + + /** + * Initializes a new <tt>ErrorDialog</tt> with a specific owner + * <tt>Frame</tt>, title and message to be displayed and of a specific type. + * + * @param owner the dialog owner + * @param title the title of the error dialog + * @param message the message to be displayed + * @param type the dialog type + */ + public ErrorDialog( Frame owner, + String title, + String message, + int type) + { + this(owner, title, message); + + if(type == WARNING) + { + iconLabel.setIcon(new ImageIcon( + DesktopUtilActivator.getImage("service.gui.icons.WARNING_ICON"))); + this.type = type; + } + } + + /** + * Initializes this dialog. + */ + private void init() + { + this.getRootPane().setDefaultButton(okButton); + + this.stackTraceScrollPane.getViewport().add(stackTraceTextArea); + this.stackTraceScrollPane.setPreferredSize( + new Dimension(this.getWidth(), 100)); + + this.buttonsPanel.add(okButton); + + this.okButton.addActionListener(this); + + this.mainPanel.add(iconLabel, BorderLayout.WEST); + + this.messagePanel.add(infoMessagePanel, BorderLayout.NORTH); + + this.mainPanel.add(messagePanel, BorderLayout.CENTER); + this.mainPanel.add(buttonsPanel, BorderLayout.SOUTH); + + this.getContentPane().add(mainPanel); + } + + /** + * Shows if previously hidden or hides if previously shown the details of + * the error. Called when the "more" link is clicked. + */ + public void showOrHideDetails() + { + String startDivTag = "<div id=\"message\">"; + String endDivTag = "</div>"; + String msgString; + + detailsShown = !detailsShown; + + if(detailsShown) + { + msgString = startDivTag + + " <p align=\"right\"><a href=\"\"><< Hide info</a></p>" + + endDivTag; + this.messagePanel.add(stackTraceScrollPane, BorderLayout.CENTER); + } + else + { + msgString = startDivTag + + " <p align=\"right\"><a href=\"\">More info >></a></p>" + + endDivTag; + this.messagePanel.remove(stackTraceScrollPane); + } + + htmlMsgEditorPane.setText(msgString); + + this.messagePanel.revalidate(); + this.messagePanel.repaint(); + // restore default values for preferred size, + // as we have resized its components let it calculate + // that size + setPreferredSize(null); + this.pack(); + } + + /** + * Shows the dialog. + */ + public void showDialog() + { + this.pack(); + + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + + this.setLocation(screenSize.width/2 - this.getWidth()/2, + screenSize.height/2 - this.getHeight()/2); + + this.setVisible(true); + + this.toFront(); + } + + /** + * Handles the <tt>ActionEvent</tt>. Depending on the user choice sets + * the return code to the appropriate value. + * + * @param e the <tt>ActionEvent</tt> instance that has just been fired. + */ + public void actionPerformed(ActionEvent e) + { + JButton button = (JButton) e.getSource(); + + if(button.equals(okButton)) + this.dispose(); + } + + /** + * Close the ErrorDialog. This function is invoked when user + * presses the Escape key. + * + * @param isEscaped Specifies whether the close was triggered by pressing + * the escape key. + */ + protected void close(boolean isEscaped) + { + this.okButton.doClick(); + } + + /** + * Update the ErrorDialog when the user clicks on the hyperlink. + * + * @param e The event generated by the click on the hyperlink. + */ + public void hyperlinkUpdate(HyperlinkEvent e) + { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) + showOrHideDetails(); + } + + /** + * Reloads icon. + */ + public void loadSkin() + { + String icon + = (type == WARNING) + ? "service.gui.icons.WARNING_ICON" + : "service.gui.icons.ERROR_ICON"; + + iconLabel.setIcon(new ImageIcon(DesktopUtilActivator.getImage(icon))); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/ExtendedTooltip.java b/src/net/java/sip/communicator/plugin/desktoputil/ExtendedTooltip.java new file mode 100644 index 0000000..6960fbb --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/ExtendedTooltip.java @@ -0,0 +1,392 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; +import javax.swing.plaf.*; +import javax.swing.plaf.metal.*; + +import net.java.sip.communicator.util.*; + +/** + * The tooltip shown over a contact in the contact list. + * + * @author Yana Stamcheva + */ +public class ExtendedTooltip + extends JToolTip +{ + private static final Logger logger + = Logger.getLogger(ExtendedTooltip.class); + + /** + * Class id key used in UIDefaults. + */ + private static final String uiClassID = + ExtendedTooltip.class.getName() + "ToolTipUI"; + + /** + * Adds the ui class to UIDefaults. + */ + static + { + UIManager.getDefaults().put(uiClassID, + ImageToolTipUI.class.getName()); + } + + private final JLabel imageLabel = new JLabel(); + + private final JLabel titleLabel = new JLabel(); + + private final JPanel linesPanel = new JPanel(); + + private final JTextArea bottomTextArea = new JTextArea(); + + private int textWidth = 0; + + private int textHeight = 0; + + private boolean isListViewEnabled; + + /** + * Created a <tt>MetaContactTooltip</tt>. + * @param isListViewEnabled indicates if the list view is enabled + */ + public ExtendedTooltip(boolean isListViewEnabled) + { + this.isListViewEnabled = isListViewEnabled; + + this.setLayout(new BorderLayout()); + + JPanel mainPanel = new JPanel(new BorderLayout(5, 5)); + JPanel centerPanel = new JPanel(new BorderLayout()); + + mainPanel.setOpaque(false); + centerPanel.setOpaque(false); + linesPanel.setOpaque(false); + bottomTextArea.setOpaque(false); + + titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD)); + + if (isListViewEnabled) + { + linesPanel.setLayout( + new BoxLayout(linesPanel, BoxLayout.Y_AXIS)); + + mainPanel.add(imageLabel, BorderLayout.WEST); + mainPanel.add(centerPanel, BorderLayout.CENTER); + + centerPanel.add(titleLabel, BorderLayout.NORTH); + centerPanel.add(linesPanel, BorderLayout.CENTER); + } + else + { + titleLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT); + mainPanel.add(imageLabel, BorderLayout.CENTER); + mainPanel.add(titleLabel, BorderLayout.NORTH); + } + + bottomTextArea.setEditable(false); + bottomTextArea.setLineWrap(true); + bottomTextArea.setWrapStyleWord(true); + bottomTextArea.setFont(bottomTextArea.getFont().deriveFont(10f)); + mainPanel.add(bottomTextArea, BorderLayout.SOUTH); + + final Window parentWindow + = KeyboardFocusManager.getCurrentKeyboardFocusManager() + .getActiveWindow(); + + // Hide the tooltip when the parent window hides. + /* + * FIXME The parentWindow will surely outlive this ExtendedTooltip so + * adding a WindowFocusListener without removing the same + * WindowFocusListener later on is guaranteed to cause a memory leak. + */ + if (parentWindow != null) + parentWindow.addWindowFocusListener(new WindowFocusListener() + { + public void windowLostFocus(WindowEvent e) + { + Window popupWindow + = SwingUtilities.getWindowAncestor( + ExtendedTooltip.this); + + if ((popupWindow != null) + && popupWindow.isVisible() + // The popup window should normally be a JWindow, so we + // check here explicitly if for some reason we didn't + // get something else. + && (popupWindow instanceof JWindow)) + { + if (logger.isInfoEnabled()) + logger.info("Tooltip window ancestor to hide: " + + popupWindow); + + popupWindow.setVisible(false); + } + } + + public void windowGainedFocus(WindowEvent e) {} + }); + + this.add(mainPanel); + } + + /** + * Sets the given image to this tooltip. + * + * @param imageIcon The image icon to set. + */ + public void setImage(ImageIcon imageIcon) + { + imageLabel.setIcon(imageIcon); + } + + /** + * Sets the title of the tooltip. The text would be shown in bold on the top + * of the tooltip panel. + * + * @param titleText The title of the tooltip. + */ + public void setTitle(String titleText) + { + titleLabel.setText(titleText); + + Dimension labelSize + = ComponentUtils.getStringSize(titleLabel,titleText); + recalculateTooltipSize(labelSize.width, labelSize.height); + } + + /** + * Adds an icon-string list, which would appear on the right of the image + * panel. + * + * @param icon the icon to show + * @param text the name to show + */ + public void addLine(Icon icon, + String text) + { + JLabel lineLabel = new JLabel( text, + icon, + JLabel.LEFT); + + linesPanel.add(lineLabel); + + Dimension labelSize = calculateLabelSize(lineLabel); + + recalculateTooltipSize(labelSize.width, labelSize.height); + } + + /** + * Adds the given array of labels as one line in this tool tip. + * + * @param labels the labels to add + */ + public void addLine(JLabel[] labels) + { + Dimension lineSize = null; + JPanel labelPanel = null; + + if (labels.length > 0) + { + labelPanel = new TransparentPanel( + new FlowLayout(FlowLayout.LEFT, 2, 0)); + linesPanel.add(labelPanel); + } + else + return; + + if (labelPanel != null) + for (JLabel label : labels) + { + labelPanel.add(label); + if (lineSize == null) + lineSize = calculateLabelSize(label); + else + lineSize = new Dimension( + lineSize.width + calculateLabelSize(label).width, + lineSize.height); + } + + recalculateTooltipSize(lineSize.width, lineSize.height); + } + + /** + * Clear all lines. + */ + public void removeAllLines() + { + linesPanel.removeAll(); + } + + /** + * Sets the text that would appear on the bottom of the tooltip. + * @param text the text to set + */ + public void setBottomText(String text) + { + this.bottomTextArea.setText(text); + } + + /** + * Calculates label size. + * + * @param label the label, which size we should calculate + * @return the Dimension indicating the label size + */ + private Dimension calculateLabelSize(JLabel label) + { + Icon icon = label.getIcon(); + String text = label.getText(); + + int iconWidth = 0; + int iconHeight = 0; + if (icon != null) + { + iconWidth = icon.getIconWidth(); + iconHeight = icon.getIconHeight(); + } + + int labelWidth + = ComponentUtils.getStringWidth(label, text) + + iconWidth + + label.getIconTextGap(); + + int textHeight = ComponentUtils.getStringSize(label, text).height; + + int labelHeight = (iconHeight > textHeight) ? iconHeight : textHeight; + + return new Dimension(labelWidth, labelHeight); + } + + /** + * Re-calculates the tooltip size. + * + * @param newTextWidth the width of the newly added text that should be + * added to the global width + * @param newTextHeight the height of the newly added text that should be + * added to the global height + */ + private void recalculateTooltipSize(int newTextWidth, int newTextHeight) + { + if (textWidth < newTextWidth) + textWidth = newTextWidth; + + textHeight += newTextHeight; + } + + /** + * Customized UI for this MetaContactTooltip. + */ + public static class ImageToolTipUI extends MetalToolTipUI + { + static ImageToolTipUI sharedInstance = new ImageToolTipUI(); + + /** + * Creates the UI. + * @param c + * @return + */ + public static ComponentUI createUI(JComponent c) + { + return sharedInstance; + } + + /** + * Overwrite the UI paint method to do nothing in order fix double + * painting of the tooltip text. + * @param g the <tt>Graphics</tt> object + * @param c the component used to render the tooltip + */ + @Override + public void paint(Graphics g, JComponent c) + {} + + /** + * Override ComponentUI update method to set visibility of bottomText. + * @param g <tt>Graphics</tt> object + * @param c the component used to render the tooltip + */ + @Override + public void update(Graphics g, JComponent c) + { + JTextArea bottomTextArea = + ((ExtendedTooltip)c).bottomTextArea; + + String bottomText = bottomTextArea.getText(); + if(bottomText == null || bottomText.length() <= 0) + bottomTextArea.setVisible(false); + else + bottomTextArea.setVisible(true); + super.update(g, c); + } + + /** + * Returns the size of the given component. + * @param c the component used to render the tooltip + * @return the size of the given component. + */ + @Override + public Dimension getPreferredSize(JComponent c) + { + ExtendedTooltip tooltip = (ExtendedTooltip)c; + + Icon icon = tooltip.imageLabel.getIcon(); + int width = 0; + if (icon != null) + width += icon.getIconWidth(); + + if (tooltip.isListViewEnabled) + width += tooltip.textWidth + 15; + else + width = tooltip.textWidth > width ? tooltip.textWidth : width; + + int imageHeight = 0; + if (icon != null) + imageHeight = icon.getIconHeight(); + + int height = 0; + if (tooltip.isListViewEnabled) + { + height = imageHeight > tooltip.textHeight + ? imageHeight : tooltip.textHeight; + } + else + height = imageHeight + tooltip.textHeight; + + String bottomText = tooltip.bottomTextArea.getText(); + if(bottomText != null && bottomText.length() > 0) + { + // Seems a little messy, but sets the proper size. + tooltip.bottomTextArea.setColumns(5); + tooltip.bottomTextArea.setSize(0,0); + tooltip.bottomTextArea.setSize( + tooltip.bottomTextArea.getPreferredSize()); + + height += tooltip.bottomTextArea.getPreferredSize().height; + } + + return new Dimension(width, height); + } + } + + /** + * Returns the name of the L&F class that renders this component. + * + * @return the string "TreeUI" + * @see JComponent#getUIClassID + * @see UIDefaults#getUI + */ + public String getUIClassID() + { + return uiClassID; + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/ExtendedTransferHandler.java b/src/net/java/sip/communicator/plugin/desktoputil/ExtendedTransferHandler.java new file mode 100644 index 0000000..9a23017 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/ExtendedTransferHandler.java @@ -0,0 +1,468 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.datatransfer.*; +import java.awt.dnd.*; +import java.awt.event.*; +import java.awt.geom.*; +import java.awt.image.*; +import java.io.*; + +import javax.swing.*; +import javax.swing.text.*; + +/** + * A TransferHandler that we use to handle copying, pasting and DnD operations. + * The string handler is heavily inspired by Sun's + * <tt>DefaultTransferHandler</tt> with the main difference being that + * we only accept pasting of plain text. We do this in order to avoid HTML + * support problems that appear when pasting formatted text into our editable + * area. + * + * @author Emil Ivov + * @author Yana Stamcheva + */ +public class ExtendedTransferHandler + extends TransferHandler +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * Returns the type of transfer actions supported by the source; + * any bitwise-OR combination of <tt>COPY</tt>, <tt>MOVE</tt> + * and <tt>LINK</tt>. + * <p> + * Some models are not mutable, so a transfer operation of <tt>MOVE</tt> + * should not be advertised in that case. Returning <tt>NONE</tt> + * disables transfers from the component. + * + * @param c the component holding the data to be transferred; + * provided to enable sharing of <code>TransferHandler</code>s + * @return {@code COPY} if the transfer property can be found, + * otherwise returns <code>NONE</code> + */ + public int getSourceActions(JComponent c) + { + return TransferHandler.COPY; + } + + /** + * Indicates whether a component will accept an import of the given + * set of data flavors prior to actually attempting to import it. We return + * <tt>true</tt> to indicate that the transfer with at least one of the + * given flavors would work and <tt>false</tt> to reject the transfer. + * <p> + * @param comp component + * @param flavor the data formats available + * @return true if the data can be inserted into the component, false + * otherwise + * @throws NullPointerException if <code>support</code> is {@code null} + */ + public boolean canImport(JComponent comp, DataFlavor flavor[]) + { + for (int i = 0, n = flavor.length; i < n; i++) + { + if (flavor[i].equals(DataFlavor.javaFileListFlavor)) + { + return true; + } + else if (flavor[i].equals(DataFlavor.stringFlavor)) + { + if (comp instanceof JTextComponent) + { + JTextComponent c = (JTextComponent) comp; + + if (c.isEditable() && c.isEnabled()) + { + return true; + } + } + + return false; + } + } + return false; + } + + /** + * Creates a transferable for text pane components in order to enable drag + * and drop of text. + * @param component the component for which to create a + * <tt>Transferable</tt> + * @return the created <tt>Transferable</tt> + */ + protected Transferable createTransferable(JComponent component) + { + if ((component instanceof JTextPane + || component instanceof JTextField)) + { + return new SelectedTextTransferable((JTextComponent) component); + } + + return super.createTransferable(component); + } + + /** + * Handles transport (cut and copy) from the chat panel to + * <tt>clipboard</tt>. This method will only transfer plain text and would + * explicitly ignore any formatting. + * <p> + * @param comp the component holding the data to be transferred; + * provided to enable sharing of <code>TransferHandler</code>s + * @param clipboard the clipboard to transfer the data into + * @param action the transfer action requested; this should + * be a value of either <code>COPY</code> or <code>MOVE</code>; + * the operation performed is the intersection of the transfer + * capabilities given by getSourceActions and the requested action; + * the intersection may result in an action of <code>NONE</code> + * if the requested action isn't supported + * @throws IllegalStateException if the clipboard is currently unavailable + * @see Clipboard#setContents(Transferable, ClipboardOwner) + */ + public void exportToClipboard(JComponent comp, + Clipboard clipboard, + int action) + throws IllegalStateException + { + if (comp instanceof JTextComponent) + { + JTextComponent textComponent = (JTextComponent)comp; + int startIndex = textComponent.getSelectionStart(); + int endIndex = textComponent.getSelectionEnd(); + if (startIndex != endIndex) + { + try + { + Document doc = textComponent.getDocument(); + String srcData = doc.getText(startIndex, + endIndex - startIndex); + StringSelection contents = new StringSelection(srcData); + + // this may throw an IllegalStateException, + // but it will be caught and handled in the + // action that invoked this method + clipboard.setContents(contents, null); + + if (action == TransferHandler.MOVE) + { + doc.remove(startIndex, endIndex - startIndex); + } + } + catch (BadLocationException ble) + { + //we simply ignore + } + } + } + } + + /** + * Transferable for text pane components that enables drag and drop of text. + */ + public class SelectedTextTransferable implements Transferable + { + private JTextComponent textComponent; + + /** + * Creates an instance of <tt>SelectedTextTransferable</tt>. + * @param component the text component + */ + public SelectedTextTransferable(JTextComponent component) + { + this.textComponent = component; + } + + /** + * Returns supported flavors. + * @return an array of supported flavors + */ + public DataFlavor[] getTransferDataFlavors() + { + return new DataFlavor[]{DataFlavor.stringFlavor}; + } + + /** + * Returns <tt>true</tt> if the given <tt>flavor</tt> is supported, + * otherwise returns <tt>false</tt>. + * @param flavor the data flavor to verify + * @return <tt>true</tt> if the given <tt>flavor</tt> is supported, + * otherwise returns <tt>false</tt> + */ + public boolean isDataFlavorSupported(DataFlavor flavor) + { + return DataFlavor.stringFlavor.equals(flavor); + } + + /** + * Returns the selected text. + * @param flavor the flavor + * @return the selected text + * @exception IOException if the data is no longer available in the + * requested flavor. + * @exception UnsupportedFlavorException if the requested data flavor + * is not supported. + */ + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, + IOException + { + if (!DataFlavor.stringFlavor.equals(flavor)) + { + throw new UnsupportedFlavorException(flavor); + } + + return textComponent.getSelectedText(); + } + } + + /** + * Overrides <tt>TransferHandler.getVisualRepresentation(Transferable t)</tt> + * in order to return a custom drag icon. + * <p> + * The default parent implementation of this method returns null. + * + * @param t the data to be transferred; this value is expected to have been + * created by the <code>createTransferable</code> method + * @return the icon to show when dragging + */ + public Icon getVisualRepresentation(Transferable t) + { + Icon icon = null; + if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) + { + String text = null; + try + { + text = (String) t.getTransferData(DataFlavor.stringFlavor); + } + catch (UnsupportedFlavorException e) {} + catch (IOException e) {} + + if (text != null) + { + Rectangle2D bounds = ComponentUtils.getDefaultStringSize(text); + BufferedImage image = new BufferedImage( + (int) Math.ceil(bounds.getWidth()), + (int) Math.ceil(bounds.getHeight()), + BufferedImage.TYPE_INT_ARGB); + + Graphics g = image.getGraphics(); + AntialiasingManager.activateAntialiasing(g); + g.setColor(Color.BLACK); + // Don't know why if we draw the string on y = 0 it doesn't + // appear in the visible area. + g.drawString(text, 0, 10); + + icon = new ImageIcon(image); + } + } + + return icon; + } + + // Patch for bug 4816922 "No way to set drag icon: + // TransferHandler.getVisualRepresentation() is not used". + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4816922 + // The following workaround comes from bug comments section! + private static SwingDragGestureRecognizer recognizer = null; + + private static class SwingDragGestureRecognizer + extends DragGestureRecognizer + { + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + SwingDragGestureRecognizer(DragGestureListener dgl) + { + super(DragSource.getDefaultDragSource(), null, NONE, dgl); + } + + void gestured(JComponent c, MouseEvent e, int srcActions, int action) + { + setComponent(c); + setSourceActions(srcActions); + appendEvent(e); + fireDragGestureRecognized(action, e.getPoint()); + } + + /** + * Registers this DragGestureRecognizer's Listeners with the Component. + */ + protected void registerListeners() {} + + /** + * Unregister this DragGestureRecognizer's Listeners with the Component. + * <p/> + * Subclasses must override this method. + */ + protected void unregisterListeners() {} + } + + /** + * Overrides <tt>TransferHandler.exportAsDrag</tt> method in order to call + * our own <tt>SwingDragGestureRecognizer</tt>, which takes care of the + * visual representation icon. + * + * @param comp the component holding the data to be transferred; this + * argument is provided to enable sharing of <code>TransferHandler</code>s + * by multiple components + * @param e the event that triggered the transfer + * @param action the transfer action initially requested; this should + * be a value of either <code>COPY</code> or <code>MOVE</code>; + * the value may be changed during the course of the drag operation + */ + public void exportAsDrag(JComponent comp, InputEvent e, int action) + { + int srcActions = getSourceActions(comp); + int dragAction = srcActions & action; + + // only mouse events supported for drag operations + if (! (e instanceof MouseEvent)) + action = NONE; + + if (action != NONE && !GraphicsEnvironment.isHeadless()) + { + if (recognizer == null) + { + recognizer = new SwingDragGestureRecognizer(new DragHandler()); + } + recognizer.gestured(comp, (MouseEvent) e, srcActions, dragAction); + } + else + { + exportDone(comp, null, NONE); + } + } + + /** + * This is the default drag handler for drag and drop operations that + * use the <code>TransferHandler</code>. + */ + private static class DragHandler + implements DragGestureListener, + DragSourceListener + { + private boolean scrolls; + + // --- DragGestureListener methods ----------------------------------- + + /** + * A Drag gesture has been recognized. + * @param dge the <tt>DragGestureEvent</tt> that notified us + */ + public void dragGestureRecognized(DragGestureEvent dge) + { + JComponent c = (JComponent) dge.getComponent(); + ExtendedTransferHandler th + = (ExtendedTransferHandler) c.getTransferHandler(); + + Transferable t = th.createTransferable(c); + if (t != null) + { + scrolls = c.getAutoscrolls(); + c.setAutoscrolls(false); + try + { + Image img = null; + Icon icn = th.getVisualRepresentation(t); + + if (icn != null) + { + if (icn instanceof ImageIcon) + { + img = ((ImageIcon) icn).getImage(); + } + else + { + img = new BufferedImage(icn.getIconWidth(), + icn.getIconHeight(), + BufferedImage.TYPE_4BYTE_ABGR); + Graphics g = img.getGraphics(); + icn.paintIcon(c, g, 0, 0); + } + } + if (img == null) + { + dge.startDrag(null, t, this); + } + else + { + dge.startDrag(null, img, + new Point(0, -1 * img.getHeight(null)), t, this); + } + + return; + } + catch (RuntimeException re) + { + c.setAutoscrolls(scrolls); + } + } + + th.exportDone(c, t, NONE); + } + + // --- DragSourceListener methods ----------------------------------- + + /** + * As the hotspot enters a platform dependent drop site. + * @param e the <tt>DragSourceDragEvent</tt> containing the details of + * the drag + */ + public void dragEnter(DragSourceDragEvent e) + {} + + /** + * As the hotspot moves over a platform dependent drop site. + * @param e the <tt>DragSourceDragEvent</tt> containing the details of + * the drag + */ + public void dragOver(DragSourceDragEvent e) + { + } + + /** + * As the hotspot exits a platform dependent drop site. + * @param e the <tt>DragSourceDragEvent</tt> containing the details of + * the drag + */ + public void dragExit(DragSourceEvent e) + {} + + /** + * As the operation completes. + * @param e the <tt>DragSourceDragEvent</tt> containing the details of + * the drag + */ + public void dragDropEnd(DragSourceDropEvent e) + { + DragSourceContext dsc = e.getDragSourceContext(); + JComponent c = (JComponent) dsc.getComponent(); + + if (e.getDropSuccess()) + { + ((ExtendedTransferHandler) c.getTransferHandler()) + .exportDone(c, dsc.getTransferable(), e.getDropAction()); + } + else + { + ((ExtendedTransferHandler) c.getTransferHandler()) + .exportDone(c, dsc.getTransferable(), NONE); + } + c.setAutoscrolls(scrolls); + } + + public void dropActionChanged(DragSourceDragEvent dsde) {} + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/FadeInBalloonPanel.java b/src/net/java/sip/communicator/plugin/desktoputil/FadeInBalloonPanel.java new file mode 100644 index 0000000..ec3bbb7 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/FadeInBalloonPanel.java @@ -0,0 +1,190 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.geom.*; + +import javax.swing.*; + +import org.jvnet.lafwidget.animation.*; + +/** + * The <tt>FadeInBaloonPanel</tt> is a semi-transparent "balloon" panel, which + * could be shown in a glass pane for example. You can define a begin point, + * where the balloon triangle would show. + * + * @author Yana Stamcheva + */ +public class FadeInBalloonPanel + extends TransparentPanel +{ + /** + * The begin point, where the balloon triangle will be shown. + */ + private Point beginPoint; + + /** + * The begin point shift, which defines the rectangle point shift. + */ + private final static int beginPointShift = 6; + + /** + * Sets the begin point. + * + * @param beginPoint the begin point + */ + public void setBeginPoint(Point beginPoint) + { + this.beginPoint = beginPoint; + } + + /** + * Overrides the <code>paintComponent</code> method of <tt>JButton</tt> to + * paint the button background and icon, and all additional effects of this + * configurable button. + * + * @param g The Graphics object. + */ + protected void paintComponent(Graphics g) + { + g = g.create(); + try + { + internalPaintComponent((Graphics2D) g); + } + finally + { + g.dispose(); + } + } + + /** + * Paints this button. + * @param g the <tt>Graphics</tt> object used for painting + */ + private void internalPaintComponent(Graphics2D g) + { + AntialiasingManager.activateAntialiasing(g); + /* + * As JComponent#paintComponent says, if you do not invoke super's + * implementation you must honor the opaque property, that is if this + * component is opaque, you must completely fill in the background in a + * non-opaque color. If you do not honor the opaque property you will + * likely see visual artifacts. + */ + if (isOpaque()) + { + g.setColor(getBackground()); + g.fillRect(0, 0, getWidth(), getHeight()); + } + + // Paint a roll over fade out. + FadeTracker fadeTracker = FadeTracker.getInstance(); + + float visibility = isVisible() ? 0.8f : 0.0f; + if (fadeTracker.isTracked(this, FadeKind.ROLLOVER)) + { + visibility = fadeTracker.getFade(this, FadeKind.ROLLOVER); + } + + g.setColor(new Color(0f, 0f, 0f, visibility)); + + int y = 0; + + // draw triangle (polygon) + if (beginPoint != null) + { + y = beginPointShift; + + int x1Points[] = { beginPoint.x, + beginPoint.x + beginPointShift, + beginPoint.x - beginPointShift}; + + int y1Points[] = { beginPoint.y, + beginPoint.y + beginPointShift, + beginPoint.y + beginPointShift}; + + GeneralPath polygon = + new GeneralPath(GeneralPath.WIND_EVEN_ODD, + x1Points.length); + + polygon.moveTo(x1Points[0], y1Points[0]); + + for (int index = 1; index < x1Points.length; index++) { + polygon.lineTo(x1Points[index], y1Points[index]); + }; + + polygon.closePath(); + g.fill(polygon); + } + + if (visibility != 0.0f) + { + g.fillRoundRect( + 0, y, this.getWidth(), this.getHeight(), 10, 10); + } + } + + /** + * The <tt>ButtonRepaintCallback</tt> is charged to repaint this button + * when the fade animation is performed. + */ + private class PanelRepaintCallback implements FadeTrackerCallback + { + public void fadeEnded(FadeKind arg0) + { + repaintLater(); + } + + public void fadePerformed(FadeKind arg0, float arg1) + { + repaintLater(); + } + + private void repaintLater() + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + FadeInBalloonPanel.this.repaint(); + } + }); + } + + public void fadeReversed(FadeKind arg0, boolean arg1, float arg2) + { + } + } + + /** + * Shows/hides this panel. + * + * @param isVisible <tt>true</tt> to show this panel, <tt>false</tt> to + * hide it + */ + public void setVisible(boolean isVisible) + { + FadeTracker fadeTracker = FadeTracker.getInstance(); + + if (isVisible) + { + fadeTracker.trackFadeIn(FadeKind.ROLLOVER, + FadeInBalloonPanel.this, + true, + new PanelRepaintCallback()); + } + else + { + fadeTracker.trackFadeOut(FadeKind.ROLLOVER, + FadeInBalloonPanel.this, + true, + new PanelRepaintCallback()); + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/FileDragLabel.java b/src/net/java/sip/communicator/plugin/desktoputil/FileDragLabel.java new file mode 100644 index 0000000..4865e11 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/FileDragLabel.java @@ -0,0 +1,206 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.datatransfer.*; +import java.awt.dnd.*; +import java.io.*; +import java.util.*; + +import javax.swing.*; + +import net.java.sip.communicator.util.*; + +/** + * The <tt>FileDragLabel</tt> extends <tt>JLabel</tt> and associates to it a + * file. The label is made draggable and it is possible to drag it directly to + * the file browser of the operating system. + * + * @author Yana Stamcheva + */ +public class FileDragLabel + extends JLabel + implements DropTargetListener, + DragSourceListener, + DragGestureListener +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + private static final Logger logger = Logger.getLogger(FileDragLabel.class); + + private final DragSource dragSource = DragSource.getDefaultDragSource(); + + private File file; + + /** + * Creates a <tt>FileDragLabel</tt>. + */ + public FileDragLabel() + { + dragSource.createDefaultDragGestureRecognizer( + this, DnDConstants.ACTION_COPY, this); + } + + /** + * Sets the file associated with this file drag label. + * + * @param file the file associated with this file drag label + */ + public void setFile(File file) + { + this.file = file; + } + + /** + * Called while a drag operation is ongoing, when the mouse pointer enters + * the operable part of the drop site for the <code>DropTarget</code> + * registered with this listener. + * + * @param dropTargetDragEvent the <code>DropTargetDragEvent</code> + */ + public void dragEnter(DropTargetDragEvent dropTargetDragEvent) + { + dropTargetDragEvent.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE); + } + + /**Called when the drag operation has terminated with a drop on + * the operable part of the drop site for the <code>DropTarget</code> + * registered with this listener. + */ + public synchronized void drop(DropTargetDropEvent event) + { + try + { + Transferable transferable = event.getTransferable(); + + if (transferable.isDataFlavorSupported( + DataFlavor.javaFileListFlavor)) + { + event.acceptDrop(DnDConstants.ACTION_COPY); + event.getDropTargetContext().dropComplete(true); + } + else + { + event.rejectDrop(); + } + } + catch (Exception ex) + { + if (logger.isDebugEnabled()) + logger.debug("Unable to drop label.", ex); + event.rejectDrop(); + } + } + + /** + * A <code>DragGestureRecognizer</code> has detected + * a platform-dependent drag initiating gesture and + * is notifying this listener + * in order for it to initiate the action for the user. + * <P> + * @param dragGestureEvent the <code>DragGestureEvent</code> describing + * the gesture that has just occurred + */ + public void dragGestureRecognized(DragGestureEvent dragGestureEvent) + { + if (file == null) + { + // Nothing selected, nothing to drag + getToolkit().beep(); + } + else + { + FileTransferable transferable = new FileTransferable(file); + dragGestureEvent.startDrag( DragSource.DefaultCopyDrop, + transferable, + this); + } + } + + public void dragDropEnd(DragSourceDropEvent DragSourceDropEvent) {} + + public void dragEnter(DragSourceDragEvent DragSourceDragEvent) {} + + public void dragExit(DragSourceEvent DragSourceEvent) {} + + public void dragOver(DragSourceDragEvent DragSourceDragEvent) {} + + public void dropActionChanged(DragSourceDragEvent DragSourceDragEvent) {} + + public void dragExit(DropTargetEvent dropTargetEvent) {} + + public void dragOver(DropTargetDragEvent dropTargetDragEvent) {} + + public void dropActionChanged(DropTargetDragEvent dropTargetDragEvent) {} + + /** + * File transferable. + */ + @SuppressWarnings("deprecation") //can't find an alternative. + private class FileTransferable + extends Vector<File> + implements Transferable + { + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + final static int FILE = 0; + final static int STRING = 1; + final static int PLAIN = 2; + + // Don't have other possibility for now instead of using the deprecated + // plainTextFlavor method. + DataFlavor flavors[] = {DataFlavor.javaFileListFlavor, + DataFlavor.stringFlavor, + DataFlavor.plainTextFlavor}; + + public FileTransferable(File file) + { + addElement(file); + } + + public synchronized DataFlavor[] getTransferDataFlavors() + { + return flavors; + } + + public boolean isDataFlavorSupported(DataFlavor flavor) + { + boolean b = false; + b = b | flavor.equals(flavors[FILE]); + b |= flavor.equals(flavors[STRING]); + b |= flavor.equals(flavors[PLAIN]); + return (b); + } + + public synchronized Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException + { + if (flavor.equals(flavors[FILE])) + { + return this; + } + else if (flavor.equals(flavors[PLAIN])) + { + return new StringReader(file.getAbsolutePath()); + } + else if (flavor.equals(flavors[STRING])) + { + return (file.getAbsolutePath()); + } + else + { + throw new UnsupportedFlavorException(flavor); + } + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/FramedImage.java b/src/net/java/sip/communicator/plugin/desktoputil/FramedImage.java new file mode 100644 index 0000000..ecbea59 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/FramedImage.java @@ -0,0 +1,189 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; + +import javax.swing.*; + +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.skin.*; + +/** + * A custom component, used to show images in a frame. + * + * @author Yana Stamcheva + * @author Adam Netocny + */ +public class FramedImage + extends JComponent + implements Skinnable +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * The frame image. + */ + private Image frameImage; + + /** + * The icon image. + */ + private ImageIcon icon; + + /** + * The default width of the image. + */ + protected final int width; + + /** + * The default height of the image. + */ + protected final int height; + + /** + * Creates a FramedImage by specifying the width and the height of the + * label. These are used to paint the image frame in the correct bounds. + * + * @param imageIcon the icon to show within the frame + * @param width the width of the frame + * @param height the height of the frame + */ + public FramedImage(ImageIcon imageIcon, int width, int height) + { + this.width = width; + this.height = height; + + this.setPreferredSize(new Dimension(width, height)); + + loadSkin(); + + if (imageIcon != null) + this.icon = getScaledImage(imageIcon.getImage()); + } + + /** + * Creates a FramedImage by specifying the width and the height of the frame. + * + * @param width the width of the frame + * @param height the height of the frame + */ + public FramedImage(int width, int height) + { + this(null, width, height); + } + + /** + * Sets the image to display in the frame. + * + * @param image the image to display in the frame + */ + public void setImageIcon(byte[] image) + { + icon = getScaledImage(image); + + if (this.isVisible()) + { + this.revalidate(); + this.repaint(); + } + } + + /** + * Sets the image to display in the frame. + * + * @param image the image to display in the frame + */ + public void setImageIcon(Image image) + { + icon = getScaledImage(image); + + if (this.isVisible()) + { + this.revalidate(); + this.repaint(); + } + } + + /** + * Returns the image that is shown. + * @return the image that is shown + */ + public Image getImage() + { + return icon.getImage(); + } + + /** + * Paints the contained image in a frame. + * + * Overrides {@link JComponent#paintComponent(Graphics)}. + */ + public void paintComponent(Graphics g) + { + if (icon != null) + { + int imageWidth = icon.getIconWidth(); + int imageHeight = icon.getIconHeight(); + if ((imageWidth != -1) && (imageHeight != -1)) + g.drawImage( + icon.getImage(), + width / 2 - imageWidth / 2, + height / 2 - imageHeight / 2, + null); + } + + int frameWidth = frameImage.getWidth(this); + int frameHeight = frameImage.getHeight(this); + if ((frameWidth != -1) && (frameHeight != -1)) + g.drawImage( + frameImage, + width / 2 - frameWidth / 2, + height / 2 - frameHeight / 2, + null); + } + + /** + * Loads the framed image. + */ + public void loadSkin() + { + this.frameImage + = ImageUtils + .scaleImageWithinBounds( + DesktopUtilActivator + .getResources() + .getImage("service.gui.USER_PHOTO_FRAME").getImage(), + width, + height); + } + + /** + * Returns the scaled image version of the given image. + * + * @param image the image to transform + * @return the scaled image version of the given image + */ + private ImageIcon getScaledImage(Image image) + { + return ImageUtils.getScaledRoundedIcon(image, width - 2, height - 2); + } + + /** + * Returns the scaled image version of the given image. + * + * @param image the image to transform + * @return the scaled image version of the given image + */ + private ImageIcon getScaledImage(byte[] image) + { + return ImageUtils.getScaledRoundedIcon(image, width - 2, height - 2); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/FramedImageWithMenu.java b/src/net/java/sip/communicator/plugin/desktoputil/FramedImageWithMenu.java new file mode 100644 index 0000000..b46211b --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/FramedImageWithMenu.java @@ -0,0 +1,279 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; +import javax.swing.event.*; + +import org.jvnet.lafwidget.animation.*; + +/** + * A custom component, used to show images in a frame. A rollover for the + * content image and optional menu in dialog. + * + * @author Damien Roth + */ +public class FramedImageWithMenu + extends FramedImage + implements MouseListener, PopupMenuListener +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * The dialog containing the menu with actions. + */ + private JPopupMenu popupMenu; + + /** + * The parent frame. + */ + private JFrame mainFrame; + + /** + * Should we currently draw overlay. + */ + private boolean drawOverlay = false; + + /** + * Are we showing custom image or the default one. + */ + private boolean isDefaultImage = true; + + /** + * The current image. + */ + private Image currentImage; + + /** + * Creates the component. + * @param mainFrame the parent frame. + * @param imageIcon the image icon to show as default one. + * @param width width of component. + * @param height height of component. + */ + public FramedImageWithMenu( + JFrame mainFrame, + ImageIcon imageIcon, + int width, + int height) + { + super(imageIcon, width, height); + + this.mainFrame = mainFrame; + this.addMouseListener(this); + } + + /** + * Sets the dialog used for menu for this Image. + * @param popupMenu the dialog to show as menu. Can be null if no menu + * will be available. + */ + public void setPopupMenu(JPopupMenu popupMenu) + { + this.popupMenu = popupMenu; + if(popupMenu != null) + this.popupMenu.addPopupMenuListener(this); + } + + /** + * Sets the image to display in the frame. + * + * @param imageIcon the image to display in the frame + */ + public void setImageIcon(ImageIcon imageIcon) + { + // Intercept the action to validate the user icon and not the default + super.setImageIcon(imageIcon.getImage()); + this.isDefaultImage = false; + + this.currentImage = imageIcon.getImage(); + } + + /** + * Returns the current image with no rounded corners. Only return the user + * image and not the default image. + * + * @return the current image - null if it's the default image + */ + public Image getAvatar() + { + return (!this.isDefaultImage) ? this.currentImage : this.getImage(); + } + + @Override + public void paintComponent(Graphics g) + { + super.paintComponent(g); + + if (drawOverlay) + { + g = g.create(); + AntialiasingManager.activateAntialiasing(g); + + try + { + // Paint a roll over fade out. + FadeTracker fadeTracker = FadeTracker.getInstance(); + + float visibility = 0.0f; + if (fadeTracker.isTracked(this, FadeKind.ROLLOVER)) + { + visibility = fadeTracker.getFade(this, FadeKind.ROLLOVER); + visibility /= 4; + } + else + visibility = 0.5f; + + // Draw black overlay + g.setColor(new Color(0.0f, 0.0f, 0.0f, visibility)); + g.fillRoundRect(1, 1, width - 2, height - 2, 10, 10); + + // Draw arrow + g.setColor(Color.WHITE); + + int[] arrowX = new int[] { + width - 17, + width - 7, + width - 12 + }; + int[] arrowY = new int[] { + height - 12, + height - 12, + height - 7 + }; + g.fillPolygon(arrowX, arrowY, arrowX.length); + } + finally + { + g.dispose(); + } + } + } + + /** + * Show the avatar dialog as a glasspane of the mainframe + * + * @param show show dialogs if sets to TRUE - hide otherwise + */ + private void showDialog(MouseEvent e, boolean show) + { + if (this.popupMenu == null) + { + return; + } + + if (show) + { + Point imageLoc = this.getLocationOnScreen(); + Point rootPaneLoc = mainFrame.getRootPane().getLocationOnScreen(); + + this.popupMenu.setSize(mainFrame.getRootPane().getWidth(), + this.popupMenu.getHeight()); + + this.popupMenu.show(this, (rootPaneLoc.x - imageLoc.x), + this.getHeight()); + } + else + { + this.drawOverlay = false; + this.repaint(); + } + } + + public void mouseEntered(MouseEvent e) + { + if (this.drawOverlay) + return; + + this.drawOverlay = true; + + FadeTracker fadeTracker = FadeTracker.getInstance(); + + fadeTracker.trackFadeIn(FadeKind.ROLLOVER, + FramedImageWithMenu.this, + true, + new AvatarRepaintCallback()); + } + + public void mouseExited(MouseEvent e) + { + // Remove overlay only if the dialog isn't visible + if (!popupMenu.isVisible()) + { + this.drawOverlay = false; + this.repaint(); + } + } + + public void mouseReleased(MouseEvent e) + { + showDialog(e, !popupMenu.isVisible()); + } + + /** + * This method is called before the popup menu becomes visible + */ + public void popupMenuWillBecomeVisible(PopupMenuEvent e) {} + + /** + * This method is called before the popup menu becomes invisible + * Note that a JPopupMenu can become invisible any time + */ + public void popupMenuWillBecomeInvisible(PopupMenuEvent e) + { + this.drawOverlay = false; + this.repaint(); + } + + /** + * This method is called when the popup menu is canceled + */ + public void popupMenuCanceled(PopupMenuEvent e){} + + /** + * The <tt>ButtonRepaintCallback</tt> is charged to repaint this button + * when the fade animation is performed. + */ + private class AvatarRepaintCallback + implements FadeTrackerCallback + { + public void fadeEnded(FadeKind arg0) + { + repaintLater(); + } + + public void fadePerformed(FadeKind arg0, float arg1) + { + repaintLater(); + } + + private void repaintLater() + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + FramedImageWithMenu.this.repaint(); + } + }); + } + + public void fadeReversed(FadeKind arg0, boolean arg1, float arg2) + { + } + } + + public void mouseClicked(MouseEvent e) {} + + public void mousePressed(MouseEvent e) {} +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/GenericFileDialog.java b/src/net/java/sip/communicator/plugin/desktoputil/GenericFileDialog.java new file mode 100644 index 0000000..d818561 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/GenericFileDialog.java @@ -0,0 +1,100 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; + +import javax.swing.*; + +import org.jitsi.util.*; + + +/** + * This class is the entry point for creating a file dialog regarding to the OS. + * + * If the current operating system is Apple Mac OS X, we create an AWT + * FileDialog (user interface is more practical under Mac OS than a + * JFileChooser), else, a Swing JFileChooser. + * + * @author Valentin Martinet + */ +public class GenericFileDialog +{ + /** + * Creates a file dialog (AWT's FileDialog or Swing's JFileChooser) + * regarding to user's operating system. + * + * @param parent the parent Frame/JFrame of this dialog + * @param title dialog's title + * @param fileOperation + * @return a SipCommFileChooser instance + */ + public static SipCommFileChooser create( + Frame parent, + String title, + int fileOperation) + { + int operation; + + if(OSUtils.IS_MAC) + { + switch (fileOperation) + { + case SipCommFileChooser.LOAD_FILE_OPERATION: + operation = FileDialog.LOAD; + break; + case SipCommFileChooser.SAVE_FILE_OPERATION: + operation = FileDialog.SAVE; + break; + default: + throw new IllegalArgumentException("fileOperation"); + } + + if (parent == null) + parent = new Frame(); + + return new SipCommFileDialogImpl(parent, title, operation); + } + else + { + switch (fileOperation) + { + case SipCommFileChooser.LOAD_FILE_OPERATION: + operation = JFileChooser.OPEN_DIALOG; + break; + case SipCommFileChooser.SAVE_FILE_OPERATION: + operation = JFileChooser.SAVE_DIALOG; + break; + default: + throw new IllegalArgumentException("fileOperation"); + } + + return new SipCommFileChooserImpl(title, operation); + } + } + + /** + * Creates a file dialog (AWT FileDialog or Swing JFileChooser) regarding to + * user's operating system. + * + * @param parent the parent Frame/JFrame of this dialog + * @param title dialog's title + * @param fileOperation + * @param path start path of this dialog + * @return SipCommFileChooser an implementation of SipCommFileChooser + */ + public static SipCommFileChooser create( + Frame parent, String title, int fileOperation, String path) + { + SipCommFileChooser scfc + = GenericFileDialog.create(parent, title, fileOperation); + + if(path != null) + scfc.setStartPath(path); + return scfc; + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/ImageCanvas.java b/src/net/java/sip/communicator/plugin/desktoputil/ImageCanvas.java new file mode 100644 index 0000000..86ccd90 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/ImageCanvas.java @@ -0,0 +1,91 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; + +import javax.swing.*; + +/** + * @author Lubomir Marinov + */ +public class ImageCanvas + extends TransparentPanel +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + private ImageIcon icon; + + /** + * Constructor. + * + * @param image the image for the canvas + */ + public ImageCanvas(Image image) + { + setImage(image); + } + + protected void paintComponent(Graphics g) + { + super.paintComponent(g); + + if (icon == null) + return; + + int imageWidth = icon.getIconWidth(); + if (imageWidth < 1) + return; + int imageHeight = icon.getIconHeight(); + if (imageHeight < 1) + return; + + int width = getWidth(); + boolean scale = false; + float scaleFactor = 1; + if (imageWidth > width) + { + scale = true; + scaleFactor = width / (float) imageWidth; + } + int height = getHeight(); + if (imageHeight > height) + { + scale = true; + scaleFactor = Math.min(scaleFactor, height / (float) imageHeight); + } + if (scale) + { + imageWidth = Math.round(imageWidth * scaleFactor); + imageHeight = Math.round(imageHeight * scaleFactor); + } + + g.drawImage(icon.getImage(), (width - imageWidth) / 2, + (height - imageHeight) / 2, imageWidth, imageHeight, null); + } + + /** + * Sets image to be painted. + * @param image Image to be painted. + */ + public void setImage(Image image) + { + icon = (image == null) ? null : new ImageIcon(image); + + if (icon != null) + { + final int preferredWidth = icon.getIconWidth(); + final int preferredHeight = icon.getIconHeight(); + setMinimumSize(new Dimension(preferredWidth / 2, + preferredHeight / 2)); + setPreferredSize(new Dimension(preferredWidth, preferredHeight)); + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/ImageUtils.java b/src/net/java/sip/communicator/plugin/desktoputil/ImageUtils.java new file mode 100644 index 0000000..4ec5b74 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/ImageUtils.java @@ -0,0 +1,449 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.image.*; +import java.io.*; +import java.net.*; + +import javax.imageio.*; +import javax.swing.*; + +import net.java.sip.communicator.util.*; + +/** + * Utility methods for image manipulation. + * + * @author Sebastien Mazy + * @author Yana Stamcheva + * @author Lubomir Marinov + */ +public class ImageUtils +{ + /** + * The <tt>Logger</tt> used by the <tt>ImageUtils</tt> class for logging + * output. + */ + private static final Logger logger = Logger.getLogger(ImageUtils.class); + + /** + * Returns a scaled image fitting within the given bounds while keeping the + * aspect ratio. + * + * @param image the image to scale + * @param width maximum width of the scaled image + * @param height maximum height of the scaled image + * @return the scaled image + */ + public static Image scaleImageWithinBounds( Image image, + int width, + int height) + { + int initialWidth = image.getWidth(null); + int initialHeight = image.getHeight(null); + + Image scaledImage; + int scaleHint = Image.SCALE_SMOOTH; + double originalRatio = + (double) initialWidth / initialHeight; + double areaRatio = (double) width / height; + + if(originalRatio > areaRatio) + scaledImage = image.getScaledInstance(width, -1, scaleHint); + else + scaledImage = image.getScaledInstance(-1, height, scaleHint); + return scaledImage; + } + + /** + * Scales the given <tt>image</tt> to fit in the given <tt>width</tt> and + * <tt>height</tt>. + * @param image the image to scale + * @param width the desired width + * @param height the desired height + * @return the scaled image + */ + public static ImageIcon scaleIconWithinBounds(Image image, int width, + int height) + { + return new ImageIcon(scaleImageWithinBounds(image, width, height)); + } + + /** + * Scales the given <tt>image</tt> to fit in the given <tt>width</tt> and + * <tt>height</tt>. + * + * @param imageBytes the bytes of the image to scale + * @param width the desired width + * @param height the desired height + * @return the scaled image + */ + public static ImageIcon scaleImageWithinBounds( byte[] imageBytes, + int width, + int height) + { + + if (imageBytes == null || !(imageBytes.length > 0)) + return null; + + Image imageIcon = null; + + try + { + Image image = null; + + // sometimes ImageIO fails, will fall back to awt Toolkit + try + { + image = ImageIO.read(new ByteArrayInputStream(imageBytes)); + } catch (Exception e) + { + try + { + image = Toolkit.getDefaultToolkit().createImage(imageBytes); + } catch (Exception e1) + { + // if it fails throw the original exception + throw e; + } + } + if(image != null) + imageIcon = scaleImageWithinBounds(image, width, height); + else + if (logger.isTraceEnabled()) + logger.trace("Unknown image format or error reading image"); + } + catch (Exception e) + { + if (logger.isDebugEnabled()) + logger.debug("Could not create image.", e); + } + + if (imageIcon != null) + return new ImageIcon(imageIcon); + + return null; + } + + /** + * Creates a rounded avatar image. + * + * @param image image of the initial avatar image. + * @param width the desired width + * @param height the desired height + * @return The rounded corner image. + */ + public static Image getScaledRoundedImage( Image image, + int width, + int height) + { + ImageIcon scaledImage = + ImageUtils.scaleIconWithinBounds(image, width, height); + int scaledImageWidth = scaledImage.getIconWidth(); + int scaledImageHeight = scaledImage.getIconHeight(); + + if(scaledImageHeight <= 0 || + scaledImageWidth <= 0) + return null; + + // Just clipping the image would cause jaggies on Windows and Linux. + // The following is a soft clipping solution based on the solution + // proposed by Chris Campbell: + // http://java.sun.com/mailers/techtips/corejava/2006/tt0923.html + BufferedImage destImage + = new BufferedImage(scaledImageWidth, scaledImageHeight, + BufferedImage.TYPE_INT_ARGB); + + Graphics2D g = destImage.createGraphics(); + + try + { + // Render our clip shape into the image. Note that we enable + // antialiasing to achieve the soft clipping effect. + g.setComposite(AlphaComposite.Src); + AntialiasingManager.activateAntialiasing(g); + g.setColor(Color.WHITE); + g.fillRoundRect(0, 0, scaledImageWidth, scaledImageHeight, 5, 5); + + // We use SrcAtop, which effectively uses the + // alpha value as a coverage value for each pixel stored in the + // destination. For the areas outside our clip shape, the + // destination alpha will be zero, so nothing is rendered in those + // areas. For the areas inside our clip shape, the destination alpha + // will be fully opaque, so the full color is rendered. At the edges, + // the original antialiasing is carried over to give us the desired + // soft clipping effect. + g.setComposite(AlphaComposite.SrcAtop); + g.drawImage(scaledImage.getImage(), 0, 0, null); + } + finally + { + g.dispose(); + } + return destImage; + } + + /** + * Returns a scaled instance of the given <tt>image</tt>. + * @param image the image to scale + * @param width the desired width + * @param height the desired height + * @return a byte array containing the scaled image + */ + public static byte[] getScaledInstanceInBytes( + Image image, int width, int height) + { + byte[] scaledBytes = null; + + BufferedImage scaledImage + = (BufferedImage) getScaledRoundedImage(image, width, height); + + if (scaledImage != null) + { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + + try + { + ImageIO.write(scaledImage, "png", outStream); + scaledBytes = outStream.toByteArray(); + } + catch (IOException e) + { + if (logger.isDebugEnabled()) + logger.debug("Could not scale image in bytes.", e); + } + + } + + return scaledBytes; + } + + /** + * Returns a scaled rounded icon from the given <tt>image</tt>, scaled + * within the given <tt>width</tt> and <tt>height</tt>. + * @param image the image to scale + * @param width the maximum width of the scaled icon + * @param height the maximum height of the scaled icon + * @return a scaled rounded icon + */ + public static ImageIcon getScaledRoundedIcon(Image image, int width, + int height) + { + Image scaledImage = getScaledRoundedImage(image, width, height); + + if (scaledImage != null) + return new ImageIcon(scaledImage); + + return null; + } + + /** + * Creates a rounded corner scaled image. + * + * @param imageBytes The bytes of the image to be scaled. + * @param width The maximum width of the scaled image. + * @param height The maximum height of the scaled image. + * + * @return The rounded corner scaled image. + */ + public static ImageIcon getScaledRoundedIcon( byte[] imageBytes, + int width, + int height) + { + if (imageBytes == null || !(imageBytes.length > 0)) + return null; + + ImageIcon imageIcon = null; + + try + { + Image image = null; + + // sometimes ImageIO fails, will fall back to awt Toolkit + try + { + image = ImageIO.read(new ByteArrayInputStream(imageBytes)); + } catch (Exception e) + { + try + { + image = Toolkit.getDefaultToolkit().createImage(imageBytes); + } catch (Exception e1) + { + // if it fails throw the original exception + throw e; + } + } + if(image != null) + imageIcon = getScaledRoundedIcon(image, width, height); + else + if (logger.isTraceEnabled()) + logger.trace("Unknown image format or error reading image"); + } + catch (Exception e) + { + if (logger.isDebugEnabled()) + logger.debug("Could not create image.", e); + } + + return imageIcon; + } + + /** + * Returns the buffered image corresponding to the given url image path. + * + * @param imagePath the path indicating, where we can find the image. + * + * @return the buffered image corresponding to the given url image path. + */ + public static BufferedImage getBufferedImage(URL imagePath) + { + BufferedImage image = null; + + if (imagePath != null) + { + try + { + image = ImageIO.read(imagePath); + } + catch (IOException ex) + { + if (logger.isDebugEnabled()) + logger.debug("Failed to load image:" + imagePath, ex); + } + } + return image; + } + + /** + * Returns the buffered image corresponding to the given image + * @param source an image + * @return the buffered image corresponding to the given image + */ + public static BufferedImage getBufferedImage(Image source) + { + if (source == null) + { + return null; + } + else if (source instanceof BufferedImage) + { + return (BufferedImage) source; + } + + int width = source.getWidth(null); + int height = source.getHeight(null); + + BufferedImage image + = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + + Graphics graphics = image.createGraphics(); + graphics.drawImage(source, 0, 0, null); + graphics.dispose(); + + return image; + } + + /** + * Extracts bytes from image. + * @param image the image. + * @return the bytes of the image. + */ + public static byte[] toByteArray(BufferedImage image) + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try + { + ImageIO.write(image, "png", out); + } + catch (IOException e) + { + logger.debug("Cannot convert buffered image to byte[]", e); + return null; + } + + return out.toByteArray(); + } + + /** + * Loads an image from a given bytes array. + * @param imageBytes The bytes array to load the image from. + * @return The image for the given bytes array. + */ + public static Image getBytesInImage(byte[] imageBytes) + { + Image image = null; + try + { + image = ImageIO.read( + new ByteArrayInputStream(imageBytes)); + + } + catch (Exception e) + { + logger.error("Failed to convert bytes to image.", e); + } + return image; + } + + /** + * Creates a composed image from two images. If one of the images + * is missing will add an empty space on its place. + * @param leftImage the left image. + * @param rightImage the right image + * @param imageObserver need to calculate image sizes. + * @return the composed image. + */ + public static Image getComposedImage( + Image leftImage, Image rightImage, + ImageObserver imageObserver) + { + int height = 0; + if(leftImage == null && rightImage == null) + return null; + if(leftImage != null && rightImage != null) + height = Math.max(leftImage.getHeight(imageObserver), + rightImage.getHeight(imageObserver)); + else if(leftImage == null) + height = rightImage.getHeight(imageObserver); + else + height = leftImage.getHeight(imageObserver); + + int width = 0; + int leftWidth = 0; + if(leftImage != null && rightImage != null) + { + leftWidth = leftImage.getWidth(imageObserver); + width = leftWidth + + rightImage.getWidth(imageObserver); + } + else if(leftImage == null) + { + leftWidth = rightImage.getWidth(imageObserver); + width = leftWidth*2; + } + else + { + leftWidth = leftImage.getWidth(imageObserver); + width = leftWidth*2; + } + + BufferedImage buffImage = + new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = (Graphics2D) buffImage.getGraphics(); + + AntialiasingManager.activateAntialiasing(g); + g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); + if(leftImage != null) + g.drawImage(leftImage, 0, 0, null); + if(rightImage != null) + g.drawImage(rightImage, leftWidth + 1, 0, null); + + return buffImage; + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/LightGrayFilter.java b/src/net/java/sip/communicator/plugin/desktoputil/LightGrayFilter.java new file mode 100644 index 0000000..44c8e4e --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/LightGrayFilter.java @@ -0,0 +1,49 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.image.*; + +import javax.swing.*; + +/** + * An image filter that "disables" an image by turning + * it into a grayscale image, and brightening the pixels + * in the image. Used by buttons to create an image for + * a disabled button. Creates a more brighter image than + * the javax.swing.GrayFilter. + * + * @author Yana Stamcheva + */ +public class LightGrayFilter extends GrayFilter +{ + /** + * Creates an instance of a LightGrayFilter. + * @param b a boolean -- true if the pixels should be brightened + * @param p an int in the range 0..100 that determines the percentage + * of gray, where 100 is the darkest gray, and 0 is the lightest + */ + public LightGrayFilter(boolean b, int p) + { + super(b, p); + } + + /** + * Creates a disabled image. + * @param i The source image. + * @return A disabled image based on the source image. + */ + public static Image createDisabledImage(Image i) + { + LightGrayFilter filter = new LightGrayFilter(true, 50); + ImageProducer prod = new FilteredImageSource(i.getSource(), filter); + Image grayImage = Toolkit.getDefaultToolkit().createImage(prod); + + return grayImage; + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/LowPriorityEventQueue.java b/src/net/java/sip/communicator/plugin/desktoputil/LowPriorityEventQueue.java new file mode 100644 index 0000000..a904448 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/LowPriorityEventQueue.java @@ -0,0 +1,53 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.event.*; + +/** + * The <tt>LowPriorityEventQueue</tt> schedules low priority events to be + * dispatched through the system event queue. + * + * @author Yana Stamcheva + */ +public class LowPriorityEventQueue +{ + /** + * Causes <code>runnable</code> to have its <code>run</code> + * method called in the event dispatch thread with low priority. + * + * @param runnable the <code>Runnable</code> whose <code>run</code> + * method should be executed synchronously on the <code>EventQueue</code> + */ + public static void invokeLater(Runnable runnable) + { + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent( + new LowPriorityInvocationEvent( + Toolkit.getDefaultToolkit(), runnable)); + } + + /** + * The <tt>LowPriorityInvocationEvent</tt> is an <tt>InvocationEvent</tt> + * that replaces the default event id with the <tt>PaintEvent.UPDATE</tt> + * in order to indicate that this event should be dispatched with the same + * priority as an update paint event, which is normally with lower priority + * than other events. + */ + private static class LowPriorityInvocationEvent extends InvocationEvent + { + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + public LowPriorityInvocationEvent(Object source, Runnable runnable) + { + super(source, PaintEvent.UPDATE, runnable, null, false); + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/MasterPasswordInputDialog.java b/src/net/java/sip/communicator/plugin/desktoputil/MasterPasswordInputDialog.java new file mode 100644 index 0000000..6542b96 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/MasterPasswordInputDialog.java @@ -0,0 +1,296 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + +import net.java.sip.communicator.util.*; + +import org.jitsi.service.resources.*; + +/** + * The master password input dialog. + * + * @author Dmitri Melnikov + */ +public class MasterPasswordInputDialog + extends SIPCommDialog + implements ActionListener, + KeyListener +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * Instance of this class. + */ + private static MasterPasswordInputDialog dialog; + + /** + * The <tt>ResourceManagementService</tt> used by this instance to access + * the localized and internationalized resources of the application. + */ + private final ResourceManagementService resources + = DesktopUtilActivator.getResources(); + + /** + * Password obtained from the user. + */ + private String password; + + /** + * UI components. + */ + private JPasswordField currentPasswdField; + private JButton okButton; + private JButton cancelButton; + private JTextArea infoTextArea; + private JTextArea errorTextArea; + private JPanel buttonsPanel; + private JPanel dataPanel; + + /** + * Builds the dialog. + */ + private MasterPasswordInputDialog() + { + super(false); + + initComponents(); + + this.setTitle(resources + .getI18NString("plugin.securityconfig.masterpassword.MP_TITLE")); + this.setModal(true); + this.setResizable(false); + + JPanel mainPanel = new TransparentPanel(new BorderLayout(10, 10)); + mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + + mainPanel.add(createIconComponent(), BorderLayout.WEST); + mainPanel.add(dataPanel); + + this.getContentPane().add(mainPanel); + + this.pack(); + + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + int x = (screenSize.width - this.getWidth()) / 2; + int y = (screenSize.height - this.getHeight()) / 2; + + this.setLocation(x, y); + } + + /** + * Initializes the UI components. + */ + private void initComponents() + { + dataPanel = new TransparentPanel(); + dataPanel.setLayout(new BoxLayout(dataPanel, BoxLayout.Y_AXIS)); + + // info text + infoTextArea = new JTextArea(); + infoTextArea.setEditable(false); + infoTextArea.setOpaque(false); + infoTextArea.setFont(infoTextArea.getFont().deriveFont(Font.BOLD)); + infoTextArea.setText(resources + .getI18NString("plugin.securityconfig.masterpassword.MP_INPUT")); + + // error text + errorTextArea = new JTextArea(); + errorTextArea.setEditable(false); + errorTextArea.setOpaque(false); + errorTextArea.setForeground(Color.red); + errorTextArea.setFont(errorTextArea.getFont().deriveFont(Font.BOLD)); + errorTextArea.setText(resources + .getI18NString("plugin.securityconfig.masterpassword" + + ".MP_VERIFICATION_FAILURE_MSG")); + + // password fields + currentPasswdField = new JPasswordField(15); + currentPasswdField.addKeyListener(this); + currentPasswdField.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent arg0) + { + okButton.doClick(); + } + }); + // Prevents the password field to select the whole text automatically + // when it regains the focus. The master password dialog may loose the + // focus and regain it when the main contact list window is loaded, so + // we make sure here that we won't delete everything that we've already + // typed when the focus lost/gained events happen. + currentPasswdField.addFocusListener(new FocusAdapter() + { + @Override + public void focusGained(FocusEvent evt2) + { + int caretPosition = currentPasswdField.getCaretPosition(); + currentPasswdField.setSelectionEnd(0); + // Reset the caret position. + currentPasswdField.setCaretPosition(caretPosition); + } + }); + + // OK and cancel buttons + okButton = new JButton(resources.getI18NString("service.gui.OK")); + okButton.setMnemonic(resources.getI18nMnemonic("service.gui.OK")); + okButton.addActionListener(this); + + cancelButton = + new JButton(resources.getI18NString("service.gui.CANCEL")); + cancelButton.setMnemonic(resources.getI18nMnemonic( + "service.gui.CANCEL")); + cancelButton.addActionListener(this); + + buttonsPanel = new TransparentPanel( + new FlowLayout(FlowLayout.RIGHT, 0, 5)); + buttonsPanel.add(okButton); + buttonsPanel.add(cancelButton); + + rebuildMainPanel(false); + } + + /** + * Removes and adds again all the components to the main panel. + * + * @param includeErrorMsg when true also includes an error text component + */ + private void rebuildMainPanel(boolean includeErrorMsg) + { + dataPanel.removeAll(); + + if (includeErrorMsg) + dataPanel.add(errorTextArea); + dataPanel.add(infoTextArea); + dataPanel.add(currentPasswdField); + dataPanel.add(buttonsPanel); + } + + /** + * Shows an input dialog to the user to obtain the master password. + * + * @param prevSuccess <tt>true</tt> if any previous call returned a correct + * master password and there is no need to show an extra "verification + * failed" message + * @return the master password obtained from the user or <tt>null</tt> if + * none was provided + */ + public static String showInput(boolean prevSuccess) + { + if (dialog == null) + dialog = new MasterPasswordInputDialog(); + + dialog.rebuildMainPanel(!prevSuccess); + dialog.resetPassword(); + + // blocks until user performs an action + dialog.setVisible(true); + + return dialog.password; + } + + /** + * OK button click event handler. Retrieves the password and hides the + * dialog. + * + * @param e action event + */ + public void actionPerformed(ActionEvent e) + { + JButton sourceButton = (JButton) e.getSource(); + if (sourceButton.equals(okButton)) + { + password + = new String( + currentPasswdField.getPassword()); + } + // hide dialog and unblock application + dialog.dispose(); + } + + /** + * Closes the dialog. + * + * @param escaped <tt>true</tt> if this dialog has been closed by pressing + * the Esc key; otherwise, <tt>false</tt> + */ + protected void close(boolean escaped) + { + cancelButton.doClick(); + } + + /** + * Resets the password by clearing the input field and setting + * <tt>password</tt> to <tt>null</tt>. Disables the OK button. + */ + private void resetPassword() + { + password = null; + currentPasswdField.setText(""); + currentPasswdField.requestFocusInWindow(); + okButton.setEnabled(false); + } + + /** + * Disables OK button if the password input field is empty. + * + * @param event key event + */ + public void keyReleased(KeyEvent event) + { + JPasswordField source = (JPasswordField) event.getSource(); + if (currentPasswdField.equals(source)) + { + String password = new String(currentPasswdField.getPassword()); + okButton.setEnabled(password.length() > 0); + password = null; + } + } + + /** + * Not overriding. + * + * @param arg0 key event + */ + public void keyPressed(KeyEvent arg0) + { + } + + /** + * Not overriding. + * + * @param arg0 key event + */ + public void keyTyped(KeyEvent arg0) + { + } + + /** + * Creates the icon component to show on the left of this dialog. + * + * @return the created component + */ + private static Component createIconComponent() + { + JPanel wrapIconPanel = new TransparentPanel(new BorderLayout()); + + JLabel iconLabel = new JLabel(); + + iconLabel.setIcon(DesktopUtilActivator.getResources() + .getImage("service.gui.icons.AUTHORIZATION_ICON")); + + wrapIconPanel.add(iconLabel, BorderLayout.NORTH); + + return wrapIconPanel; + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/MoveableTableModel.java b/src/net/java/sip/communicator/plugin/desktoputil/MoveableTableModel.java new file mode 100644 index 0000000..66830a5 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/MoveableTableModel.java @@ -0,0 +1,25 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import javax.swing.table.*; + +/** + * @author Vincent Lucas + */ +public abstract class MoveableTableModel + extends AbstractTableModel +{ + /** + * Move the row. + * + * @param rowIndex index of the row + * @param up true to move up, false to move down + 8 + * @return the next row index + */ + public abstract int move(int rowIndex, boolean up); +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/NetworkUtils.java b/src/net/java/sip/communicator/plugin/desktoputil/NetworkUtils.java new file mode 100644 index 0000000..7d76511 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/NetworkUtils.java @@ -0,0 +1,1661 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.beans.*; +import java.net.*; +import java.text.*; +import java.util.*; +import java.util.concurrent.atomic.*; + +import net.java.sip.communicator.plugin.desktoputil.dns.*; +import net.java.sip.communicator.service.netaddr.event.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.SRVRecord; + +import org.xbill.DNS.*; + +/** + * Utility methods and fields to use when working with network addresses. + * + * @author Emil Ivov + * @author Damian Minkov + * @author Vincent Lucas + * @author Alan Kelly + */ +public class NetworkUtils +{ + /** + * The <tt>Logger</tt> used by the <tt>NetworkUtils</tt> class for logging + * output. + */ + private static final Logger logger = Logger.getLogger(NetworkUtils.class); + + /** + * A string containing the "any" local address for IPv6. + */ + public static final String IN6_ADDR_ANY = "::0"; + + /** + * A string containing the "any" local address for IPv4. + */ + public static final String IN4_ADDR_ANY = "0.0.0.0"; + + /** + * A string containing the "any" local address. + */ + public static final String IN_ADDR_ANY = determineAnyAddress(); + + /** + * The length of IPv6 addresses. + */ + private final static int IN6_ADDR_SIZE = 16; + + /** + * The size of the tokens in a <tt>String</tt> representation of IPv6 + * addresses. + */ + private final static int IN6_ADDR_TOKEN_SIZE = 2; + + /** + * The length of IPv4 addresses. + */ + private final static int IN4_ADDR_SIZE = 4; + + /** + * The maximum int value that could correspond to a port number. + */ + public static final int MAX_PORT_NUMBER = 65535; + + /** + * The minimum int value that could correspond to a port number bindable + * by the SIP Communicator. + */ + public static final int MIN_PORT_NUMBER = 1024; + + /** + * The random port number generator that we use in getRandomPortNumer() + */ + private static Random portNumberGenerator = new Random(); + + /** + * The name of the property that users may use to override the + * address of our backup DNS resolver. + */ + public static final String PNAME_BACKUP_RESOLVER + = "net.java.sip.communicator.util.dns.BACKUP_RESOLVER"; + + /** + * The name of the property that users may use to disable + * our backup DNS resolver. + */ + public static final String PNAME_BACKUP_RESOLVER_ENABLED + = "net.java.sip.communicator.util.dns.BACKUP_RESOLVER_ENABLED"; + + /** + * The default of the property that users may use to disable + * our backup DNS resolver. + */ + public static final boolean PDEFAULT_BACKUP_RESOLVER_ENABLED = true; + + /** + * The name of the property that users may use to override the port + * of our backup DNS resolver. + */ + public static final String PNAME_BACKUP_RESOLVER_PORT + = "net.java.sip.communicator.util.dns.BACKUP_RESOLVER_PORT"; + + /** + * The address of the backup resolver we would use by default. + */ + public static final String DEFAULT_BACKUP_RESOLVER + = "backup-resolver.jitsi.net"; + + /** + * The name of the property that users may use to override the + * IP address of our backup DNS resolver. This is only used when the + * backup resolver name cannot be determined. + */ + public static final String PNAME_BACKUP_RESOLVER_FALLBACK_IP + = "net.java.sip.communicator.util.dns.BACKUP_RESOLVER_FALLBACK_IP"; + + /** + * The name of the boolean property that defines whether all domain names + * looked up from Jitsi should be treated as absolute. + */ + public static final String PNAME_DNS_ALWAYS_ABSOLUTE + = "net.java.sip.communicator.util.dns.DNSSEC_ALWAYS_ABSOLUTE"; + + /** + * Default value of {@link #PNAME_DNS_ALWAYS_ABSOLUTE}. + */ + public static final boolean PDEFAULT_DNS_ALWAYS_ABSOLUTE = false; + + /** + * The DNSjava resolver that we use with SRV and NAPTR queries in order to + * try and smooth the problem of DNS servers that silently drop them. + */ + private static Resolver parallelResolver = null; + + /** + * Monitor object to set or reset the parallel resolver. + */ + private final static Object parallelResolverLock = new Object(); + + /** + * Initialization flag for {@link #netListener} + */ + private static final AtomicBoolean netListenerAdded = new AtomicBoolean(); + + /** + * Listener for network change events to reset the DNS resolvers. + */ + private static final NetworkListener netListener = new NetworkListener(); + + /** + * A random number generator. + */ + private static final Random random = new Random(); + + /** + * 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 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. + */ + 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; + } + + /** + * Returns a random local port number that user applications could bind to. + * (i.e. above 1024). + * @return a random int located between 1024 and 65 535. + */ + public static int getRandomPortNumber() + { + return getRandomPortNumber(MIN_PORT_NUMBER, MAX_PORT_NUMBER); + } + + /** + * Returns a random local port number, greater than min and lower than max. + * + * @param min the minimum allowed value for the returned port number. + * @param max the maximum allowed value for the returned port number. + * + * @return a random int located between greater than min and lower than max. + */ + public static int getRandomPortNumber(int min, int max) + { + return portNumberGenerator.nextInt(max - min) + min; + } + + /** + * Returns a random local port number, greater than min and lower than max. + * If the pair flag is set to true, then the returned port number is + * guaranteed to be pair. This is useful for protocols that require this + * such as RTP + * + * @param min the minimum allowed value for the returned port number. + * @param max the maximum allowed value for the returned port number. + * @param pair specifies whether the caller would like the returned port to + * be pair. + * + * @return a random int located between greater than min and lower than max. + */ + public static int getRandomPortNumber(int min, int max, boolean pair) + { + if(pair) + { + int delta = max - min; + delta /= 2; + int port = getRandomPortNumber(min, min + delta); + return port * 2; + } + else + { + return getRandomPortNumber(min, max); + } + } + + /** + * Verifies whether <tt>address</tt> could be an IPv4 address string. + * + * @param address the String that we'd like to determine as an IPv4 address. + * + * @return true if the address contained by <tt>address</tt> is an IPv4 + * address and false otherwise. + */ + public static boolean isIPv4Address(String address) + { + return strToIPv4(address) != null; + } + + /** + * Verifies whether <tt>address</tt> could be an IPv6 address string. + * + * @param address the String that we'd like to determine as an IPv6 address. + * + * @return true if the address contained by <tt>address</tt> is an IPv6 + * address and false otherwise. + */ + public static boolean isIPv6Address(String address) + { + return strToIPv6(address) != null; + } + + /** + * Checks whether <tt>address</tt> is a valid IP address string. + * + * @param address the address that we'd like to check + * @return true if address is an IPv4 or IPv6 address and false otherwise. + */ + public static boolean isValidIPAddress(String address) + { + // empty string + if (address == null || address.length() == 0) + { + return false; + } + + // look for IPv6 brackets and remove brackets for parsing + boolean ipv6Expected = false; + if (address.charAt(0) == '[') + { + // This is supposed to be an IPv6 literal + if (address.length() > 2 + && address.charAt(address.length() - 1) == ']') + { + // remove brackets from IPv6 + address = address.substring(1, address.length() - 1); + ipv6Expected = true; + } + else + { + return false; + } + } + + // look for IP addresses + if (Character.digit(address.charAt(0), 16) != -1 + || (address.charAt(0) == ':')) + { + byte[] addr = null; + + // see if it is IPv4 address + addr = strToIPv4(address); + // if not, see if it is IPv6 address + if (addr == null) + { + addr = strToIPv6(address); + } + // if IPv4 is found when IPv6 is expected + else if (ipv6Expected) + { + // invalid address: IPv4 address surrounded with brackets! + return false; + } + // if an IPv4 or IPv6 address is found + if (addr != null) + { + // is an IP address + return true; + } + } + // no matches found + return false; + } + + /** + * Creates a byte array containing the specified <tt>ipv4AddStr</tt>. + * + * @param ipv4AddrStr a <tt>String</tt> containing an IPv4 address. + * + * @return a byte array containing the four bytes of the address represented + * by ipv4AddrStr or <tt>null</tt> if <tt>ipv4AddrStr</tt> does not contain + * a valid IPv4 address string. + */ + public static byte[] strToIPv4(String ipv4AddrStr) + { + if (ipv4AddrStr == null || ipv4AddrStr.length() == 0) + return null; + + byte[] address = new byte[IN4_ADDR_SIZE]; + String[] tokens = ipv4AddrStr.split("\\.", -1); + long currentTkn; + try + { + switch(tokens.length) + { + case 1: + //If the address was specified as a single String we can + //directly copy it into the byte array. + currentTkn = Long.parseLong(tokens[0]); + if (currentTkn < 0 || currentTkn > 0xffffffffL) + return null; + address[0] = (byte) ((currentTkn >> 24) & 0xff); + address[1] = (byte) (((currentTkn & 0xffffff) >> 16) & 0xff); + address[2] = (byte) (((currentTkn & 0xffff) >> 8) & 0xff); + address[3] = (byte) (currentTkn & 0xff); + break; + case 2: + // If the address was passed in two parts (e.g. when dealing + // with a Class A address representation), we place the + // first one in the leftmost byte and the rest in the three + // remaining bytes of the address array. + currentTkn = Integer.parseInt(tokens[0]); + + if (currentTkn < 0 || currentTkn > 0xff) + return null; + + address[0] = (byte) (currentTkn & 0xff); + currentTkn = Integer.parseInt(tokens[1]); + + if (currentTkn < 0 || currentTkn > 0xffffff) + return null; + + address[1] = (byte) ((currentTkn >> 16) & 0xff); + address[2] = (byte) (((currentTkn & 0xffff) >> 8) &0xff); + address[3] = (byte) (currentTkn & 0xff); + break; + case 3: + // If the address was passed in three parts (e.g. when + // dealing with a Class B address representation), we place + // the first two parts in the two leftmost bytes and the + // rest in the two remaining bytes of the address array. + for (int i = 0; i < 2; i++) + { + currentTkn = Integer.parseInt(tokens[i]); + + if (currentTkn < 0 || currentTkn > 0xff) + return null; + + address[i] = (byte) (currentTkn & 0xff); + } + + currentTkn = Integer.parseInt(tokens[2]); + + if (currentTkn < 0 || currentTkn > 0xffff) + return null; + + address[2] = (byte) ((currentTkn >> 8) & 0xff); + address[3] = (byte) (currentTkn & 0xff); + break; + case 4: + // And now for the most common - four part case. This time + // there's a byte for every part :). Yuppiee! :) + for (int i = 0; i < 4; i++) + { + currentTkn = Integer.parseInt(tokens[i]); + + if (currentTkn < 0 || currentTkn > 0xff) + return null; + + address[i] = (byte) (currentTkn & 0xff); + } + break; + default: + return null; + } + } + catch(NumberFormatException e) + { + return null; + } + + return address; + } + + /** + * Creates a byte array containing the specified <tt>ipv6AddStr</tt>. + * + * @param ipv6AddrStr a <tt>String</tt> containing an IPv6 address. + * + * @return a byte array containing the four bytes of the address represented + * by <tt>ipv6AddrStr</tt> or <tt>null</tt> if <tt>ipv6AddrStr</tt> does + * not contain a valid IPv6 address string. + */ + public static byte[] strToIPv6(String ipv6AddrStr) + { + // Bail out if the string is shorter than "::" + if (ipv6AddrStr == null || ipv6AddrStr.length() < 2) + return null; + + int colonIndex; + char currentChar; + boolean sawtDigit; + int currentTkn; + char[] addrBuff = ipv6AddrStr.toCharArray(); + byte[] dst = new byte[IN6_ADDR_SIZE]; + + int srcb_length = addrBuff.length; + int scopeID = ipv6AddrStr.indexOf ("%"); + + if (scopeID == srcb_length -1) + return null; + + if (scopeID != -1) + srcb_length = scopeID; + + colonIndex = -1; + int i = 0, j = 0; + // Starting : mean we need to have at least one more. + if (addrBuff[i] == ':') + if (addrBuff[++i] != ':') + return null; + + int curtok = i; + sawtDigit = false; + currentTkn = 0; + while (i < srcb_length) + { + currentChar = addrBuff[i++]; + int chval = Character.digit(currentChar, 16); + if (chval != -1) + { + currentTkn <<= 4; + currentTkn |= chval; + if (currentTkn > 0xffff) + return null; + sawtDigit = true; + continue; + } + + if (currentChar == ':') + { + curtok = i; + + if (!sawtDigit) + { + if (colonIndex != -1) + return null; + colonIndex = j; + continue; + } + else if (i == srcb_length) + { + return null; + } + + if (j + IN6_ADDR_TOKEN_SIZE > IN6_ADDR_SIZE) + return null; + + dst[j++] = (byte) ((currentTkn >> 8) & 0xff); + dst[j++] = (byte) (currentTkn & 0xff); + sawtDigit = false; + currentTkn = 0; + continue; + } + + if (currentChar == '.' && ((j + IN4_ADDR_SIZE) <= IN6_ADDR_SIZE)) + { + String ia4 = ipv6AddrStr.substring(curtok, srcb_length); + // check this IPv4 address has 3 dots, ie. A.B.C.D + int dot_count = 0, index=0; + while ((index = ia4.indexOf ('.', index)) != -1) + { + dot_count ++; + index ++; + } + + if (dot_count != 3) + return null; + + byte[] v4addr = strToIPv4(ia4); + if (v4addr == null) + return null; + + for (int k = 0; k < IN4_ADDR_SIZE; k++) + { + dst[j++] = v4addr[k]; + } + + sawtDigit = false; + break; /* '\0' was seen by inet_pton4(). */ + } + return null; + } + + if (sawtDigit) + { + if (j + IN6_ADDR_TOKEN_SIZE > IN6_ADDR_SIZE) + return null; + + dst[j++] = (byte) ((currentTkn >> 8) & 0xff); + dst[j++] = (byte) (currentTkn & 0xff); + } + + if (colonIndex != -1) + { + int n = j - colonIndex; + + if (j == IN6_ADDR_SIZE) + return null; + + for (i = 1; i <= n; i++) + { + dst[IN6_ADDR_SIZE - i] = dst[colonIndex + n - i]; + dst[colonIndex + n - i] = 0; + } + + j = IN6_ADDR_SIZE; + } + + if (j != IN6_ADDR_SIZE) + return null; + + byte[] newdst = mappedIPv4ToRealIPv4(dst); + + if (newdst != null) + { + return newdst; + } + else + { + return dst; + } + } + + /** + * Returns array of hosts from the SRV record of the specified domain. + * The records are ordered against the SRV record priority + * @param domain the name of the domain we'd like to resolve (_proto._tcp + * included). + * @return an array of SRVRecord containing records returned by the DNS + * server - address and port . + * @throws ParseException if <tt>domain</tt> is not a valid domain name. + * @throws DnssecException when a DNSSEC validation failure occurred. + */ + public static SRVRecord[] getSRVRecords(String domain) + throws ParseException, DnssecException + { + Record[] records = null; + try + { + Lookup lookup = createLookup(domain, Type.SRV); + records = lookup.run(); + } + catch (TextParseException tpe) + { + logger.error("Failed to parse domain=" + domain, tpe); + throw new ParseException(tpe.getMessage(), 0); + } + catch(DnssecRuntimeException e) + { + throw new DnssecException(e); + } + if (records == null) + { + return null; + } + + //String[][] pvhn = new String[records.length][4]; + SRVRecord srvRecords[] = new SRVRecord[records.length]; + + for (int i = 0; i < records.length; i++) + { + org.xbill.DNS.SRVRecord srvRecord = + (org.xbill.DNS.SRVRecord) records[i]; + srvRecords[i] = new SRVRecord(srvRecord); + } + + // Sort the SRV RRs by priority (lower is preferred) and weight. + sortSrvRecord(srvRecords); + + if (logger.isTraceEnabled()) + { + logger.trace("DNS SRV query for domain " + domain + " returned:"); + for (int i = 0; i < srvRecords.length; i++) + { + if (logger.isTraceEnabled()) + logger.trace(srvRecords[i]); + } + } + return srvRecords; + } + + /** + * Returns an <tt>InetSocketAddress</tt> representing the first SRV + * record available for the specified domain or <tt>null</tt> if there are + * not SRV records for <tt>domain</tt>. + * + * @param domain the name of the domain we'd like to resolve. + * @param service the service that we are trying to get a record for. + * @param proto the protocol that we'd like <tt>service</tt> on. + * + * @return the first InetSocketAddress containing records returned by the + * DNS server - address and port . + * @throws ParseException if <tt>domain</tt> is not a valid domain name. + * @throws DnssecException when a DNSSEC validation failure occurred. + */ + public static SRVRecord getSRVRecord(String service, + String proto, + String domain) + throws ParseException, DnssecException + { + SRVRecord[] records = getSRVRecords("_" + service + + "._" + proto + + "." + domain); + + if(records == null || records.length == 0) + return null; + + return records[0]; + } + + /** + * Returns an <tt>InetSocketAddress</tt> representing the first SRV + * record available for the specified domain or <tt>null</tt> if there are + * not SRV records for <tt>domain</tt>. + * + * @param domain the name of the domain we'd like to resolve. + * @param service the service that we are trying to get a record for. + * @param proto the protocol that we'd like <tt>service</tt> on. + * + * @return the InetSocketAddress[] containing records returned by the + * DNS server - address and port . + * @throws ParseException if <tt>domain</tt> is not a valid domain name. + * @throws DnssecException when a DNSSEC validation failure occurred. + */ + public static SRVRecord[] getSRVRecords(String service, + String proto, + String domain) + throws ParseException, DnssecException + { + SRVRecord[] records = getSRVRecords("_" + service + + "._" + proto + + "." + domain); + + if(records == null || records.length == 0) + return null; + + return records; + } + + /** + * Makes a NAPTR query and returns the result. The returned records are an + * array of [Order, Service(Transport) and Replacement + * (the srv to query for servers and ports)] this all for supplied + * <tt>domain</tt>. + * + * @param domain the name of the domain we'd like to resolve. + * @return an array with the values or null if no records found. + * + * @throws ParseException if <tt>domain</tt> is not a valid domain name. + * @throws DnssecException when a DNSSEC validation failure occurred. + */ + public static String[][] getNAPTRRecords(String domain) + throws ParseException, DnssecException + { + Record[] records = null; + try + { + Lookup lookup = createLookup(domain, Type.NAPTR); + records = lookup.run(); + } + catch (TextParseException tpe) + { + logger.error("Failed to parse domain="+domain, tpe); + throw new ParseException(tpe.getMessage(), 0); + } + catch(DnssecRuntimeException e) + { + throw new DnssecException(e); + } + if (records == null) + { + + if(logger.isTraceEnabled()) + logger.trace("No NAPTRs found for " + domain); + return null; + } + + String[][] recVals = new String[records.length][4]; + for (int i = 0; i < records.length; i++) + { + NAPTRRecord r = (NAPTRRecord)records[i]; + + // todo - check here for broken records as missing transport + recVals[i][0] = "" + r.getOrder(); + recVals[i][1] = getProtocolFromNAPTRRecords(r.getService()); + String replacement = r.getReplacement().toString(); + + if (replacement.endsWith(".")) + { + recVals[i][2] = + replacement.substring(0, replacement.length() - 1); + } + else + { + recVals[i][2] = replacement; + } + recVals[i][3] = "" + r.getPreference(); + } + + // sort the SRV RRs by RR value (lower is preferred) + Arrays.sort(recVals, new Comparator<String[]>() + { + // Sorts NAPTR records by ORDER (low number first), PREFERENCE (low + // number first) and PROTOCOL (0-TLS, 1-TCP, 2-UDP). + public int compare(String array1[], String array2[]) + { + // First tries to define the priority with the NAPTR order. + int order + = Integer.parseInt(array1[0]) - Integer.parseInt(array2[0]); + if(order != 0) + { + return order; + } + + // Second tries to define the priority with the NAPTR + // preference. + int preference + = Integer.parseInt(array1[4]) - Integer.parseInt(array2[4]); + if(preference != 0) + { + return preference; + } + + // Finally defines the priority with the NAPTR protocol. + int protocol + = getProtocolPriority(array1[1]) + - getProtocolPriority(array2[1]); + return protocol; + } + }); + + if(logger.isTraceEnabled()) + logger.trace("NAPTRs for " + domain + "=" + + Arrays.toString(recVals)); + return recVals; + } + + /** + * Returns the mapping from rfc3263 between service and the protocols. + * + * @param service the service from NAPTR record. + * @return the protocol TCP, UDP or TLS. + */ + private static String getProtocolFromNAPTRRecords(String service) + { + if(service.equalsIgnoreCase("SIP+D2U")) + return "UDP"; + else if(service.equalsIgnoreCase("SIP+D2T")) + return "TCP"; + else if(service.equalsIgnoreCase("SIPS+D2T")) + return "TLS"; + else + return null; + } + + /** + * Returns the priority of a protocol. The lowest priority is the highest: + * 0-TLS, 1-TCP, 2-UDP. + * + * @param protocol The protocol name: "TLS", "TCP" or "UDP". + * + * @return The priority of a protocol. The lowest priority is the highest: + * 0-TLS, 1-TCP, 2-UDP. + */ + private static int getProtocolPriority(String protocol) + { + if(protocol.equals("TLS")) + return 0; + else if(protocol.equals("TCP")) + return 1; + return 2; // "UDP". + } + + /** + * Creates an InetAddress from the specified <tt>hostAddress</tt>. The point + * of using the method rather than creating the address by yourself is that + * it would first check whether the specified <tt>hostAddress</tt> is indeed + * a valid ip address. It this is the case, the method would create the + * <tt>InetAddress</tt> using the <tt>InetAddress.getByAddress()</tt> + * method so that no DNS resolution is attempted by the JRE. Otherwise + * it would simply use <tt>InetAddress.getByName()</tt> so that we would an + * <tt>InetAddress</tt> instance even at the cost of a potential DNS + * resolution. + * + * @param hostAddress the <tt>String</tt> representation of the address + * that we would like to create an <tt>InetAddress</tt> instance for. + * + * @return an <tt>InetAddress</tt> instance corresponding to the specified + * <tt>hostAddress</tt>. + * + * @throws UnknownHostException if any of the <tt>InetAddress</tt> methods + * we are using throw an exception. + */ + public static InetAddress getInetAddress(String hostAddress) + throws UnknownHostException + { + //is null + if (hostAddress == null || hostAddress.length() == 0) + { + throw new UnknownHostException( + hostAddress + " is not a valid host address"); + } + + //transform IPv6 literals into normal addresses + if (hostAddress.charAt(0) == '[') + { + // This is supposed to be an IPv6 literal + if (hostAddress.length() > 2 + && hostAddress.charAt(hostAddress.length()-1) == ']') + { + hostAddress = hostAddress.substring(1, hostAddress.length() -1); + } + else + { + // This was supposed to be a IPv6 address, but it's not! + throw new UnknownHostException(hostAddress); + } + } + + if (NetworkUtils.isValidIPAddress(hostAddress)) + { + byte[] addr = null; + + // attempt parse as IPv4 address + addr = strToIPv4(hostAddress); + + // if not IPv4, parse as IPv6 address + if (addr == null) + { + addr = strToIPv6(hostAddress); + } + return InetAddress.getByAddress(hostAddress, addr); + } + else + { + return InetAddress.getByName(hostAddress); + } + } + + /** + * Returns array of hosts from the A and AAAA records of the specified + * domain. The records are ordered against the IPv4/IPv6 protocol priority + * + * @param domain the name of the domain we'd like to resolve. + * @param port the port number of the returned <tt>InetSocketAddress</tt> + * @return an array of InetSocketAddress containing records returned by the + * DNS server - address and port . + * @throws ParseException if <tt>domain</tt> is not a valid domain name. + * @throws DnssecException when a DNSSEC validation failure occurred. + */ + public static InetSocketAddress[] getAandAAAARecords(String domain, int port) + throws ParseException, DnssecException + { + byte[] address = null; + if((address = strToIPv4(domain)) != null + || (address = strToIPv6(domain)) != null) + { + try + { + return new InetSocketAddress[] + { + new InetSocketAddress( + InetAddress.getByAddress(domain, address), port) + }; + } + catch (UnknownHostException e) + { + //should not happen + logger.error( + "Unable to create InetAddress for <" + domain + ">", e); + return null; + } + } + + List<InetSocketAddress> addresses = new LinkedList<InetSocketAddress>(); + boolean v6lookup = Boolean.getBoolean("java.net.preferIPv6Addresses"); + + for(int i = 0; i < 2; i++) + { + Lookup lookup; + try + { + lookup = createLookup(domain, v6lookup ? Type.AAAA : Type.A); + } + catch (TextParseException tpe) + { + logger.error("Failed to parse domain <" + domain + ">", tpe); + throw new ParseException(tpe.getMessage(), 0); + } + Record[] records = null; + try + { + records = lookup.run(); + } + catch(DnssecRuntimeException e) + { + throw new DnssecException(e); + } + if(records != null) + { + for(Record r : records) + { + try + { + addresses.add( + new InetSocketAddress( + // create a new InetAddress filled with the + // domain name to avoid PTR queries + InetAddress.getByAddress( + domain, + v6lookup + ? ((AAAARecord)r).getAddress().getAddress() + : ((ARecord)r).getAddress().getAddress() + ), + port + ) + ); + } + catch (UnknownHostException e) + { + logger.error("Invalid record returned from DNS", e); + } + } + } + v6lookup = !v6lookup; + } + if(logger.isTraceEnabled()) + logger.trace("A or AAAA addresses: " + addresses); + return addresses.toArray(new InetSocketAddress[0]); + } + + /** + * Returns array of hosts from the A record of the specified domain. + * The records are ordered against the A record priority + * @param domain the name of the domain we'd like to resolve. + * @param port the port number of the returned <tt>InetSocketAddress</tt> + * @return an array of InetSocketAddress containing records returned by the + * DNS server - address and port . + * @throws ParseException if <tt>domain</tt> is not a valid domain name. + * @throws DnssecException when a DNSSEC validation failure occurred. + */ + public static InetSocketAddress getARecord(String domain, int port) + throws ParseException, DnssecException + { + byte[] address; + if((address = strToIPv4(domain)) != null) + { + try + { + return new InetSocketAddress( + InetAddress.getByAddress(domain, address), port); + } + catch (UnknownHostException e) + { + //should not happen + logger.error( + "Unable to create InetAddress for <" + domain + ">", e); + return null; + } + } + + Record[] records; + try + { + //note that we intentionally do not use our parallel resolver here. + //for starters we'd like to make sure that it works well enough + //with SRV and NAPTR queries. We may then also adopt it for As + //and AAAAs once it proves to be reliable (posted on: 2010-11-24) + Lookup lookup = createLookup(domain, Type.A); + records = lookup.run(); + } + catch (TextParseException tpe) + { + logger.error("Failed to parse domain="+domain, tpe); + throw new ParseException(tpe.getMessage(), 0); + } + catch(DnssecRuntimeException e) + { + throw new DnssecException(e); + } + if (records != null && records.length > 0) + { + if(logger.isTraceEnabled()) + logger.trace("A record for " + domain + "=" + + ((ARecord)records[0]).getAddress()); + try + { + return new InetSocketAddress( + InetAddress.getByAddress(domain, + ((ARecord)records[0]).getAddress().getAddress()), + port); + } + catch (UnknownHostException e) + { + return null; + } + } + else + { + if(logger.isTraceEnabled()) + logger.trace("No A record found for " + domain); + return null; + } + } + + /** + * Returns array of hosts from the AAAA record of the specified domain. + * The records are ordered against the AAAA record priority + * @param domain the name of the domain we'd like to resolve. + * @param port the port number of the returned <tt>InetSocketAddress</tt> + * @return an array of InetSocketAddress containing records returned by the + * DNS server - address and port . + * @throws ParseException if <tt>domain</tt> is not a valid domain name. + * @throws DnssecException + */ + public static InetSocketAddress getAAAARecord(String domain, int port) + throws ParseException, DnssecException + { + byte[] address; + if((address = strToIPv6(domain)) != null) + { + try + { + return new InetSocketAddress( + InetAddress.getByAddress(domain, address), port); + } + catch (UnknownHostException e) + { + //should not happen + logger.error( + "Unable to create InetAddress for <" + domain + ">", e); + return null; + } + } + + Record[] records; + try + { + //note that we intentionally do not use our parallel resolver here. + //for starters we'd like to make sure that it works well enough + //with SRV and NAPTR queries. We may then also adopt it for As + //and AAAAs once it proves to be reliable (posted on: 2010-11-24) + Lookup lookup = createLookup(domain, Type.AAAA); + records = lookup.run(); + } + catch (TextParseException tpe) + { + logger.error("Failed to parse domain="+domain, tpe); + throw new ParseException(tpe.getMessage(), 0); + } + catch(DnssecRuntimeException e) + { + throw new DnssecException(e); + } + if (records != null && records.length > 0) + { + if(logger.isTraceEnabled()) + logger.trace("AAAA record for " + domain + "=" + + ((AAAARecord)records[0]).getAddress()); + try + { + return new InetSocketAddress( + InetAddress.getByAddress(domain, + ((AAAARecord)records[0]).getAddress().getAddress()), + port); + } + catch (UnknownHostException e) + { + return null; + } + } + else + { + if(logger.isTraceEnabled()) + logger.trace("No AAAA record found for " + domain); + return null; + } + } + + /** + * Tries to determine if this host supports IPv6 addresses (i.e. has at + * least one IPv6 address) and returns IN6_ADDR_ANY or IN4_ADDR_ANY + * accordingly. This method is only used to initialize IN_ADDR_ANY so that + * it could be used when binding sockets. The reason we need it is because + * on mac (contrary to lin or win) binding a socket on 0.0.0.0 would make + * it deaf to IPv6 traffic. Binding on ::0 does the trick but that would + * fail on hosts that have no IPv6 support. Using the result of this method + * provides an easy way to bind sockets in cases where we simply want any + * IP packets coming on the port we are listening on (regardless of IP + * version). + * + * @return IN6_ADDR_ANY or IN4_ADDR_ANY if this host supports or not IPv6. + */ + private static String determineAnyAddress() + { + Enumeration<NetworkInterface> ifaces; + try + { + ifaces = NetworkInterface.getNetworkInterfaces(); + } + catch (SocketException e) + { + if (logger.isDebugEnabled()) + logger.debug("Couldn't retrieve local interfaces.", e); + return IN4_ADDR_ANY; + } + + while(ifaces.hasMoreElements()) + { + Enumeration<InetAddress> addrs + = ifaces.nextElement().getInetAddresses(); + while (addrs.hasMoreElements()) + { + if(addrs.nextElement() instanceof Inet6Address) + return IN6_ADDR_ANY; + } + } + + return IN4_ADDR_ANY; + } + + /** + * Determines whether <tt>port</tt> is a valid port number bindable by an + * application (i.e. an integer between 1024 and 65535). + * + * @param port the port number that we'd like verified. + * + * @return <tt>true</tt> if port is a valid and bindable port number and + * <tt>alse</tt> otherwise. + */ + public static boolean isValidPortNumber(int port) + { + return MIN_PORT_NUMBER < port && port < MAX_PORT_NUMBER; + } + + /** + * Returns an IPv4 address matching the one mapped in the IPv6 + * <tt>addr</tt>. Both input and returned value are in network order. + * + * @param addr a String representing an IPv4-Mapped address in textual + * format + * + * @return a byte array numerically representing the IPv4 address + */ + public static byte[] mappedIPv4ToRealIPv4(byte[] addr) + { + if (isMappedIPv4Addr(addr)) + { + byte[] newAddr = new byte[IN4_ADDR_SIZE]; + System.arraycopy(addr, 12, newAddr, 0, IN6_ADDR_SIZE); + return newAddr; + } + + return null; + } + + /** + * Utility method to check if the specified <tt>address</tt> is an IPv4 + * mapped IPv6 address. + * + * @param address the address that we'd like to determine as an IPv4 mapped + * one or not. + * + * @return <tt>true</tt> if address is an IPv4 mapped IPv6 address and + * <tt>false</tt> otherwise. + */ + private static boolean isMappedIPv4Addr(byte[] address) + { + if (address.length < IN6_ADDR_SIZE) + { + return false; + } + + if ((address[0] == 0x00) && (address[1] == 0x00) + && (address[2] == 0x00) && (address[3] == 0x00) + && (address[4] == 0x00) && (address[5] == 0x00) + && (address[6] == 0x00) && (address[7] == 0x00) + && (address[8] == 0x00) && (address[9] == 0x00) + && (address[10] == (byte)0xff) + && (address[11] == (byte)0xff)) + { + return true; + } + + return false; + } + + /** + * Creates a new {@link Lookup} instance using our own {@link + * ParallelResolver} if it is enabled and DNSSEC is not active. + * + * @param domain the domain we will be resolving + * @param type the type of the record we will be trying to obtain. + * + * @return the newly created {@link Lookup} instance. + * + * @throws TextParseException if <tt>domain</tt> is not a valid domain name. + */ + private static Lookup createLookup(String domain, int type) + throws TextParseException + { + // listens for network changes up/down so we can reset + // dns configuration + if(netListenerAdded.compareAndSet(false, true)) + { + if(logger.isDebugEnabled()) + logger.debug("NetConfigChange listener added: " + + netListener.hashCode()); + UtilActivator.getNetworkAddressManagerService() + .addNetworkConfigurationChangeListener(netListener); + } + + // make domain name absolute if requested + if(UtilActivator.getConfigurationService().getBoolean( + PNAME_DNS_ALWAYS_ABSOLUTE, + PDEFAULT_DNS_ALWAYS_ABSOLUTE)) + { + if(!Name.fromString(domain).isAbsolute()) + domain = domain + "."; + } + + Lookup lookup = new Lookup(domain, type); + + if(logger.isTraceEnabled()) + { + StringBuilder sb = new StringBuilder(); + sb.append("Active DNS servers in default resolver: "); + for(String s : ResolverConfig.getCurrentConfig().servers()) + { + sb.append(s); + sb.append(", "); + } + logger.trace(sb.toString()); + } + + if(!UtilActivator.getConfigurationService() + .getBoolean(PNAME_BACKUP_RESOLVER_ENABLED, + PDEFAULT_BACKUP_RESOLVER_ENABLED) + || UtilActivator.getConfigurationService().getBoolean( + DnsUtilActivator.PNAME_DNSSEC_RESOLVER_ENABLED, + DnsUtilActivator.PDEFAULT_DNSSEC_RESOLVER_ENABLED + )) + { + return lookup; + } + + //Initiate our global parallel resolver if this is our first ever + //DNS query. The lock here is heavy but necessary as a) the config + //form can cause an intermittent reset and b) multiple accounts signing + //in at the same time could cause multiple ParallelResolver instances + synchronized(parallelResolverLock) + { + if(parallelResolver == null) + { + try + { + String rslvrAddrStr + = UtilActivator.getConfigurationService() + .getString(PNAME_BACKUP_RESOLVER, + DEFAULT_BACKUP_RESOLVER); + String customRslvrIP + = UtilActivator.getConfigurationService().getString( + PNAME_BACKUP_RESOLVER_FALLBACK_IP, + UtilActivator.getResources().getSettingsString( + PNAME_BACKUP_RESOLVER_FALLBACK_IP)); + + InetAddress resolverAddress = null; + + try + { + resolverAddress = getInetAddress(rslvrAddrStr); + } + catch(UnknownHostException exc) + { + logger.warn("Oh! Seems like our primary DNS is down!" + + "Don't panic! We'll try to fall back to " + + customRslvrIP); + } + + if(resolverAddress == null) + { + /* name resolution failed for backup DNS resolver, + * try with the IP address of the default backup resolver + */ + resolverAddress = getInetAddress(customRslvrIP); + } + + int rslvrPort = UtilActivator.getConfigurationService().getInt( + PNAME_BACKUP_RESOLVER_PORT, SimpleResolver.DEFAULT_PORT); + + InetSocketAddress resolverSockAddr + = new InetSocketAddress(resolverAddress, rslvrPort); + + parallelResolver = new ParallelResolver( + new InetSocketAddress[]{resolverSockAddr}); + + //listens for changes on the parallel DNS settings + UtilActivator.getConfigurationService() + .addPropertyChangeListener( + new DnsConfigurationChangeListener()); + } + catch(Throwable t) + { + //We don't want to a problem with our parallel resolver to + //make our entire DNS resolution to fail so in case something + //goes wrong during initialization so we default to the + //dns java default resolver + logger.info("failed to initialize parallel resolver. we will " + +"be using dnsjava's default one instead"); + + if(logger.isDebugEnabled()) + logger.debug("exception was: ", t); + + parallelResolver = Lookup.getDefaultResolver(); + } + } + + lookup.setResolver(parallelResolver); + } + + return lookup; + } + + /** + * Gets the default port used by DNS servers obtained through + * SimpleResolver.DEFAULT_PORT. + * @return The default DNS server port + */ + public static short getDefaultDnsPort() + { + return SimpleResolver.DEFAULT_PORT; + } + + /** + * Listens when network is going from down to up and + * resets dns configuration. + */ + private static class NetworkListener + implements NetworkConfigurationChangeListener + { + /** + * Fired when a change has occurred in the + * computer network configuration. + * + * @param event the change event. + */ + public void configurationChanged(ChangeEvent event) + { + if((event.getType() == ChangeEvent.IFACE_UP + || event.getType() == ChangeEvent.DNS_CHANGE) + && !event.isInitial()) + { + reloadDnsResolverConfig(); + } + } + } + + /** + * Listens for changes in the DNS configuration and resets + * the parallelResolver when necessary + */ + private static class DnsConfigurationChangeListener + implements PropertyChangeListener + { + @SuppressWarnings("serial") + private final Set<String> configNames = new HashSet<String>(5){{ + add(PNAME_BACKUP_RESOLVER); + add(PNAME_BACKUP_RESOLVER_FALLBACK_IP); + add(PNAME_BACKUP_RESOLVER_PORT); + add(ParallelResolver.PNAME_DNS_PATIENCE); + add(ParallelResolver.PNAME_DNS_REDEMPTION); + }}; + + public void propertyChange(PropertyChangeEvent evt) + { + if(configNames.contains(evt.getPropertyName()) && + parallelResolver != null) + { + parallelResolver = null; + logger.info("Parallel DNS resolver reset"); + } + } + } + + /** + * Reloads dns server configuration in the resolver. + */ + public static void reloadDnsResolverConfig() + { + // reread system dns configuration + ResolverConfig.refresh(); + try + { + DnsUtilActivator.refreshResolver(); + } + catch(Throwable t) + { + logger.error("Error reloading dns util activator"); + } + if(parallelResolver instanceof ParallelResolver) + { + //needs a separate lock object because the parallelResolver could + //be set to null in between + synchronized(parallelResolverLock) + { + ((ParallelResolver)parallelResolver).reset(); + } + } + + if(logger.isTraceEnabled()) + { + StringBuilder sb = new StringBuilder(); + sb.append("Reloaded resolver config, active DNS servers are: "); + for(String s : ResolverConfig.getCurrentConfig().servers()) + { + sb.append(s); + sb.append(", "); + } + logger.trace(sb.toString()); + } + } + + /** + * Compares two DNS names against each other. Helper method to avoid the + * export of DNSJava. + * @param dns1 The first DNS name + * @param dns2 The DNS name that is compared against dns1 + * @return The value 0 if dns2 is a name equivalent to dns1; + * a value less than 0 if dns2 is less than dns1 in the canonical ordering, + * and a value greater than 0 if dns2 is greater than dns1 in the canonical + * ordering. + * @throws ParseException if the dns1 or dns2 is not a DNS Name + */ + public static int compareDnsNames(String dns1, String dns2) + throws ParseException + { + try + { + return Name.fromString(dns1).compareTo(Name.fromString(dns2)); + } + catch(TextParseException e) + { + throw new ParseException(e.getMessage(), 0); + } + } + + /** + * Sorts the SRV record list by priority and weight. + * + * @param srvRecords The list of SRV records. + */ + private static void sortSrvRecord(SRVRecord[] srvRecords) + { + // Sort the SRV RRs by priority (lower is preferred). + Arrays.sort(srvRecords, new Comparator<SRVRecord>() + { + public int compare(SRVRecord obj1, SRVRecord obj2) + { + return (obj1.getPriority() - obj2.getPriority()); + } + }); + + // Sort the SRV RRs by weight (larger weight has a proportionately + // higher probability of being selected). + sortSrvRecordByWeight(srvRecords); + } + + /** + * Sorts each priority of the SRV record list. Each priority is sorted with + * the probabilty given by the weight attribute. + * + * @param srvRecords The list of SRV records already sorted by priority. + */ + private static void sortSrvRecordByWeight(SRVRecord[] srvRecords) + { + int currentPriority = srvRecords[0].getPriority(); + int startIndex = 0; + + for(int i = 0; i < srvRecords.length; ++i) + { + if(currentPriority != srvRecords[i].getPriority()) + { + // Sort the current priority. + sortSrvRecordPriorityByWeight(srvRecords, startIndex, i); + // Reinit variables for the next priority. + startIndex = i; + currentPriority = srvRecords[i].getPriority(); + } + } + } + + /** + * Sorts SRV record list for a given priority: this priority is sorted with + * the probabilty given by the weight attribute. + * + * @param srvRecords The list of SRV records already sorted by priority. + * @param startIndex The first index (included) for the current priority. + * @param endIndex The last index (excluded) for the current priority. + */ + private static void sortSrvRecordPriorityByWeight( + SRVRecord[] srvRecords, + int startIndex, + int endIndex) + { + int randomWeight; + + // Loops over the items of the current priority. + while(startIndex < endIndex) + { + // Compute a random number in [0...totalPriorityWeight]. + randomWeight = getRandomWeight(srvRecords, startIndex, endIndex); + + // Move the selected item on top of the unsorted items for this + // priority. + moveSelectedSRVRecord( + srvRecords, + startIndex, + endIndex, + randomWeight); + + // Move to next index. + ++startIndex; + } + } + + /** + * Compute a random number in [0...totalPriorityWeight] with + * totalPriorityWeight the sum of all weight for the current priority. + * + * @param srvRecords The list of SRV records already sorted by priority. + * @param startIndex The first index (included) for the current priority. + * @param endIndex The last index (excluded) for the current priority. + * + * @return A random number in [0...totalPriorityWeight] with + * totalPriorityWeight the sum of all weight for the current priority. + */ + private static int getRandomWeight( + SRVRecord[] srvRecords, + int startIndex, + int endIndex) + { + int totalPriorityWeight = 0; + + // Compute the max born. + for(int i = startIndex; i < endIndex; ++i) + { + totalPriorityWeight += srvRecords[i].getWeight(); + } + + // Compute a random number in [0...totalPriorityWeight]. + return random.nextInt(totalPriorityWeight + 1); + } + + /** + * Moves the selected SRV record in top of the unsorted items for this + * priority. + * + * @param srvRecords The list of SRV records already sorted by priority. + * @param startIndex The first unsorted index (included) for the current + * priority. + * @param endIndex The last unsorted index (excluded) for the current + * priority. + * @param selectedWeight The selected weight used to design the selected + * item to move. + */ + private static void moveSelectedSRVRecord( + SRVRecord[] srvRecords, + int startIndex, + int endIndex, + int selectedWeight) + { + SRVRecord tmpSrvRecord; + int totalPriorityWeight = 0; + + for(int i = startIndex; i < endIndex; ++i) + { + totalPriorityWeight += srvRecords[i].getWeight(); + + // If we found the selecting record. + if(totalPriorityWeight >= selectedWeight) + { + // Switch between startIndex and j. + tmpSrvRecord = srvRecords[startIndex]; + srvRecords[startIndex] = srvRecords[i]; + srvRecords[i] = tmpSrvRecord; + // Break the loop; + return; + } + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/OrderedComponent.java b/src/net/java/sip/communicator/plugin/desktoputil/OrderedComponent.java new file mode 100644 index 0000000..054e31b --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/OrderedComponent.java @@ -0,0 +1,28 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +/** + * Components (like buttons) implement this interface to be able to + * order them in a Ordered Transparent Panels. + * + * @author Damian Minkov + */ +public interface OrderedComponent +{ + /** + * Change component index when we want to order it. + * @param index the button index. + */ + public void setIndex(int index); + + /** + * Returns the current component index we have set, or -1 if none used. + * @return + */ + public int getIndex(); +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/OrderedTransparentPanel.java b/src/net/java/sip/communicator/plugin/desktoputil/OrderedTransparentPanel.java new file mode 100644 index 0000000..21dd398 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/OrderedTransparentPanel.java @@ -0,0 +1,66 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; + +/** + * Ordered transparent panel. Components added to the panel + * must implement OrderedComponent to be able to order them or + * will leave the parent to add them as usual. + * + * @author Damian Minkov + */ +public class OrderedTransparentPanel + extends TransparentPanel +{ + private static final long serialVersionUID = 0L; + + @Override + public Component add(Component comp) + { + if(comp instanceof OrderedComponent) + return addOrdered(comp); + else + return super.add(comp); + } + + /** + * Method to order add OrderedComponents. + * @param comp the component to order. + * @return the component argument + */ + private Component addOrdered(Component comp) + { + int orederIndex = ((OrderedComponent)comp).getIndex(); + + Component[] cs = getComponents(); + + // don't add a component if already added or it will be removed + // and added at the end + for(Component c : cs) + { + if(c.equals(comp)) + return comp; + } + + for(int i = 0; i < cs.length; i++) + { + Component c = cs[i]; + + if(c instanceof OrderedComponent) + { + int cIndex = ((OrderedComponent) c).getIndex(); + + if(orederIndex < cIndex) + return super.add(comp, i); + } + } + + return super.add(comp); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/PasswordChangeDialog.java b/src/net/java/sip/communicator/plugin/desktoputil/PasswordChangeDialog.java new file mode 100644 index 0000000..4e4e773 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/PasswordChangeDialog.java @@ -0,0 +1,385 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.util.*; + +import org.jitsi.service.resources.*; + +/** + * UI dialog to change the master password. + * + * @author Dmitri Melnikov + * @author Boris Grozev + */ +public class PasswordChangeDialog + extends SIPCommDialog + implements KeyListener +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * The <tt>ResourceManagementService</tt> used by this instance to access + * the localized and internationalized resources of the application. + */ + protected final ResourceManagementService resources + = DesktopUtilActivator.getResources(); + + /** + * Password quality meter. + */ + private PasswordQualityMeter passwordMeter = + new PasswordQualityMeter(); + + /** + * Whether to show a current password field or not + */ + private boolean showCurrentPassword = false; + + /** + * UI components. + */ + private JPasswordField currentPasswdField; + private JPasswordField newPasswordField; + private JPasswordField newAgainPasswordField; + private JButton okButton; + private JButton cancelButton; + private JTextArea infoTextArea; + private JProgressBar passwordQualityBar; + private JPanel textFieldsPanel; + private JPanel labelsPanel; + private JPanel buttonsPanel; + private JPanel qualityPanel; + private JPanel dataPanel; + + /** + * Builds the dialog, no current password + */ + public PasswordChangeDialog() + { + this(false); + } + + /** + * Builds the dialog. + * + * @param showCurrentPassword Whether to show a "current password" field + */ + public PasswordChangeDialog(boolean showCurrentPassword) + { + super(false); + + this.showCurrentPassword = showCurrentPassword; + + initComponents(); + + this.setTitle(resources + .getI18NString("service.gui.CHANGE_PASSWORD")); + this.setResizable(false); + + JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); + mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + + mainPanel.add(createIconComponent(), BorderLayout.WEST); + mainPanel.add(dataPanel); + + this.getContentPane().add(mainPanel); + + this.pack(); + Toolkit toolkit = Toolkit.getDefaultToolkit(); + Dimension screenSize = toolkit.getScreenSize(); + + int x = (screenSize.width - this.getWidth()) / 2; + int y = (screenSize.height - this.getHeight()) / 2; + + this.setLocation(x, y); + + if (showCurrentPassword) + { + currentPasswdField.requestFocusInWindow(); + } + else + { + newPasswordField.requestFocusInWindow(); + } + } + + /** + * Initializes the UI components. + */ + private void initComponents() + { + dataPanel = new TransparentPanel(new BorderLayout(10, 10)); + dataPanel.setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 8)); + + // info text + infoTextArea = new JTextArea(); + infoTextArea.setEditable(false); + infoTextArea.setOpaque(false); + infoTextArea.setLineWrap(true); + infoTextArea.setWrapStyleWord(true); + infoTextArea.setFont(infoTextArea.getFont().deriveFont(Font.BOLD)); + infoTextArea.setText(resources + .getI18NString("service.gui.CHANGE_PASSWORD")); + + // label fields + labelsPanel = new TransparentPanel(new GridLayout(0, 1, 8, 8)); + + if(showCurrentPassword) + { + labelsPanel.add(new JLabel(resources.getI18NString( + "plugin.securityconfig.masterpassword.CURRENT_PASSWORD"))); + } + labelsPanel.add(new JLabel(resources.getI18NString( + "plugin.securityconfig.masterpassword.ENTER_PASSWORD"))); + labelsPanel.add(new JLabel(resources.getI18NString( + "plugin.securityconfig.masterpassword.REENTER_PASSWORD"))); + + // password fields + ActionListener clickOkButton = new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + if (okButton.isEnabled()) + okButton.doClick(); + } + }; + + if(showCurrentPassword) + { + currentPasswdField = new JPasswordField(15); + currentPasswdField.addActionListener(clickOkButton); + } + newPasswordField = new JPasswordField(15); + newPasswordField.addKeyListener(this); + newPasswordField.addActionListener(clickOkButton); + newAgainPasswordField = new JPasswordField(15); + newAgainPasswordField.addKeyListener(this); + newAgainPasswordField.addActionListener(clickOkButton); + + textFieldsPanel = new TransparentPanel(new GridLayout(0, 1, 8, 8)); + if(showCurrentPassword) + { + textFieldsPanel.add(currentPasswdField); + } + textFieldsPanel.add(newPasswordField); + textFieldsPanel.add(newAgainPasswordField); + + // OK and cancel buttons + okButton = new JButton(resources.getI18NString("service.gui.OK")); + okButton.setMnemonic(resources.getI18nMnemonic("service.gui.OK")); + okButton.setEnabled(false); + cancelButton + = new JButton(resources.getI18NString("service.gui.CANCEL")); + cancelButton.setMnemonic(resources.getI18nMnemonic( + "service.gui.CANCEL")); + cancelButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + dispose(); + } + }); + + passwordQualityBar = + new JProgressBar(0, PasswordQualityMeter.TOTAL_POINTS); + passwordQualityBar.setValue(0); + + qualityPanel = new TransparentPanel(); + qualityPanel.setLayout(new BoxLayout(qualityPanel, BoxLayout.Y_AXIS)); + + JLabel qualityMeterLabel = new JLabel(resources.getI18NString( + "plugin.securityconfig.masterpassword.PASSWORD_QUALITY_METER")); + qualityMeterLabel.setAlignmentX(CENTER_ALIGNMENT); + + qualityPanel.add(qualityMeterLabel); + qualityPanel.add(passwordQualityBar); + qualityPanel.add(Box.createVerticalStrut(15)); + + buttonsPanel = new TransparentPanel( + new FlowLayout(FlowLayout.RIGHT, 0, 5)); + buttonsPanel.add(okButton); + buttonsPanel.add(cancelButton); + qualityPanel.add(buttonsPanel); + + dataPanel.add(infoTextArea, BorderLayout.NORTH); + dataPanel.add(labelsPanel, BorderLayout.WEST); + dataPanel.add(textFieldsPanel, BorderLayout.CENTER); + dataPanel.add(qualityPanel, BorderLayout.SOUTH); + } + + /** + * Displays an error pop-up. + * + * @param message the message to display + */ + protected void displayPopupError(String message) + { + DesktopUtilActivator + .getUIService() + .getPopupDialog() + .showMessagePopupDialog( + message, + resources.getI18NString( + "service.gui.PASSWORD_CHANGE_FAILURE"), + PopupDialog.ERROR_MESSAGE); + } + + /** + * Displays an info pop-up. + * + * @param message the message to display. + */ + protected void displayPopupInfo(String message) + { + DesktopUtilActivator + .getUIService() + .getPopupDialog() + .showMessagePopupDialog( + message, + resources.getI18NString( + "service.gui.PASSWORD_CHANGE_SUCCESS"), + PopupDialog.INFORMATION_MESSAGE); + } + + protected void close(boolean isEscaped) + { + cancelButton.doClick(); + } + + /** + * When a key is pressed we do 2 things. The first is to compare the two + * password input fields and enable OK button if they are equal. The second + * is to measure the password quality of the password from the first input + * field. + * + * @param event key event + */ + public void keyReleased(KeyEvent event) + { + JPasswordField source = (JPasswordField) event.getSource(); + if (newPasswordField.equals(source) + || newAgainPasswordField.equals(source)) + { + String password1 = new String(newPasswordField.getPassword()); + String password2 = new String(newAgainPasswordField.getPassword()); + // measure password quality + passwordQualityBar + .setValue(passwordMeter.assessPassword(password1)); + // enable OK button if passwords are equal + boolean eq = (password1.length() != 0) + && password1.equals(password2); + okButton.setEnabled(eq); + password1 = null; + password2 = null; + } + } + + /** + * Not overriding. + * + * @param arg0 key event + */ + public void keyPressed(KeyEvent arg0) + { + } + + /** + * Not overriding. + * + * @param arg0 key event + */ + public void keyTyped(KeyEvent arg0) + { + } + + /** + * Creates the icon component to show on the left of this dialog. + * + * @return the created component + */ + private static Component createIconComponent() + { + JPanel wrapIconPanel = new JPanel(new BorderLayout()); + + JLabel iconLabel = new JLabel(); + + iconLabel.setIcon(DesktopUtilActivator.getResources() + .getImage("service.gui.icons.AUTHORIZATION_ICON")); + + wrapIconPanel.add(iconLabel, BorderLayout.NORTH); + + return wrapIconPanel; + } + + /** + * Return a reference to the "ok" button. + * + * @return a reference to the "ok" button. + */ + protected JButton getOkButton() + { + return okButton; + } + + /** + * Return a reference to the "cancel" button. + * + * @return a reference to the "cancel" button. + */ + protected JButton getCancelButton() + { + return cancelButton; + } + + /** + * Return the string entered in the password field. + * + * @return the string entered in the password field. + */ + protected String getNewPassword() + { + return new String(newPasswordField.getPassword()); + } + + /** + * Return the string entered in the "current password" field, or null if + * that field is not shown. + * + * @return the string entered in the "current password" field. + */ + protected String getCurrentPassword() + { + if(currentPasswdField == null) + { + return null; + } + else + { + return new String(currentPasswdField.getPassword()); + } + } + + /** + * Sets the descriptional text that is displayed + * @param infoText the new text to display. + */ + protected void setInfoText(String infoText) + { + infoTextArea.setText(infoText); + } + +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/PopupNotificationPanel.java b/src/net/java/sip/communicator/plugin/desktoputil/PopupNotificationPanel.java new file mode 100644 index 0000000..5963148 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/PopupNotificationPanel.java @@ -0,0 +1,158 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.Logger; +import net.java.sip.communicator.util.skin.*; + +import org.jitsi.util.*; + +/** + * A custom panel to handle systray popup notification + * + * @author Symphorien Wanko + * @author Adam Netocny + */ +public class PopupNotificationPanel + extends SIPCommFrame.MainContentPane + implements Skinnable +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * Logger for this class. + **/ + private final Logger logger = Logger.getLogger(SIPCommFrame.class); + + /** + * An object to distinguish this <tt>PopupNotificationPanel</tt> + */ + private Object tag; + + /** + * Close button. + */ + private final SIPCommButton notifClose; + + /** + * Notification title. + */ + private JLabel notifTitle; + + /** + * Creates a new <tt>PopupNotificationPanel</tt> with a customized panel + * title. + * @param titleString The title of the popup + */ + private PopupNotificationPanel(String titleString) + { + notifTitle = new JLabel( + DesktopUtilActivator.getResources().getSettingsString( + "service.gui.APPLICATION_NAME") + + (StringUtils.isNullOrEmpty(titleString, true) + ? "" + : ": " + titleString), + SwingConstants.LEFT); + + notifClose = new SIPCommButton(); + + notifClose.setToolTipText(DesktopUtilActivator.getResources() + .getI18NString("service.gui.CLOSE")); + + notifClose.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + try + { + Window parentWindow + = SwingUtilities.getWindowAncestor( + PopupNotificationPanel.this); + + parentWindow.dispose(); + } + catch (Exception ex) + { + // should never happens : if the user clicks on the close + // icon, it means that the popup window were visible + logger.warn("Error while getting the popup window :", ex); + } + } + }); + + JPanel notificationWindowTitle + = new JPanel(new BorderLayout(0, 2)); + notificationWindowTitle + .setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); + notificationWindowTitle.setOpaque(false); + notificationWindowTitle.add(notifTitle, BorderLayout.WEST); + notificationWindowTitle.add(notifClose, BorderLayout.EAST); + + JSeparator jSep = new JSeparator(); + + notificationWindowTitle.add(jSep, BorderLayout.SOUTH); + + add(notificationWindowTitle, BorderLayout.NORTH); + setBorder(BorderFactory.createLineBorder(Color.GRAY)); + + // All items are now instantiated and could safely load the skin. + loadSkin(); + } + + /** + * Creates a new notification panel with <tt>notificationContent</tt> as + * the component to put in that panel + * + * @param titleString The title of the popup + * @param notificationContent content to add in the new created + * <tt>PopupNotificationPanel</tt> + * @param tag an object to distinguish this <tt>PopupNotificationPanel</tt> + */ + public PopupNotificationPanel(String titleString, + JPanel notificationContent, Object tag) + { + this(titleString); + add(notificationContent, BorderLayout.CENTER); + this.tag = tag; + } + + /** + * @return the tag + */ + public Object getTag() + { + return tag; + } + + /** + * @param tag the tag to set + */ + public void setTag(Object tag) + { + this.tag = tag; + } + + /** + * Reloads resources for this component. + */ + public void loadSkin() + { + notifTitle.setIcon(DesktopUtilActivator.getResources().getImage( + "service.gui.SIP_COMMUNICATOR_LOGO")); + notifClose.setBackgroundImage(DesktopUtilActivator.getResources() + .getImage("service.gui.lookandfeel.CLOSE_TAB_ICON").getImage()); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/PriorityTable.java b/src/net/java/sip/communicator/plugin/desktoputil/PriorityTable.java new file mode 100644 index 0000000..7ab72e9 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/PriorityTable.java @@ -0,0 +1,170 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.table.*; + +import net.java.sip.communicator.util.*; + +import org.jitsi.service.resources.*; + +/** + * Creates a component containing a table and two buttons for the encodings of + * type(AUDIO or VIDEO) or to sort the priority of the encryption protocols. + * + * @author Vincent Lucas + */ +public class PriorityTable + extends TransparentPanel +{ + /** + * The table containing the different elements to sort by priority. + */ + private JTable table; + + /** + * The button to increase the priority of one item by moving it up in the + * table. + */ + private JButton upButton; + + /** + * The button to decrease the priority of one item by moving it down in the + * table. + */ + private JButton downButton; + + /** + * The preferred width of all panels. + */ + private final static int WIDTH = 350; + + /** + * Creates a component for the encodings of type(AUDIO or VIDEO) or to sort + * the priority of the encryption protocols. + * @param tableModel The table model to display encodings (AUDIO or VIDEO), + * or to sort the priority of the encryption protocols. + * @param height The height (preferred and maximum height) of the component. + * @return the component. + */ + public PriorityTable( + MoveableTableModel tableModel, + int height) + { + super(new BorderLayout()); + + ResourceManagementService resources = DesktopUtilActivator.getResources(); + String key; + String i18NresourcesKey; + + table = new JTable(); + table.setShowGrid(false); + table.setTableHeader(null); + + key = "impl.media.configform.UP"; + upButton = new JButton(resources.getI18NString(key)); + upButton.setMnemonic(resources.getI18nMnemonic(key)); + upButton.setOpaque(false); + + key = "impl.media.configform.DOWN"; + downButton = new JButton(resources.getI18NString(key)); + downButton.setMnemonic(resources.getI18nMnemonic(key)); + downButton.setOpaque(false); + + Container buttonBar = new TransparentPanel(new GridLayout(0, 1)); + buttonBar.add(upButton); + buttonBar.add(downButton); + + Container parentButtonBar = new TransparentPanel(new BorderLayout()); + parentButtonBar.add(buttonBar, BorderLayout.NORTH); + + //Container container = new TransparentPanel(new BorderLayout()); + this.setPreferredSize(new Dimension(WIDTH, height)); + this.setMaximumSize(new Dimension(WIDTH, height)); + + this.add(new JScrollPane(table), BorderLayout.CENTER); + this.add(parentButtonBar, BorderLayout.EAST); + + table.setModel(tableModel); + + /* + * The first column contains the check boxes which enable/disable their + * associated encodings and it doesn't make sense to make it wider than + * the check boxes. + */ + TableColumnModel tableColumnModel = table.getColumnModel(); + TableColumn tableColumn = tableColumnModel.getColumn(0); + tableColumn.setMaxWidth(tableColumn.getMinWidth()); + + ListSelectionListener tableSelectionListener = + new ListSelectionListener() + { + public void valueChanged(ListSelectionEvent event) + { + if (table.getSelectedRowCount() == 1) + { + int selectedRow = table.getSelectedRow(); + if (selectedRow > -1) + { + upButton.setEnabled(selectedRow > 0); + downButton.setEnabled(selectedRow < (table + .getRowCount() - 1)); + return; + } + } + upButton.setEnabled(false); + downButton.setEnabled(false); + } + }; + table.getSelectionModel().addListSelectionListener( + tableSelectionListener); + tableSelectionListener.valueChanged(null); + + ActionListener buttonListener = new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + Object source = event.getSource(); + boolean up; + if (source == upButton) + up = true; + else if (source == downButton) + up = false; + else + return; + + move(up); + } + }; + upButton.addActionListener(buttonListener); + downButton.addActionListener(buttonListener); + } + + /** + * Used to move encoding options. + * @param table the table with encodings + * @param up move direction. + */ + private void move(boolean up) + { + int index = + ((MoveableTableModel) table.getModel()).move(table + .getSelectedRow(), up); + table.getSelectionModel().setSelectionInterval(index, index); + } + + @Override + public void setEnabled(boolean enabled) + { + this.table.setEnabled(enabled); + this.upButton.setEnabled(enabled); + this.downButton.setEnabled(enabled); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SIPCommButton.java b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommButton.java new file mode 100644 index 0000000..ee58c87 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommButton.java @@ -0,0 +1,454 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + +import org.jvnet.lafwidget.animation.*; + +/** + * The <tt>SIPCommButton</tt> is a very flexible <tt>JButton</tt> that allows + * to configure its background, its icon, the look when a mouse is over it, etc. + * + * @author Yana Stamcheva + * @author Adam Netocny + */ +public class SIPCommButton + extends JButton + implements OrderedComponent +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + private Image bgImage; + + private Image pressedImage; + + private Image rolloverImage; + + private Image rolloverIconImage; + + private Image pressedIconImage; + + private Image iconImage; + + /** + * The index of the button, used when we want to order our buttons. + */ + private int index = -1; + + /** + * Creates a button. + */ + public SIPCommButton() + { + this(null); + } + + /** + * Creates a button with custom background image and icon image. + * + * @param bgImage The background image. + * @param rolloverImage The rollover background image. + * @param pressedImage The pressed image. + * @param iconImage The icon. + * @param rolloverIconImage The rollover icon image. + * @param pressedIconImage The pressed icon image. + */ + public SIPCommButton( Image bgImage, + Image rolloverImage, + Image pressedImage, + Image iconImage, + Image rolloverIconImage, + Image pressedIconImage) + { + MouseRolloverHandler mouseHandler = new MouseRolloverHandler(); + + this.addMouseListener(mouseHandler); + this.addMouseMotionListener(mouseHandler); + + /* + * Explicitly remove all borders that may be set from the current look + * and feel. + */ + this.setContentAreaFilled(false); + this.setBorder(null); + + this.bgImage = bgImage; + this.rolloverImage = rolloverImage; + this.pressedImage = pressedImage; + this.rolloverIconImage = rolloverIconImage; + this.pressedIconImage = pressedIconImage; + this.iconImage = iconImage; + + if (bgImage != null) + { + this.setPreferredSize(new Dimension(bgImage.getWidth(null), + bgImage.getHeight(null))); + + this.setIcon(new ImageIcon(this.bgImage)); + } + } + + /** + * Creates a button with custom background image and icon image. + * + * @param bgImage The background image. + * @param pressedImage The pressed image. + * @param iconImage The icon. + */ + public SIPCommButton( Image bgImage, + Image pressedImage, + Image iconImage) + { + this(bgImage, null, pressedImage, iconImage, null, null); + } + + /** + * Creates a button with custom background image. + * + * @param bgImage the background button image + * @param iconImage the icon of this button + */ + public SIPCommButton( Image bgImage, + Image iconImage) + { + this(bgImage, null, iconImage); + } + + /** + * Creates a button with custom background image. + * + * @param bgImage The background button image. + */ + public SIPCommButton(Image bgImage) + { + this(bgImage, null); + } + + /** + * Resets the background image for this button. + * + * @param bgImage the new image to set. + */ + public void setImage(Image bgImage) + { + this.bgImage = bgImage; + + this.repaint(); + } + + /** + * Overrides the <code>paintComponent</code> method of <tt>JButton</tt> to + * paint the button background and icon, and all additional effects of this + * configurable button. + * + * @param g The Graphics object. + */ + protected void paintComponent(Graphics g) + { + g = g.create(); + try + { + internalPaintComponent(g); + } + finally + { + g.dispose(); + } + } + + /** + * Paints this button. + * @param g the <tt>Graphics</tt> object used for painting + */ + private void internalPaintComponent(Graphics g) + { + AntialiasingManager.activateAntialiasing(g); + /* + * As JComponent#paintComponent says, if you do not invoke super's + * implementation you must honor the opaque property, that is if this + * component is opaque, you must completely fill in the background in a + * non-opaque color. If you do not honor the opaque property you will + * likely see visual artifacts. + */ + if (isOpaque()) + { + g.setColor(getBackground()); + g.fillRect(0, 0, getWidth(), getHeight()); + } + + if (this.bgImage != null) + { + // If there's no icon, we make grey the backgroundImage + // when disabled. + Image paintBgImage; + if (this.iconImage == null && !isEnabled()) + { + paintBgImage = new ImageIcon(LightGrayFilter + .createDisabledImage(bgImage)).getImage(); + } + else + paintBgImage = bgImage; + + g.drawImage(paintBgImage, + this.getWidth()/2 - paintBgImage.getWidth(null)/2, + this.getHeight()/2 - paintBgImage.getHeight(null)/2, + this); + } + + // Paint a roll over fade out. + if (rolloverImage == null) + { + FadeTracker fadeTracker = FadeTracker.getInstance(); + + float visibility = this.getModel().isRollover() ? 1.0f : 0.0f; + if (fadeTracker.isTracked(this, FadeKind.ROLLOVER)) + { + visibility = fadeTracker.getFade(this, FadeKind.ROLLOVER); + } + + visibility /= 2; + + g.setColor(new Color(1.0f, 1.0f, 1.0f, visibility)); + + if (this.bgImage == null + && (isContentAreaFilled() || (visibility != 0.0f))) + { + g.fillRoundRect( + 0, 0, this.getWidth(), this.getHeight(), 8, 8); + } + } + + // Paint pressed state. + if (this.getModel().isPressed() && this.pressedImage != null) + { + g.drawImage(this.pressedImage, 0, 0, this); + } + else if (this.getModel().isRollover() && this.rolloverImage != null) + { + g.drawImage(this.rolloverImage, 0, 0, this); + } + + Image paintIconImage = null; + if (getModel().isPressed() && pressedIconImage != null) + { + paintIconImage = pressedIconImage; + } + else if (this.getModel().isRollover() && rolloverIconImage != null) + { + paintIconImage = rolloverIconImage; + } + else if (this.iconImage != null) + { + if (!isEnabled()) + { + paintIconImage = new ImageIcon(LightGrayFilter + .createDisabledImage(iconImage)).getImage(); + } + else + paintIconImage = iconImage; + } + + if (paintIconImage != null) + g.drawImage(paintIconImage, + this.getWidth()/2 - paintIconImage.getWidth(null)/2, + this.getHeight()/2 - paintIconImage.getHeight(null)/2, + this); + } + + /** + * Returns the background image of this button. + * + * @return the background image of this button. + */ + public Image getBackgroundImage() + { + return bgImage; + } + + /** + * Sets the background image of this button. + * + * @param bgImage the background image of this button. + */ + public void setBackgroundImage(Image bgImage) + { + this.bgImage = bgImage; + + if (bgImage != null) + { + this.setPreferredSize(new Dimension(bgImage.getWidth(null), + bgImage.getHeight(null))); + + this.setIcon(new ImageIcon(this.bgImage)); + } + } + + /** + * Sets the rollover background image of this button. + * + * @param rolloverImage the rollover background image of this button. + */ + public void setRolloverImage(Image rolloverImage) + { + this.rolloverImage = rolloverImage; + } + + /** + * Sets the pressed image of this button. + * + * @param pressedImage the pressed image of this button. + */ + public void setPressedImage(Image pressedImage) + { + this.pressedImage = pressedImage; + } + + /** + * Sets the rollover icon image of this button. + * + * @param rolloverIconImage the rollover icon image of this button. + */ + public void setRolloverIcon(Image rolloverIconImage) + { + this.rolloverIconImage = rolloverIconImage; + } + + /** + * Sets the pressed icon image of this button. + * + * @param pressedIconImage the pressed icon image of this button. + */ + public void setPressedIcon(Image pressedIconImage) + { + this.pressedIconImage = pressedIconImage; + } + + /** + * Sets the icon image of this button. + * + * @param iconImage the icon image of this button. + */ + public void setIconImage(Image iconImage) + { + this.iconImage = iconImage; + } + + /** + * Change buttons index when we want to order it. + * @param index the button index. + */ + public void setIndex(int index) + { + this.index = index; + } + + /** + * Returns the current button index we have set, or -1 if none used. + * @return + */ + public int getIndex() + { + return this.index; + } + + /** + * The <tt>ButtonRepaintCallback</tt> is charged to repaint this button + * when the fade animation is performed. + */ + private class ButtonRepaintCallback implements FadeTrackerCallback + { + public void fadeEnded(FadeKind arg0) + { + repaintLater(); + } + + public void fadePerformed(FadeKind arg0, float arg1) + { + repaintLater(); + } + + private void repaintLater() + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + SIPCommButton.this.repaint(); + } + }); + } + + public void fadeReversed(FadeKind arg0, boolean arg1, float arg2) + { + } + } + + /** + * Perform a fade animation on mouse over. + */ + private class MouseRolloverHandler + implements MouseListener, + MouseMotionListener + { + public void mouseMoved(MouseEvent e) + { + } + + public void mouseExited(MouseEvent e) + { + if (isEnabled()) + { + getModel().setRollover(false); + + FadeTracker fadeTracker = FadeTracker.getInstance(); + + fadeTracker.trackFadeOut(FadeKind.ROLLOVER, + SIPCommButton.this, + true, + new ButtonRepaintCallback()); + } + } + + public void mouseClicked(MouseEvent e) + { + } + + public void mouseEntered(MouseEvent e) + { + if (isEnabled()) + { + getModel().setRollover(true); + + FadeTracker fadeTracker = FadeTracker.getInstance(); + + fadeTracker.trackFadeIn(FadeKind.ROLLOVER, + SIPCommButton.this, + true, + new ButtonRepaintCallback()); + } + } + + public void mousePressed(MouseEvent e) + { + } + + public void mouseReleased(MouseEvent e) + { + } + + public void mouseDragged(MouseEvent e) + { + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SIPCommCheckBox.java b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommCheckBox.java new file mode 100644 index 0000000..a7770d4 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommCheckBox.java @@ -0,0 +1,48 @@ +/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.desktoputil;
+
+import javax.swing.*;
+
+import org.jitsi.util.*;
+
+/**
+ * @author Lubomir Marinov
+ */
+public class SIPCommCheckBox
+ extends JCheckBox
+{
+ private static final long serialVersionUID = 0L;
+
+ private static final boolean setContentAreaFilled = (OSUtils.IS_WINDOWS
+ || OSUtils.IS_LINUX);
+
+ public SIPCommCheckBox()
+ {
+ init();
+ }
+
+ public SIPCommCheckBox(String text)
+ {
+ super(text);
+
+ init();
+ }
+
+ public SIPCommCheckBox(String text, boolean selected)
+ {
+ super(text, selected);
+
+ init();
+ }
+
+ private void init()
+ {
+ if (setContentAreaFilled)
+ setContentAreaFilled(false);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SIPCommDialog.java b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommDialog.java new file mode 100644 index 0000000..50d99f9 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommDialog.java @@ -0,0 +1,446 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + +import net.java.sip.communicator.util.Logger; + +import org.jitsi.service.configuration.*; +import org.jitsi.util.*; + +/** + * @author Yana Stamcheva + * @author Lubomir Marinov + */ +public class SIPCommDialog + extends JDialog +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * The <tt>Logger</tt> used by the <tt>SIPCommDialog</tt> class and its + * instances for logging output. + */ + private static final Logger logger = Logger.getLogger(SIPCommDialog.class); + + /** + * The action map of this dialog. + */ + private ActionMap amap; + + /** + * The input map of this dialog. + */ + private InputMap imap; + + /** + * Indicates if the size and location of this dialog are stored after + * closing. + */ + private boolean isSaveSizeAndLocation = true; + + /** + * Creates an instance of <tt>SIPCommDialog</tt>. + */ + public SIPCommDialog() + { + super(); + + this.init(); + } + + /** + * Creates an instance of <tt>SIPCommDialog</tt> by specifying the + * <tt>Dialog</tt>owner of this dialog. + * @param owner the owner of this dialog + */ + public SIPCommDialog(Dialog owner) + { + super(owner); + + this.init(); + } + + /** + * Creates an instance of <tt>SIPCommDialog</tt> by specifying the + * <tt>Frame</tt> owner. + * @param owner the owner of this dialog + */ + public SIPCommDialog(Frame owner) + { + super(owner); + + this.init(); + } + + /** + * Creates an instance of <tt>SIPCommDialog</tt> by specifying explicitly + * if the size and location properties are saved. By default size and + * location are stored. + * @param isSaveSizeAndLocation indicates whether to save the size and + * location of this dialog + */ + public SIPCommDialog(boolean isSaveSizeAndLocation) + { + this(); + + this.isSaveSizeAndLocation = isSaveSizeAndLocation; + } + + /** + * Creates an instance of <tt>SIPCommDialog</tt> by specifying the owner + * of this dialog and indicating whether to save the size and location + * properties. + * @param owner the owner of this dialog + * @param isSaveSizeAndLocation indicates whether to save the size and + * location of this dialog + */ + public SIPCommDialog(Dialog owner, boolean isSaveSizeAndLocation) + { + this(owner); + + this.isSaveSizeAndLocation = isSaveSizeAndLocation; + } + + /** + * Creates an instance of <tt>SIPCommDialog</tt> by specifying the owner + * of this dialog and indicating whether to save the size and location + * properties. + * @param owner the owner of this dialog + * @param isSaveSizeAndLocation indicates whether to save the size and + * location of this dialog + */ + public SIPCommDialog(Frame owner, boolean isSaveSizeAndLocation) + { + this(owner); + + this.isSaveSizeAndLocation = isSaveSizeAndLocation; + } + + /** + * Initializes this dialog. + */ + private void init() + { + // If on MacOS we would use the native background. + if (!OSUtils.IS_MAC) + this.setContentPane(new SIPCommFrame.MainContentPane()); + + this.addWindowListener(new DialogWindowAdapter()); + + this.initInputMap(); + + WindowUtils.addWindow(this); + } + + private void initInputMap() + { + amap = this.getRootPane().getActionMap(); + + amap.put("close", new CloseAction()); + + imap = this.getRootPane().getInputMap( + JComponent.WHEN_IN_FOCUSED_WINDOW); + + imap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "close"); + + // put the defaults for macosx + if(OSUtils.IS_MAC) + { + imap.put( + KeyStroke.getKeyStroke(KeyEvent.VK_W, InputEvent.META_DOWN_MASK), + "close"); + imap.put( + KeyStroke.getKeyStroke(KeyEvent.VK_W, InputEvent.CTRL_DOWN_MASK), + "close"); + } + } + + /** + * The action invoked when user presses Escape key. + */ + private class CloseAction extends UIAction + { + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + public void actionPerformed(ActionEvent e) + { + if(isSaveSizeAndLocation) + saveSizeAndLocation(); + + close(true); + } + } + + /** + * Adds a key - action pair for this frame. + * + * @param keyStroke the key combination + * @param action the action which will be executed when user presses the + * given key combination + */ + protected void addKeyBinding(KeyStroke keyStroke, Action action) + { + String actionID = action.getClass().getName(); + + amap.put(actionID, action); + + imap.put(keyStroke, actionID); + } + + /** + * Before closing the application window saves the current size and position + * through the <tt>ConfigurationService</tt>. + */ + public class DialogWindowAdapter extends WindowAdapter + { + /** + * Invoked when this window is in the process of being closed. + * @param e the <tt>WindowEvent</tt> that notified us + */ + public void windowClosing(WindowEvent e) + { + if(isSaveSizeAndLocation) + saveSizeAndLocation(); + + close(false); + } + } + + /** + * Saves the size and the location of this dialog through the + * <tt>ConfigurationService</tt>. + */ + private void saveSizeAndLocation() + { + try + { + SIPCommFrame.saveSizeAndLocation(this); + } + catch (ConfigPropertyVetoException e1) + { + logger.error("The proposed property change " + + "represents an unacceptable value"); + } + } + + /** + * Sets window size and position. + */ + private void setSizeAndLocation() + { + ConfigurationService config = DesktopUtilActivator.getConfigurationService(); + String className = this.getClass().getName().replaceAll("\\$", "_"); + + int width = config.getInt(className + ".width", 0); + int height = config.getInt(className + ".height", 0); + + String xString = config.getString(className + ".x"); + String yString = config.getString(className + ".y"); + + if(width > 0 && height > 0) + this.setSize(width, height); + + if(xString != null && yString != null) + { + int x = Integer.parseInt(xString); + int y = Integer.parseInt(yString); + if(ScreenInformation. + isTitleOnScreen(new Rectangle(x, y, width, height)) + || config.getBoolean( + SIPCommFrame.PNAME_CALCULATED_POSITIONING, true)) + { + this.setLocation(x, y); + } + } + else + this.setCenterLocation(); + } + + /** + * Positions this window in the center of the screen. + */ + private void setCenterLocation() + { + setLocationRelativeTo(getParent()); + } + + /** + * Checks whether the current component will + * exceeds the screen size and if it do will set a default size + */ + private void ensureOnScreenLocationAndSize() + { + ConfigurationService config = DesktopUtilActivator.getConfigurationService(); + if(!config.getBoolean(SIPCommFrame.PNAME_CALCULATED_POSITIONING, true)) + return; + + int x = this.getX(); + int y = this.getY(); + + int width = this.getWidth(); + int height = this.getHeight(); + + Rectangle virtualBounds = ScreenInformation.getScreenBounds(); + + // the default distance to the screen border + final int borderDistance = 10; + + // in case any of the sizes exceeds the screen size + // we set default one + // get the left upper point of the window + if (!(virtualBounds.contains(x, y))) + { + // top left exceeds screen bounds + if (x < virtualBounds.x) + { + // window is too far to the left + // move it to the right + x = virtualBounds.x + borderDistance; + } else if (x > virtualBounds.x) + { + // window is too far to the right + // can only occour, when screen resolution is + // changed or displayed are disconnected + + // move the window in the bounds to the very right + x = virtualBounds.x + virtualBounds.width - width + - borderDistance; + if (x < virtualBounds.x + borderDistance) + { + x = virtualBounds.x + borderDistance; + } + } + + // top left exceeds screen bounds + if (y < virtualBounds.y) + { + // window is too far to the top + // move it to the bottom + y = virtualBounds.y + borderDistance; + } else if (y > virtualBounds.y) + { + // window is too far to the bottom + // can only occour, when screen resolution is + // changed or displayed are disconnected + + // move the window in the bounds to the very bottom + y = virtualBounds.y + virtualBounds.height - height + - borderDistance; + if (y < virtualBounds.y + borderDistance) + { + y = virtualBounds.y + borderDistance; + } + } + this.setLocation(x, y); + } + + // check the lower right corder + if (!(virtualBounds.contains(x, y, width, height))) + { + if (x + width > virtualBounds.x + virtualBounds.width) + { + // location of window is too far to the right, its right + // border is out of bounds + + // calculate a new horizontal position + // move the whole window to the left + x = virtualBounds.x + virtualBounds.width - width + - borderDistance; + if (x < virtualBounds.x + borderDistance) + { + // window is already on left side, it is too wide. + x = virtualBounds.x + borderDistance; + // reduce the width, so it surely fits + width = virtualBounds.width - 2 * borderDistance; + } + } + if (y + height > virtualBounds.y + virtualBounds.height) + { + // location of window is too far to the bottom, its bottom + // border is out of bounds + + // calculate a new vertical position + // move the whole window to the top + y = virtualBounds.y + virtualBounds.height - height + - borderDistance; + if (y < virtualBounds.y + borderDistance) + { + // window is already on top, it is too high. + y = virtualBounds.y + borderDistance; + // reduce the width, so it surely fits + height = virtualBounds.height - 2 * borderDistance; + } + } + this.setPreferredSize(new Dimension(width, height)); + this.setSize(width, height); + this.setLocation(x, y); + } + } + + /** + * Overwrites the setVisible method in order to set the size and the + * position of this window before showing it. + * @param isVisible indicates if the dialog should be visible + */ + public void setVisible(boolean isVisible) + { + if(isVisible) + { + this.pack(); + + if(isSaveSizeAndLocation) + this.setSizeAndLocation(); + else + { + this.pack(); + this.setCenterLocation(); + } + + ensureOnScreenLocationAndSize(); + + JButton button = this.getRootPane().getDefaultButton(); + + if(button != null) + button.requestFocus(); + } + super.setVisible(isVisible); + } + + /** + * Overwrites the dispose method in order to save the size and the position + * of this window before closing it. + */ + public void dispose() + { + if(isSaveSizeAndLocation) + this.saveSizeAndLocation(); + + super.dispose(); + } + + /** + * All functions implemented in this method will be invoked when user + * presses the Escape key. + * + * @param escaped <tt>true</tt> if this frame has been closed by pressing + * the Esc key; otherwise, <tt>false</tt> + */ + protected void close(boolean escaped) + { + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SIPCommFrame.java b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommFrame.java new file mode 100644 index 0000000..e3fda87 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommFrame.java @@ -0,0 +1,835 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.event.*; +import java.awt.image.*; +import java.lang.reflect.*; +import java.net.*; +import java.util.*; +import java.util.List; + +import javax.swing.*; + +import net.java.sip.communicator.service.keybindings.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.Logger; + +import org.jitsi.service.configuration.*; +import org.jitsi.service.resources.*; +import org.jitsi.util.*; + +/** + * A custom frame that remembers its size and location and could have a + * semi-transparent background. + * + * @author Yana Stamcheva + * @author Lyubomir Marinov + * @author Adam Netocny + */ +public class SIPCommFrame + extends JFrame + implements Observer +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * Property that disables the automatic resizing and positioning when a + * window's top edge is outside the visible area of the screen. + * <p> + * <tt>true</tt> use automatic repositioning (default)<br/> + * <tt>false</tt> rely on the window manager to place the window + */ + static final String PNAME_CALCULATED_POSITIONING + = "net.sip.communicator.util.swing.USE_CALCULATED_POSITIONING"; + + /** + * The <tt>Logger</tt> used by the <tt>SIPCommFrame</tt> class and its + * instances for logging output. + */ + private static final Logger logger = Logger.getLogger(SIPCommFrame.class); + + /** + * The action map of this dialog. + */ + private ActionMap amap; + + /** + * The input map of this dialog. + */ + private InputMap imap; + + /** + * The key bindings set. + */ + private KeybindingSet bindings = null; + + /** + * Indicates if the size and location of this dialog are stored after + * closing. By default we store window size and location. + */ + private boolean isSaveSizeAndLocation = true; + + /** + * Creates a <tt>SIPCommFrame</tt>. + */ + public SIPCommFrame() + { + // If on MacOS we would use the native background. + if (!OSUtils.IS_MAC) + setContentPane(new MainContentPane()); + + init(); + + addWindowListener(new FrameWindowAdapter()); + + JRootPane rootPane = getRootPane(); + amap = rootPane.getActionMap(); + amap.put("close", new CloseAction()); + amap.put("closeEsc", new CloseEscAction()); + + imap = rootPane.getInputMap( + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + + imap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "closeEsc"); + + // put the defaults for macosx + if(OSUtils.IS_MAC) + { + imap.put( + KeyStroke.getKeyStroke(KeyEvent.VK_W, InputEvent.META_DOWN_MASK), + "closeEsc"); + imap.put( + KeyStroke.getKeyStroke(KeyEvent.VK_W, InputEvent.CTRL_DOWN_MASK), + "closeEsc"); + } + + WindowUtils.addWindow(this); + } + + /** + * Validates this container and all of its subcomponents. + * <p> + * The <code>validate</code> method is used to cause a container + * to lay out its subcomponents again. It should be invoked when + * this container's subcomponents are modified (added to or + * removed from the container, or layout-related information + * changed) after the container has been displayed. + * + * <p>If this {@code Container} is not valid, this method invokes + * the {@code validateTree} method and marks this {@code Container} + * as valid. Otherwise, no action is performed. + * + * @see #add(java.awt.Component) + * @see Component#invalidate + * @see javax.swing.JComponent#revalidate() + * @see #validateTree + */ + @Override + public void validate() + { + init(); + super.validate(); + } + + /** + * Initialize default values. + */ + private void init() + { + try + { + Method m = Window.class.getMethod("setIconImages", List.class); + List<Image> logos = new ArrayList<Image>(6) + { + private static final long serialVersionUID = 0L; + { + add(DesktopUtilActivator.getImage( + "service.gui.SIP_COMMUNICATOR_LOGO")); + add(DesktopUtilActivator.getImage( + "service.gui.SIP_COMMUNICATOR_LOGO_20x20")); + add(DesktopUtilActivator.getImage( + "service.gui.SIP_COMMUNICATOR_LOGO_32x32")); + add(DesktopUtilActivator.getImage( + "service.gui.SIP_COMMUNICATOR_LOGO_45x45")); + add(DesktopUtilActivator.getImage( + "service.gui.SIP_COMMUNICATOR_LOGO_64x64")); + add(DesktopUtilActivator.getImage( + "service.gui.SIP_COMMUNICATOR_LOGO_128x128")); + } + }; + m.invoke(this, logos); + // In order to have the same icon when using option panes + m.invoke(JOptionPane.getRootFrame(), logos); + } + catch (Exception e) + { + Image scLogo + = DesktopUtilActivator.getImage("service.gui.SIP_COMMUNICATOR_LOGO"); + + setIconImage(scLogo); + // In order to have the same icon when using option panes + JOptionPane.getRootFrame().setIconImage(scLogo); + } + } + + /** + * Creates an instance of <tt>SIPCommFrame</tt> by specifying explicitly + * if the size and location properties are saved. By default size and + * location are stored. + * @param isSaveSizeAndLocation indicates whether to save the size and + * location of this dialog + */ + public SIPCommFrame(boolean isSaveSizeAndLocation) + { + this(); + + this.isSaveSizeAndLocation = isSaveSizeAndLocation; + } + + /** + * The action invoked when user presses Ctrl-W and Cmd-W key combination. + */ + private class CloseAction + extends UIAction + { + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + public void actionPerformed(ActionEvent e) + { + if (isSaveSizeAndLocation) + saveSizeAndLocation(); + close(false); + } + } + + /** + * The action invoked when user presses Escape key. + */ + private class CloseEscAction + extends UIAction + { + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + public void actionPerformed(ActionEvent e) + { + if (isSaveSizeAndLocation) + saveSizeAndLocation(); + close(true); + } + } + + /** + * Sets the input map to utilize a given category of keybindings. The frame + * is updated to reflect the new bindings when they change. This replaces + * any previous bindings that have been added. + * + * @param category set of keybindings to be utilized + */ + protected void setKeybindingInput(KeybindingSet.Category category) + { + // Removes old binding set + if (bindings != null) + { + bindings.deleteObserver(this); + resetInputMap(); + } + + // Adds new bindings to input map + bindings + = DesktopUtilActivator.getKeybindingsService().getBindings(category); + + if (bindings != null) + { + for (Map.Entry<KeyStroke, String> key2action + : bindings.getBindings().entrySet()) + imap.put(key2action.getKey(), key2action.getValue()); + + bindings.addObserver(this); + } + } + + /** + * Bindings the string representation for a keybinding to the action that + * will be executed. + * + * @param binding string representation of action used by input map + * @param action the action which will be executed when user presses the + * given key combination + */ + protected void addKeybindingAction(String binding, Action action) + { + amap.put(binding, action); + } + + private static class FrameWindowAdapter + extends WindowAdapter + { + @Override + public void windowClosing(WindowEvent e) + { + ((SIPCommFrame) e.getWindow()).windowClosing(e); + } + } + + /** + * Invoked when this window is in the process of being closed. The close + * operation can be overridden at this point. + * @param e the <tt>WindowEvent</tt> that notified us + */ + protected void windowClosing(WindowEvent e) + { + /* + * Before closing the application window save the current size and + * position through the ConfigurationService. + */ + if(isSaveSizeAndLocation) + saveSizeAndLocation(); + + close(false); + } + + /** + * Invokes the {@link Window#dispose()} implementation of this instance + * thus skipping any overriding that may be in effect for the method in + * question by extenders. + */ + protected void windowDispose() + { + super.dispose(); + } + + /** + * Saves the size and the location of this frame through the + * <tt>ConfigurationService</tt>. + */ + private void saveSizeAndLocation() + { + try + { + saveSizeAndLocation(this); + } + catch (ConfigPropertyVetoException e) + { + logger + .error( + "Saving the size and the location properties failed", + e); + } + } + + /** + * Saves the size and the location of a specific <tt>Component</tt> through + * the <tt>ConfigurationService</tt>. + * + * @param component the <tt>Component</tt> which is to have its size and + * location saved through the <tt>ConfigurationService</tt> + * @throws ConfigPropertyVetoException if the <tt>ConfigurationService</tt> + * does not accept the saving because of objections from its + * <tt>PropertyVetoListener</tt>s. + */ + static void saveSizeAndLocation(Component component) + throws ConfigPropertyVetoException + { + Map<String, Object> props = new HashMap<String, Object>(); + String className + = component.getClass().getName().replaceAll("\\$", "_"); + + props.put(className + ".width", component.getWidth()); + props.put(className + ".height", component.getHeight()); + props.put(className + ".x", component.getX()); + props.put(className + ".y", component.getY()); + DesktopUtilActivator.getConfigurationService().setProperties(props); + } + + /** + * Sets window size and position. + */ + public void setSizeAndLocation() + { + ConfigurationService configService = + DesktopUtilActivator.getConfigurationService(); + String className = this.getClass().getName(); + String widthString = configService.getString(className + ".width"); + String heightString = configService.getString(className + ".height"); + String xString = configService.getString(className + ".x"); + String yString = configService.getString(className + ".y"); + + int width = 0; + int height = 0; + + if (widthString != null && heightString != null) + { + width = Integer.parseInt(widthString); + height = Integer.parseInt(heightString); + + if (width > 0 && height > 0) + { + Dimension screenSize = + Toolkit.getDefaultToolkit().getScreenSize(); + if (width <= screenSize.width && height <= screenSize.height) + this.setSize(width, height); + } + } + + int x = 0; + int y = 0; + + if (xString != null && yString != null) + { + x = Integer.parseInt(xString); + y = Integer.parseInt(yString); + + if(ScreenInformation. + isTitleOnScreen(new Rectangle(x, y, width, height)) + || configService.getBoolean( + SIPCommFrame.PNAME_CALCULATED_POSITIONING, true)) + { + this.setLocation(x, y); + } + } + else + { + this.setCenterLocation(); + } + } + + /** + * Positions this window in the center of the screen. + */ + private void setCenterLocation() + { + setLocationRelativeTo(null); + } + + /** + * Checks whether the current component will exceeds the screen size and if + * it do will set a default size + */ + private void ensureOnScreenLocationAndSize() + { + ConfigurationService config = DesktopUtilActivator.getConfigurationService(); + if(!config.getBoolean(SIPCommFrame.PNAME_CALCULATED_POSITIONING, true)) + return; + + int x = this.getX(); + int y = this.getY(); + + int width = this.getWidth(); + int height = this.getHeight(); + + Rectangle virtualBounds = ScreenInformation.getScreenBounds(); + + // the default distance to the screen border + final int borderDistance = 10; + + // in case any of the sizes exceeds the screen size + // we set default one + // get the left upper point of the window + if (!(virtualBounds.contains(x, y))) + { + // top left exceeds screen bounds + if (x < virtualBounds.x) + { + // window is too far to the left + // move it to the right + x = virtualBounds.x + borderDistance; + } + else if (x > virtualBounds.x) + { + // window is too far to the right + // can only occour, when screen resolution is + // changed or displayed are disconnected + + // move the window in the bounds to the very right + x = + virtualBounds.x + virtualBounds.width - width + - borderDistance; + if (x < virtualBounds.x + borderDistance) + { + x = virtualBounds.x + borderDistance; + } + } + + // top left exceeds screen bounds + if (y < virtualBounds.y) + { + // window is too far to the top + // move it to the bottom + y = virtualBounds.y + borderDistance; + } + else if (y > virtualBounds.y) + { + // window is too far to the bottom + // can only occour, when screen resolution is + // changed or displayed are disconnected + + // move the window in the bounds to the very bottom + y = + virtualBounds.y + virtualBounds.height - height + - borderDistance; + if (y < virtualBounds.y + borderDistance) + { + y = virtualBounds.y + borderDistance; + } + } + this.setLocation(x, y); + } + + // check the lower right corder + if (!(virtualBounds.contains(x + width, y + height))) + { + + if (x + width > virtualBounds.x + virtualBounds.width) + { + // location of window is too far to the right, its right + // border is out of bounds + + // calculate a new horizontal position + // move the whole window to the left + x = + virtualBounds.x + virtualBounds.width - width + - borderDistance; + if (x < virtualBounds.x + borderDistance) + { + // window is already on left side, it is too wide. + x = virtualBounds.x + borderDistance; + // reduce the width, so it surely fits + width = virtualBounds.width - 2 * borderDistance; + } + } + if (y + height > virtualBounds.y + virtualBounds.height) + { + // location of window is too far to the bottom, its bottom + // border is out of bounds + + // calculate a new vertical position + // move the whole window to the top + y = + virtualBounds.y + virtualBounds.height - height + - borderDistance; + if (y < virtualBounds.y + borderDistance) + { + // window is already on top, it is too high. + y = virtualBounds.y + borderDistance; + // reduce the width, so it surely fits + height = virtualBounds.height - 2 * borderDistance; + } + } + this.setPreferredSize(new Dimension(width, height)); + this.setSize(width, height); + this.setLocation(x, y); + } + } + + /** + * Overwrites the setVisible method in order to set the size and the + * position of this window before showing it. + * @param isVisible indicates if this frame should be visible + */ + @Override + public void setVisible(boolean isVisible) + { + if (isVisible) + { + this.setSizeAndLocation(); + + this.ensureOnScreenLocationAndSize(); + } + + super.setVisible(isVisible); + } + + /** + * Overwrites the setVisible method in order to set the size and the + * position of this window before showing it. + * @param isVisible indicates if this window will be made visible or will + * be hidden + * @param isPackEnabled indicates if the pack() method should be invoked + * before showing this window + */ + public void setVisible(boolean isVisible, boolean isPackEnabled) + { + if (isVisible) + { + /* + * Since setSizeAndLocation() will use the width and the height, + * pack() should be called prior to it. Otherwise, the width and the + * height may be zero or may just change after setSizeAndLocation() + * during pack(). + */ + this.pack(); + this.setSizeAndLocation(); + this.ensureOnScreenLocationAndSize(); + } + + super.setVisible(isVisible); + } + + /** + * {@inheritDoc} + * + * Overwrites the super's <tt>dispose</tt> method in order to save the size + * and the position of this <tt>Window</tt> before closing it. + */ + @Override + public void dispose() + { + if (isSaveSizeAndLocation) + saveSizeAndLocation(); + + /* + * The KeybindingsService will outlive us so don't let us retain our + * memory. + */ + if (bindings != null) + bindings.deleteObserver(this); + + super.dispose(); + } + + private void resetInputMap() + { + imap.clear(); + imap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "close"); + } + + /** + * Listens for changes in binding sets so they can be reflected in the input + * map. + * @param obs the <tt>KeybindingSet</tt> from which to update + */ + public void update(Observable obs, Object arg) + { + if (obs instanceof KeybindingSet) + { + KeybindingSet changedBindings = (KeybindingSet) obs; + + resetInputMap(); + for (Map.Entry<KeyStroke, String> key2action : changedBindings + .getBindings().entrySet()) + { + imap.put(key2action.getKey(), key2action.getValue()); + } + } + } + + /** + * The main content pane. + */ + public static class MainContentPane + extends JPanel + { + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + private boolean isColorBgEnabled; + + private boolean isImageBgEnabled; + + private Color bgStartColor; + + private Color bgEndColor; + + private BufferedImage bgImage = null; + + private TexturePaint texture = null; + + /** + * Creates an instance of <tt>MainContentPane</tt>. + */ + public MainContentPane() + { + super(new BorderLayout()); + + initColors(); + initStyles(); + } + + /** + * Validates this container and all of its subcomponents. + * <p> + * The <code>validate</code> method is used to cause a container + * to lay out its subcomponents again. It should be invoked when + * this container's subcomponents are modified (added to or + * removed from the container, or layout-related information + * changed) after the container has been displayed. + * + * <p>If this {@code Container} is not valid, this method invokes + * the {@code validateTree} method and marks this {@code Container} + * as valid. Otherwise, no action is performed. + * + * @see #add(java.awt.Component) + * @see Component#invalidate + * @see javax.swing.JComponent#revalidate() + * @see #validateTree + */ + @Override + public void validate() + { + initStyles(); + super.validate(); + } + + /** + * Repaints this component. + */ + @Override + public void repaint() + { + initColors(); + super.repaint(); + } + + /** + * Initialize color values. + */ + private void initColors() + { + ResourceManagementService resources = + DesktopUtilActivator.getResources(); + + isColorBgEnabled = + new Boolean(resources.getSettingsString( + "impl.gui.IS_WINDOW_COLOR_BACKGROUND_ENABLED")) + .booleanValue(); + + if (isColorBgEnabled) + { + bgStartColor = + new Color(resources.getColor("service.gui.MAIN_BACKGROUND")); + bgEndColor = + new Color(resources + .getColor("service.gui.MAIN_BACKGROUND_GRADIENT")); + } + else + { + bgStartColor = null; + bgEndColor = null; + } + + isImageBgEnabled = + new Boolean(resources.getSettingsString( + "impl.gui.IS_WINDOW_IMAGE_BACKGROUND_ENABLED")) + .booleanValue(); + + if (isImageBgEnabled) + { + final URL bgImagePath + = resources.getImageURL("service.gui.WINDOW_TITLE_BAR_BG"); + + bgImage = ImageUtils.getBufferedImage(bgImagePath); + + final Rectangle rect = + new Rectangle(0, 0, bgImage.getWidth(), + bgImage.getHeight()); + + texture = new TexturePaint(bgImage, rect); + } + } + + /** + * Initialize style values. + */ + private void initStyles() + { + ResourceManagementService resources = + DesktopUtilActivator.getResources(); + + int borderSize = + resources + .getSettingsInt("impl.gui.MAIN_WINDOW_BORDER_SIZE"); + this.setBorder(BorderFactory.createEmptyBorder(borderSize, + borderSize, borderSize, borderSize)); + } + + /** + * Paints this content pane. + * @param g the <tt>Graphics</tt> object used for painting + */ + @Override + public void paintComponent(Graphics g) + { + super.paintComponent(g); + + // If the custom color or image window background is not enabled we + // have nothing to do here. + if (isColorBgEnabled || isImageBgEnabled) + { + g = g.create(); + try + { + internalPaintComponent(g); + } + finally + { + g.dispose(); + } + } + } + + /** + * Provides a custom paint if the color or image background properties + * are enabled. + * @param g the <tt>Graphics</tt> object used for painting + */ + private void internalPaintComponent(Graphics g) + { + AntialiasingManager.activateAntialiasing(g); + + Graphics2D g2 = (Graphics2D) g; + int width = getWidth(); + int height = getHeight(); + + if (isColorBgEnabled) + { + GradientPaint bgGradientColor = + new GradientPaint(width / 2, 0, bgStartColor, width / 2, 80, + bgEndColor); + + g2.setPaint(bgGradientColor); + g2.fillRect(0, 0, width, 80); + + g2.setColor(bgEndColor); + g2.fillRect(0, 78, width, height); + } + + if (isImageBgEnabled) + { + if (bgImage != null && texture != null) + { + g2.setPaint(texture); + + g2.fillRect(0, 0, this.getWidth(), bgImage.getHeight()); + } + } + } + } + + /** + * Notifies this instance that it has been requested to close. The default + * <tt>SIPCommFrame</tt> implementation does nothing. + * + * @param escape <tt>true</tt> if the request to close this instance is in + * response of a press on the Escape key; otherwise, <tt>false</tt> + */ + protected void close(boolean escape) + { + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SIPCommHTMLEditorKit.java b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommHTMLEditorKit.java new file mode 100644 index 0000000..7a9914f --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommHTMLEditorKit.java @@ -0,0 +1,214 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ + +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; + +import javax.swing.*; +import javax.swing.text.*; +import javax.swing.text.html.*; +import javax.swing.text.html.ParagraphView; + +/** + * The <tt>SIPCommHTMLEditorKit</tt> is an <tt>HTMLEditorKit</tt> which uses + * an extended <tt>ParagraphView</tt>. + * + * @author Yana Stamcheva + */ +public class SIPCommHTMLEditorKit extends HTMLEditorKit +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + + private final JComponent container; + + /** + * Creates an instance of <tt>SIPCommHTMLEditorKit</tt> by specifying the + * container, where the editor kit would be used. + * + * @param container + */ + public SIPCommHTMLEditorKit(JComponent container) + { + this.container = container; + } + + /** + * Returns the extended <tt>HTMLFactory</tt> defined here. + * + * @return the extended view factory + */ + public ViewFactory getViewFactory() + { + return new HTMLFactoryX(); + } + + /** + * An extended <tt>HTMLFactory</tt> that uses the <tt>SIPCommImageView</tt> + * to represent images and the <tt>ParagraphViewX</tt> to represent + * paragraphs. + */ + private class HTMLFactoryX extends HTMLFactory + implements ViewFactory + { + public View create(Element elem) + { + View view = super.create(elem); + + if (view instanceof ParagraphView) + { + return new ParagraphViewX(elem); + } + else if (view instanceof ComponentView) + { + return new MyComponentView(elem); + } + + return view; + } + } + + /** + * An extended component view, which provides horizontal and vertical + * filling. + */ + private class MyComponentView extends ComponentView + { + /** + * Creates a new ComponentView object. + * + * @param elem the element to decorate + */ + public MyComponentView(Element elem) + { + super(elem); + } + + /** + * Determines the preferred span for this view along an + * axis. This is implemented to return the value + * returned by Component.getPreferredSize along the + * axis of interest. + * + * @param axis may be either View.X_AXIS or View.Y_AXIS + * @return the span the view would like to be rendered into >= 0. + * Typically the view is told to render into the span + * that is returned, although there is no guarantee. + * The parent may choose to resize or break the view. + * @exception IllegalArgumentException for an invalid axis + */ + public float getPreferredSpan(int axis) + { + if ((axis != X_AXIS) && (axis != Y_AXIS)) + { + throw new IllegalArgumentException("Invalid axis: " + axis); + } + if (getComponent() != null) + { + Dimension size = getComponent().getPreferredSize(); + if (axis == View.X_AXIS) + { + return container.getWidth(); + } + else + { + return size.height; + } + } + return 0; + } + + /** + * Determines the maximum span for this view along an + * axis. This is implemented to return the value + * returned by Component.getMaximumSize along the + * axis of interest. + * + * @param axis may be either View.X_AXIS or View.Y_AXIS + * @return the span the view would like to be rendered into >= 0. + * Typically the view is told to render into the span + * that is returned, although there is no guarantee. + * The parent may choose to resize or break the view. + * @exception IllegalArgumentException for an invalid axis + */ + public float getMaximumSpan(int axis) + { + if ((axis != X_AXIS) && (axis != Y_AXIS)) + { + throw new IllegalArgumentException("Invalid axis: " + axis); + } + if (getComponent() != null) + { + Dimension size = getComponent().getMaximumSize(); + if (axis == View.X_AXIS) + { + return container.getWidth(); + } + else + { + return size.height; + } + } + return 0; + } + } + + /** + * The <tt>ParagraphViewX</tt> is created in order to solve the following + * problem (Bug ID: 4855207): + * <p> + * When a paragraph in a JTextPane has a large amount of text the + * processing needed to layout the entire paragraph increases as the + * paragraph grows. + */ + static class ParagraphViewX extends ParagraphView + { + /** + * Creates an instance of <tt>ParagraphViewX</tt>. + * + * @param elem the element that this view is responsible for + */ + public ParagraphViewX(Element elem) + { + super(elem); + } + + /** + * Calculate requirements along the minor axis. This + * is implemented to forward the request to the logical + * view by calling getMinimumSpan, getPreferredSpan, and + * getMaximumSpan on it. + * + * @param axis the axis, for which we calculate size requirements + * @param sizeRequirements the initial size requirements + * @return the recalculated size requirements for the given axis + */ + protected SizeRequirements calculateMinorAxisRequirements ( + int axis, SizeRequirements sizeRequirements) + { + if (sizeRequirements == null) + { + sizeRequirements = new SizeRequirements(); + } + + float pref = layoutPool.getPreferredSpan(axis); + float min = layoutPool.getMinimumSpan(axis); + + // Don't include insets, Box.getXXXSpan will include them. + sizeRequirements.minimum = (int)min; + sizeRequirements.preferred + = Math.max(sizeRequirements.minimum, (int) pref); + sizeRequirements.maximum = Short.MAX_VALUE; + sizeRequirements.alignment = 0.5f; + return sizeRequirements; + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SIPCommLinkButton.java b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommLinkButton.java new file mode 100644 index 0000000..7708e80 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommLinkButton.java @@ -0,0 +1,279 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.net.*; + +import javax.swing.*; + +import net.java.sip.communicator.plugin.desktoputil.plaf.*; + +/** + * A button which text is a link. The button looks like a link. + */ +public class SIPCommLinkButton + extends JButton +{ + private static final long serialVersionUID = 1L; + + /** + * Class id key used in UIDefaults. + */ + private static final String UIClassID = "LinkButtonUI"; + + /** + * Adds the ui class to UIDefaults. + */ + static + { + UIManager.getDefaults().put(UIClassID, + SIPCommLinkButtonUI.class.getName()); + } + + public static final int ALWAYS_UNDERLINE = 0; + + public static final int HOVER_UNDERLINE = 1; + + public static final int NEVER_UNDERLINE = 2; + + private int linkBehavior; + + private Color linkColor; + + private Color colorPressed; + + private Color visitedLinkColor; + + private Color disabledLinkColor; + + private URL buttonURL; + + private boolean isLinkVisited; + + /** + * Created Link Button. + */ + public SIPCommLinkButton() + { + this(null, null); + } + + /** + * Created Link Button with text. + * @param text + */ + public SIPCommLinkButton(String text) + { + this(text, null); + } + + /** + * Created Link Button with url. + * @param url + */ + public SIPCommLinkButton(URL url) + { + this(null, url); + } + + /** + * Created Link Button with text and url. + * @param text + * @param url + */ + public SIPCommLinkButton(String text, URL url) + { + super(text); + + linkBehavior = SIPCommLinkButton.HOVER_UNDERLINE; + + linkColor = Color.blue; + colorPressed = Color.red; + visitedLinkColor = new Color(128, 0, 128); + + if (text == null && url != null) + this.setText(url.toExternalForm()); + setLinkURL(url); + + this.setBorderPainted(false); + this.setContentAreaFilled(false); + this.setRolloverEnabled(true); + this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } + + public String getUIClassID() + { + return SIPCommLinkButton.UIClassID; + } + + /** + * Setup the tooltip. + */ + protected void setupToolTipText() + { + String tip = null; + if (buttonURL != null) + tip = buttonURL.toExternalForm(); + setToolTipText(tip); + } + + /** + * Changes link behaviour. + * @param bnew the new behaviour. One of ALWAYS_UNDERLINE, HOVER_UNDERLINE + * and NEVER_UNDERLINE. + */ + public void setLinkBehavior(int bnew) + { + if (bnew != ALWAYS_UNDERLINE && bnew != HOVER_UNDERLINE + && bnew != NEVER_UNDERLINE) + throw new IllegalArgumentException("Not a legal LinkBehavior"); + + int old = linkBehavior; + linkBehavior = bnew; + firePropertyChange("linkBehavior", old, bnew); + repaint(); + } + + /** + * Returns the link behaviour. + * @return the link behaviour. + */ + public int getLinkBehavior() + { + return linkBehavior; + } + + /** + * Sets the link color. + * @param color the new color. + */ + public void setLinkColor(Color color) + { + Color colorOld = linkColor; + linkColor = color; + firePropertyChange("linkColor", colorOld, color); + repaint(); + } + + /** + * Return the link color. + * @return link color. + */ + public Color getLinkColor() + { + return linkColor; + } + + /** + * Sets the active link color. + * @param colorNew the new color. + */ + public void setActiveLinkColor(Color colorNew) + { + Color colorOld = colorPressed; + colorPressed = colorNew; + firePropertyChange("activeLinkColor", colorOld, colorNew); + repaint(); + } + + /** + * Returns the active link color. + * @return the active link color. + */ + public Color getActiveLinkColor() + { + return colorPressed; + } + + /** + * Sets disabled link color. + * @param color the new color. + */ + public void setDisabledLinkColor(Color color) + { + Color colorOld = disabledLinkColor; + disabledLinkColor = color; + firePropertyChange("disabledLinkColor", colorOld, color); + if (!isEnabled()) + repaint(); + } + + /** + * Returns the disabled link color. + * @return the disabled link color. + */ + public Color getDisabledLinkColor() + { + return disabledLinkColor; + } + + /** + * Set visited link color. + * @param colorNew the new visited link color. + */ + public void setVisitedLinkColor(Color colorNew) + { + Color colorOld = visitedLinkColor; + visitedLinkColor = colorNew; + firePropertyChange("visitedLinkColor", colorOld, colorNew); + repaint(); + } + + /** + * Returns visited link color. + * @return visited link color. + */ + public Color getVisitedLinkColor() + { + return visitedLinkColor; + } + + /** + * Set a link. + * @param url the url. + */ + public void setLinkURL(URL url) + { + URL urlOld = buttonURL; + buttonURL = url; + setupToolTipText(); + firePropertyChange("linkURL", urlOld, url); + revalidate(); + repaint(); + } + + /** + * Returns the url. + * @return the link url. + */ + public URL getLinkURL() + { + return buttonURL; + } + + /** + * Set a link visited. + * @param flagNew is link visited. + */ + public void setLinkVisited(boolean flagNew) + { + boolean flagOld = isLinkVisited; + isLinkVisited = flagNew; + firePropertyChange("linkVisited", flagOld, flagNew); + repaint(); + } + + /** + * Returns is link visited. + * @return is link visited. + */ + public boolean isLinkVisited() + { + return isLinkVisited; + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SIPCommMenu.java b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommMenu.java new file mode 100644 index 0000000..220f87e --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommMenu.java @@ -0,0 +1,308 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + +import org.jvnet.lafwidget.animation.*; + +/** + * The <tt>SIPCommMenu</tt> is very similar to a JComboBox. The main + * component here is a JLabel only with an icon. When user clicks on the icon a + * popup menu is opened, containing a list of icon-text pairs from which the + * user could choose one item. When user selects the desired item, the icon of + * the selected item is set to the main component label. + * + * @author Yana Stamcheva + */ +public class SIPCommMenu + extends JMenu +{ + private static final long serialVersionUID = 1L; + private Object selectedObject; + + /** + * Creates an instance of <tt>SIPCommMenu</tt>. + */ + public SIPCommMenu() + { + super(); + + init(); + } + + /** + * Creates an instance of <tt>SIPCommMenu</tt> by specifying + * the text and the icon. + * @param text the text of the menu + * @param defaultIcon the menu icon + */ + public SIPCommMenu(String text, Icon defaultIcon) + { + super(text); + + this.setIcon(defaultIcon); + init(); + } + + /** + * Creates an instance of <tt>SIPCommMenu</tt> by specifying the + * initialy selected item. + * + * @param text The item that is initialy selected. + */ + public SIPCommMenu(String text) + { + super(text); + init(); + } + + private void init() + { + MouseRolloverHandler mouseHandler = new MouseRolloverHandler(); + + this.addMouseListener(mouseHandler); + this.addMouseMotionListener(mouseHandler); + + // Hides the popup menu when the parent window loses focus. + getPopupMenu().addComponentListener(new ComponentAdapter() + { + public void componentResized(ComponentEvent evt) + { + Window parentWindow; + + Component parent = SIPCommMenu.this.getParent(); + + // If this is a submenu get the invoker first. + if (parent instanceof JPopupMenu) + parentWindow = SwingUtilities.getWindowAncestor( + ((JPopupMenu) parent).getInvoker()); + else + parentWindow + = SwingUtilities.getWindowAncestor(SIPCommMenu.this); + + if (!parentWindow.isActive()) + { + getPopupMenu().setVisible(false); + } + + parentWindow.addWindowListener(new WindowAdapter() + { + public void windowDeactivated(WindowEvent e) + { + JPopupMenu popupMenu = getPopupMenu(); + + if (popupMenu != null && popupMenu.isVisible()) + popupMenu.setVisible(false); + } + }); + } + }); + } + + /** + * Adds an item to the "choice list" of this selector box. + * + * @param text The text of the item. + * @param icon The icon of the item. + * @param actionListener The <tt>ActionListener</tt>, which handles the + * case, when the item is selected. + */ + public void addItem(String text, Icon icon, ActionListener actionListener) + { + JMenuItem item = new JMenuItem(text, icon); + + item.addActionListener(actionListener); + + this.add(item); + } + + /** + * Selects the given item. + * + * @param selectedObject The object to select. + */ + public void setSelected(SelectedObject selectedObject) + { + if (selectedObject.getIcon() != null) + this.setIcon(selectedObject.getIcon()); + + if (selectedObject.getText() != null) + this.setText(selectedObject.getText()); + + if (selectedObject.getObject() != null) + this.setSelectedObject(selectedObject.getObject()); + } + + /** + * Selects the given object. + * + * @param o The <tt>Object</tt> to select. + */ + public void setSelectedObject(Object o) + { + this.selectedObject = o; + } + + /** + * Returns the selected object. + * + * @return the selected object. + */ + public Object getSelectedObject() + { + return this.selectedObject; + } + + /** + * Sets the isMouseOver property value and repaints this component. + * + * @param isMouseOver <code>true</code> to indicate that the mouse is over + * this component, <code>false</code> - otherwise. + */ + public void setMouseOver(boolean isMouseOver) + { + this.repaint(); + } + + /** + * Paints this component. + * @param g the <tt>Graphics</tt> object used for painting + */ + public void paintComponent(Graphics g) + { + Graphics g2 = g.create(); + try + { + internalPaintComponent(g2); + } + finally + { + g2.dispose(); + } + super.paintComponent(g); + } + + /** + * Paints a rollover effect when the mouse is over this menu. + * @param g the <tt>Graphics</tt> object used for painting + */ + private void internalPaintComponent(Graphics g) + { + AntialiasingManager.activateAntialiasing(g); + + // Paint a roll over fade out. + FadeTracker fadeTracker = FadeTracker.getInstance(); + + float visibility = getModel().isRollover() ? 1.0f : 0.0f; + if (fadeTracker.isTracked(this, FadeKind.ROLLOVER)) + { + visibility = fadeTracker.getFade(this, FadeKind.ROLLOVER); + } + + visibility /= 2; + + g.setColor(new Color(1.0f, 1.0f, 1.0f, visibility)); + + g.fillRoundRect(0, 0, this.getWidth(), this.getHeight(), 20, 20); + + g.setColor(UIManager.getColor("Menu.foreground")); + } + + /** + * The <tt>ButtonRepaintCallback</tt> is charged to repaint this button + * when the fade animation is performed. + */ + private class ButtonRepaintCallback implements FadeTrackerCallback + { + public void fadeEnded(FadeKind arg0) + { + repaintLater(); + } + + public void fadePerformed(FadeKind arg0, float arg1) + { + repaintLater(); + } + + private void repaintLater() + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + SIPCommMenu.this.repaint(); + } + }); + } + + public void fadeReversed(FadeKind arg0, boolean arg1, float arg2) + { + } + } + + /** + * Perform a fade animation on mouse over. + */ + private class MouseRolloverHandler + implements MouseListener, + MouseMotionListener + { + public void mouseMoved(MouseEvent e) + { + } + + public void mouseExited(MouseEvent e) + { + if (isEnabled()) + { + getModel().setRollover(false); + + FadeTracker fadeTracker = FadeTracker.getInstance(); + + fadeTracker.trackFadeOut(FadeKind.ROLLOVER, + SIPCommMenu.this, + true, + new ButtonRepaintCallback()); + } + } + + public void mouseClicked(MouseEvent e) + { + } + + public void mouseEntered(MouseEvent e) + { + if (isEnabled()) + { + getModel().setRollover(true); + + FadeTracker fadeTracker = FadeTracker.getInstance(); + + fadeTracker.trackFadeIn(FadeKind.ROLLOVER, + SIPCommMenu.this, + true, + new ButtonRepaintCallback()); + } + } + + public void mousePressed(MouseEvent e) + { + } + + public void mouseReleased(MouseEvent e) + { + } + + public void mouseDragged(MouseEvent e) + { + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SIPCommMenuBar.java b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommMenuBar.java new file mode 100644 index 0000000..c08e5a7 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommMenuBar.java @@ -0,0 +1,71 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import javax.swing.*; + +import net.java.sip.communicator.plugin.desktoputil.plaf.*; +import net.java.sip.communicator.util.skin.*; +/** + * The SIPCommMenuBar is a <tt>JMenuBar</tt> without border decoration that can + * be used as a container for other components, like selector boxes that won't + * need a menu decoration. + * + * @author Yana Stamcheva + * @author Adam Netocny + */ +public class SIPCommMenuBar + extends JMenuBar + implements Skinnable +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * Class id key used in UIDefaults. + */ + private static final String UIClassID = "SIPCommMenuBarUI"; + + /** + * Adds the ui class to UIDefaults. + */ + static + { + UIManager.getDefaults().put(UIClassID, + SIPCommMenuBarUI.class.getName()); + } + + /** + * Creates an instance of <tt>SIPCommMenuBar</tt>. + */ + public SIPCommMenuBar() + { + loadSkin(); + } + + /** + * Reload UI defs. + */ + public void loadSkin() + { + this.setBorder(BorderFactory.createEmptyBorder()); + } + + /** + * Returns the name of the L&F class that renders this component. + * + * @return the string "TreeUI" + * @see JComponent#getUIClassID + * @see UIDefaults#getUI + */ + public String getUIClassID() + { + return UIClassID; + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SIPCommPopupMenu.java b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommPopupMenu.java new file mode 100644 index 0000000..0ecba98 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommPopupMenu.java @@ -0,0 +1,67 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + +/** + * A custom popup menu that detects parent focus lost. + * + * @author Yana Stamcheva + */ +public class SIPCommPopupMenu + extends JPopupMenu +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * Constructor. + */ + public SIPCommPopupMenu() + { + // Hides the popup menu when the parent window loses focus. + addComponentListener(new ComponentAdapter() + { + public void componentResized(ComponentEvent evt) + { + final Window parentWindow; + + Component parent = getParent(); + + // If this is a submenu get the invoker first. + if (parent instanceof JPopupMenu) + parentWindow = SwingUtilities.getWindowAncestor( + ((JPopupMenu) parent).getInvoker()); + else + parentWindow + = SwingUtilities.getWindowAncestor(getInvoker()); + + if (parentWindow != null) + { + if (!parentWindow.isActive()) + setVisible(false); + + parentWindow.addWindowListener(new WindowAdapter() + { + public void windowDeactivated(WindowEvent e) + { + if (SIPCommPopupMenu.this != null + && SIPCommPopupMenu.this.isVisible()) + SIPCommPopupMenu.this.setVisible(false); + } + }); + } + } + }); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SIPCommRadioButton.java b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommRadioButton.java new file mode 100644 index 0000000..ca71cb7 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommRadioButton.java @@ -0,0 +1,48 @@ +/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.desktoputil;
+
+import javax.swing.*;
+
+import org.jitsi.util.*;
+
+/**
+ * @author Ingo Bauersachs
+ */
+public class SIPCommRadioButton
+ extends JRadioButton
+{
+ private static final long serialVersionUID = 0L;
+
+ private static final boolean setContentAreaFilled = (OSUtils.IS_WINDOWS
+ || OSUtils.IS_LINUX);
+
+ public SIPCommRadioButton()
+ {
+ init();
+ }
+
+ public SIPCommRadioButton(String text)
+ {
+ super(text);
+
+ init();
+ }
+
+ public SIPCommRadioButton(String text, boolean selected)
+ {
+ super(text, selected);
+
+ init();
+ }
+
+ private void init()
+ {
+ if (setContentAreaFilled)
+ setContentAreaFilled(false);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SIPCommScrollPane.java b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommScrollPane.java new file mode 100644 index 0000000..2eb3b1b --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommScrollPane.java @@ -0,0 +1,236 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.image.*; +import java.beans.*; +import java.lang.reflect.*; + +import javax.swing.*; + +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.skin.*; + +import org.jitsi.util.*; + +/** + * The SCScrollPane is a JScrollPane with a custom viewport that allows to + * set an image as a background. Depending on the + * "impl.gui.IS_CONTACT_LIST_TEXTURE_BG_ENABLED" property we'll be setting a + * single image or a texture of images. + * + * @author Yana Stamcheva + * @author Adam Netocny + */ +public class SIPCommScrollPane + extends JScrollPane + implements Skinnable +{ + private static final long serialVersionUID = 0L; + + /** + * Creates an <tt>SCSCrollPane</tt>. + */ + public SIPCommScrollPane() + { + this.setBorder(BorderFactory.createMatteBorder( + 1, 0, 1, 0, Color.GRAY)); + + this.setViewport(new SCViewport()); + + this.getVerticalScrollBar().setUnitIncrement(100); + } + + /** + * Sets the view of this JViewport. + * + * @param view the view to set. + */ + @Override + public void setViewportView(Component view) + { + if (view instanceof JComponent) + { + JComponent viewAsJComponent = (JComponent) view; + + viewAsJComponent.setBorder( + BorderFactory.createEmptyBorder(3, 3, 3, 3)); + viewAsJComponent.setOpaque(false); + } + + super.setViewportView(view); + } + + /** + * Reloads skin information in viewport. + */ + public void loadSkin() + { + ((SCViewport) getViewport()).loadSkin(); + } + + /** + * The <tt>SCViewport</tt> used as viewport in this scrollpane. + */ + private static class SCViewport + extends JViewport + implements Skinnable + { + private static final long serialVersionUID = 1L; + + private BufferedImage bgImage; + + private Color color; + + private TexturePaint texture; + + /** + * Creates the <tt>SCViewport</tt>. + */ + public SCViewport() + { + this.setBackground(Color.WHITE); + + loadSkin(); + } + + /** + * Returns the boolean value of the property given by <tt>key</tt>. + * @param key the key of the property we look for + * @return the boolean value of the searched property + */ + private boolean getSettingsBoolean(String key) + { + return + Boolean.parseBoolean( + DesktopUtilActivator.getResources().getSettingsString(key)); + } + + /** + * Paints this viewport. + * @param g the <tt>Graphics</tt> object used for painting + */ + @Override + public void paintComponent(Graphics g) + { + super.paintComponent(g); + + g = g.create(); + try + { + AntialiasingManager.activateAntialiasing(g); + + Graphics2D g2 = (Graphics2D) g; + int width = getWidth(); + int height = getHeight(); + + // paint the image + if (bgImage != null) + { + if (texture != null) + { + g2.setPaint(texture); + + g2.fillRect(0, 0, width, height); + } + else + { + g.setColor(color); + + // paint the background with the chosen color + g.fillRect(0, 0, width, height); + + g2.drawImage(bgImage, width - bgImage.getWidth(), + height - bgImage.getHeight(), this); + } + } + } + finally + { + g.dispose(); + } + } + + /** + * Reloads background. + */ + public void loadSkin() + { + if(getSettingsBoolean("impl.gui.IS_CONTACT_LIST_IMG_BG_ENABLED")) + { + bgImage = + DesktopUtilActivator.getImage("service.gui.MAIN_WINDOW_BACKGROUND"); + + if (getSettingsBoolean( + "impl.gui.IS_CONTACT_LIST_TEXTURE_BG_ENABLED") + && (bgImage != null)) + { + texture = + new TexturePaint(bgImage, new Rectangle(0, 0, bgImage + .getWidth(null), bgImage.getHeight(null))); + + color = null; + } + else + { + texture = null; + color = + new Color(DesktopUtilActivator.getResources().getColor( + "service.gui.CONTACT_LIST_BACKGROUND")); + } + } + else + { + bgImage = null; + texture = null; + color = null; + } + } + } + + /** + * Releases the resources allocated by this instance throughout its lifetime + * and prepares it for garbage collection. + */ + public void dispose() + { + if(OSUtils.IS_MAC) + { + // Apple introduced a memory leak in JViewport class - + // they add a PropertyChangeListeners to the CToolkit + try + { + Toolkit defaultToolkit = Toolkit.getDefaultToolkit(); + PropertyChangeListener[] pcl + = defaultToolkit.getPropertyChangeListeners( + "apple.awt.contentScaleFactor"); + + for(PropertyChangeListener pc : pcl) + { + // find the reference to the object created the listener + Field f = pc.getClass().getDeclaredField("this$0"); + + f.setAccessible(true); + // If we are the parent, clean up. + if(f.get(pc).equals(this.getViewport())) + { + defaultToolkit.removePropertyChangeListener( + "apple.awt.contentScaleFactor", + pc); + break; + } + } + } + catch(Throwable t) + { + if (t instanceof ThreadDeath) + throw (ThreadDeath) t; + } + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SIPCommTabbedPane.java b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommTabbedPane.java new file mode 100644 index 0000000..b41ee9f --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommTabbedPane.java @@ -0,0 +1,512 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +/* + * The following code borrowed from David Bismut, davidou@mageos.com Intern, + * SETLabs, Infosys Technologies Ltd. May 2004 - Jul 2004 Ecole des Mines de + * Nantes, France + */ +import java.awt.*; +import java.awt.event.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.border.*; +import javax.swing.event.*; + +import net.java.sip.communicator.plugin.desktoputil.event.*; +import net.java.sip.communicator.plugin.desktoputil.plaf.*; +import net.java.sip.communicator.util.skin.*; + +/** + * A JTabbedPane with some added UI functionalities. A close and max/detach + * icons are added to every tab, typically to let the user close or detach the + * tab by clicking on these icons. + * + * @author Yana Stamcheva + * @author Adam Netocny + */ +public class SIPCommTabbedPane + extends JTabbedPane + implements ChangeListener, + Skinnable +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + private int overTabIndex = -1; + + private int lastSelectedIndex; + + public SIPCommTabbedPane() + { + this(false, false); + } + + /** + * Creates the <code>CloseAndMaxTabbedPane</code> with an enhanced UI if + * <code>enhancedUI</code> parameter is set to <code>true</code>. + * + * @param closingTabs support for closable tabs + * @param maximizingTabs support for maximisable tabs + */ + public SIPCommTabbedPane(boolean closingTabs, boolean maximizingTabs) + { + super.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + + UIManager.getDefaults() + .put("TabbedPane.tabAreaInsets", new Insets(0, 5, 0, 0)); + + UIManager.getDefaults() + .put("TabbedPane.contentBorderInsets", new Insets(0, 0, 0, 0)); + + this.setForeground( + new Color(DesktopUtilActivator.getResources() + .getColor("service.gui.TAB_TITLE"))); + + this.setUI(new SIPCommTabbedPaneEnhancedUI()); + + if(closingTabs) + this.setCloseIcon(true); + + if(maximizingTabs) + this.setMaxIcon(true); + + this.addChangeListener(this); + } + + /** + * Returns the index of the last tab on which the mouse did an action. + * + * @return + */ + public int getOverTabIndex() + { + return overTabIndex; + } + + /** + * Returns <code>true</code> if the close icon is enabled. + * + * @return + */ + public boolean isCloseEnabled() + { + SIPCommTabbedPaneUI ui = (SIPCommTabbedPaneUI) this.getUI(); + return ui.isCloseEnabled(); + } + + /** + * Returns <code>true</code> if the max/detach icon is enabled. + * + * @return + */ + public boolean isMaxEnabled() + { + return ((SIPCommTabbedPaneUI) getUI()).isMaxEnabled(); + } + + /** + * Override JTabbedPane method. Does nothing. + * @param tabLayoutPolicy The tab layout policy. + */ + @Override + public void setTabLayoutPolicy(int tabLayoutPolicy) + { + } + + /** + * Override JTabbedPane method. Does nothing. + * @param tabPlacement The tab placement. + */ + @Override + public void setTabPlacement(int tabPlacement) + { + } + + /** + * Sets whether the tabbedPane should have a close icon or not. + * + * @param b whether the tabbedPane should have a close icon or not + */ + public void setCloseIcon(boolean b) + { + ((SIPCommTabbedPaneUI) getUI()).setCloseIcon(b); + } + + /** + * Sets whether the tabbedPane should have a max/detach icon or not. + * + * @param b whether the tabbedPane should have a max/detach icon or not + */ + public void setMaxIcon(boolean b) + { + ((SIPCommTabbedPaneUI) getUI()).setMaxIcon(b); + } + + /** + * Detaches the <code>index</code> tab in a separate frame. When the frame + * is closed, the tab is automatically reinserted into the tabbedPane. + * + * @param index index of the tabbedPane to be detached + */ + public void detachTab(int index) + { + if (index < 0 || index >= getTabCount()) + return; + + final int tabIndex = index; + final JComponent c = (JComponent) getComponentAt(tabIndex); + + final Icon icon = getIconAt(tabIndex); + final String title = getTitleAt(tabIndex); + final String toolTip = getToolTipTextAt(tabIndex); + final Border border = c.getBorder(); + + final JFrame frame = new SIPCommFrame() + { + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + protected void close(boolean isEscaped) + { + if (isEscaped) + return; + + dispose(); + + insertTab(title, icon, c, toolTip, Math.min(tabIndex, + getTabCount())); + + c.setBorder(border); + setSelectedComponent(c); + } + }; + + Window parentWindow = SwingUtilities.windowForComponent(this); + + removeTabAt(index); + + c.setPreferredSize(c.getSize()); + + frame.setTitle(title); + frame.getContentPane().add(c); + frame.setLocation(parentWindow.getLocation()); + frame.pack(); + + WindowFocusListener windowFocusListener = new WindowFocusListener() { + long start; + + long end; + + public void windowGainedFocus(WindowEvent e) { + start = System.currentTimeMillis(); + } + + public void windowLostFocus(WindowEvent e) { + end = System.currentTimeMillis(); + long elapsed = end - start; + + if (elapsed < 100) + frame.toFront(); + + frame.removeWindowFocusListener(this); + } + }; + + /* + * This is a small hack to avoid Windows GUI bug, that prevent a new + * window from stealing focus (without this windowFocusListener, most of + * the time the new frame would just blink from foreground to + * background). A windowFocusListener is added to the frame, and if the + * time between the frame beeing in foreground and the frame beeing in + * background is less that 100ms, it just brings the windows to the + * front once again. Then it removes the windowFocusListener. Note that + * this hack would not be required on Linux or UNIX based systems. + */ + + frame.addWindowFocusListener(windowFocusListener); + + // frame.show(); + frame.setVisible(true); + frame.toFront(); + + } + + /** + * Adds a <code>CloseListener</code> to the tabbedPane. + * + * @param l the <code>CloseListener</code> to add + * @see #fireCloseTabEvent + * @see #removeCloseListener + */ + public synchronized void addCloseListener(CloseListener l) + { + listenerList.add(CloseListener.class, l); + } + + /** + * Adds a <code>MaxListener</code> to the tabbedPane. + * + * @param l the <code>MaxListener</code> to add + * @see #fireMaxTabEvent + * @see #removeMaxListener + */ + public synchronized void addMaxListener(MaxListener l) + { + listenerList.add(MaxListener.class, l); + } + + /** + * Adds a <code>DoubleClickListener</code> to the tabbedPane. + * + * @param l the <code>DoubleClickListener</code> to add + * @see #fireDoubleClickTabEvent + * @see #removeDoubleClickListener + */ + public synchronized void addDoubleClickListener(DoubleClickListener l) + { + listenerList.add(DoubleClickListener.class, l); + } + + /** + * Adds a <code>PopupOutsideListener</code> to the tabbedPane. + * + * @param l the <code>PopupOutsideListener</code> to add + * @see #firePopupOutsideTabEvent + * @see #removePopupOutsideListener + */ + public synchronized void addPopupOutsideListener(PopupOutsideListener l) + { + listenerList.add(PopupOutsideListener.class, l); + } + + /** + * Removes a <code>CloseListener</code> from this tabbedPane. + * + * @param l the <code>CloseListener</code> to remove + * @see #fireCloseTabEvent + * @see #addCloseListener + */ + public synchronized void removeCloseListener(CloseListener l) + { + listenerList.remove(CloseListener.class, l); + } + + /** + * Removes a <code>MaxListener</code> from this tabbedPane. + * + * @param l the <code>MaxListener</code> to remove + * @see #fireMaxTabEvent + * @see #addMaxListener + */ + public synchronized void removeMaxListener(MaxListener l) + { + listenerList.remove(MaxListener.class, l); + } + + /** + * Removes a <code>DoubleClickListener</code> from this tabbedPane. + * + * @param l + * the <code>DoubleClickListener</code> to remove + * @see #fireDoubleClickTabEvent + * @see #addDoubleClickListener + */ + public synchronized void removeDoubleClickListener(DoubleClickListener l) + { + listenerList.remove(DoubleClickListener.class, l); + } + + /** + * Removes a <code>PopupOutsideListener</code> from this tabbedPane. + * + * @param l + * the <code>PopupOutsideListener</code> to remove + * @see #firePopupOutsideTabEvent + * @see #addPopupOutsideListener + */ + public synchronized void removePopupOutsideListener( + PopupOutsideListener l) + { + listenerList.remove(PopupOutsideListener.class, l); + } + + /** + * Sends a <code>MouseEvent</code>, whose source is this tabbedpane, to + * every <code>CloseListener</code>. The method also updates the + * <code>overTabIndex</code> of the tabbedPane with a value coming from + * the UI. This method method is called each time a <code>MouseEvent</code> + * is received from the UI when the user clicks on the close icon of the tab + * which index is <code>overTabIndex</code>. + * + * @param e + * the <code>MouseEvent</code> to be sent + * @param overTabIndex + * the index of a tab, usually the tab over which the mouse is + * + * @see #addCloseListener + */ + public void fireCloseTabEvent(MouseEvent e, int overTabIndex) + { + this.overTabIndex = overTabIndex; + + EventListener[] closeListeners = getListeners(CloseListener.class); + for (int i = 0; i < closeListeners.length; i++) + { + ((CloseListener) closeListeners[i]).closeOperation(e); + } + } + + /** + * Sends a <code>MouseEvent</code>, whose source is this tabbedpane, to + * every <code>MaxListener</code>. The method also updates the + * <code>overTabIndex</code> of the tabbedPane with a value coming from + * the UI. This method method is called each time a <code>MouseEvent</code> + * is received from the UI when the user clicks on the max icon of the tab + * which index is <code>overTabIndex</code>. + * + * @param e + * the <code>MouseEvent</code> to be sent + * @param overTabIndex + * the index of a tab, usually the tab over which the mouse is + * + * @see #addMaxListener + */ + public void fireMaxTabEvent(MouseEvent e, int overTabIndex) + { + this.overTabIndex = overTabIndex; + + EventListener[] maxListeners = getListeners(MaxListener.class); + for (int i = 0; i < maxListeners.length; i++) + { + ((MaxListener) maxListeners[i]).maxOperation(e); + } + } + + /** + * Sends a <code>MouseEvent</code>, whose source is this tabbedpane, to + * every <code>DoubleClickListener</code>. The method also updates the + * <code>overTabIndex</code> of the tabbedPane with a value coming from + * the UI. This method method is called each time a <code>MouseEvent</code> + * is received from the UI when the user double-clicks on the tab which + * index is <code>overTabIndex</code>. + * + * @param e + * the <code>MouseEvent</code> to be sent + * @param overTabIndex + * the index of a tab, usually the tab over which the mouse is + * + * @see #addDoubleClickListener + */ + public void fireDoubleClickTabEvent(MouseEvent e, int overTabIndex) + { + this.overTabIndex = overTabIndex; + + EventListener[] dClickListeners + = getListeners(DoubleClickListener.class); + for (int i = 0; i < dClickListeners.length; i++) + { + ((DoubleClickListener) dClickListeners[i]).doubleClickOperation(e); + } + } + + /** + * Sends a <code>MouseEvent</code>, whose source is this tabbedpane, to + * every <code>PopupOutsideListener</code>. The method also sets the + * <code>overTabIndex</code> to -1. This method method is called each time + * a <code>MouseEvent</code> is received from the UI when the user + * right-clicks on the inactive part of a tabbedPane. + * + * @param e + * the <code>MouseEvent</code> to be sent + * + * @see #addPopupOutsideListener + */ + public void firePopupOutsideTabEvent(MouseEvent e) + { + this.overTabIndex = -1; + + EventListener[] popupListeners + = getListeners(PopupOutsideListener.class); + for (int i = 0; i < popupListeners.length; i++) + { + ((PopupOutsideListener) popupListeners[i]).popupOutsideOperation(e); + } + } + + /** + * Overrides setSelectedIndex in JTabbedPane in order to remove the + * highlight if the tab which is selected. + * @param tabIndex The index of the tab to be selected. + */ + @Override + public void setSelectedIndex(int tabIndex) + { + SIPCommTabbedPaneEnhancedUI ui + = (SIPCommTabbedPaneEnhancedUI) this.getUI(); + if (ui.isTabHighlighted(tabIndex)) + { + ui.tabRemoveHighlight(tabIndex); + } + super.setSelectedIndex(tabIndex); + } + + /** + * Highlights the tab with the given index. + * + * @param tabIndex The tab index. + */ + public void highlightTab(int tabIndex) + { + SIPCommTabbedPaneEnhancedUI ui + = (SIPCommTabbedPaneEnhancedUI) this.getUI(); + + if (!ui.isTabHighlighted(tabIndex) + && this.getSelectedIndex() != tabIndex) + ui.tabAddHightlight(tabIndex); + + this.repaint(); + } + + @Override + public void removeTabAt(int index) + { + if (index < lastSelectedIndex) + { + this.setSelectedIndex(lastSelectedIndex - 1); + } + else if (index > lastSelectedIndex) + { + this.setSelectedIndex(lastSelectedIndex); + } + + super.removeTabAt(index); + } + + public void stateChanged(ChangeEvent e) + { + lastSelectedIndex = this.getSelectedIndex(); + } + + /** + * Reloads skin information. + */ + public void loadSkin() + { + this.setForeground( + new Color(DesktopUtilActivator.getResources() + .getColor("service.gui.TAB_TITLE"))); + + ((SIPCommTabbedPaneEnhancedUI)this.getUI()).loadSkin(); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SIPCommTextButton.java b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommTextButton.java new file mode 100644 index 0000000..a348c5d --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommTextButton.java @@ -0,0 +1,279 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; +import javax.swing.plaf.basic.*; + +import org.jvnet.lafwidget.animation.*; + +/** + * A custom JButton that contains only text. A custom background could be set, + * which will result in a round cornered background behind the text. Note that + * you can also set a semi-transparent background. The button also supports a + * rollover effect. + * + * @author Yana Stamcheva + */ +public class SIPCommTextButton extends JButton +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * Class id key used in UIDefaults. + */ + private static final String UIClassID = "BasicButtonUI"; + + /** + * Adds the ui class to UIDefaults. + */ + static + { + UIManager.getDefaults().put(UIClassID, + BasicButtonUI.class.getName()); + } + + private final float[] borderColor + = Color.DARK_GRAY.getRGBComponents(null); + + private Image bgImage; + + /** + * Creates a <tt>SIPCommTextButton</tt>. + */ + public SIPCommTextButton() + { + this("", null); + } + + /** + * Creates a <tt>SIPCommTextButton</tt> + * @param text the text of the button + */ + public SIPCommTextButton(String text) + { + this(text, null); + } + + public SIPCommTextButton(String text, Image bgImage) + { + super(text); + + this.bgImage = bgImage; + + MouseRolloverHandler mouseHandler = new MouseRolloverHandler(); + + this.addMouseListener(mouseHandler); + this.addMouseMotionListener(mouseHandler); + + this.setIcon(null); + this.setIconTextGap(0); + + /* + * Explicitly remove all borders that may be set from the current look + * and feel. + */ + this.setContentAreaFilled(false); + } + + public void setBgImage(Image image) + { + this.bgImage = image; + } + + /** + * Return the background image. + * + * @return the background image of this button + */ + public Image getBgImage() + { + return bgImage; + } + + /** + * Overrides the <code>paintComponent</code> method of <tt>JButton</tt> to + * paint the button background and icon, and all additional effects of this + * configurable button. + * + * @param g The Graphics object. + */ + protected void paintComponent(Graphics g) + { + Graphics2D g1 = (Graphics2D) g.create(); + try + { + internalPaintComponent(g1); + } + finally + { + g1.dispose(); + } + + super.paintComponent(g); + } + + /** + * Paints this button. + * @param g the <tt>Graphics</tt> object used for painting + */ + private void internalPaintComponent(Graphics2D g) + { + AntialiasingManager.activateAntialiasing(g); + + // Paint a roll over fade out. + FadeTracker fadeTracker = FadeTracker.getInstance(); + + float visibility = this.getModel().isRollover() ? 1.0f : 0.0f; + if (fadeTracker.isTracked(this, FadeKind.ROLLOVER)) + { + visibility = fadeTracker.getFade(this, FadeKind.ROLLOVER); + } + + visibility /= 2; + + if (visibility != 0.0f) + { + g.setColor(new Color(borderColor[0], borderColor[1], + borderColor[2], visibility)); + + if (bgImage != null) + g.fillRoundRect((this.getWidth() - bgImage.getWidth(null))/2, + (this.getHeight() - bgImage.getHeight(null))/2, + bgImage.getWidth(null) - 1, + bgImage.getHeight(null) - 1, + 20, 20); + else + g.fillRoundRect(0, 0, + this.getWidth() - 1, this.getHeight() - 1, + 20, 20); + } + + if (bgImage != null) + { + g.drawImage(bgImage, + (this.getWidth() - bgImage.getWidth(null))/2, + (this.getHeight() - bgImage.getHeight(null))/2, null); + } + else + { + g.setColor(getBackground()); + g.fillRoundRect(1, 1, + this.getWidth() - 2, this.getHeight() - 2, + 20, 20); + } + + } + + /** + * Returns the name of the L&F class that renders this component. + * + * @return the string "TreeUI" + * @see JComponent#getUIClassID + * @see UIDefaults#getUI + */ + public String getUIClassID() + { + return UIClassID; + } + + /** + * The <tt>ButtonRepaintCallback</tt> is charged to repaint this button + * when the fade animation is performed. + */ + private class ButtonRepaintCallback implements FadeTrackerCallback + { + public void fadeEnded(FadeKind arg0) + { + repaintLater(); + } + + public void fadePerformed(FadeKind arg0, float arg1) + { + repaintLater(); + } + + private void repaintLater() + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + SIPCommTextButton.this.repaint(); + } + }); + } + + public void fadeReversed(FadeKind arg0, boolean arg1, float arg2) + { + } + } + + /** + * Perform a fade animation on mouse over. + */ + private class MouseRolloverHandler + implements MouseListener, + MouseMotionListener + { + public void mouseMoved(MouseEvent e) + { + } + + public void mouseExited(MouseEvent e) + { + if (isEnabled()) + { + getModel().setRollover(false); + + FadeTracker fadeTracker = FadeTracker.getInstance(); + + fadeTracker.trackFadeOut(FadeKind.ROLLOVER, + SIPCommTextButton.this, + true, + new ButtonRepaintCallback()); + } + } + + public void mouseClicked(MouseEvent e) + { + } + + public void mouseEntered(MouseEvent e) + { + if (isEnabled()) + { + getModel().setRollover(true); + + FadeTracker fadeTracker = FadeTracker.getInstance(); + + fadeTracker.trackFadeIn(FadeKind.ROLLOVER, + SIPCommTextButton.this, + true, + new ButtonRepaintCallback()); + } + } + + public void mousePressed(MouseEvent e) + { + } + + public void mouseReleased(MouseEvent e) + { + } + + public void mouseDragged(MouseEvent e) + { + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SIPCommTextField.java b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommTextField.java new file mode 100644 index 0000000..80b39dc --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommTextField.java @@ -0,0 +1,287 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.event.*; + +import net.java.sip.communicator.plugin.desktoputil.event.*; + +/** + * The <tt>SIPCommTextField</tt> is a <tt>JTextField</tt> that offers the + * possibility to specify a default (tip) text that explains what is the + * required data. + * @author Yana Stamcheva + */ +public class SIPCommTextField + extends JTextField + implements MouseListener, + FocusListener, + KeyListener, + DocumentListener +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * The default text. + */ + private String defaultText; + + /** + * A list of all listeners registered for text field change events. + */ + private Collection<TextFieldChangeListener> changeListeners + = new LinkedList<TextFieldChangeListener>(); + + /** + * Indicates if the default text is currently visible. + */ + private boolean isDefaultTextVisible; + + /** + * The color of the foreground. + */ + private Color foregroundColor = Color.BLACK; + + /** + * The foreground color of the default text. + */ + private Color defaultTextColor = Color.GRAY; + + /** + * Creates an instance of <tt>SIPCommTextField</tt> by specifying the text + * we would like to show by default in it. + * @param text the text we would like to enter by default + */ + public SIPCommTextField(String text) + { + super(text); + + if (text != null && text.length() > 0) + { + this.defaultText = text; + isDefaultTextVisible = true; + } + + this.setFont(getFont().deriveFont(10f)); + this.setForeground(defaultTextColor); + + this.addMouseListener(this); + this.addFocusListener(this); + + this.addKeyListener(this); + this.getDocument().addDocumentListener(this); + } + + /** + * Indicates that the mouse button was pressed on this component. Hides + * the default text when user clicks on the text field. + * @param e the <tt>MouseEvent</tt> that notified us + */ + public void mousePressed(MouseEvent e) + { + if (getText() == null) + { + clearDefaultText(); + } + } + + public void mouseClicked(MouseEvent e) {} + + public void mouseEntered(MouseEvent e) {} + + public void mouseExited(MouseEvent e) {} + + public void mouseReleased(MouseEvent e) {} + + /** + * Selects the user text when this text field gains the focus. + * @param e the <tt>FocusEvent</tt> that notified us + */ + public void focusGained(FocusEvent e) + { + clearDefaultText(); + } + + /** + * Sets the default text when the field looses focus. + * @param e the <tt>FocusEvent</tt> that notified us + */ + public void focusLost(FocusEvent e) + { + if (getText() == null || getText().length() == 0) + { + setDefaultText(); + } + } + + /** + * Returns the text contained in this field. + * @return the text contained in this field + */ + public String getText() + { + if (!super.getText().equals(defaultText)) + return super.getText(); + + return null; + } + + /** + * Sets the text of this text field. + * @param text the text to show in this text field + */ + public void setText(String text) + { + if ((text == null || text.length() == 0) && !isFocusOwner()) + setDefaultText(); + else + { + this.setForeground(foregroundColor); + super.setText(text); + } + } + + /** + * Sets the default text. + */ + private void setDefaultText() + { + super.setText(defaultText); + this.setForeground(defaultTextColor); + this.setCaretPosition(0); + } + + /** + * Clears the default text. + */ + private void clearDefaultText() + { + if (super.getText().equals(defaultText)) + { + super.setText(""); + this.setForeground(foregroundColor); + } + } + + /** + * Clears the default text when a key pressed event is received. + * @param e the <tt>KeyEvent</tt> that notified us + */ + public void keyPressed(KeyEvent e) + { + clearDefaultText(); + } + + /** + * Clears the default text when a key typed event is received. + * @param e the <tt>KeyEvent</tt> that notified us + */ + public void keyTyped(KeyEvent e) + { + clearDefaultText(); + } + + public void keyReleased(KeyEvent e){} + + /** + * Adds the given <tt>TextFieldChangeListener</tt> to the list of listeners + * notified on changes of the text contained in this field. + * @param l the <tt>TextFieldChangeListener</tt> to add + */ + public void addTextChangeListener(TextFieldChangeListener l) + { + synchronized (changeListeners) + { + changeListeners.add(l); + } + } + + /** + * Removes the given <tt>TextFieldChangeListener</tt> from the list of + * listeners notified on changes of the text contained in this field. + * @param l the <tt>TextFieldChangeListener</tt> to add + */ + public void removeTextChangeListener(TextFieldChangeListener l) + { + synchronized (changeListeners) + { + changeListeners.remove(l); + } + } + + public void changedUpdate(DocumentEvent e) {} + + /** + * Handles the change when a char has been inserted in the field. + * @param e the <tt>DocumentEvent</tt> that notified us + */ + public void insertUpdate(DocumentEvent e) + { + if(!super.getText().equals(defaultText)) + fireTextFieldChangeListener(0); + else + isDefaultTextVisible = true; + } + + /** + * Handles the change when a char has been removed from the field. + * @param e the <tt>DocumentEvent</tt> that notified us + */ + public void removeUpdate(DocumentEvent e) + { + if (!isDefaultTextVisible) + fireTextFieldChangeListener(1); + else + isDefaultTextVisible = false; + } + + /** + * Sets the foreground color. + * + * @param c the color to set for the text field foreground + */ + public void setForegroundColor(Color c) + { + foregroundColor = c; + } + + /** + * Sets the foreground color of the default text shown in this text field. + * + * @param c the color to set + */ + public void setDefaultTextColor(Color c) + { + defaultTextColor = c; + + if (isDefaultTextVisible) + setForeground(defaultTextColor); + } + + /** + * Notifies all registered <tt>TextFieldChangeListener</tt>s that a change + * has occurred in the text contained in this field. + * @param eventType the type of the event to transfer + */ + private void fireTextFieldChangeListener(int eventType) + { + for (TextFieldChangeListener l : changeListeners) + switch (eventType) + { + case 0: l.textInserted(); break; + case 1: l.textRemoved(); break; + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SIPCommToggleButton.java b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommToggleButton.java new file mode 100644 index 0000000..e730341 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SIPCommToggleButton.java @@ -0,0 +1,444 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + +import org.jvnet.lafwidget.animation.*; + +/** + * The <tt>SIPCommToggleButton</tt> is a flexible <tt>JToggleButton</tt> that + * allows to configure its background, its icon, the look when a mouse is over + * it, etc. + * + * @author Yana Stamcheva + */ +public class SIPCommToggleButton + extends JToggleButton + implements OrderedComponent +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * The background image shown in normal button state. + */ + private Image bgImage; + + /** + * The background image shown in rollover button state. + */ + private Image bgRolloverImage; + + /** + * The icon image shown in normal (non-pressed) button state. + */ + private Image iconImage; + + /** + * The background image shown in pressed button state. + */ + private Image pressedImage; + + /** + * The icon image shown in pressed button state. + */ + private Image pressedIconImage; + + /** + * The index of the button, used when we want to order our buttons. + */ + private int index = -1; + + /** + * Creates an instance of <tt>SIPCommToggleButton</tt>. + */ + public SIPCommToggleButton() + { + // Explicitly remove all borders that may be set from the current + // look and feel. + this.setBorder(null); + + MouseRolloverHandler mouseHandler = new MouseRolloverHandler(); + + this.addMouseListener(mouseHandler); + this.addMouseMotionListener(mouseHandler); + } + + /** + * Creates a button with custom background image and rollover image. + * + * @param bgImage the background button image + * @param rolloverImage the rollover button image + */ + public SIPCommToggleButton(Image bgImage, Image rolloverImage) + { + this(bgImage, rolloverImage, null, null); + } + + /** + * Creates a button with custom background image, rollover image and + * icon image. + * + * @param bgImage The background image. + * @param rolloverImage The roll over image. + * @param iconImage The icon. + * @param pressedImage The image used to paint the pressed state. + */ + public SIPCommToggleButton( Image bgImage, + Image rolloverImage, + Image iconImage, + Image pressedImage) + { + this(bgImage, rolloverImage, iconImage, pressedImage, null); + } + + /** + * Creates a button with custom background image, rollover image and + * icon image. + * + * @param bgImage the background image + * @param rolloverImage the roll over image + * @param iconImage the icon + * @param pressedImage the image used to paint the pressed state + * @param pressedIconImage the icon image in a pressed state + */ + public SIPCommToggleButton( Image bgImage, + Image rolloverImage, + Image iconImage, + Image pressedImage, + Image pressedIconImage) + { + this(); + + this.bgImage = bgImage; + this.bgRolloverImage = rolloverImage; + this.iconImage = iconImage; + this.pressedImage = pressedImage; + this.pressedIconImage = pressedIconImage; + + this.setPreferredSize( + new Dimension( this.bgImage.getWidth(null), + this.bgImage.getHeight(null))); + + if (iconImage != null) + this.setIcon(new ImageIcon(this.iconImage)); + } + + /** + * Overrides the <code>paintComponent</code> method of <tt>JButton</tt> + * to paint the button background and icon, and all additional effects + * of this configurable button. + * + * @param g the Graphics object + */ + + public void paintComponent(Graphics g) + { + g = g.create(); + try + { + internalPaintComponent(g); + } + finally + { + g.dispose(); + } + } + + /** + * Paints this button. + * + * @param g the Graphics object + */ + private void internalPaintComponent(Graphics g) + { + AntialiasingManager.activateAntialiasing(g); + + if (this.bgImage != null) + { + // If there's no icon, we make grey the backgroundImage + // when disabled. + if (!isEnabled()) + { + Image disabledImage = new ImageIcon(LightGrayFilter + .createDisabledImage(bgImage)).getImage(); + + g.drawImage(disabledImage, 0, 0, this); + } + else { + g.drawImage(this.bgImage, 0, 0, this); + } + } + + // Paint the roll over image. + if (getModel().isRollover() && bgRolloverImage != null) + { + g.drawImage(bgRolloverImage, 0, 0, this); + } + + // Paint the pressed image. + if (getModel().isSelected() && pressedImage != null) + { + g.drawImage(pressedImage, 0, 0, this); + } + + // Paint a roll over fade out. + FadeTracker fadeTracker = FadeTracker.getInstance(); + + float visibility = getModel().isRollover() ? 1.0f : 0.0f; + if (fadeTracker.isTracked(this, FadeKind.ROLLOVER)) + { + visibility = fadeTracker.getFade(this, FadeKind.ROLLOVER); + } + visibility /= 2; + + g.setColor(new Color(1.0f, 1.0f, 1.0f, visibility)); + + if (bgImage != null) + { + g.fillRoundRect(this.getWidth() / 2 - this.bgImage.getWidth(null) + / 2, this.getHeight() / 2 - this.bgImage.getHeight(null) / 2, + bgImage.getWidth(null), bgImage.getHeight(null), 10, 10); + } + else if (isContentAreaFilled() || (visibility != 0.0f)) + { + g.fillRoundRect(0, 0, this.getWidth(), this.getHeight(), 10, 10); + } + + // Paint the icon image. + Image iconImageFinal = null; + if (getModel().isSelected() && pressedIconImage != null) + { + iconImageFinal = pressedIconImage; + } + else if (iconImage != null) + { + if (!isEnabled()) + { + iconImageFinal = new ImageIcon(LightGrayFilter + .createDisabledImage(iconImage)).getImage(); + } + else + { + iconImageFinal = iconImage; + } + } + + int bgWidth = (bgImage != null) + ? bgImage.getWidth(null) + : getWidth(); + int bgHeight = (bgImage != null) + ? bgImage.getHeight(null) + : getHeight(); + + if (iconImageFinal != null) + g.drawImage(iconImageFinal, + (bgWidth - iconImageFinal.getWidth(null)) / 2, + (bgHeight - iconImageFinal.getHeight(null)) / 2, this); + } + + /** + * Returns the background image of this button. + * + * @return the background image of this button + */ + public Image getBgImage() + { + return bgImage; + } + + /** + * Sets the background image to this button. + * + * @param bgImage the background image to set + */ + public void setBgImage(Image bgImage) + { + this.bgImage = bgImage; + + this.setPreferredSize(new Dimension(this.bgImage.getWidth(null), + this.bgImage.getHeight(null))); + } + + /** + * Returns the background rollover image of this button. + * + * @return the background rollover image of this button + */ + public Image getBgRolloverImage() + { + return bgRolloverImage; + } + + /** + * Sets the background rollover image to this button. + * + * @param bgRolloverImage the background rollover image to set + */ + public void setBgRolloverImage(Image bgRolloverImage) + { + this.bgRolloverImage = bgRolloverImage; + } + + /** + * Returns the icon image of this button. + * + * @return the icon image of this button + */ + public Image getIconImage() + { + return iconImage; + } + + /** + * Sets the icon image to this button. + * + * @param iconImage the icon image to set + */ + public void setIconImage(Image iconImage) + { + this.iconImage = iconImage; + this.repaint(); + } + + /** + * Sets the icon image for the pressed state of this button. + * + * @param iconImage the icon image to set + */ + public void setPressedIconImage(Image iconImage) + { + this.pressedIconImage = iconImage; + this.repaint(); + } + + /** + * Sets the image representing the pressed state of this button. + * + * @param pressedImage the image representing the pressed state of this + * button + */ + public void setPressedImage(Image pressedImage) + { + this.pressedImage = pressedImage; + this.repaint(); + } + + /** + * Change buttons index when we want to order it. + * @param index the button index. + */ + public void setIndex(int index) + { + this.index = index; + } + + /** + * Returns the current button index we have set, or -1 if none used. + * @return + */ + public int getIndex() + { + return this.index; + } + + /** + * The <tt>ButtonRepaintCallback</tt> is charged to repaint this button + * when the fade animation is performed. + */ + private class ButtonRepaintCallback implements FadeTrackerCallback + { + public void fadeEnded(FadeKind arg0) + { + repaintLater(); + } + + public void fadePerformed(FadeKind arg0, float arg1) + { + repaintLater(); + } + + private void repaintLater() + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + SIPCommToggleButton.this.repaint(); + } + }); + } + + public void fadeReversed(FadeKind arg0, boolean arg1, float arg2) + { + } + } + + /** + * Perform a fade animation on mouse over. + */ + private class MouseRolloverHandler + implements MouseListener, + MouseMotionListener + { + public void mouseMoved(MouseEvent e) + { + } + + public void mouseExited(MouseEvent e) + { + if (isEnabled()) + { + getModel().setRollover(false); + + FadeTracker fadeTracker = FadeTracker.getInstance(); + + fadeTracker.trackFadeOut(FadeKind.ROLLOVER, + SIPCommToggleButton.this, + true, + new ButtonRepaintCallback()); + } + } + + public void mouseClicked(MouseEvent e) + { + } + + public void mouseEntered(MouseEvent e) + { + if (isEnabled()) + { + getModel().setRollover(true); + + FadeTracker fadeTracker = FadeTracker.getInstance(); + + fadeTracker.trackFadeIn(FadeKind.ROLLOVER, + SIPCommToggleButton.this, + true, + new ButtonRepaintCallback()); + } + } + + public void mousePressed(MouseEvent e) + { + } + + public void mouseReleased(MouseEvent e) + { + } + + public void mouseDragged(MouseEvent e) + { + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/ScreenInformation.java b/src/net/java/sip/communicator/plugin/desktoputil/ScreenInformation.java new file mode 100644 index 0000000..192fc6b --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/ScreenInformation.java @@ -0,0 +1,68 @@ +/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.desktoputil;
+
+import java.awt.*;
+
+/**
+ * A class which reads the screen bounds and provides this information.
+ *
+ * @author Ingo Bauersachs
+ */
+public class ScreenInformation
+{
+ /**
+ * Calculates the bounding box of all available screens. This method is
+ * highly inaccurate when screens of different sizes are used or not evenly
+ * aligned. A correct implementation should generate a polygon.
+ *
+ * @return A polygon of the usable screen area.
+ */
+ public static Rectangle getScreenBounds()
+ {
+ final GraphicsEnvironment ge = GraphicsEnvironment
+ .getLocalGraphicsEnvironment();
+
+ Rectangle bounds = new Rectangle();
+ for(GraphicsDevice gd : ge.getScreenDevices())
+ {
+ GraphicsConfiguration gc = gd.getDefaultConfiguration();
+ bounds = bounds.union(gc.getBounds());
+ }
+ return bounds;
+ }
+
+ /**
+ * Checks whether the top edge of the rectangle is contained in any of the
+ * available screens.
+ *
+ * @param window The bounding box of the window.
+ * @return True when the top edge is in a visible screen area; false
+ * otherwise
+ */
+ public static boolean isTitleOnScreen(Rectangle window)
+ {
+ final GraphicsEnvironment ge = GraphicsEnvironment
+ .getLocalGraphicsEnvironment();
+
+ boolean leftInside = false;
+ boolean rightInside = false;
+ Point left = new Point(window.x, window.y);
+ Point right = new Point(window.x, window.y + window.width);
+ for(GraphicsDevice gd : ge.getScreenDevices())
+ {
+ GraphicsConfiguration gc = gd.getDefaultConfiguration();
+ if(gc.getBounds().contains(left))
+ leftInside = true;
+ if(gc.getBounds().contains(right))
+ rightInside = true;
+ if(leftInside && rightInside)
+ return true;
+ }
+ return leftInside && rightInside;
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SelectedObject.java b/src/net/java/sip/communicator/plugin/desktoputil/SelectedObject.java new file mode 100644 index 0000000..aae3c0a --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SelectedObject.java @@ -0,0 +1,80 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import javax.swing.*; + +/** + * A convenience class used to store combobox complex objects. + * The <tt>SelectedObject</tt> is used for all account and status combo boxes + * throughout this gui implementation. + * + * @author Yana Stamcheva + */ +public class SelectedObject +{ + private String text; + + private Icon icon; + + private Object object; + + /** + * Creates an instance of <tt>SelectedObject</tt> by specifying the text, + * icon and object associated with it. + * + * @param text The text. + * @param icon The icon. + * @param object The object. + */ + public SelectedObject(String text, Icon icon, Object object) + { + this.text = text; + this.icon = icon; + this.object = object; + } + + /** + * Creates an instance of <tt>SelectedObject</tt> by specifying the + * icon and object associated with it. + * + * @param icon The icon. + * @param object The object. + */ + public SelectedObject(Icon icon, Object object) + { + this.icon = icon; + this.object = object; + } + + /** + * Returns the text of this <tt>SelectedObject</tt>. + * @return the text of this <tt>SelectedObject</tt>. + */ + public String getText() + { + return text; + } + + /** + * Returns the icon of this <tt>SelectedObject</tt>. + * @return the icon of this <tt>SelectedObject</tt>. + */ + public Icon getIcon() + { + return icon; + } + + /** + * Returns the real object behind this <tt>SelectedObject</tt>. + * @return the real object behind this <tt>SelectedObject</tt>. + */ + public Object getObject() + { + return object; + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SipCommFileChooser.java b/src/net/java/sip/communicator/plugin/desktoputil/SipCommFileChooser.java new file mode 100644 index 0000000..6948f1e --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SipCommFileChooser.java @@ -0,0 +1,99 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.io.*; + +/** + * The purpose of this interface is to provide an unique way to access to the + * methods of JFileChooser (javax.swing) or FileDialog (java.awt). + * + * It's interesting to use FileDialog under Mac OS X because it uses the native + * user interface for file selection provided by Mac OS which is more practical + * than the user interface performed by a JFileChooser (on Mac). + * + * Therefore, under other platforms (Microsoft Windows, Linux), the use of + * JFileChooser instead of FileDialog performs a better user interface for + * browsing among a file hierarchy. + * + * @author Valentin Martinet + */ +public interface SipCommFileChooser +{ + + /** + * Allows to request a 'load file' dialog (optional) + */ + public static int LOAD_FILE_OPERATION = 0; + + /** + * Allows to request a 'save file' dialog (optional) + */ + public static int SAVE_FILE_OPERATION = 1; + + /** + * Instruction to display only files. + */ + public static final int FILES_ONLY = 0; + + /** + * Instruction to display only directories in + * file chooser dialog. + */ + public static final int DIRECTORIES_ONLY = 1; + + /** + * Change the selection mode for the file choose. + * Possible values are DIRECTORIES_ONLY or FILES_ONLY, default is + * FILES_ONLY. + * + * @param mode the mode to use. + */ + public void setSelectionMode(int mode); + + /** + * Returns the selected file by the user from the dialog. + * + * @return File the selected file from the dialog + */ + public File getApprovedFile(); + + /** + * Sets the default path to be considered for browsing among files. + * + * @param path the default start path for this dialog + */ + public void setStartPath(String path); + + /** + * Shows the dialog and returns the selected file. + * + * @return File the selected file in this dialog + */ + public File getFileFromDialog(); + + /** + * Adds a file filter to this dialog. + * + * @param filter the filter to add + */ + public void addFilter(SipCommFileFilter filter); + + /** + * Sets a file filter to this dialog. + * + * @param filter the filter to add + */ + public void setFileFilter(SipCommFileFilter filter); + + /** + * Returns the filter the user has chosen for saving a file. + * + * @return SipCommFileFilter the used filter when saving a file + */ + public SipCommFileFilter getUsedFilter(); +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SipCommFileChooserImpl.java b/src/net/java/sip/communicator/plugin/desktoputil/SipCommFileChooserImpl.java new file mode 100644 index 0000000..60c09ac --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SipCommFileChooserImpl.java @@ -0,0 +1,163 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.io.*; + +import javax.swing.*; + +/** + * Implements <tt>SipCommFileChooser</tt> for Swing's <tt>JFileChooser</tt>. + * + * @author Valentin Martinet + */ +public class SipCommFileChooserImpl + extends JFileChooser + implements SipCommFileChooser +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * Parent component of this dialog (JFrame, Frame, etc) + */ + private Component parent; + + /** + * Constructor + * + * @param title title for this dialog + * @param operation 'Save file' or 'Load file' operation + */ + public SipCommFileChooserImpl(String title, int operation) + { + super(); + + this.setDialogTitle(title); + this.setDialogType(operation); + } + + /** + * Initializes a new <tt>SipCommFileChooserImpl</tt> instance. + * + * @param parent + * @param path + * @param title + * @param operation + */ + public SipCommFileChooserImpl( + Component parent, String path, String title, int operation) + { + this(title, operation); + + this.parent = parent; + this.setStartPath(path); + } + + /** + * Returns the selected file by the user from the dialog. + * + * @return File the selected file from the dialog + */ + public File getApprovedFile() + { + return this.getSelectedFile(); + } + + /** + * Sets the default path to be considered for browsing among files. + * + * @param path the default start path for this dialog + */ + public void setStartPath(String path) + { + // Passing null makes JFileChooser points to user's default dir. + File file = (path == null) ? null : new File(path); + + setCurrentDirectory(file); + + /* + * If the path doesn't exist, the intention of the caller may have been + * to also set a default file name. + */ + if ((file != null) && !file.isDirectory()) + setSelectedFile(file); + } + + /** + * Shows the dialog and returns the selected file. + * + * @return File the selected file in this dialog + */ + public File getFileFromDialog() + { + int choice = -1; + + if(this.getDialogType() == JFileChooser.OPEN_DIALOG) + choice = this.showOpenDialog(this.getParentComponent()); + else + choice = this.showSaveDialog(this.getParentComponent()); + + return + (choice == JFileChooser.APPROVE_OPTION) ? getSelectedFile() : null; + } + + /** + * Returns the parent component of this dialog + * + * @return Component dialog's parent component + */ + public Component getParentComponent() + { + return this.parent; + } + + /** + * Adds a file filter to this dialog. + * + * @param filter the filter to add + */ + public void addFilter(SipCommFileFilter filter) + { + this.addChoosableFileFilter(filter); + } + + /** + * Sets a file filter to this dialog. + * + * @param filter the filter to add + */ + public void setFileFilter(SipCommFileFilter filter) + { + super.setFileFilter(filter); + } + + /** + * Returns the filter the user has chosen for saving a file. + * + * @return SipCommFileFilter the used filter when saving a file + */ + public SipCommFileFilter getUsedFilter() + { + return (SipCommFileFilter)this.getFileFilter(); + } + + /** + * Change the selection mode for the file choose. + * Possible values are DIRECTORIES_ONLY or FILES_ONLY, default is + * FILES_ONLY. + * + * @param mode the mode to use. + */ + public void setSelectionMode(int mode) + { + super.setFileSelectionMode(mode); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SipCommFileDialogImpl.java b/src/net/java/sip/communicator/plugin/desktoputil/SipCommFileDialogImpl.java new file mode 100644 index 0000000..cf963b0 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SipCommFileDialogImpl.java @@ -0,0 +1,162 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.io.*; + +import org.jitsi.util.*; + +/** + * Implements <tt>SipCommFileChooser</tt> for AWT's <tt>FileDialog</tt>. + * + * @author Valentin Martinet + */ +public class SipCommFileDialogImpl + extends FileDialog + implements SipCommFileChooser +{ + /** + * The serialization-related version of the <tt>SipCommFileDialogImpl</tt> + * class explicitly defined to silence a related warning (e.g. in Eclipse + * IDE) since the <tt>SipCommFileDialogImpl</tt> class does not add instance + * fields. + */ + private static final long serialVersionUID = 0L; + + /** + * The selection mode, the default is files only, can be changed to + * DIRECTORIES_ONLY. + */ + private int selectionMode = FILES_ONLY; + + /** + * Constructor + * + * @param parent the parent frame of this dialog + * @param title the title for this dialog + */ + public SipCommFileDialogImpl(Frame parent, String title) + { + super(parent, title); + } + + /** + * Constructor + * + * @param parent the parent frame of this dialog + * @param title the title for this dialog + * @param fileOperation request a 'load file' or 'save file' dialog + */ + public SipCommFileDialogImpl(Frame parent, String title, int fileOperation) + { + super(parent, title, fileOperation); + } + + /** + * Returns the selected file by the user from the dialog. + * + * @return File the selected file from the dialog + */ + public File getApprovedFile() + { + String file = getFile(); + + return (file != null) ? new File(getDirectory(), file) : null; + } + + /** + * Sets the default path to be considered for browsing among files. + * + * @param path the default start path for this dialog + */ + public void setStartPath(String path) + { + File file = (path == null) ? null : new File(path); + + if ((file != null) && !file.isDirectory()) + { + setDirectory(file.getParent()); + setFile(file.getName()); + } + else + setDirectory(path); + } + + /** + * Shows the dialog and returns the selected file. + * + * @return File the selected file in this dialog + */ + public File getFileFromDialog() + { + this.setVisible(true); + + return this.getApprovedFile(); + } + + /** + * Adds a file filter to this dialog. + * + * @param filter the filter to add + */ + public void addFilter(SipCommFileFilter filter) + { + this.setFilenameFilter(filter); + } + + /** + * Sets a file filter to this dialog. + * + * @param filter the filter to add + */ + public void setFileFilter(SipCommFileFilter filter) + { + this.setFilenameFilter(filter); + } + + /** + * Returns the filter the user has chosen for saving a file. + * + * @return SipCommFileFilter the used filter when saving a file + */ + public SipCommFileFilter getUsedFilter() + { + return (SipCommFileFilter)this.getFilenameFilter(); + } + + /** + * Change the selection mode for the file choose. + * Possible values are DIRECTORIES_ONLY or FILES_ONLY, default is + * FILES_ONLY. + * + * @param mode the mode to use. + */ + public void setSelectionMode(int mode) + { + this.selectionMode = mode; + } + + /** + * Shows or hides the file chooser dialog. + * @param b if <code>true</code>, shows the dialog; + * otherwise, hides it + */ + public void setVisible(boolean b) + { + // workaround to make sure we choose only folders on macosx + if(OSUtils.IS_MAC && selectionMode == DIRECTORIES_ONLY) + System.setProperty( + "apple.awt.fileDialogForDirectories", "true"); + + super.setVisible(b); + + if(OSUtils.IS_MAC && selectionMode == DIRECTORIES_ONLY) + System.setProperty( + "apple.awt.fileDialogForDirectories", "false"); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SipCommFileFilter.java b/src/net/java/sip/communicator/plugin/desktoputil/SipCommFileFilter.java new file mode 100644 index 0000000..47137cd --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SipCommFileFilter.java @@ -0,0 +1,52 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.io.*; + +import javax.swing.filechooser.FileFilter; + + +/** + * The purpose of this interface is to provide an generic file filter type for + * the SipCommFileChooser, which is used either as an AWT FileDialog, either as + * a Swing JFileChooser. + * + * Both of these dialogs use their own filter type, FileFilter (class) for + * JFileChooser and FilenameFilter (interface) for FileDialog. + * + * SipCommFileFilter acts as both an implementation and an heritage from these + * two filters. To use a your own file filter with a SipCommFileChooser, you + * just have to extend from SipCommFileFilter and redefine at least the method + * 'public boolean accept(File f)' which is described in the Java FileFilter + * class. + * + * You won't have to redefine 'public boolean accept(File dir, String name)' + * from the Java FilenameFilter interface since it's done here: the method is + * transfered toward the accept method of Java FileFilter class. + * + * @author Valentin Martinet + */ +public abstract class SipCommFileFilter + extends FileFilter + implements FilenameFilter +{ + + /** + * Avoid to be obliged to implement + * 'public boolean accept(File dir, String name)' + * in your own file filter. + * + * @param dir file's parent directory + * @param name file's name + * @return boolean if the file is accepted or not + */ + public boolean accept(File dir, String name) + { + return accept(new File(dir.getAbsolutePath(), name)); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SoundLevelIndicator.java b/src/net/java/sip/communicator/plugin/desktoputil/SoundLevelIndicator.java new file mode 100644 index 0000000..b3a85c0 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SoundLevelIndicator.java @@ -0,0 +1,402 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; + +import javax.swing.*; + +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.skin.*; + +import org.jitsi.service.resources.*; + +/** + * Represents the sound level indicator for a particular peer. + * + * @author Dilshan Amadoru + * @author Yana Stamcheva + * @author Adam Netocny + * @author Lyubomir Marinov + */ +public class SoundLevelIndicator + extends TransparentPanel + implements Skinnable +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + private static final String SOUND_LEVEL_ACTIVE_LEFT + = "service.gui.soundlevel.SOUND_LEVEL_ACTIVE_LEFT"; + + private static final String SOUND_LEVEL_ACTIVE_LEFT_GRADIENT + = "service.gui.soundlevel.SOUND_LEVEL_ACTIVE_LEFT_GRADIENT"; + + private static final String SOUND_LEVEL_ACTIVE_MIDDLE + = "service.gui.soundlevel.SOUND_LEVEL_ACTIVE_MIDDLE"; + + private static final String SOUND_LEVEL_ACTIVE_RIGHT + = "service.gui.soundlevel.SOUND_LEVEL_ACTIVE_RIGHT"; + + private static final String SOUND_LEVEL_ACTIVE_RIGHT_GRADIENT + = "service.gui.soundlevel.SOUND_LEVEL_ACTIVE_RIGHT_GRADIENT"; + + private static final String SOUND_LEVEL_INACTIVE_LEFT + = "service.gui.soundlevel.SOUND_LEVEL_INACTIVE_LEFT"; + + private static final String SOUND_LEVEL_INACTIVE_MIDDLE + = "service.gui.soundlevel.SOUND_LEVEL_INACTIVE_MIDDLE"; + + private static final String SOUND_LEVEL_INACTIVE_RIGHT + = "service.gui.soundlevel.SOUND_LEVEL_INACTIVE_RIGHT"; + + /** + * The maximum possible sound level. + */ + private final int maxSoundLevel; + + /** + * The minimum possible sound level. + */ + private final int minSoundLevel; + + /** + * The number of (distinct) sound bars displayed by this instance. + */ + private int soundBarCount; + + /** + * The sound level which is currently depicted by this + * <tt>SoundLevelIndicator</tt>. + */ + private int soundLevel; + + /** + * Image when a sound level block is active + */ + private ImageIcon soundLevelActiveImageLeft; + + /** + * Image when a sound level block is active + */ + private ImageIcon soundLevelActiveImageLeftGradient; + + /** + * Image when a sound level block is active + */ + private ImageIcon soundLevelActiveImageMiddle; + + /** + * Image when a sound level block is active + */ + private ImageIcon soundLevelActiveImageRight; + + /** + * Image when a sound level block is active + */ + private ImageIcon soundLevelActiveImageRightGradient; + + /** + * Image when a sound level block is not active + */ + private ImageIcon soundLevelInactiveImageLeft; + + /** + * Image when a sound level block is not active + */ + private ImageIcon soundLevelInactiveImageMiddle; + + /** + * Image when a sound level block is not active + */ + private ImageIcon soundLevelInactiveImageRight; + + /** + * A runnable that will be used to update the sound level. + */ + private LevelUpdate levelUpdate = new LevelUpdate(); + + /** + * Initializes a new <tt>SoundLevelIndicator</tt> instance. + * + * @param minSoundLevel the minimum possible sound level + * @param maxSoundLevel the maximum possible sound level + */ + public SoundLevelIndicator(int minSoundLevel, int maxSoundLevel) + { + this.minSoundLevel = minSoundLevel; + this.maxSoundLevel = maxSoundLevel; + this.soundLevel = minSoundLevel; + + loadSkin(); + + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + } + + /** + * Update the sound level indicator component to fit the given values. + * + * @param soundLevel the sound level to show + */ + public void updateSoundLevel(int soundLevel) + { + levelUpdate.setSoundLevel(soundLevel); + LowPriorityEventQueue.invokeLater(levelUpdate); + } + + /** + * Update the sound level indicator component to fit the given values. + * + * @param soundLevel the sound level to show + */ + private void updateSoundLevelInternal(int soundLevel) + { + int range = 1; + + // Check if the given range values are correct. + if ((minSoundLevel > -1) + && (maxSoundLevel > -1) + && (minSoundLevel < maxSoundLevel)) + { + range = maxSoundLevel - minSoundLevel; + + if (soundLevel < 40 /* A WHISPER */) + soundLevel = minSoundLevel; + else if (soundLevel > 85 /* BEGINNING OF HEARING DAMAGE */) + soundLevel = maxSoundLevel; + else + { + /* + * Depict the range between "A WHISPER" and "BEGINNING OF + * HEARING DAMAGE". + */ + soundLevel = (int) (((soundLevel - 40.0) / 45.0) * range); + if (soundLevel < minSoundLevel) + soundLevel = minSoundLevel; + else if (soundLevel > maxSoundLevel) + soundLevel = maxSoundLevel; + } + } + + /* + * Audacity uses 0.9 for this.soundLevel and, consequently, 0.1 for + * soundLevel but that makes the animation too slow. + */ + this.soundLevel = (int) (this.soundLevel * 0.8 + soundLevel * 0.2); + + int activeSoundBarCount + = Math.round(this.soundLevel * soundBarCount / (float) range); + + /* + * We cannot use getComponentCount() and then call getComponent(int) + * because there are multiple threads involved and the code bellow is + * not executed on the UI thread i.e. ArrayIndexOutOfBounds may and do + * happen. + */ + Component[] components = getComponents(); + + for (int i = 0; i < components.length; i++) + { + Component c = getComponent(i); + + if (c instanceof JLabel) + { + Icon activeIcon = null; + Icon inactiveIcon = null; + if (i == 0) + { + if (activeSoundBarCount == 1) + activeIcon = soundLevelActiveImageLeftGradient; + else + { + activeIcon = soundLevelActiveImageLeft; + inactiveIcon = soundLevelInactiveImageLeft; + } + } + else if (i == activeSoundBarCount - 1) + { + if (i == components.length - 1) + activeIcon = soundLevelActiveImageRight; + else + activeIcon = soundLevelActiveImageRightGradient; + } + else if (i == components.length - 1) + { + inactiveIcon = soundLevelInactiveImageRight; + } + else + { + activeIcon = soundLevelActiveImageMiddle; + inactiveIcon = soundLevelInactiveImageMiddle; + } + + ((JLabel) c).setIcon( + (i < activeSoundBarCount) + ? activeIcon + : inactiveIcon); + } + } + + repaint(); + } + + public void resetSoundLevel() + { + soundLevel = minSoundLevel; + updateSoundLevel(minSoundLevel); + } + + @Override + public void setBounds(int x, int y, int width, int height) + { + super.setBounds(x, y, width, height); + + int newSoundBarCount = getSoundBarCount(getWidth()); + + if (newSoundBarCount > 0) + { + while (newSoundBarCount < soundBarCount) + { + for (int i = getComponentCount() - 1; i >= 0; i--) + { + Component c = getComponent(i); + + if (c instanceof JLabel) + { + remove(c); + soundBarCount--; + break; + } + } + } + while (soundBarCount < newSoundBarCount) + { + JLabel soundBar; + if (soundBarCount == 0) + soundBar = new JLabel(soundLevelInactiveImageLeft); + else if (soundBarCount == newSoundBarCount - 1) + soundBar = new JLabel(soundLevelInactiveImageRight); + else + soundBar = new JLabel(soundLevelInactiveImageMiddle); + + soundBar.setVerticalAlignment(JLabel.CENTER); + add(soundBar); + soundBarCount++; + } + } + + updateSoundLevel(soundLevel); + revalidate(); + repaint(); + } + + /** + * Returns the number of sound level bars that we could currently show in + * this panel. + * + * @param width the current width of the call window + * @return the number of sound level bars that we could currently show in + * this panel + */ + private int getSoundBarCount(int width) + { + int soundBarWidth = soundLevelActiveImageLeft.getIconWidth(); + + return width / soundBarWidth; + } + + /** + * Reloads icons. + */ + public void loadSkin() + { + ResourceManagementService resources = DesktopUtilActivator.getResources(); + + soundLevelActiveImageLeft + = resources.getImage(SOUND_LEVEL_ACTIVE_LEFT); + soundLevelActiveImageLeftGradient + = resources.getImage(SOUND_LEVEL_ACTIVE_LEFT_GRADIENT); + soundLevelActiveImageMiddle + = resources.getImage(SOUND_LEVEL_ACTIVE_MIDDLE); + soundLevelActiveImageRight + = resources.getImage(SOUND_LEVEL_ACTIVE_RIGHT); + soundLevelActiveImageRightGradient + = resources.getImage(SOUND_LEVEL_ACTIVE_RIGHT_GRADIENT); + + soundLevelInactiveImageLeft + = resources.getImage(SOUND_LEVEL_INACTIVE_LEFT); + soundLevelInactiveImageMiddle + = resources.getImage(SOUND_LEVEL_INACTIVE_MIDDLE); + soundLevelInactiveImageRight + = resources.getImage(SOUND_LEVEL_INACTIVE_RIGHT); + + if (!isPreferredSizeSet()) + { + int preferredHeight = 0; + int preferredWidth = 0; + + if (soundLevelActiveImageLeft != null) + { + int height = soundLevelActiveImageLeft.getIconHeight(); + int width = soundLevelActiveImageLeft.getIconWidth(); + + if (preferredHeight < height) + preferredHeight = height; + if (preferredWidth < width) + preferredWidth = width; + } + if (soundLevelInactiveImageLeft != null) + { + int height = soundLevelInactiveImageLeft.getIconHeight(); + int width = soundLevelInactiveImageLeft.getIconWidth(); + + if (preferredHeight < height) + preferredHeight = height; + if (preferredWidth < width) + preferredWidth = width; + } + if ((preferredHeight > 0) && (preferredWidth > 0)) + setPreferredSize( + new Dimension( + 10 * preferredWidth, + preferredHeight)); + } + + updateSoundLevel(soundLevel); + } + + /** + * Runnable used to update sound levels. + */ + private class LevelUpdate + implements Runnable + { + /** + * The current sound level to update. + */ + private int soundLevel; + + /** + * Update. + */ + public void run() + { + updateSoundLevelInternal(soundLevel); + } + + /** + * Changes the sound level. + * @param soundLevel changes the sound level. + */ + public void setSoundLevel(int soundLevel) + { + this.soundLevel = soundLevel; + } + }; +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/StyledHTMLEditorPane.java b/src/net/java/sip/communicator/plugin/desktoputil/StyledHTMLEditorPane.java new file mode 100644 index 0000000..b81ea01 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/StyledHTMLEditorPane.java @@ -0,0 +1,108 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.io.*; + +import javax.swing.*; +import javax.swing.text.*; +import javax.swing.text.html.*; + +import net.java.sip.communicator.util.*; + +/** + * A custom styled HTML editor pane. + * + * @author Yana Stamcheva + */ +public class StyledHTMLEditorPane + extends JEditorPane +{ + /** + * The serial version id. + */ + private static final long serialVersionUID = 1L; + + /** + * The logger for this class. + */ + private final Logger logger = Logger.getLogger(StyledHTMLEditorPane.class); + + /** + * The editor kit of this editor pane. + */ + private final HTMLEditorKit editorKit; + + /** + * The document of this editor pane. + */ + private final HTMLDocument document; + + /** + * Creates an instance of <tt>StyledHTMLEditorPane</tt>. + */ + public StyledHTMLEditorPane() + { + editorKit = new SIPCommHTMLEditorKit(this); + + this.document = (HTMLDocument) editorKit.createDefaultDocument(); + + this.setContentType("text/html"); + this.setEditorKitForContentType("text/html", editorKit); + this.setEditorKit(editorKit); + this.setDocument(document); + + putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE); + } + + /** + * Appends text to end of the editor pane. + * + * @param text the text to append + */ + public void appendToEnd(String text) + { + Element root = document.getDefaultRootElement(); + try + { + document.insertAfterEnd(root + .getElement(root.getElementCount() - 1), text); + } + catch (BadLocationException e) + { + logger.error("Insert in the HTMLDocument failed.", e); + } + catch (IOException e) + { + logger.error("Insert in the HTMLDocument failed.", e); + } + } + + /** + * Inserts the given text in the beginning of the editor pane. + * + * @param text the text to insert + */ + public void insertAfterStart(String text) + { + Element root = this.document.getDefaultRootElement(); + + try + { + this.document.insertBeforeStart(root + .getElement(0), text); + } + catch (BadLocationException e) + { + logger.error("Insert in the HTMLDocument failed.", e); + } + catch (IOException e) + { + logger.error("Insert in the HTMLDocument failed.", e); + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/SwingWorker.java b/src/net/java/sip/communicator/plugin/desktoputil/SwingWorker.java new file mode 100644 index 0000000..7f64b84 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/SwingWorker.java @@ -0,0 +1,229 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + * + * Based on the 3rd version of SwingWorker (also known as SwingWorker 3), an + * abstract class that you subclass to perform GUI-related work in a dedicated + * thread. For instructions on using this class, see: + * + * http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html + * + * Note that the API changed slightly in the 3rd version: + * You must now invoke start() on the SwingWorker after + * creating it. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.util.concurrent.*; + +import javax.swing.*; + +/** + * Utility class based on the javax.swing.SwingWorker. <tt>SwingWorker</tt> is + * an abstract class that you subclass to perform GUI-related work in a + * dedicated thread. In addition to the original SwingWorker this class takes + * care of exceptions occurring during the execution of the separate thread. It + * will call a catchException() method in the Swing thread if an exception + * occurs. + * + * @author Yana Stamcheva + * @author Lyubomir Marinov + */ +public abstract class SwingWorker +{ + /** + * The <tt>ExecutorService</tt> which is shared by the <tt>SwingWorker</tt> + * instances for the purposes of controlling the use of <tt>Thread</tt>s. + */ + private static ExecutorService executorService; + + /** + * The <tt>Callable</tt> implementation which is (to be) submitted to + * {@link #executorService} and invokes {@link #construct()} on behalf of + * this <tt>SwingWorker</tt>. + */ + private final Callable<Object> callable; + + /** + * The <tt>Future</tt> instance which represents the state and the return + * value of the execution of {@link #callable} i.e. {@link #construct()}. + */ + private Future<?> future; + + /** + * Start a thread that will call the <code>construct</code> method + * and then exit. + */ + public SwingWorker() + { + callable + = new Callable<Object>() + { + public Object call() + { + Object value = null; + + try + { + value = construct(); + } + catch (final Throwable t) + { + if (t instanceof ThreadDeath) + throw (ThreadDeath) t; + else + { + // catchException + SwingUtilities.invokeLater( + new Runnable() + { + public void run() + { + catchException(t); + } + }); + } + } + + // We only want to perform the finished if the thread hasn't + // been interrupted. + if (!Thread.currentThread().isInterrupted()) + // finished + SwingUtilities.invokeLater( + new Runnable() + { + public void run() + { + finished(); + } + }); + + return value; + } + }; + } + + /** + * Called on the event dispatching thread (not on the worker thread) + * if an exception has occurred during the <code>construct</code> method. + * + * @param exception the exception that has occurred + */ + protected void catchException(Throwable exception) + { + } + + /** + * Computes the value to be returned by {@link #get()}. + */ + protected abstract Object construct() + throws Exception; + + /** + * Called on the event dispatching thread (not on the worker thread) + * after the <code>construct</code> method has returned. + */ + protected void finished() + { + } + + /** + * Return the value created by the <code>construct</code> method. + * Returns null if either the constructing thread or the current + * thread was interrupted before a value was produced. + * + * @return the value created by the <code>construct</code> method + */ + public Object get() + { + Future<?> future; + + synchronized (this) + { + /* + * SwingWorker assigns a value to the future field only once and we + * do not want to invoke Future#cancel(true) while holding a lock. + */ + future = this.future; + } + + Object value = null; + + if (future != null) + { + boolean interrupted = false; + + do + { + try + { + value = future.get(); + break; + } + catch (CancellationException ce) + { + break; + } + catch (ExecutionException ee) + { + break; + } + catch (InterruptedException ie) + { + interrupted = true; + } + } + while (true); + if (interrupted) // propagate + Thread.currentThread().interrupt(); + } + + return value; + } + + /** + * A new method that interrupts the worker thread. Call this method + * to force the worker to stop what it's doing. + */ + public void interrupt() + { + Future<?> future; + + synchronized (this) + { + /* + * SwingWorker assigns a value to the future field only once and we + * do not want to invoke Future#cancel(true) while holding a lock. + */ + future = this.future; + } + + if (future != null) + future.cancel(true); + } + + /** + * Start the worker thread. + */ + public void start() + { + ExecutorService executorService; + + synchronized (SwingWorker.class) + { + if (SwingWorker.executorService == null) + SwingWorker.executorService = Executors.newCachedThreadPool(); + executorService = SwingWorker.executorService; + } + + synchronized (this) + { + if (future == null || future.isDone()) + future = executorService.submit(callable); + else + throw new IllegalStateException("future"); + } + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/plugin/desktoputil/TransparentPanel.java b/src/net/java/sip/communicator/plugin/desktoputil/TransparentPanel.java new file mode 100644 index 0000000..5b3f41e --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/TransparentPanel.java @@ -0,0 +1,30 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; + +/** + * Provides compatibility with source code written prior to the inception of + * libjitsi. + * + * @author Lyubomir Marinov + */ +public class TransparentPanel + extends org.jitsi.util.swing.TransparentPanel +{ + private static final long serialVersionUID = 0L; + + public TransparentPanel() + { + } + + public TransparentPanel(LayoutManager layout) + { + super(layout); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/TrimTextField.java b/src/net/java/sip/communicator/plugin/desktoputil/TrimTextField.java new file mode 100644 index 0000000..5b837c3 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/TrimTextField.java @@ -0,0 +1,36 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import javax.swing.*; + +/** + * A custom JTextField that trims the contents before returns it. + * + * @author Damian Minkov + */ +public class TrimTextField + extends JTextField +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * Returns the trimmed value of the text contained in the field. + * @return the trimmed value of the field. + */ + public String getText() + { + String txt = super.getText(); + if(txt != null) + return txt.trim(); + else + return null; + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/UIAction.java b/src/net/java/sip/communicator/plugin/desktoputil/UIAction.java new file mode 100644 index 0000000..2852acc --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/UIAction.java @@ -0,0 +1,26 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import javax.swing.*; + +/** + * A wrapper around AbstractAction, so we can differ our Actions. + * Used when adding actions to the action map, + * and actions will be triggered by keystrokes from the inputMap of the RootPane. + * + * @author Damian Minkov + */ +public abstract class UIAction + extends AbstractAction +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/WindowUtils.java b/src/net/java/sip/communicator/plugin/desktoputil/WindowUtils.java new file mode 100644 index 0000000..3ff5a77 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/WindowUtils.java @@ -0,0 +1,158 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.lang.reflect.*; +import java.util.*; +import java.util.List; + +/** + * Utility class for awt windows management. + * + * @author Yana Stamcheva + */ +public class WindowUtils +{ + /** + * The list of all <tt>Window</tt>s owned by this application. + */ + private static final List<Window> WINDOW_LIST; + + static + { + /* + * WINDOW_LIST is flawed because there are more calls to addWindow than + * to removeWindow. Java 6 has introduced Window#getWindows so try to + * use it instead. + */ + Method Window_getWindows = null; + + try + { + Window_getWindows = Window.class.getMethod("getWindows"); + } + catch (NoSuchMethodException nsme) + { + /* + * Ignore the exception because we are just checking whether the + * method exists. + */ + } + catch (SecurityException se) + { + } + WINDOW_LIST + = (Window_getWindows == null) ? new ArrayList<Window>() : null; + } + + /** + * Returns an array of all {@code Window}s, both owned and ownerless, + * created by this application. + * If called from an applet, the array includes only the {@code Window}s + * accessible by that applet. + * <p> + * <b>Warning:</b> this method may return system created windows, such + * as a print dialog. Applications should not assume the existence of + * these dialogs, nor should an application assume anything about these + * dialogs such as component positions, <code>LayoutManager</code>s + * or serialization. + * + * @return Returns an array of all {@code Window}s. + */ + public static Window[] getWindows() + { + if (WINDOW_LIST == null) + { + Method Window_getWindows = null; + + try + { + Window_getWindows = Window.class.getMethod("getWindows"); + } + catch (NoSuchMethodException nsme) + { + /* Ignore it because we cannot really do anything useful. */ + } + catch (SecurityException se) + { + } + + Object windows = null; + + if (Window_getWindows != null) + { + try + { + windows = Window_getWindows.invoke(null); + } + catch (ExceptionInInitializerError eiie) + { + /* Ignore it because we cannot really do anything useful. */ + } + catch (IllegalAccessException iae) + { + } + catch (IllegalArgumentException iae) + { + } + catch (InvocationTargetException ite) + { + } + catch (NullPointerException npe) + { + } + } + + return + (windows instanceof Window[]) + ? (Window[]) windows + : new Window[0]; + } + else + { + synchronized (WINDOW_LIST) + { + return WINDOW_LIST.toArray(new Window[WINDOW_LIST.size()]); + } + } + } + + /** + * Adds a {@link Window} into window list + * + * @param w {@link Window} to be added. + */ + public static void addWindow(Window w) + { + if (WINDOW_LIST != null) + { + synchronized (WINDOW_LIST) + { + if (!WINDOW_LIST.contains(w)) + WINDOW_LIST.add(w); + } + } + } + + /** + * Removes a {@link Window} into window list + * + * @param w {@link Window} to be removed. + */ + public static void removeWindow(Window w) + { + if (WINDOW_LIST != null) + { + synchronized (WINDOW_LIST) + { + WINDOW_LIST.remove(w); + } + } + } + +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/X509CertificatePanel.java b/src/net/java/sip/communicator/plugin/desktoputil/X509CertificatePanel.java new file mode 100644 index 0000000..e60c71c --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/X509CertificatePanel.java @@ -0,0 +1,423 @@ +/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.desktoputil;
+
+import java.awt.*;
+import java.security.*;
+import java.security.cert.*;
+import java.security.interfaces.*;
+import java.text.*;
+import java.util.*;
+
+import javax.naming.*;
+import javax.naming.ldap.*;
+import javax.security.auth.x500.*;
+import javax.swing.*;
+
+import net.java.sip.communicator.util.*;
+
+import org.jitsi.service.resources.*;
+
+/**
+ * Panel that shows the content of an X509Certificate.
+ */
+public class X509CertificatePanel
+ extends TransparentPanel
+{
+ private static final long serialVersionUID = -8368302061995971947L;
+
+ /**
+ * Constructs a X509 certificate panel.
+ *
+ * @param certificate <tt>X509Certificate</tt> object
+ */
+ public X509CertificatePanel(X509Certificate certificate)
+ {
+ ResourceManagementService R = DesktopUtilActivator.getResources();
+ DateFormat dateFormatter = DateFormat
+ .getDateInstance(DateFormat.MEDIUM);
+
+ Insets valueInsets = new Insets(2,10,0,0);
+ Insets titleInsets = new Insets(10,5,0,0);
+
+ setLayout(new GridBagLayout());
+
+ int currentRow = 0;
+
+ GridBagConstraints constraints = new GridBagConstraints();
+ constraints.anchor = GridBagConstraints.WEST;
+ constraints.fill = GridBagConstraints.HORIZONTAL;
+ constraints.insets = new Insets(2,5,0,0);
+ constraints.gridx = 0;
+ constraints.weightx = 0;
+ constraints.weighty = 0;
+ constraints.gridy = currentRow++;
+
+ X500Principal issuer = certificate.getIssuerX500Principal();
+ X500Principal subject = certificate.getSubjectX500Principal();
+
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_ISSUED_TO")),
+ constraints);
+
+ // subject
+ constraints.insets = valueInsets;
+ try
+ {
+ for(Rdn name : new LdapName(subject.getName()).getRdns())
+ {
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ String lbl =
+ R.getI18NString("service.gui.CERT_INFO_" + name.getType());
+ if (lbl
+ .equals("!service.gui.CERT_INFO_" + name.getType() + "!"))
+ lbl = name.getType();
+ add(new JLabel(lbl), constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(
+ name.getValue() instanceof byte[] ?
+ getHex((byte[])name.getValue()) + " ("
+ + new String((byte[]) name.getValue()) + ")"
+ : name.getValue().toString()),
+ constraints);
+ }
+ }
+ catch (InvalidNameException e1)
+ {
+ constraints.gridy = currentRow++;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_CN")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(subject.getName()),
+ constraints);
+ }
+
+ // issuer
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ constraints.insets = titleInsets;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_ISSUED_BY")),
+ constraints);
+ constraints.insets = valueInsets;
+ try
+ {
+ for(Rdn name : new LdapName(issuer.getName()).getRdns())
+ {
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ constraints.gridx = 0;
+ String lbl =
+ R.getI18NString("service.gui.CERT_INFO_" + name.getType());
+ if (lbl
+ .equals("!service.gui.CERT_INFO_" + name.getType() + "!"))
+ lbl = name.getType();
+ add(new JLabel(lbl), constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(
+ name.getValue() instanceof byte[] ?
+ getHex((byte[])name.getValue()) + " ("
+ + new String((byte[]) name.getValue()) + ")"
+ : name.getValue().toString()),
+ constraints);
+ }
+ }
+ catch (InvalidNameException e1)
+ {
+ constraints.gridy = currentRow++;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_CN")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(issuer.getName()),
+ constraints);
+ }
+
+ // validity
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ constraints.insets = titleInsets;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_VALIDITY")),
+ constraints);
+ constraints.insets = valueInsets;
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_ISSUED_ON")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(dateFormatter.format(certificate.getNotBefore())),
+ constraints);
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_EXPIRES_ON")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(dateFormatter.format(certificate.getNotAfter())),
+ constraints);
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ constraints.insets = titleInsets;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_FINGERPRINTS")),
+ constraints);
+ constraints.insets = valueInsets;
+
+ try
+ {
+ String sha1String = getThumbprint(certificate, "SHA1");
+ String md5String = getThumbprint(certificate, "MD5");
+
+ JTextArea sha1Area = new JTextArea(sha1String);
+ sha1Area.setLineWrap(false);
+ sha1Area.setOpaque(false);
+ sha1Area.setWrapStyleWord(true);
+ sha1Area.setEditable(false);
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel("SHA1:"),
+ constraints);
+
+ constraints.gridx = 1;
+ add(
+ sha1Area,
+ constraints);
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel("MD5:"),
+ constraints);
+
+ JTextArea md5Area = new JTextArea(md5String);
+ md5Area.setLineWrap(false);
+ md5Area.setOpaque(false);
+ md5Area.setWrapStyleWord(true);
+ md5Area.setEditable(false);
+
+ constraints.gridx = 1;
+ add(
+ md5Area,
+ constraints);
+ }
+ catch (Exception e)
+ {
+ // do nothing as we cannot show this value
+ }
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ constraints.insets = titleInsets;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_CERT_DETAILS")),
+ constraints);
+ constraints.insets = valueInsets;
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_SER_NUM")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(certificate.getSerialNumber().toString()),
+ constraints);
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_VER")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(String.valueOf(certificate.getVersion())),
+ constraints);
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_SIGN_ALG")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(String.valueOf(certificate.getSigAlgName())),
+ constraints);
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ constraints.insets = titleInsets;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_PUB_KEY_INFO")),
+ constraints);
+ constraints.insets = valueInsets;
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_ALG")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(certificate.getPublicKey().getAlgorithm()),
+ constraints);
+
+ if(certificate.getPublicKey().getAlgorithm().equals("RSA"))
+ {
+ RSAPublicKey key = (RSAPublicKey)certificate.getPublicKey();
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_PUB_KEY")),
+ constraints);
+
+ JTextArea pubkeyArea = new JTextArea(
+ R.getI18NString(
+ "service.gui.CERT_INFO_KEY_BYTES_PRINT",
+ new String[]{
+ String.valueOf(key.getModulus().toByteArray().length-1),
+ key.getModulus().toString(16)
+ }));
+ pubkeyArea.setLineWrap(false);
+ pubkeyArea.setOpaque(false);
+ pubkeyArea.setWrapStyleWord(true);
+ pubkeyArea.setEditable(false);
+
+ constraints.gridx = 1;
+ add(
+ pubkeyArea,
+ constraints);
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_EXP")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(key.getPublicExponent().toString()),
+ constraints);
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_KEY_SIZE")),
+ constraints);
+ constraints.gridx = 1;
+ add(
+ new JLabel(R.getI18NString(
+ "service.gui.CERT_INFO_KEY_BITS_PRINT",
+ new String[]{
+ String.valueOf(key.getModulus().bitLength())})),
+ constraints);
+ }
+ else if(certificate.getPublicKey().getAlgorithm().equals("DSA"))
+ {
+ DSAPublicKey key =
+ (DSAPublicKey)certificate.getPublicKey();
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel("Y:"), constraints);
+
+ JTextArea yArea = new JTextArea(key.getY().toString(16));
+ yArea.setLineWrap(false);
+ yArea.setOpaque(false);
+ yArea.setWrapStyleWord(true);
+ yArea.setEditable(false);
+
+ constraints.gridx = 1;
+ add(
+ yArea,
+ constraints);
+ }
+
+ constraints.gridy = currentRow++;
+ constraints.gridx = 0;
+ add(new JLabel(
+ R.getI18NString("service.gui.CERT_INFO_SIGN")),
+ constraints);
+
+ JTextArea signArea = new JTextArea(
+ R.getI18NString(
+ "service.gui.CERT_INFO_KEY_BYTES_PRINT",
+ new String[]{
+ String.valueOf(certificate.getSignature().length),
+ getHex(certificate.getSignature())
+ }));
+ signArea.setLineWrap(false);
+ signArea.setOpaque(false);
+ signArea.setWrapStyleWord(true);
+ signArea.setEditable(false);
+
+ constraints.gridx = 1;
+ add(
+ signArea,
+ constraints);
+ }
+
+ /**
+ * Converts the byte array to hex string.
+ * @param raw the data.
+ * @return the hex string.
+ */
+ private String getHex( byte [] raw )
+ {
+ if (raw == null)
+ return null;
+
+ StringBuilder hex = new StringBuilder(2 * raw.length);
+ Formatter f = new Formatter(hex);
+ for (byte b : raw)
+ {
+ f.format("%02x", b);
+ }
+ return hex.toString();
+ } /**
+ * Calculates the hash of the certificate known as the "thumbprint"
+ * and returns it as a string representation.
+ *
+ * @param cert The certificate to hash.
+ * @param algorithm The hash algorithm to use.
+ * @return The SHA-1 hash of the certificate.
+ * @throws CertificateException
+ */
+ private static String getThumbprint(X509Certificate cert, String algorithm)
+ throws CertificateException
+ {
+ MessageDigest digest;
+ try
+ {
+ digest = MessageDigest.getInstance(algorithm);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ throw new CertificateException(e);
+ }
+ byte[] encodedCert = cert.getEncoded();
+ StringBuilder sb = new StringBuilder(encodedCert.length * 2);
+ Formatter f = new Formatter(sb);
+ for (byte b : digest.digest(encodedCert))
+ {
+ f.format("%02x", b);
+ }
+ return sb.toString();
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/desktoputil/border/ExtendedEtchedBorder.java b/src/net/java/sip/communicator/plugin/desktoputil/border/ExtendedEtchedBorder.java new file mode 100644 index 0000000..62b5afe --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/border/ExtendedEtchedBorder.java @@ -0,0 +1,127 @@ +package net.java.sip.communicator.plugin.desktoputil.border; + +import java.awt.*; + +import javax.swing.border.*; + +public class ExtendedEtchedBorder + extends EtchedBorder +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * Thickness of the top line. + */ + private final int top; + + /** + * Thickness of the left line. + */ + private final int left; + + /** + * Thickness of the bottom line. + */ + private final int bottom; + + /** + * Thickness of the right line. + */ + private final int right; + + /** + * Creates an etched border with the specified etch-type and specified + * thickness of each border: top, left, bottom, right. + * + * @param etchType the type of etch to be drawn by the border + * @param top the thickness of the top border + * @param left the thickness of the left border + * @param bottom the thickness of the bottom border + * @param right the thickness of the right border + */ + public ExtendedEtchedBorder(int etchType, + int top, + int left, + int bottom, + int right) + { + super(etchType, null, null); + + this.top = top; + this.left = left; + this.bottom = bottom; + this.right = right; + } + + /** + * Paints the border for the specified component with the + * specified position and size. + * @param c the component for which this border is being painted + * @param g the paint graphics + * @param x the x position of the painted border + * @param y the y position of the painted border + * @param width the width of the painted border + * @param height the height of the painted border + */ + public void paintBorder(Component c, + Graphics g, + int x, + int y, + int width, + int height) + { + int w = width; + int h = height; + + Graphics2D g2 = (Graphics2D) g; + + g2.translate(x, y); + + g2.setColor(etchType == LOWERED ? getShadowColor(c) + : getHighlightColor(c)); + + if (top > 0) + { + g2.setStroke(new BasicStroke(top)); + g2.drawLine(0, 0, w-2, 0); + } + + if (left > 0) + { + g2.setStroke(new BasicStroke(left)); + g2.drawLine(0, 0, 0, h-2); + } + + if (bottom > 0) + { + g2.setStroke(new BasicStroke(bottom)); + g2.drawLine(0, h-2, w-2, h-2); + } + + if (right > 0) + { + g2.setStroke(new BasicStroke(right)); + g2.drawLine(w-2, 0, w-2, h-2); + } + + g2.setColor(etchType == LOWERED ? getHighlightColor(c) + : getShadowColor(c)); + + if (top > 0) + g2.drawLine(1, 1, w-3, 1); + + if (left > 0) + g2.drawLine(1, h-3, 1, 1); + + if (right > 0) + g2.drawLine(0, h-1, w-1, h-1); + + if (bottom > 0) + g2.drawLine(w-1, h-1, w-1, 0); + + g2.translate(-x, -y); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/desktoputil.manifest.mf b/src/net/java/sip/communicator/plugin/desktoputil/desktoputil.manifest.mf new file mode 100644 index 0000000..52b31ca --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/desktoputil.manifest.mf @@ -0,0 +1,64 @@ +Bundle-Activator: net.java.sip.communicator.plugin.desktoputil.DesktopUtilActivator +Bundle-Name: Jitsi Swing Utility Packages +Bundle-Description: A bundle that export packages with swing related utility classes. +Bundle-Vendor: jitsi.org +Bundle-Version: 0.0.1 +System-Bundle: yes +Import-Package: com.sun.awt, + javax.imageio, + javax.naming, + javax.naming.directory, + javax.naming.ldap, + javax.net, + javax.net.ssl, + javax.security.auth.x500, + javax.swing, + javax.swing.border, + javax.swing.event, + javax.swing.filechooser, + javax.swing.plaf, + javax.swing.plaf.basic, + javax.swing.plaf.metal, + javax.swing.table, + javax.swing.text, + javax.swing.text.html, + javax.swing.text.html.parser, + javax.swing.tree, + javax.xml.parsers, + javax.xml.transform, + javax.xml.transform.dom, + javax.xml.transform.stream, + net.java.sip.communicator.util, + net.java.sip.communicator.util.skin, + net.java.sip.communicator.plugin.desktoputil.dns, + net.java.sip.communicator.service.gui, + net.java.sip.communicator.service.resources, + net.java.sip.communicator.service.keybindings, + net.java.sip.communicator.service.netaddr, + net.java.sip.communicator.service.netaddr.event, + net.java.sip.communicator.service.contactlist, + net.java.sip.communicator.service.browserlauncher, + net.java.sip.communicator.service.protocol, + org.apache.xml.serialize, + org.jitsi.service.configuration, + org.jitsi.service.neomedia, + org.jitsi.service.neomedia.codec, + org.jitsi.service.resources, + org.jitsi.service.fileaccess, + org.jitsi.util, + org.jitsi.util.event, + org.jitsi.util.swing, + org.osgi.framework, + org.w3c.dom, + org.xbill.DNS, + org.xml.sax, + sun.awt.shell, + sun.net.dns, + sun.net.util +Export-Package: net.java.sip.communicator.plugin.desktoputil, + net.java.sip.communicator.plugin.desktoputil.border, + net.java.sip.communicator.plugin.desktoputil.event, + net.java.sip.communicator.plugin.desktoputil.plaf, + net.java.sip.communicator.plugin.desktoputil.transparent, + net.java.sip.communicator.plugin.desktoputil.wizard, + org.xbill.DNS
\ No newline at end of file diff --git a/src/net/java/sip/communicator/plugin/desktoputil/dns/ConfigurableDnssecResolver.java b/src/net/java/sip/communicator/plugin/desktoputil/dns/ConfigurableDnssecResolver.java new file mode 100644 index 0000000..d2f4fce --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/dns/ConfigurableDnssecResolver.java @@ -0,0 +1,356 @@ +/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.desktoputil.dns;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.service.notification.*;
+import net.java.sip.communicator.util.Logger;
+import net.java.sip.communicator.plugin.desktoputil.*;
+
+import org.jitsi.service.configuration.*;
+import org.jitsi.service.resources.*;
+import org.jitsi.util.*;
+import org.xbill.DNS.*;
+
+/**
+ * Resolver that wraps a DNSSEC capable resolver and handles validation
+ * failures according to the user's settings.
+ *
+ * @author Ingo Bauersachs
+ */
+public class ConfigurableDnssecResolver
+ extends UnboundResolver
+{
+ private final static Logger logger
+ = Logger.getLogger(ConfigurableDnssecResolver.class);
+
+ /**
+ * Name of the property that defines the default DNSSEC validation
+ * behavior.
+ */
+ public final static String PNAME_DNSSEC_VALIDATION_MODE
+ = "net.java.sip.communicator.util.dns.DNSSEC_VALIDATION_MODE";
+
+ /**
+ * Default value of {@link #PNAME_DNSSEC_VALIDATION_MODE}
+ */
+ public final static String PNAME_BASE_DNSSEC_PIN
+ = "net.java.sip.communicator.util.dns.pin";
+
+ final static String EVENT_TYPE = "DNSSEC_NOTIFICATION";
+
+ private ConfigurationService config
+ = DnsUtilActivator.getConfigurationService();
+ private ResourceManagementService R
+ = DnsUtilActivator.getResources();
+ private Map<String, Date> lastNotifications
+ = new HashMap<String, Date>();
+
+ /**
+ * Creates a new instance of this class. Tries to use the system's
+ * default forwarders.
+ */
+ public ConfigurableDnssecResolver()
+ {
+ super();
+ String forwarders = DnsUtilActivator.getConfigurationService()
+ .getString(DnsUtilActivator.PNAME_DNSSEC_NAMESERVERS);
+ if(!StringUtils.isNullOrEmpty(forwarders, true))
+ {
+ if(logger.isTraceEnabled())
+ {
+ logger.trace("Setting DNSSEC forwarders to: "
+ + Arrays.toString(forwarders.split(",")));
+ }
+ super.setForwarders(forwarders.split(","));
+ }
+ DnsUtilActivator.getNotificationService().
+ registerDefaultNotificationForEvent(
+ ConfigurableDnssecResolver.EVENT_TYPE,
+ NotificationAction.ACTION_POPUP_MESSAGE,
+ null, null);
+ }
+
+ /**
+ * Inspects a DNS answer message and handles validation results according to
+ * the user's preferences.
+ *
+ * @throws DnssecRuntimeException when the validation failed and the user
+ * did not choose to ignore it.
+ */
+ @Override
+ protected void validateMessage(SecureMessage msg)
+ throws DnssecRuntimeException
+ {
+ //---------------------------------------------------------------------
+ // || 1 | 2 | 3 | 4 | 5
+ //---------------------------------------------------------------------
+ // Sec. | Bog. || Ign. | Sec.Only | Sec.Or.Unsig | Warn.Bog | WarnAll
+ //---------------------------------------------------------------------
+ //a) 1 | 0 || ok | ok | ok | ok | ok
+ //b) 0 | 1 || ok | nok | nok | ask | ask
+ //c) 0 | 0 || ok | nok | ok | ok | ask
+ //---------------------------------------------------------------------
+
+ String fqdn = msg.getQuestion().getName().toString();
+ String type = Type.string(msg.getQuestion().getType());
+ String propName = createPropNameUnsigned(fqdn, type);
+ SecureResolveMode defaultAction = Enum.valueOf(SecureResolveMode.class,
+ config.getString(
+ PNAME_DNSSEC_VALIDATION_MODE,
+ SecureResolveMode.WarnIfBogus.name()
+ )
+ );
+ SecureResolveMode pinned = Enum.valueOf(SecureResolveMode.class,
+ config.getString(
+ propName,
+ defaultAction.name()
+ )
+ );
+
+ //create default entry
+ if(pinned == defaultAction)
+ config.setProperty(propName, pinned.name());
+
+ //check domain policy
+
+ //[abc]1, a[2-5]
+ if(pinned == SecureResolveMode.IgnoreDnssec || msg.isSecure())
+ return;
+
+ if(
+ //b2, c2
+ (pinned == SecureResolveMode.SecureOnly && !msg.isSecure())
+ ||
+ //b3
+ (pinned == SecureResolveMode.SecureOrUnsigned && msg.isBogus())
+ )
+ {
+ String text = getExceptionMessage(msg);
+ Date last = lastNotifications.get(text);
+ if(last == null
+ //wait at least 5 minutes before showing the same info again
+ || last.before(new Date(new Date().getTime() - 1000*60*5)))
+ {
+ DnsUtilActivator.getNotificationService().fireNotification(
+ EVENT_TYPE,
+ R.getI18NString("util.dns.INSECURE_ANSWER_TITLE"),
+ text,
+ null);
+ lastNotifications.put(text, new Date());
+ }
+ throw new DnssecRuntimeException(text);
+ }
+
+ //c3
+ if(pinned == SecureResolveMode.SecureOrUnsigned && !msg.isBogus())
+ return;
+
+ //c4
+ if(pinned == SecureResolveMode.WarnIfBogus && !msg.isBogus())
+ return;
+
+ //b4, b5, c5
+ String reason = msg.isBogus()
+ ? R.getI18NString("util.dns.DNSSEC_ADVANCED_REASON_BOGUS",
+ new String[]{fqdn, msg.getBogusReason()})
+ : R.getI18NString("util.dns.DNSSEC_ADVANCED_REASON_UNSIGNED",
+ new String[]{type, fqdn});
+ DnssecDialog dlg = new DnssecDialog(fqdn, reason);
+ dlg.setVisible(true);
+ DnssecDialogResult result = dlg.getResult();
+ switch(result)
+ {
+ case Accept:
+ break;
+ case Deny:
+ throw new DnssecRuntimeException(getExceptionMessage(msg));
+ case AlwaysAccept:
+ if(msg.isBogus())
+ config.setProperty(propName,
+ SecureResolveMode.IgnoreDnssec.name());
+ else
+ config.setProperty(propName,
+ SecureResolveMode.WarnIfBogus.name());
+ break;
+ case AlwaysDeny:
+ config.setProperty(propName, SecureResolveMode.SecureOnly);
+ throw new DnssecRuntimeException(getExceptionMessage(msg));
+ }
+ }
+
+ /**
+ * Defines the return code from the DNSSEC verification dialog.
+ */
+ private enum DnssecDialogResult
+ {
+ /** The DNS result shall be accepted. */
+ Accept,
+ /** The result shall be rejected. */
+ Deny,
+ /** The result shall be accepted permanently. */
+ AlwaysAccept,
+ /**
+ * The result shall be rejected automatically unless it is valid
+ * according to DNSSEC.
+ */
+ AlwaysDeny
+ }
+
+ /**
+ * Dialog to ask and warn the user if he wants to continue to accept an
+ * invalid dnssec result.
+ */
+ private class DnssecDialog extends SIPCommDialog implements ActionListener
+ {
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ //UI controls
+ private JPanel pnlAdvanced;
+ private JPanel pnlStandard;
+ private final String domain;
+ private final String reason;
+ private JButton cmdAck;
+ private JButton cmdShowDetails;
+
+ //state
+ private DnssecDialogResult result = DnssecDialogResult.Deny;
+
+ /**
+ * Creates a new instance of this class.
+ * @param domain The FQDN of the domain that failed.
+ * @param reason String describing why the validation failed.
+ */
+ public DnssecDialog(String domain, String reason)
+ {
+ super(false);
+ setModal(true);
+ this.domain = domain;
+ this.reason = reason;
+ initComponents();
+ }
+
+ /**
+ * Creates the UI controls
+ */
+ private void initComponents()
+ {
+ setLayout(new BorderLayout(15, 15));
+ setTitle(R.getI18NString("util.dns.INSECURE_ANSWER_TITLE"));
+
+ // warning text
+ JLabel imgWarning =
+ new JLabel(R.getImage("service.gui.icons.WARNING_ICON"));
+ imgWarning.setBorder(BorderFactory
+ .createEmptyBorder(10, 10, 10, 10));
+ add(imgWarning, BorderLayout.WEST);
+ JLabel lblWarning = new JLabel(
+ R.getI18NString("util.dns.DNSSEC_WARNING", new String[]{
+ R.getSettingsString("service.gui.APPLICATION_NAME"),
+ domain
+ })
+ );
+ add(lblWarning, BorderLayout.CENTER);
+
+ //standard panel (deny option)
+ cmdAck = new JButton(R.getI18NString("service.gui.OK"));
+ cmdAck.addActionListener(this);
+
+ cmdShowDetails = new JButton(
+ R.getI18NString("util.dns.DNSSEC_ADVANCED_OPTIONS"));
+ cmdShowDetails.addActionListener(this);
+
+ pnlStandard = new TransparentPanel(new BorderLayout());
+ pnlStandard.setBorder(BorderFactory
+ .createEmptyBorder(10, 10, 10, 10));
+ pnlStandard.add(cmdShowDetails, BorderLayout.WEST);
+ pnlStandard.add(cmdAck, BorderLayout.EAST);
+ add(pnlStandard, BorderLayout.SOUTH);
+
+ //advanced panel
+ pnlAdvanced = new TransparentPanel(new BorderLayout());
+ JPanel pnlAdvancedButtons = new TransparentPanel(
+ new FlowLayout(FlowLayout.RIGHT));
+ pnlAdvancedButtons.setBorder(BorderFactory
+ .createEmptyBorder(10, 10, 10, 10));
+ pnlAdvanced.add(pnlAdvancedButtons, BorderLayout.EAST);
+ for(DnssecDialogResult r : DnssecDialogResult.values())
+ {
+ JButton cmd = new JButton(R.getI18NString(
+ DnssecDialogResult.class.getName() + "." + r.name()));
+ cmd.setActionCommand(r.name());
+ cmd.addActionListener(this);
+ pnlAdvancedButtons.add(cmd);
+ }
+ JLabel lblReason = new JLabel(reason);
+ lblReason.setBorder(BorderFactory
+ .createEmptyBorder(10, 10, 10, 10));
+ pnlAdvanced.add(lblReason, BorderLayout.NORTH);
+ }
+
+ /**
+ * Handles the events coming from the buttons.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if(e.getSource() == cmdAck)
+ {
+ result = DnssecDialogResult.Deny;
+ dispose();
+ }
+ else if(e.getSource() == cmdShowDetails)
+ {
+ getContentPane().remove(pnlStandard);
+ add(pnlAdvanced, BorderLayout.SOUTH);
+ pack();
+ }
+ else
+ {
+ result = Enum.valueOf(DnssecDialogResult.class,
+ e.getActionCommand());
+ dispose();
+ }
+ }
+
+ /**
+ * Gets the option that user has chosen.
+ * @return the option that user has chosen.
+ */
+ public DnssecDialogResult getResult()
+ {
+ return result;
+ }
+ }
+
+ private String getExceptionMessage(SecureMessage msg)
+ {
+ return msg.getBogusReason() == null
+ ? R.getI18NString(
+ "util.dns.INSECURE_ANSWER_MESSAGE_NO_REASON",
+ new String[]{msg.getQuestion().getName().toString()}
+ )
+ : R.getI18NString(
+ "util.dns.INSECURE_ANSWER_MESSAGE_REASON",
+ new String[]{msg.getQuestion().getName().toString(),
+ //TODO parse bogus reason text and translate
+ msg.getBogusReason()}
+ );
+ }
+
+ private String createPropNameUnsigned(String fqdn, String type)
+ {
+ return PNAME_BASE_DNSSEC_PIN + "." + fqdn.replace(".", "__");
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/desktoputil/dns/DnsUtilActivator.java b/src/net/java/sip/communicator/plugin/desktoputil/dns/DnsUtilActivator.java new file mode 100644 index 0000000..9253701 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/dns/DnsUtilActivator.java @@ -0,0 +1,181 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.dns; + +import net.java.sip.communicator.service.notification.*; +import net.java.sip.communicator.service.resources.*; +import net.java.sip.communicator.util.*; + +import org.jitsi.service.configuration.*; +import org.jitsi.service.resources.*; +import org.osgi.framework.*; +import org.xbill.DNS.*; + +/** + * The DNS Util activator registers the DNSSEC resolver if enabled. + * + * @author Emil Ivov + * @author Ingo Bauersachs + */ +public class DnsUtilActivator + implements BundleActivator +{ + /** + * The name of the property that enables or disables the DNSSEC resolver + * (instead of a normal, non-validating local resolver). + */ + public static final String PNAME_DNSSEC_RESOLVER_ENABLED + = "net.java.sip.communicator.util.dns.DNSSEC_ENABLED"; + + /** + * Default value of @see PNAME_DNSSEC_RESOLVER_ENABLED. + */ + public static final boolean PDEFAULT_DNSSEC_RESOLVER_ENABLED = false; + + /** + * The name of the property that sets custom nameservers to use for all DNS + * lookups when DNSSEC is enabled. Multiple servers are separated by a comma + * (,). + */ + public static final String PNAME_DNSSEC_NAMESERVERS + = "net.java.sip.communicator.util.dns.DNSSEC_NAMESERVERS"; + + /** + * The <tt>Logger</tt> used by the <tt>UtilActivator</tt> class and its + * instances for logging output. + */ + private static final Logger logger + = Logger.getLogger(DnsUtilActivator.class); + + private static ConfigurationService configurationService; + private static NotificationService notificationService; + private static ResourceManagementService resourceService; + private static BundleContext bundleContext; + + /** + * Calls <tt>Thread.setUncaughtExceptionHandler()</tt> + * + * @param context The execution context of the bundle being started + * (unused). + * @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 + { + bundleContext = context; + + if(getConfigurationService().getBoolean( + PNAME_DNSSEC_RESOLVER_ENABLED, + PDEFAULT_DNSSEC_RESOLVER_ENABLED)) + { + getNotificationService(). + registerDefaultNotificationForEvent( + ConfigurableDnssecResolver.EVENT_TYPE, + NotificationAction.ACTION_POPUP_MESSAGE, + null, null); + } + refreshResolver(); + } + + /** + * Sets a DNSSEC resolver as default resolver on lookup when DNSSEC is + * enabled; creates a standard lookup otherwise. + */ + public static void refreshResolver() + { + if(getConfigurationService().getBoolean( + PNAME_DNSSEC_RESOLVER_ENABLED, + PDEFAULT_DNSSEC_RESOLVER_ENABLED)) + { + logger.trace("DNSSEC is enabled"); + ConfigurableDnssecResolver res = new ConfigurableDnssecResolver(); + for(int i = 1;;i++) + { + String anchor = getResources().getSettingsString( + "net.java.sip.communicator.util.dns.DS_ROOT." + i); + if(anchor == null) + break; + res.addTrustAnchor(anchor); + if(logger.isTraceEnabled()) + logger.trace("Loaded trust anchor " + anchor); + } + Lookup.setDefaultResolver(res); + } + else + { + logger.trace("DNSSEC is disabled, refresh default config"); + Lookup.refreshDefault(); + } + } + + /** + * Doesn't do anything. + * + * @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 + { + } + + /** + * Returns the <tt>ConfigurationService</tt> obtained from the bundle + * context. + * @return the <tt>ConfigurationService</tt> obtained from the bundle + * context + */ + public static ConfigurationService getConfigurationService() + { + if (configurationService == null) + { + configurationService + = ServiceUtils.getService( + bundleContext, + ConfigurationService.class); + } + return configurationService; + } + + /** + * Returns the <tt>NotificationService</tt> obtained from the bundle context. + * + * @return the <tt>NotificationService</tt> obtained from the bundle context + */ + public static NotificationService getNotificationService() + { + if (notificationService == null) + { + notificationService + = ServiceUtils.getService( + bundleContext, + NotificationService.class); + } + return notificationService; + } + + /** + * Returns the service giving access to all application resources. + * + * @return the service giving access to all application resources. + */ + public static ResourceManagementService getResources() + { + if (resourceService == null) + { + resourceService + = ResourceManagementServiceUtils.getService(bundleContext); + } + return resourceService; + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/dns/DnssecException.java b/src/net/java/sip/communicator/plugin/desktoputil/dns/DnssecException.java new file mode 100644 index 0000000..565a08a --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/dns/DnssecException.java @@ -0,0 +1,30 @@ +/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.desktoputil.dns;
+
+/**
+ * Checked DNSSEC exception for code that knows how to deal with it.
+ *
+ * @author Ingo Bauersachs
+ */
+public class DnssecException
+ extends Exception
+{
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * Creates a new instance of this class.
+ * @param e the DNSSEC runtime exception to encapsulate.
+ */
+ public DnssecException(DnssecRuntimeException e)
+ {
+ super(e);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/desktoputil/dns/DnssecRuntimeException.java b/src/net/java/sip/communicator/plugin/desktoputil/dns/DnssecRuntimeException.java new file mode 100644 index 0000000..b06ba5b --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/dns/DnssecRuntimeException.java @@ -0,0 +1,35 @@ +/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.desktoputil.dns;
+
+import java.net.*;
+
+/**
+ * Runtime exception that is thrown when a DNSSEC validation failure occurred.
+ * This is not a checked exception or a derivative of
+ * {@link UnknownHostException} so that existing code does not retry the lookup
+ * (potentially in a loop).
+ *
+ * @author Ingo Bauersachs
+ */
+public class DnssecRuntimeException
+ extends RuntimeException
+{
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * Creates a new instance of this class.
+ * @param message The reason why this exception is thrown.
+ */
+ public DnssecRuntimeException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/desktoputil/dns/ParallelResolver.java b/src/net/java/sip/communicator/plugin/desktoputil/dns/ParallelResolver.java new file mode 100644 index 0000000..105e49d --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/dns/ParallelResolver.java @@ -0,0 +1,694 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.dns; + +import java.io.*; +import java.net.*; +import java.util.*; + +import net.java.sip.communicator.plugin.desktoputil.*; +import net.java.sip.communicator.util.*; + +import org.xbill.DNS.*; + +/** + * The purpose of this class is to help avoid the significant delays that occur + * in networks where DNS servers would ignore SRV, NAPTR, and sometimes even + * A/AAAA queries (i.e. without even sending an error response). We also try to + * handle cases where DNS servers may return empty responses to some records. + * <p> + * We achieve this by entering a redundant mode whenever we detect an abnormal + * delay (longer than <tt>DNS_PATIENCE</tt>) while waiting for a DNS resonse, + * or when that response is not considered satisfying. + * <p> + * Once we enter redundant mode, we start duplicating all queries and sending + * them to both our primary and backup resolvers (in case we have any). We then + * always return the first response we get, regardless of who sent it. + * <p> + * We exit redundant mode after receiving <tt>DNS_REDEMPTION</tt> consecutive + * timely and correct responses from our primary resolver. + * + * @author Emil Ivov + */ +public class ParallelResolver implements Resolver +{ + /** + * The <tt>Logger</tt> used by the <tt>ParallelResolver</tt> + * class and its instances for logging output. + */ + private static final Logger logger = Logger + .getLogger(ParallelResolver.class.getName()); + + /** + * Indicates whether we are currently in a mode where all DNS queries are + * sent to both the primary and the backup DNS servers. + */ + private static boolean redundantMode = false; + + /** + * The default number of milliseconds it takes us to get into redundant + * mode while waiting for a DNS query response. + */ + public static final int DNS_PATIENCE = 1500; + + /** + * The name of the property that allows us to override the default + * <tt>DNS_PATIENCE</tt> value. + */ + public static final String PNAME_DNS_PATIENCE + = "net.java.sip.communicator.util.dns.DNS_PATIENCE"; + + /** + * The currently configured number of milliseconds that we need to wait + * before entering redundant mode. + */ + private static long currentDnsPatience = DNS_PATIENCE; + + /** + * The default number of times that the primary DNS would have to provide a + * faster response than the backup resolver before we consider it safe + * enough to exit redundant mode. + */ + public static final int DNS_REDEMPTION = 3; + + /** + * The name of the property that allows us to override the default + * <tt>DNS_REDEMPTION</tt> value. + */ + public static final String PNAME_DNS_REDEMPTION + = "net.java.sip.communicator.util.dns.DNS_REDEMPTION"; + + /** + * The currently configured number of times that the primary DNS would have + * to provide a faster response than the backup resolver before we consider + * it safe enough to exit redundant mode. + */ + public static int currentDnsRedemption = DNS_REDEMPTION; + + /** + * The number of fast responses that we need to get from the primary + * resolver before we exit redundant mode. <tt>0</tt> indicates that we are + * no longer in redundant mode + */ + private static int redemptionStatus = 0; + + /** + * A lock that we use while determining whether we've completed redemption + * and can exit redundant mode. + */ + private final static Object redemptionLock = new Object(); + + /** + * The default resolver that we use if everything works properly. + */ + private static Resolver defaultResolver; + + static + { + try + { + defaultResolver = new ExtendedResolver(); + } + catch (UnknownHostException e) + { + //should never happen + throw new RuntimeException("Failed to initialize resolver"); + } + + initProperties(); + } + + /** + * Default resolver property initialisation + */ + private static void initProperties() + { + try + { + currentDnsPatience = DesktopUtilActivator.getConfigurationService() + .getLong(PNAME_DNS_PATIENCE, DNS_PATIENCE); + currentDnsRedemption + = DesktopUtilActivator.getConfigurationService() + .getInt(PNAME_DNS_REDEMPTION, DNS_REDEMPTION); + } + catch(Throwable t) + { + //we don't want messed up properties to screw up DNS resolution + //so we just log. + logger.info("Failed to initialize DNS resolver properties", t); + } + + } + + /** + * Replaces the default resolver used by this class. Mostly meant for + * debugging. + * + * @param resolver the resolver we'd like to use by default from now on. + */ + public static void setDefaultResolver(Resolver resolver) + { + defaultResolver = resolver; + } + + /** + * Returns the default resolver used by this class. Mostly meant for + * debugging. + * + * @return the resolver this class consults first. + */ + public static Resolver getDefaultResolver() + { + return defaultResolver; + } + + /** + * An extended resolver that would be encapsulating all backup resolvers. + */ + private final ExtendedResolver backupResolver; + + /** + * Creates a <tt>ParallelResolver</tt> that would use the specified array + * of <tt>backupServers</tt> if the default DNS doesn't seem to be doing + * that well. + * + * @param backupServers the list of backup DNS servers that we should use + * if, and only if, the default servers don't seem to work that well. + */ + public ParallelResolver(InetSocketAddress[] backupServers) + { + try + { + backupResolver = new ExtendedResolver(new SimpleResolver[]{}); + for(InetSocketAddress backupServer : backupServers ) + { + SimpleResolver sr = new SimpleResolver(); + sr.setAddress(backupServer); + + backupResolver.addResolver(sr); + } + } + catch (UnknownHostException e) + { + //this shouldn't be thrown since we don't do any DNS querying + //in here. this is why we take an InetSocketAddress as a param. + throw new IllegalStateException("The impossible just happened: " + +"we could not initialize our backup DNS resolver"); + } + } + + /** + * Sends a message and waits for a response. + * + * @param query The query to send. + * @return The response + * + * @throws IOException An error occurred while sending or receiving. + */ + public Message send(Message query) + throws IOException + { + + ParallelResolution resolution = new ParallelResolution(query); + + resolution.sendFirstQuery(); + + //make a copy of the redundant mode variable in case we are currently + //completed a redemption that started earlier. + boolean redundantModeCopy; + + synchronized(redemptionLock) + { + redundantModeCopy = redundantMode; + } + + //if we are not in redundant mode we should wait a bit and see how this + //goes. if we get a reply we could return bravely. + if(!redundantModeCopy) + { + if(resolution.waitForResponse(currentDnsPatience)) + { + //we are done. + return resolution.returnResponseOrThrowUp(); + } + else + { + synchronized(redemptionLock) + { + redundantMode = true; + redemptionStatus = currentDnsRedemption; + logger.info("Primary DNS seems laggy as we got no " + +"response for " + currentDnsPatience + "ms. " + + "Enabling redundant mode."); + } + } + } + + //we are definitely in redundant mode now + resolution.sendBackupQueries(); + + resolution.waitForResponse(0); + + //check if it is time to end redundant mode. + synchronized(redemptionLock) + { + if(!resolution.primaryResolverRespondedFirst) + { + //primary DNS is still feeling shaky. we reinit redemption + //status in case we were about to cut the server some slack + redemptionStatus = currentDnsRedemption; + } + else + { + //primary server replied first. we let him redeem some dignity + redemptionStatus --; + + //yup, it's now time to end DNS redundant mode; + if(redemptionStatus <= 0) + { + redundantMode = false; + logger.info("Primary DNS seems back in biz. " + + "Disabling redundant mode."); + } + } + } + + return resolution.returnResponseOrThrowUp(); + } + + /** + * Supposed to asynchronously send messages but not currently implemented. + * + * @param query The query to send + * @param listener The object containing the callbacks. + * @return An identifier, which is also a parameter in the callback + */ + public Object sendAsync(final Message query, final ResolverListener listener) + { + return null; + } + + /** + * Sets the port to communicate on with the default servers. + * + * @param port The port to send messages to + */ + public void setPort(int port) + { + defaultResolver.setPort(port); + } + + /** + * Sets whether TCP connections will be sent by default with the default + * resolver. Backup servers would always be contacted the same way. + * + * @param flag Indicates whether TCP connections are made + */ + public void setTCP(boolean flag) + { + defaultResolver.setTCP(flag); + } + + /** + * Sets whether truncated responses will be ignored. If not, a truncated + * response over UDP will cause a retransmission over TCP. Backup servers + * would always be contacted the same way. + * + * @param flag Indicates whether truncated responses should be ignored. + */ + public void setIgnoreTruncation(boolean flag) + { + defaultResolver.setIgnoreTruncation(flag); + } + + /** + * Sets the EDNS version used on outgoing messages. + * + * @param level The EDNS level to use. 0 indicates EDNS0 and -1 indicates no + * EDNS. + * @throws IllegalArgumentException An invalid level was indicated. + */ + public void setEDNS(int level) + { + defaultResolver.setEDNS(level); + } + + /** + * Sets the EDNS information on outgoing messages. + * + * @param level The EDNS level to use. 0 indicates EDNS0 and -1 indicates no + * EDNS. + * @param payloadSize The maximum DNS packet size that this host is capable + * of receiving over UDP. If 0 is specified, the default (1280) is used. + * @param flags EDNS extended flags to be set in the OPT record. + * @param options EDNS options to be set in the OPT record, specified as a + * List of OPTRecord.Option elements. + * + * @throws IllegalArgumentException An invalid field was specified. + * @see OPTRecord + */ + @SuppressWarnings("rawtypes") // that's the way it is in dnsjava + public void setEDNS(int level, int payloadSize, int flags, List options) + { + defaultResolver.setEDNS(level, payloadSize, flags, options); + } + + /** + * Specifies the TSIG key that messages will be signed with + * @param key The key + */ + public void setTSIGKey(TSIG key) + { + defaultResolver.setTSIGKey(key); + } + + /** + * Sets the amount of time to wait for a response before giving up. + * + * @param secs The number of seconds to wait. + * @param msecs The number of milliseconds to wait. + */ + public void setTimeout(int secs, int msecs) + { + defaultResolver.setTimeout(secs, msecs); + } + + /** + * Sets the amount of time to wait for a response before giving up. + * + * @param secs The number of seconds to wait. + */ + public void setTimeout(int secs) + { + defaultResolver.setTimeout(secs); + } + + /** + * Resets resolver configuration and populate our default resolver + * with the newly configured servers. + */ + public void reset() + { + ExtendedResolver resolver = (ExtendedResolver)defaultResolver; + + // remove old ones + for(Resolver r : resolver.getResolvers()) + { + resolver.deleteResolver(r); + } + + // populate with new servers after refreshing configuration + try + { + String [] servers = ResolverConfig.getCurrentConfig().servers(); + if (servers != null) + { + for (int i = 0; i < servers.length; i++) + { + Resolver r = new SimpleResolver(servers[i]); + //r.setTimeout(quantum); + resolver.addResolver(r); + } + } + else + resolver.addResolver(new SimpleResolver()); + } + catch (UnknownHostException e) + { + //should never happen + throw new RuntimeException("Failed to initialize resolver"); + } + } + + /** + * Determines if <tt>response</tt> can be considered a satisfactory DNS + * response and returns accordingly. + * <p> + * We consider non-satisfactory responses that may indicate that the local + * DNS does not work properly and that we may hence need to fall back to + * the backup resolver. + * <p> + * Basically the goal here is to be able to go into redundant mode when we + * come across DNS servers that send empty responses to SRV and NAPTR + * requests. + * + * @param response the dnsjava {@link Message} that we'd like to inspect. + * + * @return <tt>true</tt> if <tt>response</tt> appears as a satisfactory + * response and <tt>false</tt> otherwise. + */ + private boolean isResponseSatisfactory(Message response) + { + if ( response == null ) + return false; + + Record[] answerRR = response.getSectionArray(Section.ANSWER); + Record[] authorityRR = response.getSectionArray(Section.AUTHORITY); + Record[] additionalRR = response.getSectionArray(Section.ADDITIONAL); + + if ( (answerRR != null && answerRR.length > 0) + || (authorityRR != null && authorityRR.length > 0) + || (additionalRR != null && additionalRR.length > 0)) + { + return true; + } + + //we didn't find any responses and unless the answer is NXDOMAIN then + //we may want to check with the backup resolver for a second opinion + if(response.getRcode() == Rcode.NXDOMAIN) + return true; + + //if we received NODATA (same as NOERROR and no response records) for + // an AAAA or a NAPTR query then it makes sense since many existing + //domains come without those two. + if( response.getRcode() == Rcode.NOERROR + && (response.getQuestion().getType() == Type.AAAA + || response.getQuestion().getType() == Type.NAPTR)) + { + return true; + } + + //nope .. this doesn't make sense ... + return false; + } + + /** + * The class that listens for responses to any of the queries we send to + * our default and backup servers and returns as soon as we get one or until + * our default resolver fails. + */ + private class ParallelResolution extends Thread + { + /** + * The query that we have sent to the default and backup DNS servers. + */ + private final Message query; + + /** + * The field where we would store the first incoming response to our + * query. + */ + public Message response; + + /** + * The field where we would store the first error we receive from a DNS + * or a backup resolver. + */ + private Throwable exception; + + /** + * Indicates whether we are still waiting for an answer from someone + */ + private boolean done = false; + + /** + * Indicates that a response was received from the primary resolver. + */ + private boolean primaryResolverRespondedFirst = true; + + /** + * Creates a {@link ParallelResolution} for the specified <tt>query</tt> + * + * @param query the DNS query that we'd like to send to our primary + * and backup resolvers. + */ + public ParallelResolution(final Message query) + { + super("ParallelResolutionThread"); + this.query = query; + } + + /** + * Starts this collector which would cause it to send its query to the + * default resolver. + */ + public void sendFirstQuery() + { + start(); + } + + /** + * Sends this collector's query to the default resolver. + */ + public void run() + { + Message localResponse = null; + try + { + localResponse = defaultResolver.send(query); + } + catch (Throwable exc) + { + logger.info("Exception occurred during parallel DNS resolving" + + exc, exc); + this.exception = exc; + } + synchronized(this) + { + //if the backup resolvers had already replied we ignore the + //reply of the primary one whatever it was. + if(done) + return; + + //if there was a response we're only done if it is satisfactory + if( localResponse != null + && isResponseSatisfactory(localResponse)) + { + response = localResponse; + done = true; + } + notify(); + } + } + + /** + * Asynchronously sends this collector's query to all backup resolvers. + */ + public void sendBackupQueries() + { + logger.info("Send DNS queries to backup resolvers"); + + //yes. a second thread in the thread ... it's ugly but it works + //and i do want to keep code simple to read ... this whole parallel + //resolving is complicated enough as it is. + new Thread(){ + public void run() + { + synchronized(ParallelResolution.this) + { + if (done) + return; + Message localResponse = null; + try + { + localResponse = backupResolver.send(query); + } + catch (Throwable exc) + { + logger.info("Exception occurred during backup " + +"DNS resolving" + exc); + + //keep this so that we can rethrow it + exception = exc; + } + //if the default resolver has already replied we + //ignore the reply of the backup ones. + if(done) + return; + + //contrary to responses from the primary resolver, + //in this case we don't care whether the response is + //satisfying: if it isn't, there's nothing we can do + response = localResponse; + done = true; + + ParallelResolution.this.notify(); + } + } + }.start(); + } + + /** + * Waits for a response or an error to occur during <tt>waitFor</tt> + * milliseconds.If neither happens, we return false. + * + * @param waitFor the number of milliseconds to wait for a response or + * an error or <tt>0</tt> if we'd like to wait until either of these + * happen. + * + * @return <tt>true</tt> if we returned because we received a response + * from a resolver or errors from everywhere, and <tt>false</tt> that + * didn't happen. + */ + public boolean waitForResponse(long waitFor) + { + synchronized(this) + { + if(done) + return done; + try + { + wait(waitFor); + } + catch (InterruptedException e) + { + //we don't care + } + + return done; + } + } + + /** + * Waits for resolution to complete (if necessary) and then either + * returns the response we received or throws whatever exception we + * saw. + * + * @return the response {@link Message} we received from the DNS. + * + * @throws IOException if this resolution ended badly because of a + * network IO error + * @throws RuntimeException if something unexpected happened + * during resolution. + * @throws IllegalArgumentException if something unexpected happened + * during resolution or if there was no response. + */ + public Message returnResponseOrThrowUp() + throws IOException, RuntimeException, IllegalArgumentException + { + if(!done) + waitForResponse(0); + + if(response != null) + { + return response; + } + else if (exception instanceof IOException) + { + logger.warn("IO exception while using DNS resolver", exception); + throw (IOException) exception; + } + else if (exception instanceof RuntimeException) + { + logger.warn("RunTimeException while using DNS resolver", + exception); + throw (RuntimeException) exception; + } + else if (exception instanceof Error) + { + logger.warn("Error while using DNS resolver", exception); + throw (Error) exception; + } + else + { + logger.warn("Received a bad response from primary DNS resolver", + exception); + throw new IllegalStateException("ExtendedResolver failure"); + } + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/dns/SecureMessage.java b/src/net/java/sip/communicator/plugin/desktoputil/dns/SecureMessage.java new file mode 100644 index 0000000..28f8fcb --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/dns/SecureMessage.java @@ -0,0 +1,93 @@ +/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.desktoputil.dns;
+
+import java.io.*;
+
+import org.xbill.DNS.*;
+
+/**
+ * DNS Message that adds DNSSEC validation information.
+ *
+ * @author Ingo Bauersachs
+ */
+public class SecureMessage
+ extends Message
+{
+ private boolean secure;
+ private boolean bogus;
+ private String bogusReason;
+
+ /**
+ * Creates a new instance of this class based on data received from an
+ * Unbound resolve.
+ *
+ * @param msg The answer of the Unbound resolver.
+ * @throws IOException
+ */
+ public SecureMessage(UnboundResult msg) throws IOException
+ {
+ super(msg.answerPacket);
+ secure = msg.secure;
+ bogus = msg.bogus;
+ bogusReason = msg.whyBogus;
+ }
+
+ /**
+ * Indicates whether the answer is secure.
+ * @return True, if the result is validated securely.
+ */
+ public boolean isSecure()
+ {
+ return secure;
+ }
+
+ /**
+ * Indicates if there was a validation failure.
+ *
+ * @return If the result was not secure (secure == false), and this result
+ * is due to a security failure, bogus is true.
+ */
+ public boolean isBogus()
+ {
+ return bogus;
+ }
+
+ /**
+ * If the result is bogus this contains a string that describes the failure.
+ *
+ * @return string that describes the failure.
+ */
+ public String getBogusReason()
+ {
+ return bogusReason;
+ }
+
+ /**
+ * Converts the Message to a String. The fields secure, bogus and whyBogus
+ * are append as a comment.
+ */
+ @Override
+ public String toString()
+ {
+ StringBuilder s = new StringBuilder(super.toString());
+ s.append('\n');
+ s.append(";; Secure: ");
+ s.append(secure);
+ s.append('\n');
+ s.append(";; Bogus: ");
+ s.append(bogus);
+ s.append('\n');
+ if(bogus)
+ {
+ s.append(";; Reason: ");
+ s.append(bogusReason);
+ s.append('\n');
+ }
+ return s.toString();
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/desktoputil/dns/SecureResolveMode.java b/src/net/java/sip/communicator/plugin/desktoputil/dns/SecureResolveMode.java new file mode 100644 index 0000000..77c21d9 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/dns/SecureResolveMode.java @@ -0,0 +1,43 @@ +/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.desktoputil.dns;
+
+/**
+ * Defines how DNSSEC validation errors should be handled.
+ *
+ * @author Ingo Bauersachs
+ */
+public enum SecureResolveMode
+{
+ /**
+ * Any DNSSEC data is completely ignored.
+ */
+ IgnoreDnssec,
+
+ /**
+ * The result of a query is only returned if it validated successfully.
+ */
+ SecureOnly,
+
+ /**
+ * The result of a query is returned if it validated successfully or when
+ * the zone is unsigned.
+ */
+ SecureOrUnsigned,
+
+ /**
+ * If the result of a query is bogus (manipulated, incorrect), the user is
+ * to be asked how to proceed.
+ */
+ WarnIfBogus,
+
+ /**
+ * If the result of a query is bogus (manipulated, incorrect) or if the zone
+ * is unsigned, the user is to be asked how to proceed.
+ */
+ WarnIfBogusOrUnsigned
+}
diff --git a/src/net/java/sip/communicator/plugin/desktoputil/dns/UnboundApi.java b/src/net/java/sip/communicator/plugin/desktoputil/dns/UnboundApi.java new file mode 100644 index 0000000..bd53010 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/dns/UnboundApi.java @@ -0,0 +1,228 @@ +/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.desktoputil.dns;
+
+/**
+ * Wrapper for the JUnbound JNI wrapper.
+ * <p>
+ * The JavaDoc of these methods is directly copied from libunbound, licensed as
+ * follows:
+ * <p>
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Ingo Bauersachs
+ */
+public class UnboundApi
+{
+ private static boolean isAvailable;
+ private static final Object syncRoot = new Object();
+
+ static
+ {
+ tryLoadUnbound();
+ }
+
+ /**
+ * Attempts to load the Unbound native library. When successful,
+ * {@link #isAvailable()} returns true.
+ */
+ public static void tryLoadUnbound()
+ {
+ synchronized(syncRoot)
+ {
+ try
+ {
+ System.loadLibrary("junbound");
+ isAvailable = true;
+ }
+ catch(UnsatisfiedLinkError e)
+ {
+ isAvailable = false;
+ }
+ }
+ }
+
+ /**
+ * Indicates whether the Unbound library is loaded.
+ * @return True when the JNI wrapper could be loaded, false otherwise.
+ */
+ public static boolean isAvailable()
+ {
+ return isAvailable;
+ }
+
+ /**
+ * Set debug verbosity for the context. Output is directed to stderr. Higher
+ * debug level gives more output.
+ *
+ * @param context context.
+ * @param level The debug level.
+ */
+ public static native void setDebugLevel(long context, int level);
+
+ /**
+ * Create a resolving and validation context.
+ * @return a new context. default initialization. returns NULL on error.
+ */
+ public static native long createContext();
+
+ /**
+ * Destroy a validation context and free all its resources. Outstanding
+ * async queries are killed and callbacks are not called for them.
+ *
+ * @param context context to delete
+ */
+ public static native void deleteContext(long context);
+
+ /**
+ * Set machine to forward DNS queries to, the caching resolver to use.
+ * <p>
+ * IP4 or IP6 address. Forwards all DNS requests to that machine, which is
+ * expected to run a recursive resolver. If the proxy is not DNSSEC-capable,
+ * validation may fail. Can be called several times, in that case the
+ * addresses are used as backup servers.
+ *
+ * @param context context. At this time it is only possible to set
+ * configuration before the first resolve is done.
+ * @param server address, IP4 or IP6 in string format. If the server is
+ * NULL, forwarding is disabled.
+ */
+ public static native void setForwarder(long context, String server);
+
+ /**
+ * Add a trust anchor to the given context.
+ * <p>
+ * The trust anchor is a string, on one line, that holds a valid DNSKEY or
+ * DS RR.
+ *
+ * @param context context. At this time it is only possible to add trusted
+ * keys before the first resolve is done.
+ * @param anchor string, with zone-format RR on one line. [domainname] [TTL
+ * optional] [type] [class optional] [rdata contents]
+ */
+ public static native void addTrustAnchor(long context, String anchor);
+
+ /**
+ * Perform resolution and validation of the target name.
+ *
+ * @param context context. The context is finalized, and can no longer
+ * accept config changes.
+ * @param name domain name in text format (a zero terminated text string).
+ * @param rrtype type of RR in host order, 1 is A (address).
+ * @param rrclass class of RR in host order, 1 is IN (for internet).
+ * @return the result data is returned in a newly allocated result
+ * structure. May be NULL on return, return value is set to an error
+ * in that case (out of memory).
+ * @throws UnboundException when an error occurred.
+ */
+ public static native UnboundResult resolve(long context, String name,
+ int rrtype, int rrclass) throws UnboundException;
+
+ /**
+ * Perform resolution and validation of the target name.
+ * <p>
+ * Asynchronous, after a while, the callback will be called with your data
+ * and the result.
+ *
+ * @param context context. If no thread or process has been created yet to
+ * perform the work in the background, it is created now. The
+ * context is finalized, and can no longer accept config changes.
+ * @param name domain name in text format (a string).
+ * @param rrtype type of RR in host order, 1 is A.
+ * @param rrclass class of RR in host order, 1 is IN (for internet).
+ * @param data this data is your own data (you can pass null), and is passed
+ * on to the callback function.
+ * @param cb this is called on completion of the resolution.
+ * @return an identifier number is returned for the query as it is in
+ * progress. It can be used to cancel the query.
+ * @throws UnboundException when an error occurred.
+ */
+ public static native int resolveAsync(long context, String name,
+ int rrtype, int rrclass, Object data, UnboundCallback cb)
+ throws UnboundException;
+
+ /**
+ * Cancel an async query in progress. Its callback will not be called.
+ *
+ * @param context context.
+ * @param asyncId which query to cancel.
+ * @throws UnboundException This routine can error if the async_id passed
+ * does not exist or has already been delivered. If another
+ * thread is processing results at the same time, the result may
+ * be delivered at the same time and the cancel fails with an
+ * error. Also the cancel can fail due to a system error, no
+ * memory or socket failures.
+ */
+ public static native void cancelAsync(long context, int asyncId)
+ throws UnboundException;
+
+ /**
+ * Convert error value to a human readable string.
+ *
+ * @param code error code from one of the Unbound functions.
+ * @return text string of the error code.
+ */
+ public static native String errorCodeToString(int code);
+
+ /**
+ * Wait for a context to finish with results. Call this routine to continue
+ * processing results from the validating resolver. After the wait, there
+ * are no more outstanding asynchronous queries.
+ *
+ * @param context context.
+ * @throws UnboundException when an error occurred.
+ */
+ public static native void processAsync(long context)
+ throws UnboundException;
+
+ /**
+ * Interface for the async resolve callback.
+ */
+ public interface UnboundCallback
+ {
+ /**
+ * Called on completion of the async resolution.
+ *
+ * @param data the same object as passed to
+ * {@link UnboundApi#resolveAsync(long, String, int, int,
+ * Object, UnboundCallback)}
+ * @param err 0 when a result has been found, an Unbound error code
+ * otherwise
+ * @param result a newly allocated result structure. The result may be
+ * null, in that case err is set.
+ */
+ public void UnboundResolveCallback(Object data, int err,
+ UnboundResult result);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/desktoputil/dns/UnboundException.java b/src/net/java/sip/communicator/plugin/desktoputil/dns/UnboundException.java new file mode 100644 index 0000000..0367e2f --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/dns/UnboundException.java @@ -0,0 +1,31 @@ +/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.desktoputil.dns;
+
+/**
+ * Exception that is being thrown when native Unbound code resulted in an error.
+ *
+ * @author Ingo Bauersachs
+ */
+public class UnboundException
+ extends Exception
+{
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * Creates a new instance of this class.
+ *
+ * @param message the detail message.
+ */
+ public UnboundException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/desktoputil/dns/UnboundResolver.java b/src/net/java/sip/communicator/plugin/desktoputil/dns/UnboundResolver.java new file mode 100644 index 0000000..abeb6ba --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/dns/UnboundResolver.java @@ -0,0 +1,384 @@ +/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.desktoputil.dns;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+import net.java.sip.communicator.plugin.desktoputil.*;
+import net.java.sip.communicator.util.*;
+
+import org.xbill.DNS.*;
+
+/**
+ * Implementation of the {@link Resolver} interface, wrapping the native NLnet
+ * Labs Unbound resolver. Only the basic methods for queries are supported.
+ *
+ * @author Ingo Bauersachs
+ */
+public class UnboundResolver
+ implements Resolver
+{
+ private final static Logger logger =
+ Logger.getLogger(UnboundResolver.class);
+
+ /**
+ * Helper class to synchronize on asynchronous queries.
+ */
+ private static class CallbackData
+ {
+ /**
+ * The resolver consumer that wishes to be informed when the request
+ * completed.
+ */
+ ResolverListener listener;
+
+ /**
+ * The unbound session context.
+ */
+ long context;
+
+ /**
+ * The ID of the unbound async query.
+ */
+ int asyncId;
+
+ /**
+ * Java synchronization on top of unbound.
+ */
+ CountDownLatch sync = new CountDownLatch(1);
+ }
+
+ /**
+ * Timeout for DNS queries.
+ */
+ private int timeout = 10000;
+
+ /**
+ * The recursive DNS servers answering our queries.
+ */
+ private String[] forwarders;
+
+ /**
+ * DNSSEC trust anchors for signed zones (usually for the root zone).
+ */
+ private List<String> trustAnchors = new LinkedList<String>();
+
+ /**
+ * Pool that executes our queries.
+ */
+ private ExecutorService threadPool;
+
+ /**
+ * Creates a new instance of this class.
+ */
+ public UnboundResolver()
+ {
+ threadPool = Executors.newCachedThreadPool();
+ }
+
+ /**
+ * Sets a list of forwarders to use instead of the system default.
+ *
+ * @param forwarders list of servers to use for our queries.
+ */
+ public void setForwarders(String[] forwarders)
+ {
+ this.forwarders = forwarders;
+ }
+
+ /**
+ * Adds a DNSSEC trust anchor validation of the DNSKEYs.
+ *
+ * @param anchor trust anchor in the form of
+ * "'zone' IN DS 'key tag' 'algorithm' 'digest type' 'digest'"
+ */
+ public void addTrustAnchor(String anchor)
+ {
+ trustAnchors.add(anchor);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public SecureMessage send(final Message query) throws IOException
+ {
+ Future<SecureMessage> future = threadPool.submit(
+ new Callable<SecureMessage>()
+ {
+ public SecureMessage call() throws Exception
+ {
+ if(logger.isDebugEnabled())
+ logger.debug(query);
+
+ SecureMessage secureMessage = null;
+ final long context = prepareContext();
+ try
+ {
+ UnboundResult result = UnboundApi.resolve(
+ context,
+ query.getQuestion().getName().toString(),
+ query.getQuestion().getType(),
+ query.getQuestion().getDClass()
+ );
+ secureMessage = new SecureMessage(result);
+ validateMessage(secureMessage);
+ }
+ finally
+ {
+ UnboundApi.deleteContext(context);
+ if(logger.isDebugEnabled() && secureMessage != null)
+ logger.debug(secureMessage);
+ }
+
+ return secureMessage;
+ }
+ });
+ try
+ {
+ return future.get(timeout, TimeUnit.SECONDS);
+ }
+ catch (InterruptedException e)
+ {
+ logger.error(e);
+ throw new IOException(e.getMessage());
+ }
+ catch (ExecutionException e)
+ {
+ if(e.getCause() instanceof DnssecRuntimeException)
+ throw new DnssecRuntimeException(e.getCause().getMessage());
+ logger.error(e);
+ throw new IOException(e.getMessage());
+ }
+ catch (TimeoutException e)
+ {
+ throw new SocketTimeoutException(e.getMessage());
+ }
+ }
+
+ /**
+ * Method to allow overriders to inspect the message. This class'
+ * implementation does nothing.
+ *
+ * @param msg The message to inspect.
+ * @throws DnssecRuntimeException if the inspector does not want the code to
+ * continue normal processing of the answer.
+ */
+ protected void validateMessage(SecureMessage msg)
+ throws DnssecRuntimeException
+ {
+ }
+
+ /**
+ * Prepares a unbound session context initialized with forwarders and trust
+ * anchors.
+ *
+ * @return The context id
+ */
+ private long prepareContext()
+ {
+ final long context = UnboundApi.createContext();
+ if(logger.isTraceEnabled())
+ UnboundApi.setDebugLevel(context, 100);
+ for(String fwd : forwarders == null
+ ? ResolverConfig.getCurrentConfig().servers()
+ : forwarders)
+ {
+ fwd = fwd.trim();
+ if(NetworkUtils.isValidIPAddress(fwd))
+ {
+ if(fwd.startsWith("["))
+ fwd = fwd.substring(1, fwd.length() - 1);
+ UnboundApi.setForwarder(context, fwd);
+ }
+ }
+ for(String anchor : trustAnchors)
+ {
+ UnboundApi.addTrustAnchor(context, anchor);
+ }
+ return context;
+ }
+
+ /**
+ * Cleans up an Unbound session context.
+ *
+ * @param cbData The helper object of the asynchronous call.
+ * @param cancelAsync Whether an outstanding asynchronous unbound query
+ * should be canceled.
+ */
+ private static synchronized void deleteContext(CallbackData cbData,
+ boolean cancelAsync)
+ {
+ if(cbData.context == 0)
+ return;
+
+ if(cancelAsync)
+ {
+ try
+ {
+ UnboundApi.cancelAsync(cbData.context, cbData.asyncId);
+ }
+ catch (UnboundException ignore)
+ {}
+ }
+ UnboundApi.deleteContext(cbData.context);
+ cbData.context = 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.xbill.DNS.Resolver#sendAsync(org.xbill.DNS.Message,
+ * org.xbill.DNS.ResolverListener)
+ */
+ public CallbackData sendAsync(Message query, ResolverListener listener)
+ {
+ if(listener == null)
+ throw new IllegalArgumentException("listener cannot be null");
+
+ final long context = prepareContext();
+ final CallbackData cbData = new CallbackData();
+ cbData.listener = listener;
+ cbData.context = context;
+ int asyncId;
+ try
+ {
+ asyncId = UnboundApi.resolveAsync(
+ context,
+ query.getQuestion().getName().toString(),
+ query.getQuestion().getType(),
+ query.getQuestion().getDClass(),
+ cbData,
+ new UnboundApi.UnboundCallback()
+ {
+ public void UnboundResolveCallback(Object data, int err,
+ UnboundResult result)
+ {
+ CallbackData cbData = (CallbackData)data;
+ deleteContext(cbData, false);
+
+ ResolverListener l = cbData.listener;
+ if(err == 0)
+ {
+ try
+ {
+ l.receiveMessage(data,
+ new SecureMessage(result));
+ }
+ catch (IOException e)
+ {
+ l.handleException(data, e);
+ }
+ }
+ else
+ l.handleException(data,
+ new Exception(
+ UnboundApi.errorCodeToString(err)));
+
+ cbData.sync.countDown();
+ }
+ }
+ );
+ }
+ catch (UnboundException e)
+ {
+ listener.handleException(null, e);
+ return null;
+ }
+ cbData.asyncId = asyncId;
+ threadPool.execute(new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ UnboundApi.processAsync(context);
+ }
+ catch(UnboundException ex)
+ {
+ cbData.listener.handleException(this, ex);
+ deleteContext(cbData, false);
+ cbData.sync.countDown();
+ }
+ }
+ });
+ return cbData;
+ }
+
+ /**
+ * Not supported.
+ * @throws UnsupportedOperationException
+ */
+ public void setEDNS(int level)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Not supported.
+ * @throws UnsupportedOperationException
+ */
+ @SuppressWarnings("rawtypes")
+ public void setEDNS(int level, int payloadSize, int flags, List options)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Not supported.
+ * @throws UnsupportedOperationException
+ */
+ public void setIgnoreTruncation(boolean flag)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Not supported.
+ * @throws UnsupportedOperationException
+ */
+ public void setPort(int port)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Not supported.
+ * @throws UnsupportedOperationException
+ */
+ public void setTCP(boolean flag)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Not supported.
+ * @throws UnsupportedOperationException
+ */
+ public void setTSIGKey(TSIG key)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /* (non-Javadoc)
+ * @see org.xbill.DNS.Resolver#setTimeout(int)
+ */
+ public void setTimeout(int secs)
+ {
+ timeout = secs * 1000;
+ }
+
+ /* (non-Javadoc)
+ * @see org.xbill.DNS.Resolver#setTimeout(int, int)
+ */
+ public void setTimeout(int secs, int msecs)
+ {
+ timeout = secs * 1000 + msecs;
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/desktoputil/dns/UnboundResult.java b/src/net/java/sip/communicator/plugin/desktoputil/dns/UnboundResult.java new file mode 100644 index 0000000..0be905d --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/dns/UnboundResult.java @@ -0,0 +1,117 @@ +/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.desktoputil.dns;
+
+/**
+ * Class that contains the answer to query processed by the native Unbound
+ * resolver. Corresponds to the <a
+ * href="http://unbound.net/documentation/doxygen/structub__result.html"
+ * >ub_result</a> data structure.
+ *
+ * The fields {@link #data} and {@link #canonname} are not filled.
+ * <p>
+ * The JavaDoc of these fields is directly copied from libunbound, licensed as
+ * follows:
+ * <p>
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * @author Ingo Bauersachs
+ */
+public class UnboundResult
+{
+ /**
+ * The original question, name text string.
+ */
+ String qname;
+
+ /**
+ * the type asked for
+ */
+ int qtype;
+
+ /**
+ * the type asked for
+ */
+ int qclass;
+
+
+ /**
+ * a list of network order DNS rdata items, terminated with a NULL pointer,
+ * so that data[0] is the first result entry, data[1] the second, and the
+ * last entry is NULL.
+ */
+ byte[][] data;
+
+ /**
+ * canonical name for the result (the final cname).
+ */
+ String canonname;
+
+ /**
+ * DNS RCODE for the result.
+ */
+ int rcode;
+
+ /**
+ * The DNS answer packet.
+ */
+ byte[] answerPacket;
+
+
+ /**
+ * If there is any data, this is true.
+ */
+ boolean haveData;
+
+ /**
+ * If there was no data, and the domain did not exist, this is true.
+ */
+ boolean nxDomain;
+
+ /**
+ * True, if the result is validated securely.
+ */
+ boolean secure;
+
+ /**
+ * If the result was not secure ({@link #secure} == false), and this result
+ * is due to a security failure, bogus is true.
+ */
+ boolean bogus;
+
+ /**
+ * If the result is bogus this contains a string (zero terminated) that
+ * describes the failure.
+ */
+ String whyBogus;
+}
diff --git a/src/net/java/sip/communicator/plugin/desktoputil/dns/desktoputil.dns.manifest.mf b/src/net/java/sip/communicator/plugin/desktoputil/dns/desktoputil.dns.manifest.mf new file mode 100644 index 0000000..e289126 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/dns/desktoputil.dns.manifest.mf @@ -0,0 +1,19 @@ +Bundle-Activator: net.java.sip.communicator.plugin.desktoputil.dns.DnsUtilActivator
+Bundle-Name: SIP Communicator DNS Utility Packages
+Bundle-SymbolicName: net.java.sip.communicator.plugin.desktoputil.dns +Bundle-Description: A bundle that export packages with DNS utility classes.
+Bundle-Vendor: jitsi.org
+Bundle-Version: 0.0.1
+System-Bundle: yes
+Import-Package: org.jitsi.util,
+ org.osgi.framework,
+ net.java.sip.communicator.util,
+ net.java.sip.communicator.plugin.desktoputil,
+ org.jitsi.service.resources,
+ net.java.sip.communicator.service.resources,
+ org.jitsi.service.configuration,
+ net.java.sip.communicator.service.notification,
+ sun.net.dns,
+ org.xbill.DNS,
+ javax.swing
+Export-Package: net.java.sip.communicator.plugin.desktoputil.dns
diff --git a/src/net/java/sip/communicator/plugin/desktoputil/event/CloseListener.java b/src/net/java/sip/communicator/plugin/desktoputil/event/CloseListener.java new file mode 100644 index 0000000..a79b99e --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/event/CloseListener.java @@ -0,0 +1,22 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.event; + +/* + * The content of this file was based on code borrowed from David Bismut, + * davidou@mageos.com Intern, SETLabs, Infosys Technologies Ltd. May 2004 - Jul + * 2004 Ecole des Mines de Nantes, France + */ +import java.awt.event.*; +import java.util.*; + +/** + * @author Yana Stamcheva + */ +public interface CloseListener extends EventListener { + public void closeOperation(MouseEvent e); +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/event/DoubleClickListener.java b/src/net/java/sip/communicator/plugin/desktoputil/event/DoubleClickListener.java new file mode 100644 index 0000000..8e84bf9 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/event/DoubleClickListener.java @@ -0,0 +1,22 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.event; + +/* + * The following code borrowed from David Bismut, davidou@mageos.com Intern, + * SETLabs, Infosys Technologies Ltd. May 2004 - Jul 2004 Ecole des Mines de + * Nantes, France + */ +import java.awt.event.*; +import java.util.*; + +/** + * @author Yana Stamcheva + */ +public interface DoubleClickListener extends EventListener { + public void doubleClickOperation(MouseEvent e); +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/event/MaxListener.java b/src/net/java/sip/communicator/plugin/desktoputil/event/MaxListener.java new file mode 100644 index 0000000..e5791e7 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/event/MaxListener.java @@ -0,0 +1,22 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.event; + +/* + * The following code borrowed from David Bismut, davidou@mageos.com Intern, + * SETLabs, Infosys Technologies Ltd. May 2004 - Jul 2004 Ecole des Mines de + * Nantes, France + */ +import java.awt.event.*; +import java.util.*; + +/** + * @author Yana Stamcheva + */ +public interface MaxListener extends EventListener { + public void maxOperation(MouseEvent e); +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/event/PopupOutsideListener.java b/src/net/java/sip/communicator/plugin/desktoputil/event/PopupOutsideListener.java new file mode 100644 index 0000000..3cbb040 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/event/PopupOutsideListener.java @@ -0,0 +1,22 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.event; + +/* + * The following code borrowed from David Bismut, davidou@mageos.com Intern, + * SETLabs, Infosys Technologies Ltd. May 2004 - Jul 2004 Ecole des Mines de + * Nantes, France + */ +import java.awt.event.*; +import java.util.*; + +/** + * @author Yana Stamcheva + */ +public interface PopupOutsideListener extends EventListener { + public void popupOutsideOperation(MouseEvent e); +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/event/TextFieldChangeListener.java b/src/net/java/sip/communicator/plugin/desktoputil/event/TextFieldChangeListener.java new file mode 100644 index 0000000..214d5b3 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/event/TextFieldChangeListener.java @@ -0,0 +1,27 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.event; + +/** + * The <tt>TextFieldChangeListener</tt> listens for any changes in the text + * contained in a <tt>SIPCommTextField</tt>. It is notified every time a char + * is inserted or removed from the field. + * + * @author Yana Stamcheva + */ +public interface TextFieldChangeListener +{ + /** + * Indicates that a text has been removed from the text field. + */ + public void textRemoved(); + + /** + * Indicates that a text has been inserted to the text field. + */ + public void textInserted(); +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/plaf/SIPCommLinkButtonUI.java b/src/net/java/sip/communicator/plugin/desktoputil/plaf/SIPCommLinkButtonUI.java new file mode 100644 index 0000000..ce54319 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/plaf/SIPCommLinkButtonUI.java @@ -0,0 +1,75 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.plaf; + +import java.awt.*; + +import javax.swing.*; +import javax.swing.plaf.*; +import javax.swing.plaf.basic.*; + +import net.java.sip.communicator.plugin.desktoputil.*; + +/** + * The SIPCommLinkButtonUI implementation. + * @author ROTH Damien + */ +public class SIPCommLinkButtonUI + extends BasicButtonUI +{ + private static final SIPCommLinkButtonUI ui = new SIPCommLinkButtonUI(); + + public static ComponentUI createUI(JComponent jcomponent) + { + return ui; + } + + protected void paintText( + Graphics g, JComponent com, Rectangle rect, String s) + { + SIPCommLinkButton bn = (SIPCommLinkButton) com; + + ButtonModel bnModel = bn.getModel(); + if (bnModel.isEnabled()) + { + if (bnModel.isPressed()) + bn.setForeground(bn.getActiveLinkColor()); + else if (bn.isLinkVisited()) + bn.setForeground(bn.getVisitedLinkColor()); + else + bn.setForeground(bn.getLinkColor()); + } + else + { + if (bn.getDisabledLinkColor() != null) + bn.setForeground(bn.getDisabledLinkColor()); + } + + super.paintText(g, com, rect, s); + int behaviour = bn.getLinkBehavior(); + + if (!(behaviour == SIPCommLinkButton.HOVER_UNDERLINE + && bnModel.isRollover()) + && behaviour != SIPCommLinkButton.ALWAYS_UNDERLINE) + return; + + FontMetrics fm = g.getFontMetrics(); + int x = rect.x + getTextShiftOffset(); + int y = (rect.y + fm.getAscent() + + fm.getDescent() + getTextShiftOffset()) - 1; + if (bnModel.isEnabled()) + { + g.setColor(bn.getForeground()); + g.drawLine(x, y, (x + rect.width) - 1, y); + } + else + { + g.setColor(bn.getBackground().brighter()); + g.drawLine(x, y, (x + rect.width) - 1, y); + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/plaf/SIPCommMenuBarUI.java b/src/net/java/sip/communicator/plugin/desktoputil/plaf/SIPCommMenuBarUI.java new file mode 100644 index 0000000..b3b0727 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/plaf/SIPCommMenuBarUI.java @@ -0,0 +1,33 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.plaf; + +import javax.swing.*; +import javax.swing.plaf.*; +import javax.swing.plaf.basic.*; + +/** + * @author Yana Stamcheva + */ +public class SIPCommMenuBarUI + extends BasicMenuBarUI +{ + /** + * Creates a new SIPCommMenuUI instance. + */ + public static ComponentUI createUI(JComponent x) + { + return new SIPCommMenuBarUI(); + } + + protected void installDefaults() + { + super.installDefaults(); + + LookAndFeel.installProperty(menuBar, "opaque", Boolean.FALSE); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/plaf/SIPCommTabbedPaneEnhancedUI.java b/src/net/java/sip/communicator/plugin/desktoputil/plaf/SIPCommTabbedPaneEnhancedUI.java new file mode 100644 index 0000000..119335b --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/plaf/SIPCommTabbedPaneEnhancedUI.java @@ -0,0 +1,477 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.plaf; + +/* + * The content of this file was based on code borrowed from David Bismut, + * davidou@mageos.com Intern, SETLabs, Infosys Technologies Ltd. May 2004 - Jul + * 2004 Ecole des Mines de Nantes, France + */ + +import java.awt.*; +import java.awt.image.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.plaf.*; +import javax.swing.plaf.basic.*; +import javax.swing.text.*; + +import net.java.sip.communicator.plugin.desktoputil.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.skin.*; + +/** + * This UI displays a different interface, which is independent from the look + * and feel. + * + * @author David Bismut, davidou@mageos.com + * @author Yana Stamcheva + * @author Adam Netocny + */ +public class SIPCommTabbedPaneEnhancedUI + extends SIPCommTabbedPaneUI + implements Skinnable +{ + private static Color TAB_HIGHLIGHT_FOREGROUND_COLOR + = new Color(DesktopUtilActivator.getResources() + .getColor("service.gui.TAB_TITLE_HIGHLIGHT")); + + private static Color TAB_SELECTED_FOREGROUND_COLOR + = new Color(DesktopUtilActivator.getResources() + .getColor("service.gui.TAB_TITLE_SELECTED")); + + private static final int TAB_OVERLAP + = Integer.parseInt(DesktopUtilActivator.getResources(). + getSettingsString("impl.gui.TAB_OVERLAP")); + + private static final int PREFERRED_WIDTH = 150; + + /** + * The image used in the <tt>SIPCommLookAndFeel</tt> to paint the background + * of a selected tab. + */ + private static final String SELECTED_TAB_LEFT_BG = + "service.gui.lookandfeel.SELECTED_TAB_LEFT_BG"; + + /** + * The image used in the <tt>SIPCommLookAndFeel</tt> to paint the background + * of a selected tab. + */ + private static final String SELECTED_TAB_MIDDLE_BG = + "service.gui.lookandfeel.SELECTED_TAB_MIDDLE_BG"; + + /** + * The image used in the <tt>SIPCommLookAndFeel</tt> to paint the background + * of a selected tab. + */ + private static final String SELECTED_TAB_RIGHT_BG = + "service.gui.lookandfeel.SELECTED_TAB_RIGHT_BG"; + + /** + * The image used in the <tt>SIPCommLookAndFeel</tt> to paint the background + * of a tab. + */ + private static final String TAB_LEFT_BG = + "service.gui.lookandfeel.TAB_LEFT_BG"; + + /** + * The image used in the <tt>SIPCommLookAndFeel</tt> to paint the background + * of a tab. + */ + private static final String TAB_MIDDLE_BG = + "service.gui.lookandfeel.TAB_MIDDLE_BG"; + + /** + * The image used in the <tt>SIPCommLookAndFeel</tt> to paint the background + * of a tab. + */ + private static final String TAB_RIGHT_BG = + "service.gui.lookandfeel.TAB_RIGHT_BG"; + + protected final java.util.List<Integer> highlightedTabs + = new Vector<Integer>(); + + public static ComponentUI createUI(JComponent c) + { + return new SIPCommTabbedPaneEnhancedUI(); + } + + protected void paintFocusIndicator(Graphics g, int tabPlacement, + Rectangle[] rects, int tabIndex, Rectangle iconRect, + Rectangle textRect, boolean isSelected) {} + + /** + * Overriden to paint nothing. + */ + protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, + int x, int y, int w, int h, boolean isSelected) {} + + protected void paintContentBorderTopEdge(Graphics g, int tabPlacement, + int selectedIndex, int x, int y, int w, int h) + { + if (tabPane.getTabCount() < 1) + return; + + g.setColor(shadow); + g.drawLine(x, y, x + w - 2, y); + } + + protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement, + int selectedIndex, int x, int y, int w, int h) + { + if (tabPane.getTabCount() < 1) + return; + + g.setColor(shadow); + + g.drawLine(x, y, x, y + h - 3); + } + + protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement, + int selectedIndex, int x, int y, int w, int h) + { + if (tabPane.getTabCount() < 1) + return; + + g.setColor(shadow); + g.drawLine(x + 1, y + h - 3, x + w - 2, y + h - 3); + g.drawLine(x + 1, y + h - 2, x + w - 2, y + h - 2); + g.setColor(shadow.brighter()); + g.drawLine(x + 2, y + h - 1, x + w - 1, y + h - 1); + + } + + protected void paintContentBorderRightEdge(Graphics g, int tabPlacement, + int selectedIndex, int x, int y, int w, int h) + { + if (tabPane.getTabCount() < 1) + return; + + g.setColor(shadow); + + g.drawLine(x + w - 3, y + 1, x + w - 3, y + h - 3); + g.drawLine(x + w - 2, y + 1, x + w - 2, y + h - 3); + g.setColor(shadow.brighter()); + g.drawLine(x + w - 1, y + 2, x + w - 1, y + h - 2); + + } + + protected void paintTabBackground(Graphics g, int tabPlacement, + int tabIndex, int x, int y, int w, int h, boolean isSelected) + { + g = g.create(); + try + { + internalPaintTabBackground(g, tabPlacement, tabIndex, x, y, w, h, + isSelected); + } + finally + { + g.dispose(); + } + } + + private void internalPaintTabBackground(Graphics g, int tabPlacement, + int tabIndex, int x, int y, int w, int h, boolean isSelected) + { + BufferedImage leftImg = null; + BufferedImage middleImg = null; + BufferedImage rightImg = null; + + Graphics2D g2 = (Graphics2D) g; + + AntialiasingManager.activateAntialiasing(g2); + + int tabOverlap = 0; + + if (isSelected) + { + if (tabPane.isEnabledAt(tabIndex)) + { + leftImg = DesktopUtilActivator.getImage(SELECTED_TAB_LEFT_BG); + middleImg = DesktopUtilActivator.getImage(SELECTED_TAB_MIDDLE_BG); + rightImg = DesktopUtilActivator.getImage(SELECTED_TAB_RIGHT_BG); + + tabOverlap = TAB_OVERLAP; + } + else + { + leftImg = DesktopUtilActivator.getImage(TAB_LEFT_BG); + middleImg = DesktopUtilActivator.getImage(TAB_MIDDLE_BG); + rightImg = DesktopUtilActivator.getImage(TAB_RIGHT_BG); + } + } + else + { + leftImg = DesktopUtilActivator.getImage(TAB_LEFT_BG); + middleImg = DesktopUtilActivator.getImage(TAB_MIDDLE_BG); + rightImg = DesktopUtilActivator.getImage(TAB_RIGHT_BG); + } + + // Remove the existing gap between the tabs and the panel, which is due + // to the removal of the tabbed pane border. + y++; + // If the current tab is not the selected tab we paint it 2 more pixels + // to the bottom in order to create the disabled look. + if (!isSelected) + y+=2; + + g2.drawImage(leftImg, x, y, null); + g2.drawImage(middleImg, x + leftImg.getWidth(), y, + w - leftImg.getWidth() - rightImg.getWidth() + tabOverlap, + leftImg.getHeight(), null); + g2.drawImage(rightImg, x + w - rightImg.getWidth() + tabOverlap, y, null); + } + + protected void paintText(Graphics g, int tabPlacement, Font font, + FontMetrics metrics, int tabIndex, String title, + Rectangle textRect, boolean isSelected) + { + g.setFont(font); + + int titleWidth = SwingUtilities.computeStringWidth(metrics, title); + + int preferredWidth = 0; + if (isOneActionButtonEnabled()) { + preferredWidth = calculateTabWidth(tabPlacement, tabIndex, metrics) + - WIDTHDELTA - 15; + + if (isCloseEnabled()) + preferredWidth -= BUTTONSIZE; + + if (isMaxEnabled()) + preferredWidth -= BUTTONSIZE; + } + else + { + preferredWidth = titleWidth; + } + + while (titleWidth > preferredWidth) + { + if (title.endsWith("...")) + title = title.substring(0, title.indexOf("...") - 1) + .concat("..."); + else + title = title.substring(0, title.length() - 4) + .concat("..."); + + titleWidth = SwingUtilities.computeStringWidth(metrics, title); + } + + textRect.width = titleWidth; + + View v = getTextViewForTab(tabIndex); + if (v != null) + { + // html + v.paint(g, textRect); + } + else + { + // plain text + int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex); + + if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) + { + if (isSelected) + g.setColor(TAB_SELECTED_FOREGROUND_COLOR); + else + { + if (this.isTabHighlighted(tabIndex)) + { + g.setColor(TAB_HIGHLIGHT_FOREGROUND_COLOR); + } + else + g.setColor(tabPane.getForegroundAt(tabIndex)); + } + + BasicGraphicsUtils + .drawString(g, title, mnemIndex, + textRect.x, textRect.y + metrics.getAscent()); + } + else + { // tab disabled + g.setColor(tabPane.getBackgroundAt(tabIndex).brighter()); + BasicGraphicsUtils + .drawStringUnderlineCharAt(g, title, mnemIndex, + textRect.x, textRect.y + metrics.getAscent()); + + g.setColor(tabPane.getBackgroundAt(tabIndex).darker()); + BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, + mnemIndex, textRect.x - 1, textRect.y + + metrics.getAscent() - 1); + } + } + } + + protected class ScrollableTabButton extends + SIPCommTabbedPaneUI.ScrollableTabButton + { + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + public ScrollableTabButton(int direction) + { + super(direction); + setRolloverEnabled(true); + } + + public Dimension getPreferredSize() + { + return new Dimension(16, calculateMaxTabHeight(0)); + } + + public void paint(Graphics g) + { + Color origColor; + boolean isPressed, isRollOver, isEnabled; + int w, h, size; + + w = getWidth(); + h = getHeight(); + origColor = g.getColor(); + isPressed = getModel().isPressed(); + isRollOver = getModel().isRollover(); + isEnabled = isEnabled(); + + g.setColor(getBackground()); + g.fillRect(0, 0, w, h); + + g.setColor(shadow); + // Using the background color set above + if (direction == WEST) { + g.drawLine(0, 0, 0, h - 1); // left + g.drawLine(w - 1, 0, w - 1, 0); // right + } else + g.drawLine(w - 2, h - 1, w - 2, 0); // right + + g.drawLine(0, 0, w - 2, 0); // top + + if (isRollOver) + { + // do highlights or shadows + Color color1; + Color color2; + + if (isPressed) + { + color2 = Color.WHITE; + color1 = shadow; + } + else + { + color1 = Color.WHITE; + color2 = shadow; + } + + g.setColor(color1); + + if (direction == WEST) { + g.drawLine(1, 1, 1, h - 1); // left + g.drawLine(1, 1, w - 2, 1); // top + g.setColor(color2); + g.drawLine(w - 1, h - 1, w - 1, 1); // right + } else { + g.drawLine(0, 1, 0, h - 1); + g.drawLine(0, 1, w - 3, 1); // top + g.setColor(color2); + g.drawLine(w - 3, h - 1, w - 3, 1); // right + } + + } + + // g.drawLine(0, h - 1, w - 1, h - 1); //bottom + + // If there's no room to draw arrow, bail + if (h < 5 || w < 5) { + g.setColor(origColor); + return; + } + + if (isPressed) { + g.translate(1, 1); + } + + // Draw the arrow + size = Math.min((h - 4) / 3, (w - 4) / 3); + size = Math.max(size, 2); + paintTriangle(g, (w - size) / 2, (h - size) / 2, size, direction, + isEnabled); + + // Reset the Graphics back to it's original settings + if (isPressed) { + g.translate(-1, -1); + } + g.setColor(origColor); + + } + + } + + protected SIPCommTabbedPaneUI.ScrollableTabButton createScrollableTabButton( + int direction) + { + return new ScrollableTabButton(direction); + } + + + protected int calculateTabWidth(int tabPlacement, int tabIndex, + FontMetrics metrics) + { + int width = super.calculateTabWidth(tabPlacement, tabIndex, metrics); + + if (isOneActionButtonEnabled()) + { + if(width > PREFERRED_WIDTH) + width = PREFERRED_WIDTH; + } + + return width + WIDTHDELTA; + } + + public void tabAddHightlight(int tabIndex) + { + this.highlightedTabs.add(tabIndex); + } + + public void tabRemoveHighlight(int tabIndex) + { + Iterator<Integer> highlightedIter = highlightedTabs.iterator(); + + while (highlightedIter.hasNext()) + { + if (highlightedIter.next().intValue() == tabIndex) + { + highlightedIter.remove(); + break; + } + } + } + + public boolean isTabHighlighted(int tabIndex) + { + return highlightedTabs.contains(tabIndex); + } + + /** + * Reloads color info. + */ + public void loadSkin() + { + super.loadSkin(); + + TAB_HIGHLIGHT_FOREGROUND_COLOR = new Color(DesktopUtilActivator.getResources() + .getColor("service.gui.TAB_TITLE_HIGHLIGHT")); + + TAB_SELECTED_FOREGROUND_COLOR = new Color(DesktopUtilActivator.getResources() + .getColor("service.gui.TAB_TITLE_SELECTED")); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/plaf/SIPCommTabbedPaneUI.java b/src/net/java/sip/communicator/plugin/desktoputil/plaf/SIPCommTabbedPaneUI.java new file mode 100644 index 0000000..072ff51 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/plaf/SIPCommTabbedPaneUI.java @@ -0,0 +1,1770 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.plaf; + +/* + * The content of this file was based on code borrowed from David Bismut, + * davidou@mageos.com Intern, SETLabs, Infosys Technologies Ltd. May 2004 - Jul + * 2004 Ecole des Mines de Nantes, France + */ +import java.awt.*; +import java.awt.event.*; +import java.awt.image.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.border.*; +import javax.swing.event.*; +import javax.swing.plaf.*; +import javax.swing.plaf.basic.*; +import javax.swing.text.*; + +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.skin.*; +import net.java.sip.communicator.plugin.desktoputil.*; + +/** + * SIPCommTabbedPaneUI implementation. + */ +public class SIPCommTabbedPaneUI + extends BasicTabbedPaneUI + implements Skinnable +{ + /** + * The image used in the <tt>SIPCommLookAndFeel</tt> to paint a close + * button on a tab. + */ + private static final String CLOSE_TAB_ICON = + "service.gui.lookandfeel.CLOSE_TAB_ICON"; + + /** + * The image used in the <tt>SIPCommLookAndFeel</tt> to paint a rollover + * close button on a tab. + */ + //private static final String CLOSE_TAB_SELECTED_ICON = + // "service.gui.lookandfeel.CLOSE_TAB_SELECTED_ICON"; + + // Instance variables initialized at installation + private ContainerListener containerListener; + + private Vector<View> htmlViews; + + private Map<Integer, Integer> mnemonicToIndexMap; + + /** + * InputMap used for mnemonics. Only non-null if the JTabbedPane has + * mnemonics associated with it. Lazily created in initMnemonics. + */ + private InputMap mnemonicInputMap; + + // For use when tabLayoutPolicy = SCROLL_TAB_LAYOUT + protected ScrollableTabSupport tabScroller; + + private int tabCount; + + protected MyMouseMotionListener motionListener; + + // UI creation + + private static final int INACTIVE = 0; + + private static final int OVER = 1; + + private static final int PRESSED = 2; + + public static final int BUTTONSIZE = 15; + + public static final int WIDTHDELTA = 1; + + private static final Border PRESSEDBORDER = new SoftBevelBorder( + SoftBevelBorder.LOWERED); + + private static final Border OVERBORDER = new SoftBevelBorder( + SoftBevelBorder.RAISED); + + //private Image closeImgB; + + //private BufferedImage maxImgB; + + private Image closeImgI; + + private BufferedImage maxImgI; + + private int overTabIndex = -1; + + private int closeIndexStatus = INACTIVE; + + private int maxIndexStatus = INACTIVE; + + private boolean mousePressed = false; + + private boolean isCloseButtonEnabled = false; + + private boolean isMaxButtonEnabled = false; + + protected JPopupMenu actionPopupMenu; + + protected JMenuItem maxItem; + + protected JMenuItem closeItem; + + public SIPCommTabbedPaneUI() + { + //closeImgB = SwingSwingUtilActivator.getImage(CLOSE_TAB_SELECTED_ICON); + + //maxImgB = new BufferedImage(BUTTONSIZE, BUTTONSIZE, + // BufferedImage.TYPE_4BYTE_ABGR); + + loadSkin(); + + actionPopupMenu = new JPopupMenu(); + + maxItem = new JMenuItem("Detach"); + closeItem = new JMenuItem("Close"); + + maxItem.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + ((SIPCommTabbedPane) tabPane).fireMaxTabEvent(null, tabPane + .getSelectedIndex()); + } + }); + + closeItem.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + ((SIPCommTabbedPane) tabPane).fireCloseTabEvent(null, tabPane + .getSelectedIndex()); + } + }); + + setPopupMenu(); + } + + protected boolean isOneActionButtonEnabled() + { + return isCloseButtonEnabled || isMaxButtonEnabled; + } + + public boolean isCloseEnabled() + { + return isCloseButtonEnabled; + } + + public boolean isMaxEnabled() + { + return isMaxButtonEnabled; + } + + public void setCloseIcon(boolean b) + { + isCloseButtonEnabled = b; + setPopupMenu(); + } + + public void setMaxIcon(boolean b) + { + isMaxButtonEnabled = b; + setPopupMenu(); + } + + private void setPopupMenu() + { + actionPopupMenu.removeAll(); + if (isMaxButtonEnabled) + actionPopupMenu.add(maxItem); + if (isMaxButtonEnabled && isCloseButtonEnabled) + actionPopupMenu.addSeparator(); + if (isCloseButtonEnabled) + actionPopupMenu.add(closeItem); + } + + protected int calculateTabWidth(int tabPlacement, int tabIndex, + FontMetrics metrics) + { + int delta = 0; + + Insets tabInsets = getTabInsets(tabPlacement, tabIndex); + + if (isOneActionButtonEnabled()) + { + tabInsets.right = 0; + + if (isCloseButtonEnabled) + delta += BUTTONSIZE; + if (isMaxButtonEnabled) + delta += BUTTONSIZE; + } + + return super.calculateTabWidth(tabPlacement, tabIndex, metrics) + delta; + } + + protected int calculateTabHeight(int tabPlacement, int tabIndex, + int fontHeight) + { + return super.calculateTabHeight(tabPlacement, tabIndex, fontHeight + 4); + } + + protected void layoutLabel(int tabPlacement, FontMetrics metrics, + int tabIndex, String title, Icon icon, Rectangle tabRect, + Rectangle iconRect, Rectangle textRect, boolean isSelected) + { + textRect.x = textRect.y = iconRect.x = iconRect.y = 0; + + View v = getTextViewForTab(tabIndex); + if (v != null) { + tabPane.putClientProperty("html", v); + } + + SwingUtilities.layoutCompoundLabel((JComponent) tabPane, + metrics, + title, + icon, + SwingUtilities.CENTER, + SwingUtilities.LEFT, + SwingUtilities.CENTER, + SwingUtilities.CENTER, + tabRect, + iconRect, + textRect, + 0); + + tabPane.putClientProperty("html", null); + + if (icon != null) + { + iconRect.y = iconRect.y + 2; + iconRect.x = tabRect.x + 7; + } + + textRect.y = textRect.y + 2; + + if (icon != null) + textRect.x = iconRect.x + iconRect.width + 5; + else + textRect.x = textRect.x + 8; + } + + protected MouseListener createMouseListener() + { + return new MyMouseHandler(); + } + + protected ScrollableTabButton createScrollableTabButton(int direction) + { + return new ScrollableTabButton(direction); + } + + protected Rectangle newCloseRect(Rectangle rect) + { + int dx = rect.x + rect.width - BUTTONSIZE - WIDTHDELTA; + int dy = rect.y + (rect.height - BUTTONSIZE) / 2 + 2; + + return new Rectangle(dx, dy, BUTTONSIZE, BUTTONSIZE); + } + + protected Rectangle newMaxRect(Rectangle rect) + { + int dx = rect.x + rect.width - BUTTONSIZE - WIDTHDELTA; + int dy = rect.y + (rect.height - BUTTONSIZE) / 2 + 2; + + if (isCloseButtonEnabled) + dx -= BUTTONSIZE; + + return new Rectangle(dx, dy, BUTTONSIZE, BUTTONSIZE); + } + + protected void updateOverTab(int x, int y) + { + int overTabIndex = getTabAtLocation(x, y); + if (this.overTabIndex != overTabIndex) + { + this.overTabIndex = overTabIndex; + tabScroller.tabPanel.repaint(); + } + } + + protected void updateCloseIcon(int x, int y) + { + if (overTabIndex != -1) { + int newCloseIndexStatus = INACTIVE; + + Rectangle closeRect = newCloseRect(rects[overTabIndex]); + if (closeRect.contains(x, y)) + newCloseIndexStatus = mousePressed ? PRESSED : OVER; + + if (closeIndexStatus != newCloseIndexStatus) + { + closeIndexStatus = newCloseIndexStatus; + tabScroller.tabPanel.repaint(); + } + } + } + + protected void updateMaxIcon(int x, int y) + { + if (overTabIndex != -1) + { + int newMaxIndexStatus = INACTIVE; + + Rectangle maxRect = newMaxRect(rects[overTabIndex]); + + if (maxRect.contains(x, y)) + newMaxIndexStatus = mousePressed ? PRESSED : OVER; + + if (maxIndexStatus != newMaxIndexStatus) + { + maxIndexStatus = newMaxIndexStatus; + tabScroller.tabPanel.repaint(); + } + } + } + + private void setTabIcons(int x, int y) + { + // if the mouse isPressed + if (!mousePressed) + updateOverTab(x, y); + + if (isCloseButtonEnabled) + updateCloseIcon(x, y); + if (isMaxButtonEnabled) + updateMaxIcon(x, y); + } + + public static ComponentUI createUI(JComponent c) + { + return new SIPCommTabbedPaneUI(); + } + + /** + * Invoked by <code>installUI</code> to create a layout manager object to + * manage the <code>JTabbedPane</code>. + * + * @return a layout manager object + * + * @see javax.swing.JTabbedPane#getTabLayoutPolicy + */ + protected LayoutManager createLayoutManager() + { + return new TabbedPaneScrollLayout(); + } + + /* + * In an attempt to preserve backward compatibility for programs which have + * extended BasicTabbedPaneUI to do their own layout, the UI uses the + * installed layoutManager (and not tabLayoutPolicy) to determine if + * scrollTabLayout is enabled. + */ + + /** + * Creates and installs any required subcomponents for the JTabbedPane. + * Invoked by installUI. + * + * @since 1.4 + */ + protected void installComponents() + { + if (tabScroller == null) + { + tabScroller = new ScrollableTabSupport(tabPane.getTabPlacement()); + tabPane.add(tabScroller.viewport); + tabPane.add(tabScroller.scrollForwardButton); + tabPane.add(tabScroller.scrollBackwardButton); + } + } + + /** + * Removes any installed subcomponents from the JTabbedPane. Invoked by + * uninstallUI. + * + * @since 1.4 + */ + protected void uninstallComponents() + { + tabPane.remove(tabScroller.viewport); + tabPane.remove(tabScroller.scrollForwardButton); + tabPane.remove(tabScroller.scrollBackwardButton); + tabScroller = null; + } + + protected void installListeners() + { + if ((propertyChangeListener = createPropertyChangeListener()) != null) + { + tabPane.addPropertyChangeListener(propertyChangeListener); + } + if ((tabChangeListener = createChangeListener()) != null) + { + tabPane.addChangeListener(tabChangeListener); + } + if ((mouseListener = createMouseListener()) != null) + { + tabScroller.tabPanel.addMouseListener(mouseListener); + } + + if ((focusListener = createFocusListener()) != null) + { + tabPane.addFocusListener(focusListener); + } + + // PENDING(api) : See comment for ContainerHandler + if ((containerListener = new ContainerHandler()) != null) + { + tabPane.addContainerListener(containerListener); + if (tabPane.getTabCount() > 0) + { + htmlViews = createHTMLVector(); + } + } + + if ((motionListener = new MyMouseMotionListener()) != null) + { + tabScroller.tabPanel.addMouseMotionListener(motionListener); + } + + } + + protected void uninstallListeners() + { + if (mouseListener != null) + { + tabScroller.tabPanel.removeMouseListener(mouseListener); + mouseListener = null; + } + + if (motionListener != null) + { + tabScroller.tabPanel.removeMouseMotionListener(motionListener); + motionListener = null; + } + + if (focusListener != null) + { + tabPane.removeFocusListener(focusListener); + focusListener = null; + } + + // PENDING(api): See comment for ContainerHandler + if (containerListener != null) + { + tabPane.removeContainerListener(containerListener); + containerListener = null; + if (htmlViews != null) + { + htmlViews.removeAllElements(); + htmlViews = null; + } + } + if (tabChangeListener != null) + { + tabPane.removeChangeListener(tabChangeListener); + tabChangeListener = null; + } + if (propertyChangeListener != null) + { + tabPane.removePropertyChangeListener(propertyChangeListener); + propertyChangeListener = null; + } + + } + + protected ChangeListener createChangeListener() + { + return new TabSelectionHandler(); + } + + protected void installKeyboardActions() + { + InputMap km + = getMyInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + + SwingUtilities.replaceUIInputMap(tabPane, + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, km); + km = getMyInputMap(JComponent.WHEN_FOCUSED); + SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, km); + + ActionMap am = createMyActionMap(); + + SwingUtilities.replaceUIActionMap(tabPane, am); + + tabScroller.scrollForwardButton.setAction(am + .get("scrollTabsForwardAction")); + tabScroller.scrollBackwardButton.setAction(am + .get("scrollTabsBackwardAction")); + + } + + InputMap getMyInputMap(int condition) + { + if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) + { + return (InputMap) UIManager.get("TabbedPane.ancestorInputMap"); + } + else if (condition == JComponent.WHEN_FOCUSED) + { + return (InputMap) UIManager.get("TabbedPane.focusInputMap"); + } + return null; + } + + ActionMap createMyActionMap() + { + ActionMap map = new ActionMapUIResource(); + map.put("navigateNext", new DirectionAction(NEXT)); + map.put("navigatePrevious", new DirectionAction(PREVIOUS)); + map.put("navigateRight", new DirectionAction(EAST)); + map.put("navigateLeft", new DirectionAction(WEST)); + map.put("navigateUp", new DirectionAction(NORTH)); + map.put("navigateDown", new DirectionAction(SOUTH)); + map.put("navigatePageUp", new PageAction(true)); + map.put("navigatePageDown", new PageAction(false)); + map.put("requestFocus", new RequestFocusAction()); + map.put("requestFocusForVisibleComponent", + new RequestFocusForVisibleAction()); + map.put("setSelectedIndex", new SetSelectedIndexAction()); + map.put("scrollTabsForwardAction", new ScrollTabsForwardAction()); + map.put("scrollTabsBackwardAction", new ScrollTabsBackwardAction()); + return map; + } + + protected void uninstallKeyboardActions() + { + SwingUtilities.replaceUIActionMap(tabPane, null); + SwingUtilities.replaceUIInputMap(tabPane, + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); + SwingUtilities + .replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, null); + } + + /** + * Reloads the mnemonics. This should be invoked when a memonic changes, + * when the title of a mnemonic changes, or when tabs are added/removed. + */ + private void updateMnemonics() + { + resetMnemonics(); + for (int counter = tabPane.getTabCount() - 1; counter >= 0; counter--) + { + int mnemonic = tabPane.getMnemonicAt(counter); + + if (mnemonic > 0) + { + addMnemonic(counter, mnemonic); + } + } + } + + /** + * Resets the mnemonics bindings to an empty state. + */ + private void resetMnemonics() + { + if (mnemonicToIndexMap != null) + { + mnemonicToIndexMap.clear(); + mnemonicInputMap.clear(); + } + } + + /** + * Adds the specified mnemonic at the specified index. + */ + private void addMnemonic(int index, int mnemonic) + { + if (mnemonicToIndexMap == null) + { + initMnemonics(); + } + + mnemonicInputMap.put(KeyStroke.getKeyStroke(mnemonic, Event.ALT_MASK), + "setSelectedIndex"); + + mnemonicToIndexMap.put(mnemonic, index); + } + + /** + * Installs the state needed for mnemonics. + */ + private void initMnemonics() + { + mnemonicToIndexMap = new Hashtable<Integer, Integer>(); + mnemonicInputMap = new InputMapUIResource(); + mnemonicInputMap.setParent(SwingUtilities.getUIInputMap(tabPane, + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)); + SwingUtilities + .replaceUIInputMap(tabPane, + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, + mnemonicInputMap); + } + + // UI Rendering + + public void paint(Graphics g, JComponent c) + { + int tc = tabPane.getTabCount(); + + if (tabCount != tc) { + tabCount = tc; + updateMnemonics(); + } + + int selectedIndex = tabPane.getSelectedIndex(); + int tabPlacement = tabPane.getTabPlacement(); + + ensureCurrentLayout(); + + // Paint content border + paintContentBorder(g, tabPlacement, selectedIndex); + } + + protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects, + int tabIndex, Rectangle iconRect, Rectangle textRect) + { + Rectangle tabRect = rects[tabIndex]; + int selectedIndex = tabPane.getSelectedIndex(); + boolean isSelected = selectedIndex == tabIndex; + boolean isOver = overTabIndex == tabIndex; + Graphics2D g2 = null; + Shape save = null; + boolean cropShape = false; + int cropx = 0; + int cropy = 0; + + if (g instanceof Graphics2D) + { + g2 = (Graphics2D) g; + + AntialiasingManager.activateAntialiasing(g2); + + // Render visual for cropped tab edge... + Rectangle viewRect = tabScroller.viewport.getViewRect(); + int cropline; + + cropline = viewRect.x + viewRect.width; + if ((tabRect.x < cropline) + && (tabRect.x + tabRect.width > cropline)) + { + + cropx = cropline - 1; + cropy = tabRect.y; + cropShape = true; + } + + if (cropShape) + { + save = g2.getClip(); + g2.clipRect(tabRect.x, tabRect.y, tabRect.width, + tabRect.height); + } + } + + paintTabBackground(g, tabPlacement, tabIndex, tabRect.x, tabRect.y, + tabRect.width, tabRect.height, isSelected); + + paintTabBorder(g, tabPlacement, tabIndex, tabRect.x, tabRect.y, + tabRect.width, tabRect.height, isSelected); + + String title = tabPane.getTitleAt(tabIndex); + Font font = tabPane.getFont(); + FontMetrics metrics = g.getFontMetrics(font); + Icon icon = getIconForTab(tabIndex); + + layoutLabel(tabPlacement, metrics, tabIndex, title, icon, tabRect, + iconRect, textRect, isSelected); + + paintText(g, tabPlacement, font, metrics, tabIndex, title, textRect, + isSelected); + + paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected); + + paintFocusIndicator(g, tabPlacement, rects, tabIndex, iconRect, + textRect, isSelected); + + if (cropShape) + { + paintCroppedTabEdge(g, tabPlacement, tabIndex, isSelected, cropx, + cropy); + g2.setClip(save); + } + else if (isOver || isSelected) + { + Rectangle closeRect = newCloseRect(tabRect); + + int dx = closeRect.x; + int dy = closeRect.y; + + if (isCloseButtonEnabled) + paintCloseIcon(g2, dx, dy, isOver); + if (isMaxButtonEnabled) + paintMaxIcon(g2, dx, dy, isOver); + } + + } + + protected void paintCloseIcon(Graphics g, int dx, int dy, boolean isOver) + { + // paintActionButton(g, dx, dy, closeIndexStatus, isOver, closeB, + // closeImgB); + g.drawImage(closeImgI, dx, dy + 1, null); + } + + protected void paintMaxIcon(Graphics g, int dx, int dy, boolean isOver) + { + if (isCloseButtonEnabled) + dx -= BUTTONSIZE; + + // paintActionButton(g, dx, dy, maxIndexStatus, isOver, maxB, maxImgB); + g.drawImage(maxImgI, dx, dy + 1, null); + } + + protected void paintActionButton(Graphics g, int dx, int dy, int status, + boolean isOver, JButton button, Image image) + { + button.setBorder(null); + + if (isOver) { + switch (status) { + case OVER: + button.setBorder(OVERBORDER); + break; + case PRESSED: + button.setBorder(PRESSEDBORDER); + break; + } + } + + button.setBackground(tabScroller.tabPanel.getBackground()); + button.paint(image.getGraphics()); + g.drawImage(image, dx, dy, null); + } + + /* + * This method will create and return a polygon shape for the given tab + * rectangle which has been cropped at the specified cropline with a torn + * edge visual. e.g. A "File" tab which has cropped been cropped just after + * the "i": ------------- | ..... | | . | | ... . | | . . | | . . | | . . | + * -------------- + * + * The x, y arrays below define the pattern used to create a "torn" edge + * segment which is repeated to fill the edge of the tab. For tabs placed on + * TOP and BOTTOM, this righthand torn edge is created by line segments + * which are defined by coordinates obtained by subtracting xCropLen[i] from + * (tab.x + tab.width) and adding yCroplen[i] to (tab.y). For tabs placed on + * LEFT or RIGHT, the bottom torn edge is created by subtracting xCropLen[i] + * from (tab.y + tab.height) and adding yCropLen[i] to (tab.x). + */ + + //private static final int CROP_SEGMENT = 12; + + private void paintCroppedTabEdge(Graphics g, int tabPlacement, + int tabIndex, boolean isSelected, int x, int y) + { + g.setColor(shadow); + g.drawLine(x, y, x, y + rects[tabIndex].height); + + } + + private void ensureCurrentLayout() + { + if (!tabPane.isValid()) + { + tabPane.validate(); + } + /* + * If tabPane doesn't have a peer yet, the validate() call will silently + * fail. We handle that by forcing a layout if tabPane is still invalid. + * See bug 4237677. + */ + if (!tabPane.isValid()) + { + TabbedPaneLayout layout = (TabbedPaneLayout) tabPane.getLayout(); + layout.calculateLayoutInfo(); + } + } + + /** + * Returns the bounds of the specified tab in the coordinate space of the + * JTabbedPane component. This is required because the tab rects are by + * default defined in the coordinate space of the component where they are + * rendered, which could be the JTabbedPane (for WRAP_TAB_LAYOUT) or a + * ScrollableTabPanel (SCROLL_TAB_LAYOUT). This method should be used + * whenever the tab rectangle must be relative to the JTabbedPane itself and + * the result should be placed in a designated Rectangle object (rather than + * instantiating and returning a new Rectangle each time). The tab index + * parameter must be a valid tabbed pane tab index (0 to tab count - 1, + * inclusive). The destination rectangle parameter must be a valid + * <code>Rectangle</code> instance. The handling of invalid parameters is + * unspecified. + * + * @param tabIndex + * the index of the tab + * @param dest + * the rectangle where the result should be placed + * @return the resulting rectangle + * + * @since 1.4 + */ + + protected Rectangle getTabBounds(int tabIndex, Rectangle dest) + { + dest.width = rects[tabIndex].width; + dest.height = rects[tabIndex].height; + + Point vpp = tabScroller.viewport.getLocation(); + Point viewp = tabScroller.viewport.getViewPosition(); + dest.x = rects[tabIndex].x + vpp.x - viewp.x; + dest.y = rects[tabIndex].y + vpp.y - viewp.y; + + return dest; + } + + private int getTabAtLocation(int x, int y) + { + ensureCurrentLayout(); + + int tabCount = tabPane.getTabCount(); + for (int i = 0; i < tabCount; i++) + { + if (rects[i].contains(x, y)) + { + return i; + } + } + return -1; + } + + public int getOverTabIndex() + { + return overTabIndex; + } + + /** + * Returns the index of the tab closest to the passed in location, note that + * the returned tab may not contain the location x,y. + */ + private int getClosestTab(int x, int y) + { + int min = 0; + int tabCount = Math.min(rects.length, tabPane.getTabCount()); + int max = tabCount; + int tabPlacement = tabPane.getTabPlacement(); + boolean useX = (tabPlacement == TOP || tabPlacement == BOTTOM); + int want = (useX) ? x : y; + + while (min != max) + { + int current = (max + min) / 2; + int minLoc; + int maxLoc; + + if (useX) + { + minLoc = rects[current].x; + maxLoc = minLoc + rects[current].width; + } + else + { + minLoc = rects[current].y; + maxLoc = minLoc + rects[current].height; + } + if (want < minLoc) + { + max = current; + if (min == max) + { + return Math.max(0, current - 1); + } + } + else if (want >= maxLoc) + { + min = current; + if (max - min <= 1) + { + return Math.max(current + 1, tabCount - 1); + } + } + else + { + return current; + } + } + return min; + } + + /** + * Returns a point which is translated from the specified point in the + * JTabbedPane's coordinate space to the coordinate space of the + * ScrollableTabPanel. This is used for SCROLL_TAB_LAYOUT ONLY. + */ + private Point translatePointToTabPanel(int srcx, int srcy, Point dest) + { + Point vpp = tabScroller.viewport.getLocation(); + Point viewp = tabScroller.viewport.getViewPosition(); + dest.x = srcx + vpp.x + viewp.x; + dest.y = srcy + vpp.y + viewp.y; + return dest; + } + + // BasicTabbedPaneUI methods + + // Tab Navigation methods + + // REMIND(aim,7/29/98): This method should be made + // protected in the next release where + // API changes are allowed + // + boolean requestMyFocusForVisibleComponent() + { + Component visibleComponent = getVisibleComponent(); + if (visibleComponent.isFocusable()) + { + visibleComponent.requestFocus(); + return true; + } + else if (visibleComponent instanceof JComponent) + { + if (((JComponent) visibleComponent).requestFocusInWindow()) + { + return true; + } + } + return false; + } + + private static class DirectionAction + extends AbstractAction + { + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + private final int direction; + + public DirectionAction(int direction) + { + this.direction = direction; + } + + public void actionPerformed(ActionEvent e) + { + JTabbedPane pane = (JTabbedPane) e.getSource(); + SIPCommTabbedPaneUI ui = (SIPCommTabbedPaneUI) pane.getUI(); + ui.navigateSelectedTab(direction); + } + }; + + private static class PageAction + extends AbstractAction + { + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + private final boolean up; + + public PageAction(boolean up) + { + this.up = up; + } + + public void actionPerformed(ActionEvent e) + { + JTabbedPane pane = (JTabbedPane) e.getSource(); + SIPCommTabbedPaneUI ui = (SIPCommTabbedPaneUI) pane.getUI(); + int tabPlacement = pane.getTabPlacement(); + if (tabPlacement == TOP || tabPlacement == BOTTOM) + { + ui.navigateSelectedTab(up ? WEST : EAST); + } + else + { + ui.navigateSelectedTab(up ? NORTH : SOUTH); + } + } + }; + + private static class RequestFocusAction + extends AbstractAction + { + private static final long serialVersionUID = 0L; + + public void actionPerformed(ActionEvent e) + { + JTabbedPane pane = (JTabbedPane) e.getSource(); + pane.requestFocus(); + } + }; + + private static class RequestFocusForVisibleAction + extends AbstractAction + { + private static final long serialVersionUID = 0L; + + public void actionPerformed(ActionEvent e) + { + JTabbedPane pane = (JTabbedPane) e.getSource(); + SIPCommTabbedPaneUI ui = (SIPCommTabbedPaneUI) pane.getUI(); + ui.requestMyFocusForVisibleComponent(); + } + }; + + /** + * Selects a tab in the JTabbedPane based on the String of the action + * command. The tab selected is based on the first tab that has a mnemonic + * matching the first character of the action command. + */ + private static class SetSelectedIndexAction + extends AbstractAction + { + private static final long serialVersionUID = 0L; + + public void actionPerformed(ActionEvent e) + { + JTabbedPane pane = (JTabbedPane) e.getSource(); + + if (pane != null && (pane.getUI() instanceof SIPCommTabbedPaneUI)) + { + SIPCommTabbedPaneUI ui = (SIPCommTabbedPaneUI) pane.getUI(); + String command = e.getActionCommand(); + + if (command != null && command.length() > 0) + { + int mnemonic = e.getActionCommand().charAt(0); + if (mnemonic >= 'a' && mnemonic <= 'z') + { + mnemonic -= ('a' - 'A'); + } + + Integer index = ui.mnemonicToIndexMap.get(mnemonic); + if (index != null && pane.isEnabledAt(index.intValue())) + { + pane.setSelectedIndex(index.intValue()); + } + } + } + } + }; + + private static class ScrollTabsForwardAction + extends AbstractAction + { + private static final long serialVersionUID = 0L; + + public void actionPerformed(ActionEvent e) + { + JTabbedPane pane = null; + Object src = e.getSource(); + if (src instanceof JTabbedPane) + { + pane = (JTabbedPane) src; + } + else if (src instanceof ScrollableTabButton) + { + pane = (JTabbedPane) ((ScrollableTabButton) src).getParent(); + } + else + { + return; // shouldn't happen + } + SIPCommTabbedPaneUI ui = (SIPCommTabbedPaneUI) pane.getUI(); + + ui.tabScroller.scrollForward(pane.getTabPlacement()); + } + } + + private static class ScrollTabsBackwardAction + extends AbstractAction + { + private static final long serialVersionUID = 0L; + + public void actionPerformed(ActionEvent e) + { + JTabbedPane pane = null; + Object src = e.getSource(); + if (src instanceof JTabbedPane) + { + pane = (JTabbedPane) src; + } + else if (src instanceof ScrollableTabButton) + { + pane = (JTabbedPane) ((ScrollableTabButton) src).getParent(); + } + else + { + return; // shouldn't happen + } + SIPCommTabbedPaneUI ui = (SIPCommTabbedPaneUI) pane.getUI(); + + ui.tabScroller.scrollBackward(pane.getTabPlacement()); + } + } + + /** + * This inner class is marked "public" due to a compiler bug. This + * class should be treated as a "protected" inner class. + * Instantiate it only within subclasses of BasicTabbedPaneUI. + */ + private class TabbedPaneScrollLayout + extends TabbedPaneLayout + { + + protected int preferredTabAreaHeight(int tabPlacement, int width) + { + return calculateMaxTabHeight(tabPlacement); + } + + protected int preferredTabAreaWidth(int tabPlacement, int height) + { + return calculateMaxTabWidth(tabPlacement); + } + + public void layoutContainer(Container parent) + { + int tabPlacement = tabPane.getTabPlacement(); + int tabCount = tabPane.getTabCount(); + Insets insets = tabPane.getInsets(); + int selectedIndex = tabPane.getSelectedIndex(); + Component visibleComponent = getVisibleComponent(); + + calculateLayoutInfo(); + + if (selectedIndex < 0) + { + if (visibleComponent != null) + { + // The last tab was removed, so remove the component + setVisibleComponent(null); + } + } + else + { + Component selectedComponent = + tabPane.getComponentAt(selectedIndex); + boolean shouldChangeFocus = false; + + // In order to allow programs to use a single component + // as the display for multiple tabs, we will not change + // the visible component if the currently selected tab + // has a null component. This is a bit dicey, as we don't + // explicitly state we support this in the spec, but since + // programs are now depending on this, we're making it work. + // + if (selectedComponent != null) + { + if (selectedComponent != visibleComponent + && visibleComponent != null) + { + if (KeyboardFocusManager.getCurrentKeyboardFocusManager() + .getFocusOwner() != null) + { + shouldChangeFocus = true; + } + } + setVisibleComponent(selectedComponent); + } + int tx, ty, tw, th; // tab area bounds + int cx, cy, cw, ch; // content area bounds + Insets contentInsets = getContentBorderInsets(tabPlacement); + Rectangle bounds = tabPane.getBounds(); + int numChildren = tabPane.getComponentCount(); + + if (numChildren > 0) + { + + // calculate tab area bounds + tw = bounds.width - insets.left - insets.right; + th = + calculateTabAreaHeight(tabPlacement, runCount, + maxTabHeight); + tx = insets.left; + ty = insets.top; + + // calculate content area bounds + cx = tx + contentInsets.left; + cy = ty + th + contentInsets.top; + cw = + bounds.width - insets.left - insets.right + - contentInsets.left - contentInsets.right; + ch = + bounds.height - insets.top - insets.bottom - th + - contentInsets.top - contentInsets.bottom; + + for (int i = 0; i < numChildren; i++) + { + Component child = tabPane.getComponent(i); + + if (child instanceof ScrollableTabViewport) + { + JViewport viewport = (JViewport) child; + Rectangle viewRect = viewport.getViewRect(); + int vw = tw; + int vh = th; + + int totalTabWidth = + rects[tabCount - 1].x + + rects[tabCount - 1].width; + if (totalTabWidth > tw) + { + // Need to allow space for scrollbuttons + vw = Math.max(tw - 36, 36); + ; + if (totalTabWidth - viewRect.x <= vw) + { + // Scrolled to the end, so ensure the + // viewport size is + // such that the scroll offset aligns with a + // tab + vw = totalTabWidth - viewRect.x; + } + } + + child.setBounds(tx, ty, vw, vh); + + } + else if (child instanceof ScrollableTabButton) + { + ScrollableTabButton scrollbutton = + (ScrollableTabButton) child; + Dimension bsize = scrollbutton.getPreferredSize(); + int bx = 0; + int by = 0; + int bw = bsize.width; + int bh = bsize.height; + boolean visible = false; + + int totalTabWidth = + rects[tabCount - 1].x + + rects[tabCount - 1].width; + + if (totalTabWidth > tw) + { + int dir = + scrollbutton.scrollsForward() ? EAST : WEST; + scrollbutton.setDirection(dir); + visible = true; + bx = + dir == EAST ? bounds.width - insets.left + - bsize.width : bounds.width + - insets.left - 2 * bsize.width; + by = + (tabPlacement == TOP ? ty + th + - bsize.height : ty); + } + + child.setVisible(visible); + if (visible) + { + child.setBounds(bx, by, bw, bh); + } + + } + else + { + // All content children... + child.setBounds(cx, cy, cw, ch); + } + } + if (shouldChangeFocus) + { + if (!requestMyFocusForVisibleComponent()) + { + tabPane.requestFocus(); + } + } + } + } + } + + protected void calculateTabRects(int tabPlacement, int tabCount) + { + FontMetrics metrics = getFontMetrics();; + Insets tabAreaInsets = getTabAreaInsets(tabPlacement); + int i; + + int x = tabAreaInsets.left - 2; + int y = tabAreaInsets.top; + int totalWidth = 0; + int totalHeight = 0; + + // + // Calculate bounds within which a tab run must fit + // + maxTabHeight = calculateMaxTabHeight(tabPlacement); + + runCount = 0; + selectedRun = -1; + + if (tabCount == 0) + return; + + selectedRun = 0; + runCount = 1; + + // Run through tabs and lay them out in a single run + Rectangle rect; + for (i = 0; i < tabCount; i++) + { + rect = rects[i]; + + if (i > 0) + { + rect.x = rects[i - 1].x + rects[i - 1].width - 1; + } + else + { + tabRuns[0] = 0; + maxTabWidth = 0; + totalHeight += maxTabHeight; + rect.x = x; + } + rect.width = calculateTabWidth(tabPlacement, i, metrics); + totalWidth = rect.x + rect.width; + maxTabWidth = Math.max(maxTabWidth, rect.width); + + rect.y = y; + rect.height = maxTabHeight /* - 2 */; + } + + // tabPanel.setSize(totalWidth, totalHeight); + tabScroller.tabPanel.setPreferredSize(new Dimension(totalWidth, + totalHeight)); + } + } + + private class ScrollableTabSupport implements ChangeListener + { + public ScrollableTabViewport viewport; + + public ScrollableTabPanel tabPanel; + + public ScrollableTabButton scrollForwardButton; + + public ScrollableTabButton scrollBackwardButton; + + public int leadingTabIndex; + + private Point tabViewPosition = new Point(0, 0); + + ScrollableTabSupport(int tabPlacement) + { + viewport = new ScrollableTabViewport(); + tabPanel = new ScrollableTabPanel(); + viewport.setView(tabPanel); + viewport.addChangeListener(this); + + scrollForwardButton = createScrollableTabButton(EAST); + scrollBackwardButton = createScrollableTabButton(WEST); + // scrollForwardButton = new ScrollableTabButton(EAST); + // scrollBackwardButton = new ScrollableTabButton(WEST); + } + + public void scrollForward(int tabPlacement) + { + Dimension viewSize = viewport.getViewSize(); + Rectangle viewRect = viewport.getViewRect(); + + if (tabPlacement == TOP || tabPlacement == BOTTOM) + { + if (viewRect.width >= viewSize.width - viewRect.x) + return; // no room left to scroll + } + else + { // tabPlacement == LEFT || tabPlacement == RIGHT + if (viewRect.height >= viewSize.height - viewRect.y) + return; + } + setLeadingTabIndex(tabPlacement, leadingTabIndex + 1); + } + + public void scrollBackward(int tabPlacement) + { + if (leadingTabIndex == 0) + return; // no room left to scroll + + setLeadingTabIndex(tabPlacement, leadingTabIndex - 1); + } + + public void setLeadingTabIndex(int tabPlacement, int index) + { + leadingTabIndex = index; + Dimension viewSize = viewport.getViewSize(); + Rectangle viewRect = viewport.getViewRect(); + + tabViewPosition.x = leadingTabIndex == 0 ? 0 + : rects[leadingTabIndex].x; + + if ((viewSize.width - tabViewPosition.x) < viewRect.width) + { + // We've scrolled to the end, so adjust the viewport size + // to ensure the view position remains aligned on a tab boundary + Dimension extentSize = new Dimension(viewSize.width + - tabViewPosition.x, viewRect.height); + viewport.setExtentSize(extentSize); + } + + viewport.setViewPosition(tabViewPosition); + } + + public void stateChanged(ChangeEvent e) + { + JViewport viewport = (JViewport) e.getSource(); + int tabPlacement = tabPane.getTabPlacement(); + int tabCount = tabPane.getTabCount(); + Rectangle vpRect = viewport.getBounds(); + Dimension viewSize = viewport.getViewSize(); + Rectangle viewRect = viewport.getViewRect(); + + leadingTabIndex = getClosestTab(viewRect.x, viewRect.y); + + // If the tab isn't right aligned, adjust it. + if (leadingTabIndex + 1 < tabCount) + { + + if (rects[leadingTabIndex].x < viewRect.x) + leadingTabIndex++; + } + Insets contentInsets = getContentBorderInsets(tabPlacement); + + tabPane.repaint(vpRect.x, vpRect.y + vpRect.height, vpRect.width, + contentInsets.top); + scrollBackwardButton.setEnabled(viewRect.x > 0); + scrollForwardButton.setEnabled(leadingTabIndex < tabCount - 1 + && viewSize.width - viewRect.x > viewRect.width); + + } + + public String toString() + { + return new String("viewport.viewSize=" + viewport.getViewSize() + + "\n" + "viewport.viewRectangle=" + viewport.getViewRect() + + "\n" + "leadingTabIndex=" + leadingTabIndex + "\n" + + "tabViewPosition=" + tabViewPosition); + } + + } + + private static class ScrollableTabViewport + extends JViewport + implements UIResource + { + private static final long serialVersionUID = 0L; + + public ScrollableTabViewport() + { + setOpaque(false); + setScrollMode(SIMPLE_SCROLL_MODE); + } + } + + private class ScrollableTabPanel + extends TransparentPanel + implements UIResource + { + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + public ScrollableTabPanel() + { + setLayout(null); + } + + public void paintComponent(Graphics g) + { + super.paintComponent(g); + + SIPCommTabbedPaneUI.this.paintTabArea(g, tabPane.getTabPlacement(), + tabPane.getSelectedIndex()); + } + } + + protected static class ScrollableTabButton + extends BasicArrowButton + implements UIResource, SwingConstants + { + private static final long serialVersionUID = 0L; + + public ScrollableTabButton(int direction) + { + super(direction, UIManager.getColor("TabbedPane.selected"), + UIManager.getColor("TabbedPane.shadow"), UIManager + .getColor("TabbedPane.darkShadow"), UIManager + .getColor("TabbedPane.highlight")); + } + + public boolean scrollsForward() + { + return direction == EAST || direction == SOUTH; + } + } + + /** + * This inner class is marked "public" due to a compiler bug. This + * class should be treated as a "protected" inner class. + * Instantiate it only within subclasses of BasicTabbedPaneUI. + */ + private class TabSelectionHandler implements ChangeListener + { + public void stateChanged(ChangeEvent e) + { + JTabbedPane tabPane = (JTabbedPane) e.getSource(); + tabPane.revalidate(); + tabPane.repaint(); + + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + { + int index = tabPane.getSelectedIndex(); + + if (index < rects.length && index != -1) + tabScroller.tabPanel.scrollRectToVisible(rects[index]); + } + } + } + + /** + * This inner class is marked "public" due to a compiler bug. This + * class should be treated as a "protected" inner class. + * Instantiate it only within subclasses of BasicTabbedPaneUI. + */ + + /* + * GES 2/3/99: The container listener code was added to support HTML + * rendering of tab titles. + * + * Ideally, we would be able to listen for property changes when a tab is + * added or its text modified. At the moment there are no such events + * because the Beans spec doesn't allow 'indexed' property changes (i.e. tab + * 2's text changed from A to B). + * + * In order to get around this, we listen for tabs to be added or removed by + * listening for the container events. we then queue up a runnable (so the + * component has a chance to complete the add) which checks the tab title of + * the new component to see if it requires HTML rendering. + * + * The Views (one per tab title requiring HTML rendering) are stored in the + * htmlViews Vector, which is only allocated after the first time we run + * into an HTML tab. Note that this vector is kept in step with the number + * of pages, and nulls are added for those pages whose tab title do not + * require HTML rendering. + * + * This makes it easy for the paint and layout code to tell whether to + * invoke the HTML engine without having to check the string during + * time-sensitive operations. + * + * When we have added a way to listen for tab additions and changes to tab + * text, this code should be removed and replaced by something which uses + * that. + */ + private class ContainerHandler implements ContainerListener + { + public void componentAdded(ContainerEvent e) + { + JTabbedPane tp = (JTabbedPane) e.getContainer(); + Component child = e.getChild(); + if (child instanceof UIResource) + return; + + int index = tp.indexOfComponent(child); + String title = tp.getTitleAt(index); + + boolean isHTML = BasicHTML.isHTMLString(title); + if (isHTML) + { + if (htmlViews == null) + { // Initialize vector + htmlViews = createHTMLVector(); + } + else + { // Vector already exists + View v = BasicHTML.createHTMLView(tp, title); + htmlViews.insertElementAt(v, index); + } + } + else + { // Not HTML + if (htmlViews != null) + { // Add placeholder + htmlViews.insertElementAt(null, index); + } // else nada! + } + } + + public void componentRemoved(ContainerEvent e) + { + JTabbedPane tp = (JTabbedPane) e.getContainer(); + Component child = e.getChild(); + if (child instanceof UIResource) + return; + + // NOTE 4/15/2002 (joutwate): + // This fix is implemented using client properties since there is + // currently no IndexPropertyChangeEvent. Once + // IndexPropertyChangeEvents have been added this code should be + // modified to use it. + Integer indexObj = (Integer) tp + .getClientProperty("__index_to_remove__"); + if (indexObj != null) + { + int index = indexObj.intValue(); + if (htmlViews != null && htmlViews.size() >= index) + { + htmlViews.removeElementAt(index); + } + } + } + } + + private Vector<View> createHTMLVector() + { + Vector<View> htmlViews = new Vector<View>(); + int count = tabPane.getTabCount(); + if (count > 0) { + for (int i = 0; i < count; i++) + { + String title = tabPane.getTitleAt(i); + if (BasicHTML.isHTMLString(title)) + { + htmlViews.addElement(BasicHTML.createHTMLView(tabPane, + title)); + } + else + { + htmlViews.addElement(null); + } + } + } + return htmlViews; + } + + private class MyMouseHandler extends MouseHandler + { + public void mousePressed(MouseEvent e) + { + if (closeIndexStatus == OVER) + { + closeIndexStatus = PRESSED; + tabScroller.tabPanel.repaint(); + } + else if (maxIndexStatus == OVER) + { + maxIndexStatus = PRESSED; + tabScroller.tabPanel.repaint(); + } + else + { + super.mousePressed(e); + } + } + + public void mouseClicked(MouseEvent e) + { + if (e.getClickCount() > 1 && overTabIndex != -1) + { + ((SIPCommTabbedPane) tabPane).fireDoubleClickTabEvent(e, + overTabIndex); + } + } + + public void mouseReleased(MouseEvent e) + { + updateOverTab(e.getX(), e.getY()); + + if (overTabIndex == -1) { + if (e.isPopupTrigger()) + ((SIPCommTabbedPane) tabPane).firePopupOutsideTabEvent(e); + return; + } + + if (isOneActionButtonEnabled() && e.isPopupTrigger()) + { + super.mousePressed(e); + + closeIndexStatus = INACTIVE; // Prevent undesired action when + maxIndexStatus = INACTIVE; // right-clicking on icons + + actionPopupMenu.show(tabScroller.tabPanel, e.getX(), e.getY()); + return; + } + + if (closeIndexStatus == PRESSED) + { + closeIndexStatus = OVER; + tabScroller.tabPanel.repaint(); + ((SIPCommTabbedPane) tabPane) + .fireCloseTabEvent(e, overTabIndex); + return; + } + + if (maxIndexStatus == PRESSED) + { + maxIndexStatus = OVER; + tabScroller.tabPanel.repaint(); + ((SIPCommTabbedPane) tabPane).fireMaxTabEvent(e, overTabIndex); + return; + } + + // Allow tabs closing with mouse middle button + if (e.getButton() == MouseEvent.BUTTON2) + ((SIPCommTabbedPane) tabPane).fireCloseTabEvent(e, overTabIndex); + } + + public void mouseExited(MouseEvent e) + { + if (!mousePressed) + { + overTabIndex = -1; + tabScroller.tabPanel.repaint(); + } + } + + } + + private class MyMouseMotionListener + implements MouseMotionListener + { + public void mouseMoved(MouseEvent e) + { + if (actionPopupMenu.isVisible()) + return; // No updates when popup is visible + mousePressed = false; + setTabIcons(e.getX(), e.getY()); + } + + public void mouseDragged(MouseEvent e) + { + if (actionPopupMenu.isVisible()) + return; // No updates when popup is visible + mousePressed = true; + setTabIcons(e.getX(), e.getY()); + } + } + + /** + * We don't want to have a content border. + */ + protected void paintContentBorder( Graphics g, + int tabPlacement, + int selectedIndex) + {} + + /** + * Reloads close icon. + */ + public void loadSkin() + { + closeImgI = DesktopUtilActivator.getImage(CLOSE_TAB_ICON); + + maxImgI = new BufferedImage(BUTTONSIZE, BUTTONSIZE, + BufferedImage.TYPE_4BYTE_ABGR); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/plaf/SIPCommTextFieldUI.java b/src/net/java/sip/communicator/plugin/desktoputil/plaf/SIPCommTextFieldUI.java new file mode 100644 index 0000000..ce4c56f --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/plaf/SIPCommTextFieldUI.java @@ -0,0 +1,513 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.plaf; + +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; +import javax.swing.plaf.*; +import javax.swing.plaf.metal.*; +import javax.swing.text.*; + +import net.java.sip.communicator.plugin.desktoputil.*; +import net.java.sip.communicator.util.skin.*; + +/** + * SIPCommTextFieldUI implementation. + * + * @author Yana Stamcheva + * @author Adam Netocny + */ +public class SIPCommTextFieldUI + extends MetalTextFieldUI + implements Skinnable, + MouseMotionListener, + MouseListener +{ + /** + * Indicates if the mouse is currently over the delete button. + */ + protected boolean isDeleteMouseOver = false; + + /** + * Indicates if the mouse is currently pressed on the delete button. + */ + protected boolean isDeleteMousePressed = false; + + /** + * The gap between the delete button and the text in the field. + */ + protected static int BUTTON_GAP = 5; + + /** + * The image of the delete text button. + */ + private Image deleteButtonImg; + + /** + * The rollover image of the delete text button. + */ + private Image deleteButtonRolloverImg; + + /** + * The image for the pressed state of the delete text button. + */ + private Image deleteButtonPressedImg; + + /** + * Indicates if the text field contains a + * delete button allowing to delete all the content at once. + */ + private boolean isDeleteButtonEnabled = false; + + /** + * The delete text button shown on the right of the field. + */ + protected SIPCommButton deleteButton; + + /** + * Indicates if the delete icon is visible. + */ + private boolean isDeleteIconVisible = false; + + /** + * The start background gradient color. + */ + private Color bgStartColor + = new Color(DesktopUtilActivator.getResources().getColor( + "service.gui.SEARCH_BACKGROUND")); + + /** + * The end background gradient color. + */ + private Color bgEndColor + = new Color(DesktopUtilActivator.getResources().getColor( + "service.gui.SEARCH_GRADIENT")); + + /** + * The start background gradient color. + */ + private Color bgBorderStartColor + = new Color(DesktopUtilActivator.getResources().getColor( + "service.gui.SEARCH_BORDER")); + + /** + * The end background gradient color. + */ + private Color bgBorderEndColor + = new Color(DesktopUtilActivator.getResources().getColor( + "service.gui.SEARCH_BORDER_GRADIENT")); + + /** + * Creates a <tt>SIPCommTextFieldUI</tt>. + */ + public SIPCommTextFieldUI() + { + loadSkin(); + } + + /** + * Returns <code>true</code> if the delete buttons is enabled and false - + * otherwise. + * @return <code>true</code> if the delete buttons is enabled and false - + * otherwise + */ + public boolean isDeleteButtonEnabled() + { + return isDeleteButtonEnabled; + } + + /** + * Updates the isDeleteButtonEnabled field. + * + * @param isDeleteButtonEnabled indicates if the delete buttons is enabled + * or not + */ + public void setDeleteButtonEnabled(boolean isDeleteButtonEnabled) + { + this.isDeleteButtonEnabled = isDeleteButtonEnabled; + } + + /** + * Adds the custom mouse listeners defined in this class to the installed + * listeners. + */ + protected void installListeners() + { + super.installListeners(); + + getComponent().addMouseListener(this); + + getComponent().addMouseMotionListener(this); + } + + /** + * Uninstalls listeners for the UI. + */ + protected void uninstallListeners() + { + super.uninstallListeners(); + + getComponent().removeMouseListener(this); + getComponent().removeMouseMotionListener(this); + } + + /** + * Paints the background of the associated component. + * @param g the <tt>Graphics</tt> object used for painting + */ + protected void customPaintBackground(Graphics g) + { + Graphics2D g2 = (Graphics2D) g.create(); + + try + { + String roundedSet = DesktopUtilActivator.getResources(). + getSettingsString( + "impl.gui.IS_SIP_COMM_TEXT_FIELD_ROUNDED"); + + boolean isRounded = true; + + if(roundedSet != null) + { + isRounded = new Boolean(roundedSet) + .booleanValue(); + } + + AntialiasingManager.activateAntialiasing(g2); + JTextComponent c = this.getComponent(); + + GradientPaint bgGradientColor = + new GradientPaint( c.getWidth() / 2, 0, + bgStartColor, + c.getWidth() / 2, c.getHeight(), + bgEndColor); + + g2.setPaint(bgGradientColor); + + if(isRounded) + { + g2.fillRoundRect(1, 1, c.getWidth() - 1, c.getHeight() - 1, + 8, 8); + } + else + { + g2.fillRect(1, 1, c.getWidth() - 1, c.getHeight() - 1); + } + + Rectangle deleteButtonRect = getDeleteButtonRect(); + + if(deleteButtonRect != null) + { + int dx = deleteButtonRect.x; + int dy = deleteButtonRect.y; + + if (c.getText() != null + && c.getText().length() > 0 + && isDeleteButtonEnabled) + { + if (isDeleteMousePressed) + g2.drawImage(deleteButtonPressedImg, dx, dy, null); + if (isDeleteMouseOver) + g2.drawImage(deleteButtonRolloverImg, dx, dy, null); + else + g2.drawImage(deleteButtonImg, dx, dy, null); + + isDeleteIconVisible = true; + } + else + isDeleteIconVisible = false; + } + else + isDeleteIconVisible = false; + + g2.setStroke(new BasicStroke(1f)); + GradientPaint bgBorderGradientColor + = new GradientPaint( c.getWidth() / 2, 0, + bgBorderStartColor, + c.getWidth() / 2, c.getHeight(), + bgBorderEndColor); + + g2.setPaint(bgBorderGradientColor); + + if(isRounded) + { + g2.drawRoundRect( + 0, 0, c.getWidth() - 1, c.getHeight() - 1, 8, 8); + } + else + { + g2.drawRect(0, 0, c.getWidth() - 1, c.getHeight() - 1); + } + } + finally + { + g2.dispose(); + } + } + + /** + * Updates the delete icon, changes the cursor and deletes the content of + * the associated text component when the mouse is pressed over the delete + * icon. + * + * @param evt the mouse event that has prompted us to update the delete + * icon. + */ + protected void updateDeleteIcon(MouseEvent evt) + { + // If the component is null we have nothing more to do here. Fixes a + // NullPointerException in getDeleteButtonRectangle(). + if (getComponent() == null) + return; + + int x = evt.getX(); + int y = evt.getY(); + + if (!isDeleteButtonEnabled) + return; + + Rectangle deleteRect = getDeleteButtonRect(); + + if (isDeleteIconVisible && deleteRect.contains(x, y)) + { + if (evt.getID() == MouseEvent.MOUSE_PRESSED) + { + isDeleteMouseOver = false; + isDeleteMousePressed = true; + } + else + { + isDeleteMouseOver = true; + isDeleteMousePressed = false; + } + + getComponent().setCursor(Cursor.getDefaultCursor()); + + if (evt.getID() == MouseEvent.MOUSE_CLICKED) + getComponent().setText(""); + } + else + { + isDeleteMouseOver = false; + isDeleteMousePressed = false; + } + + getComponent().repaint(); + } + + /** + * Calculates the delete button rectangle. + * + * @return the delete button rectangle + */ + protected Rectangle getDeleteButtonRect() + { + JTextComponent c = getComponent(); + + if(c == null) + return null; + + Rectangle rect = c.getBounds(); + + int dx = rect.width - deleteButton.getWidth() - BUTTON_GAP; + int dy = rect.height / 2 - deleteButton.getHeight()/2; + + return new Rectangle( dx, + dy, + deleteButton.getWidth(), + deleteButton.getHeight()); + } + + /** + * If we are in the case of disabled delete button, we simply call the + * parent implementation of this method, otherwise we recalculate the editor + * rectangle in order to leave place for the delete button. + * @return the visible editor rectangle + */ + protected Rectangle getVisibleEditorRect() + { + if (!isDeleteIconVisible) + { + return super.getVisibleEditorRect(); + } + + JTextComponent c = getComponent(); + + if(c == null) + return null; + + Rectangle alloc = c.getBounds(); + + if ((alloc.width > 0) && (alloc.height > 0)) + { + alloc.x = alloc.y = 0; + Insets insets = c.getInsets(); + alloc.x += insets.left; + alloc.y += insets.top; + alloc.width -= insets.left + insets.right + + getDeleteButtonRect().getWidth(); + alloc.height -= insets.top + insets.bottom; + return alloc; + } + + return null; + } + + /** + * @param bgStartColor the bgStartColor to set + */ + public void setBgStartColor(Color bgStartColor) + { + this.bgStartColor = bgStartColor; + } + + /** + * @param bgEndColor the bgEndColor to set + */ + public void setBgEndColor(Color bgEndColor) + { + this.bgEndColor = bgEndColor; + } + + /** + * @param bgBorderStartColor the bgBorderStartColor to set + */ + public void setBgBorderStartColor(Color bgBorderStartColor) + { + this.bgBorderStartColor = bgBorderStartColor; + } + + /** + * @param bgBorderEndColor the bgBorderEndColor to set + */ + public void setBgBorderEndColor(Color bgBorderEndColor) + { + this.bgBorderEndColor = bgBorderEndColor; + } + + /** + * Reloads skin information. + */ + public void loadSkin() + { + deleteButtonImg = DesktopUtilActivator.getResources() + .getImage("service.gui.lookandfeel.DELETE_TEXT_ICON").getImage(); + + deleteButtonRolloverImg = DesktopUtilActivator.getResources() + .getImage("service.gui.lookandfeel.DELETE_TEXT_ROLLOVER_ICON") + .getImage(); + + deleteButtonPressedImg = DesktopUtilActivator.getResources() + .getImage("service.gui.lookandfeel.DELETE_TEXT_PRESSED_ICON") + .getImage(); + + if(deleteButton != null) + { + deleteButton.setBackgroundImage(deleteButtonImg); + deleteButton.setRolloverImage(deleteButtonRolloverImg); + deleteButton.setPressedImage(deleteButtonPressedImg); + } + else + { + deleteButton = new SIPCommButton( + deleteButtonImg, + deleteButtonRolloverImg, + deleteButtonPressedImg, + null, null, null); + } + + deleteButton.setSize ( deleteButtonImg.getWidth(null), + deleteButtonImg.getHeight(null)); + } + + /** + * Updates the delete icon when the mouse was clicked. + * @param e the <tt>MouseEvent</tt> that notified us of the click + */ + public void mouseClicked(MouseEvent e) + { + updateDeleteIcon(e); + updateCursor(e); + } + + /** + * Updates the delete icon when the mouse is enters the component area. + * @param e the <tt>MouseEvent</tt> that notified us + */ + public void mouseEntered(MouseEvent e) + { + updateDeleteIcon(e); + updateCursor(e); + } + + /** + * Updates the delete icon when the mouse exits the component area. + * @param e the <tt>MouseEvent</tt> that notified us + */ + public void mouseExited(MouseEvent e) + { + updateDeleteIcon(e); + updateCursor(e); + } + + public void mousePressed(MouseEvent e) + { + updateDeleteIcon(e); + } + + public void mouseReleased(MouseEvent e) + { + updateDeleteIcon(e); + } + + /** + * Updates the delete icon when the mouse is dragged over. + * @param e the <tt>MouseEvent</tt> that notified us + */ + public void mouseDragged(MouseEvent e) + { + updateDeleteIcon(e); + updateCursor(e); + } + + /** + * Updates the delete icon when the mouse is moved over. + * @param e the <tt>MouseEvent</tt> that notified us + */ + public void mouseMoved(MouseEvent e) + { + updateDeleteIcon(e); + updateCursor(e); + } + + /** + * Updates the cursor type depending on a given <tt>MouseEvent</tt>. + * + * @param mouseEvent the <tt>MouseEvent</tt> on which the cursor depends + */ + private void updateCursor(MouseEvent mouseEvent) + { + Rectangle rect = getVisibleEditorRect(); + if (rect != null && rect.contains(mouseEvent.getPoint())) + { + getComponent().setCursor( + Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); + } + } + + /** + * Creates a UI for a SIPCommTextFieldUI. + * + * @param c the text field + * @return the UI + */ + public static ComponentUI createUI(JComponent c) + { + return new SIPCommTextFieldUI(); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/plaf/SIPCommTreeUI.java b/src/net/java/sip/communicator/plugin/desktoputil/plaf/SIPCommTreeUI.java new file mode 100644 index 0000000..1cb9386 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/plaf/SIPCommTreeUI.java @@ -0,0 +1,258 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.plaf; + +import java.awt.*; +import java.awt.Container; +import java.awt.event.*; + +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.plaf.*; +import javax.swing.plaf.basic.*; +import javax.swing.tree.*; + +/** + * SIPCommTreeUI implementation. + * + * @author Yana Stamcheva + */ +public class SIPCommTreeUI + extends BasicTreeUI + implements HierarchyListener, + TreeSelectionListener +{ + private static JTree tree; + + private JViewport parentViewport; + + private VariableLayoutCache layoutCache; + + /** + * Last selected index. + */ + private int lastSelectedIndex; + + /** + * Creates the UI for the given component. + * @param c the component for which we're create an UI + * @return this UI implementation + */ + public static ComponentUI createUI(JComponent c) + { + return new SIPCommTreeUI(); + } + + /** + * Installs this UI to the given component. + * @param c the component to which to install this UI + */ + public void installUI(JComponent c) + { + if ( c == null ) + throw new NullPointerException( + "null component passed to BasicTreeUI.installUI()" ); + + tree = (JTree)c; + + JViewport v = getFirstParentViewport(tree); + if(v != null) + this.parentViewport = v; + else + tree.addHierarchyListener(this); + + tree.getSelectionModel().addTreeSelectionListener(this); + + super.installUI(c); + } + + /** + * Returns the first parent view port found. + * @param c the component parents we search + * @return the first parent view port found. + */ + private JViewport getFirstParentViewport(Container c) + { + if(c == null) + return null; + else + if(c instanceof JViewport) + return (JViewport)c; + else + return getFirstParentViewport(c.getParent()); + } + + /** + * On uninstalling the ui remove the listeners. + * @param c + */ + public void uninstallUI(JComponent c) + { + tree.getSelectionModel().clearSelection(); + tree.getSelectionModel().removeTreeSelectionListener(this); + tree.removeHierarchyListener(this); + + super.uninstallUI(c); + } + + /** + * HierarchyListener's method. + * @param e the event. + */ + public void hierarchyChanged(HierarchyEvent e) + { + if (e.getID() == HierarchyEvent.HIERARCHY_CHANGED + && (e.getChangeFlags() & HierarchyEvent.PARENT_CHANGED) != 0 + && e.getChangedParent() instanceof JViewport) + { + parentViewport = (JViewport) e.getChangedParent(); + } + } + + /** + * The TreeSelectionListener's method. + * @param e the event. + */ + public void valueChanged(TreeSelectionEvent e) + { + // Update cell size. + selectionChanged( e.getOldLeadSelectionPath(), + e.getNewLeadSelectionPath()); + } + + /** + * Installs the defaults of this UI. + */ + protected void installDefaults() + { + if(tree.getBackground() == null || + tree.getBackground() instanceof UIResource) { + tree.setBackground(UIManager.getColor("Tree.background")); + } + if(getHashColor() == null || getHashColor() instanceof UIResource) { + setHashColor(UIManager.getColor("Tree.hash")); + } + if (tree.getFont() == null || tree.getFont() instanceof UIResource) + tree.setFont( UIManager.getFont("Tree.font") ); + // JTree's original row height is 16. To correctly display the + // contents on Linux we should have set it to 18, Windows 19 and + // Solaris 20. As these values vary so much it's too hard to + // be backward compatable and try to update the row height, we're + // therefor NOT going to adjust the row height based on font. If the + // developer changes the font, it's there responsibility to update + // the row height. + + setExpandedIcon(null); + setCollapsedIcon(null); + + setLeftChildIndent(0); + setRightChildIndent(0); + + LookAndFeel.installProperty(tree, "rowHeight", + UIManager.get("Tree.rowHeight")); + + largeModel = (tree.isLargeModel() && tree.getRowHeight() > 0); + + Object scrollsOnExpand = UIManager.get("Tree.scrollsOnExpand"); + if (scrollsOnExpand != null) { + LookAndFeel.installProperty( + tree, "scrollsOnExpand", scrollsOnExpand); + } + + UIManager.getDefaults().put("Tree.paintLines", false); + UIManager.getDefaults().put("Tree.lineTypeDashed", false); + } + + /** + * Creates the object responsible for managing what is expanded, as + * well as the size of nodes. + * @return the created layout cache + */ + protected AbstractLayoutCache createLayoutCache() + { + layoutCache = new VariableLayoutCache(); + return layoutCache; + } + + /** + * Do not select the <tt>ShowMoreContact</tt>. + * + * @param path the <tt>TreePath</tt> to select + * @param event the <tt>MouseEvent</tt> that provoked the select + */ + protected void selectPathForEvent(TreePath path, MouseEvent event) + { + super.selectPathForEvent(path, event); + } + + /** + * A custom layout cache that recalculates the width of the cell the match + * the width of the tree (i.e. expands the cell to the right). + */ + private class VariableLayoutCache extends VariableHeightLayoutCache + { + /** + * Returns the preferred width of the receiver. + * @param path the path, which bounds we obtain + * @param placeIn the initial rectangle of the path + * @return the bounds of the path + */ + public Rectangle getBounds(TreePath path, Rectangle placeIn) + { + Rectangle rect = super.getBounds(path, placeIn); + + if (rect != null && parentViewport != null) + { + rect.width = parentViewport.getWidth() - 2; + } + + return rect; + } + } + + /** + * Ensures the tree size. + */ + private void ensureTreeSize() + { + // Update tree height. + updateSize(); + + // Finally repaint in order the change to take place. + tree.repaint(); + } + + /** + * Refreshes row sizes corresponding to the given paths. + * + * @param oldPath the old selection path + * @param newPath the new selection path + */ + public void selectionChanged(TreePath oldPath, TreePath newPath) + { + if (oldPath != null) + layoutCache.invalidatePathBounds(oldPath); + + if (newPath != null) + { + layoutCache.invalidatePathBounds(newPath); + lastSelectedIndex = tree.getRowForPath(newPath); + } + // If the selection has disappeared, for example when the selected row + // has been removed, refresh the previously selected row. + else + { + int nextRow = (tree.getRowCount() > lastSelectedIndex) + ? lastSelectedIndex : tree.getRowCount() - 1; + + layoutCache.invalidatePathBounds( + tree.getPathForRow(nextRow)); + } + + ensureTreeSize(); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/transparent/AWTUtilitiesWrapper.java b/src/net/java/sip/communicator/plugin/desktoputil/transparent/AWTUtilitiesWrapper.java new file mode 100644 index 0000000..0cc9a72 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/transparent/AWTUtilitiesWrapper.java @@ -0,0 +1,161 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + * + * Based on the code of Anthony Petrov + * http://java.sun.com/developer/technicalArticles/GUI/translucent_shaped_windows/ + */ +package net.java.sip.communicator.plugin.desktoputil.transparent; + +import java.awt.*; +import java.lang.reflect.*; + +import net.java.sip.communicator.util.*; + +/** + * + * @author Yana Stamcheva + */ +public class AWTUtilitiesWrapper +{ + private static final Logger logger + = Logger.getLogger(AWTUtilitiesWrapper.class); + + private static Class<?> awtUtilitiesClass; + private static Class<?> translucencyClass; + private static Method mIsTranslucencySupported, mIsTranslucencyCapable, + mSetWindowShape, mSetWindowOpacity, mSetWindowOpaque; + + public static Object PERPIXEL_TRANSPARENT, TRANSLUCENT, PERPIXEL_TRANSLUCENT; + + static void init() + { + try + { + awtUtilitiesClass = Class.forName("com.sun.awt.AWTUtilities"); + translucencyClass + = Class.forName("com.sun.awt.AWTUtilities$Translucency"); + + if (translucencyClass.isEnum()) + { + Object[] kinds = translucencyClass.getEnumConstants(); + if (kinds != null) + { + PERPIXEL_TRANSPARENT = kinds[0]; + TRANSLUCENT = kinds[1]; + PERPIXEL_TRANSLUCENT = kinds[2]; + } + } + mIsTranslucencySupported = awtUtilitiesClass.getMethod( + "isTranslucencySupported", translucencyClass); + mIsTranslucencyCapable = awtUtilitiesClass.getMethod( + "isTranslucencyCapable", GraphicsConfiguration.class); + mSetWindowShape = awtUtilitiesClass.getMethod( + "setWindowShape", Window.class, Shape.class); + mSetWindowOpacity = awtUtilitiesClass.getMethod( + "setWindowOpacity", Window.class, float.class); + mSetWindowOpaque = awtUtilitiesClass.getMethod( + "setWindowOpaque", Window.class, boolean.class); + } + catch (NoSuchMethodException ex) + { + logger.info("Not available transparent windows."); + } + catch (SecurityException ex) + { + logger.info("Not available transparent windows."); + } + catch (ClassNotFoundException ex) + { + logger.info("Not available transparent windows."); + } + } + + static + { + init(); + } + + private static boolean isSupported(Method method, Object kind) + { + if (awtUtilitiesClass == null || method == null) + { + return false; + } + try + { + Object ret = method.invoke(null, kind); + if (ret instanceof Boolean) + { + return ((Boolean)ret).booleanValue(); + } + } + catch (IllegalAccessException ex) + { + logger.info("Not available transparent windows."); + } + catch (IllegalArgumentException ex) + { + logger.info("Not available transparent windows."); + } + catch (InvocationTargetException ex) + { + logger.info("Not available transparent windows."); + } + return false; + } + + public static boolean isTranslucencySupported(Object kind) + { + if (translucencyClass == null) + return false; + + return isSupported(mIsTranslucencySupported, kind); + } + + public static boolean isTranslucencyCapable(GraphicsConfiguration gc) + { + return isSupported(mIsTranslucencyCapable, gc); + } + + private static void set(Method method, Window window, Object value) + { + if (awtUtilitiesClass == null || method == null) + { + return; + } + try + { + method.invoke(null, window, value); + } + catch (IllegalAccessException ex) + { + logger.info("Not available transparent windows."); + } + catch (IllegalArgumentException ex) + { + logger.info("Not available transparent windows."); + } + catch (InvocationTargetException ex) + { + logger.info("Not available transparent windows."); + } + } + + public static void setWindowShape(Window window, Shape shape) + { + set(mSetWindowShape, window, shape); + } + + public static void setWindowOpacity(Window window, float opacity) + { + set(mSetWindowOpacity, window, Float.valueOf(opacity)); + } + + public static void setWindowOpaque(Window window, boolean opaque) + { + set(mSetWindowOpaque, window, Boolean.valueOf(opaque)); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/transparent/TransparentFrame.java b/src/net/java/sip/communicator/plugin/desktoputil/transparent/TransparentFrame.java new file mode 100644 index 0000000..95947da --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/transparent/TransparentFrame.java @@ -0,0 +1,102 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.transparent; + +import java.awt.*; + +import javax.swing.*; + +/** + * + * @author Yana Stamcheva + */ +public class TransparentFrame + extends JFrame + implements RootPaneContainer +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * Indicates if the transparency is supported from the current graphics + * environment. + */ + public static boolean isTranslucencySupported; + + /** + * Creates a transparent undecorated frame. If the transparency is not + * supported creates a normal undecorated frame. + * + * @return the created frame + */ + public static TransparentFrame createTransparentFrame() + { + isTranslucencySupported + = AWTUtilitiesWrapper.isTranslucencySupported( + AWTUtilitiesWrapper.PERPIXEL_TRANSLUCENT); + + GraphicsConfiguration translucencyCapableGC + = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + + if (!AWTUtilitiesWrapper.isTranslucencyCapable(translucencyCapableGC)) + { + translucencyCapableGC = null; + + GraphicsEnvironment env + = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice[] devices = env.getScreenDevices(); + + for (int i = 0; i < devices.length + && translucencyCapableGC == null; i++) + { + GraphicsConfiguration[] configs = devices[i].getConfigurations(); + for (int j = 0; j < configs.length + && translucencyCapableGC == null; j++) + { + if (AWTUtilitiesWrapper.isTranslucencyCapable(configs[j])) + { + translucencyCapableGC = configs[j]; + } + } + } + if (translucencyCapableGC == null) + { + isTranslucencySupported = false; + } + } + + if (isTranslucencySupported) + return new TransparentFrame(translucencyCapableGC); + + return new TransparentFrame(); + } + + /** + * Creates an undecorated transparent frame. + * + * @param gc the <tt>GraphicsConfiguration</tt> to use + */ + private TransparentFrame(GraphicsConfiguration gc) + { + super(gc); + + setUndecorated(true); + AWTUtilitiesWrapper.setWindowOpaque(this, false); + AWTUtilitiesWrapper.setWindowOpacity(this, 1f); + } + + /** + * Creates an undecorated frame. + */ + private TransparentFrame() + { + setUndecorated(true); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/wizard/EncodingsPanel.java b/src/net/java/sip/communicator/plugin/desktoputil/wizard/EncodingsPanel.java new file mode 100644 index 0000000..97c9275 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/wizard/EncodingsPanel.java @@ -0,0 +1,227 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.wizard; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import javax.swing.*; +import javax.swing.event.*; + +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.plugin.desktoputil.*; +import org.jitsi.service.neomedia.*; +import org.jitsi.service.neomedia.codec.*; +import org.jitsi.service.resources.*; + + +/** + * The encodings configuration panel (used in the account configuration wizards) + * + * @author Boris Grozev + */ +public class EncodingsPanel extends TransparentPanel +{ + /** + * The <tt>Logger</tt> used by the <tt>EncodingsPanel</tt> class for + * logging output. + */ + private static final Logger logger = Logger.getLogger(EncodingsPanel.class); + + /** + * The <tt>ResourceManagementService</tt> used by this class + */ + private static ResourceManagementService resourceService + = UtilActivator.getResources(); + + /** + * The "override global settings" checkbox. + */ + private final JCheckBox overrideCheckBox; + + /** + * The <tt>MediaConfiguration</tt> instance we'll use to obtain most of the + * <tt>Component</tt>s for the panel + */ + private final MediaConfigurationService mediaConfiguration; + + /** + * A panel to hold the audio encodings table + */ + private JPanel audioPanel; + + /** + * The audio encodings table (and "up"/"down" buttons) + */ + private Component audioControls; + + /** + * A panel to hold the video encodings table + */ + private JPanel videoPanel; + + /** + * The video encodings table (and "up"/"down" buttons) + */ + private Component videoControls; + + /** + * Holds the properties we need to get/set for the encoding preferences + */ + private Map<String, String> encodingProperties + = new HashMap<String, String>(); + + /** + * An <tt>EncodingConfiguration</tt> we'll be using to manage preferences + * for us + */ + private EncodingConfiguration encodingConfiguration; + + /** + * The "reset" button + */ + private JButton resetButton = new JButton(resourceService.getI18NString( + "plugin.jabberaccregwizz.RESET")); + + /** + * Builds an object, loads the tables with the global configuration.. + */ + public EncodingsPanel() + { + super(new BorderLayout()); + + overrideCheckBox = new SIPCommCheckBox(resourceService. + getI18NString("plugin.jabberaccregwizz.OVERRIDE_ENCODINGS"), + false); + overrideCheckBox.addChangeListener(new ChangeListener() + { + public void stateChanged(ChangeEvent e) + { + updateTableState(); + } + }); + + mediaConfiguration + = UtilActivator.getMediaConfiguration(); + + //by default (on account creation) use an <tt>EncodingConfiguration</tt> + //loaded with the global preferences. But make a new instance, because + //we do not want to change the current one + encodingConfiguration = mediaConfiguration.getMediaService() + .createEmptyEncodingConfiguration(); + encodingConfiguration.loadEncodingConfiguration(mediaConfiguration + .getMediaService().getCurrentEncodingConfiguration()); + + audioControls = mediaConfiguration. + createEncodingControls(MediaType.AUDIO, encodingConfiguration); + videoControls = mediaConfiguration. + createEncodingControls(MediaType.VIDEO, encodingConfiguration); + + JPanel mainPanel = new TransparentPanel(); + mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); + add(mainPanel, BorderLayout.NORTH); + + JPanel checkBoxPanel + = new TransparentPanel(new BorderLayout()); + checkBoxPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + checkBoxPanel.add(overrideCheckBox,BorderLayout.WEST); + resetButton.setToolTipText(resourceService.getI18NString( + "plugin.jabberaccregwizz.RESET_DESCRIPTION")); + checkBoxPanel.add(resetButton,BorderLayout.EAST); + resetButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + encodingConfiguration.loadEncodingConfiguration( + mediaConfiguration.getMediaService() + .getCurrentEncodingConfiguration()); + encodingConfiguration.storeProperties(encodingProperties, + ProtocolProviderFactory.ENCODING_PROP_PREFIX+"."); + resetTables(); + } + }); + + audioPanel = new TransparentPanel(new BorderLayout(10, 10)); + audioPanel.setBorder(BorderFactory.createTitledBorder( + resourceService.getI18NString("plugin.jabberaccregwizz.AUDIO"))); + audioPanel.add(audioControls); + + videoPanel = new TransparentPanel(new BorderLayout(10, 10)); + videoPanel.setBorder(BorderFactory.createTitledBorder( + resourceService.getI18NString("plugin.jabberaccregwizz.VIDEO"))); + videoPanel.add(videoControls); + + mainPanel.add(checkBoxPanel); + mainPanel.add(audioPanel); + mainPanel.add(videoPanel); + } + + /** + * Saves the settings we hold in <tt>registration</tt> + * @param registration the <tt>EncodingsRegistration</tt> to use + */ + public void commitPanel(EncodingsRegistration registration) + { + registration.setOverrideEncodings(overrideCheckBox.isSelected()); + + encodingConfiguration.storeProperties(encodingProperties, + ProtocolProviderFactory.ENCODING_PROP_PREFIX+"."); + + registration.setEncodingProperties(encodingProperties); + } + + /** + * Checks the given <tt>accountProperties</tt> for encoding configuration + * and loads it. + * @param accountProperties the properties to use. + */ + public void loadAccount(Map<String, String> accountProperties) + { + String overrideEncodings = accountProperties.get( + ProtocolProviderFactory.OVERRIDE_ENCODINGS); + boolean isOverrideEncodings = Boolean.parseBoolean(overrideEncodings); + overrideCheckBox.setSelected(isOverrideEncodings); + + encodingConfiguration = mediaConfiguration.getMediaService() + .createEmptyEncodingConfiguration(); + encodingConfiguration.loadProperties(accountProperties, + ProtocolProviderFactory.ENCODING_PROP_PREFIX); + encodingConfiguration.storeProperties(encodingProperties, + ProtocolProviderFactory.ENCODING_PROP_PREFIX+"."); + + resetTables(); + + } + + /** + * Recreates the audio and video controls. Necessary when + * our encodingConfiguration reference has changed. + */ + private void resetTables() + { + audioPanel.remove(audioControls); + videoPanel.remove(videoControls); + audioControls = mediaConfiguration. + createEncodingControls(MediaType.AUDIO, encodingConfiguration); + videoControls = mediaConfiguration. + createEncodingControls(MediaType.VIDEO, encodingConfiguration); + + audioPanel.add(audioControls); + videoPanel.add(videoControls); + updateTableState(); + } + + /** + * Enables or disables the encodings tables based on the override checkbox. + */ + private void updateTableState() + { + audioControls.setEnabled(overrideCheckBox.isSelected()); + videoControls.setEnabled(overrideCheckBox.isSelected()); + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/wizard/EncodingsRegistration.java b/src/net/java/sip/communicator/plugin/desktoputil/wizard/EncodingsRegistration.java new file mode 100644 index 0000000..fa3f2f4 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/wizard/EncodingsRegistration.java @@ -0,0 +1,41 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.wizard; + +import java.util.*; + +/** + * An interface to get/set settings in the encodings panel. + * + * @author Boris Grozev + */ +public interface EncodingsRegistration +{ + /** + * Get the stored encoding properties + * @return The stored encoding properties. + */ + Map<String, String> getEncodingProperties(); + + /** + * Set the encoding properties + * @param encodingProperties The encoding properties to set. + */ + void setEncodingProperties(Map<String, String> encodingProperties); + + /** + * Whether override encodings is enabled + * @return Whether override encodings is enabled + */ + boolean isOverrideEncodings(); + + /** + * Set the override encodings setting to <tt>override</tt> + * @param override The value to set the override encoding settings to. + */ + void setOverrideEncodings(boolean override); +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/wizard/EncryptionConfigurationTableModel.java b/src/net/java/sip/communicator/plugin/desktoputil/wizard/EncryptionConfigurationTableModel.java new file mode 100644 index 0000000..be32b11 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/wizard/EncryptionConfigurationTableModel.java @@ -0,0 +1,223 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.wizard; + +import java.util.*; + +import net.java.sip.communicator.plugin.desktoputil.*; + +/** + * Implements {@link TableModel} for encryption configuration (ZRTP, SDES and + * MIKEY). + * + * @author Lyubomir Marinov + * @author Vincent Lucas + */ +public class EncryptionConfigurationTableModel + extends MoveableTableModel +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * The encryption protocol names. + */ + private String[] encryptionProtocols; + + /** + * The encryption protocol status (enabled / disabled). + */ + private boolean[] encryptionProtocolStatus; + + /** + * Creates a new table model in order to manage the encryption protocols and + * the corresponding priority. + * + * @param encryptionProtocols The encryption protocol names. + * @param encryptionProtocolStatuss The encryption protocol status (enabled + * / disabled). + */ + public EncryptionConfigurationTableModel( + String[] encryptionProtocols, + boolean[] encryptionProtocolStatus) + { + this.init(encryptionProtocols, encryptionProtocolStatus); + } + + @Override + public Class<?> getColumnClass(int columnIndex) + { + return + (columnIndex == 0) + ? Boolean.class + : super.getColumnClass(columnIndex); + } + + public int getColumnCount() + { + return 2; + } + + /** + * Returns the number of row in this table model. + * + * @return the number of row in this table model. + */ + public int getRowCount() + { + return encryptionProtocols.length; + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) + { + return (columnIndex == 0); + } + + public Object getValueAt(int rowIndex, int columnIndex) + { + switch (columnIndex) + { + case 0: + return encryptionProtocolStatus[rowIndex]; + case 1: + return encryptionProtocols[rowIndex]; + default: + return null; + } + } + + @Override + public void setValueAt(Object value, int rowIndex, int columnIndex) + { + if ((columnIndex == 0) && (value instanceof Boolean)) + { + this.encryptionProtocolStatus[rowIndex] + = ((Boolean) value).booleanValue(); + + // We fire the update event before setting the configuration + // property in order to have more reactive user interface. + fireTableCellUpdated(rowIndex, columnIndex); + } + } + + /** + * Move the row. + * + * @param rowIndex index of the row + * @param up true to move up, false to move down + * @return the next row index + */ + public int move(int rowIndex, boolean up) + { + int toRowIndex; + if (up) + { + toRowIndex = rowIndex - 1; + if (toRowIndex < 0) + throw new IllegalArgumentException("rowIndex"); + } + else + { + toRowIndex = rowIndex + 1; + if (toRowIndex >= getRowCount()) + throw new IllegalArgumentException("rowIndex"); + } + + // Swaps the selection list. + boolean tmpSelectionItem = this.encryptionProtocolStatus[rowIndex]; + this.encryptionProtocolStatus[rowIndex] + = this.encryptionProtocolStatus[toRowIndex]; + this.encryptionProtocolStatus[toRowIndex] = tmpSelectionItem; + + // Swaps the label list. + String tmpLabel = this.encryptionProtocols[rowIndex]; + this.encryptionProtocols[rowIndex] + = this.encryptionProtocols[toRowIndex]; + this.encryptionProtocols[toRowIndex] = tmpLabel; + + fireTableRowsUpdated(rowIndex, toRowIndex); + return toRowIndex; + } + + /** + * Returns the map between encryption protocol names and their priority + * order. + * + * @return The map between encryption protocol names and their priority + * order. + */ + public Map<String, Integer> getEncryptionProtocols() + { + Map<String, Integer> encryptionProtocolMap + = new HashMap<String, Integer>(this.encryptionProtocols.length); + for(int i = 0; i < this.encryptionProtocols.length; ++i) + { + encryptionProtocolMap.put( + this.encryptionProtocols[i], + new Integer(i)); + } + return encryptionProtocolMap; + } + + /** + * Returns the map between encryption protocol names and their status. + * + * @return The map between encryption protocol names and their status. + */ + public Map<String, Boolean> getEncryptionProtocolStatus() + { + Map<String, Boolean> encryptionProtocolStatusMap + = new HashMap<String, Boolean>( + this.encryptionProtocolStatus.length); + for(int i = 0; i < this.encryptionProtocolStatus.length; ++i) + { + encryptionProtocolStatusMap.put( + encryptionProtocols[i], + new Boolean(encryptionProtocolStatus[i])); + } + return encryptionProtocolStatusMap; + } + + /** + * Returns if the label is enabled or disabled. + * + * @param label The label to be determined as enabled or disabled. + * + * @return True if the label given in parameter is enabled. False, + * otherwise. + */ + public boolean isEnabledLabel(String label) + { + for(int i = 0; i < this.encryptionProtocols.length; ++i) + { + if(this.encryptionProtocols[i].equals(label)) + { + return this.encryptionProtocolStatus[i]; + } + } + return false; + } + + /** + * Initiates this table model in order to manage the encryption protocols + * and the corresponding priority. + * + * @param encryptionProtocols The encryption protocol names. + * @param encryptionProtocolStatuss The encryption protocol status (enabled + * / disabled). + */ + public void init( + String[] encryptionProtocols, + boolean[] encryptionProtocolStatus) + { + this.encryptionProtocols = encryptionProtocols; + this.encryptionProtocolStatus = encryptionProtocolStatus; + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/wizard/SecurityAccountRegistration.java b/src/net/java/sip/communicator/plugin/desktoputil/wizard/SecurityAccountRegistration.java new file mode 100644 index 0000000..4c88ded --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/wizard/SecurityAccountRegistration.java @@ -0,0 +1,253 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.wizard; + +import net.java.sip.communicator.service.protocol.*; + +import java.util.*; + +/** + * The <tt>SecurityAccountRegistration</tt> is used to determine security + * options for different registration protocol (Jabber, SIP). Useful fot the + * SecurityPanel. + * + * @author Vincent Lucas + */ +public abstract class SecurityAccountRegistration +{ + /** + * Enables support to encrypt calls. + */ + private boolean defaultEncryption = true; + + /** + * Enqbles ZRTP encryption. + */ + private boolean sipZrtpAttribute = true; + + /** + * Tells if SDES is enabled for this account. + */ + private boolean sdesEnabled = false; + + /** + * The list of cipher suites enabled for SDES. + */ + private String sdesCipherSuites = null; + + /** + * The map between encryption protocols and their priority order. + */ + private Map<String, Integer> encryptionProtocols; + + /** + * The map between encryption protocols and their status (enabled or + * disabled). + */ + private Map<String, Boolean> encryptionProtocolStatus; + + /** + * Initializes the security account registration properties with the default + * values. + */ + public SecurityAccountRegistration() + { + // Sets the default values. + this.encryptionProtocols = new HashMap<String, Integer>(1); + this.encryptionProtocols.put( + ProtocolProviderFactory.ENCRYPTION_PROTOCOL + ".ZRTP", + 0); + this.encryptionProtocolStatus = new HashMap<String, Boolean>(1); + this.encryptionProtocolStatus.put( + ProtocolProviderFactory.ENCRYPTION_PROTOCOL_STATUS + ".ZRTP", + true); + } + + /** + * If default call encryption is enabled + * + * @return If default call encryption is enabled + */ + public boolean isDefaultEncryption() + { + return defaultEncryption; + } + + /** + * Sets default call encryption + * + * @param defaultEncryption if we want to set call encryption on as default + */ + public void setDefaultEncryption(boolean defaultEncryption) + { + this.defaultEncryption = defaultEncryption; + } + + /** + * Check if to include the ZRTP attribute to SIP/SDP or to Jabber/IQ + * + * @return include the ZRTP attribute to SIP/SDP or to Jabber/IQ + */ + public boolean isSipZrtpAttribute() + { + return sipZrtpAttribute; + } + + /** + * Sets ZRTP attribute support + * + * @param sipZrtpAttribute include the ZRTP attribute to SIP/SDP or to + * Jabber/IQ + */ + public void setSipZrtpAttribute(boolean sipZrtpAttribute) + { + this.sipZrtpAttribute = sipZrtpAttribute; + } + + /** + * Tells if SDES is enabled for this account. + * + * @return True if SDES is enabled. False, otherwise. + */ + public boolean isSDesEnabled() + { + return sdesEnabled; + } + + /** + * Enables or disables SDES for this account. + * + * @param sdesEnabled True to enable SDES. False, otherwise. + */ + public void setSDesEnabled(boolean sdesEnabled) + { + this.sdesEnabled = sdesEnabled; + } + + /** + * Returns the list of cipher suites enabled for SDES. + * + * @return The list of cipher suites enabled for SDES. Null if no cipher + * suite is enabled. + */ + public String getSDesCipherSuites() + { + return sdesCipherSuites; + } + + /** + * Sets the list of cipher suites enabled for SDES. + * + * @param The list of cipher suites enabled for SDES. Null if no cipher + * suite is enabled. + */ + public void setSDesCipherSuites(String cipherSuites) + { + this.sdesCipherSuites = cipherSuites; + } + + /** + * Sets the method used for RTP/SAVP indication. + */ + public abstract void setSavpOption(int savpOption); + + /** + * Returns the map between the encryption protocols and their priority + * order. + * + * @return The map between the encryption protocols and their priority + * order. + */ + public Map<String, Integer> getEncryptionProtocols() + { + return encryptionProtocols; + } + + /** + * Sets the map between the encryption protocols and their priority order. + * + * @param encryptionProtools The map between the encryption protocols and + * their priority order. + */ + public void setEncryptionProtocols( + Map<String, Integer> encryptionProtocols) + { + this.encryptionProtocols = encryptionProtocols; + } + + /** + * Returns the map between the encryption protocols and their status. + * + * @return The map between the encryption protocols and their status. + */ + public Map<String, Boolean> getEncryptionProtocolStatus() + { + return encryptionProtocolStatus; + } + + /** + * Sets the map between the encryption protocols and their status. + * + * @param encryptionProtools The map between the encryption protocols and + * their status. + */ + public void setEncryptionProtocolStatus( + Map<String, Boolean> encryptionProtocolStatus) + { + this.encryptionProtocolStatus = encryptionProtocolStatus; + } + + /** + * Adds the ordered encryption protocol names to the property list given in + * parameter. + * + * @param properties The property list to fill in. + */ + public void addEncryptionProtocolsToProperties( + Map<String, String> properties) + { + Map<String, Integer> encryptionProtocols + = this.getEncryptionProtocols(); + Iterator<String> encryptionProtocolIterator + = encryptionProtocols.keySet().iterator(); + String encryptionProtocol; + while(encryptionProtocolIterator.hasNext()) + { + encryptionProtocol = encryptionProtocolIterator.next(); + properties.put( + ProtocolProviderFactory.ENCRYPTION_PROTOCOL + + "." + + encryptionProtocol, + encryptionProtocols.get(encryptionProtocol).toString()); + } + } + + /** + * Adds the encryption protocol status to the property list given in + * parameter. + * + * @param properties The property list to fill in. + */ + public void addEncryptionProtocolStatusToProperties( + Map<String, String> properties) + { + Map<String, Boolean> encryptionProtocolStatus + = this.getEncryptionProtocolStatus(); + Iterator<String> encryptionProtocolStatusIterator + = encryptionProtocolStatus.keySet().iterator(); + String encryptionProtocol; + while(encryptionProtocolStatusIterator.hasNext()) + { + encryptionProtocol = encryptionProtocolStatusIterator.next(); + properties.put( + ProtocolProviderFactory.ENCRYPTION_PROTOCOL_STATUS + + "." + + encryptionProtocol, + encryptionProtocolStatus.get(encryptionProtocol) + .toString()); + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/wizard/SecurityPanel.java b/src/net/java/sip/communicator/plugin/desktoputil/wizard/SecurityPanel.java new file mode 100644 index 0000000..aa0fb81 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/desktoputil/wizard/SecurityPanel.java @@ -0,0 +1,591 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.desktoputil.wizard; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.util.List; + +import javax.swing.*; +import javax.swing.border.*; +import javax.swing.table.*; +import javax.swing.event.*; + +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.plugin.desktoputil.*; + +import org.jitsi.service.neomedia.*; + +import ch.imvs.sdes4j.srtp.*; + +/** + * Contains the security settings for SIP media encryption. + * + * @author Ingo Bauersachs + * @author Vincent Lucas + */ +public class SecurityPanel + extends TransparentPanel + implements ActionListener, + TableModelListener +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + private final SecurityAccountRegistration regform; + + private JPanel pnlAdvancedSettings; + private JCheckBox enableDefaultEncryption; + private JCheckBox enableSipZrtpAttribute; + private JComboBox cboSavpOption; + private JTable tabCiphers; + private CipherTableModel cipherModel; + private JLabel cmdExpandAdvancedSettings; + + /** + * The TableModel used to configure the encryption protocols preferences. + */ + private EncryptionConfigurationTableModel encryptionConfigurationTableModel; + + /** + * JTable with 2 buttons (up and down) which able to enable encryption + * protocols and to choose their priority order. + */ + private PriorityTable encryptionProtocolPreferences; + + /** + * The encryption protocols managed by this SecurityPanel. + */ + private static final String[] encryptionProtocols = {"ZRTP", "SDES"}; + + /** + * Boolean used to display or not the SAVP options (only useful for SIP, not + * for XMPP). + */ + private boolean displaySavpOtions; + + private static class SavpOption + { + int option; + + SavpOption(int option) + { + this.option = option; + } + + @Override + public String toString() + { + return UtilActivator.getResources().getI18NString( + "plugin.sipaccregwizz.SAVP_OPTION_" + option); + } + } + + private static class Entry + { + String cipher; + Boolean enabled; + + public Entry(String cipher, boolean enabled) + { + this.cipher = cipher; + this.enabled = enabled; + } + } + + private static class CipherTableModel extends AbstractTableModel + { + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + private List<Entry> data = new ArrayList<Entry>(); + private final String defaultCiphers = UtilActivator.getResources() + .getSettingsString(SDesControl.SDES_CIPHER_SUITES); + + CipherTableModel(String ciphers) + { + loadData(ciphers); + } + + public void loadData(String ciphers) + { + data.clear(); + + if(defaultCiphers == null) + return; + + if(ciphers == null) + ciphers = defaultCiphers; + //TODO the available ciphers should come from SDesControlImpl + data.add(new Entry(SrtpCryptoSuite.AES_CM_128_HMAC_SHA1_80, ciphers + .contains(SrtpCryptoSuite.AES_CM_128_HMAC_SHA1_80))); + data.add(new Entry(SrtpCryptoSuite.AES_CM_128_HMAC_SHA1_32, ciphers + .contains(SrtpCryptoSuite.AES_CM_128_HMAC_SHA1_32))); + data.add(new Entry(SrtpCryptoSuite.F8_128_HMAC_SHA1_80, ciphers + .contains(SrtpCryptoSuite.F8_128_HMAC_SHA1_80))); + fireTableDataChanged(); + } + + @Override + public Class<?> getColumnClass(int columnIndex) + { + switch(columnIndex) + { + case 0: + return Boolean.class; + case 1: + return String.class; + } + return null; + } + + public int getRowCount() + { + return data.size(); + } + + public int getColumnCount() + { + return 2; + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) + { + return (columnIndex == 0); + } + + public Object getValueAt(int rowIndex, int columnIndex) + { + Entry e = data.get(rowIndex); + switch(columnIndex) + { + case 0: + return e.enabled; + case 1: + return e.cipher; + } + return null; + } + + @Override + public void setValueAt(Object value, int rowIndex, int columnIndex) + { + if ((columnIndex == 0) && (value instanceof Boolean)) + { + Entry e = data.get(rowIndex); + e.enabled = (Boolean)value; + fireTableCellUpdated(rowIndex, columnIndex); + } + } + + String getEnabledCiphers() + { + StringBuilder sb = new StringBuilder(); + for (Entry e : data) + { + if(e.enabled) + { + sb.append(e.cipher); + sb.append(','); + } + } + if(sb.length() == 0) + { + return sb.toString(); + } + else + { + return sb.substring(0, sb.length()-1); + } + } + } + + /** + * Initiates a panel to configure the security (ZRTP and SDES) for SIP or + * XMPP protocols. + * + * @param regform The registration form of the account to configure. + * @param displaySavpOptions Boolean used to display or not the SAVP options + * (only useful for SIP, not for XMPP). + */ + public SecurityPanel( + SecurityAccountRegistration regform, + boolean displaySavpOptions) + { + super(new BorderLayout(10, 10)); + + this.regform = regform; + this.displaySavpOtions = displaySavpOptions; + initComponents(); + } + + private void initComponents() + { + setLayout(new BorderLayout()); + final JPanel mainPanel = new TransparentPanel(); + add(mainPanel, BorderLayout.NORTH); + + mainPanel.setLayout(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 0; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.fill = GridBagConstraints.HORIZONTAL; + + //general encryption option + enableDefaultEncryption = new SIPCommCheckBox(UtilActivator + .getResources() + .getI18NString("plugin.sipaccregwizz.ENABLE_DEFAULT_ENCRYPTION"), + regform.isDefaultEncryption()); + enableDefaultEncryption.addActionListener(this); + mainPanel.add(enableDefaultEncryption, c); + + //warning message and button to show advanced options + JLabel lblWarning = new JLabel(); + lblWarning.setBorder(new EmptyBorder(10, 5, 10, 0)); + lblWarning.setText(UtilActivator.getResources().getI18NString( + "plugin.sipaccregwizz.SECURITY_WARNING", + new String[]{ + UtilActivator.getResources().getSettingsString( + "service.gui.APPLICATION_NAME") + } + )); + c.gridy++; + mainPanel.add(lblWarning, c); + + cmdExpandAdvancedSettings = new JLabel(); + cmdExpandAdvancedSettings.setBorder(new EmptyBorder(0, 5, 0, 0)); + cmdExpandAdvancedSettings.setIcon(UtilActivator.getResources() + .getImage("service.gui.icons.RIGHT_ARROW_ICON")); + cmdExpandAdvancedSettings.setText(UtilActivator.getResources() + .getI18NString("plugin.sipaccregwizz.SHOW_ADVANCED")); + cmdExpandAdvancedSettings.addMouseListener(new MouseAdapter() + { + @Override + public void mouseClicked(MouseEvent e) + { + cmdExpandAdvancedSettings.setIcon( + UtilActivator.getResources().getImage( + pnlAdvancedSettings.isVisible() + ? "service.gui.icons.RIGHT_ARROW_ICON" + : "service.gui.icons.DOWN_ARROW_ICON")); + + pnlAdvancedSettings.setVisible( + !pnlAdvancedSettings.isVisible()); + + pnlAdvancedSettings.revalidate(); + } + }); + c.gridy++; + mainPanel.add(cmdExpandAdvancedSettings, c); + + pnlAdvancedSettings = new TransparentPanel(); + pnlAdvancedSettings.setLayout(new GridBagLayout()); + pnlAdvancedSettings.setVisible(false); + c.gridy++; + mainPanel.add(pnlAdvancedSettings, c); + + + c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 2; + c.gridheight = 1; + c.anchor = GridBagConstraints.LINE_START; + c.fill = GridBagConstraints.HORIZONTAL; + pnlAdvancedSettings.add(new JSeparator(), c); + + // Encryption protcol preferences. + JLabel lblEncryptionProtocolPreferences = new JLabel(); + lblEncryptionProtocolPreferences.setText(UtilActivator.getResources() + .getI18NString( + "plugin.sipaccregwizz.ENCRYPTION_PROTOCOL_PREFERENCES")); + c.gridy++; + pnlAdvancedSettings.add(lblEncryptionProtocolPreferences, c); + + int nbEncryptionProtocols = this.encryptionProtocols.length; + String[] encryptions = new String[nbEncryptionProtocols]; + boolean[] selectedEncryptions = new boolean[nbEncryptionProtocols]; + + this.encryptionConfigurationTableModel + = new EncryptionConfigurationTableModel( + encryptions, + selectedEncryptions); + this.encryptionProtocolPreferences = new PriorityTable( + this.encryptionConfigurationTableModel, + 60); + this.encryptionConfigurationTableModel.addTableModelListener(this); + c.gridy++; + pnlAdvancedSettings.add(this.encryptionProtocolPreferences, c); + + //ZRTP + JLabel lblZrtpOption = new JLabel(); + lblZrtpOption.setBorder(new EmptyBorder(5, 5, 5, 0)); + lblZrtpOption.setText(UtilActivator.getResources() + .getI18NString("plugin.sipaccregwizz.ZRTP_OPTION")); + c.gridx = 0; + c.gridy++; + c.gridwidth = 1; + pnlAdvancedSettings.add(lblZrtpOption, c); + c.gridx = 1; + pnlAdvancedSettings.add(new JSeparator(), c); + + enableSipZrtpAttribute = new SIPCommCheckBox( + UtilActivator.getResources() + .getI18NString("plugin.sipaccregwizz.ENABLE_SIPZRTP_ATTRIBUTE"), + regform.isSipZrtpAttribute()); + c.gridx = 0; + c.gridy++; + c.gridwidth = 2; + pnlAdvancedSettings.add(enableSipZrtpAttribute, c); + + //SDES + JLabel lblSDesOption = new JLabel(); + lblSDesOption.setBorder(new EmptyBorder(5, 5, 5, 0)); + lblSDesOption.setText( UtilActivator.getResources().getI18NString( + "plugin.sipaccregwizz.SDES_OPTION")); + c.gridx = 0; + c.gridy++; + c.gridwidth = 1; + pnlAdvancedSettings.add(lblSDesOption, c); + c.gridx = 1; + pnlAdvancedSettings.add(new JSeparator(), c); + + JLabel lblCipherInfo = new JLabel(); + lblCipherInfo.setText(UtilActivator.getResources() + .getI18NString("plugin.sipaccregwizz.CIPHER_SUITES")); + c.gridx = 0; + c.gridy++; + c.gridwidth = 2; + pnlAdvancedSettings.add(lblCipherInfo, c); + + cipherModel = new CipherTableModel(regform.getSDesCipherSuites()); + tabCiphers = new JTable(cipherModel); + tabCiphers.setShowGrid(false); + tabCiphers.setTableHeader(null); + TableColumnModel tableColumnModel = tabCiphers.getColumnModel(); + TableColumn tableColumn = tableColumnModel.getColumn(0); + tableColumn.setMaxWidth(tableColumn.getMinWidth()); + JScrollPane scrollPane = new JScrollPane(tabCiphers); + scrollPane.setPreferredSize(new Dimension(tabCiphers.getWidth(), 100)); + c.gridy++; + pnlAdvancedSettings.add(scrollPane, c); + + //SAVP selection + c.gridx = 0; + c.gridwidth = 1; + JLabel lblSavpOption = new JLabel(); + lblSavpOption.setBorder(new EmptyBorder(5, 5, 5, 0)); + lblSavpOption.setText( UtilActivator.getResources().getI18NString( + "plugin.sipaccregwizz.SAVP_OPTION")); + if(this.displaySavpOtions) + { + c.gridy++; + pnlAdvancedSettings.add(lblSavpOption, c); + } + c.gridx = 1; + if(this.displaySavpOtions) + { + pnlAdvancedSettings.add(new JSeparator(), c); + } + + cboSavpOption = new JComboBox(new SavpOption[]{ + new SavpOption(0), + new SavpOption(1), + new SavpOption(2) + }); + c.gridx = 0; + c.gridwidth = 2; + c.insets = new Insets(0, 20, 0, 0); + if(this.displaySavpOtions) + { + c.gridy++; + pnlAdvancedSettings.add(cboSavpOption, c); + } + } + + /** + * Saves the user input when the "Next" wizard buttons is clicked. + * + * @param registration the SIPAccountRegistration + * @return + */ + public boolean commitPanel(SecurityAccountRegistration registration) + { + registration.setDefaultEncryption(enableDefaultEncryption.isSelected()); + registration.setEncryptionProtocols( + encryptionConfigurationTableModel.getEncryptionProtocols()); + registration.setEncryptionProtocolStatus( + encryptionConfigurationTableModel + .getEncryptionProtocolStatus()); + registration.setSipZrtpAttribute(enableSipZrtpAttribute.isSelected()); + registration.setSavpOption(((SavpOption) cboSavpOption + .getSelectedItem()).option); + registration.setSDesCipherSuites(cipherModel.getEnabledCiphers()); + + return true; + } + + /** + * Loads the account with the given identifier. + * @param accountID the account identifier + */ + public void loadAccount(AccountID accountID) + { + enableDefaultEncryption.setSelected( + accountID.getAccountPropertyBoolean( + ProtocolProviderFactory.DEFAULT_ENCRYPTION, + true)); + + Map<String, Integer> encryptionProtocols + = accountID.getIntegerPropertiesByPrefix( + ProtocolProviderFactory.ENCRYPTION_PROTOCOL, + true); + Map<String, Boolean> encryptionProtocolStatus + = accountID.getBooleanPropertiesByPrefix( + ProtocolProviderFactory.ENCRYPTION_PROTOCOL_STATUS, + true, + false); + this.loadEncryptionProtocols( + encryptionProtocols, + encryptionProtocolStatus); + + enableSipZrtpAttribute.setSelected( + accountID.getAccountPropertyBoolean( + ProtocolProviderFactory.DEFAULT_SIPZRTP_ATTRIBUTE, + true)); + cboSavpOption.setSelectedIndex( + accountID.getAccountPropertyInt( + ProtocolProviderFactory.SAVP_OPTION, + ProtocolProviderFactory.SAVP_OFF)); + cipherModel.loadData( + accountID.getAccountPropertyString( + ProtocolProviderFactory.SDES_CIPHER_SUITES)); + loadStates(); + } + + public void actionPerformed(ActionEvent e) + { + if(e.getSource() == enableDefaultEncryption) + { + loadStates(); + } + else if(e.getSource() == cmdExpandAdvancedSettings) + { + pnlAdvancedSettings.setVisible(!pnlAdvancedSettings.isVisible()); + } + } + + public void tableChanged(TableModelEvent e) + { + if(e.getSource() == this.encryptionConfigurationTableModel) + { + loadStates(); + } + } + + private void loadStates() + { + boolean b = enableDefaultEncryption.isSelected(); + cboSavpOption.setEnabled(b); + this.encryptionProtocolPreferences.setEnabled(b); + enableSipZrtpAttribute.setEnabled( + b + && this.encryptionConfigurationTableModel + .isEnabledLabel("ZRTP")); + tabCiphers.setEnabled( + b + && this.encryptionConfigurationTableModel + .isEnabledLabel("SDES")); + } + + /** + * Loads the list of enabled and disabled encryption protocols with their + * priority. + * + * @param enabledEncryptionProtocols The list of enabled encryption protocol + * available for this account. + * @param disabledEncryptionProtocols The list of disabled encryption protocol + * available for this account. + */ + private void loadEncryptionProtocols( + Map<String, Integer> encryptionProtocols, + Map<String, Boolean> encryptionProtocolStatus) + { + int nbEncryptionProtocols = this.encryptionProtocols.length; + String[] encryptions = new String[nbEncryptionProtocols]; + boolean[] selectedEncryptions = new boolean[nbEncryptionProtocols]; + + // Load stored values. + int prefixeLength + = ProtocolProviderFactory.ENCRYPTION_PROTOCOL.length() + 1; + String encryptionProtocolPropertyName; + String name; + int index; + boolean enabled; + Iterator<String> encryptionProtocolNames + = encryptionProtocols.keySet().iterator(); + while(encryptionProtocolNames.hasNext()) + { + encryptionProtocolPropertyName = encryptionProtocolNames.next(); + index = encryptionProtocols.get(encryptionProtocolPropertyName); + // If the property is set. + if(index != -1) + { + name = encryptionProtocolPropertyName.substring(prefixeLength); + enabled = encryptionProtocolStatus.get( + ProtocolProviderFactory.ENCRYPTION_PROTOCOL_STATUS + + "." + + name); + encryptions[index] = name; + selectedEncryptions[index] = enabled; + } + } + + // Load default values. + String encryptionProtocol; + boolean set; + int j = 0; + for(int i = 0; i < this.encryptionProtocols.length; ++i) + { + encryptionProtocol = this.encryptionProtocols[i]; + // Specify a default value only if there is no specific value set. + if(!encryptionProtocols.containsKey( + ProtocolProviderFactory.ENCRYPTION_PROTOCOL + + "." + + encryptionProtocol)) + { + set = false; + // Search for the first empty element. + while(j < encryptions.length && !set) + { + if(encryptions[j] == null) + { + encryptions[j] = encryptionProtocol; + // By default only ZRTP is set to true. + selectedEncryptions[j] + = encryptionProtocol.equals("ZRTP"); + set = true; + } + ++j; + } + + } + } + + this.encryptionConfigurationTableModel.init( + encryptions, + selectedEncryptions); + } +} diff --git a/src/net/java/sip/communicator/plugin/dictaccregwizz/FirstWizardPage.java b/src/net/java/sip/communicator/plugin/dictaccregwizz/FirstWizardPage.java index 3e2b3b9..1e78af0 100644 --- a/src/net/java/sip/communicator/plugin/dictaccregwizz/FirstWizardPage.java +++ b/src/net/java/sip/communicator/plugin/dictaccregwizz/FirstWizardPage.java @@ -13,9 +13,9 @@ import javax.swing.*; import javax.swing.event.*; import net.java.dict4j.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; /** * The <tt>FirstWizardPage</tt> is the page, where user could enter the host, diff --git a/src/net/java/sip/communicator/plugin/dictaccregwizz/ProgressPanel.java b/src/net/java/sip/communicator/plugin/dictaccregwizz/ProgressPanel.java index d8f265e..0f71c64 100755 --- a/src/net/java/sip/communicator/plugin/dictaccregwizz/ProgressPanel.java +++ b/src/net/java/sip/communicator/plugin/dictaccregwizz/ProgressPanel.java @@ -10,7 +10,7 @@ import java.awt.event.*; import javax.swing.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * Panel showing the current status of the search of the strategies diff --git a/src/net/java/sip/communicator/plugin/dictaccregwizz/dictaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/dictaccregwizz/dictaccregwizz.manifest.mf index ebf4a69..a6e842a 100644 --- a/src/net/java/sip/communicator/plugin/dictaccregwizz/dictaccregwizz.manifest.mf +++ b/src/net/java/sip/communicator/plugin/dictaccregwizz/dictaccregwizz.manifest.mf @@ -17,7 +17,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.icqconstants, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.event, javax.swing.table, diff --git a/src/net/java/sip/communicator/plugin/dnsconfig/DnsContainerPanel.java b/src/net/java/sip/communicator/plugin/dnsconfig/DnsContainerPanel.java index ff55fbc..1d2bb9c 100644 --- a/src/net/java/sip/communicator/plugin/dnsconfig/DnsContainerPanel.java +++ b/src/net/java/sip/communicator/plugin/dnsconfig/DnsContainerPanel.java @@ -6,8 +6,8 @@ */
package net.java.sip.communicator.plugin.dnsconfig;
+import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.util.*;
-import net.java.sip.communicator.util.swing.*;
import org.jitsi.service.resources.*;
import org.osgi.framework.*;
diff --git a/src/net/java/sip/communicator/plugin/dnsconfig/DnssecPanel.java b/src/net/java/sip/communicator/plugin/dnsconfig/DnssecPanel.java index b4f741a..2d44a21 100644 --- a/src/net/java/sip/communicator/plugin/dnsconfig/DnssecPanel.java +++ b/src/net/java/sip/communicator/plugin/dnsconfig/DnssecPanel.java @@ -14,9 +14,9 @@ import javax.swing.*; import javax.swing.plaf.basic.*;
import javax.swing.table.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.util.*;
-import net.java.sip.communicator.util.dns.*;
-import net.java.sip.communicator.util.swing.*;
+import net.java.sip.communicator.plugin.desktoputil.dns.*;
import org.jitsi.service.configuration.*;
import org.jitsi.service.resources.*;
@@ -28,7 +28,8 @@ import org.osgi.framework.*; * @author Ingo Bauersachs
*/
public class DnssecPanel
- extends TransparentPanel implements ActionListener, FocusListener
+ extends TransparentPanel
+ implements ActionListener, FocusListener
{
/**
* Serial version UID.
diff --git a/src/net/java/sip/communicator/plugin/dnsconfig/DnssecTableModel.java b/src/net/java/sip/communicator/plugin/dnsconfig/DnssecTableModel.java index 424ee7c..6ebb319 100644 --- a/src/net/java/sip/communicator/plugin/dnsconfig/DnssecTableModel.java +++ b/src/net/java/sip/communicator/plugin/dnsconfig/DnssecTableModel.java @@ -13,7 +13,7 @@ import java.util.List; import javax.swing.table.*;
import net.java.sip.communicator.util.*;
-import net.java.sip.communicator.util.dns.*;
+import net.java.sip.communicator.plugin.desktoputil.dns.*;
import org.jitsi.service.configuration.*;
import org.jitsi.service.resources.*;
diff --git a/src/net/java/sip/communicator/plugin/dnsconfig/ParallelDnsPanel.java b/src/net/java/sip/communicator/plugin/dnsconfig/ParallelDnsPanel.java index aeeb5a0..8315a2f 100644 --- a/src/net/java/sip/communicator/plugin/dnsconfig/ParallelDnsPanel.java +++ b/src/net/java/sip/communicator/plugin/dnsconfig/ParallelDnsPanel.java @@ -6,19 +6,19 @@ */ package net.java.sip.communicator.plugin.dnsconfig; -import static net.java.sip.communicator.util.NetworkUtils.DEFAULT_BACKUP_RESOLVER; -import static net.java.sip.communicator.util.NetworkUtils.PDEFAULT_BACKUP_RESOLVER_ENABLED; -import static net.java.sip.communicator.util.NetworkUtils.PNAME_BACKUP_RESOLVER; -import static net.java.sip.communicator.util.NetworkUtils.PNAME_BACKUP_RESOLVER_ENABLED; -import static net.java.sip.communicator.util.NetworkUtils.PNAME_BACKUP_RESOLVER_FALLBACK_IP; -import static net.java.sip.communicator.util.NetworkUtils.PNAME_BACKUP_RESOLVER_PORT; -import static net.java.sip.communicator.util.NetworkUtils.getDefaultDnsPort; -import static net.java.sip.communicator.util.NetworkUtils.isIPv4Address; -import static net.java.sip.communicator.util.NetworkUtils.isIPv6Address; -import static net.java.sip.communicator.util.dns.ParallelResolver.DNS_PATIENCE; -import static net.java.sip.communicator.util.dns.ParallelResolver.DNS_REDEMPTION; -import static net.java.sip.communicator.util.dns.ParallelResolver.PNAME_DNS_PATIENCE; -import static net.java.sip.communicator.util.dns.ParallelResolver.PNAME_DNS_REDEMPTION; +import static net.java.sip.communicator.plugin.desktoputil.NetworkUtils.DEFAULT_BACKUP_RESOLVER; +import static net.java.sip.communicator.plugin.desktoputil.NetworkUtils.PDEFAULT_BACKUP_RESOLVER_ENABLED; +import static net.java.sip.communicator.plugin.desktoputil.NetworkUtils.PNAME_BACKUP_RESOLVER; +import static net.java.sip.communicator.plugin.desktoputil.NetworkUtils.PNAME_BACKUP_RESOLVER_ENABLED; +import static net.java.sip.communicator.plugin.desktoputil.NetworkUtils.PNAME_BACKUP_RESOLVER_FALLBACK_IP; +import static net.java.sip.communicator.plugin.desktoputil.NetworkUtils.PNAME_BACKUP_RESOLVER_PORT; +import static net.java.sip.communicator.plugin.desktoputil.NetworkUtils.getDefaultDnsPort; +import static net.java.sip.communicator.plugin.desktoputil.NetworkUtils.isIPv4Address; +import static net.java.sip.communicator.plugin.desktoputil.NetworkUtils.isIPv6Address; +import static net.java.sip.communicator.plugin.desktoputil.dns.ParallelResolver.DNS_PATIENCE; +import static net.java.sip.communicator.plugin.desktoputil.dns.ParallelResolver.DNS_REDEMPTION; +import static net.java.sip.communicator.plugin.desktoputil.dns.ParallelResolver.PNAME_DNS_PATIENCE; +import static net.java.sip.communicator.plugin.desktoputil.dns.ParallelResolver.PNAME_DNS_REDEMPTION; import java.awt.*; import java.awt.event.*; @@ -29,9 +29,9 @@ import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.dns.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.dns.*; import org.jitsi.service.configuration.*; import org.jitsi.service.resources.*; diff --git a/src/net/java/sip/communicator/plugin/dnsconfig/dnsconfig.manifest.mf b/src/net/java/sip/communicator/plugin/dnsconfig/dnsconfig.manifest.mf index b6a4bc3..1961b0b 100644 --- a/src/net/java/sip/communicator/plugin/dnsconfig/dnsconfig.manifest.mf +++ b/src/net/java/sip/communicator/plugin/dnsconfig/dnsconfig.manifest.mf @@ -11,8 +11,8 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.gui.event, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.dns, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil.dns, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.event, javax.swing.table, diff --git a/src/net/java/sip/communicator/plugin/exampleplugin/PluginDialog.java b/src/net/java/sip/communicator/plugin/exampleplugin/PluginDialog.java index bf2946e..0549766 100644 --- a/src/net/java/sip/communicator/plugin/exampleplugin/PluginDialog.java +++ b/src/net/java/sip/communicator/plugin/exampleplugin/PluginDialog.java @@ -10,8 +10,8 @@ import java.awt.*; import javax.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.contactlist.*; -import net.java.sip.communicator.util.swing.*; /** * A plugin dialog that is open through the right button menu over a contact and diff --git a/src/net/java/sip/communicator/plugin/exampleplugin/exampleplugin.manifest.mf b/src/net/java/sip/communicator/plugin/exampleplugin/exampleplugin.manifest.mf index f683c87..87ac64f 100644 --- a/src/net/java/sip/communicator/plugin/exampleplugin/exampleplugin.manifest.mf +++ b/src/net/java/sip/communicator/plugin/exampleplugin/exampleplugin.manifest.mf @@ -11,7 +11,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.gui.event, net.java.sip.communicator.service.protocol, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.event, javax.swing.table, diff --git a/src/net/java/sip/communicator/plugin/facebookaccregwizz/facebookaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/facebookaccregwizz/facebookaccregwizz.manifest.mf index a6143fe..b83b304 100644 --- a/src/net/java/sip/communicator/plugin/facebookaccregwizz/facebookaccregwizz.manifest.mf +++ b/src/net/java/sip/communicator/plugin/facebookaccregwizz/facebookaccregwizz.manifest.mf @@ -15,7 +15,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.event,
org.jitsi.service.resources, net.java.sip.communicator.service.resources,
net.java.sip.communicator.util,
- net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, net.java.sip.communicator.plugin.jabberaccregwizz,
javax.accessibility,
javax.imageio,
diff --git a/src/net/java/sip/communicator/plugin/generalconfig/GeneralConfigPluginActivator.java b/src/net/java/sip/communicator/plugin/generalconfig/GeneralConfigPluginActivator.java index 7960da7..4941f29 100644 --- a/src/net/java/sip/communicator/plugin/generalconfig/GeneralConfigPluginActivator.java +++ b/src/net/java/sip/communicator/plugin/generalconfig/GeneralConfigPluginActivator.java @@ -13,12 +13,12 @@ import javax.swing.*; import javax.swing.border.*; import net.java.sip.communicator.plugin.generalconfig.autoaway.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.resources.*; import net.java.sip.communicator.service.systray.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; import org.jitsi.service.configuration.*; import org.jitsi.service.resources.*; diff --git a/src/net/java/sip/communicator/plugin/generalconfig/GeneralConfigurationPanel.java b/src/net/java/sip/communicator/plugin/generalconfig/GeneralConfigurationPanel.java index 34b2ea5..6bb6522 100644 --- a/src/net/java/sip/communicator/plugin/generalconfig/GeneralConfigurationPanel.java +++ b/src/net/java/sip/communicator/plugin/generalconfig/GeneralConfigurationPanel.java @@ -16,10 +16,10 @@ import javax.swing.border.*; import javax.swing.event.*; import net.java.sip.communicator.plugin.generalconfig.autoaway.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.systray.*; import net.java.sip.communicator.util.*; import net.java.sip.communicator.util.Logger; -import net.java.sip.communicator.util.swing.*; import org.jitsi.util.*; import org.osgi.framework.*; diff --git a/src/net/java/sip/communicator/plugin/generalconfig/OpusConfigForm.java b/src/net/java/sip/communicator/plugin/generalconfig/OpusConfigForm.java index 48fae73..1cb1675 100644 --- a/src/net/java/sip/communicator/plugin/generalconfig/OpusConfigForm.java +++ b/src/net/java/sip/communicator/plugin/generalconfig/OpusConfigForm.java @@ -6,8 +6,8 @@ */ package net.java.sip.communicator.plugin.generalconfig; +import net.java.sip.communicator.plugin.desktoputil.*; -import net.java.sip.communicator.util.swing.*; import org.jitsi.service.configuration.*; import org.jitsi.service.neomedia.codec.*; diff --git a/src/net/java/sip/communicator/plugin/generalconfig/SIPConfigForm.java b/src/net/java/sip/communicator/plugin/generalconfig/SIPConfigForm.java index b7a5e91..c2a63f5 100644 --- a/src/net/java/sip/communicator/plugin/generalconfig/SIPConfigForm.java +++ b/src/net/java/sip/communicator/plugin/generalconfig/SIPConfigForm.java @@ -12,9 +12,9 @@ import java.util.List; import javax.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; /** * Implementation of the configuration form. diff --git a/src/net/java/sip/communicator/plugin/generalconfig/SilkConfigForm.java b/src/net/java/sip/communicator/plugin/generalconfig/SilkConfigForm.java index 3f00ec4..c287519 100644 --- a/src/net/java/sip/communicator/plugin/generalconfig/SilkConfigForm.java +++ b/src/net/java/sip/communicator/plugin/generalconfig/SilkConfigForm.java @@ -6,8 +6,8 @@ */ package net.java.sip.communicator.plugin.generalconfig; +import net.java.sip.communicator.plugin.desktoputil.*; -import net.java.sip.communicator.util.swing.*; import org.jitsi.service.configuration.*; import org.jitsi.service.neomedia.codec.*; @@ -23,8 +23,6 @@ import java.awt.event.*; public class SilkConfigForm extends TransparentPanel { - - /** * The default value for the SAT setting */ diff --git a/src/net/java/sip/communicator/plugin/generalconfig/autoaway/AutoAwayConfigurationPanel.java b/src/net/java/sip/communicator/plugin/generalconfig/autoaway/AutoAwayConfigurationPanel.java index 3c5a144..a0160d8 100644 --- a/src/net/java/sip/communicator/plugin/generalconfig/autoaway/AutoAwayConfigurationPanel.java +++ b/src/net/java/sip/communicator/plugin/generalconfig/autoaway/AutoAwayConfigurationPanel.java @@ -14,7 +14,7 @@ import javax.swing.event.*; import javax.swing.text.*; import net.java.sip.communicator.plugin.generalconfig.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.jitsi.service.resources.*; diff --git a/src/net/java/sip/communicator/plugin/generalconfig/generalconfig.manifest.mf b/src/net/java/sip/communicator/plugin/generalconfig/generalconfig.manifest.mf index 20e945e..5aa2084 100644 --- a/src/net/java/sip/communicator/plugin/generalconfig/generalconfig.manifest.mf +++ b/src/net/java/sip/communicator/plugin/generalconfig/generalconfig.manifest.mf @@ -17,7 +17,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.resources, net.java.sip.communicator.service.systray, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, net.java.sip.communicator.service.sysactivity, net.java.sip.communicator.service.sysactivity.event, com.sun.jna.win32, diff --git a/src/net/java/sip/communicator/plugin/gibberishaccregwizz/FirstWizardPage.java b/src/net/java/sip/communicator/plugin/gibberishaccregwizz/FirstWizardPage.java index bd54660..3736744 100644 --- a/src/net/java/sip/communicator/plugin/gibberishaccregwizz/FirstWizardPage.java +++ b/src/net/java/sip/communicator/plugin/gibberishaccregwizz/FirstWizardPage.java @@ -11,9 +11,9 @@ import java.awt.*; import javax.swing.*; import javax.swing.event.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; /** * The <tt>FirstWizardPage</tt> is the page, where user could enter the user ID diff --git a/src/net/java/sip/communicator/plugin/gibberishaccregwizz/gibberishaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/gibberishaccregwizz/gibberishaccregwizz.manifest.mf index 88de8d4..501cedc 100644 --- a/src/net/java/sip/communicator/plugin/gibberishaccregwizz/gibberishaccregwizz.manifest.mf +++ b/src/net/java/sip/communicator/plugin/gibberishaccregwizz/gibberishaccregwizz.manifest.mf @@ -15,7 +15,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.event, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.event, javax.swing.table, diff --git a/src/net/java/sip/communicator/plugin/globalproxyconfig/GlobalProxyConfigForm.java b/src/net/java/sip/communicator/plugin/globalproxyconfig/GlobalProxyConfigForm.java index 819b7e6..e8c3562 100644 --- a/src/net/java/sip/communicator/plugin/globalproxyconfig/GlobalProxyConfigForm.java +++ b/src/net/java/sip/communicator/plugin/globalproxyconfig/GlobalProxyConfigForm.java @@ -10,8 +10,8 @@ import java.awt.event.*; import javax.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; import org.jitsi.service.configuration.*; diff --git a/src/net/java/sip/communicator/plugin/globalproxyconfig/globalproxyconfig.manifest.mf b/src/net/java/sip/communicator/plugin/globalproxyconfig/globalproxyconfig.manifest.mf index 58d2153..9e1cc68 100644 --- a/src/net/java/sip/communicator/plugin/globalproxyconfig/globalproxyconfig.manifest.mf +++ b/src/net/java/sip/communicator/plugin/globalproxyconfig/globalproxyconfig.manifest.mf @@ -6,7 +6,7 @@ Bundle-Version: 0.0.1 System-Bundle: yes
Import-Package: org.osgi.framework,
net.java.sip.communicator.util,
- net.java.sip.communicator.util.swing,
+ net.java.sip.communicator.plugin.desktoputil,
net.java.sip.communicator.service.gui,
net.java.sip.communicator.service.protocol,
org.jitsi.service.resources, net.java.sip.communicator.service.resources,
diff --git a/src/net/java/sip/communicator/plugin/googletalkaccregwizz/googletalkaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/googletalkaccregwizz/googletalkaccregwizz.manifest.mf index 248077b..1d0fcb2 100644 --- a/src/net/java/sip/communicator/plugin/googletalkaccregwizz/googletalkaccregwizz.manifest.mf +++ b/src/net/java/sip/communicator/plugin/googletalkaccregwizz/googletalkaccregwizz.manifest.mf @@ -17,7 +17,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.icqconstants,
org.jitsi.service.resources, net.java.sip.communicator.service.resources,
net.java.sip.communicator.util,
- net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, net.java.sip.communicator.plugin.jabberaccregwizz,
javax.naming,
javax.naming.directory,
diff --git a/src/net/java/sip/communicator/plugin/icqaccregwizz/FirstWizardPage.java b/src/net/java/sip/communicator/plugin/icqaccregwizz/FirstWizardPage.java index 23518be..351b438 100644 --- a/src/net/java/sip/communicator/plugin/icqaccregwizz/FirstWizardPage.java +++ b/src/net/java/sip/communicator/plugin/icqaccregwizz/FirstWizardPage.java @@ -11,9 +11,9 @@ import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; /** * The <tt>FirstWizardPage</tt> is the page, where user could enter the uin diff --git a/src/net/java/sip/communicator/plugin/icqaccregwizz/icqaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/icqaccregwizz/icqaccregwizz.manifest.mf index d22b94b..1a85969 100644 --- a/src/net/java/sip/communicator/plugin/icqaccregwizz/icqaccregwizz.manifest.mf +++ b/src/net/java/sip/communicator/plugin/icqaccregwizz/icqaccregwizz.manifest.mf @@ -16,7 +16,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.icqconstants, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.event, javax.swing.table, diff --git a/src/net/java/sip/communicator/plugin/ippiaccregwizz/CreateIppiAccountForm.java b/src/net/java/sip/communicator/plugin/ippiaccregwizz/CreateIppiAccountForm.java index cb454d1..45de648 100644 --- a/src/net/java/sip/communicator/plugin/ippiaccregwizz/CreateIppiAccountForm.java +++ b/src/net/java/sip/communicator/plugin/ippiaccregwizz/CreateIppiAccountForm.java @@ -16,8 +16,8 @@ import javax.swing.*; import javax.swing.text.*; import net.java.sip.communicator.plugin.sipaccregwizz.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; import org.json.simple.*; diff --git a/src/net/java/sip/communicator/plugin/ippiaccregwizz/ippiaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/ippiaccregwizz/ippiaccregwizz.manifest.mf index 941c07e..76acb12 100644 --- a/src/net/java/sip/communicator/plugin/ippiaccregwizz/ippiaccregwizz.manifest.mf +++ b/src/net/java/sip/communicator/plugin/ippiaccregwizz/ippiaccregwizz.manifest.mf @@ -17,7 +17,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.icqconstants, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, net.java.sip.communicator.plugin.sipaccregwizz, javax.naming, javax.naming.directory, diff --git a/src/net/java/sip/communicator/plugin/iptelaccregwizz/iptelaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/iptelaccregwizz/iptelaccregwizz.manifest.mf index 03df9d7..e440dc1 100644 --- a/src/net/java/sip/communicator/plugin/iptelaccregwizz/iptelaccregwizz.manifest.mf +++ b/src/net/java/sip/communicator/plugin/iptelaccregwizz/iptelaccregwizz.manifest.mf @@ -17,7 +17,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.icqconstants,
org.jitsi.service.resources, net.java.sip.communicator.service.resources,
net.java.sip.communicator.util,
- net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, net.java.sip.communicator.plugin.sipaccregwizz,
javax.naming,
javax.naming.directory,
diff --git a/src/net/java/sip/communicator/plugin/ircaccregwizz/FirstWizardPage.java b/src/net/java/sip/communicator/plugin/ircaccregwizz/FirstWizardPage.java index 0d166ad..b26b3e2 100644 --- a/src/net/java/sip/communicator/plugin/ircaccregwizz/FirstWizardPage.java +++ b/src/net/java/sip/communicator/plugin/ircaccregwizz/FirstWizardPage.java @@ -13,9 +13,9 @@ import java.util.*; import javax.swing.*; import javax.swing.event.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; /** * The <tt>FirstWizardPage</tt> is the page, where user could enter the user ID diff --git a/src/net/java/sip/communicator/plugin/ircaccregwizz/ircaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/ircaccregwizz/ircaccregwizz.manifest.mf index 5731d33..e773cc6 100644 --- a/src/net/java/sip/communicator/plugin/ircaccregwizz/ircaccregwizz.manifest.mf +++ b/src/net/java/sip/communicator/plugin/ircaccregwizz/ircaccregwizz.manifest.mf @@ -13,7 +13,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.event, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.event, javax.swing.table, diff --git a/src/net/java/sip/communicator/plugin/jabberaccregwizz/AccountPanel.java b/src/net/java/sip/communicator/plugin/jabberaccregwizz/AccountPanel.java index 167b0d5..fb58b5f 100644 --- a/src/net/java/sip/communicator/plugin/jabberaccregwizz/AccountPanel.java +++ b/src/net/java/sip/communicator/plugin/jabberaccregwizz/AccountPanel.java @@ -12,8 +12,8 @@ import javax.swing.*; import javax.swing.event.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.protocol.*; /** * @author Yana Stamcheva diff --git a/src/net/java/sip/communicator/plugin/jabberaccregwizz/ConnectionPanel.java b/src/net/java/sip/communicator/plugin/jabberaccregwizz/ConnectionPanel.java index 768a69d..099d34c 100644 --- a/src/net/java/sip/communicator/plugin/jabberaccregwizz/ConnectionPanel.java +++ b/src/net/java/sip/communicator/plugin/jabberaccregwizz/ConnectionPanel.java @@ -12,7 +12,8 @@ import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; + import org.jitsi.util.*; /** diff --git a/src/net/java/sip/communicator/plugin/jabberaccregwizz/FirstWizardPage.java b/src/net/java/sip/communicator/plugin/jabberaccregwizz/FirstWizardPage.java index 60e2585..ce1b0c2 100644 --- a/src/net/java/sip/communicator/plugin/jabberaccregwizz/FirstWizardPage.java +++ b/src/net/java/sip/communicator/plugin/jabberaccregwizz/FirstWizardPage.java @@ -8,9 +8,9 @@ package net.java.sip.communicator.plugin.jabberaccregwizz; import java.awt.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; /** * The <tt>FirstWizardPage</tt> is the page, where user could enter the user diff --git a/src/net/java/sip/communicator/plugin/jabberaccregwizz/IceConfigPanel.java b/src/net/java/sip/communicator/plugin/jabberaccregwizz/IceConfigPanel.java index 3fd772c..5c523fc 100644 --- a/src/net/java/sip/communicator/plugin/jabberaccregwizz/IceConfigPanel.java +++ b/src/net/java/sip/communicator/plugin/jabberaccregwizz/IceConfigPanel.java @@ -14,9 +14,9 @@ import java.util.List; import javax.swing.*; import javax.swing.table.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; import org.jitsi.util.*; diff --git a/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountCreationForm.java b/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountCreationForm.java index 6eaf942..db934ac 100644 --- a/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountCreationForm.java +++ b/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountCreationForm.java @@ -12,8 +12,8 @@ import java.awt.event.*; import javax.swing.*; import javax.swing.text.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; import org.jivesoftware.smack.*; diff --git a/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountRegistration.java b/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountRegistration.java index f7f3501..9a23c34 100755 --- a/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountRegistration.java +++ b/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountRegistration.java @@ -8,7 +8,7 @@ package net.java.sip.communicator.plugin.jabberaccregwizz; import java.util.*; -import net.java.sip.communicator.util.plugin.wizard.*; +import net.java.sip.communicator.plugin.desktoputil.wizard.*; import net.java.sip.communicator.service.protocol.*; /** diff --git a/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountRegistrationForm.java b/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountRegistrationForm.java index 83fd431..48e4dbc 100644 --- a/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountRegistrationForm.java +++ b/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountRegistrationForm.java @@ -11,10 +11,10 @@ import java.util.List; import javax.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; +import net.java.sip.communicator.plugin.desktoputil.wizard.*; import net.java.sip.communicator.service.credentialsstorage.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; -import net.java.sip.communicator.util.plugin.wizard.*; /** * The <tt>JabberAccountRegistrationForm</tt>. diff --git a/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberServerChooserDialog.java b/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberServerChooserDialog.java index 079fc76..bd5c42d 100644 --- a/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberServerChooserDialog.java +++ b/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberServerChooserDialog.java @@ -19,8 +19,8 @@ import javax.swing.event.*; import javax.swing.table.*; import javax.xml.parsers.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; import org.jitsi.service.fileaccess.*; import org.osgi.framework.*; diff --git a/src/net/java/sip/communicator/plugin/jabberaccregwizz/TelephonyConfigPanel.java b/src/net/java/sip/communicator/plugin/jabberaccregwizz/TelephonyConfigPanel.java index a6a61ac..c7b5ad6 100644 --- a/src/net/java/sip/communicator/plugin/jabberaccregwizz/TelephonyConfigPanel.java +++ b/src/net/java/sip/communicator/plugin/jabberaccregwizz/TelephonyConfigPanel.java @@ -10,7 +10,7 @@ import java.awt.*; import javax.swing.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * Telephony related configuration panel. diff --git a/src/net/java/sip/communicator/plugin/jabberaccregwizz/jabberaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/jabberaccregwizz/jabberaccregwizz.manifest.mf index 0ff79a1..ccee2f4 100755 --- a/src/net/java/sip/communicator/plugin/jabberaccregwizz/jabberaccregwizz.manifest.mf +++ b/src/net/java/sip/communicator/plugin/jabberaccregwizz/jabberaccregwizz.manifest.mf @@ -6,7 +6,6 @@ Bundle-Version: 0.0.1 System-Bundle: yes Export-package: net.java.sip.communicator.plugin.jabberaccregwizz Import-Package: org.osgi.framework, - net.java.sip.communicator.util.plugin.wizard, net.java.sip.communicator.service.browserlauncher, org.jitsi.service.configuration, net.java.sip.communicator.service.credentialsstorage, @@ -22,7 +21,8 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.resources, org.jitsi.util, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, + net.java.sip.communicator.plugin.desktoputil.wizard, javax.naming, javax.naming.directory, javax.xml.parsers, diff --git a/src/net/java/sip/communicator/plugin/keybindingchooser/KeybindingsConfigPanel.java b/src/net/java/sip/communicator/plugin/keybindingchooser/KeybindingsConfigPanel.java index bca0d61..1e6daab 100644 --- a/src/net/java/sip/communicator/plugin/keybindingchooser/KeybindingsConfigPanel.java +++ b/src/net/java/sip/communicator/plugin/keybindingchooser/KeybindingsConfigPanel.java @@ -14,8 +14,8 @@ import javax.swing.*; import net.java.sip.communicator.plugin.keybindingchooser.chooser.*; import net.java.sip.communicator.plugin.keybindingchooser.globalchooser.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.keybindings.*; -import net.java.sip.communicator.util.swing.*; import org.osgi.framework.*; diff --git a/src/net/java/sip/communicator/plugin/keybindingchooser/chooser/BindingChooser.java b/src/net/java/sip/communicator/plugin/keybindingchooser/chooser/BindingChooser.java index 96f5243..39b2fe4 100644 --- a/src/net/java/sip/communicator/plugin/keybindingchooser/chooser/BindingChooser.java +++ b/src/net/java/sip/communicator/plugin/keybindingchooser/chooser/BindingChooser.java @@ -12,8 +12,8 @@ import java.util.*; import javax.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.keybindings.*; -import net.java.sip.communicator.util.swing.*; /** * Implementation of the BindingPanel that provides configuring functionality diff --git a/src/net/java/sip/communicator/plugin/keybindingchooser/chooser/BindingEntry.java b/src/net/java/sip/communicator/plugin/keybindingchooser/chooser/BindingEntry.java index 00abfab..c474223 100644 --- a/src/net/java/sip/communicator/plugin/keybindingchooser/chooser/BindingEntry.java +++ b/src/net/java/sip/communicator/plugin/keybindingchooser/chooser/BindingEntry.java @@ -11,7 +11,7 @@ import java.awt.event.*; import javax.swing.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * Display element for a single key binding. diff --git a/src/net/java/sip/communicator/plugin/keybindingchooser/chooser/BindingPanel.java b/src/net/java/sip/communicator/plugin/keybindingchooser/chooser/BindingPanel.java index a90da52..4f4ee0c 100644 --- a/src/net/java/sip/communicator/plugin/keybindingchooser/chooser/BindingPanel.java +++ b/src/net/java/sip/communicator/plugin/keybindingchooser/chooser/BindingPanel.java @@ -13,7 +13,7 @@ import java.util.*; import javax.swing.*; import javax.swing.event.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * Panel containing a listing of current keybinding mappings. This contains diff --git a/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutConfigForm.java b/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutConfigForm.java index d916f5a..e5b7e9a 100644 --- a/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutConfigForm.java +++ b/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutConfigForm.java @@ -15,10 +15,10 @@ import javax.swing.*; import javax.swing.event.*; import net.java.sip.communicator.plugin.keybindingchooser.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.globalshortcut.*; import net.java.sip.communicator.service.keybindings.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; /** * This ConfigurationForm shows the list of global shortcut diff --git a/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutDialog.java b/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutDialog.java index 68f9ef5..c3a46f9 100644 --- a/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutDialog.java +++ b/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutDialog.java @@ -14,10 +14,10 @@ import java.util.List; import javax.swing.*;
import net.java.sip.communicator.plugin.keybindingchooser.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
+import net.java.sip.communicator.plugin.desktoputil.plaf.*;
import net.java.sip.communicator.service.globalshortcut.*;
import net.java.sip.communicator.util.skin.*;
-import net.java.sip.communicator.util.swing.*;
-import net.java.sip.communicator.util.swing.plaf.*;
import org.jitsi.util.*;
// disambiguation
diff --git a/src/net/java/sip/communicator/plugin/keybindingchooser/keybindingChooser.manifest.mf b/src/net/java/sip/communicator/plugin/keybindingchooser/keybindingChooser.manifest.mf index 780e860..1ff1759 100644 --- a/src/net/java/sip/communicator/plugin/keybindingchooser/keybindingChooser.manifest.mf +++ b/src/net/java/sip/communicator/plugin/keybindingchooser/keybindingChooser.manifest.mf @@ -12,8 +12,8 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.globalshortcut, org.jitsi.util, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, - net.java.sip.communicator.util.swing.plaf, + net.java.sip.communicator.plugin.desktoputil, + net.java.sip.communicator.plugin.desktoputil.plaf, net.java.sip.communicator.util.skin, javax.swing, javax.swing.plaf, diff --git a/src/net/java/sip/communicator/plugin/ldap/configform/DirectorySettingsForm.java b/src/net/java/sip/communicator/plugin/ldap/configform/DirectorySettingsForm.java index 78aa393..6e9414b 100644 --- a/src/net/java/sip/communicator/plugin/ldap/configform/DirectorySettingsForm.java +++ b/src/net/java/sip/communicator/plugin/ldap/configform/DirectorySettingsForm.java @@ -18,7 +18,7 @@ import net.java.sip.communicator.service.ldap.*; import net.java.sip.communicator.service.ldap.LdapConstants.Auth; import net.java.sip.communicator.service.ldap.LdapConstants.Encryption; import net.java.sip.communicator.service.ldap.LdapConstants.Scope; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; //disambiguation /** diff --git a/src/net/java/sip/communicator/plugin/ldap/configform/LdapConfigForm.java b/src/net/java/sip/communicator/plugin/ldap/configform/LdapConfigForm.java index adece93..84c46de 100644 --- a/src/net/java/sip/communicator/plugin/ldap/configform/LdapConfigForm.java +++ b/src/net/java/sip/communicator/plugin/ldap/configform/LdapConfigForm.java @@ -16,7 +16,7 @@ import net.java.sip.communicator.plugin.ldap.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.ldap.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * This ConfigurationForm shows the list of LDAP directories diff --git a/src/net/java/sip/communicator/plugin/ldap/ldap.manifest.mf b/src/net/java/sip/communicator/plugin/ldap/ldap.manifest.mf index a418501..d45fa03 100644 --- a/src/net/java/sip/communicator/plugin/ldap/ldap.manifest.mf +++ b/src/net/java/sip/communicator/plugin/ldap/ldap.manifest.mf @@ -10,7 +10,7 @@ Import-Package: net.java.sip.communicator.service.contactsource, net.java.sip.communicator.service.ldap, net.java.sip.communicator.service.ldap.event, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.border, javax.swing.event, diff --git a/src/net/java/sip/communicator/plugin/loggingutils/LoggingConfigForm.java b/src/net/java/sip/communicator/plugin/loggingutils/LoggingConfigForm.java index 1c1d162..39c9496 100644 --- a/src/net/java/sip/communicator/plugin/loggingutils/LoggingConfigForm.java +++ b/src/net/java/sip/communicator/plugin/loggingutils/LoggingConfigForm.java @@ -16,7 +16,7 @@ import javax.swing.event.*; import net.java.sip.communicator.service.httputil.*; import net.java.sip.communicator.service.notification.*; import net.java.sip.communicator.util.Logger; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.jitsi.service.packetlogging.*; import org.jitsi.service.resources.*; diff --git a/src/net/java/sip/communicator/plugin/loggingutils/loggingutils.manifest.mf b/src/net/java/sip/communicator/plugin/loggingutils/loggingutils.manifest.mf index b4d2ecc..8d57c13 100644 --- a/src/net/java/sip/communicator/plugin/loggingutils/loggingutils.manifest.mf +++ b/src/net/java/sip/communicator/plugin/loggingutils/loggingutils.manifest.mf @@ -15,7 +15,7 @@ Import-Package: org.osgi.framework, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.net.ssl, javax.swing, javax.swing.border, diff --git a/src/net/java/sip/communicator/plugin/msnaccregwizz/FirstWizardPage.java b/src/net/java/sip/communicator/plugin/msnaccregwizz/FirstWizardPage.java index 071d40b..8d35c4b 100644 --- a/src/net/java/sip/communicator/plugin/msnaccregwizz/FirstWizardPage.java +++ b/src/net/java/sip/communicator/plugin/msnaccregwizz/FirstWizardPage.java @@ -12,7 +12,7 @@ import javax.swing.event.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * The <tt>FirstWizardPage</tt> is the page, where user could enter the uin diff --git a/src/net/java/sip/communicator/plugin/msnaccregwizz/msnaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/msnaccregwizz/msnaccregwizz.manifest.mf index c18c354..7f6f8b8 100755 --- a/src/net/java/sip/communicator/plugin/msnaccregwizz/msnaccregwizz.manifest.mf +++ b/src/net/java/sip/communicator/plugin/msnaccregwizz/msnaccregwizz.manifest.mf @@ -16,7 +16,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.icqconstants, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.event, javax.swing.table, diff --git a/src/net/java/sip/communicator/plugin/notificationconfiguration/NotificationConfigurationPanel.java b/src/net/java/sip/communicator/plugin/notificationconfiguration/NotificationConfigurationPanel.java index 3de1e8e..4c962c5 100644 --- a/src/net/java/sip/communicator/plugin/notificationconfiguration/NotificationConfigurationPanel.java +++ b/src/net/java/sip/communicator/plugin/notificationconfiguration/NotificationConfigurationPanel.java @@ -16,7 +16,7 @@ import javax.swing.event.*; import net.java.sip.communicator.service.notification.*; import net.java.sip.communicator.util.Logger; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.jitsi.service.audionotifier.*; import org.jitsi.util.*; diff --git a/src/net/java/sip/communicator/plugin/notificationconfiguration/SoundFilter.java b/src/net/java/sip/communicator/plugin/notificationconfiguration/SoundFilter.java index 9604b1b..694269c 100644 --- a/src/net/java/sip/communicator/plugin/notificationconfiguration/SoundFilter.java +++ b/src/net/java/sip/communicator/plugin/notificationconfiguration/SoundFilter.java @@ -9,7 +9,7 @@ package net.java.sip.communicator.plugin.notificationconfiguration; import java.io.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.jitsi.util.*; diff --git a/src/net/java/sip/communicator/plugin/notificationconfiguration/notificationconfiguration.manifest.mf b/src/net/java/sip/communicator/plugin/notificationconfiguration/notificationconfiguration.manifest.mf index 07ff221..62bc3e6 100644 --- a/src/net/java/sip/communicator/plugin/notificationconfiguration/notificationconfiguration.manifest.mf +++ b/src/net/java/sip/communicator/plugin/notificationconfiguration/notificationconfiguration.manifest.mf @@ -25,7 +25,7 @@ Import-Package: net.java.sip.communicator.service.notification.event, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, org.jitsi.service.audionotifier, org.jitsi.service.configuration, org.jitsi.service.resources, diff --git a/src/net/java/sip/communicator/plugin/notificationwiring/NotificationManager.java b/src/net/java/sip/communicator/plugin/notificationwiring/NotificationManager.java index 42deed6..3a69df5 100644 --- a/src/net/java/sip/communicator/plugin/notificationwiring/NotificationManager.java +++ b/src/net/java/sip/communicator/plugin/notificationwiring/NotificationManager.java @@ -14,6 +14,7 @@ import java.util.concurrent.*; import javax.imageio.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.contactlist.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.notification.*; diff --git a/src/net/java/sip/communicator/plugin/otr/KnownFingerprintsPanel.java b/src/net/java/sip/communicator/plugin/otr/KnownFingerprintsPanel.java index 3a964c0..8218838 100644 --- a/src/net/java/sip/communicator/plugin/otr/KnownFingerprintsPanel.java +++ b/src/net/java/sip/communicator/plugin/otr/KnownFingerprintsPanel.java @@ -14,7 +14,7 @@ import javax.swing.border.*; import javax.swing.event.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * @author @George Politis diff --git a/src/net/java/sip/communicator/plugin/otr/OtrBuddyAuthenticationDialog.java b/src/net/java/sip/communicator/plugin/otr/OtrBuddyAuthenticationDialog.java index c7e2076..6083970 100644 --- a/src/net/java/sip/communicator/plugin/otr/OtrBuddyAuthenticationDialog.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrBuddyAuthenticationDialog.java @@ -13,7 +13,7 @@ import javax.swing.*; import javax.swing.event.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * @author George Politis diff --git a/src/net/java/sip/communicator/plugin/otr/OtrConfigurationPanel.java b/src/net/java/sip/communicator/plugin/otr/OtrConfigurationPanel.java index ecf02e7..a67e940 100644 --- a/src/net/java/sip/communicator/plugin/otr/OtrConfigurationPanel.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrConfigurationPanel.java @@ -15,7 +15,7 @@ import javax.swing.border.*; import net.java.otr4j.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * A special {@link Panel} that manages the OTR configuration. diff --git a/src/net/java/sip/communicator/plugin/otr/OtrContactMenu.java b/src/net/java/sip/communicator/plugin/otr/OtrContactMenu.java index df3062a..6f1a4cd 100644 --- a/src/net/java/sip/communicator/plugin/otr/OtrContactMenu.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrContactMenu.java @@ -14,7 +14,7 @@ import javax.swing.*; import net.java.otr4j.*; import net.java.otr4j.session.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * A special {@link JMenu} that holds the menu items for controlling the diff --git a/src/net/java/sip/communicator/plugin/otr/OtrMetaContactButton.java b/src/net/java/sip/communicator/plugin/otr/OtrMetaContactButton.java index c7952ba..5cf4a17 100644 --- a/src/net/java/sip/communicator/plugin/otr/OtrMetaContactButton.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrMetaContactButton.java @@ -18,7 +18,7 @@ import net.java.sip.communicator.service.contactlist.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.gui.Container; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * A {@link AbstractPluginComponent} that registers the Off-the-Record button in diff --git a/src/net/java/sip/communicator/plugin/otr/OtrMetaContactMenu.java b/src/net/java/sip/communicator/plugin/otr/OtrMetaContactMenu.java index c130e7c..a08ffaa 100644 --- a/src/net/java/sip/communicator/plugin/otr/OtrMetaContactMenu.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrMetaContactMenu.java @@ -17,7 +17,7 @@ import net.java.sip.communicator.service.contactlist.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.gui.Container; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * @author George Politis diff --git a/src/net/java/sip/communicator/plugin/otr/otr.manifest.mf b/src/net/java/sip/communicator/plugin/otr/otr.manifest.mf index c8296e5..e9c0d0c 100644 --- a/src/net/java/sip/communicator/plugin/otr/otr.manifest.mf +++ b/src/net/java/sip/communicator/plugin/otr/otr.manifest.mf @@ -13,7 +13,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.event,
org.jitsi.service.resources, net.java.sip.communicator.service.resources,
net.java.sip.communicator.util,
- net.java.sip.communicator.util.swing,
+ net.java.sip.communicator.plugin.desktoputil,
javax.imageio,
javax.swing,
javax.swing.border,
diff --git a/src/net/java/sip/communicator/plugin/pluginmanager/ManageButtonsPanel.java b/src/net/java/sip/communicator/plugin/pluginmanager/ManageButtonsPanel.java index fb1a2ec..20a57bf 100644 --- a/src/net/java/sip/communicator/plugin/pluginmanager/ManageButtonsPanel.java +++ b/src/net/java/sip/communicator/plugin/pluginmanager/ManageButtonsPanel.java @@ -13,7 +13,7 @@ import javax.swing.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.osgi.framework.*; diff --git a/src/net/java/sip/communicator/plugin/pluginmanager/NewBundleDialog.java b/src/net/java/sip/communicator/plugin/pluginmanager/NewBundleDialog.java index e96c601..5c64110 100644 --- a/src/net/java/sip/communicator/plugin/pluginmanager/NewBundleDialog.java +++ b/src/net/java/sip/communicator/plugin/pluginmanager/NewBundleDialog.java @@ -15,7 +15,7 @@ import javax.swing.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.osgi.framework.*; diff --git a/src/net/java/sip/communicator/plugin/pluginmanager/PluginListCellRenderer.java b/src/net/java/sip/communicator/plugin/pluginmanager/PluginListCellRenderer.java index 3bb5de3..86d625a 100644 --- a/src/net/java/sip/communicator/plugin/pluginmanager/PluginListCellRenderer.java +++ b/src/net/java/sip/communicator/plugin/pluginmanager/PluginListCellRenderer.java @@ -12,7 +12,7 @@ import java.util.*; import javax.swing.*; import javax.swing.table.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.osgi.framework.*; diff --git a/src/net/java/sip/communicator/plugin/pluginmanager/PluginManagerPanel.java b/src/net/java/sip/communicator/plugin/pluginmanager/PluginManagerPanel.java index 817c3d7..bc59d42 100644 --- a/src/net/java/sip/communicator/plugin/pluginmanager/PluginManagerPanel.java +++ b/src/net/java/sip/communicator/plugin/pluginmanager/PluginManagerPanel.java @@ -12,7 +12,7 @@ import javax.swing.*; import javax.swing.event.*; import javax.swing.table.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.osgi.framework.*; diff --git a/src/net/java/sip/communicator/plugin/pluginmanager/pluginmanager.manifest.mf b/src/net/java/sip/communicator/plugin/pluginmanager/pluginmanager.manifest.mf index 2b2d31b..0aa72b4 100644 --- a/src/net/java/sip/communicator/plugin/pluginmanager/pluginmanager.manifest.mf +++ b/src/net/java/sip/communicator/plugin/pluginmanager/pluginmanager.manifest.mf @@ -10,7 +10,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.gui.event, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.event, javax.swing.table, diff --git a/src/net/java/sip/communicator/plugin/provisioning/ProvisioningForm.java b/src/net/java/sip/communicator/plugin/provisioning/ProvisioningForm.java index f42b6c2..b10184a 100644 --- a/src/net/java/sip/communicator/plugin/provisioning/ProvisioningForm.java +++ b/src/net/java/sip/communicator/plugin/provisioning/ProvisioningForm.java @@ -14,7 +14,7 @@ import javax.swing.*; import javax.swing.event.*; import net.java.sip.communicator.service.gui.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.jitsi.service.configuration.*; import org.jitsi.service.resources.*; diff --git a/src/net/java/sip/communicator/plugin/provisioning/ProvisioningServiceImpl.java b/src/net/java/sip/communicator/plugin/provisioning/ProvisioningServiceImpl.java index a3c26ab..d5c3f53 100644 --- a/src/net/java/sip/communicator/plugin/provisioning/ProvisioningServiceImpl.java +++ b/src/net/java/sip/communicator/plugin/provisioning/ProvisioningServiceImpl.java @@ -13,7 +13,7 @@ import net.java.sip.communicator.service.httputil.*; import net.java.sip.communicator.service.provisioning.*; import net.java.sip.communicator.util.*; import net.java.sip.communicator.util.Logger; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.jitsi.service.configuration.*; import org.jitsi.util.*; diff --git a/src/net/java/sip/communicator/plugin/provisioning/provisioning.manifest.mf b/src/net/java/sip/communicator/plugin/provisioning/provisioning.manifest.mf index bb0cfce..afa6e85 100644 --- a/src/net/java/sip/communicator/plugin/provisioning/provisioning.manifest.mf +++ b/src/net/java/sip/communicator/plugin/provisioning/provisioning.manifest.mf @@ -18,7 +18,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.provisioning, org.jitsi.util, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.event, javax.swing.table, diff --git a/src/net/java/sip/communicator/plugin/reconnectplugin/reconnectplugin.manifest.mf b/src/net/java/sip/communicator/plugin/reconnectplugin/reconnectplugin.manifest.mf index 9163cb2..d68432a 100644 --- a/src/net/java/sip/communicator/plugin/reconnectplugin/reconnectplugin.manifest.mf +++ b/src/net/java/sip/communicator/plugin/reconnectplugin/reconnectplugin.manifest.mf @@ -14,4 +14,4 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.event, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing + net.java.sip.communicator.plugin.desktoputil diff --git a/src/net/java/sip/communicator/plugin/rssaccregwizz/FirstWizardPage.java b/src/net/java/sip/communicator/plugin/rssaccregwizz/FirstWizardPage.java index 7737cf2..bb30aa8 100644 --- a/src/net/java/sip/communicator/plugin/rssaccregwizz/FirstWizardPage.java +++ b/src/net/java/sip/communicator/plugin/rssaccregwizz/FirstWizardPage.java @@ -14,7 +14,7 @@ import javax.swing.event.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * The <tt>FirstWizardPage</tt> is the page, where user could enter the user ID diff --git a/src/net/java/sip/communicator/plugin/rssaccregwizz/rssaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/rssaccregwizz/rssaccregwizz.manifest.mf index 81e2df7..9acedd6 100644 --- a/src/net/java/sip/communicator/plugin/rssaccregwizz/rssaccregwizz.manifest.mf +++ b/src/net/java/sip/communicator/plugin/rssaccregwizz/rssaccregwizz.manifest.mf @@ -15,7 +15,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.event, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.event, javax.swing.table, diff --git a/src/net/java/sip/communicator/plugin/securityconfig/SecurityConfigurationPanel.java b/src/net/java/sip/communicator/plugin/securityconfig/SecurityConfigurationPanel.java index c60d871..e5ecd67 100644 --- a/src/net/java/sip/communicator/plugin/securityconfig/SecurityConfigurationPanel.java +++ b/src/net/java/sip/communicator/plugin/securityconfig/SecurityConfigurationPanel.java @@ -8,7 +8,7 @@ package net.java.sip.communicator.plugin.securityconfig; import java.awt.*; import net.java.sip.communicator.service.gui.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.osgi.framework.*; diff --git a/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/ConfigurationPanel.java b/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/ConfigurationPanel.java index 698eab5..2de4ba0 100644 --- a/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/ConfigurationPanel.java +++ b/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/ConfigurationPanel.java @@ -8,7 +8,7 @@ package net.java.sip.communicator.plugin.securityconfig.masterpassword; import javax.swing.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * Implements a Swing <tt>Component</tt> to represent the user interface of the diff --git a/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/MasterPasswordChangeDialog.java b/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/MasterPasswordChangeDialog.java index 2ec5be7..b6fe8b1 100644 --- a/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/MasterPasswordChangeDialog.java +++ b/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/MasterPasswordChangeDialog.java @@ -10,7 +10,7 @@ import java.awt.event.*; import net.java.sip.communicator.plugin.securityconfig.*; import net.java.sip.communicator.service.credentialsstorage.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * UI dialog to change the master password. diff --git a/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/MasterPasswordPanel.java b/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/MasterPasswordPanel.java index 7e85595..d5de308 100644 --- a/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/MasterPasswordPanel.java +++ b/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/MasterPasswordPanel.java @@ -15,7 +15,7 @@ import net.java.sip.communicator.plugin.securityconfig.*; import net.java.sip.communicator.plugin.securityconfig.masterpassword.MasterPasswordChangeDialog.MasterPasswordExecutable; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.jitsi.service.resources.*; diff --git a/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/SavedPasswordsDialog.java b/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/SavedPasswordsDialog.java index 79ecdc5..10c567d 100644 --- a/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/SavedPasswordsDialog.java +++ b/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/SavedPasswordsDialog.java @@ -20,7 +20,7 @@ import net.java.sip.communicator.plugin.securityconfig.*; import net.java.sip.communicator.service.credentialsstorage.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.jitsi.service.configuration.*; import org.jitsi.service.resources.*; diff --git a/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/SavedPasswordsPanel.java b/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/SavedPasswordsPanel.java index 712f771..ef0eee9 100644 --- a/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/SavedPasswordsPanel.java +++ b/src/net/java/sip/communicator/plugin/securityconfig/masterpassword/SavedPasswordsPanel.java @@ -13,7 +13,7 @@ import javax.swing.*; import net.java.sip.communicator.plugin.securityconfig.*; import net.java.sip.communicator.service.credentialsstorage.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * Panel containing the saved passwords button. diff --git a/src/net/java/sip/communicator/plugin/securityconfig/securityconfig.manifest.mf b/src/net/java/sip/communicator/plugin/securityconfig/securityconfig.manifest.mf index 261175b..784e47a 100644 --- a/src/net/java/sip/communicator/plugin/securityconfig/securityconfig.manifest.mf +++ b/src/net/java/sip/communicator/plugin/securityconfig/securityconfig.manifest.mf @@ -13,7 +13,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.event,
org.jitsi.service.resources, net.java.sip.communicator.service.resources,
net.java.sip.communicator.util,
- net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, gnu.java.zrtp, javax.crypto, javax.crypto.interfaces, diff --git a/src/net/java/sip/communicator/plugin/simpleaccreg/InitialAccountRegistrationFrame.java b/src/net/java/sip/communicator/plugin/simpleaccreg/InitialAccountRegistrationFrame.java index e83614f..ab68a11 100644 --- a/src/net/java/sip/communicator/plugin/simpleaccreg/InitialAccountRegistrationFrame.java +++ b/src/net/java/sip/communicator/plugin/simpleaccreg/InitialAccountRegistrationFrame.java @@ -19,7 +19,7 @@ import net.java.sip.communicator.service.contactlist.*; import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
-import net.java.sip.communicator.util.swing.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
import org.jitsi.service.configuration.*;
import org.osgi.framework.*;
diff --git a/src/net/java/sip/communicator/plugin/simpleaccreg/simpleaccreg.manifest.mf b/src/net/java/sip/communicator/plugin/simpleaccreg/simpleaccreg.manifest.mf index 791a586..669759e 100644 --- a/src/net/java/sip/communicator/plugin/simpleaccreg/simpleaccreg.manifest.mf +++ b/src/net/java/sip/communicator/plugin/simpleaccreg/simpleaccreg.manifest.mf @@ -15,7 +15,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.event, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.event, javax.swing.table, diff --git a/src/net/java/sip/communicator/plugin/sip2sipaccregwizz/CreateSip2SipAccountForm.java b/src/net/java/sip/communicator/plugin/sip2sipaccregwizz/CreateSip2SipAccountForm.java index 5533fb2..ccf5c9d 100644 --- a/src/net/java/sip/communicator/plugin/sip2sipaccregwizz/CreateSip2SipAccountForm.java +++ b/src/net/java/sip/communicator/plugin/sip2sipaccregwizz/CreateSip2SipAccountForm.java @@ -17,7 +17,7 @@ import javax.swing.text.*; import net.java.sip.communicator.plugin.ippiaccregwizz.*; import net.java.sip.communicator.plugin.sipaccregwizz.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.json.simple.*; diff --git a/src/net/java/sip/communicator/plugin/sip2sipaccregwizz/sip2sipaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/sip2sipaccregwizz/sip2sipaccregwizz.manifest.mf index a4a0c6c..5df1e81 100644 --- a/src/net/java/sip/communicator/plugin/sip2sipaccregwizz/sip2sipaccregwizz.manifest.mf +++ b/src/net/java/sip/communicator/plugin/sip2sipaccregwizz/sip2sipaccregwizz.manifest.mf @@ -17,7 +17,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.icqconstants,
org.jitsi.service.resources, net.java.sip.communicator.service.resources,
net.java.sip.communicator.util,
- net.java.sip.communicator.util.swing,
+ net.java.sip.communicator.plugin.desktoputil,
net.java.sip.communicator.plugin.sipaccregwizz,
javax.naming,
javax.naming.directory,
diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/AccountPanel.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/AccountPanel.java index 6e10ace..1c5b2aa 100644 --- a/src/net/java/sip/communicator/plugin/sipaccregwizz/AccountPanel.java +++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/AccountPanel.java @@ -12,7 +12,7 @@ import javax.swing.*; import javax.swing.event.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * The panel containing all account related information like user name and diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/ConnectionPanel.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/ConnectionPanel.java index c3fbc71..3210db7 100644 --- a/src/net/java/sip/communicator/plugin/sipaccregwizz/ConnectionPanel.java +++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/ConnectionPanel.java @@ -12,7 +12,7 @@ import javax.swing.*; import javax.swing.event.*; import net.java.sip.communicator.service.certificate.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.jitsi.util.*; diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/FirstWizardPage.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/FirstWizardPage.java index f3342dc..51e8888 100644 --- a/src/net/java/sip/communicator/plugin/sipaccregwizz/FirstWizardPage.java +++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/FirstWizardPage.java @@ -9,7 +9,7 @@ import java.awt.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * The <tt>FirstWizardPage</tt> is the page, where user could enter the uin diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/PresencePanel.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/PresencePanel.java index 7a9e216..73365d0 100644 --- a/src/net/java/sip/communicator/plugin/sipaccregwizz/PresencePanel.java +++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/PresencePanel.java @@ -10,7 +10,7 @@ import java.awt.event.*; import javax.swing.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * The <tt>PresencePanel</tt> is the one containing presence information. diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistration.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistration.java index 25b7894..45b5a1d 100644 --- a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistration.java +++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistration.java @@ -7,8 +7,7 @@ package net.java.sip.communicator.plugin.sipaccregwizz; import java.util.*; -import java.util.*; -import net.java.sip.communicator.util.plugin.wizard.*; +import net.java.sip.communicator.plugin.desktoputil.wizard.*; /** * The <tt>SIPAccountRegistration</tt> is used to store all user input data * through the <tt>SIPAccountRegistrationWizard</tt>. diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationForm.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationForm.java index 212cbbf..21b9dba 100644 --- a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationForm.java +++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationForm.java @@ -7,8 +7,8 @@ import java.util.List; import javax.swing.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; -import net.java.sip.communicator.util.plugin.wizard.*; +import net.java.sip.communicator.plugin.desktoputil.*; +import net.java.sip.communicator.plugin.desktoputil.wizard.*; import org.jitsi.util.*; diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/sipaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/sipaccregwizz/sipaccregwizz.manifest.mf index 49a3fec..5f40115 100644 --- a/src/net/java/sip/communicator/plugin/sipaccregwizz/sipaccregwizz.manifest.mf +++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/sipaccregwizz.manifest.mf @@ -21,8 +21,8 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.resources, org.jitsi.util, net.java.sip.communicator.util, - net.java.sip.communicator.util.plugin.wizard, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, + net.java.sip.communicator.plugin.desktoputil.wizard, javax.swing, javax.swing.event, javax.swing.table, diff --git a/src/net/java/sip/communicator/plugin/skinmanager/SkinManagerPanel.java b/src/net/java/sip/communicator/plugin/skinmanager/SkinManagerPanel.java index 09faefc..1de892a 100644 --- a/src/net/java/sip/communicator/plugin/skinmanager/SkinManagerPanel.java +++ b/src/net/java/sip/communicator/plugin/skinmanager/SkinManagerPanel.java @@ -11,7 +11,7 @@ import java.awt.event.*; import javax.swing.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.osgi.framework.*; diff --git a/src/net/java/sip/communicator/plugin/skinmanager/SkinSelectionListener.java b/src/net/java/sip/communicator/plugin/skinmanager/SkinSelectionListener.java index 41cc4fb..2add580 100644 --- a/src/net/java/sip/communicator/plugin/skinmanager/SkinSelectionListener.java +++ b/src/net/java/sip/communicator/plugin/skinmanager/SkinSelectionListener.java @@ -13,7 +13,7 @@ import java.util.zip.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.osgi.framework.*; diff --git a/src/net/java/sip/communicator/plugin/skinmanager/SkinSelectorRenderer.java b/src/net/java/sip/communicator/plugin/skinmanager/SkinSelectorRenderer.java index a51cc8f..8031446 100644 --- a/src/net/java/sip/communicator/plugin/skinmanager/SkinSelectorRenderer.java +++ b/src/net/java/sip/communicator/plugin/skinmanager/SkinSelectorRenderer.java @@ -13,7 +13,7 @@ import java.util.*; import javax.swing.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; import org.osgi.framework.*; diff --git a/src/net/java/sip/communicator/plugin/skinmanager/skinmanager.manifest.mf b/src/net/java/sip/communicator/plugin/skinmanager/skinmanager.manifest.mf index 1464ff5..423dfd4 100644 --- a/src/net/java/sip/communicator/plugin/skinmanager/skinmanager.manifest.mf +++ b/src/net/java/sip/communicator/plugin/skinmanager/skinmanager.manifest.mf @@ -10,7 +10,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.gui.event, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.event, javax.swing.table, diff --git a/src/net/java/sip/communicator/plugin/spellcheck/LanguageMenuBar.java b/src/net/java/sip/communicator/plugin/spellcheck/LanguageMenuBar.java index ba9ac69..4366994 100644 --- a/src/net/java/sip/communicator/plugin/spellcheck/LanguageMenuBar.java +++ b/src/net/java/sip/communicator/plugin/spellcheck/LanguageMenuBar.java @@ -21,8 +21,8 @@ import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.gui.Container; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; -import net.java.sip.communicator.util.swing.SwingWorker; +import net.java.sip.communicator.plugin.desktoputil.*; +import net.java.sip.communicator.plugin.desktoputil.SwingWorker; /** * Combo box providing a listing of all available locales with corresponding diff --git a/src/net/java/sip/communicator/plugin/spellcheck/SpellCheckerConfigDialog.java b/src/net/java/sip/communicator/plugin/spellcheck/SpellCheckerConfigDialog.java index ac51b27..f005a15 100644 --- a/src/net/java/sip/communicator/plugin/spellcheck/SpellCheckerConfigDialog.java +++ b/src/net/java/sip/communicator/plugin/spellcheck/SpellCheckerConfigDialog.java @@ -15,7 +15,7 @@ import javax.swing.text.*; import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.util.*;
-import net.java.sip.communicator.util.swing.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
import org.dts.spell.dictionary.*;
import org.jitsi.service.resources.*;
diff --git a/src/net/java/sip/communicator/plugin/spellcheck/spellCheck.manifest.mf b/src/net/java/sip/communicator/plugin/spellcheck/spellCheck.manifest.mf index 3297747..5ef7fdc 100644 --- a/src/net/java/sip/communicator/plugin/spellcheck/spellCheck.manifest.mf +++ b/src/net/java/sip/communicator/plugin/spellcheck/spellCheck.manifest.mf @@ -10,7 +10,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.gui, net.java.sip.communicator.service.gui.event, org.jitsi.service.resources, net.java.sip.communicator.service.resources, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, net.java.sip.communicator.service.contactlist, net.java.sip.communicator.service.protocol, javax.swing, diff --git a/src/net/java/sip/communicator/plugin/sshaccregwizz/FirstWizardPage.java b/src/net/java/sip/communicator/plugin/sshaccregwizz/FirstWizardPage.java index 0f3e2a3..c04e313 100644 --- a/src/net/java/sip/communicator/plugin/sshaccregwizz/FirstWizardPage.java +++ b/src/net/java/sip/communicator/plugin/sshaccregwizz/FirstWizardPage.java @@ -22,7 +22,7 @@ import javax.swing.event.*; import net.java.sip.communicator.impl.protocol.ssh.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * The <tt>FirstWizardPage</tt> is the page, where user could enter the user ID diff --git a/src/net/java/sip/communicator/plugin/sshaccregwizz/sshaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/sshaccregwizz/sshaccregwizz.manifest.mf index d4fa337..6f8b5f9 100644 --- a/src/net/java/sip/communicator/plugin/sshaccregwizz/sshaccregwizz.manifest.mf +++ b/src/net/java/sip/communicator/plugin/sshaccregwizz/sshaccregwizz.manifest.mf @@ -15,7 +15,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.event, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.event, javax.swing.table, diff --git a/src/net/java/sip/communicator/plugin/update/Update.java b/src/net/java/sip/communicator/plugin/update/Update.java index e3b836d..0e00d69 100644 --- a/src/net/java/sip/communicator/plugin/update/Update.java +++ b/src/net/java/sip/communicator/plugin/update/Update.java @@ -21,7 +21,7 @@ import net.java.sip.communicator.service.httputil.*; import net.java.sip.communicator.service.update.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.Logger;
-import net.java.sip.communicator.util.swing.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
import org.jitsi.service.resources.*;
import org.jitsi.service.version.*;
diff --git a/src/net/java/sip/communicator/plugin/update/update.manifest.mf b/src/net/java/sip/communicator/plugin/update/update.manifest.mf index 98b6948..3de434b 100644 --- a/src/net/java/sip/communicator/plugin/update/update.manifest.mf +++ b/src/net/java/sip/communicator/plugin/update/update.manifest.mf @@ -16,7 +16,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.resources, net.java.sip.communicator.service.shutdown, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, net.java.sip.communicator.service.update, javax.imageio, javax.net.ssl, diff --git a/src/net/java/sip/communicator/plugin/whiteboard/gui/InvitationReceivedDialog.java b/src/net/java/sip/communicator/plugin/whiteboard/gui/InvitationReceivedDialog.java index 335accf..729c795 100644 --- a/src/net/java/sip/communicator/plugin/whiteboard/gui/InvitationReceivedDialog.java +++ b/src/net/java/sip/communicator/plugin/whiteboard/gui/InvitationReceivedDialog.java @@ -13,7 +13,7 @@ import javax.swing.*; import net.java.sip.communicator.plugin.whiteboard.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * The dialog that pops up when a chat room invitation is received. diff --git a/src/net/java/sip/communicator/plugin/whiteboard/gui/WhiteboardFileFilter.java b/src/net/java/sip/communicator/plugin/whiteboard/gui/WhiteboardFileFilter.java index 42431d3..1048984 100644 --- a/src/net/java/sip/communicator/plugin/whiteboard/gui/WhiteboardFileFilter.java +++ b/src/net/java/sip/communicator/plugin/whiteboard/gui/WhiteboardFileFilter.java @@ -9,7 +9,7 @@ package net.java.sip.communicator.plugin.whiteboard.gui; import java.io.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * A simple file filter manager diff --git a/src/net/java/sip/communicator/plugin/whiteboard/gui/WhiteboardFrame.java b/src/net/java/sip/communicator/plugin/whiteboard/gui/WhiteboardFrame.java index fb875e3..00e36eb 100644 --- a/src/net/java/sip/communicator/plugin/whiteboard/gui/WhiteboardFrame.java +++ b/src/net/java/sip/communicator/plugin/whiteboard/gui/WhiteboardFrame.java @@ -23,7 +23,7 @@ import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.service.protocol.whiteboardobjects.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * The frame for the Whiteboard diff --git a/src/net/java/sip/communicator/plugin/whiteboard/gui/WhiteboardPanel.java b/src/net/java/sip/communicator/plugin/whiteboard/gui/WhiteboardPanel.java index 5282b66..1d0a9ed 100644 --- a/src/net/java/sip/communicator/plugin/whiteboard/gui/WhiteboardPanel.java +++ b/src/net/java/sip/communicator/plugin/whiteboard/gui/WhiteboardPanel.java @@ -15,7 +15,7 @@ import javax.swing.*; import javax.swing.plaf.*; import net.java.sip.communicator.plugin.whiteboard.gui.whiteboardshapes.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * Panel for drawing shapes diff --git a/src/net/java/sip/communicator/plugin/whiteboard/gui/whiteboardshapes/WhiteboardShape.java b/src/net/java/sip/communicator/plugin/whiteboard/gui/whiteboardshapes/WhiteboardShape.java index c303fba..9fd5e47 100644 --- a/src/net/java/sip/communicator/plugin/whiteboard/gui/whiteboardshapes/WhiteboardShape.java +++ b/src/net/java/sip/communicator/plugin/whiteboard/gui/whiteboardshapes/WhiteboardShape.java @@ -12,7 +12,7 @@ import java.util.List; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.whiteboardobjects.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * Abstract WhiteboardShape (Shape for the WhitheboardFrame) diff --git a/src/net/java/sip/communicator/plugin/whiteboard/whiteboard.manifest.mf b/src/net/java/sip/communicator/plugin/whiteboard/whiteboard.manifest.mf index 14cd534..9f22dd5 100644 --- a/src/net/java/sip/communicator/plugin/whiteboard/whiteboard.manifest.mf +++ b/src/net/java/sip/communicator/plugin/whiteboard/whiteboard.manifest.mf @@ -14,7 +14,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.whiteboardobjects, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.event, javax.swing.table, diff --git a/src/net/java/sip/communicator/plugin/yahooaccregwizz/FirstWizardPage.java b/src/net/java/sip/communicator/plugin/yahooaccregwizz/FirstWizardPage.java index c1e5c0d..810fc4a 100644 --- a/src/net/java/sip/communicator/plugin/yahooaccregwizz/FirstWizardPage.java +++ b/src/net/java/sip/communicator/plugin/yahooaccregwizz/FirstWizardPage.java @@ -12,7 +12,7 @@ import javax.swing.event.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * The <tt>FirstWizardPage</tt> is the page, where user could enter the uin diff --git a/src/net/java/sip/communicator/plugin/yahooaccregwizz/yahooaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/yahooaccregwizz/yahooaccregwizz.manifest.mf index 1f27b2b..9a283ab 100644 --- a/src/net/java/sip/communicator/plugin/yahooaccregwizz/yahooaccregwizz.manifest.mf +++ b/src/net/java/sip/communicator/plugin/yahooaccregwizz/yahooaccregwizz.manifest.mf @@ -16,7 +16,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.icqconstants, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.event, javax.swing.table, diff --git a/src/net/java/sip/communicator/plugin/zeroconfaccregwizz/FirstWizardPage.java b/src/net/java/sip/communicator/plugin/zeroconfaccregwizz/FirstWizardPage.java index d46a2e4..ef8b1e1 100644 --- a/src/net/java/sip/communicator/plugin/zeroconfaccregwizz/FirstWizardPage.java +++ b/src/net/java/sip/communicator/plugin/zeroconfaccregwizz/FirstWizardPage.java @@ -13,7 +13,7 @@ import javax.swing.event.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * The <tt>FirstWizardPage</tt> is the page, where user could enter the user ID diff --git a/src/net/java/sip/communicator/plugin/zeroconfaccregwizz/zeroconfaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/zeroconfaccregwizz/zeroconfaccregwizz.manifest.mf index 611a2d8..3ff6dd5 100644 --- a/src/net/java/sip/communicator/plugin/zeroconfaccregwizz/zeroconfaccregwizz.manifest.mf +++ b/src/net/java/sip/communicator/plugin/zeroconfaccregwizz/zeroconfaccregwizz.manifest.mf @@ -15,7 +15,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.protocol.event, org.jitsi.service.resources, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, - net.java.sip.communicator.util.swing, + net.java.sip.communicator.plugin.desktoputil, javax.swing, javax.swing.event, javax.swing.table, |