package be.nikiroo.fanfix.reader.ui; import java.awt.BorderLayout; import java.util.List; import javax.swing.JPanel; import javax.swing.JTabbedPane; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import be.nikiroo.fanfix.data.MetaData; 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 * searches. * * @author niki */ public class GuiReaderSearchByPanel extends JPanel { private static final long serialVersionUID = 1L; private Waitable waitable; private boolean searchByTags; private JTabbedPane searchTabs; private GuiReaderSearchByNamePanel byName; private GuiReaderSearchByTagPanel byTag; /** * 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(); } /** * 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; byName = new GuiReaderSearchByNamePanel(waitable); byTag = new GuiReaderSearchByTagPanel(waitable); searchTabs = new JTabbedPane(); searchTabs.addTab("By name", byName); searchTabs.addTab("By tags", byTag); searchTabs.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { searchByTags = (searchTabs.getSelectedComponent() == byTag); } }); add(searchTabs, BorderLayout.CENTER); updateSearchBy(searchByTags); } /** * 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 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(); } 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(); } return byTag.getMaxPage(); } /** * 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);
} else {
search(byName.getCurrentKeywords(), page, 0);
}
}
/**
* The currently loaded stories (the result of the latest search).
*
* @return the stories
*/
public List
* Note: this is thus a 1-based index, not a 0-based index.
*
* @return the item
*/
public int getStoryItem() {
if (!searchByTags) {
return byName.getStoryItem();
}
return byTag.getStoryItem();
}
/**
* 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
public void run() {
if (!byTag) {
searchTabs.setSelectedIndex(0);
} else {
searchTabs.setSelectedIndex(1);
}
}
});
}
/**
* 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) {
updateSearchBy(false);
byName.search(keywords, page, item);
waitable.fireEvent();
}
/**
* 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) {
updateSearchBy(true);
byTag.searchTag(tag, page, item);
waitable.fireEvent();
}
/**
* Enables or disables this component, depending on the value of the
* parameter
* Disabling this component will also affect its children.
*
* @param b
* If b
. An enabled component can respond to user input
* and generate events. Components are enabled initially by default.
* true
, this component is enabled; otherwise
* this component is disabled
*/
@Override
public void setEnabled(boolean b) {
super.setEnabled(b);
searchTabs.setEnabled(b);
byName.setEnabled(b);
byTag.setEnabled(b);
}
}