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 NR |
70 | private boolean cached; |
71 | ||
4d205683 | 72 | /** |
5dd985cf | 73 | * Create a new {@link GuiReaderBook} item for the given {@link Story}. |
4d205683 | 74 | * |
e42573a0 NR |
75 | * @param reader |
76 | * the associated reader | |
79a99506 NR |
77 | * @param info |
78 | * the information about the story to represent | |
4d205683 NR |
79 | * @param cached |
80 | * TRUE if it is locally cached | |
5dd985cf | 81 | * @param seeWordCount |
793f1071 | 82 | * TRUE to see word counts, FALSE to see authors |
4d205683 | 83 | */ |
79a99506 | 84 | public GuiReaderBook(Reader reader, GuiReaderBookInfo info, boolean cached, |
e42573a0 | 85 | boolean seeWordCount) { |
10d558d2 | 86 | this.cached = cached; |
79a99506 | 87 | this.info = info; |
793f1071 | 88 | |
79a99506 | 89 | String optSecondary = info.getSecondaryInfo(seeWordCount); |
4d205683 | 90 | |
df6e2d88 | 91 | icon = new JLabel(GuiReaderCoverImager.generateCoverIcon( |
79a99506 | 92 | reader.getLibrary(), info)); |
3b2b638f | 93 | title = new JLabel( |
b4dc6ab5 NR |
94 | String.format( |
95 | "<html>" | |
96 | + "<body style='width: %d px; height: %d px; text-align: center'>" | |
97 | + "%s" + "<br>" + "<span style='color: %s;'>" | |
98 | + "%s" + "</span>" + "</body>" + "</html>", | |
df6e2d88 | 99 | GuiReaderCoverImager.TEXT_WIDTH, |
79a99506 | 100 | GuiReaderCoverImager.TEXT_HEIGHT, info.getMainInfo(), |
df6e2d88 | 101 | AUTHOR_COLOR, optSecondary)); |
b4dc6ab5 | 102 | |
edd46289 NR |
103 | setLayout(new BorderLayout(10, 10)); |
104 | add(icon, BorderLayout.CENTER); | |
105 | add(title, BorderLayout.SOUTH); | |
333f0e7b NR |
106 | |
107 | setupListeners(); | |
333f0e7b NR |
108 | } |
109 | ||
110 | /** | |
111 | * The book current selection state. | |
112 | * | |
4d205683 | 113 | * @return the selection state |
333f0e7b NR |
114 | */ |
115 | public boolean isSelected() { | |
116 | return selected; | |
117 | } | |
118 | ||
119 | /** | |
120 | * The book current selection state. | |
121 | * | |
122 | * @param selected | |
4d205683 | 123 | * TRUE if it is selected |
333f0e7b NR |
124 | */ |
125 | public void setSelected(boolean selected) { | |
edd46289 NR |
126 | if (this.selected != selected) { |
127 | this.selected = selected; | |
128 | repaint(); | |
129 | } | |
333f0e7b NR |
130 | } |
131 | ||
df6e2d88 NR |
132 | /** |
133 | * The item mouse-hover state. | |
134 | * | |
135 | * @return TRUE if it is mouse-hovered | |
136 | */ | |
137 | private boolean isHovered() { | |
138 | return this.hovered; | |
139 | } | |
140 | ||
4d205683 NR |
141 | /** |
142 | * The item mouse-hover state. | |
143 | * | |
144 | * @param hovered | |
145 | * TRUE if it is mouse-hovered | |
146 | */ | |
333f0e7b | 147 | private void setHovered(boolean hovered) { |
edd46289 NR |
148 | if (this.hovered != hovered) { |
149 | this.hovered = hovered; | |
150 | repaint(); | |
151 | } | |
333f0e7b NR |
152 | } |
153 | ||
4d205683 NR |
154 | /** |
155 | * Setup the mouse listener that will activate {@link BookActionListener} | |
156 | * events. | |
157 | */ | |
333f0e7b | 158 | private void setupListeners() { |
5dd985cf | 159 | listeners = new ArrayList<GuiReaderBook.BookActionListener>(); |
333f0e7b | 160 | addMouseListener(new MouseListener() { |
211f7ddb | 161 | @Override |
333f0e7b | 162 | public void mouseReleased(MouseEvent e) { |
9843a5e5 NR |
163 | if (e.isPopupTrigger()) { |
164 | popup(e); | |
165 | } | |
333f0e7b NR |
166 | } |
167 | ||
211f7ddb | 168 | @Override |
333f0e7b | 169 | public void mousePressed(MouseEvent e) { |
9843a5e5 NR |
170 | if (e.isPopupTrigger()) { |
171 | popup(e); | |
172 | } | |
333f0e7b NR |
173 | } |
174 | ||
211f7ddb | 175 | @Override |
333f0e7b NR |
176 | public void mouseExited(MouseEvent e) { |
177 | setHovered(false); | |
178 | } | |
179 | ||
211f7ddb | 180 | @Override |
333f0e7b NR |
181 | public void mouseEntered(MouseEvent e) { |
182 | setHovered(true); | |
183 | } | |
184 | ||
211f7ddb | 185 | @Override |
333f0e7b | 186 | public void mouseClicked(MouseEvent e) { |
3b2b638f NR |
187 | if (isEnabled()) { |
188 | Date now = new Date(); | |
189 | if (lastClick != null | |
190 | && now.getTime() - lastClick.getTime() < doubleClickDelay) { | |
191 | click(true); | |
192 | } else { | |
193 | click(false); | |
194 | } | |
9843a5e5 | 195 | |
3b2b638f | 196 | lastClick = now; |
333f0e7b | 197 | } |
333f0e7b | 198 | } |
333f0e7b | 199 | |
9843a5e5 NR |
200 | private void click(boolean doubleClick) { |
201 | for (BookActionListener listener : listeners) { | |
202 | if (doubleClick) { | |
5dd985cf | 203 | listener.action(GuiReaderBook.this); |
9843a5e5 | 204 | } else { |
5dd985cf | 205 | listener.select(GuiReaderBook.this); |
9843a5e5 NR |
206 | } |
207 | } | |
333f0e7b | 208 | } |
9843a5e5 NR |
209 | |
210 | private void popup(MouseEvent e) { | |
211 | for (BookActionListener listener : listeners) { | |
5dd985cf NR |
212 | listener.select((GuiReaderBook.this)); |
213 | listener.popupRequested(GuiReaderBook.this, e); | |
9843a5e5 NR |
214 | } |
215 | } | |
216 | }); | |
333f0e7b NR |
217 | } |
218 | ||
4d205683 NR |
219 | /** |
220 | * Add a new {@link BookActionListener} on this item. | |
221 | * | |
222 | * @param listener | |
223 | * the listener | |
224 | */ | |
92fb0719 | 225 | public void addActionListener(BookActionListener listener) { |
333f0e7b NR |
226 | listeners.add(listener); |
227 | } | |
d3c84ac3 | 228 | |
4d205683 | 229 | /** |
79a99506 | 230 | * The information about the book represented by this item. |
4d205683 | 231 | * |
22848428 | 232 | * @return the meta |
4d205683 | 233 | */ |
79a99506 NR |
234 | public GuiReaderBookInfo getInfo() { |
235 | return info; | |
10d558d2 NR |
236 | } |
237 | ||
238 | /** | |
5dd985cf | 239 | * This item {@link GuiReader} library cache state. |
10d558d2 | 240 | * |
5dd985cf | 241 | * @return TRUE if it is present in the {@link GuiReader} cache |
10d558d2 NR |
242 | */ |
243 | public boolean isCached() { | |
244 | return cached; | |
245 | } | |
246 | ||
247 | /** | |
5dd985cf | 248 | * This item {@link GuiReader} library cache state. |
10d558d2 NR |
249 | * |
250 | * @param cached | |
5dd985cf | 251 | * TRUE if it is present in the {@link GuiReader} cache |
10d558d2 NR |
252 | */ |
253 | public void setCached(boolean cached) { | |
f977d05b NR |
254 | if (this.cached != cached) { |
255 | this.cached = cached; | |
edd46289 | 256 | repaint(); |
f977d05b | 257 | } |
10d558d2 NR |
258 | } |
259 | ||
4d205683 | 260 | /** |
5dd985cf | 261 | * Paint the item, then call {@link GuiReaderBook#paintOverlay(Graphics)}. |
4d205683 | 262 | */ |
d3c84ac3 NR |
263 | @Override |
264 | public void paint(Graphics g) { | |
265 | super.paint(g); | |
df6e2d88 NR |
266 | GuiReaderCoverImager.paintOverlay(g, isEnabled(), isSelected(), |
267 | isHovered(), isCached()); | |
085a2f9a | 268 | } |
333f0e7b | 269 | } |