import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map.Entry;
import be.nikiroo.fanfix.Instance;
import be.nikiroo.fanfix.bundles.Config;
import be.nikiroo.fanfix.library.LocalLibrary;
import be.nikiroo.fanfix.supported.BasicSupport;
import be.nikiroo.utils.Progress;
+import be.nikiroo.utils.StringUtils;
import be.nikiroo.utils.serial.SerialUtils;
/**
return source;
}
+ /**
+ * Describe a {@link Story} from its {@link MetaData} and return a list of
+ * title/value that represent this {@link Story}.
+ *
+ * @param meta
+ * the {@link MetaData} to represent
+ *
+ * @return the information
+ */
+ public static List<Entry<String, String>> getMetaDesc(MetaData meta) {
+ List<Entry<String, String>> metaDesc = new ArrayList<Entry<String, String>>();
+
+ // TODO: i18n
+
+ StringBuilder tags = new StringBuilder();
+ for (String tag : meta.getTags()) {
+ if (tags.length() > 0) {
+ tags.append(", ");
+ }
+ tags.append(tag);
+ }
+
+ metaDesc.add(new SimpleEntry<String, String>("Author", meta.getAuthor()));
+ metaDesc.add(new SimpleEntry<String, String>("Publication date",
+ formatDate(meta.getDate())));
+ metaDesc.add(new SimpleEntry<String, String>("Published on", meta
+ .getPublisher()));
+ metaDesc.add(new SimpleEntry<String, String>("URL", meta.getUrl()));
+ metaDesc.add(new SimpleEntry<String, String>("Word count", format(meta
+ .getWords())));
+ metaDesc.add(new SimpleEntry<String, String>("Source", meta.getSource()));
+ metaDesc.add(new SimpleEntry<String, String>("Subject", meta
+ .getSubject()));
+ metaDesc.add(new SimpleEntry<String, String>("Language", meta.getLang()));
+ metaDesc.add(new SimpleEntry<String, String>("Tags", tags.toString()));
+
+ return metaDesc;
+ }
+
/**
* Open the {@link Story} with an external reader (the program will be
* passed the main file associated with this {@link Story}).
}
}
}
+
+ static private String format(long value) {
+ String display = "";
+
+ while (value > 0) {
+ if (!display.isEmpty()) {
+ display = "." + display;
+ }
+ display = (value % 1000) + display;
+ value = value / 1000;
+ }
+
+ return display;
+ }
+
+ static private String formatDate(String date) {
+ long ms = 0;
+
+ try {
+ ms = StringUtils.toTime(date);
+ } catch (ParseException e) {
+ }
+
+ if (ms <= 0) {
+ SimpleDateFormat sdf = new SimpleDateFormat(
+ "yyyy-MM-dd'T'HH:mm:ssXXX");
+ try {
+ ms = sdf.parse(date).getTime();
+ } catch (ParseException e) {
+ }
+ }
+
+ if (ms > 0) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ return sdf.format(new Date(ms));
+ }
+
+ // :(
+ return date;
+ }
}
package be.nikiroo.fanfix.reader.tui;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Date;
import java.util.List;
+import java.util.Map;
import jexer.TAction;
import jexer.TButton;
import be.nikiroo.fanfix.data.Paragraph;
import be.nikiroo.fanfix.data.Paragraph.ParagraphType;
import be.nikiroo.fanfix.data.Story;
+import be.nikiroo.fanfix.reader.BasicReader;
import be.nikiroo.jexer.TSizeConstraint;
import be.nikiroo.jexer.TTable;
-import be.nikiroo.utils.StringUtils;
/**
* This window will contain the {@link Story} in a readable format, with a
setCurrentTitle(meta.getTitle());
- StringBuilder tags = new StringBuilder();
- for (String tag : meta.getTags()) {
- if (tags.length() > 0) {
- tags.append(", ");
- }
- tags.append(tag);
+ List<Map.Entry<String, String>> metaDesc = BasicReader
+ .getMetaDesc(meta);
+ String[][] metaDescObj = new String[metaDesc.size()][2];
+ int i = 0;
+ for (Map.Entry<String, String> entry : metaDesc) {
+ metaDescObj[i][0] = " " + entry.getKey();
+ metaDescObj[i][1] = entry.getValue();
+ i++;
}
- table.setRowData(new String[][] { //
- new String[] { " Author", meta.getAuthor() }, //
- new String[] { " Publication date", formatDate(meta.getDate()) },
- new String[] { " Published on", meta.getPublisher() },
- new String[] { " URL", meta.getUrl() },
- new String[] { " Word count", format(meta.getWords()) },
- new String[] { " Source", meta.getSource() },
- new String[] { " Subject", meta.getSubject() },
- new String[] { " Language", meta.getLang() },
- new String[] { " Tags", tags.toString() } //
- });
+ table.setRowData(metaDescObj);
table.setHeaders(Arrays.asList("key", "value"), false);
table.toTop();
}
- private String format(long value) {
- String display = "";
-
- while (value > 0) {
- if (!display.isEmpty()) {
- display = "." + display;
- }
- display = (value % 1000) + display;
- value = value / 1000;
- }
-
- return display;
- }
-
- private String formatDate(String date) {
- long ms = 0;
-
- try {
- ms = StringUtils.toTime(date);
- } catch (ParseException e) {
- }
-
- if (ms <= 0) {
- SimpleDateFormat sdf = new SimpleDateFormat(
- "yyyy-MM-dd'T'HH:mm:ssXXX");
- try {
- ms = sdf.parse(date).getTime();
- } catch (ParseException e) {
- }
- }
-
- if (ms > 0) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
- return sdf.format(new Date(ms));
- }
-
- // :(
- return date;
- }
-
/**
* Append the current chapter.
*
package be.nikiroo.fanfix.reader.ui;
import java.awt.BorderLayout;
-import java.awt.Color;
import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Polygon;
-import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
-import java.awt.image.BufferedImage;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.EventListener;
import java.util.List;
-import javax.imageio.ImageIO;
-import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
-import be.nikiroo.fanfix.Instance;
import be.nikiroo.fanfix.data.MetaData;
import be.nikiroo.fanfix.data.Story;
import be.nikiroo.fanfix.reader.Reader;
-import be.nikiroo.utils.Image;
-import be.nikiroo.utils.ui.ImageUtilsAwt;
-import be.nikiroo.utils.ui.UIUtils;
/**
* A book item presented in a {@link GuiReaderFrame}.
private static final long serialVersionUID = 1L;
- // TODO: export some of the configuration options?
- private static final int COVER_WIDTH = 100;
- private static final int COVER_HEIGHT = 150;
- private static final int SPINE_WIDTH = 5;
- private static final int SPINE_HEIGHT = 5;
- private static final int HOFFSET = 20;
- private static final Color SPINE_COLOR_BOTTOM = new Color(180, 180, 180);
- private static final Color SPINE_COLOR_RIGHT = new Color(100, 100, 100);
- private static final int TEXT_WIDTH = COVER_WIDTH + 40;
- private static final int TEXT_HEIGHT = 50;
private static final String AUTHOR_COLOR = "#888888";
- private static final Color BORDER = Color.black;
private static final long doubleClickDelay = 200; // in ms
- //
private JLabel icon;
private JLabel title;
private Date lastClick;
private List<BookActionListener> listeners;
- private Reader reader;
private MetaData meta;
private boolean cached;
*/
public GuiReaderBook(Reader reader, MetaData meta, boolean cached,
boolean seeWordCount) {
- this.reader = reader;
this.cached = cached;
this.meta = meta;
optSecondary = "";
}
- icon = new JLabel(generateCoverIcon());
+ icon = new JLabel(GuiReaderCoverImager.generateCoverIcon(
+ reader.getLibrary(), getMeta()));
title = new JLabel(
String.format(
"<html>"
+ "<body style='width: %d px; height: %d px; text-align: center'>"
+ "%s" + "<br>" + "<span style='color: %s;'>"
+ "%s" + "</span>" + "</body>" + "</html>",
- TEXT_WIDTH, TEXT_HEIGHT, meta.getTitle(), AUTHOR_COLOR,
- optSecondary));
+ GuiReaderCoverImager.TEXT_WIDTH,
+ GuiReaderCoverImager.TEXT_HEIGHT, meta.getTitle(),
+ AUTHOR_COLOR, optSecondary));
setLayout(new BorderLayout(10, 10));
add(icon, BorderLayout.CENTER);
}
}
+ /**
+ * The item mouse-hover state.
+ *
+ * @return TRUE if it is mouse-hovered
+ */
+ private boolean isHovered() {
+ return this.hovered;
+ }
+
/**
* The item mouse-hover state.
*
@Override
public void paint(Graphics g) {
super.paint(g);
- paintOverlay(g);
- }
-
- /**
- * Draw a partially transparent overlay if needed depending upon the
- * selection and mouse-hover states on top of the normal component, as well
- * as a possible "cached" icon if the item is cached.
- *
- * @param g
- * the {@link Graphics} to paint onto
- */
- public void paintOverlay(Graphics g) {
- Rectangle clip = g.getClipBounds();
- if (clip.getWidth() <= 0 || clip.getHeight() <= 0) {
- return;
- }
-
- int h = COVER_HEIGHT;
- int w = COVER_WIDTH;
- int xOffset = (TEXT_WIDTH - COVER_WIDTH) - 1;
- int yOffset = HOFFSET;
-
- if (BORDER != null) {
- if (BORDER != null) {
- g.setColor(BORDER);
- g.drawRect(xOffset, yOffset, COVER_WIDTH, COVER_HEIGHT);
- }
-
- xOffset++;
- yOffset++;
- }
-
- int[] xs = new int[] { xOffset, xOffset + SPINE_WIDTH,
- xOffset + w + SPINE_WIDTH, xOffset + w };
- int[] ys = new int[] { yOffset + h, yOffset + h + SPINE_HEIGHT,
- yOffset + h + SPINE_HEIGHT, yOffset + h };
- g.setColor(SPINE_COLOR_BOTTOM);
- g.fillPolygon(new Polygon(xs, ys, xs.length));
- xs = new int[] { xOffset + w, xOffset + w + SPINE_WIDTH,
- xOffset + w + SPINE_WIDTH, xOffset + w };
- ys = new int[] { yOffset, yOffset + SPINE_HEIGHT,
- yOffset + h + SPINE_HEIGHT, yOffset + h };
- g.setColor(SPINE_COLOR_RIGHT);
- g.fillPolygon(new Polygon(xs, ys, xs.length));
-
- Color color = new Color(255, 255, 255, 0);
- if (!isEnabled()) {
- } else if (selected && !hovered) {
- color = new Color(80, 80, 100, 40);
- } else if (!selected && hovered) {
- color = new Color(230, 230, 255, 100);
- } else if (selected && hovered) {
- color = new Color(200, 200, 255, 100);
- }
-
- g.setColor(color);
- g.fillRect(clip.x, clip.y, clip.width, clip.height);
-
- if (cached) {
- UIUtils.drawEllipse3D(g, Color.green.darker(), COVER_WIDTH
- + HOFFSET + 30, 10, 20, 20);
- }
- }
-
- /**
- * Generate a cover icon based upon the given {@link MetaData}.
- *
- * @return the icon
- */
- private ImageIcon generateCoverIcon() {
- BufferedImage resizedImage = null;
- String id = getIconId(meta);
-
- InputStream in = Instance.getCache().getFromCache(id);
- if (in != null) {
- try {
- resizedImage = ImageUtilsAwt.fromImage(new Image(in));
- in.close();
- in = null;
- } catch (IOException e) {
- Instance.getTraceHandler().error(e);
- }
- }
-
- if (resizedImage == null) {
- try {
- Image cover = null;
- if (meta.getLuid() != null) {
- cover = reader.getLibrary().getCover(meta.getLuid());
- }
- if (cover == null) {
- cover = reader.getLibrary()
- .getSourceCover(meta.getSource());
- }
-
- resizedImage = new BufferedImage(SPINE_WIDTH + COVER_WIDTH,
- SPINE_HEIGHT + COVER_HEIGHT + HOFFSET,
- BufferedImage.TYPE_4BYTE_ABGR);
- Graphics2D g = resizedImage.createGraphics();
- g.setColor(Color.white);
- g.fillRect(0, HOFFSET, COVER_WIDTH, COVER_HEIGHT);
- if (cover != null) {
- BufferedImage coverb = ImageUtilsAwt.fromImage(cover);
- g.drawImage(coverb, 0, HOFFSET, COVER_WIDTH, COVER_HEIGHT,
- null);
- } else {
- g.setColor(Color.black);
- g.drawLine(0, HOFFSET, COVER_WIDTH, HOFFSET + COVER_HEIGHT);
- g.drawLine(COVER_WIDTH, HOFFSET, 0, HOFFSET + COVER_HEIGHT);
- }
- g.dispose();
-
- if (id != null) {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ImageIO.write(resizedImage, "png", out);
- byte[] imageBytes = out.toByteArray();
- in = new ByteArrayInputStream(imageBytes);
- Instance.getCache().addToCache(in, id);
- in.close();
- in = null;
- }
- } catch (MalformedURLException e) {
- Instance.getTraceHandler().error(e);
- } catch (IOException e) {
- Instance.getTraceHandler().error(e);
- }
- }
-
- if (resizedImage != null) {
- return new ImageIcon(resizedImage);
- }
-
- return null;
- }
-
- /**
- * Manually clear the icon set for this item.
- *
- * @param meta
- * the meta of the story or source (if luid is null)
- */
- public static void clearIcon(MetaData meta) {
- String id = getIconId(meta);
- Instance.getCache().removeFromCache(id);
- }
-
- /**
- * Get a unique ID from this meta (note that if the luid is null, it is
- * considered a source and not a {@link Story}).
- *
- * @param meta
- * the meta
- * @return the unique ID
- */
- private static String getIconId(MetaData meta) {
- String id = null;
-
- String key = meta.getUuid();
- if (meta.getLuid() == null) {
- // a fake meta (== a source)
- key = "source_" + meta.getSource();
- }
-
- id = key + ".thumb_" + SPINE_WIDTH + "x" + COVER_WIDTH + "+"
- + SPINE_HEIGHT + "+" + COVER_HEIGHT + "@" + HOFFSET;
-
- return id;
+ GuiReaderCoverImager.paintOverlay(g, isEnabled(), isSelected(),
+ isHovered(), isCached());
}
}
import java.awt.BorderLayout;
import java.awt.Color;
+import java.awt.Font;
import java.awt.Frame;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.util.Map;
import java.util.Map.Entry;
+import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileFilter;
popup.add(createMenuItemRedownload());
popup.addSeparator();
popup.add(createMenuItemDelete());
+ popup.addSeparator();
+ popup.add(createMenuItemProperties());
popup.show(e.getComponent(), e.getX(), e.getY());
}
reader.clearLocalReaderCache(selectedBook.getMeta()
.getLuid());
selectedBook.setCached(false);
- GuiReaderBook.clearIcon(selectedBook.getMeta());
+ GuiReaderCoverImager.clearIcon(selectedBook
+ .getMeta());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
return delete;
}
+ /**
+ * Create the properties menu item.
+ *
+ * @return the item
+ */
+ private JMenuItem createMenuItemProperties() {
+ JMenuItem delete = new JMenuItem("Properties", KeyEvent.VK_P);
+ delete.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (selectedBook != null) {
+ outOfUi(null, new Runnable() {
+ @Override
+ public void run() {
+ final MetaData meta = selectedBook.getMeta();
+ new JFrame() {
+ private static final long serialVersionUID = 1L;
+ @SuppressWarnings("unused")
+ private Object init = init();
+
+ private Object init() {
+ // Borders
+ int top = 20;
+ int space = 10;
+
+ // Image
+ ImageIcon img = GuiReaderCoverImager
+ .generateCoverIcon(
+ reader.getLibrary(), meta);
+
+ // frame
+ setTitle(meta.getLuid() + ": "
+ + meta.getTitle());
+
+ setSize(800, img.getIconHeight() + 2 * top);
+ setLayout(new BorderLayout());
+
+ // Main panel
+ JPanel mainPanel = new JPanel(
+ new BorderLayout());
+ JPanel mainPanelKeys = new JPanel();
+ mainPanelKeys.setLayout(new BoxLayout(
+ mainPanelKeys, BoxLayout.Y_AXIS));
+ JPanel mainPanelValues = new JPanel();
+ mainPanelValues.setLayout(new BoxLayout(
+ mainPanelValues, BoxLayout.Y_AXIS));
+
+ mainPanel.add(mainPanelKeys,
+ BorderLayout.WEST);
+ mainPanel.add(mainPanelValues,
+ BorderLayout.CENTER);
+
+ List<Entry<String, String>> infos = BasicReader
+ .getMetaDesc(meta);
+
+ Color trans = new Color(0, 0, 0, 1);
+ for (Entry<String, String> info : infos) {
+ JTextArea key = new JTextArea(info
+ .getKey());
+ key.setFont(new Font(key.getFont()
+ .getFontName(), Font.BOLD, key
+ .getFont().getSize()));
+ key.setEditable(false);
+ key.setLineWrap(false);
+ key.setBackground(trans);
+ mainPanelKeys.add(key);
+
+ JTextArea value = new JTextArea(info
+ .getValue());
+ value.setEditable(false);
+ value.setLineWrap(false);
+ value.setBackground(trans);
+ mainPanelValues.add(value);
+ }
+
+ // Image
+ JLabel imgLabel = new JLabel(img);
+ imgLabel.setVerticalAlignment(JLabel.TOP);
+
+ // Borders
+ mainPanelKeys.setBorder(BorderFactory
+ .createEmptyBorder(top, space, 0, 0));
+ mainPanelValues.setBorder(BorderFactory
+ .createEmptyBorder(top, space, 0, 0));
+ imgLabel.setBorder(BorderFactory
+ .createEmptyBorder(0, space, 0, 0));
+
+ // Add all
+ add(imgLabel, BorderLayout.WEST);
+ add(mainPanel, BorderLayout.CENTER);
+
+ return null;
+ }
+
+ }.setVisible(true);
+ }
+ });
+ }
+ }
+ });
+
+ return delete;
+ }
+
/**
* Create the open menu item for a book or a source (no LUID).
*
selectedBook.getMeta().getLuid());
MetaData source = selectedBook.getMeta().clone();
source.setLuid(null);
- GuiReaderBook.clearIcon(source);
+ GuiReaderCoverImager.clearIcon(source);
}
}
});