001 /* JAPI - (Yet another (hopefully) useful) Java API 002 * 003 * Copyright (C) 2004-2006 Christian Hujer 004 * 005 * This program is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU General Public License as 007 * published by the Free Software Foundation; either version 2 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, but 011 * WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * General Public License for more details. 014 * 015 * You should have received a copy of the GNU General Public License 016 * along with this program; if not, write to the Free Software 017 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 018 * 02111-1307, USA. 019 */ 020 021 package net.sf.japi.swing.prefs; 022 023 import java.awt.BorderLayout; 024 import java.awt.CardLayout; 025 import java.awt.Component; 026 import java.util.HashMap; 027 import java.util.Map; 028 import javax.swing.Action; 029 import static javax.swing.BorderFactory.createEmptyBorder; 030 import javax.swing.Box; 031 import javax.swing.DefaultListCellRenderer; 032 import javax.swing.JButton; 033 import javax.swing.JComponent; 034 import javax.swing.JDialog; 035 import javax.swing.JList; 036 import javax.swing.JOptionPane; 037 import javax.swing.JPanel; 038 import javax.swing.JScrollPane; 039 import javax.swing.SwingConstants; 040 import javax.swing.border.Border; 041 import javax.swing.event.ListSelectionEvent; 042 import javax.swing.event.ListSelectionListener; 043 import net.sf.japi.swing.ActionFactory; 044 045 /** Panel to display preferences. 046 * @serial exclude This class is not intended to be serialized. 047 * @author <a href="mailto:chris@riedquat.de">Christian Hujer</a> 048 */ 049 @SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"}) 050 public final class PreferencesPane extends JOptionPane implements ListSelectionListener { 051 052 /** Action Factory. */ 053 private static final ActionFactory ACTION_FACTORY = ActionFactory.getFactory("net.sf.japi.swing.prefs"); 054 055 /** A map for DIALOGS that are already displaying. 056 * This map is used to prevent the dialog for the same PreferencesGroup be shown twice within the same application. 057 */ 058 private static final Map<PreferencesGroup,JDialog> DIALOGS = new HashMap<PreferencesGroup,JDialog>(); 059 060 /** The group of preferences to display. */ 061 private final PreferencesGroup prefs; 062 063 /** The currently selected preferences module. */ 064 private Prefs currentPref; 065 066 /** Action for help. */ 067 private final Action helpAction = ACTION_FACTORY.createAction(false, "help" , this); 068 069 /** Action for defaults. */ 070 private final Action defaultsAction = ACTION_FACTORY.createAction(false, "defaults", this); 071 072 /** Action for ok. */ 073 private final Action okAction = ACTION_FACTORY.createAction(false, "ok" , this); 074 075 /** Action for apply. */ 076 private final Action applyAction = ACTION_FACTORY.createAction(false, "apply" , this); 077 078 /** Action for revert. */ 079 private final Action revertAction = ACTION_FACTORY.createAction(false, "revert" , this); 080 081 /** Action for cancel. */ 082 private final Action cancelAction = ACTION_FACTORY.createAction(false, "cancel" , this); 083 084 /** CardLayout for switching between prefs modules. 085 * @see #cardPanel 086 */ 087 private final CardLayout cards = new CardLayout(); 088 089 /** Panel where the CardLayout for switching between prefs modules is used. 090 * @see #cards 091 */ 092 private final JPanel cardPanel = new JPanel(cards); 093 094 /** Show Preferences. 095 * @param parentComponent determines the Frame in which the dialog is displayed; if <code>null</code>, or if the <code>parentComponent</code> has 096 * no <code>Frame</code>, a default <code>Frame</code> is used 097 * @param prefs PreferencesGroup to be displayed 098 * @param modal <code>true</code> if the displayed dialog should be modal, otherwise <code>false</code> 099 */ 100 public static void showPreferencesDialog(final Component parentComponent, final PreferencesGroup prefs, final boolean modal) { 101 synchronized (DIALOGS) { 102 if (DIALOGS.containsKey(prefs)) { 103 DIALOGS.get(prefs).toFront(); 104 } else { 105 final PreferencesPane pane = new PreferencesPane(prefs); 106 final JDialog dialog = pane.createDialog(parentComponent, prefs.getTitle()); 107 DIALOGS.put(prefs, dialog); 108 dialog.setResizable(true); 109 dialog.setModal(modal); 110 dialog.setVisible(true); 111 } 112 } 113 } 114 115 /** Create a PreferencesPane. 116 * @param prefs PreferencesGroup to create panel for 117 */ 118 private PreferencesPane(final PreferencesGroup prefs) { 119 this.prefs = prefs; 120 setMessage(createMessage()); 121 setOptions(createOptions()); 122 } 123 124 /** Create the Message. 125 * @return subpanel 126 */ 127 private JComponent createMessage() { 128 final JPanel panel = new JPanel(new BorderLayout()); 129 panel.add(createList(), BorderLayout.WEST); 130 panel.add(createPanel(), BorderLayout.CENTER); 131 return panel; 132 } 133 134 /** Create the list. 135 * @return list 136 */ 137 private JComponent createList() { 138 final JList list = new JList(prefs); 139 list.setCellRenderer(new PrefsListCellRenderer()); 140 list.setSelectedIndex(0); 141 list.addListSelectionListener(this); 142 return new JScrollPane(list); 143 } 144 145 /** Create the Panel. 146 * @return panel 147 */ 148 private JComponent createPanel() { 149 int index = 0; 150 for (final Prefs pref : prefs) { 151 cardPanel.add(Integer.toString(index++), pref.getEditComponent()); 152 } 153 currentPref = prefs.getElementAt(0); 154 return cardPanel; 155 } 156 157 /** Create the Options. 158 * @return options 159 */ 160 private Object[] createOptions() { 161 return new Object[] { 162 new JButton(helpAction), 163 new JButton(defaultsAction), 164 Box.createHorizontalStrut(50), 165 new JButton(okAction), 166 new JButton(applyAction), 167 Box.createHorizontalStrut(50), 168 new JButton(revertAction), 169 new JButton(cancelAction), 170 }; 171 } 172 173 /** {@inheritDoc} */ 174 public void valueChanged(final ListSelectionEvent e) { 175 if (e.getValueIsAdjusting()) { 176 return; 177 } 178 final int index = ((JList) e.getSource()).getSelectedIndex(); 179 final Prefs newPref = prefs.getElementAt(index); 180 //noinspection ObjectEquality 181 if (currentPref == newPref) { 182 return; 183 } 184 if (currentPref.isChanged()) { 185 final int result = ACTION_FACTORY.showConfirmDialog(this, YES_NO_CANCEL_OPTION, QUESTION_MESSAGE, "prefsChanged", currentPref.getListLabelText()); 186 switch (result) { 187 case CLOSED_OPTION: 188 case CANCEL_OPTION: 189 ((JList) e.getSource()).setSelectedValue(currentPref, true); 190 return; 191 case YES_OPTION: 192 currentPref.apply(); 193 break; 194 case NO_OPTION: 195 default: 196 } 197 } 198 currentPref = newPref; 199 cards.show(cardPanel, Integer.toString(index)); 200 } 201 202 /** Action method for cancel. 203 * @used 204 */ 205 public void cancel() { 206 setValue(CANCEL_OPTION); 207 } 208 209 /** {@inheritDoc} */ 210 @Override public void setValue(final Object newValue) { 211 super.setValue(newValue); 212 //noinspection ObjectEquality 213 if (newValue != null && newValue != UNINITIALIZED_VALUE) { 214 synchronized (DIALOGS) { 215 DIALOGS.remove(prefs).dispose(); 216 } 217 } 218 } 219 220 /** Action method for defaults. 221 * @used 222 */ 223 public void defaults() { 224 currentPref.defaults(); 225 } 226 227 /** Action method for help. 228 * @used 229 */ 230 public void help() { 231 // TODO 232 } 233 234 /** Action method for ok. 235 * @used 236 */ 237 @SuppressWarnings({"InstanceMethodNamingConvention"}) 238 public void ok() { 239 apply(); 240 setValue(OK_OPTION); 241 } 242 243 /** Action method for apply. 244 * @used 245 */ 246 public void apply() { 247 if (currentPref.isChanged()) { 248 currentPref.apply(); 249 } 250 } 251 252 /** Action method for revert. 253 * @used 254 */ 255 public void revert() { 256 if (currentPref.isChanged()) { 257 currentPref.revert(); 258 } 259 } 260 261 /** Class for rendering preferences list items. */ 262 private static final class PrefsListCellRenderer extends DefaultListCellRenderer { 263 264 /** The border. 265 * For some reason it gets lost when set in the initializer, so we store it and set it each time the renderer is used. 266 */ 267 private Border border = createEmptyBorder(10, 10, 10, 10); 268 269 /** Create a PrefsListCellRenderer. */ 270 PrefsListCellRenderer() { 271 setHorizontalTextPosition(SwingConstants.CENTER); 272 setVerticalTextPosition(SwingConstants.BOTTOM); 273 setHorizontalAlignment(SwingConstants.CENTER); 274 setVerticalAlignment(SwingConstants.CENTER); 275 } 276 277 /** {@inheritDoc} */ 278 @SuppressWarnings({"ReturnOfThis"}) 279 @Override public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean cellHasFocus) { 280 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 281 setBorder(border); 282 final Prefs pref = (Prefs) value; 283 setText(pref.getListLabelText()); 284 setIcon(pref.getListLabelIcon()); 285 return this; 286 } 287 288 } // class PrefsListCellRenderer 289 290 } // class PreferencesPane