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