/* * Bibliothek - DockingFrames * Library built on Java/Swing, allows the user to "drag and drop" * panels containing any Swing-Component the developer likes to add. * * Copyright (C) 2008 Benjamin Sigg * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Benjamin Sigg * benjamin_sigg@gmx.ch * CH - Switzerland */ package bibliothek.extension.gui.dock.preference; import java.awt.Component; import java.awt.Dialog; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.Insets; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.List; import javax.swing.AbstractAction; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JPanel; import javax.swing.SwingUtilities; import bibliothek.gui.dock.util.text.DialogText; import bibliothek.gui.dock.util.text.SwingActionText; import bibliothek.gui.dock.util.text.TextValue; /** * An abstract dialog used to show the content of some {@link PreferenceModel}. The * exact graphical user interface for the model depends on the subclass.<br> * <b>Note: </b> clients using this panel have to call {@link #destroy()}. The only time {@link #destroy()} has * not to be called is if the dialog was shown using {@link #openDialog(Component, boolean)} and {@link #isDestroyOnClose() destroyOnClose} * was set to <code>true</code>. * @author Benjamin Sigg * * @param <M> What kind of model this dialog can show */ public abstract class AbstractPreferenceDialog<M extends PreferenceModel> extends JPanel{ private M model; private JComponent content; private JDialog dialog; private boolean destroyOnClose; /** various texts that are used by this dialog */ private List<TextValue> texts = new ArrayList<TextValue>(); /** * Creates a new dialog. * @param destroyOnClose if set to <code>true</code>, then {@link #destroy()} is automatically called * if {@link #close()} is called. Clients have to call {@link #destroy()} manually if they are not * using {@link #openDialog(Component, boolean)}. */ public AbstractPreferenceDialog( boolean destroyOnClose ){ this( null, destroyOnClose ); } /** * Creates a new dialog using the given model. * @param model the model to use * @param destroyOnClose if set to <code>true</code>, then {@link #destroy()} is automatically called * if {@link #close()} is called. Clients have to call {@link #destroy()} manually if they are not * using {@link #openDialog(Component, boolean)}. */ public AbstractPreferenceDialog( M model, boolean destroyOnClose ){ init( model, destroyOnClose ); } /** * A constructor which does not initialize this dialog. Subclasses must * call {@link #init(PreferenceModel, boolean)} to finish constructing this dialog. * @param init whether to call {@link #init(PreferenceModel, boolean)}. * @param model the model to use, can be <code>null</code> * @param destroyOnClose if set to <code>true</code>, then {@link #destroy()} is automatically called * if {@link #close()} is called. Clients have to call {@link #destroy()} manually if they are not * using {@link #openDialog(Component, boolean)}. */ protected AbstractPreferenceDialog( boolean init, M model, boolean destroyOnClose ){ if( init ){ init( model, destroyOnClose ); } } /** * Creates the contents of this dialog. * @param model the model to use, can be <code>null</code> * @param destroyOnClose if set to <code>true</code>, then {@link #destroy()} is automatically called * if {@link #close()} is called. Clients have to call {@link #destroy()} manually if they are not * using {@link #openDialog(Component, boolean)}. */ protected void init( M model, boolean destroyOnClose ){ if( content != null ) throw new IllegalStateException( "Already initialized" ); setLayout( new GridBagLayout() ); this.model = model; this.destroyOnClose = destroyOnClose; content = getContent(); JPanel buttons = new JPanel( new GridLayout( 1, 4 )); buttons.add( new JButton( new ApplyAction() )); buttons.add( new JButton( new ResetAction() )); buttons.add( new JButton( new OkAction() )); buttons.add( new JButton( new CancelAction() )); add( content, new GridBagConstraints( 0, 0, 1, 1, 1.0, 1000.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets( 1, 1, 1, 1 ), 0, 0 )); add( buttons, new GridBagConstraints( 0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.LAST_LINE_END, GridBagConstraints.NONE, new Insets( 1, 1, 1, 1 ), 0, 0 ) ); setModel( model ); } /** * Gets the component which will show the {@link #getModel() model} * of this dialog. * @return the component */ protected abstract JComponent getContent(); /** * Informs subclasses that the model has changed and that they might * setup the {@link #getContent() content} again. * @param model the new model, can be <code>null</code> */ protected abstract void setModelForContent( M model ); /** * Sets the model of this dialog. * @param model the new model */ public void setModel( M model ){ this.model = model; if( model == null ){ for( TextValue text : texts ){ text.setManager( null ); } } else{ for( TextValue text : texts ){ text.setManager( model.getController().getTexts() ); } } setModelForContent( model ); } /** * Gets the model which is shown on this dialog. * @return the model */ public M getModel(){ return model; } /** * Opens the dialog (if not yet open) and lets the user make the changes * of the preferences. This method will call {@link PreferenceModel#read()} and * {@link PreferenceModel#write()} to reset or to apply the changes of the user. * @param owner the owner of the dialog * @param modal whether the dialog should be modal */ public void openDialog( Component owner, boolean modal ){ if( dialog != null ) return; dialog = createDialog( owner ); dialog.setModal( modal ); dialog.add( this ); doReset(); dialog.pack(); dialog.setSize( (int)(dialog.getWidth() * 1.5), dialog.getHeight() ); dialog.setLocationRelativeTo( owner ); dialog.setVisible( true ); } private JDialog createDialog( Component owner ){ JDialog dialog; if( owner == null ){ dialog = new JDialog(); } else{ Window window = SwingUtilities.getWindowAncestor( owner ); if( window instanceof Frame ){ dialog = new JDialog( (Frame)window ); } else if( window instanceof Dialog ){ dialog = new JDialog( (Dialog)window ); } else{ dialog = new JDialog(); } } final DialogText title = new DialogText( "preference.dialog.title", dialog ){ protected void changed( String oldValue, String newValue ){ getDialog().setTitle( newValue ); } }; dialog.setDefaultCloseOperation( JDialog.DO_NOTHING_ON_CLOSE ); dialog.addWindowListener( new WindowAdapter(){ @Override public void windowClosing( WindowEvent e ) { doCancel(); } @Override public void windowClosed( WindowEvent e ){ texts.remove( title ); title.setController( null ); } @Override public void windowOpened( WindowEvent e ){ texts.add( title ); if( model != null ){ title.setController( model.getController() ); } } }); return dialog; } /** * Applies all changes and closes the dialog. */ public void doOk(){ doApply(); close(); } /** * Applies all changes but does not close the dialog. */ public void doApply(){ getModel().write(); } /** * Closes the dialog without saving and changes */ public void doCancel(){ doReset(); close(); } /** * Resets all preferences to the value they had when the dialog opened */ public void doReset(){ getModel().read(); } /** * Makes the dialog invisible. */ public void close(){ if( dialog != null ){ dialog.dispose(); dialog.remove( this ); dialog = null; } if( destroyOnClose ){ destroy(); } } /** * Allows this dialog to free any resources that it used. Should be called once this dialog is no longer * used, otherwise memory leaks can appear. */ public void destroy(){ setModel( null ); } /** * Tells whether {@link #destroy()} is called automatically or not. * @return whether to free resources * @see #setDestroyOnClose(boolean) */ public boolean isDestroyOnClose(){ return destroyOnClose; } /** * If set to <code>true</code> then {@link #destroy()} is automatically called if this dialog is * {@link #close() closed} * @param destroyOnClose whether to free resources */ public void setDestroyOnClose( boolean destroyOnClose ){ this.destroyOnClose = destroyOnClose; } private class OkAction extends AbstractAction{ public OkAction(){ texts.add( new SwingActionText( "preference.dialog.ok.text", NAME, this ) ); texts.add( new SwingActionText( "preference.dialog.ok.description", SHORT_DESCRIPTION, this ) ); } public void actionPerformed( ActionEvent e ) { doOk(); } } private class ApplyAction extends AbstractAction{ public ApplyAction(){ texts.add( new SwingActionText( "preference.dialog.apply.text", NAME, this ) ); texts.add( new SwingActionText( "preference.dialog.apply.description", SHORT_DESCRIPTION, this ) ); } public void actionPerformed( ActionEvent e ) { doApply(); } } private class CancelAction extends AbstractAction{ public CancelAction(){ texts.add( new SwingActionText( "preference.dialog.cancel.text", NAME, this ) ); texts.add( new SwingActionText( "preference.dialog.cancel.description", SHORT_DESCRIPTION, this ) ); } public void actionPerformed( ActionEvent e ) { doCancel(); } } private class ResetAction extends AbstractAction{ public ResetAction(){ texts.add( new SwingActionText( "preference.dialog.reset.text", NAME, this ) ); texts.add( new SwingActionText( "preference.dialog.reset.description", SHORT_DESCRIPTION, this ) ); } public void actionPerformed( ActionEvent e ) { doReset(); } } }