work on refresh after popup action
authorNiki Roo <niki@nikiroo.be>
Wed, 8 Apr 2020 09:53:08 +0000 (11:53 +0200)
committerNiki Roo <niki@nikiroo.be>
Wed, 8 Apr 2020 09:53:08 +0000 (11:53 +0200)
src/be/nikiroo/fanfix_swing/gui/BooksPanel.java
src/be/nikiroo/fanfix_swing/gui/BrowserPanel.java
src/be/nikiroo/fanfix_swing/gui/MainFrame.java
src/be/nikiroo/fanfix_swing/gui/SearchBar.java
src/be/nikiroo/fanfix_swing/gui/book/BookPopup.java
src/be/nikiroo/fanfix_swing/gui/browser/BasicTab.java
src/be/nikiroo/fanfix_swing/gui/utils/ListenerPanel.java [new file with mode: 0644]
src/be/nikiroo/fanfix_swing/gui/utils/TreeSnapshot.java [new file with mode: 0644]
src/be/nikiroo/fanfix_swing/gui/utils/UiHelper.java

index 579f394702aa9a7d4a45f6fcd13cca3d743342d5..73de77e974d7e6e313489ffb3b17ba969c1540b5 100644 (file)
@@ -18,7 +18,6 @@ import java.util.concurrent.ExecutionException;
 
 import javax.swing.DefaultListModel;
 import javax.swing.JList;
-import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
 import javax.swing.ListCellRenderer;
 import javax.swing.ListSelectionModel;
@@ -33,10 +32,11 @@ import be.nikiroo.fanfix_swing.gui.book.BookBlock;
 import be.nikiroo.fanfix_swing.gui.book.BookInfo;
 import be.nikiroo.fanfix_swing.gui.book.BookLine;
 import be.nikiroo.fanfix_swing.gui.book.BookPopup;
+import be.nikiroo.fanfix_swing.gui.utils.ListenerPanel;
 import be.nikiroo.fanfix_swing.gui.utils.UiHelper;
 
-public class BooksPanel extends JPanel {
-       class ListModel extends DefaultListModel<BookInfo> {
+public class BooksPanel extends ListenerPanel {
+       private class ListModel extends DefaultListModel<BookInfo> {
                public void fireElementChanged(BookInfo element) {
                        int index = indexOf(element);
                        if (index >= 0) {
@@ -45,6 +45,8 @@ public class BooksPanel extends JPanel {
                }
        }
 
+       static public final String INVALIDATE_CACHE = "invalidate_cache";
+
        private List<BookInfo> bookInfos = new ArrayList<BookInfo>();
        private Map<BookInfo, BookLine> books = new HashMap<BookInfo, BookLine>();
        private boolean seeWordCount;
@@ -68,7 +70,7 @@ public class BooksPanel extends JPanel {
                searchBar.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
-                               reload(searchBar.getText());
+                               filter(searchBar.getText());
                        }
                });
 
@@ -155,11 +157,11 @@ public class BooksPanel extends JPanel {
                        updateBookQueue.clear();
                }
 
-               reload(searchBar.getText());
+               filter(searchBar.getText());
        }
 
        // cannot be NULL
-       private void reload(String filter) {
+       private void filter(String filter) {
                data.clear();
                for (BookInfo bookInfo : bookInfos) {
                        if (filter.isEmpty() || bookInfo.getMainInfo().toLowerCase().contains(filter.toLowerCase())) {
@@ -227,6 +229,11 @@ public class BooksPanel extends JPanel {
                                }
                                return null;
                        }
+
+                       @Override
+                       public void invalidateCache() {
+                               fireActionPerformed(INVALIDATE_CACHE);
+                       }
                });
 
                list.addMouseMotionListener(new MouseAdapter() {
index 47e55e422c9a2589188dd117dbdfdb15beafc3c4..25f7c9df334e34421a0d1772291539cc02c6a258 100644 (file)
@@ -145,19 +145,13 @@ public class BrowserPanel extends JPanel {
         */
        public BookInfo getHighlight() {
                BasicLibrary lib = Instance.getInstance().getLibrary();
-               if (tabs.getSelectedComponent() == sourceTab) {
-                       List<String> sel = sourceTab.getSelectedElements();
-                       if (!sel.isEmpty()) {
+               List<String> sel = sourceTab.getSelectedElements();
+               if (!sel.isEmpty()) {
+                       if (tabs.getSelectedComponent() == sourceTab) {
                                return BookInfo.fromSource(lib, sel.get(0));
-                       }
-               } else if (tabs.getSelectedComponent() == authorTab) {
-                       List<String> sel = authorTab.getSelectedElements();
-                       if (!sel.isEmpty()) {
+                       } else if (tabs.getSelectedComponent() == authorTab) {
                                return BookInfo.fromAuthor(lib, sel.get(0));
-                       }
-               } else if (tabs.getSelectedComponent() == tagsTab) {
-                       List<String> sel = tagsTab.getSelectedElements();
-                       if (!sel.isEmpty()) {
+                       } else if (tabs.getSelectedComponent() == tagsTab) {
                                return BookInfo.fromTag(lib, sel.get(0));
                        }
                }
@@ -192,6 +186,15 @@ public class BrowserPanel extends JPanel {
                return tagsTab.getSelectedElements();
        }
 
+       /**
+        * Reload all the data from the 3 tabs.
+        */
+       public void reloadData() {
+               sourceTab.reloadData();
+               authorTab.reloadData();
+               tagsTab.reloadData();
+       }
+
        /**
         * Adds the specified action listener to receive action events from this
         * {@link SearchBar}.
index 0f94fa575ee1377307f814cffcf35d0baa9d2f95..36e3c6ad5e5442765cd6b122c571d0d66f6f6945 100644 (file)
@@ -1,10 +1,8 @@
 package be.nikiroo.fanfix_swing.gui;
 
-import java.awt.Color;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.KeyEvent;
-import java.io.IOException;
 
 import javax.swing.JComponent;
 import javax.swing.JFrame;
@@ -12,9 +10,7 @@ import javax.swing.JLabel;
 import javax.swing.JMenu;
 import javax.swing.JMenuBar;
 import javax.swing.JMenuItem;
-import javax.swing.JPanel;
 import javax.swing.JSplitPane;
-import javax.swing.SwingWorker;
 
 import be.nikiroo.utils.Version;
 
@@ -50,6 +46,7 @@ public class MainFrame extends JFrame {
                }
 
                books = new BooksPanel(true);
+
                browser.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
@@ -57,6 +54,14 @@ public class MainFrame extends JFrame {
                                details.setBook(browser.getHighlight());
                        }
                });
+               books.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               if (BooksPanel.INVALIDATE_CACHE.equals(e.getActionCommand())) {
+                                       browser.reloadData();
+                               }
+                       }
+               });
 
                JSplitPane split = split(other, books, orientationH, 0.5, 0);
 
index ee5896e1d9823408632007dee63b6a22b04b4a11..2dd458790448e346e8b1a722c52e619d744f6255 100644 (file)
@@ -8,10 +8,10 @@ import java.awt.event.KeyAdapter;
 import java.awt.event.KeyEvent;
 
 import javax.swing.JButton;
-import javax.swing.JPanel;
 import javax.swing.JTextField;
 import javax.swing.SwingUtilities;
 
+import be.nikiroo.fanfix_swing.gui.utils.ListenerPanel;
 import be.nikiroo.fanfix_swing.gui.utils.UiHelper;
 import be.nikiroo.fanfix_swing.images.IconGenerator;
 import be.nikiroo.fanfix_swing.images.IconGenerator.Icon;
@@ -22,7 +22,7 @@ import be.nikiroo.fanfix_swing.images.IconGenerator.Size;
  * 
  * @author niki
  */
-public class SearchBar extends JPanel {
+public class SearchBar extends ListenerPanel {
        static private final long serialVersionUID = 1L;
 
        private JButton search;
@@ -47,7 +47,7 @@ public class SearchBar extends JPanel {
                                text.requestFocus();
 
                                if (realTime) {
-                                       fireActionPerformed();
+                                       fireActionPerformed(getText());
                                }
                        }
                });
@@ -64,7 +64,7 @@ public class SearchBar extends JPanel {
                                                clear.setVisible(!empty);
 
                                                if (realTime) {
-                                                       fireActionPerformed();
+                                                       fireActionPerformed(getText());
                                                }
                                        }
                                });
@@ -74,7 +74,7 @@ public class SearchBar extends JPanel {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                                if (!realTime) {
-                                       fireActionPerformed();
+                                       fireActionPerformed(getText());
                                }
                        }
                });
@@ -89,7 +89,7 @@ public class SearchBar extends JPanel {
                                clear.setVisible(false);
                                text.requestFocus();
 
-                               fireActionPerformed();
+                               fireActionPerformed(getText());
                        }
                });
 
@@ -98,39 +98,6 @@ public class SearchBar extends JPanel {
                add(clear, BorderLayout.EAST);
        }
 
-       /**
-        * Adds the specified action listener to receive action events from this
-        * {@link SearchBar}.
-        *
-        * @param listener the action listener to be added
-        */
-       public synchronized void addActionListener(ActionListener listener) {
-               listenerList.add(ActionListener.class, listener);
-       }
-
-       /**
-        * Removes the specified action listener so that it no longer receives action
-        * events from this {@link SearchBar}.
-        *
-        * @param listener the action listener to be removed
-        */
-       public synchronized void removeActionListener(ActionListener listener) {
-               listenerList.remove(ActionListener.class, listener);
-       }
-
-       /**
-        * Notify the listeners of an action.
-        */
-       protected void fireActionPerformed() {
-               ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, getText());
-               Object[] listeners = listenerList.getListenerList();
-               for (int i = listeners.length - 2; i >= 0; i -= 2) {
-                       if (listeners[i] == ActionListener.class) {
-                               ((ActionListener) listeners[i + 1]).actionPerformed(e);
-                       }
-               }
-       }
-
        /**
         * Return the current text displayed by this {@link SearchBar}.
         * 
index 0710549feb3112f1233261ed79f47e8cedd01681..42f7e3b7205ef1f6cc43af65eb96d5775cd2bef1 100644 (file)
@@ -47,6 +47,7 @@ public class BookPopup extends JPopupMenu {
 
                public void fireElementChanged(BookInfo book);
 
+               public void invalidateCache();
        }
 
        /**
@@ -438,7 +439,6 @@ public class BookPopup extends JPopupMenu {
                                                                        lib.changeAuthor(luid, fChangeTo, null);
                                                                }
                                                        }
-                                                       // TODO: ^-- this can create new sources/authors, update maybe required?
 
                                                        return null;
                                                }
@@ -446,11 +446,15 @@ public class BookPopup extends JPopupMenu {
                                                @Override
                                                protected void done() {
                                                        try {
-                                                               // Reload anyway
-                                                               for (BookInfo book : selected) {
-                                                                       informer.fireElementChanged(book);
-                                                               }
+                                                               // this can create new sources/authors, so a simple fireElementChanged is not
+                                                               // enough, we need to clear the whole cache (for BrowserPanel for instance)
+                                                               informer.invalidateCache();
+
+                                                               // TODO: not enough!!
+                                                               // after move, item disappears in the list, probably caused by the Library
+                                                               // itself
 
+                                                               // Even if problems occurred, still invalidate the cache
                                                                get();
                                                        } catch (Exception e) {
                                                                UiHelper.error(BookPopup.this.getParent(), e.getLocalizedMessage(), "IOException", e);
index d467a91792ea78d83107dcd1952785e9fe73b9b9..e9e8edc018f8f4e7880affb2c6c10bc10eaa32a7 100644 (file)
@@ -8,14 +8,10 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
 import javax.swing.JTree;
 import javax.swing.SwingWorker;
-import javax.swing.UIDefaults;
 import javax.swing.event.TreeSelectionEvent;
 import javax.swing.event.TreeSelectionListener;
-import javax.swing.plaf.TreeUI;
 import javax.swing.plaf.basic.BasicTreeUI;
 import javax.swing.tree.DefaultMutableTreeNode;
 import javax.swing.tree.DefaultTreeCellRenderer;
@@ -23,15 +19,16 @@ import javax.swing.tree.DefaultTreeModel;
 import javax.swing.tree.TreeCellRenderer;
 import javax.swing.tree.TreePath;
 
-import be.nikiroo.fanfix.Instance;
 import be.nikiroo.fanfix_swing.gui.SearchBar;
+import be.nikiroo.fanfix_swing.gui.utils.ListenerPanel;
 import be.nikiroo.fanfix_swing.gui.utils.TreeCellSpanner;
+import be.nikiroo.fanfix_swing.gui.utils.TreeSnapshot;
 import be.nikiroo.fanfix_swing.gui.utils.UiHelper;
 import be.nikiroo.fanfix_swing.images.IconGenerator;
 import be.nikiroo.fanfix_swing.images.IconGenerator.Icon;
 import be.nikiroo.fanfix_swing.images.IconGenerator.Size;
 
-public abstract class BasicTab<T> extends JPanel {
+public abstract class BasicTab<T> extends ListenerPanel {
        private int totalCount = 0;
        private List<String> selectedElements = new ArrayList<String>();
        private T data;
@@ -40,6 +37,7 @@ public abstract class BasicTab<T> extends JPanel {
        private int index;
 
        private JTree tree;
+       private DefaultMutableTreeNode root;
        private SearchBar searchBar;
 
        public BasicTab(int index, String listenerCommand) {
@@ -51,7 +49,7 @@ public abstract class BasicTab<T> extends JPanel {
                data = createEmptyData();
                totalCount = 0;
 
-               final DefaultMutableTreeNode root = new DefaultMutableTreeNode();
+               root = new DefaultMutableTreeNode();
 
                tree = new JTree(root);
                tree.setUI(new BasicTreeUI());
@@ -81,7 +79,7 @@ public abstract class BasicTab<T> extends JPanel {
 
                                BasicTab.this.selectedElements = selectedElements;
 
-                               fireActionPerformed();
+                               fireActionPerformed(BasicTab.this.listenerCommand);
                        }
                });
 
@@ -92,27 +90,39 @@ public abstract class BasicTab<T> extends JPanel {
                searchBar.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
-                               root.removeAllChildren();
-                               loadData(root, data, searchBar.getText());
-                               ((DefaultTreeModel) tree.getModel()).reload();
-                               fireActionPerformed();
+                               reloadData();
                        }
                });
 
+               reloadData();
+       }
+
+       public void reloadData() {
+               final TreeSnapshot snapshot = new TreeSnapshot(tree);
                SwingWorker<Map<String, List<String>>, Integer> worker = new SwingWorker<Map<String, List<String>>, Integer>() {
                        @Override
                        protected Map<String, List<String>> doInBackground() throws Exception {
-                               return Instance.getInstance().getLibrary().getSourcesGrouped();
+                               fillData(data);
+                               return null;
                        }
 
                        @Override
                        protected void done() {
-                               fillData(data);
+                               try {
+                                       get();
+                               } catch (Exception e) {
+                                       // TODO: error
+                               }
+
+                               // TODO: update is flickering...
+
                                root.removeAllChildren();
                                totalCount = loadData(root, data, searchBar.getText());
                                ((DefaultTreeModel) tree.getModel()).reload();
 
-                               fireActionPerformed();
+                               snapshot.apply();
+
+                               fireActionPerformed(listenerCommand);
                        }
                };
                worker.execute();
@@ -158,39 +168,6 @@ public abstract class BasicTab<T> extends JPanel {
                tree.clearSelection();
        }
 
-       /**
-        * Adds the specified action listener to receive action events from this
-        * {@link SearchBar}.
-        *
-        * @param listener the action listener to be added
-        */
-       public synchronized void addActionListener(ActionListener listener) {
-               listenerList.add(ActionListener.class, listener);
-       }
-
-       /**
-        * Removes the specified action listener so that it no longer receives action
-        * events from this {@link SearchBar}.
-        *
-        * @param listener the action listener to be removed
-        */
-       public synchronized void removeActionListener(ActionListener listener) {
-               listenerList.remove(ActionListener.class, listener);
-       }
-
-       /**
-        * Notify the listeners of an action.
-        */
-       protected void fireActionPerformed() {
-               ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, listenerCommand);
-               Object[] listeners = listenerList.getListenerList();
-               for (int i = listeners.length - 2; i >= 0; i -= 2) {
-                       if (listeners[i] == ActionListener.class) {
-                               ((ActionListener) listeners[i + 1]).actionPerformed(e);
-                       }
-               }
-       }
-
        protected boolean checkFilter(String filter, String value) {
                return (filter == null || filter.isEmpty() || value.toLowerCase().contains(filter.toLowerCase()));
        }
diff --git a/src/be/nikiroo/fanfix_swing/gui/utils/ListenerPanel.java b/src/be/nikiroo/fanfix_swing/gui/utils/ListenerPanel.java
new file mode 100644 (file)
index 0000000..7ed5f86
--- /dev/null
@@ -0,0 +1,54 @@
+package be.nikiroo.fanfix_swing.gui.utils;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JPanel;
+
+import be.nikiroo.fanfix_swing.gui.SearchBar;
+
+/**
+ * A {@link JPanel} with the default {@link ActionListener} add/remove/fire
+ * methods.
+ * 
+ * @author niki
+ */
+public class ListenerPanel extends JPanel {
+       private static final long serialVersionUID = 1L;
+
+       /**
+        * Adds the specified action listener to receive action events from this
+        * {@link SearchBar}.
+        *
+        * @param listener the action listener to be added
+        */
+       public synchronized void addActionListener(ActionListener listener) {
+               listenerList.add(ActionListener.class, listener);
+       }
+
+       /**
+        * Removes the specified action listener so that it no longer receives action
+        * events from this {@link SearchBar}.
+        *
+        * @param listener the action listener to be removed
+        */
+       public synchronized void removeActionListener(ActionListener listener) {
+               listenerList.remove(ActionListener.class, listener);
+       }
+
+       /**
+        * Notify the listeners of an action.
+        * 
+        * @param listenerCommand A string that may specify a command (possibly one of
+        *                        several) associated with the event
+        */
+       protected void fireActionPerformed(String listenerCommand) {
+               ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, listenerCommand);
+               Object[] listeners = listenerList.getListenerList();
+               for (int i = listeners.length - 2; i >= 0; i -= 2) {
+                       if (listeners[i] == ActionListener.class) {
+                               ((ActionListener) listeners[i + 1]).actionPerformed(e);
+                       }
+               }
+       }
+}
diff --git a/src/be/nikiroo/fanfix_swing/gui/utils/TreeSnapshot.java b/src/be/nikiroo/fanfix_swing/gui/utils/TreeSnapshot.java
new file mode 100644 (file)
index 0000000..e202544
--- /dev/null
@@ -0,0 +1,122 @@
+package be.nikiroo.fanfix_swing.gui.utils;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.swing.JTree;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+
+public class TreeSnapshot {
+       private interface NodeAction {
+               public void run(TreeNode node);
+       }
+
+       private JTree tree;
+       private TreePath[] selectionPaths;
+       private List<TreePath> expanded;
+
+       public TreeSnapshot(JTree tree) {
+               this.tree = tree;
+
+               selectionPaths = tree.getSelectionPaths();
+               if (selectionPaths == null) {
+                       selectionPaths = new TreePath[0];
+               }
+
+               expanded = new ArrayList<TreePath>();
+               forEach(tree, new NodeAction() {
+                       @Override
+                       public void run(TreeNode node) {
+                               TreePath path = nodeToPath(node);
+                               if (path != null) {
+                                       if (TreeSnapshot.this.tree.isExpanded(path)) {
+                                               expanded.add(path);
+                                       }
+                               }
+                       }
+               });
+       }
+
+       public void apply() {
+               applyTo(tree);
+       }
+
+       public void applyTo(JTree tree) {
+               final List<TreePath> newExpanded = new ArrayList<TreePath>();
+               final List<TreePath> newSlectionPaths = new ArrayList<TreePath>();
+
+               forEach(tree, new NodeAction() {
+                       @Override
+                       public void run(TreeNode newNode) {
+                               TreePath newPath = nodeToPath(newNode);
+                               if (newPath != null) {
+                                       for (TreePath path : selectionPaths) {
+                                               if (newPath.toString().equals(path.toString())) {
+                                                       newSlectionPaths.add(newPath);
+                                                       if (expanded.contains(path)) {
+                                                               newExpanded.add(newPath);
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               });
+
+               for (TreePath newPath : newExpanded) {
+                       tree.expandPath(newPath);
+               }
+
+               tree.setSelectionPaths(newSlectionPaths.toArray(new TreePath[0]));
+       }
+
+       private void forEach(JTree tree, NodeAction action) {
+               forEach(tree.getModel(), tree.getModel().getRoot(), action);
+       }
+
+       private void forEach(TreeModel model, Object parent, NodeAction action) {
+               if (!(parent instanceof TreeNode))
+                       return;
+
+               TreeNode node = (TreeNode) parent;
+
+               action.run(node);
+               int count = model.getChildCount(node);
+               for (int i = 0; i < count; i++) {
+                       Object child = model.getChild(node, i);
+                       forEach(model, child, action);
+               }
+       }
+
+       private static TreePath nodeToPath(TreeNode node) {
+               List<Object> nodes = new LinkedList<Object>();
+               if (node != null) {
+                       nodes.add(node);
+                       node = node.getParent();
+                       while (node != null) {
+                               nodes.add(0, node);
+                               node = node.getParent();
+                       }
+               }
+
+               return nodes.isEmpty() ? null : new TreePath(nodes.toArray());
+       }
+
+       @Override
+       public String toString() {
+               StringBuilder builder = new StringBuilder();
+               builder.append("Tree Snapshot of: ").append(tree).append("\n");
+               builder.append("Selected paths:\n");
+               for (TreePath path : selectionPaths) {
+                       builder.append("\t").append(path).append("\n");
+               }
+               builder.append("Expanded paths:\n");
+               for (TreePath epath : expanded) {
+                       builder.append("\t").append(epath).append("\n");
+               }
+
+               return builder.toString();
+       }
+}
index 3318bcc2464e23bcba5342892161c4daf248a1fa..5d23ab50a9a635b5dd537400bc6efcb0d3a34ede 100644 (file)
@@ -7,6 +7,7 @@ import javax.swing.JButton;
 import javax.swing.JComponent;
 import javax.swing.JOptionPane;
 import javax.swing.JScrollPane;
+import javax.swing.JTree;
 import javax.swing.SwingUtilities;
 
 import be.nikiroo.fanfix.Instance;