/** * Copyright (c) 2009 - 2010 AppWork UG(haftungsbeschränkt) <e-mail@appwork.org> * * This file is part of org.appwork.utils.swing.dialog * * This software is licensed under the Artistic License 2.0, * see the LICENSE file or http://www.opensource.org/licenses/artistic-license-2.0.php * for details */ package org.appwork.utils.swing.dialog; import java.awt.Component; import java.awt.Dimension; import java.awt.Frame; import java.awt.Point; import java.awt.Toolkit; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.HierarchyEvent; import java.awt.event.HierarchyListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.util.HashMap; import javax.swing.AbstractAction; import javax.swing.Box; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRootPane; import javax.swing.KeyStroke; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.WindowConstants; import net.miginfocom.swing.MigLayout; import org.appwork.resources.AWUTheme; import org.appwork.storage.JSonStorage; import org.appwork.utils.BinaryLogic; import org.appwork.utils.locale.APPWORKUTILS; import org.appwork.utils.logging.Log; import org.appwork.utils.os.CrossSystem; import org.appwork.utils.swing.SwingUtils; public abstract class AbstractDialog<T> extends TimerDialog implements ActionListener, WindowListener { private static final long serialVersionUID = 1831761858087385862L; private static final HashMap<String, Integer> SESSION_DONTSHOW_AGAIN = new HashMap<String, Integer>(); private static Integer getSessionDontShowAgainValue(final String key) { final Integer ret = AbstractDialog.SESSION_DONTSHOW_AGAIN.get(key); if (ret == null) { return -1; } return ret; } public static void resetDialogInformations() { try { AbstractDialog.SESSION_DONTSHOW_AGAIN.clear(); JSonStorage.getStorage("Dialogs").clear(); } catch (final Exception e) { Log.exception(e); } } protected JButton cancelButton; private final String cancelOption; private JPanel defaultButtons; private JCheckBox dontshowagain; protected int flagMask; private final ImageIcon icon; private boolean initialized = false; protected JButton okButton; private final String okOption; protected JComponent panel; private int returnBitMask = 0; private AbstractAction[] actions = null; private final String title; public AbstractDialog(final int flag, final String title, final ImageIcon icon, final String okOption, final String cancelOption) { super(); this.title = title; this.flagMask = flag; this.icon = BinaryLogic.containsAll(flag, Dialog.STYLE_HIDE_ICON) ? null : icon; this.okOption = okOption == null ? APPWORKUTILS.T.ABSTRACTDIALOG_BUTTON_OK() : okOption; this.cancelOption = cancelOption == null ? APPWORKUTILS.T.ABSTRACTDIALOG_BUTTON_CANCEL() : cancelOption; } /** * this function will init and show the dialog */ private void _init() { this.layoutDialog(); if (BinaryLogic.containsAll(this.flagMask, Dialog.LOGIC_COUNTDOWN)) { this.timerLbl.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(final MouseEvent e) { AbstractDialog.this.cancel(); AbstractDialog.this.timerLbl.removeMouseListener(this); } }); this.timerLbl.setToolTipText(APPWORKUTILS.T.TIMERDIALOG_TOOLTIP_TIMERLABEL()); this.timerLbl.setIcon(AWUTheme.I().getIcon("cancel", 16)); } /** * this is very important so the new shown dialog will become root for * all following dialogs! we save old parentWindow, then set current * dialogwindow as new root and show dialog. after dialog has been * shown, we restore old parentWindow */ final Component parentOwner = Dialog.getInstance().getParentOwner(); Dialog.getInstance().setParentOwner(this.getDialog()); try { this.setTitle(this.title); dont: if (BinaryLogic.containsAll(this.flagMask, Dialog.STYLE_SHOW_DO_NOT_DISPLAY_AGAIN)) { try { final int i = BinaryLogic.containsAll(this.flagMask, Dialog.LOGIC_DONT_SHOW_AGAIN_DELETE_ON_EXIT) ? AbstractDialog.getSessionDontShowAgainValue(this.getDontShowAgainKey()) : JSonStorage.getStorage("Dialogs").get(this.getDontShowAgainKey(), -1); if (i >= 0) { // filter saved return value int ret = i & (Dialog.RETURN_OK | Dialog.RETURN_CANCEL); // add flags ret |= Dialog.RETURN_DONT_SHOW_AGAIN | Dialog.RETURN_SKIPPED_BY_DONT_SHOW; /* * if LOGIC_DONT_SHOW_AGAIN_IGNORES_CANCEL or * LOGIC_DONT_SHOW_AGAIN_IGNORES_OK are used, we check * here if we should handle the dont show again feature */ if (BinaryLogic.containsAll(this.flagMask, Dialog.LOGIC_DONT_SHOW_AGAIN_IGNORES_CANCEL) && BinaryLogic.containsAll(ret, Dialog.RETURN_CANCEL)) { break dont; } if (BinaryLogic.containsAll(this.flagMask, Dialog.LOGIC_DONT_SHOW_AGAIN_IGNORES_OK) && BinaryLogic.containsAll(ret, Dialog.RETURN_OK)) { break dont; } this.returnBitMask = ret; return; } } catch (final Exception e) { Log.exception(e); } } if (parentOwner == null || !parentOwner.isShowing()) { this.getDialog().setAlwaysOnTop(true); } // The Dialog Modal this.getDialog().setModal(true); // Layout manager this.getDialog().setLayout(new MigLayout("ins 5", "[]", "[fill,grow][]")); // Dispose dialog on close this.getDialog().setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); this.getDialog().addWindowListener(this); // create panel for the dialog's buttons this.defaultButtons = this.getDefaultButtonPanel(); this.okButton = new JButton(this.okOption); /* * We set the focus on the ok button. if no ok button is shown, we * set the focus on cancel button */ JButton focus = this.okButton; this.cancelButton = new JButton(this.cancelOption); // add listeners here this.okButton.addActionListener(this); this.cancelButton.addActionListener(this); // add icon if available if (this.icon != null) { this.getDialog().add(new JLabel(this.icon), "split 2,alignx left,aligny center,shrinkx,gapright 10"); } // Layout the dialog content and add it to the contentpane this.panel = this.layoutDialogContent(); this.getDialog().add(this.panel, "pushx,growx,pushy,growy,spanx,aligny center,wrap"); // add the countdown timer this.getDialog().add(this.timerLbl, "split 3,growx,hidemode 2"); if (BinaryLogic.containsAll(this.flagMask, Dialog.STYLE_SHOW_DO_NOT_DISPLAY_AGAIN)) { this.dontshowagain = new JCheckBox(APPWORKUTILS.T.ABSTRACTDIALOG_STYLE_SHOW_DO_NOT_DISPLAY_AGAIN()); this.dontshowagain.setHorizontalAlignment(SwingConstants.TRAILING); this.dontshowagain.setHorizontalTextPosition(SwingConstants.LEADING); this.getDialog().add(this.dontshowagain, "growx,pushx,alignx right,gapleft 20"); } else { this.getDialog().add(Box.createHorizontalGlue(), "growx,pushx,alignx right,gapleft 20"); } this.getDialog().add(this.defaultButtons, "alignx right,shrinkx"); if ((this.flagMask & Dialog.BUTTONS_HIDE_OK) == 0) { // Set OK as defaultbutton this.getDialog().getRootPane().setDefaultButton(this.okButton); this.okButton.addHierarchyListener(new HierarchyListener() { public void hierarchyChanged(final HierarchyEvent e) { if ((e.getChangeFlags() & HierarchyEvent.PARENT_CHANGED) != 0) { final JButton defaultButton = (JButton) e.getComponent(); final JRootPane root = SwingUtilities.getRootPane(defaultButton); if (root != null) { root.setDefaultButton(defaultButton); } } } }); focus = this.okButton; this.defaultButtons.add(this.okButton, "alignx right,tag ok,sizegroup confirms,growx,pushx"); } if (!BinaryLogic.containsAll(this.flagMask, Dialog.BUTTONS_HIDE_CANCEL)) { this.defaultButtons.add(this.cancelButton, "alignx right,tag cancel,sizegroup confirms,growx,pushx"); if (BinaryLogic.containsAll(this.flagMask, Dialog.BUTTONS_HIDE_OK)) { this.getDialog().getRootPane().setDefaultButton(this.cancelButton); this.cancelButton.requestFocusInWindow(); // focus is on cancel if OK is hidden focus = this.cancelButton; } } this.addButtons(this.defaultButtons); if (BinaryLogic.containsAll(this.flagMask, Dialog.LOGIC_COUNTDOWN)) { // show timer this.initTimer(this.getCountdown()); } else { this.timerLbl.setVisible(false); } // pack dialog this.getDialog().invalidate(); // this.setMinimumSize(this.getPreferredSize()); this.getDialog().setMinimumSize(new Dimension(300, 80)); this.pack(); this.getDialog().setResizable(false); // minimum size foir a dialog // // Dimension screenDim = // Toolkit.getDefaultToolkit().getScreenSize(); // this.setMaximumSize(Toolkit.getDefaultToolkit().getScreenSize()); this.getDialog().toFront(); // if (this.getDesiredSize() != null) { // this.setSize(this.getDesiredSize()); // } if (!this.getDialog().getParent().isDisplayable() || !this.getDialog().getParent().isVisible()) { final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); this.getDialog().setLocation(new Point((int) (screenSize.getWidth() - this.getDialog().getWidth()) / 2, (int) (screenSize.getHeight() - this.getDialog().getHeight()) / 2)); } else if (this.getDialog().getParent() instanceof Frame && ((Frame) this.getDialog().getParent()).getExtendedState() == Frame.ICONIFIED) { // dock dialog at bottom right if mainframe is not visible final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); this.getDialog().setLocation(new Point((int) (screenSize.getWidth() - this.getDialog().getWidth() - 20), (int) (screenSize.getHeight() - this.getDialog().getHeight() - 60))); } else { this.getDialog().setLocation(SwingUtils.getCenter(this.getDialog().getParent(), this.getDialog())); } // register an escape listener to cancel the dialog final KeyStroke ks = KeyStroke.getKeyStroke("ESCAPE"); focus.getInputMap().put(ks, "ESCAPE"); focus.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(ks, "ESCAPE"); focus.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ks, "ESCAPE"); focus.getActionMap().put("ESCAPE", new AbstractAction() { private static final long serialVersionUID = -6666144330707394562L; public void actionPerformed(final ActionEvent e) { Log.L.fine("Answer: Key<ESCAPE>"); AbstractDialog.this.dispose(); } }); focus.requestFocus(); this.packed(); // System.out.println("NEW ONE "+this.getDialog()); /* * workaround a javabug that forces the parentframe to stay always * on top */ if (this.getDialog().getParent() != null && !CrossSystem.isMac()) { ((Window) this.getDialog().getParent()).setAlwaysOnTop(true); ((Window) this.getDialog().getParent()).setAlwaysOnTop(false); } this.setVisible(true); } finally { // System.out.println("SET OLD"); Dialog.getInstance().setParentOwner(this.getDialog().getParent()); } /* * workaround a javabug that forces the parentframe to stay always on * top */ if (this.getDialog().getParent() != null && !CrossSystem.isMac()) { ((Window) this.getDialog().getParent()).setAlwaysOnTop(true); ((Window) this.getDialog().getParent()).setAlwaysOnTop(false); } } public void actionPerformed(final ActionEvent e) { if (e.getSource() == this.okButton) { Log.L.fine("Answer: Button<OK:" + this.okButton.getText() + ">"); this.setReturnmask(true); } else if (e.getSource() == this.cancelButton) { Log.L.fine("Answer: Button<CANCEL:" + this.cancelButton.getText() + ">"); this.setReturnmask(false); } this.dispose(); } /** * Overwrite this method to add additional buttons */ protected void addButtons(final JPanel buttonBar) { } /** * called when user closes the window * * @return <code>true</code>, if and only if the dialog should be closeable **/ public boolean closeAllowed() { return true; } protected abstract T createReturnValue(); /** * This method has to be called to display the dialog. make sure that all * settings have beens et before, becvause this call very likly display a * dialog that blocks the rest of the gui until it is closed */ public void displayDialog() { if (this.initialized) { return; } this.initialized = true; this._init(); } @Override public void dispose() { if (!this.initialized) { throw new IllegalStateException("Dialog has not been initialized yet. call displayDialog()"); } super.dispose(); if (this.timer != null) { this.timer.interrupt(); this.timer = null; } } /** * @return */ protected JPanel getDefaultButtonPanel() { final JPanel ret = new JPanel(new MigLayout("ins 0", "[]", "0[]0")); if (this.actions != null) { for (final AbstractAction a : this.actions) { String tag = (String) a.getValue("tag"); if (tag == null) { tag = "help"; } ret.add(new JButton(a), "alignx right,tag " + tag + ",sizegroup confirms,growx,pushx"); } } return ret; } /** * Create the key to save the don't showmagain state in database. should be * overwritten in same dialogs. by default, the dialogs get differed by * their title and their classname * * @return */ protected String getDontShowAgainKey() { return "ABSTRACTDIALOG_DONT_SHOW_AGAIN_" + this.getClass().getSimpleName() + "_" + this.toString(); } // /** // * should be overwritten and return a Dimension of the dialog should have // a // * special size // * // * @return // */ // protected Dimension getDesiredSize() { // // return null; // } /** * Return the returnbitmask * * @return */ public int getReturnmask() { if (!this.initialized) { throw new IllegalStateException("Dialog has not been initialized yet. call displayDialog()"); } return this.returnBitMask; } public T getReturnValue() { if (!this.initialized) { throw new IllegalStateException("Dialog has not been initialized yet. call displayDialog()"); } return this.createReturnValue(); } /** * @return */ public String getTitle() { try { return this.getDialog().getTitle(); } catch (final NullPointerException e) { // not initialized yet return this.title; } } /** * This method has to be overwritten to implement custom content * * @return musst return a JComponent */ abstract public JComponent layoutDialogContent(); /** * Handle timeout */ @Override protected void onTimeout() { this.setReturnmask(false); this.returnBitMask |= Dialog.RETURN_TIMEOUT; this.dispose(); } /** * may be overwritten to set focus to special components etc. */ protected void packed() { } /** * Add Additional BUttons on the left side of ok and cancel button. You can * add a "tag" property to the action in ordner to help the layouter, * * <pre> * abstractActions[0].putValue("tag", "ok") * </pre> * * @param abstractActions * list */ public void setLeftActions(final AbstractAction... abstractActions) { this.actions = abstractActions; } /** * Sets the returnvalue and saves the don't show again states to the * database * * @param b */ protected void setReturnmask(final boolean b) { this.returnBitMask = b ? Dialog.RETURN_OK : Dialog.RETURN_CANCEL; if (BinaryLogic.containsAll(this.flagMask, Dialog.STYLE_SHOW_DO_NOT_DISPLAY_AGAIN)) { if (this.dontshowagain.isSelected() && this.dontshowagain.isEnabled()) { this.returnBitMask |= Dialog.RETURN_DONT_SHOW_AGAIN; try { if (BinaryLogic.containsAll(this.flagMask, Dialog.LOGIC_DONT_SHOW_AGAIN_DELETE_ON_EXIT)) { AbstractDialog.SESSION_DONTSHOW_AGAIN.put(this.getDontShowAgainKey(), this.returnBitMask); } else { JSonStorage.getStorage("Dialogs").put(this.getDontShowAgainKey(), this.returnBitMask); } } catch (final Exception e) { Log.exception(e); } } } } /** * @param title2 */ protected void setTitle(final String title2) { this.getDialog().setTitle(title2); } /** * Returns an id of the dialog based on it's title; */ @Override public String toString() { return ("dialog-" + this.getTitle()).replaceAll("\\W", "_"); } public void windowActivated(final WindowEvent arg0) { } public void windowClosed(final WindowEvent arg0) { } public void windowClosing(final WindowEvent arg0) { if (this.closeAllowed()) { Log.L.fine("Answer: Button<[X]>"); this.returnBitMask |= Dialog.RETURN_CLOSED; this.dispose(); } else { Log.L.fine("(Answer: Tried [X] bot not allowed)"); } } public void windowDeactivated(final WindowEvent arg0) { } public void windowDeiconified(final WindowEvent arg0) { } public void windowIconified(final WindowEvent arg0) { } public void windowOpened(final WindowEvent arg0) { } }