1 package be
.nikiroo
.fanfix
.reader
.ui
;
3 import java
.awt
.BorderLayout
;
4 import java
.awt
.EventQueue
;
5 import java
.awt
.Graphics2D
;
6 import java
.awt
.event
.ActionEvent
;
7 import java
.awt
.event
.ActionListener
;
8 import java
.awt
.image
.BufferedImage
;
10 import javax
.swing
.Icon
;
11 import javax
.swing
.ImageIcon
;
12 import javax
.swing
.JButton
;
13 import javax
.swing
.JEditorPane
;
14 import javax
.swing
.JLabel
;
15 import javax
.swing
.JPanel
;
16 import javax
.swing
.JProgressBar
;
17 import javax
.swing
.JScrollPane
;
18 import javax
.swing
.SwingConstants
;
20 import be
.nikiroo
.fanfix
.Instance
;
21 import be
.nikiroo
.fanfix
.bundles
.StringIdGui
;
22 import be
.nikiroo
.fanfix
.data
.Chapter
;
23 import be
.nikiroo
.fanfix
.data
.MetaData
;
24 import be
.nikiroo
.fanfix
.data
.Story
;
25 import be
.nikiroo
.utils
.Image
;
26 import be
.nikiroo
.utils
.ui
.ImageUtilsAwt
;
29 * A {@link JPanel} that will show a {@link Story} chapter on screen.
33 public class GuiReaderViewerPanel
extends JPanel
{
34 private static final long serialVersionUID
= 1L;
36 private boolean imageDocument
;
38 private JScrollPane scroll
;
39 private GuiReaderViewerTextOutput htmlOutput
;
42 private JEditorPane text
;
46 private JProgressBar imageProgress
;
47 private int currentImage
;
49 private JButton right
;
52 * Create a new viewer.
55 * the {@link Story} to work on
57 public GuiReaderViewerPanel(Story story
) {
58 this(story
.getMeta(), story
.getMeta().isImageDocument());
62 * Create a new viewer.
65 * the {@link MetaData} of the story to show
66 * @param isImageDocument
67 * TRUE if it is an image document, FALSE if not
69 public GuiReaderViewerPanel(MetaData meta
, boolean isImageDocument
) {
70 super(new BorderLayout());
72 this.imageDocument
= isImageDocument
;
74 this.text
= new JEditorPane("text/html", "");
75 text
.setEditable(false);
76 text
.setAlignmentY(TOP_ALIGNMENT
);
77 htmlOutput
= new GuiReaderViewerTextOutput();
80 image
.setHorizontalAlignment(SwingConstants
.CENTER
);
82 scroll
= new JScrollPane(JScrollPane
.VERTICAL_SCROLLBAR_AS_NEEDED
,
83 JScrollPane
.HORIZONTAL_SCROLLBAR_NEVER
);
84 scroll
.getVerticalScrollBar().setUnitIncrement(16);
87 add(scroll
, BorderLayout
.CENTER
);
89 imageProgress
= new JProgressBar();
90 imageProgress
.setStringPainted(true);
91 add(imageProgress
, BorderLayout
.SOUTH
);
93 JPanel main
= new JPanel(new BorderLayout());
94 main
.add(scroll
, BorderLayout
.CENTER
);
96 left
= new JButton("<HTML> < </HTML>");
97 left
.addActionListener(new ActionListener() {
99 public void actionPerformed(ActionEvent e
) {
100 setImage(--currentImage
);
103 main
.add(left
, BorderLayout
.WEST
);
105 right
= new JButton("<HTML> > </HTML>");
106 right
.addActionListener(new ActionListener() {
108 public void actionPerformed(ActionEvent e
) {
109 setImage(++currentImage
);
112 main
.add(right
, BorderLayout
.EAST
);
114 add(main
, BorderLayout
.CENTER
);
118 setChapter(meta
.getResume());
122 * Load the given chapter.
124 * Will always be text for a non-image document.
126 * Will be an image and left/right controls for an image-document, except
127 * for chapter 0 which will be text (chapter 0 = resume).
130 * the chapter to load
132 public void setChapter(Chapter chap
) {
135 if (!imageDocument
) {
138 left
.setVisible(chap
.getNumber() > 0);
139 right
.setVisible(chap
.getNumber() > 0);
140 imageProgress
.setVisible(chap
.getNumber() > 0);
142 imageProgress
.setMinimum(0);
143 imageProgress
.setMaximum(chap
.getParagraphs().size() - 1);
145 if (chap
.getNumber() == 0) {
154 * Will set and display the current chapter text.
157 * the chapter to display
159 private void setText(final Chapter chap
) {
160 new Thread(new Runnable() {
163 final String content
= htmlOutput
.convert(chap
);
164 // Wait until size computations are correct
165 while (!scroll
.isValid()) {
168 } catch (InterruptedException e
) {
178 * Actually set the text in the UI.
180 * Do <b>NOT</b> use this method from the UI thread.
185 private void setText(final String content
) {
186 EventQueue
.invokeLater(new Runnable() {
189 text
.setText(content
);
190 text
.setCaretPosition(0);
191 scroll
.setViewportView(text
);
197 * Will set and display the current image, take care about the progression
198 * and update the left and right cursors' <tt>enabled</tt> property.
201 * the image index to load
203 private void setImage(int i
) {
204 left
.setEnabled(i
> 0);
205 right
.setEnabled(i
+ 1 < chap
.getParagraphs().size());
207 if (i
< 0 || i
>= chap
.getParagraphs().size()) {
211 imageProgress
.setValue(i
);
212 imageProgress
.setString(GuiReader
.trans(StringIdGui
.IMAGE_PROGRESSION
,
213 i
+ 1, chap
.getParagraphs().size()));
217 final Image img
= chap
.getParagraphs().get(i
).getContentImage();
219 // prepare the viewport to get the right sizes later on
221 scroll
.setViewportView(image
);
223 new Thread(new Runnable() {
226 // Wait until size computations are correct
227 while (!scroll
.isValid()) {
230 } catch (InterruptedException e
) {
235 setText("Error: cannot render image.");
244 * Actually set the image in the UI.
246 * Do <b>NOT</b> use this method from the UI thread.
251 private void setImage(Image img
) {
253 int scrollWidth
= scroll
.getWidth()
254 - scroll
.getVerticalScrollBar().getWidth();
256 BufferedImage buffImg
= ImageUtilsAwt
.fromImage(img
);
258 int iw
= buffImg
.getWidth();
259 int ih
= buffImg
.getHeight();
260 double ratio
= ((double) ih
) / iw
;
263 int h
= (int) (ratio
* scrollWidth
);
265 BufferedImage resizedImage
= new BufferedImage(w
, h
,
266 BufferedImage
.TYPE_4BYTE_ABGR
);
268 Graphics2D g
= resizedImage
.createGraphics();
270 g
.drawImage(buffImg
, 0, 0, w
, h
, null);
275 final Icon icon
= new ImageIcon(resizedImage
);
276 EventQueue
.invokeLater(new Runnable() {
280 scroll
.setViewportView(image
);
283 } catch (Exception e
) {
284 Instance
.getTraceHandler().error(
285 new Exception("Failed to load image into label", e
));
286 EventQueue
.invokeLater(new Runnable() {
289 text
.setText("Error: cannot load image.");