1 package be
.nikiroo
.fanfix
.reader
.ui
;
3 import java
.awt
.BorderLayout
;
5 import java
.awt
.Component
;
6 import java
.awt
.event
.ActionListener
;
7 import java
.awt
.event
.ComponentAdapter
;
8 import java
.awt
.event
.ComponentEvent
;
9 import java
.awt
.event
.FocusAdapter
;
10 import java
.awt
.event
.FocusEvent
;
11 import java
.awt
.event
.KeyAdapter
;
12 import java
.awt
.event
.KeyEvent
;
13 import java
.util
.ArrayList
;
14 import java
.util
.List
;
16 import javax
.swing
.JLabel
;
17 import javax
.swing
.JPanel
;
19 import be
.nikiroo
.fanfix
.bundles
.StringIdGui
;
20 import be
.nikiroo
.fanfix
.reader
.ui
.GuiReaderBook
.BookActionListener
;
21 import be
.nikiroo
.utils
.ui
.WrapLayout
;
24 * A group of {@link GuiReaderBook}s for display.
28 public class GuiReaderGroup
extends JPanel
{
29 private static final long serialVersionUID
= 1L;
30 private BookActionListener action
;
31 private Color backgroundColor
;
32 private GuiReader reader
;
33 private List
<GuiReaderBookInfo
> infos
;
34 private List
<GuiReaderBook
> books
;
36 private JLabel titleLabel
;
37 private boolean words
; // words or authors (secondary info on books)
38 private int itemsPerLine
;
41 * Create a new {@link GuiReaderGroup}.
44 * the {@link GuiReaderBook} used to probe some information about
47 * the title of this group (can be NULL for "no title", an empty
48 * {@link String} will trigger a default title for empty groups)
49 * @param backgroundColor
50 * the background colour to use (or NULL for default)
52 public GuiReaderGroup(GuiReader reader
, String title
, Color backgroundColor
) {
54 this.backgroundColor
= backgroundColor
;
56 this.pane
= new JPanel();
58 pane
.setLayout(new WrapLayout(WrapLayout
.LEADING
, 5, 5));
59 if (backgroundColor
!= null) {
60 pane
.setBackground(backgroundColor
);
61 setBackground(backgroundColor
);
64 setLayout(new BorderLayout(0, 10));
71 add(pane
, BorderLayout
.CENTER
);
73 titleLabel
= new JLabel();
74 titleLabel
.setHorizontalAlignment(JLabel
.CENTER
);
75 add(titleLabel
, BorderLayout
.NORTH
);
78 // Compute the number of items per line at each resize
79 addComponentListener(new ComponentAdapter() {
81 public void componentResized(ComponentEvent e
) {
82 super.componentResized(e
);
83 computeItemsPerLine();
86 computeItemsPerLine();
88 addKeyListener(new KeyAdapter() {
90 public void keyPressed(KeyEvent e
) {
95 public void keyTyped(KeyEvent e
) {
100 addFocusListener(new FocusAdapter() {
102 public void focusGained(FocusEvent e
) {
103 if (getSelectedBookIndex() < 0) {
104 setSelectedBook(0, true);
109 public void focusLost(FocusEvent e
) {
111 setSelectedBook(-1, false);
117 * The title of this group (can be NULL for "no title", an empty
118 * {@link String} will trigger a default title for empty groups)
123 public void setTitle(String title
) {
125 if (title
.isEmpty()) {
126 title
= GuiReader
.trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
);
129 titleLabel
.setText(String
.format("<html>"
130 + "<body style='text-align: center; color: gray;'><br><b>"
131 + "%s" + "</b></body>" + "</html>", title
));
132 titleLabel
.setVisible(true);
134 titleLabel
.setVisible(false);
139 * Compute how many items can fit in a line so UP and DOWN can be used to go
140 * up/down one line at a time.
142 private void computeItemsPerLine() {
148 * Set the {@link ActionListener} that will be fired on each
149 * {@link GuiReaderBook} action.
154 public void setActionListener(BookActionListener action
) {
155 this.action
= action
;
156 refreshBooks(infos
, words
);
160 * Refresh the list of {@link GuiReaderBook}s displayed in the control.
163 * the new list of infos
164 * @param seeWordcount
165 * TRUE to see word counts, FALSE to see authors
167 public void refreshBooks(List
<GuiReaderBookInfo
> infos
, boolean seeWordcount
) {
169 refreshBooks(seeWordcount
);
173 * Refresh the list of {@link GuiReaderBook}s displayed in the control.
175 * Will not change the current stories.
177 * @param seeWordcount
178 * TRUE to see word counts, FALSE to see authors
180 public void refreshBooks(boolean seeWordcount
) {
181 this.words
= seeWordcount
;
183 books
= new ArrayList
<GuiReaderBook
>();
189 for (GuiReaderBookInfo info
: infos
) {
190 boolean isCached
= false;
191 if (info
.getMeta() != null && info
.getMeta().getLuid() != null) {
192 isCached
= reader
.isCached(info
.getMeta().getLuid());
195 GuiReaderBook book
= new GuiReaderBook(reader
, info
, isCached
,
197 if (backgroundColor
!= null) {
198 book
.setBackground(backgroundColor
);
203 book
.addActionListener(new BookActionListener() {
205 public void select(GuiReaderBook book
) {
206 GuiReaderGroup
.this.requestFocusInWindow();
207 for (GuiReaderBook abook
: books
) {
208 abook
.setSelected(abook
== book
);
213 public void popupRequested(GuiReaderBook book
,
214 Component target
, int x
, int y
) {
218 public void action(GuiReaderBook book
) {
222 if (action
!= null) {
223 book
.addActionListener(action
);
237 * Enables or disables this component, depending on the value of the
238 * parameter <code>b</code>. An enabled component can respond to user input
239 * and generate events. Components are enabled initially by default.
241 * Disabling this component will also affect its children.
244 * If <code>true</code>, this component is enabled; otherwise
245 * this component is disabled
248 public void setEnabled(boolean b
) {
250 for (GuiReaderBook book
: books
) {
262 * The number of books in this group.
266 public int getBooksCount() {
271 * Return the index of the currently selected book if any, -1 if none.
273 * @return the index or -1
275 public int getSelectedBookIndex() {
277 for (int i
= 0; i
< books
.size(); i
++) {
278 if (books
.get(i
).isSelected()) {
287 * Select the given book, or unselect all items.
290 * the index of the book to select, can be outside the bounds
291 * (either all the items will be unselected or the first or last
292 * book will then be selected, see <tt>forceRange>/tt>)
294 * TRUE to constraint the index to the first/last element, FALSE
295 * to unselect when outside the range
297 public void setSelectedBook(int index
, boolean forceRange
) {
298 int previousIndex
= getSelectedBookIndex();
300 if (index
>= books
.size()) {
302 index
= books
.size() - 1;
308 if (index
< 0 && forceRange
) {
312 if (previousIndex
>= 0) {
313 books
.get(previousIndex
).setSelected(false);
316 if (index
>= 0 && !books
.isEmpty()) {
317 books
.get(index
).setSelected(true);
322 * The action to execute when a key is typed.
327 private void onKeyTyped(KeyEvent e
) {
328 boolean consumed
= false;
329 boolean action
= e
.getKeyChar() == '\n';
330 boolean popup
= e
.getKeyChar() == ' ';
331 if (action
|| popup
) {
334 int index
= getSelectedBookIndex();
336 GuiReaderBook book
= books
.get(index
);
340 book
.popup(book
, book
.getWidth() / 2, book
.getHeight() / 2);
351 * The action to execute when a key is pressed.
356 private void onKeyPressed(KeyEvent e
) {
357 boolean consumed
= false;
358 if (e
.isActionKey()) {
360 switch (e
.getKeyCode()) {
361 case KeyEvent
.VK_LEFT
:
364 case KeyEvent
.VK_RIGHT
:
368 offset
= -itemsPerLine
;
370 case KeyEvent
.VK_DOWN
:
371 offset
= itemsPerLine
;
378 int previousIndex
= getSelectedBookIndex();
379 if (previousIndex
>= 0) {
380 setSelectedBook(previousIndex
+ offset
, true);