# 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
# 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
* 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);
}
/**
* 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
this.getClass().getSimpleName() + ": deleting story " + luid);
doDelete(luid);
- deleteInfo(luid);
+ invalidateInfo(luid);
Instance.getTraceHandler().trace(
this.getClass().getSimpleName() + ": story deleted (" + luid
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);
}
* By default, delete the old {@link Story} then recreate a new
* {@link Story}.
* <p>
- * 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 <b>MUST NOT</b> change)
throw new IOException("Story not found: " + meta.getLuid());
}
+ // TODO: this is not safe!
delete(meta.getLuid());
-
story.setMeta(meta);
save(story, meta.getLuid(), pgSet);
}
@Override
- protected void deleteInfo(String luid) {
+ protected void invalidateInfo(String luid) {
if (luid == null) {
metas = null;
} else if (metas != null) {
}
}
- cacheLib.deleteInfo(luid);
- lib.deleteInfo(luid);
+ cacheLib.invalidateInfo(luid);
+ lib.invalidateInfo(luid);
}
@Override
}
@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();
}
}
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();
}
@Override
protected synchronized void updateInfo(MetaData meta) {
- deleteInfo();
+ invalidateInfo();
}
@Override
- protected void deleteInfo(String luid) {
+ protected void invalidateInfo(String luid) {
stories = null;
sourceCovers = null;
}
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);
}
}
- deleteInfo();
+ invalidateInfo();
}
@Override
pg.add(1);
}
- deleteInfo();
+ invalidateInfo();
pg.done();
return;
}
* {@link LocalLibrary#baseDir}.
* <p>
* Will use a cached list when possible (see
- * {@link BasicLibrary#deleteInfo()}).
+ * {@link BasicLibrary#invalidateInfo()}).
*
* @param pg
* the optional {@link Progress}
@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 {
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;
}
@Override
- protected void deleteInfo(String luid) {
+ protected void invalidateInfo(String luid) {
// Will be taken care of directly server side
}
} 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);
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}.
+ * <p>
+ * 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);
+ }
+ }
}
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());
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);
}
/**
- * 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<String> types = new ArrayList<String>();
- types.add(null);
+ List<String> values = new ArrayList<String>();
+ 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<Entry<String, List<String>>> authorGroups = reader
+ .getLibrary().getAuthorsGrouped();
+
+ if (authorGroups.size() > 1) {
+ for (Entry<String, List<String>> 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));
+ }
+ });
+ }
+ });
+ }
+ }
+ };
}
/**