78e1d06a473bc91b8cafcff9147bd3f2325d85ed
[fanfix.git] / src / be / nikiroo / fanfix / reader / ui / GuiReaderGroup.java
1 package be.nikiroo.fanfix.reader.ui;
2
3 import java.awt.BorderLayout;
4 import java.awt.Color;
5 import java.awt.event.ActionListener;
6 import java.awt.event.ComponentAdapter;
7 import java.awt.event.ComponentEvent;
8 import java.awt.event.KeyAdapter;
9 import java.awt.event.KeyEvent;
10 import java.awt.event.MouseEvent;
11 import java.util.ArrayList;
12 import java.util.List;
13
14 import javax.swing.JLabel;
15 import javax.swing.JPanel;
16
17 import be.nikiroo.fanfix.bundles.StringIdGui;
18 import be.nikiroo.fanfix.reader.ui.GuiReaderBook.BookActionListener;
19 import be.nikiroo.utils.ui.WrapLayout;
20
21 /**
22 * A group of {@link GuiReaderBook}s for display.
23 *
24 * @author niki
25 */
26 public class GuiReaderGroup extends JPanel {
27 private static final long serialVersionUID = 1L;
28 private BookActionListener action;
29 private Color backgroundColor;
30 private GuiReader reader;
31 private List<GuiReaderBookInfo> infos;
32 private List<GuiReaderBook> books;
33 private JPanel pane;
34 private boolean words; // words or authors (secondary info on books)
35 private int itemsPerLine;
36
37 /**
38 * Create a new {@link GuiReaderGroup}.
39 *
40 * @param reader
41 * the {@link GuiReaderBook} used to probe some information about
42 * the stories
43 * @param title
44 * the title of this group
45 * @param backgroundColor
46 * the background colour to use (or NULL for default)
47 */
48 public GuiReaderGroup(GuiReader reader, String title, Color backgroundColor) {
49 this.reader = reader;
50 this.backgroundColor = backgroundColor;
51
52 this.pane = new JPanel();
53
54 pane.setLayout(new WrapLayout(WrapLayout.LEADING, 5, 5));
55 if (backgroundColor != null) {
56 pane.setBackground(backgroundColor);
57 setBackground(backgroundColor);
58 }
59
60 setLayout(new BorderLayout(0, 10));
61 add(pane, BorderLayout.CENTER);
62
63 if (title != null) {
64 if (title.isEmpty()) {
65 title = GuiReader.trans(StringIdGui.MENU_AUTHORS_UNKNOWN);
66 }
67
68 JLabel label = new JLabel();
69 label.setText(String.format("<html>"
70 + "<body style='text-align: center; color: gray;'><br><b>"
71 + "%s" + "</b></body>" + "</html>", title));
72 label.setHorizontalAlignment(JLabel.CENTER);
73 add(label, BorderLayout.NORTH);
74 }
75
76 // Compute the number of items per line at each resize
77 addComponentListener(new ComponentAdapter() {
78 @Override
79 public void componentResized(ComponentEvent e) {
80 super.componentResized(e);
81 computeItemsPerLine();
82 }
83 });
84 computeItemsPerLine();
85
86 addKeyListener(new KeyAdapter() {
87 @Override
88 public void keyTyped(KeyEvent e) {
89 onKeyTyped(e);
90 }
91 });
92 }
93
94 /**
95 * Compute how many items can fit in a line so UP and DOWN can be used to go
96 * up/down one line at a time.
97 */
98 private void computeItemsPerLine() {
99 // TODO
100 itemsPerLine = 5;
101 }
102
103 /**
104 * Set the {@link ActionListener} that will be fired on each
105 * {@link GuiReaderBook} action.
106 *
107 * @param action
108 * the action
109 */
110 public void setActionListener(BookActionListener action) {
111 this.action = action;
112 refreshBooks(infos, words);
113 }
114
115 /**
116 * Refresh the list of {@link GuiReaderBook}s displayed in the control.
117 *
118 * @param infos
119 * the new list of infos
120 * @param seeWordcount
121 * TRUE to see word counts, FALSE to see authors
122 */
123 public void refreshBooks(List<GuiReaderBookInfo> infos, boolean seeWordcount) {
124 this.infos = infos;
125 refreshBooks(seeWordcount);
126 }
127
128 /**
129 * Refresh the list of {@link GuiReaderBook}s displayed in the control.
130 * <p>
131 * Will not change the current stories.
132 *
133 * @param seeWordcount
134 * TRUE to see word counts, FALSE to see authors
135 */
136 public void refreshBooks(boolean seeWordcount) {
137 this.words = seeWordcount;
138
139 books = new ArrayList<GuiReaderBook>();
140 invalidate();
141 pane.invalidate();
142 pane.removeAll();
143
144 if (infos != null) {
145 for (GuiReaderBookInfo info : infos) {
146 boolean isCached = false;
147 if (info.getMeta() != null) {
148 isCached = reader.isCached(info.getMeta().getLuid());
149 }
150
151 GuiReaderBook book = new GuiReaderBook(reader, info, isCached,
152 words);
153 if (backgroundColor != null) {
154 book.setBackground(backgroundColor);
155 }
156
157 books.add(book);
158
159 book.addActionListener(new BookActionListener() {
160 @Override
161 public void select(GuiReaderBook book) {
162 for (GuiReaderBook abook : books) {
163 abook.setSelected(abook == book);
164 }
165 }
166
167 @Override
168 public void popupRequested(GuiReaderBook book, MouseEvent e) {
169 }
170
171 @Override
172 public void action(GuiReaderBook book) {
173 }
174 });
175
176 if (action != null) {
177 book.addActionListener(action);
178 }
179
180 pane.add(book);
181 }
182 }
183
184 pane.validate();
185 pane.repaint();
186 validate();
187 repaint();
188 }
189
190 /**
191 * Enables or disables this component, depending on the value of the
192 * parameter <code>b</code>. An enabled component can respond to user input
193 * and generate events. Components are enabled initially by default.
194 * <p>
195 * Disabling this component will also affect its children.
196 *
197 * @param b
198 * If <code>true</code>, this component is enabled; otherwise
199 * this component is disabled
200 */
201 @Override
202 public void setEnabled(boolean b) {
203 if (books != null) {
204 for (GuiReaderBook book : books) {
205 book.setEnabled(b);
206 book.repaint();
207 }
208 }
209
210 pane.setEnabled(b);
211 super.setEnabled(b);
212 repaint();
213 }
214
215 /**
216 * The action to execute when a key is typed.
217 *
218 * @param e
219 * the key event
220 */
221 private void onKeyTyped(KeyEvent e) {
222 boolean consumed = false;
223 System.out.println(e);
224 if (e.isActionKey()) {
225 int offset = 0;
226 switch (e.getKeyCode()) {
227 case KeyEvent.VK_LEFT:
228 offset = -1;
229 break;
230 case KeyEvent.VK_RIGHT:
231 offset = 1;
232 break;
233 case KeyEvent.VK_UP:
234 offset = itemsPerLine;
235 break;
236 case KeyEvent.VK_DOWN:
237 offset = -itemsPerLine;
238 break;
239 }
240
241 if (offset != 0) {
242 consumed = true;
243
244 int selected = -1;
245 for (int i = 0; i < books.size(); i++) {
246 if (books.get(i).isSelected()) {
247 selected = i;
248 break;
249 }
250 }
251
252 if (selected >= 0) {
253 int newSelect = selected + offset;
254 if (newSelect >= books.size()) {
255 newSelect = books.size() - 1;
256 }
257
258 if (selected != newSelect && newSelect >= 0) {
259 if (selected >= 0) {
260 books.get(selected).setSelected(false);
261 books.get(newSelect).setSelected(true);
262 }
263 }
264 }
265 }
266 }
267
268 if (consumed) {
269 e.consume();
270 } else {
271 super.processKeyEvent(e);
272 }
273 }
274 }