/*************************************************** * * cismet GmbH, Saarbruecken, Germany * * ... and it just works. * ****************************************************/ /* * SearchControlPanel.java * * Created on 13.12.2011, 10:36:40 */ package Sirius.navigator.search.dynamic; import Sirius.navigator.search.CidsSearchExecutor; import Sirius.navigator.ui.ComponentRegistry; import Sirius.server.middleware.types.Node; import org.apache.log4j.Logger; import org.jdesktop.swingx.JXErrorPane; import org.jdesktop.swingx.error.ErrorInfo; import java.awt.Dimension; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.net.URL; import java.util.Collection; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.ImageIcon; import javax.swing.SwingWorker; import de.cismet.cids.server.search.MetaObjectNodeServerSearch; /** * DOCUMENT ME! * * @author jweintraut * @version $Revision$, $Date$ */ public class SearchControlPanel extends javax.swing.JPanel implements PropertyChangeListener { //~ Static fields/initializers --------------------------------------------- private static final Logger LOG = Logger.getLogger(SearchControlPanel.class); //~ Instance fields -------------------------------------------------------- private SearchControlListener listener; private SwingWorker<Node[], Void> searchThread; private SwingWorker<Boolean, Void> searchPreparationThread; private boolean searching = false; private ImageIcon iconSearch; private ImageIcon iconCancel; private boolean simpleSort; // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton btnSearchCancel; private org.jdesktop.swingx.JXBusyLabel lblBusyIcon; private javax.swing.Box.Filler strGap; // End of variables declaration//GEN-END:variables //~ Constructors ----------------------------------------------------------- /** * Creates new form SearchControlPanel. * * @param listener DOCUMENT ME! */ public SearchControlPanel(final SearchControlListener listener) { if (listener == null) { LOG.warn("Given listener is null. Panel won't work."); } this.listener = listener; initComponents(); final URL iconSearch = getClass().getResource( "/Sirius/navigator/search/dynamic/SearchControlPanel_btnSearchCancel.png"); if (iconSearch != null) { this.iconSearch = new ImageIcon(iconSearch); } else { this.iconSearch = new ImageIcon(); } final URL iconCancel = getClass().getResource( "/Sirius/navigator/search/dynamic/SearchControlPanel_btnSearchCancel_cancel.png"); if (iconCancel != null) { this.iconCancel = new ImageIcon(iconCancel); } else { this.iconCancel = new ImageIcon(); } btnSearchCancel.setIcon(this.iconSearch); } //~ Methods ---------------------------------------------------------------- /** * This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The * content of this method is always regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { lblBusyIcon = new org.jdesktop.swingx.JXBusyLabel(new java.awt.Dimension(20, 20)); strGap = new javax.swing.Box.Filler(new java.awt.Dimension(5, 0), new java.awt.Dimension(5, 25), new java.awt.Dimension(5, 32767)); btnSearchCancel = new javax.swing.JButton(); setMinimumSize(new java.awt.Dimension(125, 25)); setPreferredSize(new java.awt.Dimension(125, 25)); setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.TRAILING, 0, 0)); lblBusyIcon.setEnabled(false); add(lblBusyIcon); add(strGap); btnSearchCancel.setText(org.openide.util.NbBundle.getMessage( SearchControlPanel.class, "SearchControlPanel.btnSearchCancel.text")); // NOI18N btnSearchCancel.setToolTipText(org.openide.util.NbBundle.getMessage( SearchControlPanel.class, "SearchControlPanel.btnSearchCancel.toolTipText")); // NOI18N btnSearchCancel.setMaximumSize(new java.awt.Dimension( 100, (new Double(getMaximumSize().getHeight()).intValue()))); btnSearchCancel.setMinimumSize(new java.awt.Dimension( 100, (new Double(getMinimumSize().getHeight()).intValue()))); btnSearchCancel.setPreferredSize(new java.awt.Dimension( 100, (new Double(getPreferredSize().getHeight()).intValue()))); btnSearchCancel.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(final java.awt.event.ActionEvent evt) { btnSearchCancelActionPerformed(evt); } }); add(btnSearchCancel); } // </editor-fold>//GEN-END:initComponents /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ private void btnSearchCancelActionPerformed(final java.awt.event.ActionEvent evt) { //GEN-FIRST:event_btnSearchCancelActionPerformed if (LOG.isInfoEnabled()) { LOG.info((searching ? "Cancel" : "Search") + " button was clicked."); } if (searching) { if (listener == null) { LOG.error("Search should be started, but listener is null."); return; } if (searchThread != null) { searchThread.cancel(true); } if (searchPreparationThread != null) { searchPreparationThread.cancel(true); } ComponentRegistry.getRegistry().getSearchResultsTree().cancelNodeLoading(); } else { if (listener == null) { LOG.error("Search should be started, but listener is null."); return; } final MetaObjectNodeServerSearch search = listener.assembleSearch(); if (search == null) { LOG.warn("The listener didn't provide a search."); return; } searching = true; setControlsAccordingToState(); listener.searchStarted(); searchPreparationThread = new SwingWorker<Boolean, Void>() { @Override protected Boolean doInBackground() throws Exception { return SearchControlPanel.this.checkIfSearchShouldBeStarted(this, search); } @Override protected void done() { boolean startSearch = false; try { startSearch = get(); } catch (InterruptedException ex) { LOG.error("Could not start search.", ex); } catch (ExecutionException ex) { LOG.error("Could not start search.", ex); } catch (CancellationException ex) { LOG.info("Search cancelled.", ex); } if (startSearch) { ComponentRegistry.getRegistry() .getSearchResultsTree() .addPropertyChangeListener("browse", SearchControlPanel.this); searchThread = CidsSearchExecutor.searchAndDisplayResults( search, SearchControlPanel.this, SearchControlPanel.this, listener.suppressEmptyResultMessage(), simpleSort); } else { searching = false; setControlsAccordingToState(); listener.searchCanceled(); } } }; searchPreparationThread.execute(); } } //GEN-LAST:event_btnSearchCancelActionPerformed /** * This method is called before the search is actually started and gives a possibility to abort the search. In the * default implementation it always returns true, but subclasses can override this method. * * <p>Note: the method is called in the doInBackground() of a SwingWorker and therefor not in the EDT</p> * * @param calledBySwingWorker the SwingWorker instance by which this method was called, to check e.g. if the * SwingWorker was canceled * @param search the search, which will be started or aborted later on * * @return true: the search will be started. False: the search will be aborted */ public boolean checkIfSearchShouldBeStarted(final SwingWorker calledBySwingWorker, final MetaObjectNodeServerSearch search) { return true; } /** * DOCUMENT ME! */ private void setControlsAccordingToState() { if (searching) { btnSearchCancel.setText(org.openide.util.NbBundle.getMessage( SearchControlPanel.class, "SearchControlPanel.btnSearchCancel_cancel.text")); // NOI18N btnSearchCancel.setToolTipText(org.openide.util.NbBundle.getMessage( SearchControlPanel.class, "SearchControlPanel.btnSearchCancel_cancel.toolTipText")); // NOI18N btnSearchCancel.setIcon(iconCancel); lblBusyIcon.setEnabled(true); lblBusyIcon.setBusy(true); } else { btnSearchCancel.setText(org.openide.util.NbBundle.getMessage( SearchControlPanel.class, "SearchControlPanel.btnSearchCancel.text")); // NOI18N btnSearchCancel.setToolTipText(org.openide.util.NbBundle.getMessage( SearchControlPanel.class, "SearchControlPanel.btnSearchCancel.toolTipText")); // NOI18N btnSearchCancel.setIcon(iconSearch); lblBusyIcon.setEnabled(false); lblBusyIcon.setBusy(false); } } @Override public void propertyChange(final PropertyChangeEvent evt) { if (!(evt.getSource() instanceof SwingWorker)) { LOG.warn("Listened object is not of type 'SwingWorker'. Skipping process of event: '" + evt + "'."); return; } if (!"state".equalsIgnoreCase(evt.getPropertyName())) { return; } if (listener == null) { LOG.error("Got an event from a search thread but listener is null. Skip processing."); return; } final SwingWorker source = (SwingWorker)evt.getSource(); if (SwingWorker.StateValue.DONE.equals(evt.getNewValue())) { if (source.isCancelled()) { searching = false; listener.searchCanceled(); setControlsAccordingToState(); } else { int results = 0; try { final Object obj = source.get(); if (obj instanceof Node[]) { results = ((Node[])obj).length; } if (obj instanceof Collection) { results = ((Collection)obj).size(); } } catch (InterruptedException ex) { LOG.error("Search result can't be get().", ex); } catch (ExecutionException ex) { LOG.error("Search result can't be get().", ex); final ErrorInfo errorInfo = new ErrorInfo( org.openide.util.NbBundle.getMessage( SearchControlPanel.class, "SearchControlPanel.propertyChange(PropertyChangeEvent).JOptionPane_anon.title"), org.openide.util.NbBundle.getMessage( SearchControlPanel.class, "SearchControlPanel.propertyChange(PropertyChangeEvent).JOptionPane_anon.message"), null, "ERROR", ex.getCause(), Level.WARNING, null); JXErrorPane.showDialog(getRootPane(), errorInfo); } // SearchControlPanel is used as listener of the search thread and as a listener for the thread which // refreshes the SearchResultsTree. So this point is reached by on of two conditions: // - The search thread is done. // - Refreshing the SearchResultsTree is done. // SearchControlPanel can display normal mode only if: // - Search is done and has no results (refreshing the SearchResultsTree is not started). // - Or refreshing SearchResultsTree is done. if ((source.equals(searchThread) && (results == 0)) || !source.equals(searchThread)) { searching = false; listener.searchDone(results); setControlsAccordingToState(); } } } } @Override public void setEnabled(final boolean enabled) { super.setEnabled(enabled); lblBusyIcon.setEnabled(searching); btnSearchCancel.setEnabled(enabled); } /** * DOCUMENT ME! */ public void startSearch() { startSearch(false); } /** * DOCUMENT ME! * * @param simpleSort if true, sorts the search results alphabetically. Usually set to false, as a more specific * sorting order is wished. */ public void startSearch(final boolean simpleSort) { if (LOG.isInfoEnabled()) { LOG.info("Start search programmatically."); } this.simpleSort = simpleSort; btnSearchCancel.doClick(); this.simpleSort = false; } @Override public void setMaximumSize(final Dimension maximumSize) { btnSearchCancel.setMaximumSize(new java.awt.Dimension(100, (new Double(maximumSize.getHeight()).intValue()))); super.setMaximumSize(maximumSize); } @Override public void setMinimumSize(final Dimension minimumSize) { btnSearchCancel.setMinimumSize(new java.awt.Dimension(100, (new Double(minimumSize.getHeight()).intValue()))); super.setMinimumSize(minimumSize); } @Override public void setPreferredSize(final Dimension preferredSize) { btnSearchCancel.setPreferredSize(new java.awt.Dimension( 100, (new Double(preferredSize.getHeight()).intValue()))); super.setPreferredSize(preferredSize); } }