From 9c5e71ee09d03224af90e486a49f3cac0d86b981 Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Sun, 19 Apr 2020 00:39:32 +0200 Subject: [PATCH] properties dialog --- .../fanfix_swing/gui/PropertiesFrame.java | 40 ++++ .../fanfix_swing/gui/PropertiesPane.java | 102 ++++++++++ .../fanfix_swing/gui/book/BookBlock.java | 11 +- .../gui/book/BookCoverImager.java | 155 +-------------- .../fanfix_swing/gui/book/BookLine.java | 3 +- .../fanfix_swing/gui/book/BookPopup.java | 21 +- .../gui/importer/ImporterItem.java | 25 +-- .../fanfix_swing/gui/utils/CoverImager.java | 182 ++++++++++++++++++ 8 files changed, 349 insertions(+), 190 deletions(-) create mode 100644 src/be/nikiroo/fanfix_swing/gui/PropertiesFrame.java create mode 100644 src/be/nikiroo/fanfix_swing/gui/PropertiesPane.java create mode 100644 src/be/nikiroo/fanfix_swing/gui/utils/CoverImager.java diff --git a/src/be/nikiroo/fanfix_swing/gui/PropertiesFrame.java b/src/be/nikiroo/fanfix_swing/gui/PropertiesFrame.java new file mode 100644 index 00000000..db0654bf --- /dev/null +++ b/src/be/nikiroo/fanfix_swing/gui/PropertiesFrame.java @@ -0,0 +1,40 @@ +package be.nikiroo.fanfix_swing.gui; + +import java.awt.BorderLayout; + +import javax.swing.JFrame; + +import be.nikiroo.fanfix.Instance; +import be.nikiroo.fanfix.bundles.StringIdGui; +import be.nikiroo.fanfix.data.MetaData; +import be.nikiroo.fanfix.data.Story; +import be.nikiroo.fanfix.library.BasicLibrary; + +/** + * A frame displaying properties and other information of a {@link Story}. + * + * @author niki + */ +public class PropertiesFrame extends JFrame { + private static final long serialVersionUID = 1L; + + /** + * Create a new {@link PropertiesFrame}. + * + * @param lib + * the library to use for the cover image + * @param meta + * the meta to describe + */ + public PropertiesFrame(BasicLibrary lib, MetaData meta) { + setTitle(Instance.getInstance().getTransGui().getString( + StringIdGui.TITLE_STORY, meta.getLuid(), meta.getTitle())); + + PropertiesPane desc = new PropertiesPane(lib, meta); + setSize(800, (int) desc.getPreferredSize().getHeight() + + 2 * desc.getBorderThickness()); + + setLayout(new BorderLayout()); + add(desc, BorderLayout.NORTH); + } +} diff --git a/src/be/nikiroo/fanfix_swing/gui/PropertiesPane.java b/src/be/nikiroo/fanfix_swing/gui/PropertiesPane.java new file mode 100644 index 00000000..b701fe6c --- /dev/null +++ b/src/be/nikiroo/fanfix_swing/gui/PropertiesPane.java @@ -0,0 +1,102 @@ +package be.nikiroo.fanfix_swing.gui; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Font; +import java.util.Map; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextArea; + +import be.nikiroo.fanfix.data.MetaData; +import be.nikiroo.fanfix.data.Story; +import be.nikiroo.fanfix.library.BasicLibrary; +import be.nikiroo.fanfix.reader.BasicReader; +import be.nikiroo.fanfix_swing.gui.book.BookInfo; +import be.nikiroo.fanfix_swing.gui.utils.CoverImager; + +/** + * A panel displaying properties and other information of a {@link Story}. + * + * @author niki + */ +public class PropertiesPane extends JPanel { + private static final long serialVersionUID = 1L; + private final int space = 10; + + /** + * Create a new {@link PropertiesPane}. + * + * @param lib + * the library to use for the cover image + * @param meta + * the meta to describe + */ + public PropertiesPane(BasicLibrary lib, MetaData meta) { + // Image + ImageIcon img = new ImageIcon(CoverImager.generateCoverImage(lib, + BookInfo.fromMeta(lib, meta))); + + 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); + + Map desc = BasicReader.getMetaDesc(meta); + + Color trans = new Color(0, 0, 0, 1); + Color base = mainPanelValues.getBackground(); + for (String key : desc.keySet()) { + JTextArea jKey = new JTextArea(key); + jKey.setFont(new Font(jKey.getFont().getFontName(), Font.BOLD, + jKey.getFont().getSize())); + jKey.setEditable(false); + jKey.setLineWrap(false); + jKey.setBackground(trans); + mainPanelKeys.add(jKey); + + final JTextArea jValue = new JTextArea(desc.get(key)); + jValue.setEditable(false); + jValue.setLineWrap(false); + jValue.setBackground(base); + mainPanelValues.add(jValue); + } + + // Image + JLabel imgLabel = new JLabel(img); + imgLabel.setVerticalAlignment(JLabel.TOP); + + // Borders + mainPanelKeys.setBorder( + BorderFactory.createEmptyBorder(space, space, space, space)); + mainPanelValues.setBorder( + BorderFactory.createEmptyBorder(space, space, space, space)); + imgLabel.setBorder(BorderFactory.createEmptyBorder(0, space, space, 0)); + + // Add all + add(imgLabel, BorderLayout.WEST); + add(mainPanel, BorderLayout.CENTER); + } + + /** + * The invisible border size (multiply by 2 if you need the total width or + * the total height). + * + * @return the invisible border thickness + */ + public int getBorderThickness() { + return space; + } +} diff --git a/src/be/nikiroo/fanfix_swing/gui/book/BookBlock.java b/src/be/nikiroo/fanfix_swing/gui/book/BookBlock.java index 329c714e..183bd576 100644 --- a/src/be/nikiroo/fanfix_swing/gui/book/BookBlock.java +++ b/src/be/nikiroo/fanfix_swing/gui/book/BookBlock.java @@ -11,6 +11,7 @@ import javax.swing.JPanel; import be.nikiroo.fanfix.data.Story; import be.nikiroo.fanfix.library.BasicLibrary; import be.nikiroo.fanfix_swing.gui.BooksPanel; +import be.nikiroo.fanfix_swing.gui.utils.CoverImager; /** * A book item presented in a {@link BooksPanel}. @@ -21,7 +22,7 @@ import be.nikiroo.fanfix_swing.gui.BooksPanel; */ public class BookBlock extends BookLine { static private final long serialVersionUID = 1L; - static private Image empty = BookCoverImager.generateCoverImage(null, + static private Image empty = CoverImager.generateCoverImage(null, (BookInfo) null); private JLabel title; @@ -46,8 +47,8 @@ public class BookBlock extends BookLine { updateMeta(); JPanel filler = new JPanel(); - filler.setPreferredSize(new Dimension(BookCoverImager.getCoverWidth(), - BookCoverImager.getCoverHeight())); + filler.setPreferredSize(new Dimension(CoverImager.getCoverWidth(), + CoverImager.getCoverHeight())); filler.setOpaque(false); setLayout(new BorderLayout(10, 10)); @@ -88,7 +89,7 @@ public class BookBlock extends BookLine { + "" + "", BookCoverImager.TEXT_WIDTH, BookCoverImager.TEXT_HEIGHT, main, color, optSecondary)); - setBackground(BookCoverImager.getBackground(isEnabled(), isSelected(), + setBackground(CoverImager.getBackground(isEnabled(), isSelected(), isHovered())); } @@ -104,6 +105,6 @@ public class BookBlock extends BookLine { */ static public java.awt.Image generateCoverImage(BasicLibrary lib, BookInfo info) { - return BookCoverImager.generateCoverImage(lib, info); + return CoverImager.generateCoverImage(lib, info); } } diff --git a/src/be/nikiroo/fanfix_swing/gui/book/BookCoverImager.java b/src/be/nikiroo/fanfix_swing/gui/book/BookCoverImager.java index 2b0795f6..ca166d57 100644 --- a/src/be/nikiroo/fanfix_swing/gui/book/BookCoverImager.java +++ b/src/be/nikiroo/fanfix_swing/gui/book/BookCoverImager.java @@ -2,22 +2,10 @@ package be.nikiroo.fanfix_swing.gui.book; import java.awt.Color; import java.awt.Graphics; -import java.awt.Graphics2D; import java.awt.Polygon; import java.awt.Rectangle; -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 javax.imageio.ImageIO; - -import be.nikiroo.fanfix.Instance; -import be.nikiroo.fanfix.library.BasicLibrary; -import be.nikiroo.utils.Image; -import be.nikiroo.utils.ui.ImageUtilsAwt; +import be.nikiroo.fanfix_swing.gui.utils.CoverImager; import be.nikiroo.utils.ui.UIUtils; /** @@ -43,23 +31,6 @@ class BookCoverImager { public static final int TEXT_HEIGHT = 50; public static final int TEXT_WIDTH = COVER_WIDTH + 40; - // - - static public Color getBackground(boolean enabled, boolean selected, - boolean hovered) { - Color color = new Color(255, 255, 255, 0); - if (!enabled) { - } 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); - } - - return color; - } - /** * Draw a partially transparent overlay if needed depending upon the * selection and mouse-hover states on top of the normal component, as well @@ -111,7 +82,7 @@ class BookCoverImager { g.setColor(SPINE_COLOR_RIGHT); g.fillPolygon(new Polygon(xs, ys, xs.length)); - Color color = getBackground(enabled, selected, hovered); + Color color = CoverImager.getBackground(enabled, selected, hovered); g.setColor(color); g.fillRect(clip.x, clip.y, clip.width, clip.height); @@ -119,126 +90,4 @@ class BookCoverImager { UIUtils.drawEllipse3D(g, UNCACHED_ICON_COLOR, COVER_WIDTH + HOFFSET + 30, 10, 20, 20, cached); } - - /** - * The width of a cover image. - * - * @return the width - */ - static public int getCoverWidth() { - return SPINE_WIDTH + COVER_WIDTH; - } - - /** - * The height of a cover image. - * - * @return the height - */ - static public int getCoverHeight() { - return COVER_HEIGHT + HOFFSET; - } - - /** - * Generate a cover icon based upon the given {@link GuiReaderBookInfo}. - * - * @param lib - * the library the meta comes from (can be NULL) - * @param info - * the {@link GuiReaderBookInfo} - * - * @return the image - */ - static public java.awt.Image generateCoverImage(BasicLibrary lib, - BookInfo info) { - BufferedImage resizedImage = null; - String id = getIconId(info); - - InputStream in = Instance.getInstance().getCache().getFromCache(id); - if (in != null) { - try { - resizedImage = ImageUtilsAwt.fromImage(new Image(in)); - in.close(); - in = null; - } catch (IOException e) { - Instance.getInstance().getTraceHandler().error(e); - } - } - - if (resizedImage == null) { - try { - Image cover = null; - if (info != null) { - cover = info.getBaseImage(lib); - } - - resizedImage = new BufferedImage(getCoverWidth(), - getCoverHeight(), BufferedImage.TYPE_4BYTE_ABGR); - - Graphics2D g = resizedImage.createGraphics(); - try { - if (info != null && info.supportsCover()) { - 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); - } - } - } finally { - g.dispose(); - } - - // Only save image with a cover, not the X thing - if (id != null && cover != null) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ImageIO.write(resizedImage, "png", out); - byte[] imageBytes = out.toByteArray(); - in = new ByteArrayInputStream(imageBytes); - Instance.getInstance().getCache().addToCache(in, id); - in.close(); - in = null; - } - } catch (MalformedURLException e) { - Instance.getInstance().getTraceHandler().error(e); - } catch (IOException e) { - Instance.getInstance().getTraceHandler().error(e); - } - } - - return resizedImage; - } - - /** - * Manually clear the icon set for this item. - * - * @param info - * the info about the story or source/type or author - */ - static public void clearIcon(BookInfo info) { - String id = getIconId(info); - Instance.getInstance().getCache().removeFromCache(id); - } - - /** - * Get a unique ID from this {@link GuiReaderBookInfo} (note that it can be - * a story, a fake item for a source/type or a fake item for an author). - * - * @param info - * the info or NULL for a generic (non unique!) ID - * @return the unique ID - */ - static private String getIconId(BookInfo info) { - return (info == null ? "" : info.getId() + ".") + "book-thumb_" - + SPINE_WIDTH + "x" + COVER_WIDTH + "+" + SPINE_HEIGHT + "+" - + COVER_HEIGHT + "@" + HOFFSET; - } } diff --git a/src/be/nikiroo/fanfix_swing/gui/book/BookLine.java b/src/be/nikiroo/fanfix_swing/gui/book/BookLine.java index 4e2e3f4c..984a5b94 100644 --- a/src/be/nikiroo/fanfix_swing/gui/book/BookLine.java +++ b/src/be/nikiroo/fanfix_swing/gui/book/BookLine.java @@ -10,6 +10,7 @@ import javax.swing.SwingConstants; import be.nikiroo.fanfix.data.Story; import be.nikiroo.fanfix_swing.gui.BooksPanel; +import be.nikiroo.fanfix_swing.gui.utils.CoverImager; /** * A book item presented in a {@link BooksPanel}. @@ -204,7 +205,7 @@ public class BookLine extends JPanel { title.setText(main); secondary.setText(optSecondary + " "); - setBackground(BookCoverImager.getBackground(isEnabled(), isSelected(), + setBackground(CoverImager.getBackground(isEnabled(), isSelected(), isHovered())); remove(iconCached); diff --git a/src/be/nikiroo/fanfix_swing/gui/book/BookPopup.java b/src/be/nikiroo/fanfix_swing/gui/book/BookPopup.java index 991f4419..3d874cc1 100644 --- a/src/be/nikiroo/fanfix_swing/gui/book/BookPopup.java +++ b/src/be/nikiroo/fanfix_swing/gui/book/BookPopup.java @@ -30,6 +30,8 @@ import be.nikiroo.fanfix.library.BasicLibrary; import be.nikiroo.fanfix.library.BasicLibrary.Status; import be.nikiroo.fanfix.output.BasicOutput.OutputType; import be.nikiroo.fanfix_swing.Actions; +import be.nikiroo.fanfix_swing.gui.PropertiesFrame; +import be.nikiroo.fanfix_swing.gui.utils.CoverImager; import be.nikiroo.fanfix_swing.gui.utils.UiHelper; import be.nikiroo.utils.Progress; import be.nikiroo.utils.ui.ConfigEditor; @@ -268,7 +270,7 @@ public class BookPopup extends JPopupMenu { protected Void doInBackground() throws Exception { for (BookInfo book : selected) { lib.clearFromCache(book.getMeta().getLuid()); - BookCoverImager.clearIcon(book); + CoverImager.clearIcon(book); } return null; } @@ -685,18 +687,11 @@ public class BookPopup extends JPopupMenu { delete.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - // final GuiReaderBook selectedBook = - // mainPanel.getSelectedBook(); - // if (selectedBook != null) { - // mainPanel.outOfUi(null, false, new Runnable() { - // @Override - // public void run() { - // new GuiReaderPropertiesFrame(lib, - // selectedBook.getInfo().getMeta()) - // .setVisible(true); - // } - // }); - // } + BookInfo selected = informer.getUniqueSelected(); + if (selected != null) { + new PropertiesFrame(lib, selected.getMeta()) + .setVisible(true); + } } }); diff --git a/src/be/nikiroo/fanfix_swing/gui/importer/ImporterItem.java b/src/be/nikiroo/fanfix_swing/gui/importer/ImporterItem.java index 2e88c638..11234631 100644 --- a/src/be/nikiroo/fanfix_swing/gui/importer/ImporterItem.java +++ b/src/be/nikiroo/fanfix_swing/gui/importer/ImporterItem.java @@ -9,6 +9,7 @@ import java.awt.Rectangle; import javax.swing.JLabel; import javax.swing.SwingUtilities; +import be.nikiroo.fanfix_swing.gui.utils.CoverImager; import be.nikiroo.fanfix_swing.gui.utils.ListenerPanel; import be.nikiroo.utils.Progress; import be.nikiroo.utils.Progress.ProgressListener; @@ -44,21 +45,6 @@ public class ImporterItem extends ListenerPanel { init(pg); } - static public Color getBackground(boolean enabled, boolean selected, - boolean hovered) { - Color color = new Color(255, 255, 255, 0); - if (!enabled) { - } 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); - } - - return color; - } - public String getStoryName() { return basename + ": " + storyName; } @@ -79,7 +65,8 @@ public class ImporterItem extends ListenerPanel { public void setSelected(boolean selected) { if (this.selected != selected) { this.selected = selected; - setBackground(getBackground(isEnabled(), selected, hovered)); + setBackground( + CoverImager.getBackground(isEnabled(), selected, hovered)); } } @@ -90,7 +77,8 @@ public class ImporterItem extends ListenerPanel { public void setHovered(boolean hovered) { if (this.hovered != hovered) { this.hovered = hovered; - setBackground(getBackground(isEnabled(), selected, hovered)); + setBackground( + CoverImager.getBackground(isEnabled(), selected, hovered)); } } @@ -98,7 +86,8 @@ public class ImporterItem extends ListenerPanel { public void setEnabled(boolean enabled) { if (isEnabled() != enabled) { super.setEnabled(enabled); - setBackground(getBackground(isEnabled(), selected, hovered)); + setBackground( + CoverImager.getBackground(isEnabled(), selected, hovered)); } } diff --git a/src/be/nikiroo/fanfix_swing/gui/utils/CoverImager.java b/src/be/nikiroo/fanfix_swing/gui/utils/CoverImager.java new file mode 100644 index 00000000..93df707a --- /dev/null +++ b/src/be/nikiroo/fanfix_swing/gui/utils/CoverImager.java @@ -0,0 +1,182 @@ +package be.nikiroo.fanfix_swing.gui.utils; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.awt.Rectangle; +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 javax.imageio.ImageIO; + +import be.nikiroo.fanfix.Instance; +import be.nikiroo.fanfix.library.BasicLibrary; +import be.nikiroo.fanfix_swing.gui.book.BookInfo; +import be.nikiroo.utils.Image; +import be.nikiroo.utils.ui.ImageUtilsAwt; +import be.nikiroo.utils.ui.UIUtils; + +/** + * This class can create a cover icon ready to use for the graphical + * application. + * + * @author niki + */ +public class CoverImager { + // TODO: export some of the configuration options? + static final int COVER_WIDTH = 100; + static final int COVER_HEIGHT = 150; + static final int SPINE_WIDTH = 5; + static final int SPINE_HEIGHT = 5; + static final int HOFFSET = 20; + static final Color SPINE_COLOR_BOTTOM = new Color(180, 180, 180); + static final Color SPINE_COLOR_RIGHT = new Color(100, 100, 100); + static final Color BORDER = Color.black; + + public static final int TEXT_HEIGHT = 50; + public static final int TEXT_WIDTH = COVER_WIDTH + 40; + + // + + static public Color getBackground(boolean enabled, boolean selected, + boolean hovered) { + Color color = new Color(255, 255, 255, 0); + if (!enabled) { + } 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); + } + + return color; + } + + /** + * The width of a cover image. + * + * @return the width + */ + static public int getCoverWidth() { + return SPINE_WIDTH + COVER_WIDTH; + } + + /** + * The height of a cover image. + * + * @return the height + */ + static public int getCoverHeight() { + return COVER_HEIGHT + HOFFSET; + } + + /** + * Generate a cover icon based upon the given {@link GuiReaderBookInfo}. + * + * @param lib + * the library the meta comes from (can be NULL) + * @param info + * the {@link GuiReaderBookInfo} + * + * @return the image + */ + static public java.awt.Image generateCoverImage(BasicLibrary lib, + BookInfo info) { + BufferedImage resizedImage = null; + String id = getIconId(info); + + InputStream in = Instance.getInstance().getCache().getFromCache(id); + if (in != null) { + try { + resizedImage = ImageUtilsAwt.fromImage(new Image(in)); + in.close(); + in = null; + } catch (IOException e) { + Instance.getInstance().getTraceHandler().error(e); + } + } + + if (resizedImage == null) { + try { + Image cover = null; + if (info != null) { + cover = info.getBaseImage(lib); + } + + resizedImage = new BufferedImage(getCoverWidth(), + getCoverHeight(), BufferedImage.TYPE_4BYTE_ABGR); + + Graphics2D g = resizedImage.createGraphics(); + try { + if (info != null && info.supportsCover()) { + 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); + } + } + } finally { + g.dispose(); + } + + // Only save image with a cover, not the X thing + if (id != null && cover != null) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ImageIO.write(resizedImage, "png", out); + byte[] imageBytes = out.toByteArray(); + in = new ByteArrayInputStream(imageBytes); + Instance.getInstance().getCache().addToCache(in, id); + in.close(); + in = null; + } + } catch (MalformedURLException e) { + Instance.getInstance().getTraceHandler().error(e); + } catch (IOException e) { + Instance.getInstance().getTraceHandler().error(e); + } + } + + return resizedImage; + } + + /** + * Manually clear the icon set for this item. + * + * @param info + * the info about the story or source/type or author + */ + static public void clearIcon(BookInfo info) { + String id = getIconId(info); + Instance.getInstance().getCache().removeFromCache(id); + } + + /** + * Get a unique ID from this {@link GuiReaderBookInfo} (note that it can be + * a story, a fake item for a source/type or a fake item for an author). + * + * @param info + * the info or NULL for a generic (non unique!) ID + * @return the unique ID + */ + static private String getIconId(BookInfo info) { + return (info == null ? "" : info.getId() + ".") + "book-thumb_" + + SPINE_WIDTH + "x" + COVER_WIDTH + "+" + SPINE_HEIGHT + "+" + + COVER_HEIGHT + "@" + HOFFSET; + } +} -- 2.27.0