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