/* Violet - A program for editing UML diagrams. Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) Alexandre de Pellegrin (http://alexdp.free.fr); This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.horstmann.violet.framework.dialog; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.Frame; import java.awt.Point; import java.awt.Rectangle; import java.awt.Toolkit; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.List; import javax.swing.ImageIcon; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import com.horstmann.violet.framework.injection.bean.ManiocFramework.ManagedBean; import com.horstmann.violet.framework.injection.resources.ResourceBundleInjector; import com.horstmann.violet.framework.injection.resources.annotation.ResourceBundleBean; import com.horstmann.violet.framework.theme.ThemeManager; /** * This factory manages dialog. It could create dialogs or call a listener if the dialog constrction is delegated to an external * program such as an Eclipse plugin * * @author Alexandre de Pellegrin * */ @ManagedBean(registeredManually=true) public class DialogFactory { /** * Default constructor. */ public DialogFactory(DialogFactoryMode factoryMode) { this.factoryMode = factoryMode; ResourceBundleInjector.getInjector().inject(this); } public static DialogFactory getInstance() { throw new RuntimeException("Java method no longer reachable. Code needs to be refactored.") ; } /** * Adds a listener that could be invoked if the factory is set to DELEGATED_MODE * * @param listener */ public void setListener(DialogFactoryListener listener) { this.listener = listener; performCachedDialogs(); } /** * If the dialog factory is configured to DELEGATED_MODE and does not still have a listener that listens and display dialogs, * all dialogs are put into a kind of cache. Then the listener is registered, all cached dialogs are sent to it to be displayed. */ private void performCachedDialogs() { while (!this.delegatedDialogCache.isEmpty()) { Object[] object = (Object[]) this.delegatedDialogCache.get(0); JOptionPane optionPane = (JOptionPane) object[0]; String title = (String) object[1]; Boolean isModal = (Boolean) object[2]; invokeListener(optionPane, title, isModal.booleanValue()); this.delegatedDialogCache.remove(0); } } public void showErrorDialog(String message) { JOptionPane optionPane = new JOptionPane(); JLabel label = new JLabel(message); label.setFont(label.getFont().deriveFont(Font.PLAIN)); optionPane.setIcon(this.genericErrorImageIcon); optionPane.setMessage(label); showDialog(optionPane, this.genericErrorTitle, true); } public void showWarningDialog(String message) { JOptionPane optionPane = new JOptionPane(); JLabel label = new JLabel(message); label.setFont(label.getFont().deriveFont(Font.PLAIN)); optionPane.setIcon(this.genericWarningImageIcon); optionPane.setMessage(label); showDialog(optionPane, this.genericWarningTitle, true); } public void showInformationDialog(String message) { JOptionPane optionPane = new JOptionPane(); JLabel label = new JLabel(message); label.setFont(label.getFont().deriveFont(Font.PLAIN)); optionPane.setIcon(this.genericInfoImageIcon); optionPane.setMessage(label); showDialog(optionPane, this.genericInfoTitle, true); } /** * According to the choosen mode : creates and shows a dialog or invokes via the listeners external program that must create and * display this dialog. * * @param optionPane * @param title * @param isModal */ public void showDialog(JOptionPane optionPane, String title, boolean isModal) { updateBackgroundColor(optionPane); if (this.factoryMode.equals(DialogFactoryMode.INTERNAL)) { createDialog(optionPane, title, isModal); } if (this.factoryMode.equals(DialogFactoryMode.DELEGATED)) { invokeListener(optionPane, title, isModal); } } /** * Creates (and displays) a dialog * * @param optionPane * @param title * @param isModal */ private void createDialog(final JOptionPane optionPane, String title, boolean isModal) { Frame mainFrame = getDialogOwner(); final JDialog dialog = new JDialog(mainFrame, title, isModal); dialog.setTitle(title); dialog.getContentPane().add(optionPane); dialog.pack(); dialog.setResizable(false); optionPane.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { if (dialog.isVisible() && (event.getPropertyName().equals(JOptionPane.VALUE_PROPERTY)) && event.getNewValue() != null && event.getNewValue() != JOptionPane.UNINITIALIZED_VALUE) { dialog.setVisible(false); dialog.dispose(); } } }); centerDialog(dialog, mainFrame); dialog.setVisible(true); } /** * Updates the jOptionPane background color for all its components * * @param component optionPane */ private void updateBackgroundColor(Component component) { // Replace color if only its color is the default background panel color // (Could be really optimized) if (component.getBackground().equals(JPANEL_BG_COLOR)) { component.setBackground(ThemeManager.getInstance().getTheme().getBackgroundColor()); } if (component instanceof Container) { Container container = (Container) component; for (int i = 0, ub = container.getComponentCount(); i < ub; ++i) updateBackgroundColor(container.getComponent(i)); } } /** * Set dialog location to the center of the owner frame repscting the screen bound * * @param dialog * @param owner */ private void centerDialog(JDialog dialog, Frame owner) { Rectangle b = owner.getBounds(); double x = b.getX() + b.getWidth() / 2 - dialog.getWidth() / 2; double y = b.getY() + b.getHeight() / 2 - dialog.getHeight() / 2; Dimension screenSize = owner.getToolkit().getScreenSize(); if (x + dialog.getWidth() > screenSize.getWidth()) { x = screenSize.getWidth() - dialog.getWidth(); } if (y + dialog.getHeight() > screenSize.getHeight()) { y = screenSize.getHeight() - dialog.getHeight(); } Point newLocation = new Point(Math.max((int) x, 0), Math.max((int) y, 0)); dialog.setLocation(newLocation); } /** * Calls listener that must display a dialog * * @param optionPane * @param title * @param isModal */ private void invokeListener(JOptionPane optionPane, String title, boolean isModal) { if (listener != null) { listener.mustDisplayPanel(optionPane, title, isModal); } if (listener == null) { this.delegatedDialogCache.add(new Object[] { optionPane, title, new Boolean(isModal) }); } } /** * Sets the dialog owner. This argument is mandatory. Anyway, I think that, as it usually never changes, it is not necessary to * give it as argument each time we construct a dialog. Only one time is efficient. * * @param owner */ public void setDialogOwner(Frame owner) { this.dialogOwner = owner; } /** * @return current's main frame */ private Frame getDialogOwner() { if (this.dialogOwner != null) { return this.dialogOwner; } return getEmergencyDialogOwner(); } /** * Return a temporary frame if the real frame owner is not yet set * (for example, */ private Frame getEmergencyDialogOwner() { JFrame emergencyFrame = new JFrame(); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); int screenWidth = (int) screenSize.getWidth(); int screenHeight = (int) screenSize.getHeight(); emergencyFrame.setBounds(screenWidth / 16, screenHeight / 16, screenWidth * 7 / 8, screenHeight * 7 / 8); return emergencyFrame; } /** * Listener that have the responsability to create and display dialogs Note that ther is one and only one listener. Why? Because * of the Eclipse framework that create an instance of the application for each document opened, and in the same JVM. */ private DialogFactoryListener listener; /** * Working mode */ private DialogFactoryMode factoryMode; /** * The frame that owns all dialogs */ private Frame dialogOwner; /** * JPanel default background color */ private static final Color JPANEL_BG_COLOR = (new JPanel()).getBackground(); /** * Delegated dialog cache (Used if the dialog mode is set to delegated and no listener is registered. So, the foactory stores * doalogs that will be displayed when a new listener will be registered */ private List<Object[]> delegatedDialogCache = new ArrayList<Object[]>(); @ResourceBundleBean(key="dialog.generic.error.icon") private ImageIcon genericErrorImageIcon; @ResourceBundleBean(key="dialog.generic.error.title") private String genericErrorTitle; @ResourceBundleBean(key="dialog.generic.warning.icon") private ImageIcon genericWarningImageIcon; @ResourceBundleBean(key="dialog.generic.warning.title") private String genericWarningTitle; @ResourceBundleBean(key="dialog.generic.information.icon") private ImageIcon genericInfoImageIcon; @ResourceBundleBean(key="dialog.generic.information.title") private String genericInfoTitle; }