4683f71507ccaf20b6cbfd76c454640e38368a8e
[fanfix.git] / src / be / nikiroo / fanfix / reader / ui / GuiReaderBook.java
1 package be.nikiroo.fanfix.reader.ui;
2
3 import java.awt.BorderLayout;
4 import java.awt.Graphics;
5 import java.awt.event.MouseEvent;
6 import java.awt.event.MouseListener;
7 import java.util.ArrayList;
8 import java.util.Date;
9 import java.util.EventListener;
10 import java.util.List;
11
12 import javax.swing.JLabel;
13 import javax.swing.JPanel;
14
15 import be.nikiroo.fanfix.data.MetaData;
16 import be.nikiroo.fanfix.data.Story;
17 import be.nikiroo.fanfix.reader.Reader;
18
19 /**
20 * A book item presented in a {@link GuiReaderFrame}.
21 *
22 * @author niki
23 */
24 class GuiReaderBook extends JPanel {
25 /**
26 * Action on a book item.
27 *
28 * @author niki
29 */
30 interface BookActionListener extends EventListener {
31 /**
32 * The book was selected (single click).
33 *
34 * @param book
35 * the {@link GuiReaderBook} itself
36 */
37 public void select(GuiReaderBook book);
38
39 /**
40 * The book was double-clicked.
41 *
42 * @param book
43 * the {@link GuiReaderBook} itself
44 */
45 public void action(GuiReaderBook book);
46
47 /**
48 * A popup menu was requested for this {@link GuiReaderBook}.
49 *
50 * @param book
51 * the {@link GuiReaderBook} itself
52 * @param e
53 * the {@link MouseEvent} that generated this call
54 */
55 public void popupRequested(GuiReaderBook book, MouseEvent e);
56 }
57
58 private static final long serialVersionUID = 1L;
59
60 private static final String AUTHOR_COLOR = "#888888";
61 private static final long doubleClickDelay = 200; // in ms
62
63 private JLabel icon;
64 private JLabel title;
65 private boolean selected;
66 private boolean hovered;
67 private Date lastClick;
68
69 private List<BookActionListener> listeners;
70 private MetaData meta;
71 private boolean cached;
72
73 /**
74 * Create a new {@link GuiReaderBook} item for the given {@link Story}.
75 *
76 * @param reader
77 * the associated reader
78 * @param meta
79 * the story {@link MetaData} or source (if no LUID)
80 * @param cached
81 * TRUE if it is locally cached
82 * @param seeWordCount
83 * TRUE to see word counts, FALSE to see authors
84 */
85 public GuiReaderBook(Reader reader, MetaData meta, boolean cached,
86 boolean seeWordCount) {
87 this.cached = cached;
88 this.meta = meta;
89
90 String optSecondary = meta.getAuthor();
91 if (seeWordCount) {
92 if (meta.getWords() >= 4000) {
93 optSecondary = "" + (meta.getWords() / 1000) + "k";
94 } else if (meta.getWords() > 0) {
95 optSecondary = "" + meta.getWords();
96 } else {
97 optSecondary = "";
98 }
99
100 if (!optSecondary.isEmpty()) {
101 if (meta.isImageDocument()) {
102 optSecondary += " images";
103 } else {
104 optSecondary += " words";
105 }
106 }
107 }
108
109 if (optSecondary != null && !optSecondary.isEmpty()) {
110 optSecondary = "(" + optSecondary + ")";
111 } else {
112 optSecondary = "";
113 }
114
115 icon = new JLabel(GuiReaderCoverImager.generateCoverIcon(
116 reader.getLibrary(), getMeta()));
117 title = new JLabel(
118 String.format(
119 "<html>"
120 + "<body style='width: %d px; height: %d px; text-align: center'>"
121 + "%s" + "<br>" + "<span style='color: %s;'>"
122 + "%s" + "</span>" + "</body>" + "</html>",
123 GuiReaderCoverImager.TEXT_WIDTH,
124 GuiReaderCoverImager.TEXT_HEIGHT, meta.getTitle(),
125 AUTHOR_COLOR, optSecondary));
126
127 setLayout(new BorderLayout(10, 10));
128 add(icon, BorderLayout.CENTER);
129 add(title, BorderLayout.SOUTH);
130
131 setupListeners();
132 }
133
134 /**
135 * The book current selection state.
136 *
137 * @return the selection state
138 */
139 public boolean isSelected() {
140 return selected;
141 }
142
143 /**
144 * The book current selection state.
145 *
146 * @param selected
147 * TRUE if it is selected
148 */
149 public void setSelected(boolean selected) {
150 if (this.selected != selected) {
151 this.selected = selected;
152 repaint();
153 }
154 }
155
156 /**
157 * The item mouse-hover state.
158 *
159 * @return TRUE if it is mouse-hovered
160 */
161 private boolean isHovered() {
162 return this.hovered;
163 }
164
165 /**
166 * The item mouse-hover state.
167 *
168 * @param hovered
169 * TRUE if it is mouse-hovered
170 */
171 private void setHovered(boolean hovered) {
172 if (this.hovered != hovered) {
173 this.hovered = hovered;
174 repaint();
175 }
176 }
177
178 /**
179 * Setup the mouse listener that will activate {@link BookActionListener}
180 * events.
181 */
182 private void setupListeners() {
183 listeners = new ArrayList<GuiReaderBook.BookActionListener>();
184 addMouseListener(new MouseListener() {
185 @Override
186 public void mouseReleased(MouseEvent e) {
187 if (e.isPopupTrigger()) {
188 popup(e);
189 }
190 }
191
192 @Override
193 public void mousePressed(MouseEvent e) {
194 if (e.isPopupTrigger()) {
195 popup(e);
196 }
197 }
198
199 @Override
200 public void mouseExited(MouseEvent e) {
201 setHovered(false);
202 }
203
204 @Override
205 public void mouseEntered(MouseEvent e) {
206 setHovered(true);
207 }
208
209 @Override
210 public void mouseClicked(MouseEvent e) {
211 if (isEnabled()) {
212 Date now = new Date();
213 if (lastClick != null
214 && now.getTime() - lastClick.getTime() < doubleClickDelay) {
215 click(true);
216 } else {
217 click(false);
218 }
219
220 lastClick = now;
221 }
222 }
223
224 private void click(boolean doubleClick) {
225 for (BookActionListener listener : listeners) {
226 if (doubleClick) {
227 listener.action(GuiReaderBook.this);
228 } else {
229 listener.select(GuiReaderBook.this);
230 }
231 }
232 }
233
234 private void popup(MouseEvent e) {
235 for (BookActionListener listener : listeners) {
236 listener.select((GuiReaderBook.this));
237 listener.popupRequested(GuiReaderBook.this, e);
238 }
239 }
240 });
241 }
242
243 /**
244 * Add a new {@link BookActionListener} on this item.
245 *
246 * @param listener
247 * the listener
248 */
249 public void addActionListener(BookActionListener listener) {
250 listeners.add(listener);
251 }
252
253 /**
254 * The Library {@link MetaData} of the book represented by this item.
255 *
256 * @return the meta
257 */
258 public MetaData getMeta() {
259 return meta;
260 }
261
262 /**
263 * This item {@link GuiReader} library cache state.
264 *
265 * @return TRUE if it is present in the {@link GuiReader} cache
266 */
267 public boolean isCached() {
268 return cached;
269 }
270
271 /**
272 * This item {@link GuiReader} library cache state.
273 *
274 * @param cached
275 * TRUE if it is present in the {@link GuiReader} cache
276 */
277 public void setCached(boolean cached) {
278 if (this.cached != cached) {
279 this.cached = cached;
280 repaint();
281 }
282 }
283
284 /**
285 * Paint the item, then call {@link GuiReaderBook#paintOverlay(Graphics)}.
286 */
287 @Override
288 public void paint(Graphics g) {
289 super.paint(g);
290 GuiReaderCoverImager.paintOverlay(g, isEnabled(), isSelected(),
291 isHovered(), isCached());
292 }
293 }