001 /*
002 * File $Workfile: ControlPanel.java $
003 * $Archive: /Tower Of Hanoi/toh/src/net/pacbell/jfai/toh/ui/ControlPanel.java $
004 * Last changed by $Author: Jürgen Failenschmid $
005 * Last modified $Modtime: 12/09/05 4:57p $
006 * Last checked in $Date: 12/09/05 5:10p $
007 * $Revision: 3 $
008 *
009 *
010 * Copyright (C) 2004-2005 Jürgen Failenschmid. All rights reserved.
011 */
012
013 package net.pacbell.jfai.toh.ui;
014
015 import java.awt.GridBagConstraints;
016 import java.awt.GridBagLayout;
017 import java.awt.GridLayout;
018 import java.awt.Insets;
019 import java.util.Dictionary;
020
021 import javax.swing.ButtonGroup;
022 import javax.swing.JButton;
023 import javax.swing.JLabel;
024 import javax.swing.JPanel;
025 import javax.swing.JSlider;
026 import javax.swing.JToggleButton;
027 import javax.swing.SwingConstants;
028
029 /**
030 * A panel containing the configuration controls, such as sliders, buttons.
031 *
032 * @author {@link <a href="http://www.anycpu.com">Jürgen Failenschmid</a>}
033 */
034 @SuppressWarnings("serial")
035 public class ControlPanel extends JPanel
036 {
037 private static final String SLIDER_MIN_LABEL = "1"; //$NON-NLS-1$
038 private static final int ACTION_BUTTON_GRID_GAP_SIZE = 5;
039 private static final int BORDER_SIZE = 5;
040 private static final int DISKS_SLIDER_LABEL_INCREMENT = 5;
041 private static final int DISKS_SLIDER_LABEL_START = 5;
042 private static final int DISKS_SLIDER_MIN_UPPER_VALUE = 20;
043 private static final String DISKS_SLIDER_MIN_LABEL = SLIDER_MIN_LABEL;
044 private static final int PIN_PICK_BUTTON_GRID_GAP_SIZE = 10;
045 private static final int SPEED_SLIDER_LABEL_INCREMENT = 5;
046 private static final int SPEED_SLIDER_LABEL_START = 5;
047 private static final String SPEED_SLIDER_MIN_LABEL = SLIDER_MIN_LABEL;
048 private static final int SPEED_SLIDER_UPPER_VALUE = 10;
049
050 private JButton applyButton;
051 private JButton cancelButton;
052 private JSlider disksSlider;
053 private JToggleButton invisibleButton;
054 private JToggleButton sourceButton;
055 private JSlider speedSlider;
056 private JButton startButton;
057 private JToggleButton targetButton;
058
059 /**
060 * Creates a panel with the various buttons and sliders for the
061 * configuration of the puzzle.
062 *
063 * @param sourcePin label for the button to select the source pin
064 * @param targetPin label for the button to select the target pin
065 * @param numberOfDisks the total number of disks of the puzzle
066 */
067 public ControlPanel(String sourcePin, String targetPin, int numberOfDisks) {
068 super(new GridBagLayout());
069
070 // Vertical and horizontal border for each component
071 final Insets insets = new Insets(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE,
072 BORDER_SIZE);
073 /*
074 * Constraints that apply to all components: adjust size in x and y,
075 * occupy 1 cell in y (default), relative placement (default)
076 */
077 final GridBagConstraints constraints = new GridBagConstraints();
078 constraints.fill = GridBagConstraints.BOTH;
079 constraints.insets = insets;
080
081 int row = 0;
082
083 addNumberOfDisksComponents(constraints, row, numberOfDisks);
084 row++;
085
086 addPinSelectionComponents(constraints, row, sourcePin, targetPin);
087 row++;
088
089 addActionButtons(constraints, row);
090
091 addSpeedComponents(constraints, row);
092 }
093
094 /**
095 * Gets the apply button.
096 *
097 * @return the applyButton.
098 */
099 public JButton getApplyButton() {
100 return applyButton;
101 }
102
103 /**
104 * Gets the cancel button.
105 *
106 * @return the cancelButton.
107 */
108 public JButton getCancelButton() {
109 return cancelButton;
110 }
111
112 /**
113 * Gets the slider for the number of disks.
114 *
115 * @return the disksSlider.
116 */
117 public JSlider getDisksSlider() {
118 return disksSlider;
119 }
120
121 /**
122 * Gets the invisible button.
123 *
124 * @return the invisibleButton.
125 */
126 public JToggleButton getInvisibleButton() {
127 return invisibleButton;
128 }
129
130 /**
131 * Gets the source button.
132 *
133 * @return the sourceButton.
134 */
135 public JToggleButton getSourceButton() {
136 return sourceButton;
137 }
138
139 /**
140 * Gets the speed slider.
141 *
142 * @return the speedSlider.
143 */
144 public JSlider getSpeedSlider() {
145 return speedSlider;
146 }
147
148 /**
149 * Gets the start button.
150 *
151 * @return the startButton.
152 */
153 public JButton getStartButton() {
154 return startButton;
155 }
156
157 /**
158 * Gets the target button.
159 *
160 * @return the targetButton.
161 */
162 public JToggleButton getTargetButton() {
163 return targetButton;
164 }
165
166 private void addActionButtons(GridBagConstraints constraints, int row) {
167 /*
168 * Puts all action buttons in a row into a JPanel with a GridLayout to
169 * make all buttons the same size. Uses 5 pixels as horizontal and
170 * vertical gap between buttons.
171 */
172 final JPanel panel = new JPanel(new GridLayout(1, 0,
173 ACTION_BUTTON_GRID_GAP_SIZE, ACTION_BUTTON_GRID_GAP_SIZE));
174
175 // Action button to accept changes
176 setApplyButton(new JButton("Apply")); //$NON-NLS-1$
177 panel.add(getApplyButton());
178 getApplyButton().setToolTipText("Press this button to apply changes"); //$NON-NLS-1$
179
180 // Cancel button to reject changes
181 setCancelButton(new JButton("Cancel")); //$NON-NLS-1$
182 panel.add(getCancelButton());
183 getCancelButton().setToolTipText("Press this button to undo changes"); //$NON-NLS-1$
184
185 /*
186 * Button to start the animation - will change depending on solver's
187 * state
188 */
189 setStartButton(new JButton("Solve")); //$NON-NLS-1$
190 panel.add(getStartButton());
191
192 /*
193 * Adds the button panel to the container: first column, 2 cells wide,
194 * don't grow
195 */
196 constraints.gridx = 0;
197 constraints.gridy = row;
198 constraints.gridwidth = 2;
199 constraints.weightx = 0;
200 constraints.weighty = 0;
201 add(panel, constraints);
202 }
203
204 @SuppressWarnings({"boxing", "unchecked" })
205 private void addNumberOfDisksComponents(GridBagConstraints constraints,
206 int row, final int numberOfDisks)
207 {
208 // Label for number of disks: first column, 1 cell wide, don't grow
209 constraints.gridx = 0;
210 constraints.gridy = row;
211 constraints.gridwidth = 1;
212 constraints.weightx = 0;
213 constraints.weighty = 0;
214 add(new JLabel("Number of disks:"), constraints); //$NON-NLS-1$
215
216 // Slider for number of disks, second column, use rest of row
217 setDisksSlider(new JSlider(1, Math.max(DISKS_SLIDER_MIN_UPPER_VALUE,
218 numberOfDisks), numberOfDisks));
219 final Dictionary<Integer, JLabel> labels = getDisksSlider()
220 .createStandardLabels(DISKS_SLIDER_LABEL_INCREMENT,
221 DISKS_SLIDER_LABEL_START);
222 labels.put(1, new JLabel(DISKS_SLIDER_MIN_LABEL));
223 getDisksSlider().setLabelTable(labels);
224 getDisksSlider().setMinorTickSpacing(1);
225 getDisksSlider().setPaintTicks(true);
226 getDisksSlider().setPaintLabels(true);
227 getDisksSlider().setSnapToTicks(true);
228 constraints.gridx = 1;
229 constraints.gridy = row;
230 constraints.gridwidth = GridBagConstraints.REMAINDER;
231 constraints.weightx = 1;
232 constraints.weighty = 1;
233 add(getDisksSlider(), constraints);
234 getDisksSlider().setToolTipText("Select the number of disks"); //$NON-NLS-1$
235 }
236
237 private void addPinSelectionComponents(GridBagConstraints constraints,
238 int row, String sourcePin, String targetPin)
239 {
240 final JPanel panel = new JPanel(new GridLayout(1, 0,
241 PIN_PICK_BUTTON_GRID_GAP_SIZE, PIN_PICK_BUTTON_GRID_GAP_SIZE));
242
243 // Group for toggle buttons
244 final ButtonGroup buttonGroup = new ButtonGroup();
245
246 // Label for source pin
247 panel.add(new JLabel("Source:", SwingConstants.RIGHT)); //$NON-NLS-1$
248
249 // Toggle button for source pin
250 setSourceButton(new JToggleButton(sourcePin));
251 buttonGroup.add(getSourceButton());
252 panel.add(getSourceButton());
253 getSourceButton().setToolTipText(
254 "Press button to select the source pin. Click on a pin."); //$NON-NLS-1$
255
256 // Label for target pin
257 panel.add(new JLabel("Target:", SwingConstants.RIGHT)); //$NON-NLS-1$
258
259 // Toggle button for target pin
260 setTargetButton(new JToggleButton(targetPin));
261 buttonGroup.add(getTargetButton());
262 panel.add(getTargetButton());
263 getTargetButton().setToolTipText(
264 "Press button to select the target pin. Click on a pin."); //$NON-NLS-1$
265
266 // Adds panel to grid bag: 1st column, all columns, grow
267 constraints.gridx = 0;
268 constraints.gridy = row;
269 constraints.gridwidth = GridBagConstraints.REMAINDER;
270 constraints.weightx = 1;
271 constraints.weighty = 1;
272 add(panel, constraints);
273
274 /*
275 * Invisible toggle button used to remove focus from the two visible
276 * buttons
277 */
278 setInvisibleButton(new JToggleButton());
279 buttonGroup.add(getInvisibleButton());
280 getInvisibleButton().setVisible(false);
281 constraints.gridx = 0;
282 constraints.gridy = row;
283 constraints.gridwidth = 0;
284 constraints.weightx = 0;
285 constraints.weighty = 0;
286 add(getInvisibleButton(), constraints);
287 }
288
289 @SuppressWarnings({"boxing", "unchecked" })
290 private void addSpeedComponents(GridBagConstraints constraints, int row)
291 {
292 // Label for speed: 2nd column, 1 cell wide, don't grow
293 constraints.gridx = 2;
294 constraints.gridy = row;
295 constraints.gridwidth = GridBagConstraints.RELATIVE;
296 constraints.weightx = 0;
297 constraints.weighty = 0;
298 add(new JLabel("Seconds per move:"), constraints); //$NON-NLS-1$
299
300 /*
301 * Slider for speed, 3rd column, use rest of row. Speed of animator is
302 * in milliseconds.
303 */
304 setSpeedSlider(new JSlider(1, SPEED_SLIDER_UPPER_VALUE,
305 (int) DiskMoveAnimator.getDuration() / DiskMoveAnimator.MS_PER_S));
306 final Dictionary<Integer, JLabel> labels = getSpeedSlider()
307 .createStandardLabels(SPEED_SLIDER_LABEL_INCREMENT,
308 SPEED_SLIDER_LABEL_START);
309 labels.put(1, new JLabel(SPEED_SLIDER_MIN_LABEL));
310 getSpeedSlider().setLabelTable(labels);
311 getSpeedSlider().setMinorTickSpacing(1);
312 getSpeedSlider().setPaintTicks(true);
313 getSpeedSlider().setPaintLabels(true);
314 getSpeedSlider().setSnapToTicks(true);
315
316 constraints.gridx = 3;
317 constraints.gridy = row;
318 constraints.gridwidth = GridBagConstraints.REMAINDER;
319 constraints.weightx = 1;
320 constraints.weighty = 1;
321 add(getSpeedSlider(), constraints);
322 getSpeedSlider().setToolTipText("Select the time per move"); //$NON-NLS-1$
323 }
324
325 /**
326 * Sets the apply button.
327 *
328 * @param applyButton The applyButton to set.
329 */
330 private void setApplyButton(JButton applyButton) {
331 this.applyButton = applyButton;
332 }
333
334 /**
335 * Sets the cancel button.
336 *
337 * @param cancelButton The cancelButton to set.
338 */
339 private void setCancelButton(JButton cancelButton) {
340 this.cancelButton = cancelButton;
341 }
342
343 /**
344 * Sets the slider for the number of disks.
345 *
346 * @param disksSlider The disksSlider to set.
347 */
348 private void setDisksSlider(JSlider disksSlider) {
349 this.disksSlider = disksSlider;
350 }
351
352 /**
353 * Sets the invisible button.
354 *
355 * @param invisibleButton The invisibleButton to set.
356 */
357 private void setInvisibleButton(JToggleButton invisibleButton) {
358 this.invisibleButton = invisibleButton;
359 }
360
361 /**
362 * Sets the source button.
363 *
364 * @param sourceButton The sourceButton to set.
365 */
366 private void setSourceButton(JToggleButton sourceButton) {
367 this.sourceButton = sourceButton;
368 }
369
370 /**
371 * Sets the speed slider.
372 *
373 * @param speedSlider The speedSlider to set.
374 */
375 private void setSpeedSlider(JSlider speedSlider) {
376 this.speedSlider = speedSlider;
377 }
378
379 /**
380 * Sets the start button.
381 *
382 * @param startButton The startButton to set.
383 */
384 private void setStartButton(JButton startButton) {
385 this.startButton = startButton;
386 }
387
388 /**
389 * Sets the target button.
390 *
391 * @param targetButton The targetButton to set.
392 */
393 private void setTargetButton(JToggleButton targetButton) {
394 this.targetButton = targetButton;
395 }
396 }