import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
-import javax.swing.JScrollPane;
import javax.swing.SwingConstants;
import be.nikiroo.fanfix.data.Chapter;
import be.nikiroo.fanfix.data.Story;
import be.nikiroo.fanfix.library.BasicLibrary;
-public class GuiReaderTextViewer extends JFrame {
+/**
+ * An internal, Swing-based {@link Story} viewer.
+ * <p>
+ * Works on both text and image document (see {@link MetaData#isImageDocument()}
+ * ).
+ *
+ * @author niki
+ */
+public class GuiReaderViewer extends JFrame {
private static final long serialVersionUID = 1L;
private Story story;
private MetaData meta;
private JLabel title;
- private JLabel text;
private JLabel chapterLabel;
private GuiReaderPropertiesPane descPane;
private int currentChapter = -42; // cover = -1
- private GuiReaderTextViewerOutput htmlOutput = new GuiReaderTextViewerOutput();
-
- public GuiReaderTextViewer(BasicLibrary lib, Story story) {
+ private GuiReaderViewerPanel mainPanel;
+ private JButton[] navButtons;
+
+ /**
+ * Create a new {@link Story} viewer.
+ *
+ * @param lib
+ * the {@link BasicLibrary} to load the cover from
+ * @param story
+ * the {@link Story} to display
+ */
+ public GuiReaderViewer(BasicLibrary lib, Story story) {
super(story.getMeta().getLuid() + ": " + story.getMeta().getTitle());
setSize(800, 600);
setChapter(-1);
}
+ /**
+ * Initialise the base panel with everything but the navigation buttons.
+ *
+ * @param lib
+ * the {@link BasicLibrary} to use to retrieve the cover image in
+ * the description panel
+ */
private void initGuiBase(BasicLibrary lib) {
setLayout(new BorderLayout());
descPane = new GuiReaderPropertiesPane(lib, meta);
contentPane.add(descPane, BorderLayout.NORTH);
- text = new JLabel();
- text.setAlignmentY(TOP_ALIGNMENT);
-
- // TODO: why is it broken?
- // text.setPreferredSize(new Dimension(500, 500));
-
- JScrollPane scroll = new JScrollPane(text);
- scroll.getVerticalScrollBar().setUnitIncrement(16);
- contentPane.add(scroll, BorderLayout.CENTER);
+ mainPanel = new GuiReaderViewerPanel(story);
+ contentPane.add(mainPanel, BorderLayout.CENTER);
}
+ /**
+ * Create the 4 navigation buttons in {@link GuiReaderViewer#navButtons} and
+ * initialise them.
+ */
private void initGuiNavButtons() {
- JPanel navButtons = new JPanel();
- LayoutManager layout = new BoxLayout(navButtons, BoxLayout.X_AXIS);
- navButtons.setLayout(layout);
+ JPanel navButtonsPane = new JPanel();
+ LayoutManager layout = new BoxLayout(navButtonsPane, BoxLayout.X_AXIS);
+ navButtonsPane.setLayout(layout);
- navButtons.add(createNavButton("<<", new ActionListener() {
+ navButtons = new JButton[4];
+
+ navButtons[0] = createNavButton("<<", new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setChapter(-1);
}
- }));
- navButtons.add(createNavButton(" < ", new ActionListener() {
+ });
+ navButtons[1] = createNavButton(" < ", new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setChapter(currentChapter - 1);
}
- }));
- navButtons.add(createNavButton(" > ", new ActionListener() {
+ });
+ navButtons[2] = createNavButton(" > ", new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setChapter(currentChapter + 1);
}
- }));
- navButtons.add(createNavButton(">>", new ActionListener() {
+ });
+ navButtons[3] = createNavButton(">>", new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setChapter(story.getChapters().size() - 1);
}
- }));
+ });
- add(navButtons, BorderLayout.SOUTH);
+ for (JButton navButton : navButtons) {
+ navButtonsPane.add(navButton);
+ }
+
+ add(navButtonsPane, BorderLayout.SOUTH);
chapterLabel = new JLabel("");
- navButtons.add(chapterLabel);
+ navButtonsPane.add(chapterLabel);
}
+ /**
+ * Create a single navigation button.
+ *
+ * @param text
+ * the text to display
+ * @param action
+ * the action to take on click
+ * @return the button
+ */
private JButton createNavButton(String text, ActionListener action) {
JButton navButton = new JButton(text);
navButton.addActionListener(action);
return navButton;
}
+ /**
+ * Set the current chapter, 0-based.
+ * <p>
+ * Chapter -1 is reserved for the description page.
+ *
+ * @param chapter
+ * the chapter number to set
+ */
private void setChapter(int chapter) {
+ navButtons[0].setEnabled(chapter >= 0);
+ navButtons[1].setEnabled(chapter >= 0);
+ navButtons[2].setEnabled(chapter + 1 < story.getChapters().size());
+ navButtons[3].setEnabled(chapter + 1 < story.getChapters().size());
+
if (chapter >= -1 && chapter < story.getChapters().size()
&& chapter != currentChapter) {
currentChapter = chapter;
Chapter chap;
if (chapter == -1) {
chap = meta.getResume();
- setCoverPage();
+ descPane.setVisible(true);
} else {
chap = story.getChapters().get(chapter);
descPane.setVisible(false);
chapterLabel.setText("<HTML> <B>Chapter "
+ chap.getNumber() + "</B>: " + chap.getName() + "</HTML>");
- text.setText(htmlOutput.convert(chap));
+ System.out.println(chap);
+ mainPanel.setChapter(chap);
}
}
-
- private void setCoverPage() {
- descPane.setVisible(true);
- }
}
--- /dev/null
+package be.nikiroo.fanfix.reader.ui;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.image.BufferedImage;
+
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+import javax.swing.JScrollPane;
+
+import be.nikiroo.fanfix.Instance;
+import be.nikiroo.fanfix.data.Chapter;
+import be.nikiroo.fanfix.data.Story;
+import be.nikiroo.utils.Image;
+import be.nikiroo.utils.ui.ImageUtilsAwt;
+
+/**
+ * A {@link JPanel} that will show a {@link Story} chapter on screen.
+ *
+ * @author niki
+ */
+public class GuiReaderViewerPanel extends JPanel {
+ private static final long serialVersionUID = 1L;
+
+ private boolean imageDocument;
+ private Chapter chap;
+
+ private JLabel label;
+ private GuiReaderViewerTextOutput htmlOutput;
+ private JProgressBar imageProgress;
+ private int currentImage;
+ private JButton left;
+ private JButton right;
+
+ /**
+ * Create a new viewer.
+ *
+ * @param story
+ * the {@link Story} to work on.
+ */
+ public GuiReaderViewerPanel(Story story) {
+ super(new BorderLayout());
+
+ this.imageDocument = story.getMeta().isImageDocument();
+ this.label = new JLabel();
+ label.setAlignmentY(TOP_ALIGNMENT);
+ htmlOutput = new GuiReaderViewerTextOutput();
+
+ if (!imageDocument) {
+ // TODO: why is it broken?
+ // text.setPreferredSize(new Dimension(500, 500));
+
+ JScrollPane scroll = new JScrollPane(label);
+ scroll.getVerticalScrollBar().setUnitIncrement(16);
+
+ add(scroll, BorderLayout.CENTER);
+ } else {
+ imageProgress = new JProgressBar();
+ imageProgress.setStringPainted(true);
+ add(imageProgress, BorderLayout.SOUTH);
+
+ JPanel main = new JPanel(new BorderLayout());
+ main.add(label, BorderLayout.CENTER);
+
+ left = new JButton(" < ");
+ left.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ setImage(--currentImage);
+ }
+ });
+ main.add(left, BorderLayout.WEST);
+
+ right = new JButton(" > ");
+ right.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ setImage(++currentImage);
+ }
+ });
+ main.add(right, BorderLayout.EAST);
+
+ add(main, BorderLayout.CENTER);
+ }
+
+ setChapter(story.getMeta().getResume());
+ }
+
+ /**
+ * Load the given chapter.
+ * <p>
+ * Will always be text for a non-image document.
+ * <p>
+ * Will be an image and left/right controls for an image-document, except
+ * for chapter 0 which will be text (chapter 0 = resume).
+ *
+ * @param chap
+ * the chapter to load
+ */
+ public void setChapter(Chapter chap) {
+ this.chap = chap;
+
+ if (!imageDocument) {
+ label.setText(htmlOutput.convert(chap));
+ } else {
+ left.setVisible(chap.getNumber() > 0);
+ right.setVisible(chap.getNumber() > 0);
+ imageProgress.setVisible(chap.getNumber() > 0);
+
+ imageProgress.setMinimum(0);
+ imageProgress.setMaximum(chap.getParagraphs().size() - 1);
+
+ if (chap.getNumber() == 0) {
+ label.setText(htmlOutput.convert(chap));
+ } else {
+ setImage(0);
+ }
+ }
+ }
+
+ /**
+ * Will set and display the current image, take care about the progression
+ * and update the left and right cursors' <tt>enabled</tt> property.
+ *
+ * @param i
+ * the image index to load
+ */
+ private void setImage(int i) {
+ left.setEnabled(i > 0);
+ right.setEnabled(i + 1 < chap.getParagraphs().size());
+
+ if (i < 0 || i >= chap.getParagraphs().size()) {
+ return;
+ }
+
+ imageProgress.setValue(i);
+ imageProgress.setString(String.format("Image %d / %d", 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 {
+ 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.");
+ }
+ }
+ }
+}