gui: allow 'all' and 'listing' for sources and authors
authorNiki Roo <niki@nikiroo.be>
Mon, 18 Mar 2019 22:18:52 +0000 (23:18 +0100)
committerNiki Roo <niki@nikiroo.be>
Mon, 18 Mar 2019 22:18:52 +0000 (23:18 +0100)
changelog-fr.md
changelog.md
src/be/nikiroo/fanfix/bundles/UiConfig.java
src/be/nikiroo/fanfix/bundles/ui.properties
src/be/nikiroo/fanfix/bundles/ui_description.properties
src/be/nikiroo/fanfix/library/BasicLibrary.java
src/be/nikiroo/fanfix/reader/ui/GuiReaderFrame.java

index 117d55cdfde481d39138bf12aa02d6c3cd7dfcbb..667de9fd727cbd59be152fb1f6dfcf8bf787c4d4 100644 (file)
@@ -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
index 0a291b383b5afc9087a318415936b3a32040fe3c..817d49a52b9d2ad7d5a7b15f0f1ed209e63e2d0e 100644 (file)
@@ -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
index f36d2bba78219dc9b8fcd089450b8d2697128ff1..b6be9cb5ada1f93ac094608424d70a91b8685ec9 100644 (file)
@@ -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, //
 }
index 8756d63d9526534b0ff8a4387af4fca49eb59be8..da9fdf9b1f8c1e6ed11b03ac8c2a34a5c423cb03 100644 (file)
@@ -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
index 99df88a5db2de3ac2a6dc302578a7b38207def2a..413dbf41fca90ff89f4997f8623c72ad3a200d76 100644 (file)
@@ -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 = 
index b3b49fdbce50ec5aa6b4877af9c42b2a949e39df..8ec4e5620655ad148fb6b4b47b2c16d0f4284ee8 100644 (file)
@@ -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<Entry<String, List<String>>> getAuthorsGrouped() {
+       public Map<String, List<String>> getAuthorsGrouped() {
                int MAX = 20;
 
-               List<Entry<String, List<String>>> groups = new ArrayList<Entry<String, List<String>>>();
+               Map<String, List<String>> groups = new TreeMap<String, List<String>>();
                List<String> authors = getAuthors();
 
+               // If all authors fit the max, just report them as is
                if (authors.size() <= MAX) {
-                       groups.add(new SimpleEntry<String, List<String>>("", authors));
+                       groups.put("", authors);
                        return groups;
                }
 
-               groups.add(new SimpleEntry<String, List<String>>("*", getAuthorsGroup(
-                               authors, '*')));
-               groups.add(new SimpleEntry<String, List<String>>("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<String, List<String>>(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<String, List<String>> now = groups.get(i);
-                       Entry<String, List<String>> next = groups.get(i + 1);
-                       int currentTotal = now.getValue().size() + next.getValue().size();
+               // Collapse them
+               List<String> keys = new ArrayList<String>(groups.keySet());
+               for (int i = 0; i + 1 < keys.size(); i++) {
+                       String keyNow = keys.get(i);
+                       String keyNext = keys.get(i + 1);
+
+                       List<String> now = groups.get(keyNow);
+                       List<String> 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<String> all = new ArrayList<String>();
-                               all.addAll(now.getValue());
-                               all.addAll(next.getValue());
-                               groups.set(i, new SimpleEntry<String, List<String>>(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<String>(groups.keySet());
+               for (String key : keys) {
+                       if (groups.get(key).isEmpty()) {
+                               groups.remove(key);
                        }
                }
 
index b1ba90c9acde6cca6d23ad060eaf9526bac0a479..d28f941e933e9d2c53e2cbd3f5db4491cad82ec6 100644 (file)
@@ -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<String> values,
+                       final boolean type) {
                // Sources -> i18n
-               GuiReaderGroup bookPane = new GuiReaderGroup(reader, "Sources", color);
+               GuiReaderGroup bookPane = new GuiReaderGroup(reader, name, color);
 
-               List<MetaData> sources = new ArrayList<MetaData>();
-               for (String source : reader.getLibrary().getSources()) {
+               List<MetaData> metas = new ArrayList<MetaData>();
+               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.
+        * <p>
+        * A display of all the sources/types or all the authors will show one icon
+        * per source/type or author.
+        * <p>
+        * 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<String, List<String>> groupedSources = new HashMap<String, List<String>>();
+               if (libOk) {
+                       groupedSources = reader.getLibrary().getSourcesGrouped();
+               }
                JMenu sources = new JMenu("Sources");
                sources.setMnemonic(KeyEvent.VK_S);
+               populateMenuSA(sources, groupedSources, true);
+               bar.add(sources);
 
-               Map<String, List<String>> groupedSources = new HashMap<String, List<String>>();
+               Map<String, List<String>> goupedAuthors = new HashMap<String, List<String>>();
                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<String> 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<String, List<String>> 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<String> 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<Entry<String, List<String>>> authorGroups = reader.getLibrary()
-                               .getAuthorsGrouped();
-               if (authorGroups.size() > 1) {
-                       // Multiple groups
-
-                       // null -> "All" authors special item
-                       populateMenuAuthorList(authors, Arrays.asList((String) null));
-
-                       for (Entry<String, List<String>> 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<String> authorNames = new ArrayList<String>();
-                       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}.
-        * <p>
-        * 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<String> 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<Entry<String, List<String>>> authorGroups = reader
-                                       .getLibrary().getAuthorsGrouped();
+                       Map<String, List<String>> groupedAuthors = 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()) {
+                       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);