From: Niki Roo Date: Fri, 19 Apr 2019 15:40:14 +0000 (+0200) Subject: GUI search: code cleanup + jDoc X-Git-Url: http://git.nikiroo.be/?p=fanfix.git;a=commitdiff_plain;h=dc3b0033126c7f8158499b8ae9759a22f29d78ac GUI search: code cleanup + jDoc --- diff --git a/src/be/nikiroo/fanfix/reader/ui/GuiReaderGroup.java b/src/be/nikiroo/fanfix/reader/ui/GuiReaderGroup.java index a002389..7275498 100644 --- a/src/be/nikiroo/fanfix/reader/ui/GuiReaderGroup.java +++ b/src/be/nikiroo/fanfix/reader/ui/GuiReaderGroup.java @@ -362,7 +362,7 @@ public class GuiReaderGroup extends JPanel { * @param index * the index of the book to select, can be outside the bounds * (either all the items will be unselected or the first or last - * book will then be selected, see forceRange>/tt>) + * book will then be selected, see forceRange>) * @param forceRange * TRUE to constraint the index to the first/last element, FALSE * to unselect when outside the range diff --git a/src/be/nikiroo/fanfix/reader/ui/GuiReaderSearchByNamePanel.java b/src/be/nikiroo/fanfix/reader/ui/GuiReaderSearchByNamePanel.java index 94579fd..51db24c 100644 --- a/src/be/nikiroo/fanfix/reader/ui/GuiReaderSearchByNamePanel.java +++ b/src/be/nikiroo/fanfix/reader/ui/GuiReaderSearchByNamePanel.java @@ -14,6 +14,7 @@ import javax.swing.JPanel; import javax.swing.JTextField; import be.nikiroo.fanfix.data.MetaData; +import be.nikiroo.fanfix.reader.ui.GuiReaderSearchByPanel.Waitable; import be.nikiroo.fanfix.searchable.BasicSearchable; /** @@ -35,7 +36,7 @@ public class GuiReaderSearchByNamePanel extends JPanel { private List stories = new ArrayList(); private int storyItem; - public GuiReaderSearchByNamePanel(final Runnable fireEvent) { + public GuiReaderSearchByNamePanel(final Waitable waitable) { super(new BorderLayout()); keywordsField = new JTextField(); @@ -44,11 +45,25 @@ public class GuiReaderSearchByNamePanel extends JPanel { submitKeywords = new JButton("Search"); add(submitKeywords, BorderLayout.EAST); + // 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) { - search(keywordsField.getText(), 1, 0); + new Thread(go).start(); } else { super.keyReleased(e); } @@ -58,13 +73,7 @@ public class GuiReaderSearchByNamePanel extends JPanel { submitKeywords.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - new Thread(new Runnable() { - @Override - public void run() { - search(keywordsField.getText(), 1, 0); - fireEvent.run(); - } - }).start(); + new Thread(go).start(); } }); @@ -88,28 +97,66 @@ public class GuiReaderSearchByNamePanel extends JPanel { updateKeywords(""); } + /** + * The currently displayed page of result for the current search (see the + * page parameter of + * {@link GuiReaderSearchByNamePanel#search(String, int, int)}). + * + * @return the currently displayed page of results + */ public int getPage() { return page; } + /** + * The number of pages of result for the current search (see the + * page parameter of + * {@link GuiReaderSearchByPanel#search(String, int, int)}). + *

+ * 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; } + /** + * 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 getStories() { return stories; } - // selected item or 0 if none ! one-based ! + /** + * Return the currently selected story (the item) if it was + * specified in the latest, or 0 if not. + *

+ * Note: this is thus a 1-based index, not a 0-based index. + * + * @return the item + */ public int getStoryItem() { return storyItem; } - // cannot be NULL + /** + * Update the keywords displayed on screen. + * + * @param keywords + * the keywords + */ private void updateKeywords(final String keywords) { if (!keywords.equals(keywordsField.getText())) { GuiReaderSearchFrame.inUi(new Runnable() { @@ -121,8 +168,20 @@ public class GuiReaderSearchByNamePanel extends JPanel { } } - // item 0 = no selection, else = default selection - // throw if page > max + /** + * Search for the given terms on the currently selected searchable. + *

+ * 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 stories = new ArrayList(); int storyItem = 0; @@ -130,10 +189,12 @@ public class GuiReaderSearchByNamePanel extends JPanel { updateKeywords(keywords); int maxPage = -1; - try { - maxPage = searchable.searchPages(keywords); - } catch (IOException e) { - GuiReaderSearchFrame.error(e); + if (searchable != null) { + try { + maxPage = searchable.searchPages(keywords); + } catch (IOException e) { + GuiReaderSearchFrame.error(e); + } } if (page > 0) { @@ -142,11 +203,12 @@ public class GuiReaderSearchByNamePanel extends JPanel { + maxPage); } - try { - stories = searchable.search(keywords, page); - } catch (IOException e) { - GuiReaderSearchFrame.error(e); - stories = new ArrayList(); + if (searchable != null) { + try { + stories = searchable.search(keywords, page); + } catch (IOException e) { + GuiReaderSearchFrame.error(e); + } } if (item > 0 && item <= stories.size()) { diff --git a/src/be/nikiroo/fanfix/reader/ui/GuiReaderSearchByPanel.java b/src/be/nikiroo/fanfix/reader/ui/GuiReaderSearchByPanel.java index 4fcda5c..8f95d4c 100644 --- a/src/be/nikiroo/fanfix/reader/ui/GuiReaderSearchByPanel.java +++ b/src/be/nikiroo/fanfix/reader/ui/GuiReaderSearchByPanel.java @@ -1,9 +1,6 @@ package be.nikiroo.fanfix.reader.ui; import java.awt.BorderLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.ArrayList; import java.util.List; import javax.swing.JPanel; @@ -25,39 +22,52 @@ import be.nikiroo.fanfix.supported.SupportType; public class GuiReaderSearchByPanel extends JPanel { private static final long serialVersionUID = 1L; - private int actionEventId = ActionEvent.ACTION_FIRST; - private Waitable waitable; private boolean searchByTags; - private JTabbedPane searchTabs; private GuiReaderSearchByNamePanel byName; private GuiReaderSearchByTagPanel byTag; - private List actions = new ArrayList(); - + /** + * This interface represents an item that wan be put in "wait" mode. It is + * supposed to be used for long running operations during which we want to + * disable UI interactions. + *

+ * It also allows reporting an event to the item. + * + * @author niki + */ public interface Waitable { + /** + * Set the item in wait mode, blocking it from accepting UI input. + * + * @param waiting + * TRUE for wait more, FALSE to restore normal mode + */ public void setWaiting(boolean waiting); + + /** + * Notify the {@link Waitable} that an event occured (i.e., new stories + * were found). + */ + public void fireEvent(); } - // will throw illegalArgEx if bad support type, NULL allowed - public GuiReaderSearchByPanel(final SupportType supportType, - Waitable waitable) { + /** + * Create a new {@link GuiReaderSearchByPanel}. + * + * @param waitable + * the waitable we can wait on for long UI operations + */ + public GuiReaderSearchByPanel(Waitable waitable) { setLayout(new BorderLayout()); this.waitable = waitable; searchByTags = false; - Runnable fireEvent = new Runnable() { - @Override - public void run() { - fireAction(); - } - }; - - byName = new GuiReaderSearchByNamePanel(fireEvent); - byTag = new GuiReaderSearchByTagPanel(fireEvent); + byName = new GuiReaderSearchByNamePanel(waitable); + byTag = new GuiReaderSearchByTagPanel(waitable); searchTabs = new JTabbedPane(); searchTabs.addTab("By name", byName); @@ -71,20 +81,43 @@ public class GuiReaderSearchByPanel extends JPanel { add(searchTabs, BorderLayout.CENTER); updateSearchBy(searchByTags); - setSupportType(supportType); } + /** + * Set the new {@link SupportType}. + *

+ * This operation can be long and should be run outside the UI thread. + *

+ * Note that if a non-searchable {@link SupportType} is used, an + * {@link IllegalArgumentException} will be thrown. + * + * @param supportType + * the support mode, must be searchable or NULL + * + * @throws IllegalArgumentException + * if the {@link SupportType} is not NULL but not searchable + * (see {@link BasicSearchable#getSearchable(SupportType)}) + */ public void setSupportType(SupportType supportType) { BasicSearchable searchable = BasicSearchable.getSearchable(supportType); if (searchable == null && supportType != null) { - throw new java.lang.IllegalArgumentException( - "Unupported support type: " + supportType); + throw new IllegalArgumentException("Unupported support type: " + + supportType); } byName.setSearchable(searchable); byTag.setSearchable(searchable); } + /** + * The currently displayed page of result for the current search (see the + * page parameter of + * {@link GuiReaderSearchByPanel#search(String, int, int)} or + * {@link GuiReaderSearchByPanel#searchTag(SupportType, int, int, SearchableTag)} + * ). + * + * @return the currently displayed page of results + */ public int getPage() { if (!searchByTags) { return byName.getPage(); @@ -93,6 +126,17 @@ public class GuiReaderSearchByPanel extends JPanel { return byTag.getPage(); } + /** + * The number of pages of result for the current search (see the + * page parameter of + * {@link GuiReaderSearchByPanel#search(String, int, int)} or + * {@link GuiReaderSearchByPanel#searchTag(SupportType, int, int, SearchableTag)} + * ). + *

+ * For an unknown number or when not applicable, -1 is returned. + * + * @return the number of pages of results or -1 + */ public int getMaxPage() { if (!searchByTags) { return byName.getMaxPage(); @@ -101,7 +145,17 @@ public class GuiReaderSearchByPanel extends JPanel { return byTag.getMaxPage(); } - // throw outOfBounds if needed + /** + * Set the page of results to display for the current search. This will + * cause {@link Waitable#fireEvent()} to be called if needed. + *

+ * This operation can be long and should be run outside the UI thread. + * + * @param page + * the page of results to set + * + * @throw IndexOutOfBoundsException if the page is out of bounds + */ public void setPage(int page) { if (searchByTags) { searchTag(byTag.getCurrentTag(), page, 0); @@ -110,15 +164,11 @@ public class GuiReaderSearchByPanel extends JPanel { } } - // actions will be fired in UIthread - public void addActionListener(ActionListener action) { - actions.add(action); - } - - public boolean removeActionListener(ActionListener action) { - return actions.remove(action); - } - + /** + * The currently loaded stories (the result of the latest search). + * + * @return the stories + */ public List getStories() { if (!searchByTags) { return byName.getStories(); @@ -127,7 +177,14 @@ public class GuiReaderSearchByPanel extends JPanel { return byTag.getStories(); } - // selected item or 0 if none ! one-based ! + /** + * Return the currently selected story (the item) if it was + * specified in the latest, or 0 if not. + *

+ * Note: this is thus a 1-based index, not a 0-based index. + * + * @return the item + */ public int getStoryItem() { if (!searchByTags) { return byName.getStoryItem(); @@ -136,29 +193,13 @@ public class GuiReaderSearchByPanel extends JPanel { return byTag.getStoryItem(); } - private void fireAction() { - GuiReaderSearchFrame.inUi(new Runnable() { - @Override - public void run() { - ActionEvent ae = new ActionEvent(GuiReaderSearchByPanel.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); - } - } - } - }); - } - + /** + * Update the kind of searches to make: search by keywords or search by tags + * (it will impact what the user can see and interact with on the UI). + * + * @param byTag + * TRUE for tag-based searches, FALSE for keywords-based searches + */ private void updateSearchBy(final boolean byTag) { GuiReaderSearchFrame.inUi(new Runnable() { @Override @@ -172,41 +213,51 @@ public class GuiReaderSearchByPanel extends JPanel { }); } - // slow, start in UI mode + /** + * Search for the given terms on the currently selected searchable. This + * will cause {@link Waitable#fireEvent()} to be called if needed. + *

+ * 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(final String keywords, final int page, final int item) { - waitable.setWaiting(true); updateSearchBy(false); - new Thread(new Runnable() { - @Override - public void run() { - try { - byName.search(keywords, page, item); - fireAction(); - } finally { - waitable.setWaiting(false); - } - } - }).start(); + byName.search(keywords, page, item); + waitable.fireEvent(); } - // slow, start in UI mode - // tag: null = base tags + /** + * Search for the given tag on the currently selected searchable. This will + * cause {@link Waitable#fireEvent()} to be called if needed. + *

+ * If the tag contains children tags, those will be displayed so you can + * select them; if the tag is a leaf tag, the linked stories will be + * displayed. + *

+ * This operation can be long and should be run outside the UI thread. + * + * @param tag + * the tag to search for, or NULL for base tags + * @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 searchTag(final SearchableTag tag, final int page, final int item) { - waitable.setWaiting(true); updateSearchBy(true); - new Thread(new Runnable() { - @Override - public void run() { - try { - - byTag.searchTag(tag, page, item); - fireAction(); - } finally { - waitable.setWaiting(false); - } - } - }).start(); + byTag.searchTag(tag, page, item); + waitable.fireEvent(); } /** diff --git a/src/be/nikiroo/fanfix/reader/ui/GuiReaderSearchByTagPanel.java b/src/be/nikiroo/fanfix/reader/ui/GuiReaderSearchByTagPanel.java index aa00b8b..a654d16 100644 --- a/src/be/nikiroo/fanfix/reader/ui/GuiReaderSearchByTagPanel.java +++ b/src/be/nikiroo/fanfix/reader/ui/GuiReaderSearchByTagPanel.java @@ -16,8 +16,10 @@ import javax.swing.JPanel; 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 @@ -31,7 +33,7 @@ public class GuiReaderSearchByTagPanel extends JPanel { private static final long serialVersionUID = 1L; private BasicSearchable searchable; - private Runnable fireEvent; + private Waitable waitable; private SearchableTag currentTag; private JPanel tagBars; @@ -42,10 +44,10 @@ public class GuiReaderSearchByTagPanel extends JPanel { private List stories = new ArrayList(); private int storyItem; - public GuiReaderSearchByTagPanel(Runnable fireEvent) { + public GuiReaderSearchByTagPanel(Waitable waitable) { setLayout(new BorderLayout()); - this.fireEvent = fireEvent; + this.waitable = waitable; combos = new ArrayList(); page = 1; maxPage = -1; @@ -58,6 +60,8 @@ public class GuiReaderSearchByTagPanel extends JPanel { /** * The {@link BasicSearchable} object use for the searches themselves. *

+ * This operation can be long and should be run outside the UI thread. + *

* Can be NULL, but no searches will work. * * @param searchable @@ -72,29 +76,70 @@ public class GuiReaderSearchByTagPanel extends JPanel { updateTags(null); } + /** + * The currently displayed page of result for the current search (see the + * page parameter of + * {@link GuiReaderSearchByTagPanel#searchTag(SupportType, int, int, SearchableTag)} + * ). + * + * @return the currently displayed page of results + */ public int getPage() { return page; } + /** + * The number of pages of result for the current search (see the + * page parameter of + * {@link GuiReaderSearchByPanel#searchTag(SupportType, int, int, SearchableTag)} + * ). + *

+ * 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; } + /** + * Return the tag used for the current search. + * + * @return the tag (which can be NULL, for "base tags") + */ public SearchableTag getCurrentTag() { return currentTag; } + /** + * The currently loaded stories (the result of the latest search). + * + * @return the stories + */ public List getStories() { return stories; } - // selected item or 0 if none ! one-based ! + /** + * Return the currently selected story (the item) if it was + * specified in the latest, or 0 if not. + *

+ * Note: this is thus a 1-based index, not a 0-based index. + * + * @return the item + */ public int getStoryItem() { return storyItem; } - // update and reset the tagsbar - // can be NULL, for base tags + /** + * Update the tags displayed on screen and reset the tags bar. + *

+ * This operation can be long and should be run outside the UI thread. + * + * @param tag + * the tag to use, or NULL for base tags + */ private void updateTags(final SearchableTag tag) { final List parents = new ArrayList(); SearchableTag parent = (tag == null) ? null : tag; @@ -142,7 +187,16 @@ public class GuiReaderSearchByTagPanel extends JPanel { }); } - // must be quick and no thread change + /** + * Add a tags bar (do not remove possible previous ones). + *

+ * Will always add an "empty" (NULL) tag as first option. + * + * @param tags + * the tags to display + * @param selected + * the selected tag if any, or NULL for none + */ private void addTagBar(List tags, final SearchableTag selected) { tags.add(0, null); @@ -187,6 +241,22 @@ public class GuiReaderSearchByTagPanel extends JPanel { tagBars.add(combo); } + /** + * The action to do on {@link JComboBox} selection. + *

+ * The content of the action is: + *

    + *
  • Remove all tags bar below this one
  • + *
  • Load the subtags if any in anew tags bar
  • + *
  • Load the related stories if the tag was a leaf tag and notify the + * {@link Waitable} (via {@link Waitable#fireEvent()})
  • + *
+ * + * @param comboIndex + * the index of the related {@link JComboBox} + * + * @return the action + */ private ActionListener createComboTagAction(final int comboIndex) { return new ActionListener() { @Override @@ -209,31 +279,36 @@ public class GuiReaderSearchByTagPanel extends JPanel { new Thread(new Runnable() { @Override public void run() { - final List children = getChildrenForTag(tag); - if (children != null) { - GuiReaderSearchFrame.inUi(new Runnable() { - @Override - public void run() { - addTagBar(children, tag); + waitable.setWaiting(true); + try { + final List children = getChildrenForTag(tag); + if (children != null) { + GuiReaderSearchFrame.inUi(new Runnable() { + @Override + public void run() { + addTagBar(children, tag); + } + }); + } + + if (tag != null && tag.isLeaf()) { + storyItem = 0; + try { + searchable.fillTag(tag); + page = 1; + stories = searchable.search(tag, 1); + maxPage = searchable.searchPages(tag); + } catch (IOException e) { + GuiReaderSearchFrame.error(e); + page = 0; + maxPage = -1; + stories = new ArrayList(); } - }); - } - if (tag != null && tag.isLeaf()) { - storyItem = 0; - try { - searchable.fillTag(tag); - page = 1; - stories = searchable.search(tag, 1); - maxPage = searchable.searchPages(tag); - } catch (IOException e) { - GuiReaderSearchFrame.error(e); - page = 0; - maxPage = -1; - stories = new ArrayList(); + waitable.fireEvent(); } - - fireEvent.run(); + } finally { + waitable.setWaiting(false); } } }).start(); @@ -241,8 +316,19 @@ public class GuiReaderSearchByTagPanel extends JPanel { }; } - // sync, add children of tag, NULL = base tags - // return children of the tag or base tags or NULL + /** + * Get the children of the given tag (or the base tags if the given tag is + * NULL). + *

+ * This action will "fill" ({@link BasicSearchable#fillTag(SearchableTag)}) + * the given tag if needed first. + *

+ * This operation can be long and should be run outside the UI thread. + * + * @param tag + * the tag to search into or NULL for the base tags + * @return the children + */ private List getChildrenForTag(final SearchableTag tag) { List children = new ArrayList(); if (tag == null) { @@ -269,9 +355,24 @@ public class GuiReaderSearchByTagPanel extends JPanel { return children; } - // slow - // tag: null = base tags - // throw if page > max, but only if stories + /** + * Search for the given tag on the currently selected searchable. + *

+ * If the tag contains children tags, those will be displayed so you can + * select them; if the tag is a leaf tag, the linked stories will be + * displayed. + *

+ * This operation can be long and should be run outside the UI thread. + * + * @param tag + * the tag to search for, or NULL for base tags + * @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 searchTag(SearchableTag tag, int page, int item) { List stories = new ArrayList(); int storyItem = 0; diff --git a/src/be/nikiroo/fanfix/reader/ui/GuiReaderSearchFrame.java b/src/be/nikiroo/fanfix/reader/ui/GuiReaderSearchFrame.java index c67c735..11d45e4 100644 --- a/src/be/nikiroo/fanfix/reader/ui/GuiReaderSearchFrame.java +++ b/src/be/nikiroo/fanfix/reader/ui/GuiReaderSearchFrame.java @@ -34,11 +34,11 @@ public class GuiReaderSearchFrame extends JFrame { private static final long serialVersionUID = 1L; private List supportTypes; - private SupportType supportType; private int page; private int maxPage; private JComboBox comboSupportTypes; + private ActionListener comboSupportTypesListener; private GuiReaderSearchByPanel searchPanel; private boolean seeWordcount; @@ -53,59 +53,65 @@ public class GuiReaderSearchFrame extends JFrame { maxPage = -1; supportTypes = new ArrayList(); + supportTypes.add(null); for (SupportType type : SupportType.values()) { if (BasicSearchable.getSearchable(type) != null) { supportTypes.add(type); } } - supportType = supportTypes.isEmpty() ? null : supportTypes.get(0); comboSupportTypes = new JComboBox( supportTypes.toArray(new SupportType[] {})); - comboSupportTypes.addActionListener(new ActionListener() { + + comboSupportTypesListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { + final SupportType support = (SupportType) comboSupportTypes + .getSelectedItem(); setWaiting(true); - updateSupportType( - (SupportType) comboSupportTypes.getSelectedItem(), - new Runnable() { - @Override - public void run() { - setWaiting(false); - } - }); + new Thread(new Runnable() { + @Override + public void run() { + try { + updateSupportType(support); + } finally { + setWaiting(false); + } + } + }).start(); } - }); + }; + comboSupportTypes.addActionListener(comboSupportTypesListener); + JPanel searchSites = new JPanel(new BorderLayout()); searchSites.add(comboSupportTypes, BorderLayout.CENTER); searchSites.add(new JLabel(" " + "Website : "), BorderLayout.WEST); - searchPanel = new GuiReaderSearchByPanel(supportType, + searchPanel = new GuiReaderSearchByPanel( new GuiReaderSearchByPanel.Waitable() { @Override public void setWaiting(boolean waiting) { GuiReaderSearchFrame.this.setWaiting(waiting); } - }); - searchPanel.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - updatePages(searchPanel.getPage(), searchPanel.getMaxPage()); - List infos = new ArrayList(); - for (MetaData meta : searchPanel.getStories()) { - infos.add(GuiReaderBookInfo.fromMeta(meta)); - } - - updateBooks(infos); - - // ! 1-based index ! - int item = searchPanel.getStoryItem(); - if (item > 0 && item <= books.getBooksCount()) { - // TODO: "click" on item ITEM - } - } - }); + @Override + public void fireEvent() { + updatePages(searchPanel.getPage(), + searchPanel.getMaxPage()); + List infos = new ArrayList(); + for (MetaData meta : searchPanel.getStories()) { + infos.add(GuiReaderBookInfo.fromMeta(meta)); + } + + updateBooks(infos); + + // ! 1-based index ! + int item = searchPanel.getStoryItem(); + if (item > 0 && item <= books.getBooksCount()) { + books.setSelectedBook(item - 1, false); + } + } + }); JPanel top = new JPanel(new BorderLayout()); top.add(searchSites, BorderLayout.NORTH); @@ -135,21 +141,46 @@ public class GuiReaderSearchFrame extends JFrame { add(scroll, BorderLayout.CENTER); } - private void updateSupportType(final SupportType supportType, - final Runnable inUi) { - this.supportType = supportType; - comboSupportTypes.setSelectedItem(supportType); - books.clear(); - - new Thread(new Runnable() { + /** + * Update the {@link SupportType} currently displayed to the user. + *

+ * Will also cause a search for the new base tags of the given support if + * not NULL. + *

+ * This operation can be long and should be run outside the UI thread. + * + * @param supportType + * the new {@link SupportType} + */ + private void updateSupportType(final SupportType supportType) { + inUi(new Runnable() { @Override public void run() { - searchPanel.setSupportType(supportType); - inUi(inUi); + books.clear(); + + comboSupportTypes + .removeActionListener(comboSupportTypesListener); + comboSupportTypes.setSelectedItem(supportType); + comboSupportTypes.addActionListener(comboSupportTypesListener); + } - }).start(); + }); + + searchPanel.setSupportType(supportType); } + /** + * Update the pages and the lined buttons currently displayed on screen. + *

+ * Those are the same pages and maximum pages used by + * {@link GuiReaderSearchByPanel#search(String, int, int)} and + * {@link GuiReaderSearchByPanel#searchTag(SearchableTag, int, int)}. + * + * @param page + * the current page of results + * @param maxPage + * the maximum number of pages of results + */ private void updatePages(final int page, final int maxPage) { inUi(new Runnable() { @Override @@ -164,51 +195,79 @@ public class GuiReaderSearchFrame extends JFrame { }); } + /** + * Update the currently displayed books. + * + * @param infos + * the new books + */ private void updateBooks(final List infos) { - setWaiting(true); inUi(new Runnable() { @Override public void run() { books.refreshBooks(infos, seeWordcount); - setWaiting(false); } }); } - // item 0 = no selection, else = default selection + /** + * Search for the given terms on the currently selected searchable. This + * will update the displayed books if needed. + *

+ * This operation is asynchronous. + * + * @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) + */ public void search(final SupportType searchOn, final String keywords, final int page, final int item) { setWaiting(true); new Thread(new Runnable() { @Override public void run() { - searchPanel.setSupportType(searchOn); - searchPanel.search(keywords, page, item); - inUi(new Runnable() { - @Override - public void run() { - setWaiting(false); - } - }); + try { + updateSupportType(searchOn); + searchPanel.search(keywords, page, item); + } finally { + setWaiting(false); + } } }).start(); } - // tag: null = base tags + /** + * Search for the given tag on the currently selected searchable. This will + * update the displayed books if needed. + *

+ * If the tag contains children tags, those will be displayed so you can + * select them; if the tag is a leaf tag, the linked stories will be + * displayed. + *

+ * This operation is asynchronous. + * + * @param tag + * the tag to search for, or NULL for base tags + * @param page + * the page of results to load + * @param item + * the item to select (or 0 for none by default) + */ public void searchTag(final SupportType searchOn, final int page, final int item, final SearchableTag tag) { setWaiting(true); new Thread(new Runnable() { @Override public void run() { - searchPanel.setSupportType(searchOn); - searchPanel.searchTag(tag, page, item); - inUi(new Runnable() { - @Override - public void run() { - setWaiting(false); - } - }); + try { + updateSupportType(searchOn); + searchPanel.searchTag(tag, page, item); + } finally { + setWaiting(false); + } } }).start(); } @@ -238,10 +297,22 @@ public class GuiReaderSearchFrame extends JFrame { } } + /** + * An error occurred, inform the user and/or log the error. + * + * @param e + * the error + */ static void error(Exception e) { Instance.getTraceHandler().error(e); } + /** + * An error occurred, inform the user and/or log the error. + * + * @param e + * the error message + */ static void error(String e) { Instance.getTraceHandler().error(e); } @@ -264,6 +335,12 @@ public class GuiReaderSearchFrame extends JFrame { searchPanel.setEnabled(b); } + /** + * Set the item in wait mode, blocking it from accepting UI input. + * + * @param waiting + * TRUE for wait more, FALSE to restore normal mode + */ private void setWaiting(final boolean waiting) { inUi(new Runnable() { @Override