From 97b36d32a43b7fbbd4938c95c4b40db0c68daa71 Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Mon, 18 Mar 2019 23:18:52 +0100 Subject: [PATCH] gui: allow 'all' and 'listing' for sources and authors --- changelog-fr.md | 1 + changelog.md | 1 + src/be/nikiroo/fanfix/bundles/UiConfig.java | 2 - src/be/nikiroo/fanfix/bundles/ui.properties | 3 - .../fanfix/bundles/ui_description.properties | 3 - .../nikiroo/fanfix/library/BasicLibrary.java | 66 +++-- .../fanfix/reader/ui/GuiReaderFrame.java | 259 +++++++++--------- 7 files changed, 175 insertions(+), 160 deletions(-) diff --git a/changelog-fr.md b/changelog-fr.md index 117d55c..667de9f 100644 --- a/changelog-fr.md +++ b/changelog-fr.md @@ -5,6 +5,7 @@ - new: les sources peuvent contenir "/" (et utiliseront des sous-répertoires en fonction) - gui: nouvelle page pour voir les propriétés d'une histoire - gui: renommer les histoires, changer l'auteur +- gui: permet de lister les auteurs ou les sources en mode "tout" ou "listing" - 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/changelog.md b/changelog.md index 0a291b3..817d49a 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,7 @@ - new: sources can contain "/" (and will use subdirectories) - gui: new Properties page for stories - gui: rename stories, change author +- gui: allow "all" and "listing" modes for sources and authors - 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/src/be/nikiroo/fanfix/bundles/UiConfig.java b/src/be/nikiroo/fanfix/bundles/UiConfig.java index f36d2bb..b6be9cb 100644 --- a/src/be/nikiroo/fanfix/bundles/UiConfig.java +++ b/src/be/nikiroo/fanfix/bundles/UiConfig.java @@ -23,6 +23,4 @@ public enum UiConfig { 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, // } diff --git a/src/be/nikiroo/fanfix/bundles/ui.properties b/src/be/nikiroo/fanfix/bundles/ui.properties index 8756d63..da9fdf9 100644 --- a/src/be/nikiroo/fanfix/bundles/ui.properties +++ b/src/be/nikiroo/fanfix/bundles/ui.properties @@ -22,6 +22,3 @@ NON_IMAGES_DOCUMENT_READER = # 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 diff --git a/src/be/nikiroo/fanfix/bundles/ui_description.properties b/src/be/nikiroo/fanfix/bundles/ui_description.properties index 99df88a..413dbf4 100644 --- a/src/be/nikiroo/fanfix/bundles/ui_description.properties +++ b/src/be/nikiroo/fanfix/bundles/ui_description.properties @@ -27,6 +27,3 @@ NON_IMAGES_DOCUMENT_READER = # 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 = diff --git a/src/be/nikiroo/fanfix/library/BasicLibrary.java b/src/be/nikiroo/fanfix/library/BasicLibrary.java index b3b49fd..8ec4e56 100644 --- a/src/be/nikiroo/fanfix/library/BasicLibrary.java +++ b/src/be/nikiroo/fanfix/library/BasicLibrary.java @@ -4,12 +4,10 @@ import java.io.File; import java.io.IOException; import java.net.URL; import java.net.UnknownHostException; -import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.TreeMap; import be.nikiroo.fanfix.Instance; @@ -326,48 +324,60 @@ abstract public class BasicLibrary { * * @return the authors' names, grouped by letter(s) */ - public List>> getAuthorsGrouped() { + public Map> getAuthorsGrouped() { int MAX = 20; - List>> groups = new ArrayList>>(); + Map> groups = new TreeMap>(); List authors = getAuthors(); + // If all authors fit the max, just report them as is if (authors.size() <= MAX) { - groups.add(new SimpleEntry>("", authors)); + groups.put("", authors); return groups; } - groups.add(new SimpleEntry>("*", getAuthorsGroup( - authors, '*'))); - groups.add(new SimpleEntry>("0-9", - getAuthorsGroup(authors, '0'))); - + // Create groups A to Z, which can be empty here for (char car = 'A'; car <= 'Z'; car++) { - groups.add(new SimpleEntry>(Character - .toString(car), getAuthorsGroup(authors, car))); + groups.put(Character.toString(car), getAuthorsGroup(authors, car)); } - // do NOT collapse * and [0-9] with the rest - for (int i = 2; i + 1 < groups.size(); i++) { - Entry> now = groups.get(i); - Entry> next = groups.get(i + 1); - int currentTotal = now.getValue().size() + next.getValue().size(); + // Collapse them + List keys = new ArrayList(groups.keySet()); + for (int i = 0; i + 1 < keys.size(); i++) { + String keyNow = keys.get(i); + String keyNext = keys.get(i + 1); + + List now = groups.get(keyNow); + List next = groups.get(keyNext); + + int currentTotal = now.size() + next.size(); if (currentTotal <= MAX) { - String key = now.getKey().charAt(0) + "-" - + next.getKey().charAt(next.getKey().length() - 1); + String key = keyNow.charAt(0) + "-" + + keyNext.charAt(keyNext.length() - 1); + List all = new ArrayList(); - all.addAll(now.getValue()); - all.addAll(next.getValue()); - groups.set(i, new SimpleEntry>(key, all)); - groups.remove(i + 1); - i--; + all.addAll(now); + all.addAll(next); + + groups.remove(keyNow); + groups.remove(keyNext); + groups.put(key, all); + + keys.set(i, key); // set the new key instead of key(i) + keys.remove(i + 1); // remove the next, consumed key + i--; // restart at key(i) } } - for (int i = 0; i < groups.size(); i++) { - if (groups.get(i).getValue().size() == 0) { - groups.remove(i); - i--; + // Add "special" groups + groups.put("*", getAuthorsGroup(authors, '*')); + groups.put("0-9", getAuthorsGroup(authors, '0')); + + // Prune empty groups + keys = new ArrayList(groups.keySet()); + for (String key : keys) { + if (groups.get(key).isEmpty()) { + groups.remove(key); } } diff --git a/src/be/nikiroo/fanfix/reader/ui/GuiReaderFrame.java b/src/be/nikiroo/fanfix/reader/ui/GuiReaderFrame.java index b1ba90c..d28f941 100644 --- a/src/be/nikiroo/fanfix/reader/ui/GuiReaderFrame.java +++ b/src/be/nikiroo/fanfix/reader/ui/GuiReaderFrame.java @@ -16,7 +16,6 @@ import java.io.IOException; import java.net.URL; import java.net.UnknownHostException; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -169,7 +168,11 @@ class GuiReaderFrame extends JFrame { lib.refresh(pg); invalidate(); setJMenuBar(createMenu(true)); - addBookPane(typeF, true); + if (typeF == null) { + addBookPane(true, false); + } else { + addBookPane(typeF, true); + } refreshBooks(); validate(); pane.setVisible(true); @@ -205,20 +208,21 @@ class GuiReaderFrame extends JFrame { setVisible(true); } - private void addSourcePanes() { + private void addListPane(String name, List values, + final boolean type) { // Sources -> i18n - GuiReaderGroup bookPane = new GuiReaderGroup(reader, "Sources", color); + GuiReaderGroup bookPane = new GuiReaderGroup(reader, name, color); - List sources = new ArrayList(); - for (String source : reader.getLibrary().getSources()) { + List metas = new ArrayList(); + for (String source : values) { MetaData mSource = new MetaData(); mSource.setLuid(null); mSource.setTitle(source); mSource.setSource(source); - sources.add(mSource); + metas.add(mSource); } - bookPane.refreshBooks(sources, false); + bookPane.refreshBooks(metas, false); this.invalidate(); pane.invalidate(); @@ -242,35 +246,43 @@ class GuiReaderFrame extends JFrame { @Override public void action(final GuiReaderBook book) { removeBookPanes(); - addBookPane(book.getMeta().getSource(), true); + addBookPane(book.getMeta().getSource(), type); refreshBooks(); } }); } /** - * Add a new {@link GuiReaderGroup} on the frame to display the books of the - * selected type or author. + * Add a new {@link GuiReaderGroup} on the frame to display all the + * sources/types or all the authors, or a listing of all the books sorted + * either by source or author. + *

+ * A display of all the sources/types or all the authors will show one icon + * per source/type or author. + *

+ * A listing of all the books sorted by source/type or author will display + * all the books. * - * @param value - * the author or the type, or NULL to get all the - * authors-or-types * @param type - * TRUE for type, FALSE for author + * TRUE for type/source, FALSE for author + * @param listMode + * TRUE to get a listing of all the sources or authors, FALSE to + * get one icon per source or author */ - private void addBookPane(String value, boolean type) { - if (value == null) { - if (type) { - if (Instance.getUiConfig().getBoolean(UiConfig.SOURCE_PAGE, - false)) { - addSourcePanes(); - } else { - for (String tt : reader.getLibrary().getSources()) { - if (tt != null) { - addBookPane(tt, type); - } + private void addBookPane(boolean type, boolean listMode) { + if (type) { + if (!listMode) { + addListPane("Sources", reader.getLibrary().getSources(), type); + } else { + for (String tt : reader.getLibrary().getSources()) { + if (tt != null) { + addBookPane(tt, type); } } + } + } else { + if (!listMode) { + addListPane("Authors", reader.getLibrary().getAuthors(), type); } else { for (String tt : reader.getLibrary().getAuthors()) { if (tt != null) { @@ -278,10 +290,21 @@ class GuiReaderFrame extends JFrame { } } } - - return; } + } + /** + * Add a new {@link GuiReaderGroup} on the frame to display the books of the + * selected type or author. + * + * @param value + * the author or the type, or NULL to get all the + * authors-or-types + * @param type + * TRUE for type/source, FALSE for author + * + */ + private void addBookPane(String value, boolean type) { GuiReaderGroup bookPane = new GuiReaderGroup(reader, value, color); if (type) { booksByType.put(bookPane, value); @@ -328,6 +351,10 @@ class GuiReaderFrame extends JFrame { }); } + /** + * Clear the pane from any book that may be present, usually prior to adding + * new ones. + */ private void removeBookPanes() { booksByType.clear(); booksByAuthor.clear(); @@ -443,82 +470,85 @@ class GuiReaderFrame extends JFrame { view.add(vwords); bar.add(view); + Map> groupedSources = new HashMap>(); + if (libOk) { + groupedSources = reader.getLibrary().getSourcesGrouped(); + } JMenu sources = new JMenu("Sources"); sources.setMnemonic(KeyEvent.VK_S); + populateMenuSA(sources, groupedSources, true); + bar.add(sources); - Map> groupedSources = new HashMap>(); + Map> goupedAuthors = new HashMap>(); if (libOk) { - groupedSources = reader.getLibrary().getSourcesGrouped(); + goupedAuthors = reader.getLibrary().getAuthorsGrouped(); } + JMenu authors = new JMenu("Authors"); + authors.setMnemonic(KeyEvent.VK_A); + populateMenuSA(authors, goupedAuthors, false); + bar.add(authors); - JMenuItem item = new JMenuItem("All"); - item.addActionListener(getActionOpenSource(null)); - sources.add(item); - sources.addSeparator(); + JMenu options = new JMenu("Options"); + options.setMnemonic(KeyEvent.VK_O); + options.add(createMenuItemConfig()); + options.add(createMenuItemUiConfig()); + bar.add(options); - for (final String type : groupedSources.keySet()) { - List list = groupedSources.get(type); - if (list.size() == 1 && list.get(0).isEmpty()) { - item = new JMenuItem(type); - item.addActionListener(getActionOpenSource(type)); - sources.add(item); + return bar; + } + + // "" = [unknown] + private void populateMenuSA(JMenu menu, + Map> groupedValues, boolean type) { + + // "All" and "Listing" special items + JMenuItem item = new JMenuItem("All"); + item.addActionListener(getActionOpenList(type, false)); + menu.add(item); + item = new JMenuItem("Listing"); + item.addActionListener(getActionOpenList(type, true)); + menu.add(item); + menu.addSeparator(); + + for (final String value : groupedValues.keySet()) { + List list = groupedValues.get(value); + if (type && list.size() == 1 && list.get(0).isEmpty()) { + // leaf item source/type + item = new JMenuItem(value.isEmpty() ? "[unknown]" : value); + item.addActionListener(getActionOpen(value, type)); + menu.add(item); } else { - JMenu dir = new JMenu(type); + JMenu dir; + if (!type && groupedValues.size() == 1) { + // only one group of authors + dir = menu; + } else { + dir = new JMenu(value.isEmpty() ? "[unknown]" : value); + } + for (String sub : list) { // " " instead of "" for the visual height String itemName = sub.isEmpty() ? " " : sub; - String actualType = type; - if (!sub.isEmpty()) { - actualType += "/" + sub; + String actualValue = value; + + if (type) { + if (!sub.isEmpty()) { + actualValue += "/" + sub; + } + } else { + actualValue = sub; } item = new JMenuItem(itemName); - item.addActionListener(getActionOpenSource(actualType)); + item.addActionListener(getActionOpen(actualValue, type)); dir.add(item); } - sources.add(dir); - } - } - - bar.add(sources); - - JMenu authors = new JMenu("Authors"); - authors.setMnemonic(KeyEvent.VK_A); - - List>> authorGroups = reader.getLibrary() - .getAuthorsGrouped(); - if (authorGroups.size() > 1) { - // Multiple groups - - // null -> "All" authors special item - populateMenuAuthorList(authors, Arrays.asList((String) null)); - - for (Entry> group : authorGroups) { - JMenu thisGroup = new JMenu(group.getKey()); - populateMenuAuthorList(thisGroup, group.getValue()); - authors.add(thisGroup); - } - } else { - // Only one group - // null -> "All" authors special item - List authorNames = new ArrayList(); - authorNames.add(null); - if (authorGroups.size() > 0) { - authorNames.addAll(authorGroups.get(0).getValue()); + if (menu != dir) { + menu.add(dir); + } } - populateMenuAuthorList(authors, authorNames); } - - bar.add(authors); - - JMenu options = new JMenu("Options"); - options.setMnemonic(KeyEvent.VK_O); - options.add(createMenuItemConfig()); - options.add(createMenuItemUiConfig()); - bar.add(options); - - return bar; } /** @@ -526,50 +556,31 @@ class GuiReaderFrame extends JFrame { * the selected/displayed one. * * @param type - * the type (source) to select + * the type (source) to select, cannot be NULL * * @return the {@link ActionListener} */ - private ActionListener getActionOpenSource(final String type) { + private ActionListener getActionOpen(final String source, final boolean type) { return new ActionListener() { @Override public void actionPerformed(ActionEvent e) { removeBookPanes(); - addBookPane(type, true); + addBookPane(source, type); refreshBooks(); } }; } - /** - * Populate a list of authors as {@link JMenuItem}s into the given - * {@link JMenu}. - *

- * Each item will select the author when clicked. - * - * @param authors - * the parent {@link JMenuItem} - * @param names - * the authors' names - */ - private void populateMenuAuthorList(JMenu authors, List names) { - for (final String name : names) { - JMenuItem item = new JMenuItem(name == null ? "All" - : name.isEmpty() ? "[unknown]" : name); - item.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - removeBookPanes(); - addBookPane(name, false); - refreshBooks(); - } - }); - authors.add(item); - - if (name == null || name.isEmpty()) { - authors.addSeparator(); + private ActionListener getActionOpenList(final boolean type, + final boolean listMode) { + return new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + removeBookPanes(); + addBookPane(type, listMode); + refreshBooks(); } - } + }; } /** @@ -817,21 +828,21 @@ class GuiReaderFrame extends JFrame { // Existing authors if (libOk) { - List>> authorGroups = reader - .getLibrary().getAuthorsGrouped(); + Map> groupedAuthors = reader.getLibrary() + .getAuthorsGrouped(); - if (authorGroups.size() > 1) { - for (Entry> entry : authorGroups) { - JMenu group = new JMenu(entry.getKey()); - for (String value : entry.getValue()) { + if (groupedAuthors.size() > 1) { + for (String key : groupedAuthors.keySet()) { + JMenu group = new JMenu(key); + for (String value : groupedAuthors.get(key)) { 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()) { + } else if (groupedAuthors.size() == 1) { + for (String value : groupedAuthors.values().iterator().next()) { JMenuItem item = new JMenuItem(value); item.addActionListener(createMoveAction("AUTHOR", value)); changeTo.add(item); -- 2.27.0