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