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