diff options
40 files changed, 2409 insertions, 771 deletions
@@ -83,5 +83,6 @@ <classpathentry kind="lib" path="lib/installer-exclude/bcprov-jdk15on-149.jar"/> <classpathentry kind="lib" path="lib/installer-exclude/bccontrib-1.0-SNAPSHOT.jar"/> <classpathentry kind="lib" path="lib/installer-exclude/zrtp4j-light.jar"/> + <classpathentry kind="lib" path="lib/installer-exclude/jcalendar-1.4.jar"/> <classpathentry kind="output" path="classes"/> </classpath> @@ -1079,8 +1079,8 @@ bundle-globalshortcut,bundle-plugin-msofficecomm,bundle-libjitsi, bundle-customcontactactions, bundle-phonenumbercontactsource, bundle-demuxcontactsource, bundle-muc, - bundle-desktoputil, - bundle-globaldisplaydetails,bundle-plugin-propertieseditor"/> + bundle-desktoputil,bundle-globaldisplaydetails, + bundle-plugin-propertieseditor,bundle-plugin-accountinfo"/> <!--BUNDLE-SC-LAUNCHER--> <target name="bundle-sc-launcher"> @@ -2226,6 +2226,7 @@ javax.swing.event, javax.swing.border"/> manifest="${src}/net/java/sip/communicator/plugin/accountinfo/accountinfo.manifest.mf"> <zipfileset dir="${dest}/net/java/sip/communicator/plugin/accountinfo" prefix="net/java/sip/communicator/plugin/accountinfo"/> + <zipfileset src="${lib.noinst}/jcalendar-1.4.jar"/> </jar> </target> diff --git a/lib/felix.client.run.properties b/lib/felix.client.run.properties index cf8c6e4..f567442 100644 --- a/lib/felix.client.run.properties +++ b/lib/felix.client.run.properties @@ -134,6 +134,7 @@ felix.auto.start.60= \ felix.auto.start.66= \ reference:file:sc-bundles/swing-ui.jar \ reference:file:sc-bundles/update.jar \ + reference:file:sc-bundles/accountinfo.jar \ reference:file:sc-bundles/swingnotification.jar \ reference:file:sc-bundles/systray-service.jar \ reference:file:sc-bundles/osdependent.jar \ diff --git a/lib/installer-exclude/jcalendar-1.4.jar b/lib/installer-exclude/jcalendar-1.4.jar Binary files differnew file mode 100644 index 0000000..8b0bff6 --- /dev/null +++ b/lib/installer-exclude/jcalendar-1.4.jar diff --git a/resources/images/images.properties b/resources/images/images.properties index b313f79..eed832e 100644 --- a/resources/images/images.properties +++ b/resources/images/images.properties @@ -123,6 +123,7 @@ service.gui.statusicons.USER_FFC_ICON=resources/images/impl/gui/common/statusico service.gui.statusicons.USER_ON_THE_PHONE_ICON=resources/images/impl/gui/common/statusicons/onThePhone.png # service gui buttons +service.gui.buttons.ACCOUNT_EDIT_ICON=resources/images/impl/gui/buttons/accountEditIcon.png service.gui.buttons.CONTACT_LIST_BUTTON_BG_LEFT=resources/images/impl/gui/buttons/contactListButtonBgLeft.png service.gui.buttons.CONTACT_LIST_BUTTON_BG_RIGHT=resources/images/impl/gui/buttons/contactListButtonBgRight.png service.gui.buttons.CONTACT_LIST_BUTTON_BG_MIDDLE=resources/images/impl/gui/buttons/contactListButtonBgMiddle.png diff --git a/resources/images/impl/gui/buttons/accountEditIcon.png b/resources/images/impl/gui/buttons/accountEditIcon.png Binary files differnew file mode 100644 index 0000000..a13ecc1 --- /dev/null +++ b/resources/images/impl/gui/buttons/accountEditIcon.png diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties index bee961c..a410494 100644 --- a/resources/languages/resources.properties +++ b/resources/languages/resources.properties @@ -857,15 +857,33 @@ impl.googlecontacts.WRONG_CREDENTIALS=Wrong credentials for Google account {0} plugin.accountinfo.TITLE=Account Info plugin.accountinfo.EXTENDED=Extended plugin.accountinfo.NOT_SUPPORTED=Account info not available. +plugin.accountinfo.SELECT_ACCOUNT=Please select an account: +plugin.accountinfo.DISPLAY_NAME=Display name: plugin.accountinfo.FIRST_NAME=First Name: plugin.accountinfo.MIDDLE_NAME=Middle Name: plugin.accountinfo.LAST_NAME=Last Name: +plugin.accountinfo.NICKNAME=Nickname: +plugin.accountinfo.URL=URL: plugin.accountinfo.AGE=Age: plugin.accountinfo.BDAY=Birth Date: +plugin.accountinfo.BDAY_FORMAT=MMM dd, yyyy plugin.accountinfo.GENDER=Gender: +plugin.accountinfo.STREET=Street Address: +plugin.accountinfo.CITY=City: +plugin.accountinfo.REGION=Region: +plugin.accountinfo.POST=Postal code: +plugin.accountinfo.COUNTRY=Country: plugin.accountinfo.EMAIL=E-mail: +plugin.accountinfo.WORK_EMAIL=Work E-mail: plugin.accountinfo.PHONE=Phone: +plugin.accountinfo.WORK_PHONE=Work Phone: +plugin.accountinfo.ORGANIZATION=Organization Name: +plugin.accountinfo.JOB_TITLE=Job Title: +plugin.accountinfo.ABOUT_ME=About Me: +plugin.accountinfo.ABOUT_ME_MAX_CHARACTERS=200 plugin.accountinfo.USER_PICTURES=User Pictures +plugin.accountinfo.GLOBAL_ICON=Use global icon +plugin.accountinfo.LOCAL_ICON=Use this icon: plugin.accountinfo.CHANGE=Change plugin.accountinfo.ONLY_MESSAGE=Only messages diff --git a/src/net/java/sip/communicator/impl/gui/main/MainFrame.java b/src/net/java/sip/communicator/impl/gui/main/MainFrame.java index 2313cdb..a0fd2ae 100644 --- a/src/net/java/sip/communicator/impl/gui/main/MainFrame.java +++ b/src/net/java/sip/communicator/impl/gui/main/MainFrame.java @@ -186,7 +186,7 @@ public class MainFrame this.contactListPanel = new ContactListPane(this); - this.accountStatusPanel = new AccountStatusPanel(this); + this.accountStatusPanel = new AccountStatusPanel(); this.searchField = new SearchField( this, TreeContactList.searchFilter, diff --git a/src/net/java/sip/communicator/impl/gui/main/account/AccountList.java b/src/net/java/sip/communicator/impl/gui/main/account/AccountList.java index ae0a105..c2cc0ae 100644 --- a/src/net/java/sip/communicator/impl/gui/main/account/AccountList.java +++ b/src/net/java/sip/communicator/impl/gui/main/account/AccountList.java @@ -57,6 +57,12 @@ public class AccountList private final JButton editButton; /** + * The menu that appears when right click on account is detected. + */ + private final AccountRightButtonMenu rightButtonMenu + = new AccountRightButtonMenu(); + + /** * Creates an instance of this account list by specifying the parent * container of the list. * @@ -69,6 +75,29 @@ public class AccountList this.addMouseListener(this); + this.addMouseListener(new MouseAdapter() + { + public void mousePressed(MouseEvent e) + { + if (SwingUtilities.isRightMouseButton(e)) + { + Point point = e.getPoint(); + + AccountList.this.setSelectedIndex(getRow(point)); + + rightButtonMenu.setAccount(getSelectedAccount()); + + SwingUtilities.convertPointToScreen( + point, AccountList.this); + + ((JPopupMenu) rightButtonMenu).setInvoker(AccountList.this); + + rightButtonMenu.setLocation(point.x, point.y); + rightButtonMenu.setVisible(true); + } + } + }); + this.accountsInit(); GuiActivator.bundleContext.addServiceListener(this); @@ -76,6 +105,11 @@ public class AccountList this.editButton = parentConfigPanel.getEditButton(); } + private int getRow(Point point) + { + return locationToIndex(point); + } + /** * Initializes the accounts table. */ diff --git a/src/net/java/sip/communicator/impl/gui/main/account/AccountRightButtonMenu.java b/src/net/java/sip/communicator/impl/gui/main/account/AccountRightButtonMenu.java new file mode 100644 index 0000000..f74ffb9 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/account/AccountRightButtonMenu.java @@ -0,0 +1,260 @@ +/* + * 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.impl.gui.main.account; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; + +import javax.swing.*; + +import net.java.sip.communicator.impl.gui.*; +import net.java.sip.communicator.impl.gui.event.*; +import net.java.sip.communicator.impl.gui.utils.*; +import net.java.sip.communicator.plugin.desktoputil.*; +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.gui.Container; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.skin.*; + +import org.jitsi.service.resources.*; +import org.osgi.framework.*; + +/** + * AccountRightButtonMenu is the menu that opens when user right clicks on any + * of his accounts in the account list section. + * + * @author Marin Dzhigarov + */ +public class AccountRightButtonMenu + extends SIPCommPopupMenu + implements ActionListener, + PluginComponentListener, + Skinnable +{ + /** + * The serial version UID. + */ + private static final long serialVersionUID = 1L; + + /** + * The logger of this class. + */ + private final Logger logger + = Logger.getLogger(AccountRightButtonMenu.class); + + /** + * The Account that is clicked on. + */ + private Account account = null; + + /** + * A list of all PluginComponents that are registered through the OSGi + * bundle context. + */ + private java.util.List<PluginComponent> pluginComponents + = new ArrayList<PluginComponent>(); + + /** + * The edit item. + */ + private final JMenuItem editItem = new JMenuItem( + GuiActivator.getResources().getI18NString( + "service.gui.EDIT")); + + /** + * Creates an instance of AccountRightButtonMenu + */ + public AccountRightButtonMenu() + { + super(); + + this.setLocation(getLocation()); + + this.init(); + + loadSkin(); + } + + /** + * Sets the current Account that is clicked on. + * @param account the Account that is clicked on. + */ + public void setAccount(Account account) + { + this.account = account; + editItem.setEnabled(account != null && this.account.isEnabled()); + for (PluginComponent pluginComponent : pluginComponents) + pluginComponent.setCurrentAccountID(account.getAccountID()); + } + + /** + * Returns the Account that was last clicked on. + * @return the Account that was last clicked on. + */ + public Account getAccount() + { + return account; + } + + /** + * Initialized the menu by adding all containing menu items. + */ + private void init() + { + initPluginComponents(); + add(editItem); + editItem.addActionListener(this); + } + + /** + * Initializes plug-in components for this container. + */ + private void initPluginComponents() + { + // Search for plugin components registered through the OSGI bundle + // context. + ServiceReference[] serRefs = null; + + String osgiFilter = "(" + + Container.CONTAINER_ID + + "="+Container.CONTAINER_ACCOUNT_RIGHT_BUTTON_MENU.getID()+")"; + + try + { + serRefs = GuiActivator.bundleContext.getServiceReferences( + PluginComponentFactory.class.getName(), + osgiFilter); + } + catch (InvalidSyntaxException exc) + { + logger.error("Could not obtain plugin reference.", exc); + } + + if (serRefs != null) + { + for (int i = 0; i < serRefs.length; i ++) + { + PluginComponentFactory factory = + (PluginComponentFactory) GuiActivator + .bundleContext.getService(serRefs[i]); + + PluginComponent component = + factory.getPluginComponentInstance(this); + + if (component.getComponent() == null) + continue; + + pluginComponents.add(component); + + if (factory.getPositionIndex() != -1) + this.add((Component)component.getComponent(), + factory.getPositionIndex()); + else + this.add((Component)component.getComponent()); + } + } + GuiActivator.getUIService().addPluginComponentListener(this); + } + + /** + * Reloads skin related information. + */ + public void loadSkin() + { + editItem.setIcon(new ImageIcon( + ImageLoader.getImage(ImageLoader.ACCOUNT_EDIT_ICON))); + } + + /** + * Indicates that a plugin component has been added to this container. + * + * @param event the <tt>PluginComponentEvent</tt> that notified us + */ + /** + * Indicates that a new plugin component has been added. Adds it to this + * container if it belongs to it. + * + * @param event the <tt>PluginComponentEvent</tt> that notified us + */ + public void pluginComponentAdded(PluginComponentEvent event) + { + PluginComponentFactory factory = event.getPluginComponentFactory(); + + if (!factory.getContainer().equals( + Container.CONTAINER_ACCOUNT_RIGHT_BUTTON_MENU)) + return; + + PluginComponent c = factory.getPluginComponentInstance(this); + + this.add((Component) c.getComponent()); + + this.repaint(); + } + + /** + * Removes the according plug-in component from this container. + * @param event the <tt>PluginComponentEvent</tt> that notified us + */ + public void pluginComponentRemoved(PluginComponentEvent event) + { + PluginComponentFactory factory = event.getPluginComponentFactory(); + + if(factory.getContainer() + .equals(Container.CONTAINER_ACCOUNT_RIGHT_BUTTON_MENU)) + { + Component c = + (Component)factory.getPluginComponentInstance(this) + .getComponent(); + this.remove(c); + pluginComponents.remove(c); + } + } + + /** + * Handles the <tt>ActionEvent</tt>. Determines which menu item was + * selected and performs the appropriate operations. + * @param e the <tt>ActionEvent</tt>, which notified us of the action + */ + public void actionPerformed(ActionEvent e) + { + JMenuItem menuItem = (JMenuItem) e.getSource(); + + if (menuItem.equals(editItem)) + { + if (account == null) + return; + + AccountRegWizardContainerImpl wizard = + (AccountRegWizardContainerImpl) GuiActivator.getUIService() + .getAccountRegWizardContainer(); + + AccountRegistrationWizard protocolWizard = + wizard.getProtocolWizard(account.getProtocolProvider()); + + ResourceManagementService resources = GuiActivator.getResources(); + if (protocolWizard != null) + { + wizard.setTitle(resources.getI18NString( + "service.gui.ACCOUNT_REGISTRATION_WIZARD")); + + wizard.modifyAccount(account.getProtocolProvider()); + wizard.showDialog(false); + } + else + { + // There is no wizard for this account - just show an error + // dialog: + String title = resources.getI18NString("service.gui.ERROR"); + String message = + resources.getI18NString("service.gui.EDIT_NOT_SUPPORTED"); + ErrorDialog dialog = new ErrorDialog(null, title, message); + dialog.setVisible(true); + } + } + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/presence/AccountStatusPanel.java b/src/net/java/sip/communicator/impl/gui/main/presence/AccountStatusPanel.java index 9b39b5d..5e7d615 100644 --- a/src/net/java/sip/communicator/impl/gui/main/presence/AccountStatusPanel.java +++ b/src/net/java/sip/communicator/impl/gui/main/presence/AccountStatusPanel.java @@ -14,9 +14,9 @@ import javax.swing.*; import net.java.sip.communicator.impl.gui.*; import net.java.sip.communicator.impl.gui.event.*; import net.java.sip.communicator.impl.gui.lookandfeel.*; -import net.java.sip.communicator.impl.gui.main.*; -import net.java.sip.communicator.impl.gui.main.presence.avatar.*; import net.java.sip.communicator.impl.gui.utils.*; +import net.java.sip.communicator.plugin.desktoputil.*; +import net.java.sip.communicator.plugin.desktoputil.presence.avatar.*; import net.java.sip.communicator.service.globaldisplaydetails.event.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.gui.Container; @@ -25,8 +25,6 @@ import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.util.*; import net.java.sip.communicator.util.skin.*; -import net.java.sip.communicator.plugin.desktoputil.*; - import org.jitsi.util.*; /** @@ -126,15 +124,13 @@ public class AccountStatusPanel /** * Creates an instance of <tt>AccountStatusPanel</tt> by specifying the * main window, where this panel is added. - * @param mainFrame the main window, where this panel is added */ - public AccountStatusPanel(MainFrame mainFrame) + public AccountStatusPanel() { super(new BorderLayout(10, 0)); FramedImageWithMenu imageWithMenu = new FramedImageWithMenu( - mainFrame, new ImageIcon( ImageLoader .getImage(ImageLoader.DEFAULT_USER_PHOTO)), diff --git a/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf b/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf index ce04afb..607dffc 100644 --- a/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf +++ b/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf @@ -58,6 +58,7 @@ Import-Package: com.apple.eawt, net.java.sip.communicator.plugin.desktoputil.event, net.java.sip.communicator.plugin.desktoputil.plaf, net.java.sip.communicator.plugin.desktoputil.presence, + net.java.sip.communicator.plugin.desktoputil.presence.avatar, net.java.sip.communicator.plugin.desktoputil.transparent, net.java.sip.communicator.service.customcontactactions, net.java.sip.communicator.service.globaldisplaydetails, diff --git a/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java b/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java index 083e2d1..c2dba57 100644 --- a/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java +++ b/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java @@ -161,6 +161,12 @@ public class ImageLoader = new ImageID("service.gui.icons.RIGHT_ARROW_ICON"); /** + * The edit icon that is shown when account is right clicked on. + */ + public static final ImageID ACCOUNT_EDIT_ICON + = new ImageID("service.gui.buttons.ACCOUNT_EDIT_ICON"); + + /** * The call button image. */ public static final ImageID INCOMING_CALL_BUTTON_BG diff --git a/src/net/java/sip/communicator/impl/protocol/icq/OperationSetServerStoredAccountInfoIcqImpl.java b/src/net/java/sip/communicator/impl/protocol/icq/OperationSetServerStoredAccountInfoIcqImpl.java index 19c0992..0d10453 100644 --- a/src/net/java/sip/communicator/impl/protocol/icq/OperationSetServerStoredAccountInfoIcqImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/icq/OperationSetServerStoredAccountInfoIcqImpl.java @@ -52,6 +52,7 @@ public class OperationSetServerStoredAccountInfoIcqImpl public static final Map<Class<? extends GenericDetail>, int[]> supportedTypes = new Hashtable<Class<? extends GenericDetail>, int[]>(); static { + supportedTypes.put(ServerStoredDetails.ImageDetail.class, new int[]{1}); supportedTypes.put(ServerStoredDetails.CountryDetail.class, new int[]{1, 0x01A4}); supportedTypes.put(ServerStoredDetails.NicknameDetail.class, new int[]{1, 0x0154}); supportedTypes.put(ServerStoredDetails.FirstNameDetail.class, new int[]{1, 0x0140}); @@ -227,6 +228,23 @@ public class OperationSetServerStoredAccountInfoIcqImpl } /** + * Determines whether the underlying implementation supports edition + * of this detail class. + * <p> + * @param detailClass the class whose edition we'd like to determine if it's + * possible + * @return true if the underlying implementation supports edition of this + * type of detail and false otherwise. + */ + public boolean isDetailClassEditable( + Class<? extends GenericDetail> detailClass) + { + return + isDetailClassSupported(detailClass) + && ImageDetail.class.isAssignableFrom(detailClass); + } + + /** * Utility method throwing an exception if the icq stack is not properly * initialized. * @throws java.lang.IllegalStateException if the underlying ICQ stack is @@ -794,6 +812,18 @@ public class OperationSetServerStoredAccountInfoIcqImpl return false; } + /* + * (non-Javadoc) + * @see net.java.sip.communicator.service.protocol.OperationSetServerStoredAccountInfo#save() + * This method is currently unimplemented. + * The idea behind this method is for users to call it only once, meaning + * that all ServerStoredDetails previously modified by addDetail/removeDetail + * and/or replaceDetail will be saved online on the server in one step. + * Currently, addDetail/removeDetail/replaceDetail methods are doing the + * actual saving but in the future the saving part must be carried here. + */ + public void save() throws OperationFailedException {} + /** * Requests the account image if its missing. * @return the new image or the one that has been already downloaded. diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/InfoRetreiver.java b/src/net/java/sip/communicator/impl/protocol/jabber/InfoRetreiver.java index af1be57..869e51d 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/InfoRetreiver.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/InfoRetreiver.java @@ -8,6 +8,7 @@ package net.java.sip.communicator.impl.protocol.jabber; import java.lang.reflect.*; import java.net.*; +import java.text.*; import java.util.*; import net.java.sip.communicator.service.protocol.ServerStoredDetails.*; @@ -175,6 +176,24 @@ public class InfoRetreiver if(tmp != null) result.add(new NicknameDetail(tmp)); + tmp = card.getField("BDAY"); + if (tmp != null) + { + try + { + Calendar birthDateCalendar = Calendar.getInstance(); + DateFormat dateFormat = + new SimpleDateFormat( + JabberActivator.getResources().getI18NString( + "plugin.accountinfo.BDAY_FORMAT")); + Date birthDate = + dateFormat.parse(tmp); + birthDateCalendar.setTime(birthDate); + BirthDateDetail bd = new BirthDateDetail(birthDateCalendar); + result.add(bd); + } + catch (ParseException e) {} + } // Home Details // addrField one of // POSTAL, PARCEL, (DOM | INTL), PREF, POBOX, EXTADR, STREET, @@ -195,9 +214,9 @@ public class InfoRetreiver if(tmp != null) result.add(new PostalCodeDetail(tmp)); -// tmp = card.getAddressFieldHome("CTRY"); -// if(tmp != null) -// result.add(new CountryDetail(tmp); + tmp = card.getAddressFieldHome("CTRY"); + if(tmp != null) + result.add(new CountryDetail(tmp)); // phoneType one of //VOICE, FAX, PAGER, MSG, CELL, VIDEO, BBS, MODEM, ISDN, PCS, PREF @@ -276,7 +295,7 @@ public class InfoRetreiver tmp = card.getEmailWork(); if(tmp != null) - result.add(new EmailAddressDetail(tmp)); + result.add(new WorkEmailAddressDetail(tmp)); tmp = card.getOrganization(); if(tmp != null) @@ -286,15 +305,25 @@ public class InfoRetreiver if(tmp != null) result.add(new WorkDepartmentNameDetail(tmp)); + tmp = card.getField("TITLE"); + if(tmp != null) + result.add(new JobTitleDetail(tmp)); + + tmp = card.getField("ABOUTME"); + if (tmp != null) + result.add(new AboutMeDetail(tmp)); + byte[] imageBytes = card.getAvatar(); if(imageBytes != null && imageBytes.length > 0) + { result.add(new ImageDetail("Image", imageBytes)); + } try { tmp = card.getField("URL"); if(tmp != null) - result.add(new WebPageDetail(new URL(tmp))); + result.add(new URLDetail("URL", new URL(tmp))); } catch(MalformedURLException e){} } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetServerStoredAccountInfoJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetServerStoredAccountInfoJabberImpl.java index 9d148e8..09b9e5f 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetServerStoredAccountInfoJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetServerStoredAccountInfoJabberImpl.java @@ -6,11 +6,12 @@ */ package net.java.sip.communicator.impl.protocol.jabber; +import java.net.*; +import java.text.*; import java.util.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.ServerStoredDetails.GenericDetail; -import net.java.sip.communicator.service.protocol.ServerStoredDetails.ImageDetail; +import net.java.sip.communicator.service.protocol.ServerStoredDetails.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.util.*; @@ -22,6 +23,7 @@ import org.jivesoftware.smack.*; * provider. * * @author Damian Minkov + * @author Marin Dzhigarov */ public class OperationSetServerStoredAccountInfoJabberImpl extends AbstractOperationSetServerStoredAccountInfo @@ -43,6 +45,35 @@ public class OperationSetServerStoredAccountInfoJabberImpl private ProtocolProviderServiceJabberImpl jabberProvider = null; /** + * List of all supported <tt>ServerStoredDetails</tt> + * for this implementation. + */ + public static final List<Class<? extends GenericDetail>> supportedTypes + = new ArrayList<Class<? extends GenericDetail>>(); + + static { + supportedTypes.add(ImageDetail.class); + supportedTypes.add(FirstNameDetail.class); + supportedTypes.add(MiddleNameDetail.class); + supportedTypes.add(LastNameDetail.class); + supportedTypes.add(NicknameDetail.class); + supportedTypes.add(AddressDetail.class); + supportedTypes.add(CityDetail.class); + supportedTypes.add(ProvinceDetail.class); + supportedTypes.add(PostalCodeDetail.class); + supportedTypes.add(CountryDetail.class); + supportedTypes.add(EmailAddressDetail.class); + supportedTypes.add(WorkEmailAddressDetail.class); + supportedTypes.add(PhoneNumberDetail.class); + supportedTypes.add(WorkPhoneDetail.class); + supportedTypes.add(WorkOrganizationNameDetail.class); + supportedTypes.add(URLDetail.class); + supportedTypes.add(BirthDateDetail.class); + supportedTypes.add(JobTitleDetail.class); + supportedTypes.add(AboutMeDetail.class); + } + + /** * Our account UIN. */ private String uin = null; @@ -124,14 +155,7 @@ public class OperationSetServerStoredAccountInfoJabberImpl */ public Iterator<Class<? extends GenericDetail>> getSupportedDetailTypes() { - List<GenericDetail> details = infoRetreiver.getContactDetails(uin); - List<Class<? extends GenericDetail>> result - = new Vector<Class<? extends GenericDetail>>(); - - for (GenericDetail obj : details) - result.add(obj.getClass()); - - return result.iterator(); + return supportedTypes.iterator(); } /** @@ -150,12 +174,7 @@ public class OperationSetServerStoredAccountInfoJabberImpl public boolean isDetailClassSupported( Class<? extends GenericDetail> detailClass) { - List<GenericDetail> details = infoRetreiver.getContactDetails(uin); - - for (GenericDetail obj : details) - if(detailClass.isAssignableFrom(obj.getClass())) - return true; - return false; + return supportedTypes.contains(detailClass); } /** @@ -173,7 +192,7 @@ public class OperationSetServerStoredAccountInfoJabberImpl } /** - * Adds the specified detail to the list of details registered on-line + * Adds the specified detail to the list of details ready to be saved online * for this account. If such a detail already exists its max instance number * is consulted and if it allows it - a second instance is added or otherwise * and illegal argument exception is thrown. An IllegalArgumentException is @@ -187,67 +206,50 @@ public class OperationSetServerStoredAccountInfoJabberImpl * max instances number has been attained or if the underlying * implementation does not support setting details of the corresponding * class. - * @throws OperationFailedException with code Network Failure if putting the - * new value online has failed * @throws java.lang.ArrayIndexOutOfBoundsException if the number of * instances currently registered by the application is already equal to the * maximum number of supported instances (@see getMaxDetailInstances()) */ public void addDetail(ServerStoredDetails.GenericDetail detail) throws IllegalArgumentException, - OperationFailedException, ArrayIndexOutOfBoundsException { - assertConnected(); - - /* - Currently as the function only provided the list of classes that - currently have data associated with them - in Jabber InfoRetreiver we have to skip this check*/ - //if (!isDetailClassSupported(detail.getClass())) { - // throw new IllegalArgumentException( - // "implementation does not support such details " + - // detail.getClass()); - //} + if (!isDetailClassSupported(detail.getClass())) { + throw new IllegalArgumentException( + "implementation does not support such details " + + detail.getClass()); + } Iterator<GenericDetail> iter = getDetails(detail.getClass()); int currentDetailsSize = 0; while (iter.hasNext()) { currentDetailsSize++; + iter.next(); } - if (currentDetailsSize >= getMaxDetailInstances(detail.getClass())) + if (currentDetailsSize > getMaxDetailInstances(detail.getClass())) { throw new ArrayIndexOutOfBoundsException( "Max count for this detail is already reached"); } - if(detail instanceof ImageDetail) - { - // Push the avatar photo to the server. - this.uploadImageDetail( - ServerStoredDetailsChangeEvent.DETAIL_ADDED, - null, - detail); - } + infoRetreiver.getCachedContactDetails(uin).add(detail); } /** - * Removes the specified detail from the list of details stored online for - * this account. The method returns a boolean indicating if such a detail - * was found (and removed) or not. + * Removes the specified detail from the list of details ready to be saved + * online this account. The method returns a boolean indicating if such a + * detail was found (and removed) or not. * <p> * @param detail the detail to remove * @return true if the specified detail existed and was successfully removed * and false otherwise. - * @throws OperationFailedException with code Network Failure if removing the - * detail from the server has failed */ public boolean removeDetail(ServerStoredDetails.GenericDetail detail) - throws OperationFailedException { - return false; + + return infoRetreiver.getCachedContactDetails(uin).remove(detail); } /** @@ -264,16 +266,12 @@ public class OperationSetServerStoredAccountInfoJabberImpl * call to addDetail is required). * @throws ClassCastException if newDetailValue is not an instance of the * same class as currentDetailValue. - * @throws OperationFailedException with code Network Failure if putting the - * new value back online has failed */ public boolean replaceDetail( ServerStoredDetails.GenericDetail currentDetailValue, ServerStoredDetails.GenericDetail newDetailValue) - throws ClassCastException, OperationFailedException + throws ClassCastException { - assertConnected(); - if (!newDetailValue.getClass().equals(currentDetailValue.getClass())) { throw new ClassCastException( @@ -304,22 +302,130 @@ public class OperationSetServerStoredAccountInfoJabberImpl return false; } - if(newDetailValue instanceof ImageDetail) + removeDetail(currentDetailValue); + addDetail(newDetailValue); + return true; + } + + /** + * Saves the list of details for this account that were ready to be stored + * online on the server. This method performs the actual saving of details + * online on the server and is supposed to be invoked after addDetail(), + * replaceDetail() and/or removeDetail(). + * <p> + * @throws OperationFailedException with code Network Failure if putting the + * new values back online has failed. + */ + public void save() throws OperationFailedException + { + assertConnected(); + + List<GenericDetail> details = infoRetreiver.getContactDetails(uin); + VCardXEP0153 vCard = new VCardXEP0153(); + for (GenericDetail detail : details) + { + if (detail instanceof ImageDetail) + { + byte[] avatar = ((ImageDetail) detail).getBytes(); + if (avatar == null) vCard.setAvatar(new byte[0]); + else vCard.setAvatar(avatar); + fireServerStoredDetailsChangeEvent( + jabberProvider, + ServerStoredDetailsChangeEvent.DETAIL_ADDED, + null, + detail); + } + else if (detail.getClass().equals(FirstNameDetail.class)) + vCard.setFirstName((String)detail.getDetailValue()); + else if (detail.getClass().equals(MiddleNameDetail.class)) + vCard.setMiddleName((String)detail.getDetailValue()); + else if (detail.getClass().equals(LastNameDetail.class)) + vCard.setLastName((String)detail.getDetailValue()); + else if (detail.getClass().equals(NicknameDetail.class)) + vCard.setNickName((String)detail.getDetailValue()); + else if (detail.getClass().equals(URLDetail.class)) + { + if (detail.getDetailValue() != null) + vCard.setField( + "URL", ((URL)detail.getDetailValue()).toString()); + } + else if (detail.getClass().equals(BirthDateDetail.class)) + { + if (detail.getDetailValue() != null) + { + Calendar c = ((BirthDateDetail)detail).getCalendar(); + DateFormat dateFormat = + new SimpleDateFormat( + JabberActivator.getResources().getI18NString( + "plugin.accountinfo.BDAY_FORMAT")); + String strdate = dateFormat.format(c.getTime()); + vCard.setField("BDAY", strdate); + } + } + else if (detail.getClass().equals(AddressDetail.class)) + vCard.setAddressFieldHome( + "STREET", (String)detail.getDetailValue()); + else if (detail.getClass().equals(CityDetail.class)) + vCard.setAddressFieldHome( + "LOCALITY", (String)detail.getDetailValue()); + else if (detail.getClass().equals(ProvinceDetail.class)) + vCard.setAddressFieldHome( + "REGION", (String)detail.getDetailValue()); + else if (detail.getClass().equals(PostalCodeDetail.class)) + vCard.setAddressFieldHome( + "PCODE", (String)detail.getDetailValue()); + else if (detail.getClass().equals(CountryDetail.class)) + vCard.setAddressFieldHome( + "CTRY", (String)detail.getDetailValue()); + else if (detail.getClass().equals(PhoneNumberDetail.class)) + vCard.setPhoneHome("VOICE", (String)detail.getDetailValue()); + else if (detail.getClass().equals(WorkPhoneDetail.class)) + vCard.setPhoneWork("VOICE", (String)detail.getDetailValue()); + else if (detail.getClass().equals(EmailAddressDetail.class)) + vCard.setEmailHome((String)detail.getDetailValue()); + else if (detail.getClass().equals(WorkEmailAddressDetail.class)) + vCard.setEmailWork((String)detail.getDetailValue()); + else if (detail.getClass().equals(WorkOrganizationNameDetail.class)) + vCard.setOrganization((String)detail.getDetailValue()); + else if (detail.getClass().equals(JobTitleDetail.class)) + vCard.setField("TITLE", (String)detail.getDetailValue()); + else if (detail.getClass().equals(AboutMeDetail.class)) + vCard.setField("ABOUTME", (String)detail.getDetailValue()); + } + try { - // Push the new avatar photo to the server. - return this.uploadImageDetail( - ServerStoredDetailsChangeEvent.DETAIL_REPLACED, - currentDetailValue, - newDetailValue); + vCard.save(jabberProvider.getConnection()); } + catch (XMPPException xmppe) + { + logger.error("Error loading/saving vcard: ", xmppe); + throw new OperationFailedException( + "Error loading/saving vcard: ", 1, xmppe); + } + } + /** + * Determines whether the underlying implementation supports edition + * of this detail class. + * <p> + * @param detailClass the class whose edition we'd like to determine if it's + * possible + * @return true if the underlying implementation supports edition of this + * type of detail and false otherwise. + */ + public boolean isDetailClassEditable( + Class<? extends GenericDetail> detailClass) + { + if (isDetailClassSupported(detailClass)) { + return true; + } return false; } /** - * Utility method throwing an exception if the icq stack is not properly + * Utility method throwing an exception if the jabber stack is not properly * initialized. - * @throws java.lang.IllegalStateException if the underlying ICQ stack is + * @throws java.lang.IllegalStateException if the underlying jabber stack is * not registered and initialized. */ private void assertConnected() throws IllegalStateException @@ -333,73 +439,4 @@ public class OperationSetServerStoredAccountInfoJabberImpl "The jabber provider must be signed on before " +"being able to communicate."); } - - /** - * Uploads the new avatar image to the server via the vCard mechanism - * (XEP-0153). - * - * @param changeEventID the int ID of the event to dispatch - * @param currentDetailValue the detail value we'd like to replace. - * @param newDetailValue the value of the detail that we'd like to replace - * currentDetailValue with. If ((ImageDetail) newDetailValue).getBytes() is - * null, then this function removes the current avatar from the server by - * sending a vCard with a "photo" tag without any content. - * - * @return "true" if the new avatar image has been uploaded (even if the - * current avatar image is removed). "false" if an XMPPException occurs. - */ - private boolean uploadImageDetail( - int changeEventID, - ServerStoredDetails.GenericDetail currentDetailValue, - ServerStoredDetails.GenericDetail newDetailValue) - { - boolean isPhotoChanged = false; - - try - { - byte[] newAvatar = ((ImageDetail) newDetailValue).getBytes(); - - VCardXEP0153 v1 = new VCardXEP0153(); - // Retrieve the old vCard. - v1.load(jabberProvider.getConnection()); - // Checks if the new avatar photo is diferent form the server one. - // If yes, then upload the new avatar photo. - if(!Arrays.equals(v1.getAvatar(), newAvatar)) - { - if(newAvatar == null) - { - v1.setAvatar(new byte[0]); - } - else - { - v1.setAvatar(newAvatar); - } - // Saves the new vCard. - v1.save(jabberProvider.getConnection()); - } - - // Sets the new avatar photo advertised in all presence messages, - // and send one presence messge immediately. - ((OperationSetPersistentPresenceJabberImpl) - this.jabberProvider.getOperationSet( - OperationSetPersistentPresence.class)) - .updateAccountPhotoPresenceExtension(newAvatar); - - // Advertises all detail change listeners, that the server stored - // details have changed. - fireServerStoredDetailsChangeEvent( - jabberProvider, - changeEventID, - currentDetailValue, - newDetailValue); - - isPhotoChanged = true; - } - catch (XMPPException xmppe) - { - logger.error("Error loading/saving vcard: ", xmppe); - } - - return isPhotoChanged; - } -} +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/msn/OperationSetServerStoredAccountInfoMsnImpl.java b/src/net/java/sip/communicator/impl/protocol/msn/OperationSetServerStoredAccountInfoMsnImpl.java index 81aa89a..fb43723 100644 --- a/src/net/java/sip/communicator/impl/protocol/msn/OperationSetServerStoredAccountInfoMsnImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/msn/OperationSetServerStoredAccountInfoMsnImpl.java @@ -268,6 +268,23 @@ public class OperationSetServerStoredAccountInfoMsnImpl } /** + * Determines whether the underlying implementation supports edition + * of this detail class. + * <p> + * @param detailClass the class whose edition we'd like to determine if it's + * possible + * @return true if the underlying implementation supports edition of this + * type of detail and false otherwise. + */ + public boolean isDetailClassEditable( + Class<? extends GenericDetail> detailClass) + { + return + isDetailClassSupported(detailClass) + && ImageDetail.class.isAssignableFrom(detailClass); + } + + /** * The method returns the number of instances supported for a particular * detail type. Some protocols offer storing multiple values for a * particular detail type. Spoken languages are a good example. @@ -518,6 +535,18 @@ public class OperationSetServerStoredAccountInfoMsnImpl return false; } + /* + * (non-Javadoc) + * @see net.java.sip.communicator.service.protocol.OperationSetServerStoredAccountInfo#save() + * This method is currently unimplemented. + * The idea behind this method is for users to call it only once, meaning + * that all ServerStoredDetails previously modified by addDetail/removeDetail + * and/or replaceDetail will be saved online on the server in one step. + * Currently, addDetail/removeDetail/replaceDetail methods are doing the + * actual saving but in the future the saving part must be carried here. + */ + public void save() throws OperationFailedException {} + /** * Utility method throwing an exception if the icq stack is not properly * initialized. diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetServerStoredAccountInfoSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetServerStoredAccountInfoSipImpl.java index 4ad22a7..6b528ae 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetServerStoredAccountInfoSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetServerStoredAccountInfoSipImpl.java @@ -195,6 +195,23 @@ public class OperationSetServerStoredAccountInfoSipImpl } /** + * Determines whether the underlying implementation supports the edition + * of this detail class. + * <p> + * @param detailClass the class whose edition we'd like to determine if it's + * possible + * @return true if the underlying implementation supports edition of this + * type of detail and false otherwise. + */ + public boolean isDetailClassEditable( + Class<? extends GenericDetail> detailClass) + { + return + isDetailClassSupported(detailClass) + && ImageDetail.class.isAssignableFrom(detailClass); + } + + /** * The method returns the number of instances supported for a particular * detail type. * @@ -411,6 +428,18 @@ public class OperationSetServerStoredAccountInfoSipImpl return true; } + /* + * (non-Javadoc) + * @see net.java.sip.communicator.service.protocol.OperationSetServerStoredAccountInfo#save() + * This method is currently unimplemented. + * The idea behind this method is for users to call it only once, meaning + * that all ServerStoredDetails previously modified by addDetail/removeDetail + * and/or replaceDetail will be saved online on the server in one step. + * Currently, addDetail/removeDetail/replaceDetail methods are doing the + * actual saving but in the future the saving part must be carried here. + */ + public void save() throws OperationFailedException {} + /** * Determines if image details is supported. * diff --git a/src/net/java/sip/communicator/plugin/accountinfo/AccountDetailsPanel.java b/src/net/java/sip/communicator/plugin/accountinfo/AccountDetailsPanel.java index 7ddf425..fa96f03 100644 --- a/src/net/java/sip/communicator/plugin/accountinfo/AccountDetailsPanel.java +++ b/src/net/java/sip/communicator/plugin/accountinfo/AccountDetailsPanel.java @@ -7,75 +7,150 @@ package net.java.sip.communicator.plugin.accountinfo; import java.awt.*; import java.awt.event.*; -import java.io.*; -import java.text.*; +import java.beans.*; +import java.net.*; import java.util.*; -import javax.imageio.*; import javax.swing.*; +import javax.swing.text.*; + +import org.jitsi.util.*; import net.java.sip.communicator.plugin.desktoputil.*; +import net.java.sip.communicator.plugin.desktoputil.presence.avatar.*; +import net.java.sip.communicator.service.globaldisplaydetails.*; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.ServerStoredDetails.BinaryDetail; +import net.java.sip.communicator.service.protocol.ServerStoredDetails.AboutMeDetail; +import net.java.sip.communicator.service.protocol.ServerStoredDetails.AddressDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.BirthDateDetail; +import net.java.sip.communicator.service.protocol.ServerStoredDetails.CityDetail; +import net.java.sip.communicator.service.protocol.ServerStoredDetails.CountryDetail; +import net.java.sip.communicator.service.protocol.ServerStoredDetails.DisplayNameDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.EmailAddressDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.FirstNameDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.GenderDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.GenericDetail; +import net.java.sip.communicator.service.protocol.ServerStoredDetails.ImageDetail; +import net.java.sip.communicator.service.protocol.ServerStoredDetails.JobTitleDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.LastNameDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.MiddleNameDetail; +import net.java.sip.communicator.service.protocol.ServerStoredDetails.NicknameDetail; 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.service.protocol.ServerStoredDetails.PostalCodeDetail; +import net.java.sip.communicator.service.protocol.ServerStoredDetails.ProvinceDetail; +import net.java.sip.communicator.service.protocol.ServerStoredDetails.URLDetail; +import net.java.sip.communicator.service.protocol.ServerStoredDetails.WorkEmailAddressDetail; +import net.java.sip.communicator.service.protocol.ServerStoredDetails.WorkOrganizationNameDetail; +import net.java.sip.communicator.service.protocol.ServerStoredDetails.WorkPhoneDetail; + +import net.java.sip.communicator.util.Logger; + +import com.toedter.calendar.*; /** - * The right side panel of AccountDetailsDialog. Shows one tab of a summary of - * contact information for the selected subcontact, and has an extended tab - * listing all of the details. + * The main panel that allows users to view and edit their account information. + * Different instances of this class are created for every registered + * <tt>ProtocolProviderService</tt>. + * Currently, supported account details are first/middle/last names, nickname, + * street/city/region/country address, postal code, birth date, gender, + * organization name, job title, about me, home/work email, home/work phone. * * @author Yana Stamcheva * @author Adam Netocny + * @author Marin Dzhigarov */ public class AccountDetailsPanel extends TransparentPanel - implements Skinnable { - private static final long serialVersionUID = 5524135388175045624L; + /** + * Serial version UID. + */ + private static final long serialVersionUID = 1L; + /** + * The logger + */ private Logger logger = Logger.getLogger(AccountDetailsPanel.class); /** + * Mapping between all supported by this plugin <tt>ServerStoredDetails</tt> + * and their respective <tt>JTextField</tt> that are used for modifying + * the details. + */ + private final Map<Class<? extends GenericDetail>, JTextField> + detailToTextField + = new HashMap<Class<? extends GenericDetail>, JTextField>(); + + /** + * The <tt>ProtocolProviderService</tt> that this panel is associated with. + */ + ProtocolProviderService protocolProvider; + + /** * The operation set giving access to the server stored account details. */ private OperationSetServerStoredAccountInfo accountInfoOpSet; - private JTextField firstNameField = new JTextField(); + private JTextField displayNameField; + + private JTextField firstNameField; + + private JTextField middleNameField; + + private JTextField lastNameField; + + private JTextField nicknameField; + + private JTextField urlField; + + private JTextField streetAddressField; + + private JTextField cityField; - private JTextField middleNameField = new JTextField(); + private JTextField regionField; - private JTextField lastNameField = new JTextField(); + private JTextField postalCodeField; - private JTextField genderField = new JTextField(); + private JTextField countryField; - private JTextField ageField = new JTextField(); + private JTextField phoneField; - private JTextField birthdayField = new JTextField(); + private JTextField workPhoneField; - private JTextField emailField = new JTextField(); + private JTextField emailField; - private JTextField phoneField = new JTextField(); + private JTextField workEmailField; - private JLabel avatarLabel = new JLabel(); + private JTextField organizationField; + private JTextField jobTitleField; + + private JTextArea aboutMeArea; + + private JTextField genderField; + + private JTextField ageField; + + private JDateChooser birthDayCalendar; + + private JRadioButton globalIcon; + + private JRadioButton localIcon; + + private FramedImageWithMenu imageWithMenu; + /** + * The "apply" button. + */ private JButton applyButton = new JButton(Resources.getString("service.gui.APPLY")); + /** + * The panel containing all buttons. + */ private JPanel buttonPanel = new TransparentPanel(new FlowLayout(FlowLayout.CENTER)); - private JScrollPane mainScrollPane = new JScrollPane(); - - private boolean isDataLoaded = false; + private DisplayNameDetail displayNameDetail; private FirstNameDetail firstNameDetail; @@ -83,24 +158,47 @@ public class AccountDetailsPanel private LastNameDetail lastNameDetail; - private GenderDetail genderDetail; + private NicknameDetail nicknameDetail; - private BirthDateDetail birthDateDetail; + private URLDetail urlDetail; - private EmailAddressDetail emailDetail; + private AddressDetail streetAddressDetail; + + private CityDetail cityDetail; + + private ProvinceDetail regionDetail; + + private PostalCodeDetail postalCodeDetail; + + private CountryDetail countryDetail; private PhoneNumberDetail phoneDetail; + + private WorkPhoneDetail workPhoneDetail; + + private EmailAddressDetail emailDetail; + + private WorkEmailAddressDetail workEmailDetail; + + private WorkOrganizationNameDetail organizationDetail; + + private JobTitleDetail jobTitleDetail; + + private AboutMeDetail aboutMeDetail; - private BinaryDetail avatarDetail; + private GenderDetail genderDetail; - private byte[] newAvatarImage; + private BirthDateDetail birthDateDetail; - private ImageIcon avatarImageIcon = null; + private ImageDetail avatarDetail; /** - * The last avatar file directory open. + * The panel that contains description labels and text fields + * for every account detail. */ - private File lastAvatarDir; + private JPanel valuesPanel; + + private JScrollPane mainScrollPane; /** * Construct a panel containing all account details for the given protocol @@ -110,10 +208,11 @@ public class AccountDetailsPanel */ public AccountDetailsPanel(ProtocolProviderService protocolProvider) { - super(new BorderLayout()); - + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + setOpaque(false); + this.setPreferredSize(new Dimension(600, 400)); + this.protocolProvider = protocolProvider; this.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); -// this.setPreferredSize(new Dimension(500, 400)); accountInfoOpSet = protocolProvider @@ -134,550 +233,931 @@ public class AccountDetailsPanel } } + /** + * Creates the panel that indicates to the user that the currently selected + * contact does not support server stored contact info. + */ + private void initUnsupportedPanel() + { + JTextArea unsupportedTextArea = + new JTextArea(Resources.getString( + "plugin.accountinfo.NOT_SUPPORTED")); + + unsupportedTextArea.setEditable(false); + unsupportedTextArea.setOpaque(false); + + JPanel unsupportedPanel + = new TransparentPanel(); + + unsupportedPanel.setBorder( + BorderFactory.createEmptyBorder(50, 20, 50, 20)); + + unsupportedPanel.add(unsupportedTextArea); + + this.add(unsupportedPanel, BorderLayout.NORTH); + } + + /** + * Initialized the main panel that contains all <tt>ServerStoredDetails</tt> + */ private void initSummaryPanel() { JPanel summaryPanel = new TransparentPanel(new BorderLayout(10, 10)); summaryPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - summaryPanel.setSize(this.getWidth(), this.getHeight()); // Create the avatar panel. JPanel leftPanel = new TransparentPanel(new BorderLayout()); JPanel avatarPanel = new TransparentPanel(new BorderLayout()); - JButton changeAvatarButton = new JButton(Resources.getString("plugin.accountinfo.CHANGE")); - JPanel changeButtonPanel - = new TransparentPanel(new FlowLayout(FlowLayout.CENTER)); - - changeAvatarButton.addActionListener(new ChangeAvatarActionListener()); - changeButtonPanel.add(changeAvatarButton); - - avatarPanel.add(avatarLabel, BorderLayout.CENTER); - avatarPanel.add(changeButtonPanel, BorderLayout.SOUTH); + JPanel radioButtonPanel = new TransparentPanel(new GridLayout(2, 1)); + globalIcon = + new JRadioButton( + Resources.getString("plugin.accountinfo.GLOBAL_ICON")); + globalIcon.setSelected(true); + globalIcon.setOpaque(false); + globalIcon.setEnabled(false); + localIcon = + new JRadioButton( + Resources.getString("plugin.accountinfo.LOCAL_ICON")); + localIcon.setOpaque(false); + localIcon.setEnabled(false); + ButtonGroup group = new ButtonGroup(); + group.add(globalIcon); + group.add(localIcon); + radioButtonPanel.add(globalIcon); + radioButtonPanel.add(localIcon); + avatarPanel.add(radioButtonPanel, BorderLayout.NORTH); leftPanel.add(avatarPanel, BorderLayout.NORTH); - summaryPanel.add(leftPanel, BorderLayout.WEST); + detailToTextField.put(ImageDetail.class, new JTextField()); + + imageWithMenu + = new FramedImageWithMenu( + Resources.getImage( + "service.gui.DEFAULT_USER_PHOTO"), + Resources.getImage( + "service.gui.DEFAULT_USER_PHOTO").getIconWidth(), + Resources.getImage( + "service.gui.DEFAULT_USER_PHOTO").getIconHeight()); + SelectAvatarMenu selectAvatarMenu = new SelectAvatarMenu(imageWithMenu); + selectAvatarMenu.setAccountID(protocolProvider.getAccountID()); + imageWithMenu.setPopupMenu(selectAvatarMenu); + avatarPanel.add(imageWithMenu, BorderLayout.SOUTH); + + globalIcon.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + imageWithMenu.setEnabled(false); + } + }); + localIcon.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + imageWithMenu.setEnabled(true); + } + }); + imageWithMenu.setEnabled(false); + + valuesPanel = new TransparentPanel(new GridBagLayout()); + valuesPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + GridBagConstraints first = new GridBagConstraints(); + first.gridx = 0; + first.gridy = 0; + first.weightx = 0; + first.anchor = GridBagConstraints.LINE_START; + first.gridwidth = 1; + first.insets = new Insets(4, 4, 4, 4); + first.fill = GridBagConstraints.HORIZONTAL; + GridBagConstraints second = new GridBagConstraints(); + second.gridx = 1; + second.gridy = 0; + second.weightx = 2; + second.anchor = GridBagConstraints.LINE_START; + second.gridwidth = 1; // GridBagConstraints.REMAINDER; + second.insets = first.insets; + second.fill = GridBagConstraints.HORIZONTAL; + + if (accountInfoOpSet.isDetailClassSupported(DisplayNameDetail.class)) + { + displayNameField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.DISPLAY_NAME")) + , first); + valuesPanel.add(displayNameField, second); + first.gridy = ++second.gridy; + detailToTextField.put(DisplayNameDetail.class, displayNameField); + } - // Create the summary details panel. - JPanel detailsPanel = new TransparentPanel(new BorderLayout(10, 10)); - detailsPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + firstNameField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.FIRST_NAME")) + , first); + valuesPanel.add(firstNameField, second); + first.gridy = ++second.gridy; + detailToTextField.put(FirstNameDetail.class, firstNameField); + + middleNameField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.MIDDLE_NAME")) + , first); + valuesPanel.add(middleNameField, second); + first.gridy = ++second.gridy; + detailToTextField.put(MiddleNameDetail.class, middleNameField); + + lastNameField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.LAST_NAME")) + , first); + valuesPanel.add(lastNameField, second); + first.gridy = ++second.gridy; + detailToTextField.put(LastNameDetail.class, lastNameField); + + if (accountInfoOpSet.isDetailClassSupported(NicknameDetail.class)) + { + nicknameField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.NICKNAME")) + , first); + valuesPanel.add(nicknameField, second); + first.gridy = ++second.gridy; + detailToTextField.put(NicknameDetail.class, nicknameField); + } + if (accountInfoOpSet.isDetailClassSupported(URLDetail.class)) + { + urlField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.URL")) + , first); + valuesPanel.add(urlField, second); + first.gridy = ++second.gridy; + detailToTextField.put(URLDetail.class, urlField); + } + if (accountInfoOpSet.isDetailClassSupported(GenderDetail.class)) + { + genderField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.GENDER")) + , first); + valuesPanel.add(genderField, second); + first.gridy = ++second.gridy; + detailToTextField.put(GenderDetail.class, genderField); + } - summaryPanel.add(detailsPanel, BorderLayout.CENTER); + birthDayCalendar = new JDateChooser(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.BDAY")) + , first); + valuesPanel.add(birthDayCalendar, second); + birthDayCalendar.setDateFormatString( + Resources.getString("plugin.accountinfo.BDAY_FORMAT")); + birthDayCalendar.addPropertyChangeListener( + new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) + { + if (evt.getPropertyName().equals("date")) + { + Date date = (Date) evt.getNewValue(); + if (date != null) + { + Calendar currentDate = Calendar.getInstance(); + Calendar c = Calendar.getInstance(); + c.setTime(date); + int age = + currentDate.get(Calendar.YEAR) - + c.get(Calendar.YEAR); + + if (currentDate.get(Calendar.MONTH) < + c.get(Calendar.MONTH)) + age--; + if ((currentDate.get(Calendar.MONTH) == + c.get(Calendar.MONTH)) + && + (currentDate.get(Calendar.DAY_OF_MONTH) < + c.get(Calendar.DAY_OF_MONTH))) + age--; + String ageDetail = Integer.toString(age).trim(); + ageField.setText(ageDetail); + } + } + } + }); + first.gridy = ++second.gridy; + ageField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.AGE")), first); + valuesPanel.add(ageField, second); + first.gridy = ++second.gridy; + ageField.setEditable(false); + detailToTextField.put(BirthDateDetail.class, new JTextField()); + + if (accountInfoOpSet.isDetailClassSupported(AddressDetail.class)) + { + streetAddressField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.STREET")) + , first); + valuesPanel.add(streetAddressField, second); + first.gridy = ++second.gridy; + detailToTextField.put(AddressDetail.class, streetAddressField); + } + if (accountInfoOpSet.isDetailClassSupported(CityDetail.class)) + { + cityField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.CITY")), first); + valuesPanel.add(cityField, second); + first.gridy = ++second.gridy; + detailToTextField.put(CityDetail.class, cityField); + } + if (accountInfoOpSet.isDetailClassSupported(ProvinceDetail.class)) + { + regionField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.REGION")) + , first); + valuesPanel.add(regionField, second); + first.gridy = ++second.gridy; + detailToTextField.put(ProvinceDetail.class, regionField); + } + if (accountInfoOpSet.isDetailClassSupported(PostalCodeDetail.class)) + { + postalCodeField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.POST")) + , first); + valuesPanel.add(postalCodeField, second); + first.gridy = ++second.gridy; + detailToTextField.put(PostalCodeDetail.class, postalCodeField); + } + if (accountInfoOpSet.isDetailClassSupported(CountryDetail.class)) + { + countryField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.COUNTRY")) + , first); + valuesPanel.add(countryField, second); + first.gridy = ++second.gridy; + detailToTextField.put(CountryDetail.class, countryField); + } - // Labels panel. - JPanel labelsPanel = new TransparentPanel(new GridLayout(0, 1, 5, 5)); + emailField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.EMAIL")) + , first); + valuesPanel.add(emailField, second); + first.gridy = ++second.gridy; + detailToTextField.put(EmailAddressDetail.class, emailField); - labelsPanel.add(new JLabel( - Resources.getString("plugin.accountinfo.FIRST_NAME"))); -// labelsPanel.add(new JLabel(Resources.getString("plugin.accountinfo.MIDDLE_NAME"))); - labelsPanel.add(new JLabel( - Resources.getString("plugin.accountinfo.LAST_NAME"))); - labelsPanel.add(new JLabel( - Resources.getString("plugin.accountinfo.GENDER"))); - labelsPanel.add(new JLabel( - Resources.getString("plugin.accountinfo.AGE"))); - labelsPanel.add(new JLabel( - Resources.getString("plugin.accountinfo.BDAY"))); - labelsPanel.add(new JLabel( - Resources.getString("plugin.accountinfo.EMAIL"))); - labelsPanel.add(new JLabel( - Resources.getString("plugin.accountinfo.PHONE"))); + if (accountInfoOpSet.isDetailClassSupported( + WorkEmailAddressDetail.class)) + { + workEmailField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.WORK_EMAIL")) + , first); + valuesPanel.add(workEmailField, second); + first.gridy = ++second.gridy; + detailToTextField.put(WorkEmailAddressDetail.class, workEmailField); + } - detailsPanel.add(labelsPanel, BorderLayout.WEST); + phoneField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.PHONE")) + , first); + valuesPanel.add(phoneField, second); + first.gridy = ++second.gridy; + detailToTextField.put(PhoneNumberDetail.class, phoneField); - // Values panel. - JPanel valuesPanel = new TransparentPanel(new GridLayout(0, 1, 5, 5)); + if (accountInfoOpSet.isDetailClassSupported(WorkPhoneDetail.class)) + { + workPhoneField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.WORK_PHONE")) + , first); + valuesPanel.add(workPhoneField, second); + first.gridy = ++second.gridy; + detailToTextField.put( + WorkPhoneDetail.class, workPhoneField); + } + if (accountInfoOpSet.isDetailClassSupported( + WorkOrganizationNameDetail.class)) + { + organizationField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.ORGANIZATION")) + , first); + valuesPanel.add(organizationField, second); + first.gridy = ++second.gridy; + detailToTextField.put( + WorkOrganizationNameDetail.class, organizationField); + } + if (accountInfoOpSet.isDetailClassSupported(JobTitleDetail.class)) + { + jobTitleField = new JTextField(); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.JOB_TITLE")) + , first); + valuesPanel.add(jobTitleField, second); + first.gridy = ++second.gridy; + detailToTextField.put( + JobTitleDetail.class, jobTitleField); + } + if (accountInfoOpSet.isDetailClassSupported(AboutMeDetail.class)) + { + aboutMeArea = new JTextArea(3, 0); + valuesPanel.add(new JLabel( + Resources.getString("plugin.accountinfo.ABOUT_ME")) + , first); + second.gridheight = 3; + JScrollPane areaScrollPane = new JScrollPane(aboutMeArea); + areaScrollPane.setOpaque(false); + areaScrollPane.setBorder(BorderFactory.createEmptyBorder()); + areaScrollPane.setVerticalScrollBarPolicy( + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + valuesPanel.add(areaScrollPane, second); + first.gridy = ++second.gridy; + + DefaultStyledDocument doc = new DefaultStyledDocument(); + doc.setDocumentFilter(new DocumentFilter() { + + private final int MAX_CHARACTERS = + Integer.valueOf(Resources.getString( + "plugin.accountinfo.ABOUT_ME_MAX_CHARACTERS")); + + public void insertString(FilterBypass fb, int offs, + String str, AttributeSet a) + throws BadLocationException { + //This rejects the entire insertion if it would make + //the contents too long. Another option would be + //to truncate the inserted string so the contents + //would be exactly maxCharacters in length. + if ((fb.getDocument().getLength() + str.length()) + <= MAX_CHARACTERS) + super.insertString(fb, offs, str, a); + else + Toolkit.getDefaultToolkit().beep(); + } - valuesPanel.add(firstNameField); -// valuesPanel.add(middleNameField); - valuesPanel.add(lastNameField); - valuesPanel.add(genderField); - valuesPanel.add(ageField); - valuesPanel.add(birthdayField); - valuesPanel.add(emailField); - valuesPanel.add(phoneField); + public void replace(FilterBypass fb, int offs, + int length, + String str, AttributeSet a) + throws BadLocationException { + //This rejects the entire replacement if it would make + //the contents too long. Another option would be + //to truncate the replacement string so the contents + //would be exactly maxCharacters in length. + if ((fb.getDocument().getLength() + str.length() + - length) <= MAX_CHARACTERS) + super.replace(fb, offs, length, str, a); + else + Toolkit.getDefaultToolkit().beep(); + } + }); + + aboutMeArea.setDocument(doc); + aboutMeArea.setBorder(firstNameField.getBorder()); + aboutMeArea.setLineWrap(true); + aboutMeArea.setWrapStyleWord(true); + aboutMeArea.setPreferredSize(new Dimension(50, 100)); + aboutMeArea.setMinimumSize(new Dimension(50, 100)); + detailToTextField.put(AboutMeDetail.class, new JTextField()); + } - detailsPanel.add(valuesPanel, BorderLayout.CENTER); + mainScrollPane = new JScrollPane(summaryPanel, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + mainScrollPane.getViewport().setOpaque(false); + mainScrollPane.setOpaque(false); + mainScrollPane.setBorder(BorderFactory.createEmptyBorder()); - this.mainScrollPane.getViewport().add(summaryPanel); + summaryPanel.add(valuesPanel, BorderLayout.CENTER); - this.add(mainScrollPane, BorderLayout.NORTH); + this.add(mainScrollPane); this.applyButton.addActionListener(new SubmitActionListener()); + JButton cancelButton = + new JButton(Resources.getString("service.gui.CANCEL")); + cancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) + { + AccountInfoMenuItemComponent.dialog.setVisible(false); + mainScrollPane.getVerticalScrollBar().setValue(0); + } + }); this.buttonPanel.add(applyButton); + this.buttonPanel.add(cancelButton); - this.add(buttonPanel, BorderLayout.SOUTH); + this.add(buttonPanel); - // All items are now instantiated and could safely load the skin. - loadSkin(); + for (Component component : valuesPanel.getComponents()) + component.setEnabled(false); + if (aboutMeArea != null) + aboutMeArea.setEnabled(false); + applyButton.setEnabled(false); } /** - * Loads details for + * Loads all <tt>ServerStoredDetails</tt> which are currently supported by + * this plugin. Note that some <tt>OperationSetServerStoredAccountInfo</tt> + * implementations may support details that are not supported by this plugin. + * In this case they will not be loaded. */ public void loadDetails() { - this.loadSummaryDetails(); - this.isDataLoaded = true; + if (accountInfoOpSet != null) + { + Iterator<GenericDetail> allDetails = + accountInfoOpSet.getAllAvailableDetails(); + + while (allDetails.hasNext()) + { + GenericDetail detail = allDetails.next(); + loadDetail(detail); + } + + boolean isAnyDetailEditable = false; + for (Class<? extends GenericDetail> editable : + detailToTextField.keySet()) + { + if (accountInfoOpSet.isDetailClassEditable(editable)) + { + isAnyDetailEditable = true; + if (editable.equals(AboutMeDetail.class)) + aboutMeArea.setEnabled(true); + else if (editable.equals(BirthDateDetail.class)) + birthDayCalendar.setEnabled(true); + else if (editable.equals(ImageDetail.class)) + { + globalIcon.setEnabled(true); + localIcon.setEnabled(true); + imageWithMenu.setEnabled(true); + } + else + { + JTextField field = detailToTextField.get(editable); + field.setEnabled(true); + } + } + } + if (isAnyDetailEditable) + applyButton.setEnabled(true); + } } /** - * Creates the panel that indicates to the user that the currently selected - * contact does not support server stored contact info. + * Loads a single <tt>GenericDetail</tt> obtained from the + * <tt>OperationSetServerStoredAccountInfo</tt> into this plugin. + * @param detail to be loaded. */ - private void initUnsupportedPanel() + private void loadDetail(GenericDetail detail) { - JTextArea unsupportedTextArea = - new JTextArea(Resources.getString( - "plugin.accountinfo.NOT_SUPPORTED")); - - unsupportedTextArea.setEditable(false); - unsupportedTextArea.setLineWrap(true); - - JPanel unsupportedPanel - = new TransparentPanel(new FlowLayout(FlowLayout.CENTER)); - - unsupportedTextArea.setPreferredSize(new Dimension(200, 200)); + if (detail.getClass().equals(AboutMeDetail.class)) + { + aboutMeDetail = (AboutMeDetail) detail; + aboutMeArea.setText((String) detail.getDetailValue()); + return; + } + if (detail instanceof BirthDateDetail) + { + birthDateDetail = (BirthDateDetail) detail; - unsupportedPanel.setBorder( - BorderFactory.createEmptyBorder(50, 20, 50, 20)); + Calendar calendarDetail = + (Calendar) birthDateDetail.getDetailValue(); - unsupportedPanel.add(unsupportedTextArea); + birthDayCalendar.setCalendar(calendarDetail); + return; + } - this.add(unsupportedPanel); + JTextField field = detailToTextField.get(detail.getClass()); + if (field != null) + { + if (detail instanceof ImageDetail) + { + localIcon.setSelected(true); + avatarDetail = (ImageDetail) detail; + byte[] avatarImage = avatarDetail.getBytes(); + imageWithMenu.setImageIcon(avatarImage); + } + else if (detail instanceof URLDetail) + { + urlDetail = (URLDetail) detail; + urlField.setText(urlDetail.getURL().toString()); + } + else + { + field.setText((String) detail.getDetailValue()); + if (detail.getClass().equals(DisplayNameDetail.class)) + displayNameDetail = (DisplayNameDetail) detail; + else if (detail.getClass().equals(FirstNameDetail.class)) + firstNameDetail = (FirstNameDetail) detail; + else if (detail.getClass().equals(MiddleNameDetail.class)) + middleNameDetail = (MiddleNameDetail) detail; + else if (detail.getClass().equals(LastNameDetail.class)) + lastNameDetail = (LastNameDetail) detail; + else if (detail.getClass().equals(NicknameDetail.class)) + nicknameDetail = (NicknameDetail) detail; + else if (detail.getClass().equals(URLDetail.class)) + urlDetail = (URLDetail) detail; + else if (detail.getClass().equals(GenderDetail.class)) + genderDetail = (GenderDetail) detail; + else if (detail.getClass().equals(AddressDetail.class)) + streetAddressDetail = (AddressDetail) detail; + else if (detail.getClass().equals(CityDetail.class)) + cityDetail = (CityDetail) detail; + else if (detail.getClass().equals(ProvinceDetail.class)) + regionDetail = (ProvinceDetail) detail; + else if (detail.getClass().equals(PostalCodeDetail.class)) + postalCodeDetail = (PostalCodeDetail) detail; + else if (detail.getClass().equals(CountryDetail.class)) + countryDetail = (CountryDetail) detail; + else if (detail.getClass().equals(PhoneNumberDetail.class)) + phoneDetail = (PhoneNumberDetail) detail; + else if (detail.getClass().equals(WorkPhoneDetail.class)) + workPhoneDetail = (WorkPhoneDetail) detail; + else if (detail.getClass().equals(EmailAddressDetail.class)) + emailDetail = (EmailAddressDetail) detail; + else if (detail.getClass().equals(WorkEmailAddressDetail.class)) + workEmailDetail = (WorkEmailAddressDetail) detail; + else if (detail.getClass().equals( + WorkOrganizationNameDetail.class)) + organizationDetail = (WorkOrganizationNameDetail) detail; + else if (detail.getClass().equals(JobTitleDetail.class)) + jobTitleDetail = (JobTitleDetail) detail; + else if (detail.getClass().equals(AboutMeDetail.class)) + aboutMeDetail = (AboutMeDetail) detail; + } + } } /** - * Creates a panel that can be added as the summary tab that displays the - * following details: - - * <p> - * Avatar(Contact image) - FirstNameDetail - MiddleNameDetail - - * LastNameDetail - BirthdateDetail (and calculate age) - GenderDetail - - * EmailAddressDetail - PhoneNumberDetail. All other details will be* added - * to our list of extended details. + * Attempts to upload all <tt>ServerStoredDetails</tt> on the server using + * <tt>OperationSetServerStoredAccountInfo</tt> + * */ - private void loadSummaryDetails() + private class SubmitActionListener implements ActionListener { - Iterator<GenericDetail> contactDetails; - - // Avatar details. - contactDetails - = accountInfoOpSet.getDetails(BinaryDetail.class); - - byte[] avatarImage = null; - if (contactDetails.hasNext()) + public void actionPerformed(ActionEvent e) { - avatarDetail = (BinaryDetail) contactDetails.next(); + if (accountInfoOpSet.isDetailClassSupported(ImageDetail.class)) + { + if (globalIcon.isSelected()) + { + GlobalDisplayDetailsService serv = + AccountInfoActivator.getGlobalDisplayDetailsService(); - avatarImage = avatarDetail.getBytes(); - } + byte[] newIcon = serv.getGlobalDisplayAvatar(); - if (avatarImage != null && avatarImage.length > 0) - { - avatarImageIcon = new ImageIcon( - getScaledImageInstance(avatarImage)); - avatarLabel.setIcon(avatarImageIcon); - } + ImageDetail newDetail = null; + if (newIcon != null) + newDetail = + new ImageDetail( + "avatar", serv.getGlobalDisplayAvatar()); - // First name details. - contactDetails = - accountInfoOpSet.getDetails(FirstNameDetail.class); + if (avatarDetail != null || newDetail != null) + changeDetail(avatarDetail, newDetail); + } + } + if (accountInfoOpSet.isDetailClassSupported + (DisplayNameDetail.class)) + { + String text = + detailToTextField.get(DisplayNameDetail.class).getText(); - String firstNameDetailString = ""; - while (contactDetails.hasNext()) - { - firstNameDetail = (FirstNameDetail) contactDetails.next(); + DisplayNameDetail newDetail = null; + if (!StringUtils.isNullOrEmpty(text, true)) + newDetail = new DisplayNameDetail(text); - firstNameDetailString = - firstNameDetailString + " " + firstNameDetail.getDetailValue(); - } + if (displayNameDetail != null || newDetail != null) + changeDetail(displayNameDetail, newDetail); + } + if (accountInfoOpSet.isDetailClassSupported( + FirstNameDetail.class)) + { + String text = + detailToTextField.get(FirstNameDetail.class).getText(); - firstNameField.setText(firstNameDetailString); + FirstNameDetail newDetail = null; + if (!StringUtils.isNullOrEmpty(text, true)) + newDetail = new FirstNameDetail(text); - // Middle name details. - contactDetails = - accountInfoOpSet.getDetails(MiddleNameDetail.class); + if (firstNameDetail != null || newDetail != null) + changeDetail(firstNameDetail, newDetail); + } + if (accountInfoOpSet.isDetailClassSupported( + MiddleNameDetail.class)) + { + String text = + detailToTextField.get(MiddleNameDetail.class).getText(); - String middleNameDetailString = ""; - while (contactDetails.hasNext()) - { - middleNameDetail = (MiddleNameDetail) contactDetails.next(); - middleNameDetailString = - middleNameDetailString + " " + middleNameDetail.getDetailValue(); - } + MiddleNameDetail newDetail = null; + if (!StringUtils.isNullOrEmpty(text, true)) + newDetail = new MiddleNameDetail(text); - middleNameField.setText(middleNameDetailString); + if (middleNameDetail != null || newDetail != null) + changeDetail(middleNameDetail, newDetail); + } + if (accountInfoOpSet.isDetailClassSupported( + LastNameDetail.class)) + { + String text = + detailToTextField.get(LastNameDetail.class).getText(); + LastNameDetail newDetail = null; - // Last name details. - contactDetails = - accountInfoOpSet.getDetails(LastNameDetail.class); + if (!StringUtils.isNullOrEmpty(text, true)) + newDetail = new LastNameDetail(text); - String lastNameDetailString = ""; - while (contactDetails.hasNext()) - { - lastNameDetail = (LastNameDetail) contactDetails.next(); + if (lastNameDetail != null || newDetail != null) + changeDetail(lastNameDetail, newDetail); + } + if (accountInfoOpSet.isDetailClassSupported(NicknameDetail.class)) + { + String text = + detailToTextField.get(NicknameDetail.class).getText(); - lastNameDetailString = - lastNameDetailString + " " + lastNameDetail.getDetailValue(); - } + NicknameDetail newDetail = null; + if (!StringUtils.isNullOrEmpty(text, true)) + newDetail = new NicknameDetail(text); - lastNameField.setText(lastNameDetailString); + if (nicknameDetail != null || newDetail != null) + changeDetail(nicknameDetail, newDetail); + } - // Gender details. - contactDetails = - accountInfoOpSet.getDetails(GenderDetail.class); + if (accountInfoOpSet.isDetailClassSupported(URLDetail.class)) + { + String text + = detailToTextField.get(URLDetail.class).getText(); - String genderDetailString = ""; - while (contactDetails.hasNext()) - { - genderDetail = (GenderDetail) contactDetails.next(); - genderDetailString = genderDetailString + " " - + genderDetail.getDetailValue(); - } + URL url = null; + if (!StringUtils.isNullOrEmpty(text, true)) + try + { + url = new URL(text); + } + catch (MalformedURLException e1) + { + logger.debug("Failed to update URL detail due to " + + "malformed URL."); + } - genderField.setText(genderDetailString); + URLDetail newDetail = null; - // Birthday details. - contactDetails = - accountInfoOpSet.getDetails(BirthDateDetail.class); + if (url != null) + newDetail = new URLDetail("URL", url); - String birthDateDetailString = ""; - String ageDetail = ""; - if (contactDetails.hasNext()) - { - birthDateDetail = (BirthDateDetail) contactDetails.next(); + if (urlDetail != null || newDetail != null) + changeDetail(urlDetail, newDetail); + } - Calendar calendarDetail = - (Calendar) birthDateDetail.getDetailValue(); + if (accountInfoOpSet.isDetailClassSupported( + GenderDetail.class)) + { + String text = + detailToTextField.get(GenderDetail.class).getText(); - Date birthDate = calendarDetail.getTime(); - DateFormat dateFormat = DateFormat.getDateInstance(); + GenderDetail newDetail = null; - birthDateDetailString = dateFormat.format(birthDate).trim(); + if (!StringUtils.isNullOrEmpty(text, true)) + newDetail = new GenderDetail(text); - Calendar c = Calendar.getInstance(); - int age = c.get(Calendar.YEAR) - calendarDetail.get(Calendar.YEAR); + if (genderDetail != null || newDetail != null) + changeDetail(genderDetail, newDetail); + } - if (c.get(Calendar.MONTH) < calendarDetail.get(Calendar.MONTH)) - age--; + if (accountInfoOpSet.isDetailClassSupported( + BirthDateDetail.class)) + { + BirthDateDetail newDetail = null; - ageDetail = Integer.toString(age).trim(); - } + if (birthDayCalendar.getDate() != null) + { + newDetail = + new BirthDateDetail(birthDayCalendar.getCalendar()); + } - birthdayField.setText(birthDateDetailString); - ageField.setText(ageDetail); + if (birthDateDetail != null || newDetail != null) + changeDetail(birthDateDetail, newDetail); + } - // Email details. - contactDetails = - accountInfoOpSet.getDetails(EmailAddressDetail.class); + if (accountInfoOpSet.isDetailClassSupported( + AddressDetail.class)) + { + String text = + detailToTextField.get(AddressDetail.class).getText(); - String emailDetailString = ""; - while (contactDetails.hasNext()) - { - emailDetail = (EmailAddressDetail) contactDetails.next(); - emailDetailString = emailDetailString + " " - + emailDetail.getDetailValue(); - } + AddressDetail newDetail = null; + if (!StringUtils.isNullOrEmpty(text, true)) + newDetail = new AddressDetail(text); - emailField.setText(emailDetailString); + if (streetAddressDetail != null || newDetail != null) + changeDetail(streetAddressDetail, newDetail); + } - // Phone number details. - contactDetails = - accountInfoOpSet.getDetails(PhoneNumberDetail.class); + if (accountInfoOpSet.isDetailClassSupported( + CityDetail.class)) + { + String text = + detailToTextField.get(CityDetail.class).getText(); - String phoneNumberDetailString = ""; - while (contactDetails.hasNext()) - { - phoneDetail = (PhoneNumberDetail) contactDetails.next(); - phoneNumberDetailString = - phoneNumberDetailString + " " + phoneDetail.getDetailValue(); - } + CityDetail newDetail = null; + if (!StringUtils.isNullOrEmpty(text, true)) + newDetail = new CityDetail(text); - phoneField.setText(phoneNumberDetailString); - } + if (cityDetail != null || newDetail != null) + changeDetail(cityDetail, newDetail); + } - private class SubmitActionListener implements ActionListener - { - public void actionPerformed(ActionEvent e) - { - String firstName = firstNameField.getText(); -// String middleName = middleNameField.getText(); - String lastName = lastNameField.getText(); - String gender = genderField.getText(); - String email = emailField.getText(); - String phoneNumber = phoneField.getText(); + if (accountInfoOpSet.isDetailClassSupported( + ProvinceDetail.class)) + { + String text = + detailToTextField.get(ProvinceDetail.class).getText(); - Calendar birthDateCalendar = Calendar.getInstance(); + ProvinceDetail newDetail = null; + if (!StringUtils.isNullOrEmpty(text, true)) + newDetail = new ProvinceDetail(text); - if(birthdayField.getText() != null - && birthdayField.getText().length() > 0) + if (regionDetail != null || newDetail != null) + changeDetail(regionDetail, newDetail); + } + + if (accountInfoOpSet.isDetailClassSupported( + PostalCodeDetail.class)) { - try - { - DateFormat dateFormat - = DateFormat.getDateInstance(DateFormat.DEFAULT); + String text = + detailToTextField.get(PostalCodeDetail.class).getText(); - Date birthDate = dateFormat.parse(birthdayField.getText()); + PostalCodeDetail newDetail = null; + if (!StringUtils.isNullOrEmpty(text, true)) + newDetail = new PostalCodeDetail(text); - birthDateCalendar.setTime(birthDate); - } - catch (ParseException e2) - { - logger.error("Failed to parse birth date.", e2); - } + if (postalCodeDetail != null || newDetail != null) + changeDetail(postalCodeDetail, newDetail); } - try + if (accountInfoOpSet.isDetailClassSupported(CountryDetail.class)) { - FirstNameDetail newFirstNameDetail - = new ServerStoredDetails.FirstNameDetail(firstName); + String text = detailToTextField.get(CountryDetail.class).getText(); - if (firstNameDetail == null) - accountInfoOpSet.addDetail(newFirstNameDetail); - else - accountInfoOpSet.replaceDetail( firstNameDetail, - newFirstNameDetail); - -// MiddleNameDetail newMiddleNameDetail -// = new ServerStoredDetails.MiddleNameDetail(middleName); -// -// if (middleNameDetail == null) -// accountInfoOpSet.addDetail(newMiddleNameDetail); -// else -// accountInfoOpSet.replaceDetail( middleNameDetail, -// newMiddleNameDetail); - - LastNameDetail newLastNameDetail - = new ServerStoredDetails.LastNameDetail(lastName); - - if (lastNameDetail == null) - accountInfoOpSet.addDetail(newLastNameDetail); - else - accountInfoOpSet.replaceDetail( lastNameDetail, - newLastNameDetail); + CountryDetail newDetail = null; + if (!StringUtils.isNullOrEmpty(text, true)) + newDetail = new CountryDetail(text); - GenderDetail newGenderDetail - = new ServerStoredDetails.GenderDetail(gender); + if (countryDetail != null || newDetail != null) + changeDetail(countryDetail, newDetail); + } - if (genderDetail == null) - accountInfoOpSet.addDetail(newGenderDetail); - else - accountInfoOpSet.replaceDetail( genderDetail, - newGenderDetail); + if (accountInfoOpSet.isDetailClassSupported( + EmailAddressDetail.class)) + { + String text = + detailToTextField.get(EmailAddressDetail.class).getText(); - BirthDateDetail newBirthDateDetail - = new ServerStoredDetails.BirthDateDetail(birthDateCalendar); + EmailAddressDetail newDetail = null; + if (!StringUtils.isNullOrEmpty(text, true)) + newDetail = new EmailAddressDetail(text); - if (birthDateDetail == null) - accountInfoOpSet.addDetail(newBirthDateDetail); - else - accountInfoOpSet.replaceDetail( birthDateDetail, - newBirthDateDetail); + if (emailDetail != null || newDetail != null) + changeDetail(emailDetail, newDetail); + } - EmailAddressDetail newEmailDetail - = new ServerStoredDetails.EmailAddressDetail(email); + if (accountInfoOpSet.isDetailClassSupported( + WorkEmailAddressDetail.class)) + { + String text = + detailToTextField.get(WorkEmailAddressDetail.class) + .getText(); - if (emailDetail == null) - accountInfoOpSet.addDetail(newEmailDetail); - else - accountInfoOpSet.replaceDetail( emailDetail, - newEmailDetail); + WorkEmailAddressDetail newDetail = null; + if (!StringUtils.isNullOrEmpty(text, true)) + newDetail = new WorkEmailAddressDetail(text); - PhoneNumberDetail newPhoneDetail - = new ServerStoredDetails.PhoneNumberDetail(phoneNumber); + if (workEmailDetail != null || newDetail != null) + changeDetail(workEmailDetail, newDetail); + } - if (phoneDetail == null) - accountInfoOpSet.addDetail(newPhoneDetail); - else - accountInfoOpSet.replaceDetail( phoneDetail, - newPhoneDetail); + if (accountInfoOpSet.isDetailClassSupported( + PhoneNumberDetail.class)) + { + String text = + detailToTextField.get(PhoneNumberDetail.class).getText(); - BinaryDetail newAvatarDetail - = new ServerStoredDetails.BinaryDetail( - "Avatar", - newAvatarImage); + PhoneNumberDetail newDetail = null; + if (!StringUtils.isNullOrEmpty(text, true)) + newDetail = new PhoneNumberDetail(text); - if (avatarDetail == null) - accountInfoOpSet.addDetail(newAvatarDetail); - else - accountInfoOpSet.replaceDetail( avatarDetail, - newAvatarDetail); + if (phoneDetail != null || newDetail != null) + changeDetail(phoneDetail, newDetail); } - catch (ClassCastException e1) + if (accountInfoOpSet.isDetailClassSupported( + WorkPhoneDetail.class)) { - logger.error("Failed to update account details.", e1); + String text = + detailToTextField.get(WorkPhoneDetail.class).getText(); + + WorkPhoneDetail newDetail = null; + if (!StringUtils.isNullOrEmpty(text, true)) + newDetail = new WorkPhoneDetail(text); + + if (workPhoneDetail != null || newDetail != null) + changeDetail(workPhoneDetail, newDetail); } - catch (OperationFailedException e1) + if (accountInfoOpSet.isDetailClassSupported( + WorkOrganizationNameDetail.class)) { - logger.error("Failed to update account details.", e1); - } - } - } + String text = + detailToTextField.get(WorkOrganizationNameDetail.class) + .getText(); - private class ChangeAvatarActionListener implements ActionListener - { - public void actionPerformed(ActionEvent e) - { - SipCommFileChooser chooser = GenericFileDialog.create( - null, "Change avatar...", - SipCommFileChooser.LOAD_FILE_OPERATION, - lastAvatarDir.getAbsolutePath()); - chooser.addFilter(new ImageFilter()); + WorkOrganizationNameDetail newDetail = null; + if (!StringUtils.isNullOrEmpty(text, true)) + newDetail = new WorkOrganizationNameDetail(text); - File file = chooser.getFileFromDialog(); + if (organizationDetail != null || newDetail != null) + changeDetail(organizationDetail, newDetail); + } - if (file != null) + if (accountInfoOpSet.isDetailClassSupported(JobTitleDetail.class)) { - try - { - lastAvatarDir = file.getParentFile(); + String text = + detailToTextField.get(JobTitleDetail.class) + .getText(); - FileInputStream in = new FileInputStream(file); - byte[] buffer; + JobTitleDetail newDetail = null; + if (!StringUtils.isNullOrEmpty(text, true)) + newDetail = new JobTitleDetail(text); - try - { - buffer = new byte[in.available()]; - in.read(buffer); - } - finally - { - in.close(); - } - if (buffer == null || buffer.length <= 0) - return; + if (jobTitleDetail != null || newDetail != null) + changeDetail(jobTitleDetail, newDetail); + } + if (accountInfoOpSet.isDetailClassSupported(AboutMeDetail.class)) + { + String text = + aboutMeArea.getText(); - newAvatarImage = buffer; + AboutMeDetail newDetail = null; + if (!StringUtils.isNullOrEmpty(text, true)) + newDetail = new AboutMeDetail(text); - avatarImageIcon = new ImageIcon( - getScaledImageInstance(newAvatarImage)); - avatarLabel.setIcon(avatarImageIcon); - } - catch (IOException ex) - { - logger.error("Failed to load image.", ex); - } + if (aboutMeDetail != null || newDetail != null) + changeDetail(aboutMeDetail, newDetail); + } + + try + { + AccountInfoMenuItemComponent.dialog.setVisible(false); + mainScrollPane.getVerticalScrollBar().setValue(0); + accountInfoOpSet.save(); + } + catch (OperationFailedException e1) + { + logger.debug("Failed to update account details.", e1); } } - } - /** - * A custom filter that would accept only image files. - */ - private static class ImageFilter extends SipCommFileFilter - { /** - * Accept all directories and all gif, jpg, tiff, or png files. - * Method implemented from FileFilter abstract class. - * - * @param f a file to accept or not + * A helper method to decide whether to add new + * <tt>ServerStoredDetails</tt> or to replace an old one. + * @param oldDetail the detail to be replaced. + * @param newDetail the replacement. */ - @Override - public boolean accept(File f) + private void changeDetail( GenericDetail oldDetail, + GenericDetail newDetail) { - if (f.isDirectory()) - { - return true; - } - - String extension = getExtension(f); - if (extension != null) + try { - if (extension.equals("tiff") || - extension.equals("tif") || - extension.equals("gif") || - extension.equals("jpeg") || - extension.equals("jpg") || - extension.equals("png")) + if (newDetail == null) + { + accountInfoOpSet.removeDetail(oldDetail); + } + else if (oldDetail == null) { - return true; + accountInfoOpSet.addDetail(newDetail); } else { - return false; + accountInfoOpSet.replaceDetail(oldDetail, newDetail); } } - - return false; - } - - /** - * Get the extension of a file. - * - * @param f the file - * @return the extension of the file - */ - public String getExtension(File f) - { - String ext = null; - String s = f.getName(); - int i = s.lastIndexOf('.'); - - if (i > 0 && i < s.length() - 1) { - ext = s.substring(i+1).toLowerCase(); + catch (ArrayIndexOutOfBoundsException e1) + { + logger.debug("Failed to update account details. " + + newDetail.getDetailDisplayName() + " " + e1); + } + catch (OperationFailedException e1) + { + logger.debug("Failed to update account details. " + + newDetail.getDetailDisplayName() + " " + e1); } - return ext; - } - - /** - * The description of this filter. - */ - @Override - public String getDescription() - { - return Resources.getString("plugin.accountinfo.ONLY_MESSAGE"); - } - - } - - /** - * Returns a scaled <tt>Image</tt> instance of the given byte image. - * - * @param image the image in bytes - * @return a scaled <tt>Image</tt> instance of the given byte image. - */ - private Image getScaledImageInstance(byte[] image) - { - Image resultImage = null; - - try - { - resultImage = ImageIO.read( - new ByteArrayInputStream(image)); - } - catch (Exception e) - { - logger.error("Failed to convert bytes to image.", e); } - - if(resultImage == null) - return null; - - return resultImage.getScaledInstance( - avatarLabel.getWidth(), - avatarLabel.getHeight(), - Image.SCALE_SMOOTH); - } - - /** - * Returns <code>true</code> if the account details are loaded, - * <code>false</code> - otherwise. - * - * @return <code>true</code> if the account details are loaded, - * <code>false</code> - otherwise - */ - public boolean isDataLoaded() - { - return isDataLoaded; - } - - /** - * Loads the avatar default icon. - */ - public void loadSkin() - { - avatarLabel.setIcon(Resources.getImage("accountInfoDefaultPersonIcon")); } } diff --git a/src/net/java/sip/communicator/plugin/accountinfo/AccountInfoActivator.java b/src/net/java/sip/communicator/plugin/accountinfo/AccountInfoActivator.java index 9f34c9c..6e1692b 100644 --- a/src/net/java/sip/communicator/plugin/accountinfo/AccountInfoActivator.java +++ b/src/net/java/sip/communicator/plugin/accountinfo/AccountInfoActivator.java @@ -8,7 +8,8 @@ package net.java.sip.communicator.plugin.accountinfo; import java.util.*;
-import net.java.sip.communicator.service.browserlauncher.*;
+import net.java.sip.communicator.service.globaldisplaydetails.*;
+import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
@@ -18,6 +19,7 @@ import org.osgi.framework.*; * Starts the account info bundle.
*
* @author Adam Glodstein
+ * @author Marin Dzhigarov
*/
public class AccountInfoActivator
implements BundleActivator
@@ -30,21 +32,52 @@ public class AccountInfoActivator */
public static BundleContext bundleContext;
- private static BrowserLauncherService browserLauncherService;
+ private static GlobalDisplayDetailsService globalDisplayDetailsService;
public void start(BundleContext bc) throws Exception
{
AccountInfoActivator.bundleContext = bc;
-// new LazyConfigurationForm(
-// "net.java.sip.communicator.plugin.accountinfo.AccountInfoPanel",
-// getClass().getClassLoader(), "plugin.accountinfo.PLUGIN_ICON",
-// "plugin.accountinfo.TITLE");
+ Hashtable<String, String> containerFilter
+ = new Hashtable<String, String>();
+ containerFilter.put(
+ Container.CONTAINER_ID,
+ Container.CONTAINER_TOOLS_MENU.getID());
+
+ bundleContext.registerService(
+ PluginComponentFactory.class.getName(),
+ new PluginComponentFactory(Container.CONTAINER_TOOLS_MENU)
+ {
+ @Override
+ protected PluginComponent getPluginInstance()
+ {
+ return new AccountInfoMenuItemComponent(
+ getContainer(), this);
+ }
+ },
+ containerFilter);
+
+ containerFilter = new Hashtable<String, String>();
+ containerFilter.put(
+ Container.CONTAINER_ID,
+ Container.CONTAINER_ACCOUNT_RIGHT_BUTTON_MENU.getID());
+
+ bundleContext.registerService(
+ PluginComponentFactory.class.getName(),
+ new PluginComponentFactory(
+ Container.CONTAINER_ACCOUNT_RIGHT_BUTTON_MENU)
+ {
+ @Override
+ protected PluginComponent getPluginInstance()
+ {
+ return new AccountInfoMenuItemComponent(
+ getContainer(), this);
+ }
+ },
+ containerFilter);
}
- public void stop(BundleContext bc) throws Exception
- {
- }
+ public void stop(BundleContext bc) throws Exception {}
/**
* Returns all <tt>ProtocolProviderFactory</tt>s obtained from the bundle
@@ -53,7 +86,8 @@ public class AccountInfoActivator * @return all <tt>ProtocolProviderFactory</tt>s obtained from the bundle
* context
*/
- public static Map<Object, ProtocolProviderFactory> getProtocolProviderFactories()
+ public static Map<Object, ProtocolProviderFactory>
+ getProtocolProviderFactories()
{
Map<Object, ProtocolProviderFactory> providerFactoriesMap =
new Hashtable<Object, ProtocolProviderFactory>();
@@ -87,23 +121,21 @@ public class AccountInfoActivator }
/**
- * Returns the <tt>BrowserLauncherService</tt> currently registered.
+ * Returns the <tt>GlobalDisplayDetailsService</tt> obtained from the bundle
+ * context.
*
- * @return the <tt>BrowserLauncherService</tt>
+ * @return the <tt>GlobalDisplayDetailsService</tt> obtained from the bundle
+ * context
*/
- public static BrowserLauncherService getBrowserLauncher()
+ public static GlobalDisplayDetailsService getGlobalDisplayDetailsService()
{
- if (browserLauncherService == null)
+ if (globalDisplayDetailsService == null)
{
- ServiceReference serviceReference =
- bundleContext.getServiceReference(BrowserLauncherService.class
- .getName());
-
- browserLauncherService =
- (BrowserLauncherService) bundleContext
- .getService(serviceReference);
+ globalDisplayDetailsService
+ = ServiceUtils.getService(
+ bundleContext,
+ GlobalDisplayDetailsService.class);
}
-
- return browserLauncherService;
+ return globalDisplayDetailsService;
}
}
diff --git a/src/net/java/sip/communicator/plugin/accountinfo/AccountInfoMenuItemComponent.java b/src/net/java/sip/communicator/plugin/accountinfo/AccountInfoMenuItemComponent.java new file mode 100644 index 0000000..7ce1ad6 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/accountinfo/AccountInfoMenuItemComponent.java @@ -0,0 +1,119 @@ +/* + * 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.accountinfo; + +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.service.protocol.*; + +/** + * Implements <tt>PluginComponent</tt> for the "Account Info" menu item. + * + * @author Marin Dzhigarov + */ +public class AccountInfoMenuItemComponent + extends AbstractPluginComponent +{ + /** + * The "Account Info" menu item. + */ + JMenuItem accountInfoMenuItem; + + /** + * The dialog that appears when "Account Info" menu item is clicked. + */ + static final SIPCommDialog dialog = new SIPCommDialog() { + /** + * Serial version UID. + */ + private static final long serialVersionUID = 1L; + + /** + * Presses programmatically the cancel button, when Esc key is pressed. + * + * @param isEscaped indicates if the Esc button was pressed on close + */ + protected void close(boolean isEscaped) + { + this.setVisible(false); + } + }; + + /** + * The main panel containing account information. + */ + static final AccountInfoPanel accountInfoPanel = new AccountInfoPanel(); + + /** + * Initializes a new "Account Info" menu item. + * + * @param container the container of the update menu component + */ + public AccountInfoMenuItemComponent(Container container, + PluginComponentFactory parentFactory) + { + super(container, parentFactory); + + AccountInfoActivator.bundleContext.addServiceListener(accountInfoPanel); + + dialog.setPreferredSize(new java.awt.Dimension(600, 400)); + dialog.setTitle(Resources.getString("plugin.accountinfo.TITLE")); + dialog.add(accountInfoPanel); + } + + public void setCurrentAccountID(AccountID accountID) + { + accountInfoMenuItem.setEnabled( + accountID != null && accountID.isEnabled()); + accountInfoPanel.getAccountsComboBox().setSelectedItem( + accountInfoPanel.getAccountsTable().get(accountID)); + } + + /** + * Gets the UI <tt>Component</tt> of this <tt>PluginComponent</tt>. + * + * @return the UI <tt>Component</tt> of this <tt>PluginComponent</tt> + * @see PluginComponent#getComponent() + */ + public Object getComponent() + { + if(accountInfoMenuItem == null) + { + accountInfoMenuItem + = new JMenuItem( + Resources.getString("plugin.accountinfo.TITLE")); + accountInfoMenuItem.setIcon( + Resources.getImage( + "plugin.contactinfo.CONTACT_INFO_ICON")); + accountInfoMenuItem.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + dialog.setVisible(true); + accountInfoPanel.setVisible(true); + } + }); + } + return accountInfoMenuItem; + } + + /** + * Gets the name of this <tt>PluginComponent</tt>. + * + * @return the name of this <tt>PluginComponent</tt> + * @see PluginComponent#getName() + */ + public String getName() + { + return + Resources.getString("plugin.accountinfo.TITLE"); + } +} diff --git a/src/net/java/sip/communicator/plugin/accountinfo/AccountInfoPanel.java b/src/net/java/sip/communicator/plugin/accountinfo/AccountInfoPanel.java index 99d8c40..4e052c5 100644 --- a/src/net/java/sip/communicator/plugin/accountinfo/AccountInfoPanel.java +++ b/src/net/java/sip/communicator/plugin/accountinfo/AccountInfoPanel.java @@ -6,6 +6,7 @@ package net.java.sip.communicator.plugin.accountinfo;
import java.awt.*;
+import java.awt.event.*;
import java.util.*;
import javax.swing.*;
@@ -17,13 +18,15 @@ import net.java.sip.communicator.service.protocol.event.*; import org.osgi.framework.*;
/**
- * A GUI plug-in for SIP Communicator that will allow users to set cross
+ * A GUI plug-in for Jitsi that will allow users to set cross
* protocol account information.
*
* @author Adam Goldstein
+ * @author Marin Dzhigarov
*/
public class AccountInfoPanel
extends TransparentPanel
+ implements ServiceListener
{
/**
* Serial version UID.
@@ -31,25 +34,86 @@ public class AccountInfoPanel private static final long serialVersionUID = 0L;
/**
- * The right side of the AccountInfo frame that contains protocol specific
- * account details.
+ * The panel that contains the currently active <tt>AccountDetailsPanel</tt>
*/
- private AccountDetailsPanel detailsPanel;
+ private final JPanel centerPanel =
+ new TransparentPanel(new BorderLayout(10, 10));
- private final Map<ProtocolProviderService, AccountDetailsPanel> accountsTable =
- new Hashtable<ProtocolProviderService, AccountDetailsPanel>();
+ /**
+ * The currently active <tt>AccountDetailsPanel</tt>
+ */
+ private AccountDetailsPanel currentDetailsPanel;
+
+ /**
+ * Combo box that is used for switching between accounts.
+ */
+ private final JComboBox accountsComboBox;
/**
- * Constructs a frame with an AccuontInfoAccountPanel to display all
- * registered accounts on the left, and an information interface,
- * AccountDetailsPanel, on the right.
+ * Instances of the <tt>AccountDetailsPanel</tt> are created for every
+ * registered <tt>AccountID</tt>. All such pairs are stored in
+ * this map.
+ */
+ private final Map<AccountID, AccountDetailsPanel>
+ accountsTable =
+ new HashMap<AccountID, AccountDetailsPanel>();
+
+ /**
+ * Creates an instance of <tt>AccountInfoPanel</tt> that contains combo box
+ * component with active user accounts and <tt>AccountDetailsPanel</tt> to
+ * display and edit account information.
*/
public AccountInfoPanel()
{
- super(new BorderLayout());
+ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+
+ accountsComboBox = new JComboBox();
+ accountsComboBox.addItemListener(new ItemListener()
+ {
+ @Override
+ public void itemStateChanged(ItemEvent e)
+ {
+ if (e.getStateChange() == ItemEvent.SELECTED)
+ {
+ AccountDetailsPanel panel =
+ (AccountDetailsPanel) e.getItem();
+ panel.setOpaque(false);
+ centerPanel.removeAll();
+ centerPanel.add(panel, BorderLayout.CENTER);
+ centerPanel.revalidate();
+ centerPanel.repaint();
+ currentDetailsPanel = panel;
+ }
+ }
+ });
+
+ init();
+
+ centerPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+ ComboBoxRenderer renderer = new ComboBoxRenderer();
+ accountsComboBox.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
+ accountsComboBox.setRenderer(renderer);
+
+ JLabel comboLabel = new JLabel(
+ Resources.getString(
+ "plugin.accountinfo.SELECT_ACCOUNT"));
+ comboLabel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
- JTabbedPane accountsTabbedPane = new SIPCommTabbedPane();
+ JPanel comboBoxPanel = new TransparentPanel();
+ comboBoxPanel.setLayout(new BoxLayout(comboBoxPanel, BoxLayout.X_AXIS));
+ comboBoxPanel.setBorder(
+ BorderFactory.createEmptyBorder(10, 10, 10, 10));
+ comboBoxPanel.add(comboLabel);
+ comboBoxPanel.add(accountsComboBox);
+
+ add(comboBoxPanel);
+ add(centerPanel);
+ }
+
+ private void init()
+ {
for (ProtocolProviderFactory providerFactory : AccountInfoActivator
.getProtocolProviderFactories().values())
{
@@ -63,22 +127,56 @@ public class AccountInfoPanel {
serRef = providerFactory.getProviderForAccount(accountID);
- protocolProvider = (ProtocolProviderService) AccountInfoActivator
+ protocolProvider = (ProtocolProviderService)AccountInfoActivator
.bundleContext.getService(serRef);
- detailsPanel = new AccountDetailsPanel(protocolProvider);
+ currentDetailsPanel = new AccountDetailsPanel(protocolProvider);
+
+ accountsTable.put(
+ protocolProvider.getAccountID(), currentDetailsPanel);
- accountsTable.put(protocolProvider, detailsPanel);
+ accountsComboBox.addItem(currentDetailsPanel);
protocolProvider.addRegistrationStateChangeListener(
new RegistrationStateChangeListenerImpl());
-
- accountsTabbedPane.addTab(
- accountID.getUserID(), detailsPanel);
}
}
+ }
+
+ /**
+ * A custom renderer to display properly <tt>AccountDetailsPanel</tt>
+ * in a combo box.
+ */
+ private class ComboBoxRenderer extends DefaultListCellRenderer
+ {
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
- this.add(accountsTabbedPane, BorderLayout.CENTER);
+ @Override
+ public Component getListCellRendererComponent(
+ JList list, Object value, int index,
+ boolean isSelected, boolean hasFocus)
+ {
+ JLabel renderer
+ = (JLabel) super.getListCellRendererComponent(
+ list, value, index, isSelected, hasFocus);
+
+ if (value != null)
+ {
+ AccountDetailsPanel panel = (AccountDetailsPanel) value;
+
+ renderer.setText(
+ panel.protocolProvider.getAccountID().getUserID());
+ ImageIcon protocolIcon =
+ new ImageIcon(panel.protocolProvider.getProtocolIcon().
+ getIcon((ProtocolIcon.ICON_SIZE_16x16)));
+ renderer.setIcon(protocolIcon);
+ }
+
+ return renderer;
+ }
}
private class RegistrationStateChangeListenerImpl
@@ -88,19 +186,117 @@ public class AccountInfoPanel {
ProtocolProviderService protocolProvider = evt.getProvider();
- if (protocolProvider.getOperationSet(
- OperationSetServerStoredAccountInfo.class) != null
- && evt.getNewState() == RegistrationState.REGISTERED)
+ if (evt.getNewState() == RegistrationState.REGISTERED)
{
- if (accountsTable.containsKey(protocolProvider))
+ if (accountsTable.containsKey(protocolProvider.getAccountID()))
{
AccountDetailsPanel detailsPanel
- = accountsTable.get(protocolProvider);
+ = accountsTable.get(protocolProvider.getAccountID());
+ detailsPanel.loadDetails();
+ }
+ else
+ {
+ AccountDetailsPanel panel =
+ new AccountDetailsPanel(protocolProvider);
+ accountsTable.put(protocolProvider.getAccountID(), panel);
+ accountsComboBox.addItem(panel);
+ }
+ }
+ else if (evt.getNewState() == RegistrationState.UNREGISTERING)
+ {
+ AccountDetailsPanel panel
+ = accountsTable.get(protocolProvider.getAccountID());
+ if (panel != null)
+ {
+ accountsTable.remove(protocolProvider.getAccountID());
+ accountsComboBox.removeItem(panel);
+ if (currentDetailsPanel == panel)
+ {
+ currentDetailsPanel = null;
+ centerPanel.removeAll();
+ centerPanel.revalidate();
+ centerPanel.repaint();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Handles registration and unregistration of
+ * <tt>ProtocolProviderService</tt>
+ *
+ * @param event
+ */
+ @Override
+ public void serviceChanged(ServiceEvent event)
+ {
+ // Get the service from the event.
+ Object service
+ = AccountInfoActivator.bundleContext.getService(
+ event.getServiceReference());
- if(!detailsPanel.isDataLoaded())
- detailsPanel.loadDetails();
+ // We are not interested in any services
+ // other than ProtocolProviderService
+ if (!(service instanceof ProtocolProviderService))
+ return;
+
+ ProtocolProviderService protocolProvider =
+ (ProtocolProviderService) service;
+
+ // If a new protocol provider is registered we to add new
+ // AccountDetailsPanel to the combo box containing active accounts.
+ if (event.getType() == ServiceEvent.REGISTERED)
+ {
+ if (accountsTable.get(protocolProvider.getAccountID()) == null)
+ {
+ AccountDetailsPanel panel =
+ new AccountDetailsPanel(protocolProvider);
+ accountsTable.put(protocolProvider.getAccountID(), panel);
+ accountsComboBox.addItem(panel);
+ protocolProvider.addRegistrationStateChangeListener(
+ new RegistrationStateChangeListenerImpl());
+ }
+ }
+ // If the protocol provider is being unregistered we have to remove
+ // a AccountDetailsPanel from the combo box containing active accounts.
+ else if (event.getType() == ServiceEvent.UNREGISTERING)
+ {
+ AccountDetailsPanel panel
+ = accountsTable.get(protocolProvider.getAccountID());
+ if (panel != null)
+ {
+ accountsTable.remove(protocolProvider.getAccountID());
+ accountsComboBox.removeItem(panel);
+ if (currentDetailsPanel == panel)
+ {
+ currentDetailsPanel = null;
+ centerPanel.removeAll();
+ centerPanel.revalidate();
+ centerPanel.repaint();
}
}
}
}
+
+ /**
+ * Returns the combo box that switches between account detail panels.
+ *
+ * @return The combo box that switches between account detail panels.
+ */
+ public JComboBox getAccountsComboBox()
+ {
+ return accountsComboBox;
+ }
+
+ /**
+ * Returns mapping between registered AccountIDs and their respective
+ * AccountDetailsPanel that contains all the details for the account.
+ *
+ * @return mapping between registered AccountIDs and AccountDetailsPanel.
+ */
+ public Map<AccountID, AccountDetailsPanel> getAccountsTable()
+ {
+ return accountsTable;
+ }
}
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 3426abd..fca236b 100644 --- a/src/net/java/sip/communicator/plugin/accountinfo/accountinfo.manifest.mf +++ b/src/net/java/sip/communicator/plugin/accountinfo/accountinfo.manifest.mf @@ -5,27 +5,19 @@ Bundle-Vendor: jitsi.org Bundle-Version: 0.0.1
System-Bundle: yes
Import-Package: org.osgi.framework,
- net.java.sip.communicator.service.browserlauncher,
- net.java.sip.communicator.service.contactlist,
- net.java.sip.communicator.service.contactlist.event,
net.java.sip.communicator.service.gui,
- net.java.sip.communicator.service.gui.event,
+ net.java.sip.communicator.service.globaldisplaydetails,
net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.event,
- org.jitsi.service.resources, net.java.sip.communicator.service.resources,
+ org.jitsi.service.resources, + org.jitsi.util, + net.java.sip.communicator.service.resources,
net.java.sip.communicator.util,
+ net.java.sip.communicator.util.skin,
net.java.sip.communicator.plugin.desktoputil,
+ net.java.sip.communicator.plugin.desktoputil.presence.avatar,
javax.swing,
javax.swing.event,
- javax.swing.table,
+ javax.swing.border,
javax.swing.text,
- javax.swing.text.html,
- javax.accessibility,
- javax.swing.plaf,
- javax.swing.plaf.metal,
- javax.swing.plaf.basic,
- javax.imageio,
- javax.swing.filechooser,
- javax.swing.tree,
- javax.swing.undo,
- javax.swing.border
+ javax.imageio
diff --git a/src/net/java/sip/communicator/plugin/desktoputil/DesktopUtilActivator.java b/src/net/java/sip/communicator/plugin/desktoputil/DesktopUtilActivator.java index de92b28..23bb10d 100644 --- a/src/net/java/sip/communicator/plugin/desktoputil/DesktopUtilActivator.java +++ b/src/net/java/sip/communicator/plugin/desktoputil/DesktopUtilActivator.java @@ -12,10 +12,14 @@ import net.java.sip.communicator.service.certificate.*; import net.java.sip.communicator.service.credentialsstorage.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.keybindings.*; +import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.resources.*; import net.java.sip.communicator.util.*; +import org.jitsi.service.audionotifier.*; import org.jitsi.service.configuration.*; +import org.jitsi.service.fileaccess.*; +import org.jitsi.service.neomedia.*; import org.jitsi.service.resources.*; import org.osgi.framework.*; @@ -40,6 +44,14 @@ public class DesktopUtilActivator private static UIService uiService; + private static AccountManager accountManager; + + private static FileAccessService fileAccessService; + + private static MediaService mediaService; + + private static AudioNotifierService audioNotifierService; + static BundleContext bundleContext; /** @@ -247,4 +259,69 @@ public class DesktopUtilActivator { return new VerifyCertificateDialogImpl(certs, title, message); } + + /** + * Returns the <tt>AccountManager</tt> obtained from the bundle context. + * @return the <tt>AccountManager</tt> obtained from the bundle context + */ + public static AccountManager getAccountManager() + { + if(accountManager == null) + { + accountManager + = ServiceUtils.getService(bundleContext, AccountManager.class); + } + return accountManager; + } + + /** + * Returns the <tt>FileAccessService</tt> obtained from the bundle context. + * + * @return the <tt>FileAccessService</tt> obtained from the bundle context + */ + public static FileAccessService getFileAccessService() + { + if (fileAccessService == null) + { + fileAccessService + = ServiceUtils.getService( + bundleContext, + FileAccessService.class); + } + return fileAccessService; + } + + /** + * Returns an instance of the <tt>MediaService</tt> obtained from the + * bundle context. + * @return an instance of the <tt>MediaService</tt> obtained from the + * bundle context + */ + public static MediaService getMediaService() + { + if (mediaService == null) + { + mediaService + = ServiceUtils.getService(bundleContext, MediaService.class); + } + return mediaService; + } + + /** + * Returns the <tt>AudioNotifierService</tt> obtained from the bundle + * context. + * @return the <tt>AudioNotifierService</tt> obtained from the bundle + * context + */ + public static AudioNotifierService getAudioNotifier() + { + if (audioNotifierService == null) + { + audioNotifierService + = ServiceUtils.getService( + bundleContext, + AudioNotifierService.class); + } + return audioNotifierService; + } }
\ No newline at end of file diff --git a/src/net/java/sip/communicator/plugin/desktoputil/FramedImage.java b/src/net/java/sip/communicator/plugin/desktoputil/FramedImage.java index a60df48..de85557 100644 --- a/src/net/java/sip/communicator/plugin/desktoputil/FramedImage.java +++ b/src/net/java/sip/communicator/plugin/desktoputil/FramedImage.java @@ -142,14 +142,17 @@ public class FramedImage 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); + if (frameImage != 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); + } } /** @@ -157,14 +160,21 @@ public class FramedImage */ public void loadSkin() { - this.frameImage - = ImageUtils - .scaleImageWithinBounds( - DesktopUtilActivator - .getResources() - .getImage("service.gui.USER_PHOTO_FRAME").getImage(), - width, - height); + ImageIcon frameIcon = DesktopUtilActivator + .getResources() + .getImage("service.gui.USER_PHOTO_FRAME"); + + // Frame image will be drawn only if's bigger or equal to the underlying + // image. We would like to avoid pixelated results! + if (frameIcon.getIconWidth() >= width + && frameIcon.getIconHeight() >= frameIcon.getIconHeight()) + { + this.frameImage + = ImageUtils + .scaleImageWithinBounds(frameIcon.getImage(), + width, + height); + } } /** diff --git a/src/net/java/sip/communicator/plugin/desktoputil/FramedImageWithMenu.java b/src/net/java/sip/communicator/plugin/desktoputil/FramedImageWithMenu.java index f50ca68..4262e3b 100644 --- a/src/net/java/sip/communicator/plugin/desktoputil/FramedImageWithMenu.java +++ b/src/net/java/sip/communicator/plugin/desktoputil/FramedImageWithMenu.java @@ -35,11 +35,6 @@ public class FramedImageWithMenu private JPopupMenu popupMenu; /** - * The parent frame. - */ - private JFrame mainFrame; - - /** * Should we currently draw overlay. */ private boolean drawOverlay = false; @@ -62,14 +57,12 @@ public class FramedImageWithMenu * @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); } @@ -189,9 +182,9 @@ public class FramedImageWithMenu if (show) { Point imageLoc = this.getLocationOnScreen(); - Point rootPaneLoc = mainFrame.getRootPane().getLocationOnScreen(); + Point rootPaneLoc = this.getRootPane().getLocationOnScreen(); - this.popupMenu.setSize(mainFrame.getRootPane().getWidth(), + this.popupMenu.setSize(this.getRootPane().getWidth(), this.popupMenu.getHeight()); this.popupMenu.show(this, (rootPaneLoc.x - imageLoc.x), @@ -206,7 +199,7 @@ public class FramedImageWithMenu public void mouseEntered(MouseEvent e) { - if (this.drawOverlay) + if (this.drawOverlay || !this.isEnabled()) return; this.drawOverlay = true; @@ -222,7 +215,7 @@ public class FramedImageWithMenu public void mouseExited(MouseEvent e) { // Remove overlay only if the dialog isn't visible - if (!popupMenu.isVisible()) + if (!popupMenu.isVisible() && this.isEnabled()) { this.drawOverlay = false; this.repaint(); @@ -231,7 +224,8 @@ public class FramedImageWithMenu public void mouseReleased(MouseEvent e) { - showDialog(e, !popupMenu.isVisible()); + if (this.isEnabled()) + showDialog(e, !popupMenu.isVisible()); } /** diff --git a/src/net/java/sip/communicator/plugin/desktoputil/desktoputil.manifest.mf b/src/net/java/sip/communicator/plugin/desktoputil/desktoputil.manifest.mf index 56edae3..4b91013 100644 --- a/src/net/java/sip/communicator/plugin/desktoputil/desktoputil.manifest.mf +++ b/src/net/java/sip/communicator/plugin/desktoputil/desktoputil.manifest.mf @@ -30,6 +30,7 @@ Import-Package: com.sun.awt, javax.xml.transform.dom, javax.xml.transform.stream, net.java.sip.communicator.util, + net.java.sip.communicator.util.account, net.java.sip.communicator.util.skin, net.java.sip.communicator.util.wizard, net.java.sip.communicator.service.certificate, @@ -64,5 +65,6 @@ Export-Package: net.java.sip.communicator.plugin.desktoputil, net.java.sip.communicator.plugin.desktoputil.event, net.java.sip.communicator.plugin.desktoputil.plaf, net.java.sip.communicator.plugin.desktoputil.presence, + net.java.sip.communicator.plugin.desktoputil.presence.avatar, net.java.sip.communicator.plugin.desktoputil.transparent, net.java.sip.communicator.plugin.desktoputil.wizard diff --git a/src/net/java/sip/communicator/impl/gui/main/presence/avatar/AvatarStackManager.java b/src/net/java/sip/communicator/plugin/desktoputil/presence/avatar/AvatarStackManager.java index 948716e..3a81a24 100644 --- a/src/net/java/sip/communicator/impl/gui/main/presence/avatar/AvatarStackManager.java +++ b/src/net/java/sip/communicator/plugin/desktoputil/presence/avatar/AvatarStackManager.java @@ -4,18 +4,18 @@ * Distributable under LGPL license. * See terms of license at gnu.org. */ -package net.java.sip.communicator.impl.gui.main.presence.avatar; +package net.java.sip.communicator.plugin.desktoputil.presence.avatar; import java.awt.image.*; import java.io.*; import javax.imageio.*; -import org.jitsi.service.fileaccess.*; - -import net.java.sip.communicator.impl.gui.*; +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.util.*; +import org.jitsi.service.fileaccess.*; + /** * Take cares of storing(deleting, moving) images with the given indexes. */ @@ -44,8 +44,9 @@ public class AvatarStackManager try { File imageFile - = GuiActivator.getFileAccessService().getPrivatePersistentFile( - fileName, FileCategory.CACHE); + = DesktopUtilActivator.getFileAccessService() + .getPrivatePersistentFile( + fileName); if (imageFile.exists() && !imageFile.delete()) logger.error("Failed to delete stored image at index " + index); @@ -71,8 +72,9 @@ public class AvatarStackManager String imagePath = STORE_DIR + index + ".png"; imageFile - = GuiActivator.getFileAccessService().getPrivatePersistentFile( - imagePath, FileCategory.CACHE); + = DesktopUtilActivator.getFileAccessService(). + getPrivatePersistentFile( + imagePath); } catch (Exception e) { @@ -107,14 +109,12 @@ public class AvatarStackManager try { - FileAccessService fas = GuiActivator.getFileAccessService(); - File oldFile = fas.getPrivatePersistentFile(oldImagePath, - FileCategory.CACHE); + FileAccessService fas = DesktopUtilActivator.getFileAccessService(); + File oldFile = fas.getPrivatePersistentFile(oldImagePath); if (oldFile.exists()) { - File newFile = fas.getPrivatePersistentFile(newImagePath, - FileCategory.CACHE); + File newFile = fas.getPrivatePersistentFile(newImagePath); oldFile.renameTo(newFile); } @@ -148,15 +148,13 @@ public class AvatarStackManager try { - FileAccessService fas = GuiActivator.getFileAccessService(); - File storeDir = fas.getPrivatePersistentDirectory(STORE_DIR, - FileCategory.CACHE); + FileAccessService fas = DesktopUtilActivator.getFileAccessService(); + File storeDir = fas.getPrivatePersistentDirectory(STORE_DIR); // if dir doesn't exist create it storeDir.mkdirs(); - File file = fas.getPrivatePersistentFile(imagePath, - FileCategory.CACHE); + File file = fas.getPrivatePersistentFile(imagePath); ImageIO.write(image, "png", file); } diff --git a/src/net/java/sip/communicator/impl/gui/main/presence/avatar/SelectAvatarMenu.java b/src/net/java/sip/communicator/plugin/desktoputil/presence/avatar/SelectAvatarMenu.java index 01c70aa..7b00fcc 100644 --- a/src/net/java/sip/communicator/impl/gui/main/presence/avatar/SelectAvatarMenu.java +++ b/src/net/java/sip/communicator/plugin/desktoputil/presence/avatar/SelectAvatarMenu.java @@ -4,18 +4,20 @@ * Distributable under LGPL license. * See terms of license at gnu.org. */ -package net.java.sip.communicator.impl.gui.main.presence.avatar; +package net.java.sip.communicator.plugin.desktoputil.presence.avatar; import java.awt.*; import java.awt.event.*; import java.awt.image.*; +import java.util.*; import javax.swing.*; -import net.java.sip.communicator.impl.gui.*; -import net.java.sip.communicator.impl.gui.main.presence.avatar.imagepicker.*; import net.java.sip.communicator.plugin.desktoputil.*; +import net.java.sip.communicator.plugin.desktoputil.presence.avatar.imagepicker.*; import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.ServerStoredDetails.GenericDetail; +import net.java.sip.communicator.service.protocol.ServerStoredDetails.ImageDetail; import net.java.sip.communicator.util.*; import net.java.sip.communicator.util.account.*; @@ -86,6 +88,12 @@ public class SelectAvatarMenu private FramedImageWithMenu avatarImage; /** + * The AccountID that we want to select avatar for. Could be null if + * we want to select a global avatar. + */ + private AccountID accountID; + + /** * Creates the dialog. * @param avatarImage the button that will trigger this menu. */ @@ -98,6 +106,11 @@ public class SelectAvatarMenu this.pack(); } + public void setAccountID(AccountID accountID) + { + this.accountID = accountID; + } + /** * Init visible components. */ @@ -108,7 +121,7 @@ public class SelectAvatarMenu panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); // Title label - JLabel titleLabel = new JLabel(GuiActivator.getResources() + JLabel titleLabel = new JLabel(DesktopUtilActivator.getResources() .getI18NString("service.gui.avatar.RECENT_ICONS")); titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD)); @@ -146,17 +159,17 @@ public class SelectAvatarMenu Color linkColor = new JMenuItem().getForeground(); addActionButton(buttonsPanel, this, - GuiActivator.getResources().getI18NString( + DesktopUtilActivator.getResources().getI18NString( "service.gui.avatar.CHOOSE_ICON"), CHOSE_BUTTON_NAME, linkColor); addActionButton(buttonsPanel, this, - GuiActivator.getResources().getI18NString( + DesktopUtilActivator.getResources().getI18NString( "service.gui.avatar.REMOVE_ICON"), REMOVE_BUTTON_NAME, linkColor); addActionButton(buttonsPanel, this, - GuiActivator.getResources().getI18NString( + DesktopUtilActivator.getResources().getI18NString( "service.gui.avatar.CLEAR_RECENT"), CLEAR_BUTTON_NAME, linkColor); @@ -267,11 +280,11 @@ public class SelectAvatarMenu public void run() { AccountManager accountManager - = GuiActivator.getAccountManager(); + = DesktopUtilActivator.getAccountManager(); - for(AccountID accountID : accountManager.getStoredAccounts()) + for (AccountID accountID : accountManager.getStoredAccounts()) { - if(accountManager.isAccountLoaded(accountID)) + if (accountManager.isAccountLoaded(accountID)) { ProtocolProviderService protocolProvider = AccountUtils.getRegisteredProviderForAccount( @@ -280,27 +293,78 @@ public class SelectAvatarMenu if(protocolProvider != null && protocolProvider.isRegistered()) { - OperationSetAvatar opSetAvatar - = protocolProvider - .getOperationSet(OperationSetAvatar.class); - - if(opSetAvatar != null) + // If account id is set this means that we want to + // edit our current account image, not the global + // avatar. Hence, we might not want to save this + // account image on the server yet. For example: in + // the account info plugin the user might set a new + // avatar and then click the cancel button. + if (SelectAvatarMenu.this.accountID != null) { - byte[] imageByte = null; - // Sets new avatar if not null. Otherwise, the - // opSetAvatar.setAvatar(null) will removes the - // current one. - if(image != null) - { - imageByte = ImageUtils.toByteArray(image); - } - try + if (accountID.equals( + SelectAvatarMenu.this.accountID)) { - opSetAvatar.setAvatar(imageByte); + OperationSetServerStoredAccountInfo opSet = + protocolProvider.getOperationSet( + OperationSetServerStoredAccountInfo.class); + if (opSet != null) + { + byte[] imageByte = null; + if (image != null) + { + imageByte = + ImageUtils.toByteArray(image); + } + avatarImage.setImageIcon(imageByte); + ImageDetail newDetail = + new ImageDetail( + "avatar", imageByte); + + Iterator<GenericDetail> oldDetail = + opSet.getDetails(ImageDetail.class); + try + { + if (oldDetail.hasNext()) + { + opSet.replaceDetail( + oldDetail.next(), + newDetail); + } + else + opSet.addDetail(newDetail); + } + catch (Throwable t) + { + logger.error( + "Error setting image", t); + } + } } - catch(Throwable t) + } + else + { + OperationSetAvatar opSetAvatar + = protocolProvider + .getOperationSet(OperationSetAvatar.class); + + if(opSetAvatar != null) { - logger.error("Error setting image", t); + byte[] imageByte = null; + // Sets new avatar if not null. Otherwise, the + // opSetAvatar.setAvatar(null) will removes the + // current one. + if(image != null) + { + imageByte = ImageUtils.toByteArray(image); + } + try + { + opSetAvatar.setAvatar(imageByte); + } + catch(Throwable t) + { + logger.error("Error setting image", t); + } } } } diff --git a/src/net/java/sip/communicator/impl/gui/main/presence/avatar/imagepicker/EditPanel.java b/src/net/java/sip/communicator/plugin/desktoputil/presence/avatar/imagepicker/EditPanel.java index b8273c8..aff0280 100644 --- a/src/net/java/sip/communicator/impl/gui/main/presence/avatar/imagepicker/EditPanel.java +++ b/src/net/java/sip/communicator/plugin/desktoputil/presence/avatar/imagepicker/EditPanel.java @@ -4,7 +4,7 @@ * Distributable under LGPL license. * See terms of license at gnu.org. */ -package net.java.sip.communicator.impl.gui.main.presence.avatar.imagepicker; +package net.java.sip.communicator.plugin.desktoputil.presence.avatar.imagepicker; import java.awt.*; import java.awt.event.*; @@ -13,7 +13,6 @@ import java.awt.image.*; import javax.swing.*; import javax.swing.event.*; -import net.java.sip.communicator.impl.gui.*; import net.java.sip.communicator.plugin.desktoputil.*; /** @@ -53,15 +52,15 @@ public class EditPanel this.clippingZoneWidth = clippingZoneWidth; this.clippingZoneHeight = clippingZoneHeight; - this.zoomOut = new JButton(GuiActivator.getResources() + this.zoomOut = new JButton(DesktopUtilActivator.getResources() .getImage("service.gui.buttons.ZOOM_OUT")); this.zoomOut.addActionListener(this); - this.zoomIn = new JButton(GuiActivator.getResources() + this.zoomIn = new JButton(DesktopUtilActivator.getResources() .getImage("service.gui.buttons.ZOOM_IN")); this.zoomIn.addActionListener(this); - this.reset = new JButton(GuiActivator.getResources() + this.reset = new JButton(DesktopUtilActivator.getResources() .getImage("service.gui.buttons.RESET")); - this.reset.setToolTipText(GuiActivator.getResources() + this.reset.setToolTipText(DesktopUtilActivator.getResources() .getI18NString("service.gui.avatar.imagepicker.RESET")); this.reset.addActionListener(this); @@ -69,7 +68,7 @@ public class EditPanel clippingZoneWidth); imageSizeSlider.addChangeListener(this); imageSizeSlider.setOpaque(false); - imageSizeSlider.setToolTipText(GuiActivator.getResources() + imageSizeSlider.setToolTipText(DesktopUtilActivator.getResources() .getI18NString("service.gui.avatar.imagepicker.IMAGE_SIZE")); TransparentPanel sliderPanel = new TransparentPanel(); diff --git a/src/net/java/sip/communicator/impl/gui/main/presence/avatar/imagepicker/ImageClipper.java b/src/net/java/sip/communicator/plugin/desktoputil/presence/avatar/imagepicker/ImageClipper.java index 5f42a26..853b906 100644 --- a/src/net/java/sip/communicator/impl/gui/main/presence/avatar/imagepicker/ImageClipper.java +++ b/src/net/java/sip/communicator/plugin/desktoputil/presence/avatar/imagepicker/ImageClipper.java @@ -4,7 +4,7 @@ * Distributable under LGPL license. * See terms of license at gnu.org. */ -package net.java.sip.communicator.impl.gui.main.presence.avatar.imagepicker; +package net.java.sip.communicator.plugin.desktoputil.presence.avatar.imagepicker; import java.awt.*; import java.awt.event.*; diff --git a/src/net/java/sip/communicator/impl/gui/main/presence/avatar/imagepicker/ImagePickerDialog.java b/src/net/java/sip/communicator/plugin/desktoputil/presence/avatar/imagepicker/ImagePickerDialog.java index 06e135b..8a1b2f4 100644 --- a/src/net/java/sip/communicator/impl/gui/main/presence/avatar/imagepicker/ImagePickerDialog.java +++ b/src/net/java/sip/communicator/plugin/desktoputil/presence/avatar/imagepicker/ImagePickerDialog.java @@ -4,7 +4,7 @@ * Distributable under LGPL license. * See terms of license at gnu.org. */ -package net.java.sip.communicator.impl.gui.main.presence.avatar.imagepicker; +package net.java.sip.communicator.plugin.desktoputil.presence.avatar.imagepicker; import java.awt.*; import java.awt.event.*; @@ -14,7 +14,6 @@ import java.io.*; import javax.imageio.*; import javax.swing.*; -import net.java.sip.communicator.impl.gui.*; import net.java.sip.communicator.plugin.desktoputil.*; /** @@ -51,7 +50,7 @@ public class ImagePickerDialog */ private void initDialog() { - this.setTitle(GuiActivator.getResources() + this.setTitle(DesktopUtilActivator.getResources() .getI18NString("service.gui.avatar.imagepicker.IMAGE_PICKER")); this.setModal(true); this.setResizable(true); @@ -91,22 +90,22 @@ public class ImagePickerDialog this.editPanel = new EditPanel(clipperZoneWidth, clipperZoneHeight); // Buttons - this.okButton = new JButton(GuiActivator.getResources() + this.okButton = new JButton(DesktopUtilActivator.getResources() .getI18NString("service.gui.avatar.imagepicker.SET")); this.okButton.addActionListener(this); this.okButton.setName("okButton"); - this.cancelButton = new JButton(GuiActivator.getResources() + this.cancelButton = new JButton(DesktopUtilActivator.getResources() .getI18NString("service.gui.avatar.imagepicker.CANCEL")); this.cancelButton.addActionListener(this); this.cancelButton.setName("cancelButton"); - this.selectFileButton = new JButton(GuiActivator.getResources() + this.selectFileButton = new JButton(DesktopUtilActivator.getResources() .getI18NString("service.gui.avatar.imagepicker.CHOOSE_FILE")); this.selectFileButton.addActionListener(this); this.selectFileButton.setName("selectFileButton"); - this.webcamButton = new JButton(GuiActivator.getResources() + this.webcamButton = new JButton(DesktopUtilActivator.getResources() .getI18NString("service.gui.avatar.imagepicker.TAKE_PHOTO")); this.webcamButton.addActionListener(this); @@ -146,8 +145,9 @@ public class ImagePickerDialog else if (name.equals("selectFileButton")) { SipCommFileChooser chooser = GenericFileDialog.create( - GuiActivator.getUIService().getMainFrame(), - GuiActivator.getResources().getI18NString( + //GuiActivator.getUIService().getMainFrame(), + null, + DesktopUtilActivator.getResources().getI18NString( "service.gui.avatar.imagepicker.CHOOSE_FILE"), SipCommFileChooser.LOAD_FILE_OPERATION); @@ -217,7 +217,7 @@ public class ImagePickerDialog @Override public String getDescription() { - return GuiActivator.getResources() + return DesktopUtilActivator.getResources() .getI18NString("service.gui.avatar.imagepicker.IMAGE_FILES"); } } diff --git a/src/net/java/sip/communicator/impl/gui/main/presence/avatar/imagepicker/WebcamDialog.java b/src/net/java/sip/communicator/plugin/desktoputil/presence/avatar/imagepicker/WebcamDialog.java index a72f13d..ed4a8ba 100644 --- a/src/net/java/sip/communicator/impl/gui/main/presence/avatar/imagepicker/WebcamDialog.java +++ b/src/net/java/sip/communicator/plugin/desktoputil/presence/avatar/imagepicker/WebcamDialog.java @@ -4,7 +4,7 @@ * Distributable under LGPL license. * See terms of license at gnu.org. */ -package net.java.sip.communicator.impl.gui.main.presence.avatar.imagepicker; +package net.java.sip.communicator.plugin.desktoputil.presence.avatar.imagepicker; import java.awt.*; import java.awt.event.*; @@ -12,10 +12,8 @@ import java.awt.image.*; import javax.swing.*; -import net.java.sip.communicator.impl.gui.*; -import net.java.sip.communicator.util.*; import net.java.sip.communicator.plugin.desktoputil.*; -import net.java.sip.communicator.plugin.desktoputil.TransparentPanel; +import net.java.sip.communicator.util.*; import org.jitsi.service.audionotifier.*; import org.jitsi.service.neomedia.*; @@ -51,7 +49,7 @@ public class WebcamDialog public WebcamDialog(ImagePickerDialog parent) { super(false); - this.setTitle(GuiActivator.getResources() + this.setTitle(DesktopUtilActivator.getResources() .getI18NString("service.gui.avatar.imagepicker.TAKE_PHOTO")); this.setModal(true); @@ -66,13 +64,13 @@ public class WebcamDialog private void init() { this.grabSnapshot = new JButton(); - this.grabSnapshot.setText(GuiActivator.getResources() + this.grabSnapshot.setText(DesktopUtilActivator.getResources() .getI18NString("service.gui.avatar.imagepicker.CLICK")); this.grabSnapshot.setName("grab"); this.grabSnapshot.addActionListener(this); this.grabSnapshot.setEnabled(false); - JButton cancelButton = new JButton(GuiActivator.getResources() + JButton cancelButton = new JButton(DesktopUtilActivator.getResources() .getI18NString("service.gui.avatar.imagepicker.CANCEL")); cancelButton.setName("cancel"); cancelButton.addActionListener(this); @@ -120,7 +118,7 @@ public class WebcamDialog private void initAccessWebcam() { //Call the method in the media service - MediaService mediaService = GuiActivator.getMediaService(); + MediaService mediaService = DesktopUtilActivator.getMediaService(); this.videoContainer = (Component) @@ -174,10 +172,10 @@ public class WebcamDialog */ private void playSound() { - String soundKey = GuiActivator.getResources() + String soundKey = DesktopUtilActivator.getResources() .getSoundPath("WEBCAM_SNAPSHOT"); - SCAudioClip audio = GuiActivator.getAudioNotifier() + SCAudioClip audio = DesktopUtilActivator.getAudioNotifier() .createAudio(soundKey); audio.play(); diff --git a/src/net/java/sip/communicator/plugin/spellcheck/LanguageMenuBar.java b/src/net/java/sip/communicator/plugin/spellcheck/LanguageMenuBar.java index 2bdf423..a79b535 100644 --- a/src/net/java/sip/communicator/plugin/spellcheck/LanguageMenuBar.java +++ b/src/net/java/sip/communicator/plugin/spellcheck/LanguageMenuBar.java @@ -261,6 +261,11 @@ public class LanguageMenuBar } + public void setCurrentAccountID(AccountID account) + { + + } + private static ImageIcon getLocaleIcon(Parameters.Locale locale, boolean isAvailable) { diff --git a/src/net/java/sip/communicator/service/gui/AbstractPluginComponent.java b/src/net/java/sip/communicator/service/gui/AbstractPluginComponent.java index fd6ab6c..c5ec1da 100644 --- a/src/net/java/sip/communicator/service/gui/AbstractPluginComponent.java +++ b/src/net/java/sip/communicator/service/gui/AbstractPluginComponent.java @@ -106,6 +106,13 @@ public abstract class AbstractPluginComponent { } + /* + * Implements PluginComponent#setCurrentAccountID(AccountID). + */ + public void setCurrentAccountID(AccountID accountID) + { + } + /** * Returns the factory that has created the component. * @return the parent factory. diff --git a/src/net/java/sip/communicator/service/gui/Container.java b/src/net/java/sip/communicator/service/gui/Container.java index 03434fb..a493d86 100644 --- a/src/net/java/sip/communicator/service/gui/Container.java +++ b/src/net/java/sip/communicator/service/gui/Container.java @@ -72,6 +72,12 @@ public class Container = new Container("CONTAINER_CONTACT_RIGHT_BUTTON_MENU"); /** + * Accounts window "right button menu" over an account. + */ + public static final Container CONTAINER_ACCOUNT_RIGHT_BUTTON_MENU + = new Container("CONTAINER_ACCOUNT_RIGHT_BUTTON_MENU"); + + /** * Main application window "right button menu" over a group container. */ public static final Container CONTAINER_GROUP_RIGHT_BUTTON_MENU diff --git a/src/net/java/sip/communicator/service/gui/PluginComponent.java b/src/net/java/sip/communicator/service/gui/PluginComponent.java index 3d039f3..f1984cf 100644 --- a/src/net/java/sip/communicator/service/gui/PluginComponent.java +++ b/src/net/java/sip/communicator/service/gui/PluginComponent.java @@ -21,6 +21,11 @@ import net.java.sip.communicator.service.protocol.*; * implement the <tt>setCurrentContact</tt> and * <tt>setCurrentContactGroup</tt> methods. * <p> + * <p> + * All components interested in the current account that they're dealing + * with (i.g. the one selected in the account list for example), should + * implement the <tt>setCurrentAccountID</tt> method. + * <p> * Note that <tt>getComponent</tt> should return a valid AWT, SWT or Swing * control in order to appear properly in the GUI. * @@ -77,6 +82,16 @@ public interface PluginComponent public void setCurrentContactGroup(MetaContactGroup metaGroup); /** + * Sets the current AccountID. Meant to be used by plugin components that are + * interested in the current AccountID. The current AccountID could be that + * of a currently selected account in the account list. It depends on the + * container, where this component is meant to be added. + * + * @param account the current account. + */ + public void setCurrentAccountID(AccountID accountID); + + /** * Returns the factory that has created the component. * @return the parent factory. */ diff --git a/src/net/java/sip/communicator/service/protocol/AbstractOperationSetAvatar.java b/src/net/java/sip/communicator/service/protocol/AbstractOperationSetAvatar.java index f997238..7f1e192 100644 --- a/src/net/java/sip/communicator/service/protocol/AbstractOperationSetAvatar.java +++ b/src/net/java/sip/communicator/service/protocol/AbstractOperationSetAvatar.java @@ -107,6 +107,7 @@ public abstract class AbstractOperationSetAvatar<T extends ProtocolProviderServi this.accountInfoOpSet.addDetail(newDetail); else this.accountInfoOpSet.replaceDetail(oldDetail, newDetail); + accountInfoOpSet.save(); } catch (OperationFailedException e) { logger.warn("Unable to set new avatar", e); diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetServerStoredAccountInfo.java b/src/net/java/sip/communicator/service/protocol/OperationSetServerStoredAccountInfo.java index 3b859c6..82f281c 100644 --- a/src/net/java/sip/communicator/service/protocol/OperationSetServerStoredAccountInfo.java +++ b/src/net/java/sip/communicator/service/protocol/OperationSetServerStoredAccountInfo.java @@ -116,6 +116,18 @@ public interface OperationSetServerStoredAccountInfo Class<? extends GenericDetail> detailClass); /** + * Determines whether the underlying implementation supports edition + * of this detail class. + * <p> + * @param detailClass the class whose edition we'd like to determine if it's + * possible + * @return true if the underlying implementation supports edition of this + * type of detail and false otherwise. + */ + public boolean isDetailClassEditable( + Class<? extends GenericDetail> detailClass); + + /** * The method returns the number of instances supported for a particular * detail type. Some protocols offer storing mutliple values for a * particular detail type. Spoken languages are a good example. @@ -128,7 +140,7 @@ public interface OperationSetServerStoredAccountInfo Class<? extends GenericDetail> detailClass); /** - * Adds the specified detail to the list of details registered on-line + * Adds the specified detail to the list of details ready to be saved online * for this account. If such a detail already exists its max instance number * is consulted and if it allows it - a second instance is added or otherwise * and illegal argument exception is thrown. An IllegalArgumentException is @@ -139,11 +151,9 @@ public interface OperationSetServerStoredAccountInfo * @param detail the detail that we'd like registered on the server. * <p> * @throws IllegalArgumentException if such a detail already exists and its - * max instances number has been atteined or if the underlying + * max instances number has been attained or if the underlying * implementation does not support setting details of the corresponding * class. - * @throws OperationFailedException with code Network Failure if putting the - * new value online has failed * @throws java.lang.ArrayIndexOutOfBoundsException if the number of * instances currently registered by the application is already equal to the * maximum number of supported instances (@see getMaxDetailInstances()) @@ -154,15 +164,13 @@ public interface OperationSetServerStoredAccountInfo ArrayIndexOutOfBoundsException; /** - * Removes the specified detail from the list of details stored online for - * this account. The method returns a boolean indicating if such a detail - * was found (and removed) or not. + * Removes the specified detail from the list of details ready to be saved + * online this account. The method returns a boolean indicating if such a + * detail was found (and removed) or not. * <p> * @param detail the detail to remove * @return true if the specified detail existed and was successfully removed * and false otherwise. - * @throws OperationFailedException with code Network Failure if removing the - * detail from the server has failed */ public boolean removeDetail(ServerStoredDetails.GenericDetail detail) throws OperationFailedException; @@ -187,6 +195,17 @@ public interface OperationSetServerStoredAccountInfo throws ClassCastException, OperationFailedException; /** + * Saves the list of details for this account that were ready to be stored + * online on the server. This method performs the actual saving of details + * online on the server and is supposed to be invoked after addDetail(), + * replaceDetail() and/or removeDetail(). + * <p> + * @throws OperationFailedException with code Network Failure if putting the + * new values back online has failed. + */ + public void save() throws OperationFailedException; + + /** * Registers a ServerStoredDetailsChangeListener with this operation set so * that it gets notifications of details change. * diff --git a/src/net/java/sip/communicator/service/protocol/ServerStoredDetails.java b/src/net/java/sip/communicator/service/protocol/ServerStoredDetails.java index 4002e18..16b7e01 100644 --- a/src/net/java/sip/communicator/service/protocol/ServerStoredDetails.java +++ b/src/net/java/sip/communicator/service/protocol/ServerStoredDetails.java @@ -306,6 +306,12 @@ public class ServerStoredDetails { super("Country", locale); } + + public CountryDetail(String country) + { + super("Country", null); + value = country; + } } /** @@ -317,6 +323,11 @@ public class ServerStoredDetails { super(locale); } + + public WorkCountryDetail(String country) + { + super(country); + } } //-------------------------------- Language ------------------------------------ @@ -447,6 +458,43 @@ public class ServerStoredDetails { return (URL)getDetailValue(); } + + /** + * Compares two URLDetails according their name + * and URLs + * + * @param obj Object expected URLDetail otherwise return false + * @return <tt>true</tt> if this object has the same name and + * URL value as <tt>obj</tt> and false otherwise + */ + @Override + public boolean equals(Object obj) + { + if (!(obj instanceof URLDetail)) + return false; + + if (this == obj) + { + return true; + } + + URLDetail other = (URLDetail)obj; + boolean equalsDisplayName = + this.detailDisplayName != null + && other.getDetailDisplayName() != null + && this.detailDisplayName.equals(other.getDetailDisplayName()); + boolean equalValues = + this.value != null + && other.getDetailValue() != null + && this.value.equals(other.getDetailValue()); + + boolean bothNullValues = + this.value == null && other.value == null; + if (equalsDisplayName && (equalValues || bothNullValues)) + return true; + else + return false; + } } /** @@ -487,6 +535,44 @@ public class ServerStoredDetails { return (byte[])getDetailValue(); } + + /** + * Compares two BinaryDetails according their DetailDisplayName + * and the result of invoking their getBytes() methods. + * + * @param obj Object expected BinaryDetail otherwise return false + * @return <tt>true</tt> if this object has the same display name and + * value as <tt>obj</tt> and false otherwise + */ + @Override + public boolean equals(Object obj) + { + if (!(obj instanceof BinaryDetail)) + return false; + + if (this == obj) + { + return true; + } + + BinaryDetail other = (BinaryDetail)obj; + boolean equalsDisplayName = + this.detailDisplayName != null + && other.getDetailDisplayName() != null + && this.detailDisplayName.equals(other.getDetailDisplayName()); + boolean equalsNotNull = + this.value != null + && other.getDetailValue() != null + && Arrays.equals(this.getBytes(), other.getBytes()); + boolean nullOrEmpty = + (this.value == null || this.getBytes().length == 0) + && (other.getDetailValue() == null + || other.getBytes().length == 0); + if (equalsDisplayName && (equalsNotNull || nullOrEmpty)) + return true; + else + return false; + } } /** @@ -641,6 +727,48 @@ public class ServerStoredDetails { super("Birth Date", date); } + + /** + * Compares two BirthDateDetails according to their + * Calender's year, month and day. + * + * @param obj Object expected BirthDateDetail otherwise return false + * @return <tt>true</tt> if this object has the same value as + * <tt>obj</tt> and false otherwise + */ + @Override + public boolean equals(Object obj) + { + if(!(obj instanceof BirthDateDetail)) + return false; + + if(this == obj) + { + return true; + } + + BirthDateDetail other = (BirthDateDetail)obj; + + // both null dates + if (this.value == null && other.getDetailValue() == null) + return true; + + if (this.value != null && other.getDetailValue() != null) + { + boolean yearEquals = + ((Calendar)this.value).get(Calendar.YEAR) == + ((Calendar)other.value).get(Calendar.YEAR); + boolean monthEquals = + ((Calendar)this.value).get(Calendar.MONTH) == + ((Calendar)other.value).get(Calendar.MONTH); + boolean dayEquals = + ((Calendar)this.value).get(Calendar.DAY_OF_MONTH) == + ((Calendar)other.value).get(Calendar.DAY_OF_MONTH); + return yearEquals && monthEquals && dayEquals; + } + else + return false; + } } /** @@ -747,4 +875,27 @@ public class ServerStoredDetails return ((Boolean)getDetailValue()).booleanValue(); } } + +//---------------------------- Others ------------------------------------------ + /** + * A job title. + */ + public static class JobTitleDetail extends StringDetail + { + public JobTitleDetail(String jobTitle) + { + super("Job Title", jobTitle); + } + } + + /** + * Represents a (personal) "about me" short description. + */ + public static class AboutMeDetail extends StringDetail + { + public AboutMeDetail(String description) + { + super("Description", description); + } + } } |