Instance: use getInstance()
[nikiroo-utils.git] / src / be / nikiroo / fanfix / reader / ui / GuiReaderViewerPanel.java
index 1ff4fcc45c69de3e2c4ebb7c48cf9b7bc2df536e..0577a0ab2f4bac5c734e5282d589f5d8004926cc 100644 (file)
@@ -1,6 +1,8 @@
 package be.nikiroo.fanfix.reader.ui;
 
 import java.awt.BorderLayout;
+import java.awt.EventQueue;
+import java.awt.Graphics2D;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.image.BufferedImage;
@@ -8,13 +10,17 @@ import java.awt.image.BufferedImage;
 import javax.swing.Icon;
 import javax.swing.ImageIcon;
 import javax.swing.JButton;
+import javax.swing.JEditorPane;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
 import javax.swing.JProgressBar;
 import javax.swing.JScrollPane;
+import javax.swing.SwingConstants;
 
 import be.nikiroo.fanfix.Instance;
+import be.nikiroo.fanfix.bundles.StringIdGui;
 import be.nikiroo.fanfix.data.Chapter;
+import be.nikiroo.fanfix.data.MetaData;
 import be.nikiroo.fanfix.data.Story;
 import be.nikiroo.utils.Image;
 import be.nikiroo.utils.ui.ImageUtilsAwt;
@@ -29,9 +35,14 @@ public class GuiReaderViewerPanel extends JPanel {
 
        private boolean imageDocument;
        private Chapter chap;
-
-       private JLabel label;
+       private JScrollPane scroll;
        private GuiReaderViewerTextOutput htmlOutput;
+
+       // text only:
+       private JEditorPane text;
+
+       // image only:
+       private JLabel image;
        private JProgressBar imageProgress;
        private int currentImage;
        private JButton left;
@@ -41,23 +52,42 @@ public class GuiReaderViewerPanel extends JPanel {
         * Create a new viewer.
         * 
         * @param story
-        *            the {@link Story} to work on.
+        *            the {@link Story} to work on
         */
        public GuiReaderViewerPanel(Story story) {
+               this(story.getMeta(), story.getMeta().isImageDocument());
+       }
+
+       /**
+        * Create a new viewer.
+        * 
+        * @param meta
+        *            the {@link MetaData} of the story to show
+        * @param isImageDocument
+        *            TRUE if it is an image document, FALSE if not
+        */
+       public GuiReaderViewerPanel(MetaData meta, boolean isImageDocument) {
                super(new BorderLayout());
 
-               this.imageDocument = story.getMeta().isImageDocument();
-               this.label = new JLabel();
-               label.setAlignmentY(TOP_ALIGNMENT);
+               this.imageDocument = isImageDocument;
+
+               this.text = new JEditorPane("text/html", "");
+               text.setEditable(false);
+               text.setAlignmentY(TOP_ALIGNMENT);
                htmlOutput = new GuiReaderViewerTextOutput();
 
-               if (!imageDocument) {
-                       // TODO: why is it broken?
-                       // text.setPreferredSize(new Dimension(500, 500));
+               image = new JLabel();
+               image.setHorizontalAlignment(SwingConstants.CENTER);
 
-                       JScrollPane scroll = new JScrollPane(label);
-                       scroll.getVerticalScrollBar().setUnitIncrement(16);
+               scroll = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+                               JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+               scroll.getVerticalScrollBar().setUnitIncrement(16);
 
+               // TODO:
+               // JButton up = new BasicArrowButton(BasicArrowButton.NORTH);
+               // JButton down = new BasicArrowButton(BasicArrowButton.SOUTH);
+
+               if (!imageDocument) {
                        add(scroll, BorderLayout.CENTER);
                } else {
                        imageProgress = new JProgressBar();
@@ -65,9 +95,9 @@ public class GuiReaderViewerPanel extends JPanel {
                        add(imageProgress, BorderLayout.SOUTH);
 
                        JPanel main = new JPanel(new BorderLayout());
-                       main.add(label, BorderLayout.CENTER);
+                       main.add(scroll, BorderLayout.CENTER);
 
-                       left = new JButton(" < ");
+                       left = new JButton("<HTML>&nbsp; &nbsp; &lt; &nbsp; &nbsp;</HTML>");
                        left.addActionListener(new ActionListener() {
                                @Override
                                public void actionPerformed(ActionEvent e) {
@@ -76,7 +106,7 @@ public class GuiReaderViewerPanel extends JPanel {
                        });
                        main.add(left, BorderLayout.WEST);
 
-                       right = new JButton(" > ");
+                       right = new JButton("<HTML>&nbsp; &nbsp; &gt; &nbsp; &nbsp;</HTML>");
                        right.addActionListener(new ActionListener() {
                                @Override
                                public void actionPerformed(ActionEvent e) {
@@ -86,9 +116,10 @@ public class GuiReaderViewerPanel extends JPanel {
                        main.add(right, BorderLayout.EAST);
 
                        add(main, BorderLayout.CENTER);
+                       main.invalidate();
                }
 
-               setChapter(story.getMeta().getResume());
+               setChapter(meta.getResume());
        }
 
        /**
@@ -106,7 +137,7 @@ public class GuiReaderViewerPanel extends JPanel {
                this.chap = chap;
 
                if (!imageDocument) {
-                       label.setText(htmlOutput.convert(chap));
+                       setText(chap);
                } else {
                        left.setVisible(chap.getNumber() > 0);
                        right.setVisible(chap.getNumber() > 0);
@@ -116,13 +147,56 @@ public class GuiReaderViewerPanel extends JPanel {
                        imageProgress.setMaximum(chap.getParagraphs().size() - 1);
 
                        if (chap.getNumber() == 0) {
-                               label.setText(htmlOutput.convert(chap));
+                               setText(chap);
                        } else {
                                setImage(0);
                        }
                }
        }
 
+       /**
+        * Will set and display the current chapter text.
+        * 
+        * @param chap
+        *            the chapter to display
+        */
+       private void setText(final Chapter chap) {
+               new Thread(new Runnable() {
+                       @Override
+                       public void run() {
+                               final String content = htmlOutput.convert(chap);
+                               // Wait until size computations are correct
+                               while (!scroll.isValid()) {
+                                       try {
+                                               Thread.sleep(1);
+                                       } catch (InterruptedException e) {
+                                       }
+                               }
+
+                               setText(content);
+                       }
+               }).start();
+       }
+
+       /**
+        * Actually set the text in the UI.
+        * <p>
+        * Do <b>NOT</b> use this method from the UI thread.
+        * 
+        * @param content
+        *            the text
+        */
+       private void setText(final String content) {
+               EventQueue.invokeLater(new Runnable() {
+                       @Override
+                       public void run() {
+                               text.setText(content);
+                               text.setCaretPosition(0);
+                               scroll.setViewportView(text);
+                       }
+               });
+       }
+
        /**
         * Will set and display the current image, take care about the progression
         * and update the left and right cursors' <tt>enabled</tt> property.
@@ -139,26 +213,86 @@ public class GuiReaderViewerPanel extends JPanel {
                }
 
                imageProgress.setValue(i);
-               imageProgress.setString(String.format("Image %d / %d", i + 1, chap
-                               .getParagraphs().size()));
+               imageProgress.setString(GuiReader.trans(StringIdGui.IMAGE_PROGRESSION,
+                               i + 1, chap.getParagraphs().size()));
 
                currentImage = i;
-               label.setText("");
-               label.setIcon(null);
 
-               Image img = chap.getParagraphs().get(i).getContentImage();
-               if (img == null) {
-                       label.setText("Error: cannot render image.");
-               } else {
+               final Image img = chap.getParagraphs().get(i).getContentImage();
+
+               // prepare the viewport to get the right sizes later on
+               image.setIcon(null);
+               scroll.setViewportView(image);
+
+               new Thread(new Runnable() {
+                       @Override
+                       public void run() {
+                               // Wait until size computations are correct
+                               while (!scroll.isValid()) {
+                                       try {
+                                               Thread.sleep(1);
+                                       } catch (InterruptedException e) {
+                                       }
+                               }
+
+                               if (img == null) {
+                                       setText("Error: cannot render image.");
+                               } else {
+                                       setImage(img);
+                               }
+                       }
+               }).start();
+       }
+
+       /**
+        * Actually set the image in the UI.
+        * <p>
+        * Do <b>NOT</b> use this method from the UI thread.
+        * 
+        * @param img
+        *            the image to set
+        */
+       private void setImage(Image img) {
+               try {
+                       int scrollWidth = scroll.getWidth()
+                                       - scroll.getVerticalScrollBar().getWidth();
+
+                       BufferedImage buffImg = ImageUtilsAwt.fromImage(img);
+
+                       int iw = buffImg.getWidth();
+                       int ih = buffImg.getHeight();
+                       double ratio = ((double) ih) / iw;
+
+                       int w = scrollWidth;
+                       int h = (int) (ratio * scrollWidth);
+
+                       BufferedImage resizedImage = new BufferedImage(w, h,
+                                       BufferedImage.TYPE_4BYTE_ABGR);
+
+                       Graphics2D g = resizedImage.createGraphics();
                        try {
-                               BufferedImage buffImg = ImageUtilsAwt.fromImage(img);
-                               Icon icon = new ImageIcon(buffImg);
-                               label.setIcon(icon);
-                       } catch (Exception e) {
-                               Instance.getTraceHandler().error(
-                                               new Exception("Failed to load image into label", e));
-                               label.setText("Error: cannot load image.");
+                               g.drawImage(buffImg, 0, 0, w, h, null);
+                       } finally {
+                               g.dispose();
                        }
+
+                       final Icon icon = new ImageIcon(resizedImage);
+                       EventQueue.invokeLater(new Runnable() {
+                               @Override
+                               public void run() {
+                                       image.setIcon(icon);
+                                       scroll.setViewportView(image);
+                               }
+                       });
+               } catch (Exception e) {
+                       Instance.getInstance().getTraceHandler().error(
+                                       new Exception("Failed to load image into label", e));
+                       EventQueue.invokeLater(new Runnable() {
+                               @Override
+                               public void run() {
+                                       text.setText("Error: cannot load image.");
+                               }
+                       });
                }
        }
 }