Version 1.0.0
[fanfix.git] / src / be / nikiroo / fanfix / reader / LocalReaderBook.java
1 package be.nikiroo.fanfix.reader;
2
3 import java.awt.BorderLayout;
4 import java.awt.Color;
5 import java.awt.Graphics;
6 import java.awt.Graphics2D;
7 import java.awt.Polygon;
8 import java.awt.Rectangle;
9 import java.awt.event.MouseEvent;
10 import java.awt.event.MouseListener;
11 import java.awt.image.BufferedImage;
12 import java.util.ArrayList;
13 import java.util.Date;
14 import java.util.EventListener;
15 import java.util.List;
16
17 import javax.swing.ImageIcon;
18 import javax.swing.JLabel;
19 import javax.swing.JPanel;
20
21 import be.nikiroo.fanfix.data.MetaData;
22
23 /**
24 * A book item presented in a {@link LocalReaderFrame}.
25 *
26 * @author niki
27 */
28 class LocalReaderBook extends JPanel {
29 /**
30 * Action on a book item.
31 *
32 * @author niki
33 */
34 interface BookActionListner extends EventListener {
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
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";
62
63 private static final long serialVersionUID = 1L;
64 private JLabel icon;
65 private JLabel tt;
66 private boolean selected;
67 private boolean hovered;
68 private Date lastClick;
69 private long doubleClickDelay = 200; // in ms
70 private List<BookActionListner> listeners;
71
72 public LocalReaderBook(MetaData meta) {
73 if (meta.getCover() != null) {
74 BufferedImage resizedImage = new BufferedImage(SPINE_WIDTH
75 + COVER_WIDTH, SPINE_HEIGHT + COVER_HEIGHT + HOFFSET,
76 BufferedImage.TYPE_4BYTE_ABGR);
77 Graphics2D g = resizedImage.createGraphics();
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);
82 g.dispose();
83
84 icon = new JLabel(new ImageIcon(resizedImage));
85 } else {
86 // TODO: a big black "X" ?
87 icon = new JLabel(" [ no cover ] ");
88 }
89
90 String optAuthor = meta.getAuthor();
91 if (optAuthor != null && !optAuthor.isEmpty()) {
92 optAuthor = "(" + optAuthor + ")";
93 }
94 tt = new JLabel(
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));
104 this.add(icon, BorderLayout.CENTER);
105 this.add(tt, BorderLayout.SOUTH);
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;
128 repaint();
129 }
130
131 private void setHovered(boolean hovered) {
132 this.hovered = hovered;
133 repaint();
134 }
135
136 private void setupListeners() {
137 listeners = new ArrayList<LocalReaderBook.BookActionListner>();
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) {
154 Date now = new Date();
155 if (lastClick != null
156 && now.getTime() - lastClick.getTime() < doubleClickDelay) {
157 click(true);
158 } else {
159 click(false);
160 }
161 lastClick = now;
162 }
163 });
164 }
165
166 private void click(boolean doubleClick) {
167 for (BookActionListner listener : listeners) {
168 if (doubleClick) {
169 listener.action(this);
170 } else {
171 listener.select(this);
172 }
173 }
174 }
175
176 public void addActionListener(BookActionListner listener) {
177 listeners.add(listener);
178 }
179
180 @Override
181 public void paint(Graphics g) {
182 super.paint(g);
183
184 int h = COVER_HEIGHT;
185 int w = COVER_WIDTH;
186 int xOffset = (TEXT_WIDTH - COVER_WIDTH) - 4;
187
188 int[] xs = new int[] { xOffset, xOffset + SPINE_WIDTH,
189 xOffset + w + SPINE_WIDTH, xOffset + w };
190 int[] ys = new int[] { HOFFSET + h, HOFFSET + h + SPINE_HEIGHT,
191 HOFFSET + h + SPINE_HEIGHT, HOFFSET + h };
192 g.setColor(SPINE_COLOR_BOTTOM);
193 g.fillPolygon(new Polygon(xs, ys, xs.length));
194 xs = new int[] { xOffset + w, xOffset + w + SPINE_WIDTH,
195 xOffset + w + SPINE_WIDTH, xOffset + w };
196 ys = new int[] { HOFFSET, HOFFSET + SPINE_HEIGHT,
197 HOFFSET + h + SPINE_HEIGHT, HOFFSET + h };
198 g.setColor(SPINE_COLOR_RIGHT);
199 g.fillPolygon(new Polygon(xs, ys, xs.length));
200
201 Color color = new Color(255, 255, 255, 0);
202 if (selected && !hovered) {
203 color = new Color(80, 80, 100, 40);
204 } else if (!selected && hovered) {
205 color = new Color(230, 230, 255, 100);
206 } else if (selected && hovered) {
207 color = new Color(200, 200, 255, 100);
208 }
209
210 Rectangle clip = g.getClipBounds();
211 g.setColor(color);
212 g.fillRect(clip.x, clip.y, clip.width, clip.height);
213 }
214 }