Merge branch 'subtree'
[nikiroo-utils.git] / src / be / nikiroo / fanfix / reader / ui / GuiReaderSearchByNamePanel.java
index a648b72286b2dcbd8831cbb75be5fd057434da9a..ebdb21ab2cf760562089447d48fffac6b873445f 100644 (file)
@@ -1,27 +1,21 @@
 package be.nikiroo.fanfix.reader.ui;
 
 import java.awt.BorderLayout;
-import java.awt.Color;
-import java.awt.Component;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
-import javax.swing.BoxLayout;
 import javax.swing.JButton;
-import javax.swing.JComboBox;
-import javax.swing.JList;
 import javax.swing.JPanel;
-import javax.swing.JTabbedPane;
 import javax.swing.JTextField;
-import javax.swing.ListCellRenderer;
 
 import be.nikiroo.fanfix.data.MetaData;
+import be.nikiroo.fanfix.reader.ui.GuiReaderSearchByPanel.Waitable;
 import be.nikiroo.fanfix.searchable.BasicSearchable;
-import be.nikiroo.fanfix.searchable.SearchableTag;
-import be.nikiroo.fanfix.supported.SupportType;
 
 /**
  * This panel represents a search panel that works for keywords and tags based
@@ -29,366 +23,192 @@ import be.nikiroo.fanfix.supported.SupportType;
  * 
  * @author niki
  */
-// JCombobox<E> not 1.6 compatible
-@SuppressWarnings({ "unchecked", "rawtypes" })
 public class GuiReaderSearchByNamePanel extends JPanel {
        private static final long serialVersionUID = 1L;
 
-       private int actionEventId = ActionEvent.ACTION_FIRST;
-
-       private SupportType supportType;
        private BasicSearchable searchable;
-       private int page;
-       private boolean searchByTags;
 
-       private String keywords;
-       private JTabbedPane searchTabs;
        private JTextField keywordsField;
        private JButton submitKeywords;
 
-       private JPanel tagBars;
-       private List<JComboBox> combos;
-       private JComboBox comboSupportTypes;
-
-       private List<ActionListener> actions = new ArrayList<ActionListener>();
+       private int page;
+       private int maxPage;
        private List<MetaData> stories = new ArrayList<MetaData>();
        private int storyItem;
 
-       // will throw illegalArgEx if bad support type
-       public GuiReaderSearchByNamePanel(SupportType supportType) {
-               setLayout(new BorderLayout());
-
-               setSupportType(supportType);
-               page = 1;
-               searchByTags = false;
-
-               searchTabs = new JTabbedPane();
-               searchTabs.addTab("By name", createByNameSearchPanel());
-               searchTabs.addTab("By tags", createByTagSearchPanel());
-
-               add(searchTabs, BorderLayout.CENTER);
-       }
-
-       private JPanel createByNameSearchPanel() {
-               JPanel byName = new JPanel(new BorderLayout());
+       public GuiReaderSearchByNamePanel(final Waitable waitable) {
+               super(new BorderLayout());
 
                keywordsField = new JTextField();
-               byName.add(keywordsField, BorderLayout.CENTER);
+               add(keywordsField, BorderLayout.CENTER);
 
                submitKeywords = new JButton("Search");
-               byName.add(submitKeywords, BorderLayout.EAST);
+               add(submitKeywords, BorderLayout.EAST);
 
-               // TODO: ENTER -> search
+               // should be done out of UI
+               final Runnable go = new Runnable() {
+                       @Override
+                       public void run() {
+                               waitable.setWaiting(true);
+                               try {
+                                       search(keywordsField.getText(), 1, 0);
+                                       waitable.fireEvent();
+                               } finally {
+                                       waitable.setWaiting(false);
+                               }
+                       }
+               };
+
+               keywordsField.addKeyListener(new KeyAdapter() {
+                       @Override
+                       public void keyReleased(KeyEvent e) {
+                               if (e.getKeyCode() == KeyEvent.VK_ENTER) {
+                                       new Thread(go).start();
+                               } else {
+                                       super.keyReleased(e);
+                               }
+                       }
+               });
 
                submitKeywords.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
-                               search(keywordsField.getText(), 0);
+                               new Thread(go).start();
                        }
                });
 
-               return byName;
-       }
-
-       private JPanel createByTagSearchPanel() {
-               combos = new ArrayList<JComboBox>();
-
-               JPanel byTag = new JPanel();
-               tagBars = new JPanel();
-               tagBars.setLayout(new BoxLayout(tagBars, BoxLayout.Y_AXIS));
-               byTag.add(tagBars, BorderLayout.NORTH);
-
-               return byTag;
-       }
-
-       public SupportType getSupportType() {
-               return supportType;
+               setSearchable(null);
        }
 
-       public void setSupportType(SupportType supportType) {
-               BasicSearchable searchable = BasicSearchable.getSearchable(supportType);
-               if (searchable == null) {
-                       throw new java.lang.IllegalArgumentException(
-                                       "Unupported support type: " + supportType);
-               }
-
-               this.supportType = supportType;
+       /**
+        * The {@link BasicSearchable} object use for the searches themselves.
+        * <p>
+        * Can be NULL, but no searches will work.
+        * 
+        * @param searchable
+        *            the new searchable
+        */
+       public void setSearchable(BasicSearchable searchable) {
                this.searchable = searchable;
+               page = 0;
+               maxPage = -1;
+               storyItem = 0;
+               stories = new ArrayList<MetaData>();
+               updateKeywords("");
        }
 
+       /**
+        * The currently displayed page of result for the current search (see the
+        * <tt>page</tt> parameter of
+        * {@link GuiReaderSearchByNamePanel#search(String, int, int)}).
+        * 
+        * @return the currently displayed page of results
+        */
        public int getPage() {
                return page;
        }
 
-       public void setPage(int page) {
-               // TODO: set against maxPage
-               // TODO: update last search?
-               this.page = page;
-       }
-
-       // actions will be fired in UIthread
-       public void addActionListener(ActionListener action) {
-               actions.add(action);
+       /**
+        * The number of pages of result for the current search (see the
+        * <tt>page</tt> parameter of
+        * {@link GuiReaderSearchByPanel#search(String, int, int)}).
+        * <p>
+        * For an unknown number or when not applicable, -1 is returned.
+        * 
+        * @return the number of pages of results or -1
+        */
+       public int getMaxPage() {
+               return maxPage;
        }
 
-       public boolean removeActionListener(ActionListener action) {
-               return actions.remove(action);
+       /**
+        * Return the keywords used for the current search.
+        * 
+        * @return the keywords
+        */
+       public String getCurrentKeywords() {
+               return keywordsField.getText();
        }
 
+       /**
+        * The currently loaded stories (the result of the latest search).
+        * 
+        * @return the stories
+        */
        public List<MetaData> getStories() {
                return stories;
        }
 
-       // selected item or 0 if none ! one-based !
+       /**
+        * Return the currently selected story (the <tt>item</tt>) if it was
+        * specified in the latest, or 0 if not.
+        * <p>
+        * Note: this is thus a 1-based index, <b>not</b> a 0-based index.
+        * 
+        * @return the item
+        */
        public int getStoryItem() {
                return storyItem;
        }
 
-       private void fireAction() {
-               GuiReaderSearchFrame.inUi(new Runnable() {
-                       @Override
-                       public void run() {
-                               ActionEvent ae = new ActionEvent(
-                                               GuiReaderSearchByNamePanel.this, actionEventId,
-                                               "stories found");
-
-                               actionEventId++;
-                               if (actionEventId > ActionEvent.ACTION_LAST) {
-                                       actionEventId = ActionEvent.ACTION_FIRST;
-                               }
-
-                               for (ActionListener action : actions) {
-                                       try {
-                                               action.actionPerformed(ae);
-                                       } catch (Exception e) {
-                                               GuiReaderSearchFrame.error(e);
-                                       }
-                               }
-                       }
-               });
-       }
-
-       private void updateSearchBy(final boolean byTag) {
-               if (byTag != this.searchByTags) {
-                       GuiReaderSearchFrame.inUi(new Runnable() {
-                               @Override
-                               public void run() {
-                                       if (!byTag) {
-                                               searchTabs.setSelectedIndex(0);
-                                       } else {
-                                               searchTabs.setSelectedIndex(1);
-                                       }
-                               }
-                       });
-               }
-       }
-
-       // cannot be NULL
+       /**
+        * Update the keywords displayed on screen.
+        * 
+        * @param keywords
+        *            the keywords
+        */
        private void updateKeywords(final String keywords) {
-               if (!keywords.equals(this.keywords)) {
+               if (!keywords.equals(keywordsField.getText())) {
                        GuiReaderSearchFrame.inUi(new Runnable() {
                                @Override
                                public void run() {
-                                       GuiReaderSearchByNamePanel.this.keywords = keywords;
                                        keywordsField.setText(keywords);
                                }
                        });
                }
        }
 
-       // update and reset the tagsbar
-       // can be NULL, for base tags
-       private void updateTags(final SearchableTag tag) {
-               final List<SearchableTag> parents = new ArrayList<SearchableTag>();
-               SearchableTag parent = (tag == null) ? null : tag;
-               while (parent != null) {
-                       parents.add(parent);
-                       parent = parent.getParent();
-               }
-
-               List<SearchableTag> rootTags = null;
-               SearchableTag selectedRootTag = null;
-               selectedRootTag = parents.isEmpty() ? null : parents
-                               .get(parents.size() - 1);
-
-               try {
-                       rootTags = searchable.getTags();
-               } catch (IOException e) {
-                       GuiReaderSearchFrame.error(e);
-               }
-
-               final List<SearchableTag> rootTagsF = rootTags;
-               final SearchableTag selectedRootTagF = selectedRootTag;
-
-               GuiReaderSearchFrame.inUi(new Runnable() {
-                       @Override
-                       public void run() {
-                               tagBars.invalidate();
-                               tagBars.removeAll();
-
-                               addTagBar(rootTagsF, selectedRootTagF);
-
-                               for (int i = parents.size() - 1; i >= 0; i--) {
-                                       SearchableTag selectedChild = null;
-                                       if (i > 0) {
-                                               selectedChild = parents.get(i - 1);
-                                       }
-
-                                       SearchableTag parent = parents.get(i);
-                                       addTagBar(parent.getChildren(), selectedChild);
-                               }
-
-                               tagBars.validate();
-                       }
-               });
-       }
-
-       // must be quick and no thread change
-       private void addTagBar(List<SearchableTag> tags,
-                       final SearchableTag selected) {
-               tags.add(0, null);
-
-               final int comboIndex = combos.size();
-
-               final JComboBox combo = new JComboBox(
-                               tags.toArray(new SearchableTag[] {}));
-               combo.setSelectedItem(selected);
-
-               final ListCellRenderer basic = combo.getRenderer();
-
-               combo.setRenderer(new ListCellRenderer() {
-                       @Override
-                       public Component getListCellRendererComponent(JList list,
-                                       Object value, int index, boolean isSelected,
-                                       boolean cellHasFocus) {
-
-                               Object displayValue = value;
-                               if (value instanceof SearchableTag) {
-                                       displayValue = ((SearchableTag) value).getName();
-                               } else {
-                                       displayValue = "Select a tag...";
-                                       cellHasFocus = false;
-                                       isSelected = false;
-                               }
-
-                               Component rep = basic.getListCellRendererComponent(list,
-                                               displayValue, index, isSelected, cellHasFocus);
-
-                               if (value == null) {
-                                       rep.setForeground(Color.GRAY);
-                               }
-
-                               return rep;
-                       }
-               });
-
-               combo.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               final SearchableTag tag = (SearchableTag) combo
-                                               .getSelectedItem();
-                               if (tag != null) {
-                                       while (comboIndex + 1 < combos.size()) {
-                                               JComboBox combo = combos.remove(comboIndex + 1);
-                                               tagBars.remove(combo);
-                                       }
-
-                                       addTagBar(tag, new Runnable() {
-                                               @Override
-                                               public void run() {
-                                                       // TODO: slow ui
-                                                       SearchableTag tag = ((SearchableTag) combo
-                                                                       .getSelectedItem());
-                                                       if (tag != null && tag.isLeaf()) {
-                                                               BasicSearchable searchable = BasicSearchable
-                                                                               .getSearchable(supportType);
-                                                               List<MetaData> metas = new ArrayList<MetaData>();
-                                                               try {
-                                                                       metas = searchable.search(tag, 1);
-                                                                       search(metas, 1,
-                                                                                       searchable.searchPages(tag), 0);
-                                                               } catch (IOException e) {
-                                                                       error(e);
-                                                               }
-                                                       }
-
-                                                       setWaitingScreen(false);
-                                               }
-                                       });
-                               }
-                       }
-               });
-
-               combos.add(combo);
-               tagBars.add(combo);
-       }
-
-       // async, add children of tag, NULL = base tags
-       private void addTagBar(final SearchableTag tag, final Runnable inUi) {
-               new Thread(new Runnable() {
-                       @Override
-                       public void run() {
-                               List<SearchableTag> children = new ArrayList<SearchableTag>();
-                               if (tag == null) {
-                                       try {
-                                               List<SearchableTag> baseTags = searchable.getTags();
-                                               children = baseTags;
-                                       } catch (IOException e) {
-                                               error(e);
-                                       }
-                               } else {
-                                       try {
-                                               searchable.fillTag(tag);
-                                       } catch (IOException e) {
-                                               error(e);
-                                       }
-
-                                       if (!tag.isLeaf()) {
-                                               children = tag.getChildren();
-                                       } else {
-                                               children = null;
-                                       }
-                               }
-
-                               final List<SearchableTag> fchildren = children;
-                               inUi(new Runnable() {
-                                       @Override
-                                       public void run() {
-                                               if (fchildren != null) {
-                                                       addTagBar(fchildren, tag);
-                                               }
-
-                                               if (inUi != null) {
-                                                       inUi.run();
-                                               }
-                                       }
-                               });
-                       }
-               }).start();
-       }
-
-       // item 0 = no selection, else = default selection
-       // return: maxpage
-       public int search(String keywords, int item) {
+       /**
+        * Search for the given terms on the currently selected searchable.
+        * <p>
+        * This operation can be long and should be run outside the UI thread.
+        * 
+        * @param keywords
+        *            the keywords to search for
+        * @param page
+        *            the page of results to load
+        * @param item
+        *            the item to select (or 0 for none by default)
+        * 
+        * @throw IndexOutOfBoundsException if the page is out of bounds
+        */
+       public void search(String keywords, int page, int item) {
                List<MetaData> stories = new ArrayList<MetaData>();
                int storyItem = 0;
 
-               updateSearchBy(false);
                updateKeywords(keywords);
 
                int maxPage = -1;
-               try {
-                       maxPage = searchable.searchPages(keywords);
-               } catch (IOException e) {
-                       GuiReaderSearchFrame.error(e);
-               }
-
-               if (page > 0) {
+               if (searchable != null) {
                        try {
-                               stories = searchable.search(keywords, page);
+                               maxPage = searchable.searchPages(keywords);
                        } catch (IOException e) {
                                GuiReaderSearchFrame.error(e);
-                               stories = new ArrayList<MetaData>();
+                       }
+               }
+
+               if (page > 0) {
+                       if (maxPage >= 0 && (page <= 0 || page > maxPage)) {
+                               throw new IndexOutOfBoundsException("Page " + page + " out of "
+                                               + maxPage);
+                       }
+
+                       if (searchable != null) {
+                               try {
+                                       stories = searchable.search(keywords, page);
+                               } catch (IOException e) {
+                                       GuiReaderSearchFrame.error(e);
+                               }
                        }
 
                        if (item > 0 && item <= stories.size()) {
@@ -400,73 +220,10 @@ public class GuiReaderSearchByNamePanel extends JPanel {
                        }
                }
 
+               this.page = page;
+               this.maxPage = maxPage;
                this.stories = stories;
                this.storyItem = storyItem;
-               fireAction();
-
-               return maxPage;
-       }
-
-       // tag: null = base tags
-       // return: max pages
-       public int searchTag(SearchableTag tag, int item) {
-               List<MetaData> stories = new ArrayList<MetaData>();
-               int storyItem = 0;
-
-               updateSearchBy(true);
-               updateTags(tag);
-
-               int maxPage = 1;
-               if (tag != null) {
-                       try {
-                               searchable.fillTag(tag);
-
-                               if (!tag.isLeaf()) {
-                                       List<SearchableTag> subtags = tag.getChildren();
-                                       if (item > 0 && item <= subtags.size()) {
-                                               SearchableTag subtag = subtags.get(item - 1);
-                                               try {
-                                                       tag = subtag;
-                                                       searchable.fillTag(tag);
-                                               } catch (IOException e) {
-                                                       GuiReaderSearchFrame.error(e);
-                                               }
-                                       } else if (item > 0) {
-                                               GuiReaderSearchFrame.error(String.format(
-                                                               "Tag item does not exist: Tag [%s], item %d",
-                                                               tag.getFqName(), item));
-                                       }
-                               }
-
-                               maxPage = searchable.searchPages(tag);
-                               if (page > 0) {
-                                       if (tag.isLeaf()) {
-                                               try {
-                                                       stories = searchable.search(tag, page);
-                                                       if (item > 0 && item <= stories.size()) {
-                                                               storyItem = item;
-                                                       } else if (item > 0) {
-                                                               GuiReaderSearchFrame
-                                                                               .error(String
-                                                                                               .format("Story item does not exist: Tag [%s], item %d",
-                                                                                                               tag.getFqName(), item));
-                                                       }
-                                               } catch (IOException e) {
-                                                       GuiReaderSearchFrame.error(e);
-                                               }
-                                       }
-                               }
-                       } catch (IOException e) {
-                               GuiReaderSearchFrame.error(e);
-                               maxPage = 0;
-                       }
-               }
-
-               this.stories = stories;
-               this.storyItem = storyItem;
-               fireAction();
-
-               return maxPage;
        }
 
        /**
@@ -481,15 +238,9 @@ public class GuiReaderSearchByNamePanel extends JPanel {
         *            this component is disabled
         */
        @Override
-       public void setEnabled(final boolean waiting) {
-               GuiReaderSearchFrame.inUi(new Runnable() {
-                       @Override
-                       public void run() {
-                               GuiReaderSearchByNamePanel.super.setEnabled(!waiting);
-                               keywordsField.setEnabled(!waiting);
-                               submitKeywords.setEnabled(!waiting);
-                               // TODO
-                       }
-               });
+       public void setEnabled(boolean b) {
+               super.setEnabled(b);
+               keywordsField.setEnabled(b);
+               submitKeywords.setEnabled(b);
        }
 }