099b3c87a0e66eb7d7074e3d58f350889c685f9d
[fanfix.git] / src / be / nikiroo / fanfix / reader / ui / GuiReaderNavBar.java
1 package be.nikiroo.fanfix.reader.ui;
2
3 import java.awt.Color;
4 import java.awt.LayoutManager;
5 import java.awt.event.ActionEvent;
6 import java.awt.event.ActionListener;
7 import java.util.ArrayList;
8 import java.util.List;
9
10 import javax.swing.BoxLayout;
11 import javax.swing.JButton;
12 import javax.swing.JLabel;
13 import javax.swing.JPanel;
14
15 import be.nikiroo.fanfix.Instance;
16
17 /**
18 * A Swing-based navigation bar, that displays first/previous/next/last page
19 * buttons.
20 *
21 * @author niki
22 */
23 public class GuiReaderNavBar extends JPanel {
24 private static final long serialVersionUID = 1L;
25
26 private JLabel label;
27 private int index = 0;
28 private int min = 0;
29 private int max = 0;
30 private JButton[] navButtons;
31 String extraLabel = null;
32
33 private List<ActionListener> listeners = new ArrayList<ActionListener>();
34
35 /**
36 * Create a new navigation bar.
37 * <p>
38 * The minimum must be lower or equal to the maximum.
39 * <p>
40 * Note than a max of "-1" means "infinite".
41 *
42 * @param min
43 * the minimum page number (cannot be negative)
44 * @param max
45 * the maximum page number (cannot be lower than min, except if
46 * -1 (infinite))
47 *
48 * @throws IndexOutOfBoundsException
49 * if min &gt; max and max is not "-1"
50 */
51 public GuiReaderNavBar(int min, int max) {
52 if (min > max && max != -1) {
53 throw new IndexOutOfBoundsException(String.format(
54 "min (%d) > max (%d)", min, max));
55 }
56
57 LayoutManager layout = new BoxLayout(this, BoxLayout.X_AXIS);
58 setLayout(layout);
59
60 navButtons = new JButton[4];
61
62 navButtons[0] = createNavButton("<<", new ActionListener() {
63 @Override
64 public void actionPerformed(ActionEvent e) {
65 setIndex(GuiReaderNavBar.this.min);
66 fireEvent();
67 }
68 });
69 navButtons[1] = createNavButton(" < ", new ActionListener() {
70 @Override
71 public void actionPerformed(ActionEvent e) {
72 setIndex(index - 1);
73 fireEvent();
74 }
75 });
76 navButtons[2] = createNavButton(" > ", new ActionListener() {
77 @Override
78 public void actionPerformed(ActionEvent e) {
79 setIndex(index + 1);
80 fireEvent();
81 }
82 });
83 navButtons[3] = createNavButton(">>", new ActionListener() {
84 @Override
85 public void actionPerformed(ActionEvent e) {
86 setIndex(GuiReaderNavBar.this.max);
87 fireEvent();
88 }
89 });
90
91 for (JButton navButton : navButtons) {
92 add(navButton);
93 }
94
95 label = new JLabel("");
96 add(label);
97
98 this.min = min;
99 this.max = max;
100 this.index = min;
101
102 updateEnabled();
103 updateLabel();
104 fireEvent();
105 }
106
107 /**
108 * The current index, must be between {@link GuiReaderNavBar#min} and
109 * {@link GuiReaderNavBar#max}, both inclusive.
110 *
111 * @return the index
112 */
113 public int getIndex() {
114 return index;
115 }
116
117 /**
118 * The current index, must be between {@link GuiReaderNavBar#min} and
119 * {@link GuiReaderNavBar#max}, both inclusive.
120 *
121 * @param index
122 * the new index
123 */
124 public void setIndex(int index) {
125 if (index != this.index) {
126 if (index < min || (index > max && max != -1)) {
127 throw new IndexOutOfBoundsException(String.format(
128 "Index %d but min/max is [%d/%d]", index, min, max));
129 }
130
131 this.index = index;
132 updateLabel();
133 }
134
135 updateEnabled();
136 }
137
138 /**
139 * The minimun page number. Cannot be negative.
140 *
141 * @return the min
142 */
143 public int getMin() {
144 return min;
145 }
146
147 /**
148 * The minimum page number. Cannot be negative.
149 * <p>
150 * May update the index if needed (if the index is &lt; the new min).
151 * <p>
152 * Will also (always) update the label and enable/disable the required
153 * buttons.
154 *
155 * @param min
156 * the new min
157 */
158 public void setMin(int min) {
159 this.min = min;
160 if (index < min) {
161 index = min;
162 }
163 updateEnabled();
164 updateLabel();
165
166 }
167
168 /**
169 * The maximum page number. Cannot be lower than min, except if -1
170 * (infinite).
171 *
172 * @return the max
173 */
174 public int getMax() {
175 return max;
176 }
177
178 /**
179 * The maximum page number. Cannot be lower than min, except if -1
180 * (infinite).
181 * <p>
182 * May update the index if needed (if the index is &gt; the new max).
183 * <p>
184 * Will also (always) update the label and enable/disable the required
185 * buttons.
186 *
187 * @param max
188 * the new max
189 */
190 public void setMax(int max) {
191 this.max = max;
192 if (index > max && max != -1) {
193 index = max;
194 }
195 updateEnabled();
196 updateLabel();
197 }
198
199 /**
200 * The current extra label to display with the default
201 * {@link GuiReaderNavBar#computeLabel(int, int, int)} implementation.
202 *
203 * @return the current label
204 */
205 public String getExtraLabel() {
206 return extraLabel;
207 }
208
209 /**
210 * The current extra label to display with the default
211 * {@link GuiReaderNavBar#computeLabel(int, int, int)} implementation.
212 *
213 * @param currentLabel
214 * the new current label
215 */
216 public void setExtraLabel(String currentLabel) {
217 this.extraLabel = currentLabel;
218 updateLabel();
219 }
220
221 /**
222 * Add a listener that will be called on each page change.
223 *
224 * @param listener
225 * the new listener
226 */
227 public void addActionListener(ActionListener listener) {
228 listeners.add(listener);
229 }
230
231 /**
232 * Remove the given listener if possible.
233 *
234 * @param listener
235 * the listener to remove
236 * @return TRUE if it was removed, FALSE if it was not found
237 */
238 public boolean removeActionListener(ActionListener listener) {
239 return listeners.remove(listener);
240 }
241
242 /**
243 * Remove all the listeners.
244 */
245 public void clearActionsListeners() {
246 listeners.clear();
247 }
248
249 /**
250 * Notify a change of page.
251 */
252 public void fireEvent() {
253 for (ActionListener listener : listeners) {
254 try {
255 listener.actionPerformed(new ActionEvent(this,
256 ActionEvent.ACTION_FIRST, "page changed"));
257 } catch (Exception e) {
258 Instance.getTraceHandler().error(e);
259 }
260 }
261 }
262
263 /**
264 * Create a single navigation button.
265 *
266 * @param text
267 * the text to display
268 * @param action
269 * the action to take on click
270 * @return the button
271 */
272 private JButton createNavButton(String text, ActionListener action) {
273 JButton navButton = new JButton(text);
274 navButton.addActionListener(action);
275 navButton.setForeground(Color.BLUE);
276 return navButton;
277 }
278
279 /**
280 * Update the label displayed in the UI.
281 */
282 private void updateLabel() {
283 label.setText(computeLabel(index, min, max));
284 }
285
286 /**
287 * Update the navigation buttons "enabled" state according to the current
288 * index value.
289 */
290 private void updateEnabled() {
291 navButtons[0].setEnabled(index > min);
292 navButtons[1].setEnabled(index > min);
293 navButtons[2].setEnabled(index < max || max == -1);
294 navButtons[3].setEnabled(index < max || max == -1);
295 }
296
297 /**
298 * Return the label to display for the given index.
299 * <p>
300 * Swing HTML (HTML3) is supported if surrounded by &lt;HTML&gt; and
301 * &lt;/HTML&gt;.
302 * <p>
303 * By default, return "Page 1/5: current_label" (with the current index and
304 * {@link GuiReaderNavBar#getCurrentLabel()}).
305 *
306 * @param index
307 * the new index number
308 * @param mix
309 * the minimum index (inclusive)
310 * @param max
311 * the maximum index (inclusive)
312 * @return the label
313 */
314 protected String computeLabel(int index,
315 @SuppressWarnings("unused") int min, int max) {
316
317 String base = "&nbsp;&nbsp;<B>Page <SPAN COLOR='#444466'>%d</SPAN>&nbsp;";
318 if (max >= 0) {
319 base += "/&nbsp;%d";
320 }
321 base += "</B>";
322
323 String ifLabel = ": %s";
324
325 String display = base;
326 String label = getExtraLabel();
327 if (label != null && !label.trim().isEmpty()) {
328 display += ifLabel;
329 }
330
331 display = "<HTML>" + display + "</HTML>";
332
333 if (max >= 0) {
334 return String.format(display, index, max, label);
335 }
336
337 return String.format(display, index, label);
338 }
339 }