Add progress reporting on GUI
[fanfix.git] / src / be / nikiroo / fanfix / reader / LocalReaderBook.java
CommitLineData
333f0e7b
NR
1package be.nikiroo.fanfix.reader;
2
3import java.awt.BorderLayout;
4import java.awt.Color;
d3c84ac3 5import java.awt.Graphics;
333f0e7b 6import java.awt.Graphics2D;
b4dc6ab5 7import java.awt.Polygon;
d3c84ac3 8import java.awt.Rectangle;
333f0e7b
NR
9import java.awt.event.MouseEvent;
10import java.awt.event.MouseListener;
11import java.awt.image.BufferedImage;
12import java.util.ArrayList;
13import java.util.Date;
14import java.util.EventListener;
15import java.util.List;
16
17import javax.swing.ImageIcon;
18import javax.swing.JLabel;
19import javax.swing.JPanel;
20
21import be.nikiroo.fanfix.data.MetaData;
22
23/**
24 * A book item presented in a {@link LocalReaderFrame}.
25 *
26 * @author niki
27 */
28class LocalReaderBook extends JPanel {
29 /**
30 * Action on a book item.
31 *
32 * @author niki
33 */
92fb0719 34 interface BookActionListener extends EventListener {
333f0e7b
NR
35 /**
36 * The book was selected (single click).
37 *
38 * @param book
39 * the {@link LocalReaderBook} itself
40 */
41 public void select(LocalReaderBook book);
42
43 /**
44 * The book was double-clicked.
45 *
46 * @param book
47 * the {@link LocalReaderBook} itself
48 */
49 public void action(LocalReaderBook book);
50 }
51
b4dc6ab5
NR
52 private static final int COVER_WIDTH = 100;
53 private static final int COVER_HEIGHT = 150;
54 private static final int SPINE_WIDTH = 5;
55 private static final int SPINE_HEIGHT = 5;
56 private static final int HOFFSET = 20;
57 private static final Color SPINE_COLOR_BOTTOM = new Color(180, 180, 180);
58 private static final Color SPINE_COLOR_RIGHT = new Color(100, 100, 100);
59 private static final int TEXT_WIDTH = COVER_WIDTH + 40;
60 private static final int TEXT_HEIGHT = 50;
61 private static final String AUTHOR_COLOR = "#888888";
333f0e7b 62 private static final long serialVersionUID = 1L;
3b2b638f 63
333f0e7b 64 private JLabel icon;
3b2b638f 65 private JLabel title;
333f0e7b
NR
66 private boolean selected;
67 private boolean hovered;
68 private Date lastClick;
69 private long doubleClickDelay = 200; // in ms
92fb0719 70 private List<BookActionListener> listeners;
333f0e7b
NR
71
72 public LocalReaderBook(MetaData meta) {
73 if (meta.getCover() != null) {
b4dc6ab5
NR
74 BufferedImage resizedImage = new BufferedImage(SPINE_WIDTH
75 + COVER_WIDTH, SPINE_HEIGHT + COVER_HEIGHT + HOFFSET,
333f0e7b
NR
76 BufferedImage.TYPE_4BYTE_ABGR);
77 Graphics2D g = resizedImage.createGraphics();
b4dc6ab5
NR
78 g.setColor(Color.white);
79 g.fillRect(0, HOFFSET, COVER_WIDTH, COVER_HEIGHT);
80 g.drawImage(meta.getCover(), 0, HOFFSET, COVER_WIDTH, COVER_HEIGHT,
81 null);
333f0e7b
NR
82 g.dispose();
83
84 icon = new JLabel(new ImageIcon(resizedImage));
85 } else {
b4dc6ab5 86 // TODO: a big black "X" ?
333f0e7b
NR
87 icon = new JLabel(" [ no cover ] ");
88 }
89
b4dc6ab5
NR
90 String optAuthor = meta.getAuthor();
91 if (optAuthor != null && !optAuthor.isEmpty()) {
92 optAuthor = "(" + optAuthor + ")";
93 }
3b2b638f 94 title = new JLabel(
b4dc6ab5
NR
95 String.format(
96 "<html>"
97 + "<body style='width: %d px; height: %d px; text-align: center'>"
98 + "%s" + "<br>" + "<span style='color: %s;'>"
99 + "%s" + "</span>" + "</body>" + "</html>",
100 TEXT_WIDTH, TEXT_HEIGHT, meta.getTitle(), AUTHOR_COLOR,
101 optAuthor));
102
103 this.setLayout(new BorderLayout(10, 10));
333f0e7b 104 this.add(icon, BorderLayout.CENTER);
3b2b638f 105 this.add(title, BorderLayout.SOUTH);
333f0e7b
NR
106
107 setupListeners();
108 setSelected(false);
109 }
110
111 /**
112 * The book current selection state.
113 *
114 * @return the selected
115 */
116 public boolean isSelected() {
117 return selected;
118 }
119
120 /**
121 * The book current selection state.
122 *
123 * @param selected
124 * the selected to set
125 */
126 public void setSelected(boolean selected) {
127 this.selected = selected;
d3c84ac3 128 repaint();
333f0e7b
NR
129 }
130
131 private void setHovered(boolean hovered) {
132 this.hovered = hovered;
d3c84ac3 133 repaint();
333f0e7b
NR
134 }
135
136 private void setupListeners() {
92fb0719 137 listeners = new ArrayList<LocalReaderBook.BookActionListener>();
333f0e7b
NR
138 addMouseListener(new MouseListener() {
139 public void mouseReleased(MouseEvent e) {
140 }
141
142 public void mousePressed(MouseEvent e) {
143 }
144
145 public void mouseExited(MouseEvent e) {
146 setHovered(false);
147 }
148
149 public void mouseEntered(MouseEvent e) {
150 setHovered(true);
151 }
152
153 public void mouseClicked(MouseEvent e) {
3b2b638f
NR
154 if (isEnabled()) {
155 Date now = new Date();
156 if (lastClick != null
157 && now.getTime() - lastClick.getTime() < doubleClickDelay) {
158 click(true);
159 } else {
160 click(false);
161 }
162 lastClick = now;
333f0e7b 163 }
333f0e7b
NR
164 }
165 });
166 }
167
168 private void click(boolean doubleClick) {
92fb0719 169 for (BookActionListener listener : listeners) {
333f0e7b
NR
170 if (doubleClick) {
171 listener.action(this);
172 } else {
173 listener.select(this);
174 }
175 }
176 }
177
92fb0719 178 public void addActionListener(BookActionListener listener) {
333f0e7b
NR
179 listeners.add(listener);
180 }
d3c84ac3
NR
181
182 @Override
183 public void paint(Graphics g) {
184 super.paint(g);
185
b4dc6ab5
NR
186 int h = COVER_HEIGHT;
187 int w = COVER_WIDTH;
188 int xOffset = (TEXT_WIDTH - COVER_WIDTH) - 4;
189
190 int[] xs = new int[] { xOffset, xOffset + SPINE_WIDTH,
191 xOffset + w + SPINE_WIDTH, xOffset + w };
192 int[] ys = new int[] { HOFFSET + h, HOFFSET + h + SPINE_HEIGHT,
193 HOFFSET + h + SPINE_HEIGHT, HOFFSET + h };
194 g.setColor(SPINE_COLOR_BOTTOM);
195 g.fillPolygon(new Polygon(xs, ys, xs.length));
196 xs = new int[] { xOffset + w, xOffset + w + SPINE_WIDTH,
197 xOffset + w + SPINE_WIDTH, xOffset + w };
198 ys = new int[] { HOFFSET, HOFFSET + SPINE_HEIGHT,
199 HOFFSET + h + SPINE_HEIGHT, HOFFSET + h };
200 g.setColor(SPINE_COLOR_RIGHT);
201 g.fillPolygon(new Polygon(xs, ys, xs.length));
202
d3c84ac3 203 Color color = new Color(255, 255, 255, 0);
3b2b638f
NR
204 if (!isEnabled()) {
205 } else if (selected && !hovered) {
d3c84ac3
NR
206 color = new Color(80, 80, 100, 40);
207 } else if (!selected && hovered) {
208 color = new Color(230, 230, 255, 100);
209 } else if (selected && hovered) {
210 color = new Color(200, 200, 255, 100);
211 }
212
213 Rectangle clip = g.getClipBounds();
214 g.setColor(color);
215 g.fillRect(clip.x, clip.y, clip.width, clip.height);
216 }
333f0e7b 217}