View Javadoc

1   /*
2    * Copyright (c) 2003-2008 by Cosylab d. d. This file is part of CosyBeans-Common.
3    * CosyBeans-Common is free software: you can redistribute it and/or modify it under the
4    * terms of the GNU General Public License as published by the Free Software Foundation,
5    * either version 3 of the License, or (at your option) any later version.
6    * CosyBeans-Common is distributed in the hope that it will be useful, but WITHOUT ANY
7    * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
8    * PARTICULAR PURPOSE. See the GNU General Public License for more details. You should
9    * have received a copy of the GNU General Public License along with CosyBeans-Common. If
10   * not, see <http://www.gnu.org/licenses/>.
11   */
12  
13  package com.cosylab.gui.components.slider;
14  
15  import java.awt.Color;
16  import java.awt.Component;
17  import java.awt.Dimension;
18  import java.awt.Font;
19  import java.awt.GridBagConstraints;
20  import java.awt.GridBagLayout;
21  import java.awt.Insets;
22  import java.awt.event.ActionEvent;
23  import java.awt.event.ActionListener;
24  import java.awt.event.MouseAdapter;
25  import java.awt.event.MouseEvent;
26  import java.beans.Beans;
27  
28  import javax.swing.Icon;
29  import javax.swing.JButton;
30  import javax.swing.JPanel;
31  import javax.swing.JTextField;
32  import javax.swing.Timer;
33  
34  import com.cosylab.events.SetListener;
35  import com.cosylab.gui.components.NumberField;
36  import com.cosylab.gui.components.ResizableTextLabel;
37  import com.cosylab.gui.components.Slider;
38  import com.cosylab.gui.components.Slider.Button;
39  import com.cosylab.gui.components.util.ColorHelper;
40  import com.cosylab.gui.components.util.IconHelper;
41  import com.cosylab.util.PrintfFormat;
42  
43  /**
44   * Button bar for exact control over slider behaviour. Consists of six buttons (3 for
45   * decrementing, 3 for incrementing the value) and editable text field for entering exact
46   * values. Text field is updated to reflect current value.
47   * 
48   * @author <a href="mailto:ales.pucelj@cosylab.com">Ales Pucelj</a>
49   * @version $id$
50   */
51  public class NavigationBar extends JPanel {
52  	private static final long serialVersionUID = 1L;
53  	
54  	private class HoldMouseListener extends MouseAdapter {
55  		private Button button;
56  		public HoldMouseListener(Button button) {
57  			this.button = button;
58  		}
59  		public void mousePressed(MouseEvent me) {
60  			if (slider.isContinuousModeEnabled()) {
61  				setAutoUpdateButton(this.button);
62  				getAutoUpdateTimer().start();
63  			}
64  		}
65  
66  		public void mouseReleased(MouseEvent me) {
67  			getAutoUpdateTimer().stop();
68  		}
69  	}
70  
71  	private boolean unitsVisible = true;
72  	private String units;
73  	private String format = "%1.4f";
74  
75  	private NumberField valueEditor;
76  	private ResizableTextLabel unitsLabel;
77  	private Slider slider;
78  
79  	private PrintfFormat formater;
80  
81  	private JButton fastIncrement;
82  	private JButton fastDecrement;
83  	private JButton bitIncrement;
84  	private JButton bitDecrement;
85  	private JButton slowIncrement;
86  	private JButton slowDecrement;
87  	
88  	private Button autoUpdateButton = null;
89  
90  	private Timer autoUpdateTimer;
91  	// Default inital delay before auto incerement/decrement is applied
92  	private int continuousModeDelayTime = 1000;
93  	// Defautl time between individual increment/decremtn repetitions
94  	private int continuousModeRepeatTime = 100;
95  
96  	/**
97  	 * Constructor for NavigationBar.
98  	 * 
99  	 * @param owner Slider
100 	 * @throws IllegalArgumentException if owner is null.
101 	 */
102 	public NavigationBar(Slider owner) {
103 		super();
104 
105 		if (owner == null) {
106 			throw new IllegalArgumentException("Cannot create new NavigationBar with null parameter.");
107 		}
108 
109 		slider = owner;
110 		initialize();
111 	}
112 	private JButton createButton(String description, String iconName, int x, int min) {
113 		JButton button = new JButton();
114 		button.setBackground(ColorHelper.getCosyControl());
115 		button.setToolTipText(description);
116 		button.setPreferredSize(new Dimension(25,20));
117 		button.setMinimumSize(new Dimension(min,18));
118 
119 		try {
120 			button.setIcon(IconHelper.createIcon("icons/navigation/" + iconName));
121 		} catch (Exception e) {
122 			System.out.println("Icon resource not found: " + iconName);
123 		}
124 		add(button,new GridBagConstraints(x,0,1,1,1,0,GridBagConstraints.CENTER,GridBagConstraints.HORIZONTAL,new Insets(1,1,1,1),0,0));
125 		return button;
126 	}
127 
128 	/*
129 	 * (non-Javadoc)
130 	 * @see javax.swing.JComponent#setEnabled(boolean)
131 	 */
132 	@Override
133 	public void setEnabled(boolean enabled) {
134 		super.setEnabled(enabled);
135 		/*
136 		 * If Slider is disabled, valueEditor NumberField should still follow current
137 		 * value setting. On the other hand user should not be able to edit it.
138 		 */
139 		valueEditor.setEditable(enabled);
140 		/*
141 		 * Setting valueEditor to not focusable prevents updating slider value of a
142 		 * disabled slider when focus is lost.
143 		 */
144 		valueEditor.setFocusable(enabled);
145 		unitsLabel.setEnabled(enabled);
146 		fastIncrement.setEnabled(enabled);
147 		fastDecrement.setEnabled(enabled);
148 		bitIncrement.setEnabled(enabled);
149 		bitDecrement.setEnabled(enabled);
150 		slowIncrement.setEnabled(enabled);
151 		slowDecrement.setEnabled(enabled);
152 	}
153 
154 	/**
155 	 * Sets the value to the text field.
156 	 * 
157 	 * @param value new value
158 	 */
159 	public void setValue(double value) {
160 		valueEditor.setDoubleValue(value);
161 		valueEditor.setToolTipText(String.valueOf(value));
162 	}
163 
164 	/**
165 	 * Returns text field value.
166 	 * 
167 	 * @return current value of text field.
168 	 */
169 	public double getValue() {
170 		return valueEditor.getDoubleValue();
171 	}
172 
173 	/**
174 	 * Sets the continuous mode delay time for the buttons hold update.
175 	 * 
176 	 * @param continuousModeDelayTime the initial delay time in millis
177 	 */
178 	public void setContinuousModeDelayTime(int continuousModeDelayTime) {
179 		this.continuousModeDelayTime = continuousModeDelayTime;
180 		this.getAutoUpdateTimer().setInitialDelay(continuousModeDelayTime);
181 	}
182 	
183 	/**
184 	 * Sets the continuous mode delay time for the buttons hold update.
185 	 * 
186 	 * @return the initial delay time in millis
187 	 */
188 	public int getContinuousModeDelayTime() {
189 		return continuousModeDelayTime;
190 	}
191 	
192 	/**
193 	 * Sets the continuous mode repeat time for the buttons hold update.
194 	 * 
195 	 * @param continuousModeRepeatTime the update period in millis
196 	 */
197 	public void setContinuousModeRepeatTime(int continuousModeRepeatTime) {
198 		this.continuousModeRepeatTime = continuousModeRepeatTime;
199 		this.getAutoUpdateTimer().setDelay(continuousModeRepeatTime);
200 	}
201 	
202 	/**
203 	 * Returns the continuous mode repeat time for the buttons hold update.
204 	 * 
205 	 * @return the update period in millis
206 	 */
207 	public int getContinuousModeRepeatTime() {
208 		return continuousModeRepeatTime;
209 	}
210 	
211 	/**
212 	 * Returns current format of text field.
213 	 * 
214 	 * @return current format of text field.
215 	 */
216 	public String getFormat() {
217 		return format;
218 	}
219 
220 	/**
221 	 * Sets new format for text field. This format is used only when displaying the value.
222 	 * 
223 	 * @param format to set for textfield.
224 	 */
225 	public void setFormat(String format) {
226 		this.format = format;
227 
228 		if (format == null) {
229 			format = "%1.4f";
230 		}
231 
232 		valueEditor.setFormat(format);
233 		formater = new PrintfFormat(format);
234 	}
235 
236 	/**
237 	 * Sets units to be displayed next to text field.
238 	 * 
239 	 * @param units string to set.
240 	 */
241 	public void setUnits(String units) {
242 		this.units = units;
243 
244 		if (units == null && Beans.isDesignTime()) {
245 			units = "<u>";
246 		}
247 
248 		unitsLabel.setText((unitsVisible ? units : ""));
249 	}
250 
251 	/**
252 	 * Returns units currently displayed next to edit field.
253 	 * 
254 	 * @return current units string.
255 	 */
256 	public String getUnits() {
257 		return units;
258 	}
259 
260 	private void initialize() {
261 		
262 		setLayout(new GridBagLayout());
263 		setOpaque(false);
264 
265 		formater = new PrintfFormat("%1.4f");
266 
267 		fastDecrement = createButton("Fast decrement","ArrowLeftLeft16.gif",0,15);
268 		fastDecrement.addMouseListener(new HoldMouseListener(Button.FAST_DECREMENT));
269 		fastDecrement.addActionListener(new ActionListener(){
270 			public void actionPerformed(ActionEvent e) {
271 				slider.decrementLarge();
272 			}
273 		});
274 
275 		slowDecrement = createButton("Slow decrement","ArrowLeft16.gif",1,12);
276 		slowDecrement.addMouseListener(new HoldMouseListener(Button.SLOW_DECREMENT));
277 		slowDecrement.addActionListener(new ActionListener(){
278 			public void actionPerformed(ActionEvent e) {
279 				slider.decrementSmall();				
280 			}
281 		});
282 
283 		bitDecrement = createButton("Bit decrement","Minus16.gif",2,12);
284 		bitDecrement.addMouseListener(new HoldMouseListener(Button.BIT_DECREMENT));
285 		bitDecrement.addActionListener(new ActionListener(){
286 			public void actionPerformed(ActionEvent e) {
287 				slider.decrementBit();
288 			}
289 		});
290 
291 		bitIncrement = createButton("Bit increment","Plus16.gif",5,12);
292 		bitIncrement.addMouseListener(new HoldMouseListener(Button.BIT_INCREMENT));
293 		bitIncrement.addActionListener(new ActionListener(){
294 			public void actionPerformed(ActionEvent e) {
295 				slider.incrementBit();
296 			}
297 		});
298 
299 		slowIncrement = createButton("Slow increment","ArrowRight16.gif",6,12);
300 		slowIncrement.addMouseListener(new HoldMouseListener(Button.SLOW_INCREMENT));
301 		slowIncrement.addActionListener(new ActionListener(){
302 			public void actionPerformed(ActionEvent e) {
303 				slider.incrementSmall();
304 			}
305 		});
306 
307 		fastIncrement = createButton("Fast increment","ArrowRightRight16.gif",7,15);
308 		fastIncrement.addMouseListener(new HoldMouseListener(Button.FAST_INCREMENT));
309 		fastIncrement.addActionListener(new ActionListener(){
310 			public void actionPerformed(ActionEvent e) {
311 				slider.incrementLarge();
312 			}
313 		});
314 
315 		valueEditor = new NumberField();
316 		valueEditor.setNumberType(Double.class);
317 		valueEditor.setFormat("%1.4f");
318 		valueEditor.setResizable(false);
319 		valueEditor.setHorizontalAlignment(JTextField.CENTER);
320 
321 		valueEditor.setPreferredSize(new java.awt.Dimension(50,20));
322 		
323 		add(valueEditor,new GridBagConstraints(3,0,1,1,8,0,GridBagConstraints.CENTER,GridBagConstraints.HORIZONTAL,new Insets(0,3,0,0),0,0));
324 		
325 		unitsLabel = new ResizableTextLabel();
326 		unitsLabel.setOpaque(false);
327 		unitsLabel.setResizable(false);
328 
329 		if (Beans.isDesignTime()) {
330 			unitsLabel.setText("<u>");
331 		}
332 
333 		add(unitsLabel,new GridBagConstraints(4,0,1,1,0,0,GridBagConstraints.CENTER,GridBagConstraints.HORIZONTAL,new Insets(0,1,0,3),0,0));
334 	}
335 
336 	/**
337 	 * Automatically updates the value on auto update timer tick. This method is called
338 	 * when one of the auto update buttons is pressed and held down.
339 	 */
340 	private void autoUpdate() {
341 		if (slider.isContinuousModeEnabled()) {
342 			synchronized (this) {
343 				// Value updated only after button released
344 				// supressPropChEvent = supressSetEvent = true;
345 				if (getAutoUpdateButton() == Button.BIT_DECREMENT) {
346 					slider.decrementBit();
347 				} else if (getAutoUpdateButton() == Button.BIT_INCREMENT) {
348 					slider.incrementBit();
349 				} else if (getAutoUpdateButton() == Button.SLOW_DECREMENT) {
350 					slider.decrementSmall();
351 				} else if (getAutoUpdateButton() == Button.SLOW_INCREMENT) {
352 					slider.incrementSmall();
353 				} else if (getAutoUpdateButton() == Button.FAST_DECREMENT) {
354 					slider.decrementLarge();
355 				} else if (getAutoUpdateButton() == Button.FAST_INCREMENT) {
356 					slider.incrementLarge();
357 				}
358 			}
359 		} else {
360 			if (this.getAutoUpdateTimer().isRunning()) this.getAutoUpdateTimer().stop();
361 		}
362 	}
363 
364 	private Timer getAutoUpdateTimer() {
365 		if (autoUpdateTimer == null) {
366 			autoUpdateTimer = new Timer(continuousModeRepeatTime,new ActionListener() {
367 				public void actionPerformed(ActionEvent e) {
368 					if (slider.isContinuousModeEnabled()) autoUpdate();
369 				}
370 			});
371 			autoUpdateTimer.setInitialDelay(continuousModeDelayTime);
372 		}
373 		return autoUpdateTimer;
374 	}
375 
376 	/**
377 	 * Convinience format with Slider format
378 	 * 
379 	 * @param value the value
380 	 * @return formated value as String
381 	 */
382 	public String format(double value) {
383 		return formater.sprintf(value);
384 	}
385 
386 	/**
387 	 * Sets the minimum value.
388 	 * 
389 	 * @param min minimum
390 	 */
391 	public void setMinimum(double min) {
392 		valueEditor.setMinimum(new Double(min));
393 	}
394 
395 	/**
396 	 * Sets the maximum value.
397 	 * 
398 	 * @param min maximum
399 	 */
400 	public void setMaximum(double min) {
401 		valueEditor.setMaximum(new Double(min));
402 	}
403 
404 	/**
405 	 * Sets the units visibility.
406 	 * 
407 	 * @param b true if units are visible
408 	 */
409 	public void setUnitsVisible(boolean b) {
410 		this.unitsVisible = b;
411 		unitsLabel.setText((unitsVisible ? units : ""));
412 	}
413 
414 	/**
415 	 * Returns true if units are visible.
416 	 * 
417 	 * @return true if visible
418 	 */
419 	public boolean isUnitsVisible() {
420 		return unitsVisible;
421 	}
422 
423 	/*
424 	 * (non-Javadoc)
425 	 * @see javax.swing.JComponent#setBackground(java.awt.Color)
426 	 */
427 	@Override
428 	public void setBackground(Color bg) {
429 		super.setBackground(bg);
430 		Component[] comp = getComponents();
431 		for (Component c : comp) {
432 			if (c instanceof JButton) {
433 				c.setBackground(bg);
434 			}
435 		}
436 	}
437 
438 	/*
439 	 * (non-Javadoc)
440 	 * @see javax.swing.JComponent#setForeground(java.awt.Color)
441 	 */
442 	@Override
443 	public void setForeground(Color fg) {
444 		super.setForeground(fg);
445 		Component[] comp = getComponents();
446 		for (Component c : comp) {
447 			c.setForeground(fg);
448 		}
449 	}
450 
451 	/**
452 	 * Adds a set listener which receives notification when a new value is set in the
453 	 * field.
454 	 * 
455 	 * @param sl
456 	 */
457 	public void addSetListener(SetListener sl) {
458 		valueEditor.addSetListener(sl);
459 	}
460 
461 	/**
462 	 * Removes a set listener.
463 	 * 
464 	 * @param sl
465 	 */
466 	public void removeSetListener(SetListener sl) {
467 		valueEditor.removeSetListener(sl);
468 	}
469 
470 	/**
471 	 * Sets text for buttons.
472 	 * 
473 	 * @see
474 	 * @param button
475 	 * @param text
476 	 */
477 	public void setButtonText(Button button, String text) {
478 		if (button.equals(Button.FAST_INCREMENT)) {
479 			fastIncrement.setText(text);
480 		} else if (button.equals(Button.FAST_DECREMENT)) {
481 			fastDecrement.setText(text);
482 		} else if (button.equals(Button.SLOW_INCREMENT)) {
483 			slowIncrement.setText(text);
484 		} else if (button.equals(Button.SLOW_DECREMENT)) {
485 			slowDecrement.setText(text);
486 		} else if (button.equals(Button.BIT_INCREMENT)) {
487 			bitIncrement.setText(text);
488 		} else if (button.equals(Button.BIT_DECREMENT)) {
489 			bitDecrement.setText(text);
490 		}
491 	}
492 
493 	/**
494 	 * Returns the button text.
495 	 * 
496 	 * @param button
497 	 * @return
498 	 */
499 	public String getButtonText(Button button) {
500 		if (button.equals(Button.FAST_INCREMENT)) {
501 			return fastIncrement.getText();
502 		} else if (button.equals(Button.FAST_DECREMENT)) {
503 			return fastDecrement.getText();
504 		} else if (button.equals(Button.SLOW_INCREMENT)) {
505 			return slowIncrement.getText();
506 		} else if (button.equals(Button.SLOW_DECREMENT)) {
507 			return slowDecrement.getText();
508 		} else if (button.equals(Button.BIT_INCREMENT)) {
509 			return bitIncrement.getText();
510 		} else if (button.equals(Button.BIT_DECREMENT)) {
511 			return bitDecrement.getText();
512 		}
513 		return null;
514 	}
515 
516 	/**
517 	 * Sets tooltips for buttons.
518 	 * 
519 	 * @see
520 	 * @param button
521 	 * @param tooltip
522 	 */
523 	public void setButtonTooltip(Button button, String tooltip) {
524 		if (button.equals(Button.FAST_INCREMENT)) {
525 			fastIncrement.setToolTipText(tooltip);
526 		} else if (button.equals(Button.FAST_DECREMENT)) {
527 			fastDecrement.setToolTipText(tooltip);
528 		} else if (button.equals(Button.SLOW_INCREMENT)) {
529 			slowIncrement.setToolTipText(tooltip);
530 		} else if (button.equals(Button.SLOW_DECREMENT)) {
531 			slowDecrement.setToolTipText(tooltip);
532 		} else if (button.equals(Button.BIT_INCREMENT)) {
533 			bitIncrement.setToolTipText(tooltip);
534 		} else if (button.equals(Button.BIT_DECREMENT)) {
535 			bitDecrement.setToolTipText(tooltip);
536 		}
537 	}
538 
539 	/**
540 	 * Returns the button tooltip.
541 	 * 
542 	 * @param button
543 	 * @return
544 	 */
545 	public String getButtonTooltip(Button button) {
546 		if (button.equals(Button.FAST_INCREMENT)) {
547 			return fastIncrement.getToolTipText();
548 		} else if (button.equals(Button.FAST_DECREMENT)) {
549 			return fastDecrement.getToolTipText();
550 		} else if (button.equals(Button.SLOW_INCREMENT)) {
551 			return slowIncrement.getToolTipText();
552 		} else if (button.equals(Button.SLOW_DECREMENT)) {
553 			return slowDecrement.getToolTipText();
554 		} else if (button.equals(Button.BIT_INCREMENT)) {
555 			return bitIncrement.getToolTipText();
556 		} else if (button.equals(Button.BIT_DECREMENT)) {
557 			return bitDecrement.getToolTipText();
558 		}
559 		return null;
560 	}
561 
562 	/**
563 	 * Sets the visibility of the button.
564 	 * 
565 	 * @param button the button
566 	 * @param visible true if button should be visible
567 	 */
568 	public void setButtonVisible(Button button, boolean visible) {
569 		if (button.equals(Button.FAST_INCREMENT)) {
570 			fastIncrement.setVisible(visible);
571 		} else if (button.equals(Button.FAST_DECREMENT)) {
572 			fastDecrement.setVisible(visible);
573 		} else if (button.equals(Button.SLOW_INCREMENT)) {
574 			slowIncrement.setVisible(visible);
575 		} else if (button.equals(Button.SLOW_DECREMENT)) {
576 			slowDecrement.setVisible(visible);
577 		} else if (button.equals(Button.BIT_INCREMENT)) {
578 			bitIncrement.setVisible(visible);
579 		} else if (button.equals(Button.BIT_DECREMENT)) {
580 			bitDecrement.setVisible(visible);
581 		}
582 	}
583 
584 	/**
585 	 * Returns the visibility of the button.
586 	 * 
587 	 * @param button
588 	 * @return visibility of the button
589 	 */
590 	public boolean isButtonVisible(Button button) {
591 		if (button.equals(Button.FAST_INCREMENT)) {
592 			return fastIncrement.isVisible();
593 		} else if (button.equals(Button.FAST_DECREMENT)) {
594 			return fastDecrement.isVisible();
595 		} else if (button.equals(Button.SLOW_INCREMENT)) {
596 			return slowIncrement.isVisible();
597 		} else if (button.equals(Button.SLOW_DECREMENT)) {
598 			return slowDecrement.isVisible();
599 		} else if (button.equals(Button.BIT_INCREMENT)) {
600 			return bitIncrement.isVisible();
601 		} else if (button.equals(Button.BIT_DECREMENT)) {
602 			return bitDecrement.isVisible();
603 		}
604 		return false;
605 	}
606 
607 	/**
608 	 * Sets the font used to display units.
609 	 * 
610 	 * @param font new font
611 	 */
612 	public void setUnitsFont(Font font) {
613 		unitsLabel.setFont(font);
614 	}
615 
616 	/**
617 	 * Returns the font used to display units.
618 	 * 
619 	 * @return
620 	 */
621 	public Font getUnitsFont() {
622 		return unitsLabel.getFont();
623 	}
624 
625 	public Button getAutoUpdateButton() {
626 		return autoUpdateButton;
627 	}
628 
629 	public void setAutoUpdateButton(Button autoUpdateButton) {
630 		this.autoUpdateButton = autoUpdateButton;
631 	}
632 
633 	public void setButtonIcon(Button button, Icon icon) {
634 		if (button.equals(Button.FAST_INCREMENT)) {
635 			fastIncrement.setIcon(icon);
636 		} else if (button.equals(Button.FAST_DECREMENT)) {
637 			fastDecrement.setIcon(icon);
638 		} else if (button.equals(Button.SLOW_INCREMENT)) {
639 			slowIncrement.setIcon(icon);
640 		} else if (button.equals(Button.SLOW_DECREMENT)) {
641 			slowDecrement.setIcon(icon);
642 		} else if (button.equals(Button.BIT_INCREMENT)) {
643 			bitIncrement.setIcon(icon);
644 		} else if (button.equals(Button.BIT_DECREMENT)) {
645 			bitDecrement.setIcon(icon);
646 		}
647 	}
648 
649 	public Icon getButtonIcon(Button button) {
650 		if (button.equals(Button.FAST_INCREMENT)) {
651 			return fastIncrement.getIcon();
652 		} else if (button.equals(Button.FAST_DECREMENT)) {
653 			return fastDecrement.getIcon();
654 		} else if (button.equals(Button.SLOW_INCREMENT)) {
655 			return slowIncrement.getIcon();
656 		} else if (button.equals(Button.SLOW_DECREMENT)) {
657 			return slowDecrement.getIcon();
658 		} else if (button.equals(Button.BIT_INCREMENT)) {
659 			return bitIncrement.getIcon();
660 		} else if (button.equals(Button.BIT_DECREMENT)) {
661 			return bitDecrement.getIcon();
662 		}
663 		return null;
664 	}
665 }
666 
667 /* __oOo__ */