move Navbar into nikiroo-utils
[fanfix.git] / ui / NavBar.java
1 package be.nikiroo.utils.ui;
2
3 import java.awt.Dimension;
4 import java.awt.LayoutManager;
5 import java.awt.event.ActionEvent;
6 import java.awt.event.ActionListener;
7
8 import javax.swing.BoxLayout;
9 import javax.swing.Icon;
10 import javax.swing.JButton;
11 import javax.swing.JLabel;
12 import javax.swing.JTextField;
13
14 /**
15 * A Swing-based navigation bar, that displays first/previous/next/last page
16 * buttons.
17 *
18 * @author niki
19 */
20 public class NavBar extends ListenerPanel {
21 private static final long serialVersionUID = 1L;
22
23 /** The event that is fired on page change. */
24 public static final String PAGE_CHANGED = "page changed";
25
26 private JTextField page;
27 private JLabel maxPage;
28 private JLabel label;
29
30 private int index = 0;
31 private int min = 0;
32 private int max = 0;
33 private String extraLabel = null;
34
35 private JButton first;
36 private JButton previous;
37 private JButton next;
38 private JButton last;
39
40 /**
41 * Create a new navigation bar.
42 * <p>
43 * The minimum must be lower or equal to the maximum.
44 * <p>
45 * Note than a max of "-1" means "infinite".
46 *
47 * @param min
48 * the minimum page number (cannot be negative)
49 * @param max
50 * the maximum page number (cannot be lower than min, except if
51 * -1 (infinite))
52 *
53 * @throws IndexOutOfBoundsException
54 * if min &gt; max and max is not "-1"
55 */
56 public NavBar(int min, int max) {
57 if (min > max && max != -1) {
58 throw new IndexOutOfBoundsException(
59 String.format("min (%d) > max (%d)", min, max));
60 }
61
62 LayoutManager layout = new BoxLayout(this, BoxLayout.X_AXIS);
63 setLayout(layout);
64
65 // Page navigation
66 first = new JButton();
67 first.addActionListener(new ActionListener() {
68 @Override
69 public void actionPerformed(ActionEvent e) {
70 first();
71 }
72 });
73
74 previous = new JButton();
75 previous.addActionListener(new ActionListener() {
76 @Override
77 public void actionPerformed(ActionEvent e) {
78 previous();
79 }
80 });
81
82 page = new JTextField(Integer.toString(min));
83 page.setPreferredSize(new Dimension(page.getPreferredSize().width * 2,
84 page.getPreferredSize().height));
85 page.addActionListener(new ActionListener() {
86 @Override
87 public void actionPerformed(ActionEvent e) {
88 try {
89 int pageNb = Integer.parseInt(page.getText());
90 if (pageNb < NavBar.this.min || pageNb > NavBar.this.max) {
91 throw new NumberFormatException("invalid");
92 }
93
94 if (setIndex(pageNb))
95 fireActionPerformed(PAGE_CHANGED);
96 } catch (NumberFormatException nfe) {
97 page.setText(Integer.toString(index + 1));
98 }
99 }
100 });
101
102 maxPage = new JLabel(" of " + max);
103
104 next = new JButton();
105 next.addActionListener(new ActionListener() {
106 @Override
107 public void actionPerformed(ActionEvent e) {
108 next();
109 }
110 });
111
112 last = new JButton();
113 last.addActionListener(new ActionListener() {
114 @Override
115 public void actionPerformed(ActionEvent e) {
116 last();
117 }
118 });
119
120 // Set the << < > >> "icons"
121 setIcons(null, null, null, null);
122
123 this.add(first);
124 this.add(previous);
125 this.add(page);
126 this.add(maxPage);
127 this.add(next);
128 this.add(last);
129
130 label = new JLabel("");
131 this.add(label);
132
133 this.min = min;
134 this.max = max;
135 this.index = min;
136
137 updateEnabled();
138 updateLabel();
139 fireActionPerformed(PAGE_CHANGED);
140 }
141
142 /**
143 * The current index, must be between {@link NavBar#min} and
144 * {@link NavBar#max}, both inclusive.
145 *
146 * @return the index
147 */
148 public int getIndex() {
149 return index;
150 }
151
152 /**
153 * The current index, must be between {@link NavBar#min} and
154 * {@link NavBar#max}, both inclusive.
155 *
156 * @param index
157 * the new index
158 *
159 * @return TRUE if the index changed
160 *
161 * @throws IndexOutOfBoundsException
162 * if the index is out of bounds according to
163 * {@link NavBar#getMin()} and {@link NavBar#getMax()}.
164 */
165 public boolean setIndex(int index) {
166 if (index != this.index) {
167 if (index < min || (index > max && max != -1)) {
168 throw new IndexOutOfBoundsException(String.format(
169 "Index %d but min/max is [%d/%d]", index, min, max));
170 }
171
172 this.index = index;
173 updateLabel();
174 updateEnabled();
175
176 return true;
177 }
178
179 return false;
180 }
181
182 /**
183 * The minimun page number. Cannot be negative.
184 *
185 * @return the min
186 */
187 public int getMin() {
188 return min;
189 }
190
191 /**
192 * The minimum page number. Cannot be negative.
193 * <p>
194 * May update the index if needed (if the index is &lt; the new min).
195 * <p>
196 * Will also (always) update the label and enable/disable the required
197 * buttons.
198 *
199 * @param min
200 * the new min
201 */
202 public void setMin(int min) {
203 this.min = min;
204 if (index < min) {
205 index = min;
206 }
207
208 updateEnabled();
209 updateLabel();
210 }
211
212 /**
213 * The maximum page number. Cannot be lower than min, except if -1
214 * (infinite).
215 *
216 * @return the max
217 */
218 public int getMax() {
219 return max;
220 }
221
222 /**
223 * The maximum page number. Cannot be lower than min, except if -1
224 * (infinite).
225 * <p>
226 * May update the index if needed (if the index is &gt; the new max).
227 * <p>
228 * Will also (always) update the label and enable/disable the required
229 * buttons.
230 *
231 * @param max
232 * the new max
233 */
234 public void setMax(int max) {
235 this.max = max;
236 if (index > max && max != -1) {
237 index = max;
238 }
239
240 maxPage.setText(" of " + max);
241 updateEnabled();
242 updateLabel();
243 }
244
245 /**
246 * The current extra label to display.
247 *
248 * @return the current label
249 */
250 public String getExtraLabel() {
251 return extraLabel;
252 }
253
254 /**
255 * The current extra label to display.
256 *
257 * @param currentLabel
258 * the new current label
259 */
260 public void setExtraLabel(String currentLabel) {
261 this.extraLabel = currentLabel;
262 updateLabel();
263 }
264
265 /**
266 * Change the page to the next one.
267 *
268 * @return TRUE if it changed
269 */
270 public boolean next() {
271 if (setIndex(index + 1)) {
272 fireActionPerformed(PAGE_CHANGED);
273 return true;
274 }
275
276 return false;
277 }
278
279 /**
280 * Change the page to the previous one.
281 *
282 * @return TRUE if it changed
283 */
284 public boolean previous() {
285 if (setIndex(index - 1)) {
286 fireActionPerformed(PAGE_CHANGED);
287 return true;
288 }
289
290 return false;
291 }
292
293 /**
294 * Change the page to the first one.
295 *
296 * @return TRUE if it changed
297 */
298 public boolean first() {
299 if (setIndex(min)) {
300 fireActionPerformed(PAGE_CHANGED);
301 return true;
302 }
303
304 return false;
305 }
306
307 /**
308 * Change the page to the last one.
309 *
310 * @return TRUE if it changed
311 */
312 public boolean last() {
313 if (setIndex(max)) {
314 fireActionPerformed(PAGE_CHANGED);
315 return true;
316 }
317
318 return false;
319 }
320
321 /**
322 * Set icons for the buttons instead of square brackets.
323 * <p>
324 * Any NULL value will make the button use square brackets again.
325 *
326 * @param first
327 * the icon of the button "go to first page"
328 * @param previous
329 * the icon of the button "go to previous page"
330 * @param next
331 * the icon of the button "go to next page"
332 * @param last
333 * the icon of the button "go to last page"
334 */
335 public void setIcons(Icon first, Icon previous, Icon next, Icon last) {
336 this.first.setIcon(first);
337 this.first.setText(first == null ? "<<" : "");
338 this.previous.setIcon(previous);
339 this.previous.setText(previous == null ? "<" : "");
340 this.next.setIcon(next);
341 this.next.setText(next == null ? ">" : "");
342 this.last.setIcon(last);
343 this.last.setText(last == null ? ">>" : "");
344 }
345
346 /**
347 * Update the label displayed in the UI.
348 */
349 private void updateLabel() {
350 label.setText(getExtraLabel());
351 page.setText(Integer.toString(index));
352 }
353
354 /**
355 * Update the navigation buttons "enabled" state according to the current
356 * index value.
357 */
358 private void updateEnabled() {
359 first.setEnabled(index > min);
360 previous.setEnabled(index > min);
361 next.setEnabled(index < max || max == -1);
362 last.setEnabled(index < max || max == -1);
363 }
364 }