From c8d48938ca540d7b619a2c19bd76623d689b72cb Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Sun, 17 Mar 2019 19:05:07 +0100 Subject: [PATCH] gui: change name, author --- changelog-fr.md | 1 + changelog.md | 1 + .../nikiroo/fanfix/library/BasicLibrary.java | 88 ++++++++- .../nikiroo/fanfix/library/CacheLibrary.java | 16 +- .../nikiroo/fanfix/library/LocalLibrary.java | 12 +- .../nikiroo/fanfix/library/RemoteLibrary.java | 11 +- .../fanfix/library/RemoteLibraryServer.java | 6 +- .../nikiroo/fanfix/reader/ui/GuiReader.java | 34 ++++ .../fanfix/reader/ui/GuiReaderFrame.java | 187 +++++++++++++----- 9 files changed, 279 insertions(+), 77 deletions(-) diff --git a/changelog-fr.md b/changelog-fr.md index 27f0348e..cdc426f1 100644 --- a/changelog-fr.md +++ b/changelog-fr.md @@ -3,6 +3,7 @@ # Version WIP - gui: new Properties page for stories +- gui: rename stories, change author - tui: now working well enough to be considered stable - remote: fix setSourceCover (was not seen by client) - remote: can now import local files into a remote library diff --git a/changelog.md b/changelog.md index b2577477..954a1825 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,7 @@ # Version WIP - gui: nouvelle page pour voir les propriétés d'une histoire +- gui: renommer les histoires, changer l'auteur - tui: fonctionne maintenant assez bien que pour être déclaré stable - remote: fix de setSourceCover (ce n'était pas vu par le client) - remote: on peut maintenant importer un fichier local diff --git a/src/be/nikiroo/fanfix/library/BasicLibrary.java b/src/be/nikiroo/fanfix/library/BasicLibrary.java index f6daed62..5898ac49 100644 --- a/src/be/nikiroo/fanfix/library/BasicLibrary.java +++ b/src/be/nikiroo/fanfix/library/BasicLibrary.java @@ -158,8 +158,8 @@ abstract public class BasicLibrary { * Invalidate the {@link Story} cache (when the content should be re-read * because it was changed). */ - protected void deleteInfo() { - deleteInfo(null); + protected void invalidateInfo() { + invalidateInfo(null); } /** @@ -171,7 +171,7 @@ abstract public class BasicLibrary { * the LUID of the {@link Story} to clear from the cache, or NULL * for all stories */ - protected abstract void deleteInfo(String luid); + protected abstract void invalidateInfo(String luid); /** * Invalidate the {@link Story} cache (when the content has changed, but we @@ -705,7 +705,7 @@ abstract public class BasicLibrary { this.getClass().getSimpleName() + ": deleting story " + luid); doDelete(luid); - deleteInfo(luid); + invalidateInfo(luid); Instance.getTraceHandler().trace( this.getClass().getSimpleName() + ": story deleted (" + luid @@ -732,7 +732,83 @@ abstract public class BasicLibrary { throw new IOException("Story not found: " + luid); } + changeSTA(luid, newSource, meta.getTitle(), meta.getAuthor(), pg); + } + + /** + * Change the title (name) of the given {@link Story}. + * + * @param luid + * the {@link Story} LUID + * @param newTitle + * the new title + * @param pg + * the optional progress reporter + * + * @throws IOException + * in case of I/O error or if the {@link Story} was not found + */ + public synchronized void changeTitle(String luid, String newTitle, + Progress pg) throws IOException { + MetaData meta = getInfo(luid); + if (meta == null) { + throw new IOException("Story not found: " + luid); + } + + changeSTA(luid, meta.getSource(), newTitle, meta.getAuthor(), pg); + } + + /** + * Change the author of the given {@link Story}. + * + * @param luid + * the {@link Story} LUID + * @param newAuthor + * the new author + * @param pg + * the optional progress reporter + * + * @throws IOException + * in case of I/O error or if the {@link Story} was not found + */ + public synchronized void changeAuthor(String luid, String newAuthor, + Progress pg) throws IOException { + MetaData meta = getInfo(luid); + if (meta == null) { + throw new IOException("Story not found: " + luid); + } + + changeSTA(luid, meta.getSource(), meta.getTitle(), newAuthor, pg); + } + + /** + * Change the Source, Title and Author of the {@link Story} in one single + * go. + * + * @param luid + * the {@link Story} LUID + * @param newSource + * the new source + * @param newTitle + * the new title + * @param newAuthor + * the new author + * @param pg + * the optional progress reporter + * + * @throws IOException + * in case of I/O error or if the {@link Story} was not found + */ + protected synchronized void changeSTA(String luid, String newSource, + String newTitle, String newAuthor, Progress pg) throws IOException { + MetaData meta = getInfo(luid); + if (meta == null) { + throw new IOException("Story not found: " + luid); + } + meta.setSource(newSource); + meta.setTitle(newTitle); + meta.setAuthor(newAuthor); saveMeta(meta, pg); } @@ -743,7 +819,7 @@ abstract public class BasicLibrary { * By default, delete the old {@link Story} then recreate a new * {@link Story}. *

- * Note that this behaviour can lead to data loss. + * Note that this behaviour can lead to data loss in case of problems! * * @param meta * the new {@link MetaData} (LUID MUST NOT change) @@ -769,8 +845,8 @@ abstract public class BasicLibrary { throw new IOException("Story not found: " + meta.getLuid()); } + // TODO: this is not safe! delete(meta.getLuid()); - story.setMeta(meta); save(story, meta.getLuid(), pgSet); diff --git a/src/be/nikiroo/fanfix/library/CacheLibrary.java b/src/be/nikiroo/fanfix/library/CacheLibrary.java index a550740b..918b763f 100644 --- a/src/be/nikiroo/fanfix/library/CacheLibrary.java +++ b/src/be/nikiroo/fanfix/library/CacheLibrary.java @@ -159,7 +159,7 @@ public class CacheLibrary extends BasicLibrary { } @Override - protected void deleteInfo(String luid) { + protected void invalidateInfo(String luid) { if (luid == null) { metas = null; } else if (metas != null) { @@ -170,8 +170,8 @@ public class CacheLibrary extends BasicLibrary { } } - cacheLib.deleteInfo(luid); - lib.deleteInfo(luid); + cacheLib.invalidateInfo(luid); + lib.invalidateInfo(luid); } @Override @@ -210,8 +210,8 @@ public class CacheLibrary extends BasicLibrary { } @Override - public synchronized void changeSource(String luid, String newSource, - Progress pg) throws IOException { + protected synchronized void changeSTA(String luid, String newSource, + String newTitle, String newAuthor, Progress pg) throws IOException { if (pg == null) { pg = new Progress(); } @@ -228,14 +228,16 @@ public class CacheLibrary extends BasicLibrary { } if (isCached(luid)) { - cacheLib.changeSource(luid, newSource, pgCache); + cacheLib.changeSTA(luid, newSource, newTitle, newAuthor, pgCache); } pgCache.done(); - lib.changeSource(luid, newSource, pgOrig); + lib.changeSTA(luid, newSource, newTitle, newAuthor, pgOrig); pgOrig.done(); meta.setSource(newSource); + meta.setTitle(newTitle); + meta.setAuthor(newAuthor); pg.done(); } diff --git a/src/be/nikiroo/fanfix/library/LocalLibrary.java b/src/be/nikiroo/fanfix/library/LocalLibrary.java index eafd18a6..9ebff72c 100644 --- a/src/be/nikiroo/fanfix/library/LocalLibrary.java +++ b/src/be/nikiroo/fanfix/library/LocalLibrary.java @@ -145,11 +145,11 @@ public class LocalLibrary extends BasicLibrary { @Override protected synchronized void updateInfo(MetaData meta) { - deleteInfo(); + invalidateInfo(); } @Override - protected void deleteInfo(String luid) { + protected void invalidateInfo(String luid) { stories = null; sourceCovers = null; } @@ -199,8 +199,8 @@ public class LocalLibrary extends BasicLibrary { try { String name = relatedFile.getName().replaceFirst( "\\.info$", ""); - InfoCover.writeInfo(newDir, name, meta); relatedFile.delete(); + InfoCover.writeInfo(newDir, name, meta); relatedFile.getParentFile().delete(); } catch (IOException e) { Instance.getTraceHandler().error(e); @@ -211,7 +211,7 @@ public class LocalLibrary extends BasicLibrary { } } - deleteInfo(); + invalidateInfo(); } @Override @@ -325,7 +325,7 @@ public class LocalLibrary extends BasicLibrary { pg.add(1); } - deleteInfo(); + invalidateInfo(); pg.done(); return; } @@ -466,7 +466,7 @@ public class LocalLibrary extends BasicLibrary { * {@link LocalLibrary#baseDir}. *

* Will use a cached list when possible (see - * {@link BasicLibrary#deleteInfo()}). + * {@link BasicLibrary#invalidateInfo()}). * * @param pg * the optional {@link Progress} diff --git a/src/be/nikiroo/fanfix/library/RemoteLibrary.java b/src/be/nikiroo/fanfix/library/RemoteLibrary.java index 75f98be8..c01275b6 100644 --- a/src/be/nikiroo/fanfix/library/RemoteLibrary.java +++ b/src/be/nikiroo/fanfix/library/RemoteLibrary.java @@ -348,8 +348,9 @@ public class RemoteLibrary extends BasicLibrary { @Override // Could work (more slowly) without it - public synchronized void changeSource(final String luid, - final String newSource, Progress pg) throws IOException { + protected synchronized void changeSTA(final String luid, + final String newSource, final String newTitle, + final String newAuthor, Progress pg) throws IOException { final Progress pgF = pg == null ? new Progress() : pg; try { @@ -358,8 +359,8 @@ public class RemoteLibrary extends BasicLibrary { public void action(Version serverVersion) throws Exception { Progress pg = pgF; - Object rep = send(new Object[] { md5, "CHANGE_SOURCE", - luid, newSource }); + Object rep = send(new Object[] { md5, "CHANGE_STA", luid, + newSource, newTitle, newAuthor }); while (true) { if (!RemoteLibraryServer.updateProgress(pg, rep)) { break; @@ -427,7 +428,7 @@ public class RemoteLibrary extends BasicLibrary { } @Override - protected void deleteInfo(String luid) { + protected void invalidateInfo(String luid) { // Will be taken care of directly server side } diff --git a/src/be/nikiroo/fanfix/library/RemoteLibraryServer.java b/src/be/nikiroo/fanfix/library/RemoteLibraryServer.java index 8d24c9f6..aa5f3056 100644 --- a/src/be/nikiroo/fanfix/library/RemoteLibraryServer.java +++ b/src/be/nikiroo/fanfix/library/RemoteLibraryServer.java @@ -185,10 +185,10 @@ public class RemoteLibraryServer extends ServerObject { } else if ("SET_SOURCE_COVER".equals(command)) { Instance.getLibrary().setSourceCover((String) args[0], (String) args[1]); - } else if ("CHANGE_SOURCE".equals(command)) { + } else if ("CHANGE_STA".equals(command)) { Progress pg = createPgForwarder(action); - Instance.getLibrary().changeSource((String) args[0], - (String) args[1], pg); + Instance.getLibrary().changeSTA((String) args[0], (String) args[1], + (String) args[2], (String) args[3], pg); forcePgDoneSent(pg); } else if ("EXIT".equals(command)) { stop(0, false); diff --git a/src/be/nikiroo/fanfix/reader/ui/GuiReader.java b/src/be/nikiroo/fanfix/reader/ui/GuiReader.java index 02f153cd..7dc373c7 100644 --- a/src/be/nikiroo/fanfix/reader/ui/GuiReader.java +++ b/src/be/nikiroo/fanfix/reader/ui/GuiReader.java @@ -292,4 +292,38 @@ class GuiReader extends BasicReader { Instance.getTraceHandler().error(e); } } + + /** + * Change the title of the given {@link Story}. + * + * @param luid + * the luid of the {@link Story} to change + * @param newTitle + * the new title + */ + void changeTitle(String luid, String newTitle) { + try { + cacheLib.changeTitle(luid, newTitle, null); + } catch (IOException e) { + Instance.getTraceHandler().error(e); + } + } + + /** + * Change the author of the given {@link Story}. + *

+ * The author can be a new one, it needs not exist before hand. + * + * @param luid + * the luid of the {@link Story} to change + * @param newAuthor + * the new author + */ + void changeAuthor(String luid, String newAuthor) { + try { + cacheLib.changeAuthor(luid, newAuthor, null); + } catch (IOException e) { + Instance.getTraceHandler().error(e); + } + } } diff --git a/src/be/nikiroo/fanfix/reader/ui/GuiReaderFrame.java b/src/be/nikiroo/fanfix/reader/ui/GuiReaderFrame.java index d849a438..f4c5eb39 100644 --- a/src/be/nikiroo/fanfix/reader/ui/GuiReaderFrame.java +++ b/src/be/nikiroo/fanfix/reader/ui/GuiReaderFrame.java @@ -307,11 +307,14 @@ class GuiReaderFrame extends JFrame { popup.add(createMenuItemOpenBook()); popup.addSeparator(); popup.add(createMenuItemExport()); - popup.add(createMenuItemMove(true)); + popup.add(createMenuItemMoveTo(true)); popup.add(createMenuItemSetCover()); popup.add(createMenuItemClearCache()); popup.add(createMenuItemRedownload()); popup.addSeparator(); + popup.add(createMenuItemRename(true)); + popup.add(createMenuItemSetAuthor(true)); + popup.addSeparator(); popup.add(createMenuItemDelete()); popup.addSeparator(); popup.add(createMenuItemProperties()); @@ -394,11 +397,14 @@ class GuiReaderFrame extends JFrame { file.add(createMenuItemOpenBook()); file.add(createMenuItemExport()); - file.add(createMenuItemMove(libOk)); + file.add(createMenuItemMoveTo(libOk)); file.addSeparator(); file.add(imprt); file.add(imprtF); file.addSeparator(); + file.add(createMenuItemRename(libOk)); + file.add(createMenuItemSetAuthor(libOk)); + file.addSeparator(); file.add(exit); bar.add(file); @@ -711,74 +717,155 @@ class GuiReaderFrame extends JFrame { } /** - * Create the delete menu item. + * Create the "move to" menu item. * * @param libOk * the library can be queried * * @return the item */ - private JMenuItem createMenuItemMove(boolean libOk) { - JMenu moveTo = new JMenu("Move to..."); - moveTo.setMnemonic(KeyEvent.VK_M); + private JMenuItem createMenuItemMoveTo(boolean libOk) { + JMenuItem changeTo = new JMenu("Move to"); + changeTo.setMnemonic(KeyEvent.VK_M); - List types = new ArrayList(); - types.add(null); + List values = new ArrayList(); + values.add(null); if (libOk) { - types.addAll(reader.getLibrary().getSources()); + values.addAll(reader.getLibrary().getSources()); } - for (String type : types) { - JMenuItem item = new JMenuItem(type == null ? "New type..." : type); + for (String value : values) { + JMenuItem item = new JMenuItem(value == null ? "New type..." + : value); - moveTo.add(item); - if (type == null) { - moveTo.addSeparator(); - } + item.addActionListener(createMoveAction("SOURCE", value)); - final String ftype = type; - item.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (selectedBook != null) { - String type = ftype; - if (type == null) { - Object rep = JOptionPane.showInputDialog( - GuiReaderFrame.this, "Move to:", - "Moving story", - JOptionPane.QUESTION_MESSAGE, null, null, - selectedBook.getMeta().getSource()); - - if (rep == null) { - return; - } + changeTo.add(item); + if (value == null) { + ((JMenu) changeTo).addSeparator(); + } + } - type = rep.toString(); - } + return changeTo; + } - final String ftype = type; - outOfUi(null, new Runnable() { - @Override - public void run() { - reader.changeSource(selectedBook.getMeta() - .getLuid(), ftype); + /** + * Create the "set author" menu item. + * + * @param libOk + * the library can be queried + * + * @return the item + */ + private JMenuItem createMenuItemSetAuthor(boolean libOk) { + JMenu changeTo = new JMenu("Set author"); + changeTo.setMnemonic(KeyEvent.VK_A); - selectedBook = null; + // New author + JMenuItem newItem = new JMenuItem("New author..."); + changeTo.add(newItem); + changeTo.addSeparator(); + newItem.addActionListener(createMoveAction("AUTHOR", null)); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - setJMenuBar(createMenu(true)); - } - }); - } - }); + // Existing authors + if (libOk) { + List>> authorGroups = reader + .getLibrary().getAuthorsGrouped(); + + if (authorGroups.size() > 1) { + for (Entry> entry : authorGroups) { + JMenu group = new JMenu(entry.getKey()); + for (String value : entry.getValue()) { + JMenuItem item = new JMenuItem(value); + item.addActionListener(createMoveAction("AUTHOR", value)); + group.add(item); } + changeTo.add(group); } - }); + } else if (authorGroups.size() == 1) { + for (String value : authorGroups.get(0).getValue()) { + JMenuItem item = new JMenuItem(value); + item.addActionListener(createMoveAction("AUTHOR", value)); + changeTo.add(item); + } + } } - return moveTo; + return changeTo; + } + + /** + * Create the "rename" menu item. + * + * @param libOk + * the library can be queried + * + * @return the item + */ + private JMenuItem createMenuItemRename( + @SuppressWarnings("unused") boolean libOk) { + JMenuItem changeTo = new JMenuItem("Rename..."); + changeTo.setMnemonic(KeyEvent.VK_R); + changeTo.addActionListener(createMoveAction("TITLE", null)); + return changeTo; + } + + private ActionListener createMoveAction(final String what, final String type) { + return new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (selectedBook != null) { + String changeTo = type; + if (type == null) { + String init = ""; + if (what.equals("SOURCE")) { + init = selectedBook.getMeta().getSource(); + } else if (what.equals("TITLE")) { + init = selectedBook.getMeta().getTitle(); + } else if (what.equals("AUTHOR")) { + init = selectedBook.getMeta().getAuthor(); + } + + Object rep = JOptionPane.showInputDialog( + GuiReaderFrame.this, "Move to:", + "Moving story", JOptionPane.QUESTION_MESSAGE, + null, null, init); + + if (rep == null) { + return; + } + + changeTo = rep.toString(); + } + + final String fChangeTo = changeTo; + outOfUi(null, new Runnable() { + @Override + public void run() { + if (what.equals("SOURCE")) { + reader.changeSource(selectedBook.getMeta() + .getLuid(), fChangeTo); + } else if (what.equals("TITLE")) { + reader.changeTitle(selectedBook.getMeta() + .getLuid(), fChangeTo); + } else if (what.equals("AUTHOR")) { + reader.changeAuthor(selectedBook.getMeta() + .getLuid(), fChangeTo); + } + + selectedBook = null; + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + setJMenuBar(createMenu(true)); + } + }); + } + }); + } + } + }; } /** -- 2.27.0