# Fanfix
+## Version WIP
+- New option (disabled by default) to show one item per source type in GUI instead of one item per story when showing ALL sources (which is also the start page)
+
## Version 1.6.0
- TUI (text with windows and menus) -- not compiled by default (configure.sh)
NON_IMAGES_DOCUMENT_READER, //
@Meta(format = Format.COLOR, description = "The background colour if you don't want the default system one")
BACKGROUND_COLOR, //
+ @Meta(format = Format.BOOLEAN, description = "Show one item per source type when in ALL sources mode instead of one per story")
+ SOURCE_PAGE, //
}
# The background colour if you don't want the default system one
# (FORMAT: COLOR)
BACKGROUND_COLOR = #FFFFFF
+# Show one item per source type when in ALL sources mode instead of one per story
+# (FORMAT: BOOLEAN)
+SOURCE_PAGE = false
# The background colour if you don't want the default system one
# (FORMAT: COLOR)
BACKGROUND_COLOR =
+# Show one item per source type when in ALL sources mode instead of one per story
+# (FORMAT: BOOLEAN)
+SOURCE_PAGE =
*/
public abstract BufferedImage getCover(String luid);
+ /**
+ * Return the cover image associated to this source.
+ * <p>
+ * By default, return the cover of the first story with this source.
+ *
+ * @param source
+ * the source
+ *
+ * @return the cover image or NULL
+ */
+ public BufferedImage getSourceCover(String source) {
+ List<MetaData> metas = getListBySource(source);
+ if (metas.size() > 0) {
+ return getCover(metas.get(0).getLuid());
+ }
+
+ return null;
+ }
+
+ /**
+ * Fix the source cover to the given story cover.
+ *
+ * @param source
+ * the source to change
+ * @param luid
+ * the story LUID
+ */
+ public abstract void setSourceCover(String source, String luid);
+
/**
* Return the list of stories (represented by their {@link MetaData}, which
* <b>MAY</b> not have the cover included).
if (getInfo(luid) != null) {
delete(luid);
}
-
+
doSave(story, pg);
clearCache();
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileFilter;
+import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import javax.imageio.ImageIO;
+
import be.nikiroo.fanfix.Instance;
import be.nikiroo.fanfix.bundles.Config;
import be.nikiroo.fanfix.data.MetaData;
import be.nikiroo.fanfix.output.InfoCover;
import be.nikiroo.fanfix.supported.InfoReader;
import be.nikiroo.utils.IOUtils;
+import be.nikiroo.utils.ImageUtils;
+import be.nikiroo.utils.MarkableFileInputStream;
import be.nikiroo.utils.Progress;
/**
public class LocalLibrary extends BasicLibrary {
private int lastId;
private Map<MetaData, File[]> stories; // Files: [ infoFile, TargetFile ]
+ private Map<String, BufferedImage> sourceCovers;
private File baseDir;
private OutputType text;
this.lastId = 0;
this.stories = null;
+ this.sourceCovers = new HashMap<String, BufferedImage>();
baseDir.mkdirs();
}
@Override
protected void clearCache() {
stories = null;
+ sourceCovers = new HashMap<String, BufferedImage>();
}
@Override
protected synchronized int getNextId() {
+ getStories(null); // make sure lastId is set
return ++lastId;
}
clearCache();
}
+ @Override
+ public BufferedImage getSourceCover(String source) {
+ if (!sourceCovers.containsKey(source)) {
+ sourceCovers.put(source, super.getSourceCover(source));
+ }
+
+ return sourceCovers.get(source);
+ }
+
+ @Override
+ public void setSourceCover(String source, String luid) {
+ sourceCovers.put(source, getCover(luid));
+ File cover = new File(getExpectedDir(source), ".cover.png");
+ try {
+ ImageIO.write(sourceCovers.get(source), "png", cover);
+ } catch (IOException e) {
+ Instance.syserr(e);
+ sourceCovers.remove(source);
+ }
+ }
+
/**
* Return the {@link OutputType} for this {@link Story}.
*
pgDirs.addProgress(pgFiles, 100);
pgDirs.setName("Loading from: " + dir.getName());
+ String source = null;
for (File infoFile : infoFiles) {
pgFiles.setName(infoFile.getName());
try {
MetaData meta = InfoReader.readMeta(infoFile, false);
+ source = meta.getSource();
try {
int id = Integer.parseInt(meta.getLuid());
if (id > lastId) {
pgFiles.add(1);
}
+ File cover = new File(dir, ".cover.png");
+ if (cover.exists()) {
+ try {
+ InputStream in = new MarkableFileInputStream(
+ new FileInputStream(cover));
+ try {
+ sourceCovers.put(source, ImageUtils.fromStream(in));
+ } finally {
+ in.close();
+ }
+ } catch (IOException e) {
+ Instance.syserr(e);
+ }
+ }
+
pgFiles.setName(null);
}
throw new java.lang.InternalError(
"No write support allowed on remote Libraries");
}
+
+ @Override
+ public void setSourceCover(String source, String luid) {
+ throw new java.lang.InternalError(
+ "No write support allowed on remote Libraries");
+ }
}
* @param reader
* the associated reader
* @param meta
- * the story {@link MetaData}
+ * the story {@link MetaData} or source (if no LUID)
* @param cached
* TRUE if it is locally cached
* @param seeWordCount
if (optSecondary != null && !optSecondary.isEmpty()) {
optSecondary = "(" + optSecondary + ")";
+ } else {
+ optSecondary = "";
}
icon = new JLabel(generateCoverIcon(meta));
* Generate a cover icon based upon the given {@link MetaData}.
*
* @param meta
- * the {@link MetaData} about the target {@link Story}
+ * the {@link MetaData} about the target {@link Story} or source
+ * (if no LUID)
*
* @return the icon
*/
private ImageIcon generateCoverIcon(MetaData meta) {
- String id = meta.getUuid() + ".thumb_" + SPINE_WIDTH + "x"
- + COVER_WIDTH + "+" + SPINE_HEIGHT + "+" + COVER_HEIGHT + "@"
- + HOFFSET;
BufferedImage resizedImage = null;
-
- InputStream in = Instance.getCache().getFromCache(id);
- if (in != null) {
- try {
- resizedImage = ImageUtils.fromStream(in);
- in.close();
- in = null;
- } catch (IOException e) {
- Instance.syserr(e);
+ String id = null;
+
+ if (meta.getLuid() != null) {
+ id = meta.getUuid() + ".thumb_" + SPINE_WIDTH + "x" + COVER_WIDTH
+ + "+" + SPINE_HEIGHT + "+" + COVER_HEIGHT + "@" + HOFFSET;
+ InputStream in = Instance.getCache().getFromCache(id);
+ if (in != null) {
+ try {
+ resizedImage = ImageUtils.fromStream(in);
+ in.close();
+ in = null;
+ } catch (IOException e) {
+ Instance.syserr(e);
+ }
}
}
if (resizedImage == null) {
try {
- BufferedImage cover = reader.getLibrary().getCover(
- meta.getLuid());
+ BufferedImage cover = null;
+ if (meta.getLuid() == null) {
+ cover = reader.getLibrary()
+ .getSourceCover(meta.getSource());
+ } else {
+ cover = reader.getLibrary().getCover(meta.getLuid());
+ }
resizedImage = new BufferedImage(SPINE_WIDTH + COVER_WIDTH,
SPINE_HEIGHT + COVER_HEIGHT + HOFFSET,
}
g.dispose();
- 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;
+ if (id != null) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ImageIO.write(resizedImage, "png", out);
+ byte[] imageBytes = out.toByteArray();
+ InputStream in = new ByteArrayInputStream(imageBytes);
+ Instance.getCache().addToCache(in, id);
+ in.close();
+ in = null;
+ }
} catch (MalformedURLException e) {
Instance.syserr(e);
} catch (IOException e) {
setVisible(true);
}
+ private void addSourcePanes() {
+ // Sources -> i18n
+ GuiReaderGroup bookPane = new GuiReaderGroup(reader, "Sources", color);
+
+ List<MetaData> sources = new ArrayList<MetaData>();
+ for (String source : reader.getLibrary().getSources()) {
+ MetaData mSource = new MetaData();
+ mSource.setLuid(null);
+ mSource.setTitle(source);
+ mSource.setSource(source);
+ sources.add(mSource);
+ }
+
+ bookPane.refreshBooks(sources, false);
+
+ this.invalidate();
+ pane.invalidate();
+ pane.add(bookPane);
+ pane.validate();
+ this.validate();
+
+ bookPane.setActionListener(new BookActionListener() {
+ @Override
+ public void select(GuiReaderBook book) {
+ selectedBook = book;
+ }
+
+ @Override
+ public void popupRequested(GuiReaderBook book, MouseEvent e) {
+ JPopupMenu popup = new JPopupMenu();
+ popup.add(createMenuItemOpenBook());
+ popup.show(e.getComponent(), e.getX(), e.getY());
+ }
+
+ @Override
+ public void action(final GuiReaderBook book) {
+ removeBookPanes();
+ addBookPane(book.getMeta().getSource(), true);
+ refreshBooks();
+ }
+ });
+ }
+
/**
* Add a new {@link GuiReaderGroup} on the frame to display the books of the
* selected type or author.
private void addBookPane(String value, boolean type) {
if (value == null) {
if (type) {
- for (String tt : reader.getLibrary().getSources()) {
- if (tt != null) {
- addBookPane(tt, type);
+ if (Instance.getUiConfig().getBoolean(UiConfig.SOURCE_PAGE,
+ false)) {
+ addSourcePanes();
+ } else {
+ for (String tt : reader.getLibrary().getSources()) {
+ if (tt != null) {
+ addBookPane(tt, type);
+ }
}
}
} else {
popup.addSeparator();
popup.add(createMenuItemExport());
popup.add(createMenuItemMove());
+ popup.add(createMenuItemSetCover());
popup.add(createMenuItemClearCache());
popup.add(createMenuItemRedownload());
popup.addSeparator();
"Moving story",
JOptionPane.QUESTION_MESSAGE, null, null,
selectedBook.getMeta().getSource());
-
+
if (rep == null) {
return;
}
}
/**
- * Create the open menu item.
+ * Create the open menu item for a book or a source (no LUID).
*
* @return the item
*/
@Override
public void actionPerformed(ActionEvent e) {
if (selectedBook != null) {
- openBook(selectedBook);
+ if (selectedBook.getMeta().getLuid() == null) {
+ removeBookPanes();
+ addBookPane(selectedBook.getMeta().getSource(), true);
+ refreshBooks();
+ } else {
+ openBook(selectedBook);
+ }
+ }
+ }
+ });
+
+ return open;
+ }
+
+ /**
+ * Create the SetCover menu item for a book to change the linked source
+ * cover.
+ *
+ * @return the item
+ */
+ private JMenuItem createMenuItemSetCover() {
+ JMenuItem open = new JMenuItem("Set as cover for source", KeyEvent.VK_C);
+ open.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (selectedBook != null) {
+ reader.getLibrary().setSourceCover(
+ selectedBook.getMeta().getSource(),
+ selectedBook.getMeta().getLuid());
}
}
});