From 0ede668cd667dcd2918e4895c0199d90ebaf747c Mon Sep 17 00:00:00 2001 From: Thomas Hofer Date: Thu, 29 Nov 2007 13:04:59 +0000 Subject: [PATCH] refactored the about window and moved it to splashscreen-bundle. the next step is to refactor the splashcreen bundle to a new name. --- .../communicator/impl/gui/UIServiceImpl.java | 9 +- .../impl/gui/main/chat/menus/HelpMenu.java | 67 +- .../impl/gui/main/menus/HelpMenu.java | 96 +- .../splashscreen}/AboutWindow.java | 452 ++++----- .../plugin/splashscreen/Constants.java | 141 +++ .../splashscreen/SIPCommHTMLEditorKit.java | 106 +++ .../plugin/splashscreen/SIPCommImageView.java | 861 ++++++++++++++++++ .../splashscreen/SplashScreenActivator.java | 137 ++- .../splashscreen/StyledHTMLEditorPane.java | 70 ++ .../plugin/splashscreen/WindowBackground.java | 3 +- .../WindowTransparentBackground.java | 2 +- .../plugin/splashscreen/resources.properties | 5 +- .../resources/aboutWindowBackground.png | Bin 0 -> 24872 bytes .../splashscreen/resources/defaultStyle.css | 71 ++ .../splashScreenBackground.png} | Bin .../splashscreen/splashscreen.manifest.mf | 5 + 16 files changed, 1725 insertions(+), 300 deletions(-) rename src/net/java/sip/communicator/{impl/gui/main => plugin/splashscreen}/AboutWindow.java (61%) create mode 100644 src/net/java/sip/communicator/plugin/splashscreen/Constants.java create mode 100644 src/net/java/sip/communicator/plugin/splashscreen/SIPCommHTMLEditorKit.java create mode 100644 src/net/java/sip/communicator/plugin/splashscreen/SIPCommImageView.java create mode 100644 src/net/java/sip/communicator/plugin/splashscreen/StyledHTMLEditorPane.java create mode 100644 src/net/java/sip/communicator/plugin/splashscreen/resources/aboutWindowBackground.png create mode 100644 src/net/java/sip/communicator/plugin/splashscreen/resources/defaultStyle.css rename src/net/java/sip/communicator/plugin/splashscreen/{aboutWindowBackground.png => resources/splashScreenBackground.png} (100%) diff --git a/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java b/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java index 58f28bb77..f0bb1e776 100644 --- a/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java +++ b/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java @@ -54,6 +54,7 @@ public class UIServiceImpl supportedContainers.add(UIService.CONTAINER_CONTACT_RIGHT_BUTTON_MENU); supportedContainers.add(UIService.CONTAINER_GROUP_RIGHT_BUTTON_MENU); supportedContainers.add(UIService.CONTAINER_TOOLS_MENU); + supportedContainers.add(UIService.CONTAINER_HELP_MENU); supportedContainers.add(UIService.CONTAINER_CHAT_TOOL_BAR); supportedContainers.add(UIService.CONTAINER_CALL_HISTORY); supportedContainers.add(UIService.CONTAINER_MAIN_TABBED_PANE); @@ -75,7 +76,8 @@ public class UIServiceImpl * Creates an instance of UIServiceImpl. */ public UIServiceImpl() - {} + { + } /** * Initializes all frames and panels and shows the gui. @@ -514,11 +516,10 @@ public boolean getExitOnMainWindowClose() */ public void initExportedWindows() { - AboutWindow aboutWindow = new AboutWindow(mainFrame); AddContactWizard addContactWizard = new AddContactWizard(mainFrame); - exportedWindows.put(aboutWindow.getIdentifier(), aboutWindow); - exportedWindows.put(configurationFrame.getIdentifier(), configurationFrame); + exportedWindows.put(configurationFrame.getIdentifier(), + configurationFrame); exportedWindows.put(addContactWizard.getIdentifier(), addContactWizard); } diff --git a/src/net/java/sip/communicator/impl/gui/main/chat/menus/HelpMenu.java b/src/net/java/sip/communicator/impl/gui/main/chat/menus/HelpMenu.java index 7277108b5..499681b6a 100644 --- a/src/net/java/sip/communicator/impl/gui/main/chat/menus/HelpMenu.java +++ b/src/net/java/sip/communicator/impl/gui/main/chat/menus/HelpMenu.java @@ -7,14 +7,19 @@ package net.java.sip.communicator.impl.gui.main.chat.menus; +import java.awt.Component; import java.awt.event.*; +import java.util.Iterator; import javax.swing.*; +import net.java.sip.communicator.impl.gui.GuiActivator; import net.java.sip.communicator.impl.gui.customcontrols.*; import net.java.sip.communicator.impl.gui.i18n.*; import net.java.sip.communicator.impl.gui.main.*; import net.java.sip.communicator.impl.gui.main.chat.*; +import net.java.sip.communicator.service.gui.UIService; +import net.java.sip.communicator.service.gui.event.*; import net.java.sip.communicator.util.*; /** * The HelpMenu is a menu in the main application menu bar. @@ -23,15 +28,14 @@ */ public class HelpMenu extends SIPCommMenu - implements ActionListener + implements ActionListener, + PluginComponentListener { private Logger logger = Logger.getLogger(HelpMenu.class.getName()); private I18NString aboutString = Messages.getI18NString("about"); - private JMenuItem aboutItem = new JMenuItem(aboutString.getText()); - private ChatWindow chatWindow; /** @@ -44,29 +48,60 @@ public HelpMenu(ChatWindow chatWindow) { this.chatWindow = chatWindow; - this.add(aboutItem); + this.chatWindow = chatWindow; + + this.setMnemonic(Messages.getI18NString("help").getMnemonic()); + + this.initPluginComponents(); + } - this.aboutItem.setName("about"); + /** + * Initialize plugin components already registered for this container. + */ + private void initPluginComponents() + { + Iterator pluginComponents = GuiActivator.getUIService() + .getComponentsForContainer(UIService.CONTAINER_CHAT_HELP_MENU); - this.aboutItem.addActionListener(this); + while (pluginComponents.hasNext()) + { + Component o = (Component) pluginComponents.next(); - this.aboutItem.setMnemonic(aboutString.getMnemonic()); + this.add(o); + } + + GuiActivator.getUIService().addPluginComponentListener(this); } /** - * Handles the ActionEvent when one of the menu items is selected. - * - * @param evt the ActionEvent that has just occurred. + * Handles the ActionEvent when one of the menu items is + * selected. */ - public void actionPerformed(ActionEvent evt) { + public void actionPerformed(ActionEvent e) + { + } - JMenuItem menuItem = (JMenuItem) evt.getSource(); - String itemName = menuItem.getName(); + public void pluginComponentAdded(PluginComponentEvent event) + { + Component c = (Component) event.getSource(); - if (itemName.equals("about")) { - AboutWindow aboutWindow = new AboutWindow(chatWindow); + if (event.getContainerID().equals(UIService.CONTAINER_CHAT_HELP_MENU)) + { + this.add(c); - aboutWindow.setVisible(true); + this.revalidate(); + this.repaint(); } } + + public void pluginComponentRemoved(PluginComponentEvent event) + { + Component c = (Component) event.getSource(); + + if (event.getContainerID().equals(UIService.CONTAINER_CHAT_HELP_MENU)) + { + this.remove(c); + } + } + } diff --git a/src/net/java/sip/communicator/impl/gui/main/menus/HelpMenu.java b/src/net/java/sip/communicator/impl/gui/main/menus/HelpMenu.java index a7fabe57c..4a7aaed0d 100644 --- a/src/net/java/sip/communicator/impl/gui/main/menus/HelpMenu.java +++ b/src/net/java/sip/communicator/impl/gui/main/menus/HelpMenu.java @@ -1,5 +1,4 @@ /* - * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. * * Distributable under LGPL license. * See terms of license at gnu.org. @@ -7,67 +6,96 @@ package net.java.sip.communicator.impl.gui.main.menus; +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.customcontrols.*; import net.java.sip.communicator.impl.gui.i18n.*; import net.java.sip.communicator.impl.gui.main.*; -import net.java.sip.communicator.util.*; +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.gui.event.*; + /** * The HelpMenu is a menu in the main application menu bar. * * @author Yana Stamcheva + * @author Thomas Hofer */ -public class HelpMenu +public class HelpMenu extends SIPCommMenu - implements ActionListener + implements ActionListener, + PluginComponentListener { - private Logger logger = Logger.getLogger(HelpMenu.class.getName()); - - private I18NString aboutString = Messages.getI18NString("about"); - - private JMenuItem aboutItem - = new JMenuItem(aboutString.getText()); - private MainFrame mainFrame; - + /** * Creates an instance of HelpMenu. - * @param mainFrame the parent window + * + * @param mainFrame + * the parent window */ - public HelpMenu (MainFrame mainFrame) + public HelpMenu(MainFrame mainFrame) { super(Messages.getI18NString("help").getText()); - + this.mainFrame = mainFrame; - - this.add(aboutItem); - - this.aboutItem.setName("about"); - - this.aboutItem.addActionListener(this); - + this.setMnemonic(Messages.getI18NString("help").getMnemonic()); - - this.aboutItem.setMnemonic(aboutString.getMnemonic()); + + this.initPluginComponents(); } /** - * Handles the ActionEvent when one of the menu items is selected. + * Initialize plugin components already registered for this container. */ - public void actionPerformed(ActionEvent e) { + private void initPluginComponents() + { + Iterator pluginComponents = GuiActivator.getUIService() + .getComponentsForContainer(UIService.CONTAINER_HELP_MENU); - JMenuItem menuItem = (JMenuItem) e.getSource(); - String itemName = menuItem.getName(); + while (pluginComponents.hasNext()) + { + Component o = (Component) pluginComponents.next(); - if (itemName.equals("about")) { - AboutWindow aboutWindow = new AboutWindow(mainFrame); - - aboutWindow.setVisible(true); + this.add(o); } + + GuiActivator.getUIService().addPluginComponentListener(this); + } + + /** + * Handles the ActionEvent when one of the menu items is + * selected. + */ + public void actionPerformed(ActionEvent e) + { } + + public void pluginComponentAdded(PluginComponentEvent event) + { + Component c = (Component) event.getSource(); + + if (event.getContainerID().equals(UIService.CONTAINER_HELP_MENU)) + { + this.add(c); + + this.revalidate(); + this.repaint(); + } + } + + public void pluginComponentRemoved(PluginComponentEvent event) + { + Component c = (Component) event.getSource(); + + if (event.getContainerID().equals(UIService.CONTAINER_HELP_MENU)) + { + this.remove(c); + } + } + } diff --git a/src/net/java/sip/communicator/impl/gui/main/AboutWindow.java b/src/net/java/sip/communicator/plugin/splashscreen/AboutWindow.java similarity index 61% rename from src/net/java/sip/communicator/impl/gui/main/AboutWindow.java rename to src/net/java/sip/communicator/plugin/splashscreen/AboutWindow.java index 86f59230d..f83b9ee7f 100644 --- a/src/net/java/sip/communicator/impl/gui/main/AboutWindow.java +++ b/src/net/java/sip/communicator/plugin/splashscreen/AboutWindow.java @@ -1,213 +1,239 @@ -package net.java.sip.communicator.impl.gui.main; - -import java.awt.*; -import java.awt.event.*; - -import javax.swing.*; -import javax.swing.event.*; - -import net.java.sip.communicator.impl.gui.*; -import net.java.sip.communicator.impl.gui.customcontrols.*; -import net.java.sip.communicator.impl.gui.i18n.*; -import net.java.sip.communicator.impl.gui.utils.*; -import net.java.sip.communicator.service.gui.*; - -public class AboutWindow - extends SIPCommDialog - implements HyperlinkListener, - ActionListener, - ExportedWindow -{ - private WindowBackground mainPanel = new WindowBackground(); - - private JLabel titleLabel = new JLabel("SIP Communicator"); - - private JLabel versionLabel = - new JLabel(" " + System.getProperty("sip-communicator.version")); - - private JTextArea logoArea = - new JTextArea("Open Source VoIP & Instant Messaging"); - - private StyledHTMLEditorPane rightsArea = new StyledHTMLEditorPane(); - - private StyledHTMLEditorPane licenseArea = new StyledHTMLEditorPane(); - - private I18NString okString = Messages.getI18NString("ok"); - - private JButton okButton = new JButton(okString.getText()); - - private JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); - - private JPanel textPanel = new JPanel(); - - public AboutWindow(Frame owner) - { - super(owner); - - this.setTitle(Messages.getI18NString("about").getText()); - this.setModal(false); - - this.mainPanel.setLayout(new BorderLayout()); - - this.textPanel.setPreferredSize(new Dimension(470, 280)); - this.textPanel.setLayout(new BoxLayout(textPanel, BoxLayout.Y_AXIS)); - this.textPanel.setBorder(BorderFactory - .createEmptyBorder(15, 15, 15, 15)); - this.textPanel.setOpaque(false); - - this.titleLabel.setFont(Constants.FONT.deriveFont(Font.BOLD, 28)); - this.titleLabel.setForeground(Constants.DARK_BLUE); - this.titleLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); - - this.versionLabel.setFont(Constants.FONT.deriveFont(Font.BOLD, 18)); - this.versionLabel.setForeground(Color.GRAY); - this.versionLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); - - this.logoArea.setFont(Constants.FONT.deriveFont(Font.BOLD, 14)); - this.logoArea.setForeground(Constants.DARK_BLUE); - this.logoArea.setOpaque(false); - this.logoArea.setLineWrap(true); - this.logoArea.setWrapStyleWord(true); - this.logoArea.setEditable(false); - this.logoArea.setPreferredSize(new Dimension(100, 20)); - this.logoArea.setAlignmentX(Component.RIGHT_ALIGNMENT); - this.logoArea.setBorder(BorderFactory.createEmptyBorder(30, 180, 0, 0)); - - this.rightsArea.setContentType("text/html"); - - String startDivTag = "
"; - String endDivTag = "
"; - - this.rightsArea.appendToEnd(startDivTag - + "(c)2003-2007 Copyright sip-communicator.org." - + " All rights reserved. Visit " - + "" - + "http://sip-communicator.org." + endDivTag); - - this.rightsArea.setPreferredSize(new Dimension(50, 20)); - this.rightsArea - .setBorder(BorderFactory.createEmptyBorder(0, 180, 0, 0)); - this.rightsArea.setOpaque(false); - this.rightsArea.setEditable(false); - this.rightsArea.setAlignmentX(Component.RIGHT_ALIGNMENT); - this.rightsArea.addHyperlinkListener(this); - - this.licenseArea.setContentType("text/html"); - this.licenseArea.appendToEnd(startDivTag - + "The SIP Communicator is distributed under the" - + " terms of the LGPL " + "(" - + "http://www.gnu.org)." + endDivTag); - - this.licenseArea.setPreferredSize(new Dimension(50, 20)); - this.licenseArea.setBorder(BorderFactory.createEmptyBorder(10, 180, 0, - 0)); - this.licenseArea.setOpaque(false); - this.licenseArea.setEditable(false); - this.licenseArea.setAlignmentX(Component.RIGHT_ALIGNMENT); - this.licenseArea.addHyperlinkListener(this); - - this.textPanel.add(titleLabel); - this.textPanel.add(versionLabel); - this.textPanel.add(logoArea); - this.textPanel.add(rightsArea); - this.textPanel.add(licenseArea); - - this.getRootPane().setDefaultButton(okButton); - - this.okButton.setMnemonic(okString.getMnemonic()); - - this.okButton.addActionListener(this); - - this.buttonPanel.add(okButton); - this.buttonPanel.setOpaque(false); - - this.mainPanel.add(textPanel, BorderLayout.CENTER); - this.mainPanel.add(buttonPanel, BorderLayout.SOUTH); - - this.getContentPane().add(mainPanel); - - this.setResizable(false); - } - - protected void close(boolean isEscaped) - { - this.dispose(); - } - - /** - * Constructs the window background in order to have a background image. - */ - private class WindowBackground - extends JPanel - { - private Image bgImage = - ImageLoader.getImage(ImageLoader.ABOUT_WINDOW_BACKGROUND); - - public WindowBackground() - { - this.setPreferredSize(new Dimension(bgImage.getWidth(this), bgImage - .getHeight(this))); - } - - protected void paintComponent(Graphics g) - { - super.paintComponent(g); - - AntialiasingManager.activateAntialiasing(g); - - Graphics2D g2 = (Graphics2D) g; - - g2.drawImage(bgImage, 0, 0, null); - - g2.setColor(new Color(255, 255, 255, 100)); - - g2.fillRect(0, 0, getWidth(), getHeight()); - } - } - - public void hyperlinkUpdate(HyperlinkEvent e) - { - if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) - { - String href = e.getDescription(); - - GuiActivator.getBrowserLauncher().openURL(href); - } - } - - public void actionPerformed(ActionEvent e) - { - this.dispose(); - } - - /** - * Implements the ExportedWindow.getIdentifier() method. - */ - public WindowID getIdentifier() - { - return ExportedWindow.ABOUT_WINDOW; - } - - /** - * This dialog could not be minimized. - */ - public void minimize() - { - } - - /** - * This dialog could not be maximized. - */ - public void maximize() - { - } - - /** - * Implements the ExportedWindow.bringToFront() method. Brings - * this window to front. - */ - public void bringToFront() - { - this.toFront(); - } -} +package net.java.sip.communicator.plugin.splashscreen; + +import java.awt.*; +import java.awt.event.*; +import java.io.IOException; + +import javax.imageio.ImageIO; +import javax.swing.*; +import javax.swing.event.*; + +import net.java.sip.communicator.service.browserlauncher.BrowserLauncherService; +import net.java.sip.communicator.service.gui.*; + +import org.osgi.framework.ServiceReference; + +public class AboutWindow extends JDialog implements HyperlinkListener, + ActionListener, ExportedWindow +{ + private WindowBackground mainPanel = new WindowBackground(); + + private JLabel titleLabel = new JLabel("SIP Communicator"); + + private JLabel versionLabel = new JLabel(" " + + System.getProperty("sip-communicator.version")); + + private JTextArea logoArea = new JTextArea( + "Open Source VoIP & Instant Messaging"); + + private StyledHTMLEditorPane rightsArea = new StyledHTMLEditorPane(); + + private StyledHTMLEditorPane licenseArea = new StyledHTMLEditorPane(); + + private JButton okButton = new JButton(Resources.getString("ok")); + + private JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + + private JPanel textPanel = new JPanel(); + + public AboutWindow(Frame owner) + { + super(owner); + + this.setTitle(Resources.getString("about")); + this.setModal(false); + + this.mainPanel.setLayout(new BorderLayout()); + + this.textPanel.setPreferredSize(new Dimension(470, 280)); + this.textPanel.setLayout(new BoxLayout(textPanel, BoxLayout.Y_AXIS)); + this.textPanel.setBorder(BorderFactory + .createEmptyBorder(15, 15, 15, 15)); + this.textPanel.setOpaque(false); + + this.titleLabel.setFont(Constants.FONT.deriveFont(Font.BOLD, 28)); + this.titleLabel.setForeground(Constants.DARK_BLUE); + this.titleLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); + + this.versionLabel.setFont(Constants.FONT.deriveFont(Font.BOLD, 18)); + this.versionLabel.setForeground(Color.GRAY); + this.versionLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); + + this.logoArea.setFont(Constants.FONT.deriveFont(Font.BOLD, 14)); + this.logoArea.setForeground(Constants.DARK_BLUE); + this.logoArea.setOpaque(false); + this.logoArea.setLineWrap(true); + this.logoArea.setWrapStyleWord(true); + this.logoArea.setEditable(false); + this.logoArea.setPreferredSize(new Dimension(100, 20)); + this.logoArea.setAlignmentX(Component.RIGHT_ALIGNMENT); + this.logoArea.setBorder(BorderFactory.createEmptyBorder(30, 180, 0, 0)); + + this.rightsArea.setContentType("text/html"); + + String startDivTag = "
"; + String endDivTag = "
"; + + this.rightsArea.appendToEnd(startDivTag + + "(c)2003-2007 Copyright sip-communicator.org." + + " All rights reserved. Visit " + + "" + + "http://sip-communicator.org." + endDivTag); + + this.rightsArea.setPreferredSize(new Dimension(50, 20)); + this.rightsArea + .setBorder(BorderFactory.createEmptyBorder(0, 180, 0, 0)); + this.rightsArea.setOpaque(false); + this.rightsArea.setEditable(false); + this.rightsArea.setAlignmentX(Component.RIGHT_ALIGNMENT); + this.rightsArea.addHyperlinkListener(this); + + this.licenseArea.setContentType("text/html"); + this.licenseArea.appendToEnd(startDivTag + + "The SIP Communicator is distributed under the" + + " terms of the LGPL " + "(" + + "http://www.gnu.org)." + endDivTag); + + this.licenseArea.setPreferredSize(new Dimension(50, 20)); + this.licenseArea.setBorder(BorderFactory.createEmptyBorder(10, 180, 0, + 0)); + this.licenseArea.setOpaque(false); + this.licenseArea.setEditable(false); + this.licenseArea.setAlignmentX(Component.RIGHT_ALIGNMENT); + this.licenseArea.addHyperlinkListener(this); + + this.textPanel.add(titleLabel); + this.textPanel.add(versionLabel); + this.textPanel.add(logoArea); + this.textPanel.add(rightsArea); + this.textPanel.add(licenseArea); + + this.getRootPane().setDefaultButton(okButton); + + this.okButton.setMnemonic(Resources.getString("ok").charAt(0)); + + this.okButton.addActionListener(this); + + this.buttonPanel.add(okButton); + this.buttonPanel.setOpaque(false); + + this.mainPanel.add(textPanel, BorderLayout.CENTER); + this.mainPanel.add(buttonPanel, BorderLayout.SOUTH); + + this.getContentPane().add(mainPanel); + this.setSize(mainPanel.getPreferredSize()); + + this.setResizable(false); + this.setLocation(Toolkit.getDefaultToolkit().getScreenSize().width / 2 + - getWidth() / 2, + Toolkit.getDefaultToolkit().getScreenSize().height / 2 + - getHeight() / 2); + + } + + protected void close(boolean isEscaped) + { + this.dispose(); + } + + /** + * Constructs the window background in order to have a background image. + */ + private class WindowBackground extends JPanel + { + private Image bgImage = null; + + public WindowBackground() + { + try + { + bgImage = ImageIO.read(WindowBackground.class + .getResource("resources/aboutWindowBackground.png")); + } catch (IOException e) + { + bgImage = null; + } + this.setPreferredSize(new Dimension(bgImage.getWidth(this), bgImage + .getHeight(this))); + } + + protected void paintComponent(Graphics g) + { + super.paintComponent(g); + + activateAntialiasing(g); + + Graphics2D g2 = (Graphics2D) g; + + g2.drawImage(bgImage, 0, 0, null); + + g2.setColor(new Color(255, 255, 255, 100)); + + g2.fillRect(0, 0, getWidth(), getHeight()); + } + } + + public void hyperlinkUpdate(HyperlinkEvent e) + { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) + { + String href = e.getDescription(); + ServiceReference serviceReference = SplashScreenActivator + .getBundleContext().getServiceReference( + BrowserLauncherService.class.getName()); + + if (serviceReference != null) + { + BrowserLauncherService browserLauncherService = (BrowserLauncherService) SplashScreenActivator + .getBundleContext().getService(serviceReference); + browserLauncherService.openURL(href); + + } + } + } + + public void actionPerformed(ActionEvent e) + { + this.dispose(); + } + + /** + * Implements the ExportedWindow.getIdentifier() method. + */ + public WindowID getIdentifier() + { + return ExportedWindow.ABOUT_WINDOW; + } + + /** + * This dialog could not be minimized. + */ + public void minimize() + { + } + + /** + * This dialog could not be maximized. + */ + public void maximize() + { + } + + /** + * Implements the ExportedWindow.bringToFront() method. Brings + * this window to front. + */ + public void bringToFront() + { + this.toFront(); + } + + public static void activateAntialiasing(Graphics g) + { + Graphics2D g2d = (Graphics2D) g; + + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + } + +} diff --git a/src/net/java/sip/communicator/plugin/splashscreen/Constants.java b/src/net/java/sip/communicator/plugin/splashscreen/Constants.java new file mode 100644 index 000000000..08999115d --- /dev/null +++ b/src/net/java/sip/communicator/plugin/splashscreen/Constants.java @@ -0,0 +1,141 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ + +package net.java.sip.communicator.plugin.splashscreen; + +import java.awt.*; +import java.io.*; + +import javax.swing.text.html.StyleSheet; + +/** + * All look and feel related constants are stored here. + * + * @author Yana Stamcheva + */ + +public class Constants +{ + /* + * ====================================================================== + * -------------------- FONTS AND COLOR CONSTANTS ------------------------ + * ====================================================================== + */ + + /** + * The color used to paint the background of an incoming call history + * record. + */ + public static final Color HISTORY_DATE_COLOR = new Color(255, 201, 102); + + /** + * The color used to paint the background of an incoming call history + * record. + */ + public static final Color HISTORY_IN_CALL_COLOR = new Color(249, 255, 197); + + /** + * The color used to paint the background of an outgoing call history + * record. + */ + public static final Color HISTORY_OUT_CALL_COLOR = new Color(243, 244, 247); + + /** + * The start color used to paint a gradient selected background of some + * components. + */ + public static final Color SELECTED_START_COLOR = new Color(151, 169, 198); + + /** + * The end color used to paint a gradient selected background of some + * components. + */ + public static final Color SELECTED_END_COLOR = new Color(209, 212, 225); + + /** + * The start color used to paint a gradient mouse over background of some + * components. + */ + public static final Color MOVER_START_COLOR = new Color(230, 230, 230); + + /** + * The end color used to paint a gradient mouse over background of some + * components. + */ + public static final Color MOVER_END_COLOR = new Color(255, 255, 255); + + /** + * Gray color used to paint some borders, like the button border for + * example. + */ + public static final Color GRAY_COLOR = new Color(154, 154, 154); + + /** + * A color between blue and gray used to paint some borders. + */ + public static final Color BLUE_GRAY_BORDER_COLOR = new Color(142, 160, 188); + + /** + * A color between blue and gray (darker than the other one), used to paint + * some borders. + */ + public static final Color BLUE_GRAY_BORDER_DARKER_COLOR = new Color(131, + 149, 178); + + /** + * Light gray color used in the look and feel. + */ + public static final Color LIGHT_GRAY_COLOR = new Color(200, 200, 200); + + /** + * Dark blue color used in the About Window. + */ + public static final Color DARK_BLUE = new Color(23, 65, 125); + + /* + * ====================================================================== + * --------------------------- FONT CONSTANTS --------------------------- + * ====================================================================== + */ + + /** + * The name of the font used in this ui implementation. + */ + public static final String FONT_NAME = "Verdana"; + + /** + * The size of the font used in this ui implementation. + */ + public static final String FONT_SIZE = "12"; + + /** + * The default Font object used through this ui implementation. + */ + public static final Font FONT = new Font(Constants.FONT_NAME, Font.PLAIN, + new Integer(Constants.FONT_SIZE).intValue()); + + /** + * Temporary method to load the css style used in the chat window. + * + * @param style + */ + public static void loadSimpleStyle(StyleSheet style) + { + + InputStream is = Constants.class + .getResourceAsStream("resources/defaultStyle.css"); + + Reader r = new BufferedReader(new InputStreamReader(is)); + try + { + style.loadRules(r, null); + r.close(); + } catch (IOException e) + { + } + } +} diff --git a/src/net/java/sip/communicator/plugin/splashscreen/SIPCommHTMLEditorKit.java b/src/net/java/sip/communicator/plugin/splashscreen/SIPCommHTMLEditorKit.java new file mode 100644 index 000000000..7125769b5 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/splashscreen/SIPCommHTMLEditorKit.java @@ -0,0 +1,106 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ + +package net.java.sip.communicator.plugin.splashscreen; + +import javax.swing.*; +import javax.swing.text.*; +import javax.swing.text.html.*; +import javax.swing.text.html.ParagraphView; + +/** + * The SIPCommHTMLEditorKit is an HTMLEditorKit which uses + * the SIPCommImageView and an extended ParagraphView. + * + * @author Yana Stamcheva + */ +public class SIPCommHTMLEditorKit extends HTMLEditorKit { + + /** + * Returns the extended HTMLFactory defined here. + */ + public ViewFactory getViewFactory() { + return new HTMLFactoryX(); + } + + /** + * An extended HTMLFactory that uses the SIPCommImageView + * to represent images and the ParagraphViewX to represent + * paragraphs. + */ + static class HTMLFactoryX extends HTMLFactory + implements ViewFactory { + + public View create(Element elem) { + + View v=super.create(elem); + + if(v instanceof ImageView){ + return new SIPCommImageView(elem); + } + else if (v instanceof ParagraphView) { + return new ParagraphViewX(elem); + } + return v; + } + } + + /** + * The ParagraphViewX is created in order to solve the following + * problem (Bug ID: 4855207): + *

+ * When a paragraph in a JTextPane has a large amount of text the + * processing needed to layout the entire paragraph increases as the + * paragraph grows. + */ + static class ParagraphViewX extends ParagraphView { + public ParagraphViewX(Element elem) { + super(elem); + } + + /** + * Calculate equirements along the minor axis. This + * is implemented to forward the request to the logical + * view by calling getMinimumSpan, getPreferredSpan, and + * getMaximumSpan on it. + */ + protected SizeRequirements calculateMinorAxisRequirements ( + int axis, SizeRequirements r) { + if (r == null) { + r = new SizeRequirements(); + } + float pref = layoutPool.getPreferredSpan(axis); + float min = layoutPool.getMinimumSpan(axis); + // Don't include insets, Box.getXXXSpan will include them. + r.minimum = (int)min; + r.preferred = Math.max(r.minimum, (int) pref); + r.maximum = Short.MAX_VALUE; + r.alignment = 0.5f; + return r; + } + } + + + /** + * Create an uninitialized text storage model + * that is appropriate for this type of editor. + * + * @return the model. + */ + public Document createDefaultDocument() { + StyleSheet styles = getStyleSheet(); + StyleSheet ss = new StyleSheet(); + + ss.addStyleSheet(styles); + + HTMLDocument doc = new HTMLDocument(ss); + doc.setParser(getParser()); + doc.setAsynchronousLoadPriority(4); + doc.setTokenThreshold(100); + return doc; + } +} diff --git a/src/net/java/sip/communicator/plugin/splashscreen/SIPCommImageView.java b/src/net/java/sip/communicator/plugin/splashscreen/SIPCommImageView.java new file mode 100644 index 000000000..dbbfc3326 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/splashscreen/SIPCommImageView.java @@ -0,0 +1,861 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ + +package net.java.sip.communicator.plugin.splashscreen; + +import java.io.*; +import java.net.*; +import java.util.*; +import javax.imageio.*; + +import java.awt.*; +import java.awt.image.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.text.*; +import javax.swing.text.html.*; + +import net.java.sip.communicator.util.*; + +/* + * The content of this file was based on code borrowed from Rob Kenworthy, + * http://www.javaworld.com/javaworld/javatips/jw-javatip109.html#resources. + */ +/** + * The SIPCommImageView is an ImageView which allows to + * specify a related path when describing an image within an html document. + * + * @author Yana Stamcheva + */ +public class SIPCommImageView extends ImageView implements ImageObserver +{ + private Logger logger = Logger.getLogger(SIPCommImageView.class); + + public static final String TOP = "top"; + + public static final String TEXTTOP = "texttop"; + + public static final String MIDDLE = "middle"; + + public static final String ABSMIDDLE = "absmiddle"; + + public static final String CENTER = "center"; + + public static final String BOTTOM = "bottom"; + + /** + * Creates a new view that represents an IMG element. + * + * @param elem + * the element to create a view for. + */ + public SIPCommImageView(Element elem) + { + + super(elem); + + initialize(elem); + + StyleSheet sheet = getStyleSheet(); + + attr = sheet.getViewAttributes(this); + } + + /** + * Initializes this view. + * + * @param elem + * the element. + */ + private void initialize(Element elem) + { + synchronized (this) + { + loading = true; + fWidth = fHeight = 0; + } + + int width = 0; + int height = 0; + boolean customWidth = false; + boolean customHeight = false; + + try + { + fElement = elem; + + if (isURL()) + { + URL src = getSourceURL(); + + if (src != null) + { + + Dictionary cache = (Dictionary) getDocument().getProperty( + IMAGE_CACHE_PROPERTY); + + if (cache != null) + fImage = (Image) cache.get(src); + else + fImage = Toolkit.getDefaultToolkit().getImage(src); + } + } else + { + /*--Code to load from relative path--*/ + String src = (String) fElement.getAttributes().getAttribute( + HTML.Attribute.SRC); + + if (src == null) + return; + + // fImage = Toolkit.getDefaultToolkit().createImage(src); + try + { + fImage = ImageIO.read(SIPCommImageView.class + .getResource(src)); + + try + { + waitForImage(); + } catch (InterruptedException e) + { + fImage = null; + } + + } catch (IOException e) + { + fImage = null; + logger.error("Failed to read image.", e); + } catch (IllegalArgumentException iae) + { + fImage = null; + logger.error("Failed to read image.", iae); + } + } + + // Get height/width from params or image or defaults: + height = getIntAttr(HTML.Attribute.HEIGHT, -1); + + customHeight = (height > 0); + + if (!customHeight && fImage != null) + height = fImage.getHeight(this); + + if (height <= 0) + height = DEFAULT_HEIGHT; + + width = getIntAttr(HTML.Attribute.WIDTH, -1); + + customWidth = (width > 0); + + if (!customWidth && fImage != null) + width = fImage.getWidth(this); + + if (width <= 0) + width = DEFAULT_WIDTH; + + // Make sure the image starts loading: + if (fImage != null) + if (customWidth && customHeight) + Toolkit.getDefaultToolkit().prepareImage(fImage, height, + width, this); + else + Toolkit.getDefaultToolkit().prepareImage(fImage, -1, -1, + this); + } finally + { + + synchronized (this) + { + loading = false; + + if (customWidth || fWidth == 0) + { + fWidth = width; + } + if (customHeight || fHeight == 0) + { + fHeight = height; + } + } + } + } + + /** + * Determines if path is in the form of an URL. + * + * @return TRUE if the source is URL, FALSE otherwise. + */ + private boolean isURL() + { + String src = (String) fElement.getAttributes().getAttribute( + HTML.Attribute.SRC); + + if (src == null) + return false; + + return src.toLowerCase().startsWith("file") + || src.toLowerCase().startsWith("http"); + } + + /** + * Make sure an image is loaded - ie no broken images. So far its used only + * for images loaded off the disk (non-URL). + * + * @throws InterruptedException + */ + private void waitForImage() throws InterruptedException + { + + int w = fImage.getWidth(this); + int h = fImage.getHeight(this); + + while (true) + { + int flags = Toolkit.getDefaultToolkit().checkImage(fImage, w, h, + this); + + if (((flags & ERROR) != 0) || ((flags & ABORT) != 0)) + throw new InterruptedException(); + + else if ((flags & (ALLBITS | FRAMEBITS)) != 0) + return; + + Thread.sleep(10); + } + } + + /** + * Fetches the attributes to use when rendering. This is implemented to + * multiplex the attributes specified in the model with a StyleSheet. + */ + public AttributeSet getAttributes() + { + + return attr; + } + + /** + * Checks if the image is within a link. + */ + boolean isLink() + { + // ! It would be nice to cache this but in an editor it can change + // See if I have an HREF attribute courtesy of the enclosing A tag: + + AttributeSet anchorAttr = (AttributeSet) + + fElement.getAttributes().getAttribute(HTML.Tag.A); + + if (anchorAttr != null) + { + return anchorAttr.isDefined(HTML.Attribute.HREF); + } + return false; + } + + /** + * Returns the size of the border to use. + */ + int getBorder() + { + return getIntAttr(HTML.Attribute.BORDER, isLink() ? DEFAULT_BORDER : 0); + } + + /** + * Returns the amount of extra space to add along an axis. + */ + int getSpace(int axis) + { + return getIntAttr(axis == X_AXIS ? HTML.Attribute.HSPACE + : HTML.Attribute.VSPACE, 0); + } + + /** + * Returns the border's color, or null if this is not a link. + */ + Color getBorderColor() + { + StyledDocument doc = (StyledDocument) getDocument(); + return doc.getForeground(getAttributes()); + } + + /** + * Returns the image's vertical alignment. + */ + float getVerticalAlignment() + { + String align = (String) fElement.getAttributes().getAttribute( + HTML.Attribute.ALIGN); + if (align != null) + { + align = align.toLowerCase(); + if (align.equals(TOP) || align.equals(TEXTTOP)) + return 0.0f; + else if (align.equals(this.CENTER) || align.equals(MIDDLE) + || align.equals(ABSMIDDLE)) + return 0.5f; + } + + return 1.0f; // default alignment is bottom + } + + /** + * Checks if the image is at least one pixel. + * + * @param obs + * The ImageObserver. + * @return true if the image is not null and is at least one + * pixel big, false otherwise. + */ + boolean hasPixels(ImageObserver obs) + { + + return fImage != null && fImage.getHeight(obs) > 0 + && fImage.getWidth(obs) > 0; + } + + /** + * Returns an URL for the image source, or null if it could not be + * determined. + */ + private URL getSourceURL() + { + + String src = (String) fElement.getAttributes().getAttribute( + HTML.Attribute.SRC); + if (src == null) + return null; + + URL reference = ((HTMLDocument) getDocument()).getBase(); + try + { + URL u = new URL(reference, src); + return u; + } catch (MalformedURLException e) + { + return null; + } + } + + /** + * Look up an integer-valued attribute. Not recursive. + */ + private int getIntAttr(HTML.Attribute name, int deflt) + { + AttributeSet attr = fElement.getAttributes(); + if (attr.isDefined(name)) + { // does not check parents! + int i; + String val = (String) attr.getAttribute(name); + if (val == null) + i = deflt; + else + try + { + i = Math.max(0, Integer.parseInt(val)); + } catch (NumberFormatException x) + { + i = deflt; + } + return i; + } else + return deflt; + } + + /** + * Establishes the parent view for this view. Seize this moment to cache the + * AWT Container I'm in. + */ + public void setParent(View parent) + { + super.setParent(parent); + fContainer = parent != null ? getContainer() : null; + if (parent == null && fComponent != null) + { + fComponent.getParent().remove(fComponent); + fComponent = null; + } + } + + /** + * The attributes may have changed. + */ + public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) + { + super.changedUpdate(e, a, f); + float align = getVerticalAlignment(); + + int height = fHeight; + int width = fWidth; + + initialize(getElement()); + + boolean hChanged = fHeight != height; + boolean wChanged = fWidth != width; + if (hChanged || wChanged || getVerticalAlignment() != align) + { + getParent().preferenceChanged(this, hChanged, wChanged); + } + } + + /** + * Paints the image. + * + * @param g + * the rendering surface to use + * @param a + * the allocated region to render into + * @see View#paint + */ + public void paint(Graphics g, Shape a) + { + Color oldColor = g.getColor(); + fBounds = a.getBounds(); + int border = getBorder(); + int x = fBounds.x + border + getSpace(X_AXIS); + int y = fBounds.y + border + getSpace(Y_AXIS); + int width = fWidth; + int height = fHeight; + int sel = getSelectionState(); + + // If no pixels yet, draw gray outline and icon: + if (!hasPixels(this)) + { + g.setColor(Color.lightGray); + g.drawRect(x, y, width - 1, height - 1); + g.setColor(oldColor); + loadIcons(); + Icon icon = fImage == null ? sMissingImageIcon : sPendingImageIcon; + if (icon != null) + icon.paintIcon(getContainer(), g, x, y); + } + + // Draw image: + if (fImage != null) + { + g.drawImage(fImage, x, y, width, height, this); + // Use the following instead of g.drawImage when + // BufferedImageGraphics2D.setXORMode is fixed (4158822). + + // Use Xor mode when selected/highlighted. + // ! Could darken image instead, but it would be more expensive. + /* + * if( sel > 0 ) g.setXORMode(Color.white); g.drawImage(fImage,x, y, + * width,height,this); if( sel > 0 ) g.setPaintMode(); + */ + } + + // If selected exactly, we need a black border & grow-box: + Color bc = getBorderColor(); + if (sel == 2) + { + // Make sure there's room for a border: + int delta = 2 - border; + if (delta > 0) + { + x += delta; + y += delta; + width -= delta << 1; + height -= delta << 1; + border = 2; + } + bc = null; + g.setColor(Color.black); + // Draw grow box: + g.fillRect(x + width - 5, y + height - 5, 5, 5); + } + + // Draw border: + if (border > 0) + { + if (bc != null) + g.setColor(bc); + // Draw a thick rectangle: + for (int i = 1; i <= border; i++) + g.drawRect(x - i, y - i, width - 1 + i + i, height - 1 + i + i); + g.setColor(oldColor); + } + } + + /** + * Request that this view be repainted. Assumes the view is still at its + * last-drawn location. + */ + protected void repaint(long delay) + { + if (fContainer != null && fBounds != null) + { + fContainer.repaint(delay, fBounds.x, fBounds.y, fBounds.width, + fBounds.height); + } + } + + /** + * Determines whether the image is selected, and if it's the only thing + * selected. + * + * @return 0 if not selected, 1 if selected, 2 if exclusively selected. + * "Exclusive" selection is only returned when editable. + */ + protected int getSelectionState() + { + int p0 = fElement.getStartOffset(); + int p1 = fElement.getEndOffset(); + if (fContainer instanceof JTextComponent) + { + JTextComponent textComp = (JTextComponent) fContainer; + int start = textComp.getSelectionStart(); + int end = textComp.getSelectionEnd(); + if (start <= p0 && end >= p1) + { + if (start == p0 && end == p1 && isEditable()) + return 2; + else + return 1; + } + } + return 0; + } + + /** + * Checks whether the container is editable. + * + * @return true if the container is editable, + * false otherwise. + */ + protected boolean isEditable() + { + return fContainer instanceof JEditorPane + && ((JEditorPane) fContainer).isEditable(); + } + + /** + * Returns the text editor's highlight color. + */ + protected Color getHighlightColor() + { + JTextComponent textComp = (JTextComponent) fContainer; + return textComp.getSelectionColor(); + } + + // --- Progressive display --------------------------------------------- + + // This can come on any thread. If we are in the process of reloading + // the image and determining our state (loading == true) we don't fire + // preference changed, or repaint, we just reset the fWidth/fHeight as + // necessary and return. This is ok as we know when loading finishes + // it will pick up the new height/width, if necessary. + public boolean imageUpdate(Image img, int flags, int x, int y, int width, + int height) + { + if (fImage == null || fImage != img) + return false; + + // Bail out if there was an error: + if ((flags & (ABORT | ERROR)) != 0) + { + fImage = null; + repaint(0); + return false; + } + + // Resize image if necessary: + short changed = 0; + if ((flags & ImageObserver.HEIGHT) != 0) + if (!getElement().getAttributes().isDefined(HTML.Attribute.HEIGHT)) + { + changed |= 1; + } + if ((flags & ImageObserver.WIDTH) != 0) + if (!getElement().getAttributes().isDefined(HTML.Attribute.WIDTH)) + { + changed |= 2; + } + synchronized (this) + { + if ((changed & 1) == 1) + { + fWidth = width; + } + if ((changed & 2) == 2) + { + fHeight = height; + } + if (loading) + { + // No need to resize or repaint, still in the process of + // loading. + return true; + } + } + if (changed != 0) + { + // May need to resize myself, asynchronously: + Document doc = getDocument(); + try + { + if (doc instanceof AbstractDocument) + { + ((AbstractDocument) doc).readLock(); + } + preferenceChanged(this, true, true); + } finally + { + if (doc instanceof AbstractDocument) + { + ((AbstractDocument) doc).readUnlock(); + } + } + + return true; + } + + // Repaint when done or when new pixels arrive: + if ((flags & (FRAMEBITS | ALLBITS)) != 0) + repaint(0); + else if ((flags & SOMEBITS) != 0) + if (sIsInc) + repaint(sIncRate); + + return ((flags & ALLBITS) == 0); + } + + /* + * Static properties for incremental drawing. Swiped from Component.java + * + * @see #imageUpdate + */ + private static boolean sIsInc = true; + + private static int sIncRate = 100; + + // --- Layout ---------------------------------------------------------- + + /** + * Determines the preferred span for this view along an axis. + * + * @param axis + * may be either X_AXIS or Y_AXIS + * @return the span the view would like to be rendered into. Typically the + * view is told to render into the span that is returned, although + * there is no guarantee. The parent may choose to resize or break + * the view. + */ + public float getPreferredSpan(int axis) + { + int extra = 2 * (getBorder() + getSpace(axis)); + switch (axis) + { + case View.X_AXIS: + return fWidth + extra; + case View.Y_AXIS: + return fHeight + extra; + default: + throw new IllegalArgumentException("Invalid axis: " + axis); + } + } + + /** + * Determines the desired alignment for this view along an axis. This is + * implemented to give the alignment to the bottom of the icon along the y + * axis, and the default along the x axis. + * + * @param axis + * may be either X_AXIS or Y_AXIS + * @return the desired alignment. This should be a value between 0.0 and 1.0 + * where 0 indicates alignment at the origin and 1.0 indicates + * alignment to the full span away from the origin. An alignment of + * 0.5 would be the center of the view. + */ + public float getAlignment(int axis) + { + switch (axis) + { + case View.Y_AXIS: + return getVerticalAlignment(); + default: + return super.getAlignment(axis); + } + } + + /** + * Provides a mapping from the document model coordinate space to the + * coordinate space of the view mapped to it. + * + * @param pos + * the position to convert + * @param a + * the allocated region to render into + * @return the bounding box of the given position + * @exception BadLocationException + * if the given position does not represent a valid + * location in the associated document + * @see View#modelToView + */ + public Shape modelToView(int pos, Shape a, Position.Bias b) + throws BadLocationException + { + int p0 = getStartOffset(); + int p1 = getEndOffset(); + if ((pos >= p0) && (pos <= p1)) + { + Rectangle r = a.getBounds(); + if (pos == p1) + { + r.x += r.width; + } + r.width = 0; + return r; + } + return null; + } + + /** + * Provides a mapping from the view coordinate space to the logical + * coordinate space of the model. + * + * @param x + * the X coordinate + * @param y + * the Y coordinate + * @param a + * the allocated region to render into + * @return the location within the model that best represents the given + * point of view + * @see View#viewToModel + */ + public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) + { + Rectangle alloc = (Rectangle) a; + if (x < alloc.x + alloc.width) + { + bias[0] = Position.Bias.Forward; + return getStartOffset(); + } + bias[0] = Position.Bias.Backward; + return getEndOffset(); + } + + /** + * Set the size of the view. (Ignored.) + * + * @param width + * the width + * @param height + * the height + */ + public void setSize(float width, float height) + { + // Ignore this -- image size is determined by the tag attrs and + // the image itself, not the surrounding layout! + } + + // --- Static icon accessors ------------------------------------------- + + private Icon makeIcon(final String gifFile) throws IOException + { + /* + * Copy resource into a byte array. This is necessary because several + * browsers consider Class.getResource a security risk because it can be + * used to load additional classes. Class.getResourceAsStream just + * returns raw bytes, which we can convert to an image. + */ + InputStream resource = SIPCommImageView.class + .getResourceAsStream(gifFile); + + if (resource == null) + { + logger.error(SIPCommImageView.class.getName() + "/" + gifFile + + " not found."); + return null; + } + BufferedInputStream in = new BufferedInputStream(resource); + ByteArrayOutputStream out = new ByteArrayOutputStream(1024); + byte[] buffer = new byte[1024]; + int n; + while ((n = in.read(buffer)) > 0) + { + out.write(buffer, 0, n); + } + in.close(); + out.flush(); + + buffer = out.toByteArray(); + if (buffer.length == 0) + { + logger.error(gifFile + " is zero-length."); + return null; + } + return new ImageIcon(buffer); + } + + private void loadIcons() + { + try + { + if (sPendingImageIcon == null) + sPendingImageIcon = makeIcon(PENDING_IMAGE_SRC); + if (sMissingImageIcon == null) + sMissingImageIcon = makeIcon(MISSING_IMAGE_SRC); + } catch (Exception ex) + { + logger.error("Couldn't load image icons.", ex); + } + } + + protected StyleSheet getStyleSheet() + { + HTMLDocument doc = (HTMLDocument) getDocument(); + return doc.getStyleSheet(); + } + + // --- member variables ------------------------------------------------ + + private AttributeSet attr; + + private Element fElement; + + private Image fImage; + + private int fHeight, fWidth; + + private Container fContainer; + + private Rectangle fBounds; + + private Component fComponent; + + private Point fGrowBase; // base of drag while growing image + + private boolean fGrowProportionally; // should grow be proportional? + + /** + * Set to true, while the receiver is locked, to indicate the reciever is + * loading the image. This is used in imageUpdate. + */ + private boolean loading; + + // --- constants and static stuff -------------------------------- + + private static Icon sPendingImageIcon, sMissingImageIcon; + + private static final String PENDING_IMAGE_SRC = "icons/image-delayed.gif", // both + // stolen + // from + // HotJava + MISSING_IMAGE_SRC = "icons/image-failed.gif"; + + // $ move this someplace public + static final String IMAGE_CACHE_PROPERTY = "imageCache"; + + // Height/width to use before we know the real size: + private static final int DEFAULT_WIDTH = 32, DEFAULT_HEIGHT = 32, + // Default value of BORDER param: //? possibly move into stylesheet? + DEFAULT_BORDER = 2; + +} diff --git a/src/net/java/sip/communicator/plugin/splashscreen/SplashScreenActivator.java b/src/net/java/sip/communicator/plugin/splashscreen/SplashScreenActivator.java index 7710bf3c8..339e0cc28 100644 --- a/src/net/java/sip/communicator/plugin/splashscreen/SplashScreenActivator.java +++ b/src/net/java/sip/communicator/plugin/splashscreen/SplashScreenActivator.java @@ -6,54 +6,133 @@ */ package net.java.sip.communicator.plugin.splashscreen; -import net.java.sip.communicator.service.gui.*; +import java.awt.event.*; + +import javax.swing.JMenuItem; + +import net.java.sip.communicator.service.gui.UIService; import org.osgi.framework.*; -public class SplashScreenActivator - implements BundleActivator, - ServiceListener, - BundleListener +public class SplashScreenActivator implements BundleActivator, BundleListener { private WelcomeWindow welcomeWindow; - private BundleContext bundleContext; - - public void start(BundleContext bundleContext) throws Exception + private static BundleContext bundleContext; + private JMenuItem aboutEntry; + + public void start(BundleContext bc) throws Exception { - this.bundleContext = bundleContext; - + bundleContext = bc; + welcomeWindow = new WelcomeWindow(); - welcomeWindow.pack(); welcomeWindow.setVisible(true); - - this.bundleContext.addServiceListener(this); - this.bundleContext.addBundleListener(this); + + bundleContext.addBundleListener(this); } public void stop(BundleContext arg0) throws Exception { - + unRegisterMenuEntry(); } - public void serviceChanged(ServiceEvent evt) - { - ServiceReference serviceRef = evt.getServiceReference(); - - if(serviceRef.getBundle().getState() != Bundle.STARTING) + public void bundleChanged(BundleEvent evt) + { + if (welcomeWindow != null && welcomeWindow.isShowing() + && evt.getType() == BundleEvent.STARTED) + welcomeWindow.setBundle(evt.getBundle().getHeaders().get( + "Bundle-Name").toString()); + + ServiceReference[] services = evt.getBundle().getRegisteredServices(); + if (services == null) + { return; - - if (bundleContext.getServiceReference(UIService.class.getName()) - == serviceRef) + } + for (ServiceReference serviceRef : services) { - this.welcomeWindow.close(); + ServiceReference uiServiceRef = bundleContext + .getServiceReference(UIService.class.getName()); + if (serviceRef == uiServiceRef) + { + int state = serviceRef.getBundle().getState(); + if (state == Bundle.ACTIVE) + { + // UI-Service started. + + // register the about dialog menu entry + registerMenuEntry(uiServiceRef); + + welcomeWindow.close(); + } + } } + } - public void bundleChanged(BundleEvent evt) + private void registerMenuEntry(ServiceReference uiServiceRef) { - if(welcomeWindow != null && welcomeWindow.isShowing()) - welcomeWindow.setBundle(evt.getBundle().getHeaders() - .get("Bundle-Name").toString()); - } + final UIService uiService = (UIService) bundleContext + .getService(uiServiceRef); + + // add menu entry to file menu + // Add your menu item to the help menu + if (aboutEntry == null) + { + aboutEntry = new JMenuItem(Resources.getString("menuEntry")); + aboutEntry.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + + AboutWindow aboutWindow = new AboutWindow(null); + aboutWindow.setVisible(true); + } + }); + } + // Check if the help menu is a supported container. + if (uiService.isContainerSupported(UIService.CONTAINER_HELP_MENU)) + { + uiService.addComponent(UIService.CONTAINER_HELP_MENU, aboutEntry); + } + // Check if the help menu is a supported container. + if (uiService.isContainerSupported(UIService.CONTAINER_CHAT_HELP_MENU)) + { + uiService.addComponent(UIService.CONTAINER_CHAT_HELP_MENU, + aboutEntry); + } + } + + private void unRegisterMenuEntry() + { + // Obtain the UI Service + ServiceReference uiServiceRef = bundleContext + .getServiceReference(UIService.class.getName()); + + if (uiServiceRef == null + || uiServiceRef.getBundle().getState() != Bundle.ACTIVE) + { + return; + } + + UIService uiService = (UIService) bundleContext + .getService(uiServiceRef); + + // Check if the tools menu is a supported container. + boolean isContainerSupported = uiService + .isContainerSupported(UIService.CONTAINER_HELP_MENU); + + if (isContainerSupported) + { + // add menu entry to file menu + // Add your menu item to the help menu + uiService + .removeComponent(UIService.CONTAINER_HELP_MENU, aboutEntry); + } + } + + static BundleContext getBundleContext() + { + return bundleContext; + } + } diff --git a/src/net/java/sip/communicator/plugin/splashscreen/StyledHTMLEditorPane.java b/src/net/java/sip/communicator/plugin/splashscreen/StyledHTMLEditorPane.java new file mode 100644 index 000000000..9502a89ae --- /dev/null +++ b/src/net/java/sip/communicator/plugin/splashscreen/StyledHTMLEditorPane.java @@ -0,0 +1,70 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.splashscreen; + +import java.io.IOException; + +import javax.swing.JEditorPane; +import javax.swing.text.*; +import javax.swing.text.html.*; + +import net.java.sip.communicator.util.Logger; + +public class StyledHTMLEditorPane + extends JEditorPane +{ + private Logger logger = Logger.getLogger(StyledHTMLEditorPane.class); + + private HTMLEditorKit editorKit = new SIPCommHTMLEditorKit(); + + private HTMLDocument document; + + public StyledHTMLEditorPane() + { + this.document = (HTMLDocument) editorKit.createDefaultDocument(); + + this.setContentType("text/html"); + this.setEditorKitForContentType("text/html", editorKit); + this.setEditorKit(editorKit); + this.setDocument(document); + + Constants.loadSimpleStyle(document.getStyleSheet()); + } + + + public void appendToEnd(String text) + { + Element root = document.getDefaultRootElement(); + try + { + document.insertAfterEnd(root + .getElement(root.getElementCount() - 1), text); + } + catch (BadLocationException e) + { + logger.error("Insert in the HTMLDocument failed.", e); + } + catch (IOException e) + { + logger.error("Insert in the HTMLDocument failed.", e); + } + } + + public void insertAfterStart(String text) + { + Element root = this.document.getDefaultRootElement(); + + try { + this.document.insertBeforeStart(root + .getElement(0), text); + } catch (BadLocationException e) { + logger.error("Insert in the HTMLDocument failed.", e); + } catch (IOException e) { + logger.error("Insert in the HTMLDocument failed.", e); + } + } +} diff --git a/src/net/java/sip/communicator/plugin/splashscreen/WindowBackground.java b/src/net/java/sip/communicator/plugin/splashscreen/WindowBackground.java index 014a0b514..78331ef49 100644 --- a/src/net/java/sip/communicator/plugin/splashscreen/WindowBackground.java +++ b/src/net/java/sip/communicator/plugin/splashscreen/WindowBackground.java @@ -12,7 +12,6 @@ public class WindowBackground extends JPanel { - private Image bgImage; public WindowBackground() @@ -22,7 +21,7 @@ public WindowBackground() try { bgImage = ImageIO.read(WindowBackground.class - .getResource("aboutWindowBackground.png")); + .getResource("resources/aboutWindowBackground.png")); } catch (IOException e) { diff --git a/src/net/java/sip/communicator/plugin/splashscreen/WindowTransparentBackground.java b/src/net/java/sip/communicator/plugin/splashscreen/WindowTransparentBackground.java index d428070d9..cbdbc9a0c 100644 --- a/src/net/java/sip/communicator/plugin/splashscreen/WindowTransparentBackground.java +++ b/src/net/java/sip/communicator/plugin/splashscreen/WindowTransparentBackground.java @@ -75,7 +75,7 @@ public WindowTransparentBackground() { robot = new Robot(); bgImage = ImageIO.read( - WindowBackground.class.getResource("aboutWindowBackground.png")); + WindowBackground.class.getResource("resources/aboutWindowBackground.png")); bgWidth = bgImage.getWidth(null); diff --git a/src/net/java/sip/communicator/plugin/splashscreen/resources.properties b/src/net/java/sip/communicator/plugin/splashscreen/resources.properties index fb2d24e15..2b903aa71 100644 --- a/src/net/java/sip/communicator/plugin/splashscreen/resources.properties +++ b/src/net/java/sip/communicator/plugin/splashscreen/resources.properties @@ -1,3 +1,6 @@ +about=About logoMessage=Open Source VoIP & Instant Messaging loading=Loading -welcomeMessage=The SIP Communicator is currently under active development. The version you are running is only experimental and MAY NOT work as expected. Please refer to {0} for more information. \ No newline at end of file +menuEntry=About +ok=OK +welcomeMessage=The SIP Communicator is currently under active development. The version you are running is only experimental and MAY NOT work as expected. Please refer to {0} for more information. diff --git a/src/net/java/sip/communicator/plugin/splashscreen/resources/aboutWindowBackground.png b/src/net/java/sip/communicator/plugin/splashscreen/resources/aboutWindowBackground.png new file mode 100644 index 0000000000000000000000000000000000000000..4a178171dcfdbc0a04786634a038395f70e91600 GIT binary patch literal 24872 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 Nl;l)pi=|D2{~yr?>`wpy literal 0 HcmV?d00001 diff --git a/src/net/java/sip/communicator/plugin/splashscreen/resources/defaultStyle.css b/src/net/java/sip/communicator/plugin/splashscreen/resources/defaultStyle.css new file mode 100644 index 000000000..a9326a68c --- /dev/null +++ b/src/net/java/sip/communicator/plugin/splashscreen/resources/defaultStyle.css @@ -0,0 +1,71 @@ +h1 +{ +background-color:#c6d0e1; +margin:0px; +text-align:center; +font-weight:bold; +font-size:12 +} + +h2 +{ +margin:0px; +font-family:Verdana; +font-size:12; +color:#ef7b1e +} + +h3 +{ +margin:0px; +font-family:Verdana; +font-size:12; +color:#2e538b +} + +h4 +{ +margin:0px; +font-family:Verdana; +font-size:12; +font-weight:bold; +color:#FFC875 +} + +h5 +{ +margin:0px; +font-family:Verdana; +font-size:12; +font-weight:bold; +color:#CC0000 +} + +h6 +{ +margin:0px; +font-family:Verdana; +font-size:12; +font-weight:bold; +color:#990000 +} +p +{ +margin:0px; +font-family:Verdana; +font-size:12; +font-weight:bold; +color:#62BD80 +} + +div +{ +font-family:Verdana; +font-size:12; +color:#000000 +} + +i +{ +background-color:#FFF4AB +} \ No newline at end of file diff --git a/src/net/java/sip/communicator/plugin/splashscreen/aboutWindowBackground.png b/src/net/java/sip/communicator/plugin/splashscreen/resources/splashScreenBackground.png similarity index 100% rename from src/net/java/sip/communicator/plugin/splashscreen/aboutWindowBackground.png rename to src/net/java/sip/communicator/plugin/splashscreen/resources/splashScreenBackground.png diff --git a/src/net/java/sip/communicator/plugin/splashscreen/splashscreen.manifest.mf b/src/net/java/sip/communicator/plugin/splashscreen/splashscreen.manifest.mf index 0ac55f24c..12cd0c270 100755 --- a/src/net/java/sip/communicator/plugin/splashscreen/splashscreen.manifest.mf +++ b/src/net/java/sip/communicator/plugin/splashscreen/splashscreen.manifest.mf @@ -4,8 +4,13 @@ Bundle-Description: Splash Screen plugin. Bundle-Vendor: sip-communicator.org Bundle-Version: 0.0.1 Import-Package: org.osgi.framework, + net.java.sip.communicator.service.browserlauncher, net.java.sip.communicator.service.gui, net.java.sip.communicator.service.gui.event, + net.java.sip.communicator.util, javax.swing, javax.swing.event, + javax.swing.border, + javax.swing.text, + javax.swing.text.html, javax.imageio