Merge branch 'subtree'
[nikiroo-utils.git] / src / be / nikiroo / fanfix / reader / ui / GuiReaderBook.java
CommitLineData
16a81ef7 1package be.nikiroo.fanfix.reader.ui;
333f0e7b
NR
2
3import java.awt.BorderLayout;
484a31aa 4import java.awt.Component;
d3c84ac3 5import java.awt.Graphics;
333f0e7b
NR
6import java.awt.event.MouseEvent;
7import java.awt.event.MouseListener;
333f0e7b
NR
8import java.util.ArrayList;
9import java.util.Date;
10import java.util.EventListener;
11import java.util.List;
12
333f0e7b
NR
13import javax.swing.JLabel;
14import javax.swing.JPanel;
15
4d205683 16import be.nikiroo.fanfix.data.Story;
16a81ef7 17import be.nikiroo.fanfix.reader.Reader;
333f0e7b
NR
18
19/**
5dd985cf 20 * A book item presented in a {@link GuiReaderFrame}.
ca7d4e2f
NR
21 * <p>
22 * Can be a story, or a comic or... a group.
333f0e7b
NR
23 *
24 * @author niki
25 */
5dd985cf 26class GuiReaderBook extends JPanel {
333f0e7b
NR
27 /**
28 * Action on a book item.
29 *
30 * @author niki
31 */
92fb0719 32 interface BookActionListener extends EventListener {
333f0e7b
NR
33 /**
34 * The book was selected (single click).
35 *
36 * @param book
5dd985cf 37 * the {@link GuiReaderBook} itself
333f0e7b 38 */
5dd985cf 39 public void select(GuiReaderBook book);
333f0e7b
NR
40
41 /**
42 * The book was double-clicked.
43 *
44 * @param book
5dd985cf 45 * the {@link GuiReaderBook} itself
333f0e7b 46 */
5dd985cf 47 public void action(GuiReaderBook book);
9843a5e5
NR
48
49 /**
5dd985cf 50 * A popup menu was requested for this {@link GuiReaderBook}.
9843a5e5
NR
51 *
52 * @param book
5dd985cf 53 * the {@link GuiReaderBook} itself
484a31aa
NR
54 * @param target
55 * the target component for the popup
56 * @param x
57 * the X position of the click/request (in case of popup
58 * request from the keyboard, the center of the target is
59 * selected as point of reference)
60 * @param y
61 * the Y position of the click/request (in case of popup
62 * request from the keyboard, the center of the target is
63 * selected as point of reference)
9843a5e5 64 */
484a31aa
NR
65 public void popupRequested(GuiReaderBook book, Component target, int x,
66 int y);
333f0e7b
NR
67 }
68
4d205683
NR
69 private static final long serialVersionUID = 1L;
70
b4dc6ab5 71 private static final String AUTHOR_COLOR = "#888888";
4d205683 72 private static final long doubleClickDelay = 200; // in ms
3b2b638f 73
333f0e7b 74 private JLabel icon;
3b2b638f 75 private JLabel title;
333f0e7b
NR
76 private boolean selected;
77 private boolean hovered;
78 private Date lastClick;
4d205683 79
92fb0719 80 private List<BookActionListener> listeners;
79a99506 81 private GuiReaderBookInfo info;
10d558d2 82 private boolean cached;
c349fd48 83 private boolean seeWordCount;
10d558d2 84
4d205683 85 /**
5dd985cf 86 * Create a new {@link GuiReaderBook} item for the given {@link Story}.
4d205683 87 *
e42573a0
NR
88 * @param reader
89 * the associated reader
79a99506
NR
90 * @param info
91 * the information about the story to represent
4d205683
NR
92 * @param cached
93 * TRUE if it is locally cached
5dd985cf 94 * @param seeWordCount
793f1071 95 * TRUE to see word counts, FALSE to see authors
4d205683 96 */
79a99506 97 public GuiReaderBook(Reader reader, GuiReaderBookInfo info, boolean cached,
e42573a0 98 boolean seeWordCount) {
79a99506 99 this.info = info;
c349fd48
NR
100 this.cached = cached;
101 this.seeWordCount = seeWordCount;
4d205683 102
df6e2d88 103 icon = new JLabel(GuiReaderCoverImager.generateCoverIcon(
79a99506 104 reader.getLibrary(), info));
c349fd48
NR
105
106 title = new JLabel();
107 updateTitle();
b4dc6ab5 108
edd46289
NR
109 setLayout(new BorderLayout(10, 10));
110 add(icon, BorderLayout.CENTER);
111 add(title, BorderLayout.SOUTH);
333f0e7b
NR
112
113 setupListeners();
333f0e7b
NR
114 }
115
116 /**
117 * The book current selection state.
118 *
4d205683 119 * @return the selection state
333f0e7b
NR
120 */
121 public boolean isSelected() {
122 return selected;
123 }
124
125 /**
126 * The book current selection state.
17fafa56
NR
127 * <p>
128 * Setting this value to true can cause a "select" action to occur if the
129 * previous state was "unselected".
333f0e7b
NR
130 *
131 * @param selected
4d205683 132 * TRUE if it is selected
333f0e7b
NR
133 */
134 public void setSelected(boolean selected) {
edd46289
NR
135 if (this.selected != selected) {
136 this.selected = selected;
137 repaint();
17fafa56
NR
138
139 if (selected) {
140 select();
141 }
edd46289 142 }
333f0e7b
NR
143 }
144
df6e2d88
NR
145 /**
146 * The item mouse-hover state.
147 *
148 * @return TRUE if it is mouse-hovered
149 */
484a31aa 150 public boolean isHovered() {
df6e2d88
NR
151 return this.hovered;
152 }
153
4d205683
NR
154 /**
155 * The item mouse-hover state.
156 *
157 * @param hovered
158 * TRUE if it is mouse-hovered
159 */
484a31aa 160 public void setHovered(boolean hovered) {
edd46289
NR
161 if (this.hovered != hovered) {
162 this.hovered = hovered;
163 repaint();
164 }
333f0e7b
NR
165 }
166
4d205683
NR
167 /**
168 * Setup the mouse listener that will activate {@link BookActionListener}
169 * events.
170 */
333f0e7b 171 private void setupListeners() {
5dd985cf 172 listeners = new ArrayList<GuiReaderBook.BookActionListener>();
333f0e7b 173 addMouseListener(new MouseListener() {
211f7ddb 174 @Override
333f0e7b 175 public void mouseReleased(MouseEvent e) {
e20686b4 176 if (isEnabled() && e.isPopupTrigger()) {
9843a5e5
NR
177 popup(e);
178 }
333f0e7b
NR
179 }
180
211f7ddb 181 @Override
333f0e7b 182 public void mousePressed(MouseEvent e) {
e20686b4 183 if (isEnabled() && e.isPopupTrigger()) {
9843a5e5
NR
184 popup(e);
185 }
333f0e7b
NR
186 }
187
211f7ddb 188 @Override
333f0e7b
NR
189 public void mouseExited(MouseEvent e) {
190 setHovered(false);
191 }
192
211f7ddb 193 @Override
333f0e7b
NR
194 public void mouseEntered(MouseEvent e) {
195 setHovered(true);
196 }
197
211f7ddb 198 @Override
333f0e7b 199 public void mouseClicked(MouseEvent e) {
3b2b638f
NR
200 if (isEnabled()) {
201 Date now = new Date();
202 if (lastClick != null
203 && now.getTime() - lastClick.getTime() < doubleClickDelay) {
204 click(true);
205 } else {
206 click(false);
207 }
9843a5e5 208
3b2b638f 209 lastClick = now;
e20686b4 210 e.consume();
333f0e7b 211 }
333f0e7b 212 }
333f0e7b 213
9843a5e5 214 private void click(boolean doubleClick) {
17fafa56
NR
215 if (doubleClick) {
216 action();
217 } else {
218 select();
9843a5e5 219 }
333f0e7b 220 }
9843a5e5
NR
221
222 private void popup(MouseEvent e) {
484a31aa
NR
223 GuiReaderBook.this
224 .popup(GuiReaderBook.this, e.getX(), e.getY());
e20686b4 225 e.consume();
9843a5e5
NR
226 }
227 });
333f0e7b
NR
228 }
229
4d205683
NR
230 /**
231 * Add a new {@link BookActionListener} on this item.
232 *
233 * @param listener
234 * the listener
235 */
92fb0719 236 public void addActionListener(BookActionListener listener) {
333f0e7b
NR
237 listeners.add(listener);
238 }
d3c84ac3 239
17fafa56
NR
240 /**
241 * Cause an action to occur on this {@link GuiReaderBook}.
242 */
243 public void action() {
244 for (BookActionListener listener : listeners) {
245 listener.action(GuiReaderBook.this);
246 }
247 }
248
249 /**
250 * Cause a select event on this {@link GuiReaderBook}.
484a31aa
NR
251 * <p>
252 * Have a look at {@link GuiReaderBook#setSelected(boolean)}.
17fafa56
NR
253 */
254 private void select() {
255 for (BookActionListener listener : listeners) {
256 listener.select(GuiReaderBook.this);
257 }
258 }
259
484a31aa
NR
260 /**
261 * Request a popup.
262 *
263 * @param target
264 * the target component for the popup
265 * @param x
266 * the X position of the click/request (in case of popup request
267 * from the keyboard, the center of the target should be selected
268 * as point of reference)
269 * @param y
270 * the Y position of the click/request (in case of popup request
271 * from the keyboard, the center of the target should be selected
272 * as point of reference)
273 */
274 public void popup(Component target, int x, int y) {
275 for (BookActionListener listener : listeners) {
276 listener.select((GuiReaderBook.this));
277 listener.popupRequested(GuiReaderBook.this, target, x, y);
278 }
279 }
280
4d205683 281 /**
79a99506 282 * The information about the book represented by this item.
4d205683 283 *
22848428 284 * @return the meta
4d205683 285 */
79a99506
NR
286 public GuiReaderBookInfo getInfo() {
287 return info;
10d558d2
NR
288 }
289
290 /**
5dd985cf 291 * This item {@link GuiReader} library cache state.
10d558d2 292 *
5dd985cf 293 * @return TRUE if it is present in the {@link GuiReader} cache
10d558d2
NR
294 */
295 public boolean isCached() {
296 return cached;
297 }
298
299 /**
5dd985cf 300 * This item {@link GuiReader} library cache state.
10d558d2
NR
301 *
302 * @param cached
5dd985cf 303 * TRUE if it is present in the {@link GuiReader} cache
10d558d2
NR
304 */
305 public void setCached(boolean cached) {
f977d05b
NR
306 if (this.cached != cached) {
307 this.cached = cached;
edd46289 308 repaint();
f977d05b 309 }
10d558d2
NR
310 }
311
4d205683 312 /**
c349fd48
NR
313 * Update the title, paint the item, then call
314 * {@link GuiReaderCoverImager#paintOverlay(Graphics, boolean, boolean, boolean, boolean)}
315 * .
4d205683 316 */
d3c84ac3
NR
317 @Override
318 public void paint(Graphics g) {
c349fd48 319 updateTitle();
d3c84ac3 320 super.paint(g);
df6e2d88
NR
321 GuiReaderCoverImager.paintOverlay(g, isEnabled(), isSelected(),
322 isHovered(), isCached());
085a2f9a 323 }
c349fd48
NR
324
325 /**
326 * Update the title with the currently registered information.
327 */
328 private void updateTitle() {
329 String optSecondary = info.getSecondaryInfo(seeWordCount);
330 title.setText(String
331 .format("<html>"
332 + "<body style='width: %d px; height: %d px; text-align: center'>"
333 + "%s" + "<br>" + "<span style='color: %s;'>" + "%s"
334 + "</span>" + "</body>" + "</html>",
335 GuiReaderCoverImager.TEXT_WIDTH,
336 GuiReaderCoverImager.TEXT_HEIGHT, info.getMainInfo(),
337 AUTHOR_COLOR, optSecondary));
338 }
333f0e7b 339}