blob: c88de40bdb85bc866a78f8cbda7a81fdf8f53bfe [file] [log] [blame]
package net.sf.taverna.biocatalogue.ui.search_results;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.net.URI;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.ListCellRenderer;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import net.sf.taverna.biocatalogue.model.BioCataloguePluginConstants;
import net.sf.taverna.biocatalogue.model.LoadingExpandedResource;
import net.sf.taverna.biocatalogue.model.LoadingResource;
import net.sf.taverna.biocatalogue.model.Resource;
import net.sf.taverna.biocatalogue.model.Resource.TYPE;
import net.sf.taverna.biocatalogue.model.ResourceManager;
import net.sf.taverna.biocatalogue.model.Util;
import net.sf.taverna.biocatalogue.model.connectivity.BioCatalogueClient;
import net.sf.taverna.biocatalogue.model.search.SearchInstance;
import net.sf.taverna.biocatalogue.ui.JWaitDialog;
import net.sf.taverna.t2.lang.ui.ModelMap;
import net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponent;
import net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponentFactory;
import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.Integration;
import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.health_check.ServiceHealthChecker;
import net.sf.taverna.t2.workbench.MainWindow;
import net.sf.taverna.t2.workbench.ModelMapConstants;
import net.sf.taverna.t2.workbench.ui.Workbench;
import net.sf.taverna.t2.workbench.ui.zaria.PerspectiveSPI;
import org.apache.log4j.Logger;
import org.biocatalogue.x2009.xml.rest.ResourceLink;
import org.biocatalogue.x2009.xml.rest.RestMethod;
import org.biocatalogue.x2009.xml.rest.Service;
import org.biocatalogue.x2009.xml.rest.ServiceTechnologyType;
/**
* This class is responsible for producing search results listing panel. It only
* shows a single listing for a specified type. Multiple types are handled by
* having different tabs in {@link SearchResultsMainPanel} with instances of
* this class in each.
*
* @author Sergejs Aleksejevs
*/
@SuppressWarnings("serial")
public class SearchResultsListingPanel extends JPanel implements MouseListener,
SearchResultsRenderer, MouseMotionListener {
public static final int SEARCH_STATUS_TOOLTIP_LINE_LENGTH = 65;
private static final Logger logger = Logger.getLogger(SearchResultsListingPanel.class);
private final SearchResultsMainPanel parentMainSearchResultsPanel;
// currently displayed search results
SearchInstance searchInstance;
// main UI components
private SearchResultsListingPanel thisPanel;
private DefaultListModel resultsListingModel;
private JList jlResultsListing;
private JScrollPane spResultsListing;
// contextual menu
private JPopupMenu contextualMenu;
private Action addToServicePanelAction;
private Action addToWorkflowDiagramAction;
private Action openInBioCatalogueAction;
private Action doHealthCheckAction;
private Action addAllOperationsToServicePanelAction;
// search status and actions on selected items in the list
private JToolBar tbSelectedItemActions;
protected JPanel jpSearchStatus;
private JLabel jlSearchStatus;
// this is used for previewing items from the result listing through
// contextual menu -
// value will be updated by mouse event accordingly
private ResourceLink potentialObjectToPreview;
private final TYPE typeToPreview;
// Design perspective - some actions require switching to it
PerspectiveSPI designPerspective;
private ListCellRenderer listingCellRenderer;
/**
* @param typeToPreview
* Resource type that will be previewed in this panel.
* @param parentMainSearchResultsPanel
* Reference to a "parent" of this panel - this is needed to
* notify the main results panel with the
*/
public SearchResultsListingPanel(TYPE typeToPreview,
SearchResultsMainPanel parentMainSearchResultsPanel) {
this.thisPanel = this;
this.typeToPreview = typeToPreview;
listingCellRenderer = this.typeToPreview
.getResultListingCellRenderer();
this.parentMainSearchResultsPanel = parentMainSearchResultsPanel;
MainComponentFactory
.getSharedInstance();
initialiseUI();
this.setPreferredSize(new Dimension(800, 400));
}
private void initialiseUI() {
this.addToServicePanelAction = new AbstractAction(
"Add to Service Panel",
ResourceManager
.getImageIcon(ResourceManager.ADD_PROCESSOR_AS_FAVOURITE_ICON)) {
// Tooltip
{
this.putValue(SHORT_DESCRIPTION, "Add selected "
+ typeToPreview.getTypeName()
+ " to the Service Panel");
}
public void actionPerformed(ActionEvent e) {
final JWaitDialog jwd = new JWaitDialog(
MainComponent.dummyOwnerJFrame,
"Service Catalogue Plugin - Adding "
+ typeToPreview.getTypeName(),
"<html><center>Please wait for selected "
+ typeToPreview.getTypeName()
+ " details to be fetched from the Service Catalogue<br>"
+ "and to be added into the Service Panel.</center></html>");
new Thread("Adding " + typeToPreview.getTypeName()
+ " into Service Panel") {
public void run() {
// if it is the expanded that we are looking at, need to extract
// the 'associated' object
ResourceLink processorResourceToAdd = (potentialObjectToPreview instanceof LoadingExpandedResource ? ((LoadingExpandedResource) potentialObjectToPreview)
.getAssociatedObj()
: potentialObjectToPreview);
JComponent insertionOutcome = Integration
.insertProcesorIntoServicePanel(processorResourceToAdd);
jwd.waitFinished(insertionOutcome);
// Switch to Design Perspective
switchToDesignPerspective();
}
}.start();
// NB! The modal dialog window needs to be made visible after
// the background
// process (i.e. adding a processor) has already been started!
jwd.setVisible(true);
}
};
// For a parent Web service, action to add all operations to the Service Panel.
// Works for SOAP services at the moment.
this.addAllOperationsToServicePanelAction = new AbstractAction(
"Add all operations to Service Panel",
ResourceManager
.getImageIcon(ResourceManager.ADD_ALL_SERVICES_AS_FAVOURITE_ICON)) {
// Tooltip
{
this.putValue(SHORT_DESCRIPTION, "Add all associated services to the Service Panel");
}
public void actionPerformed(ActionEvent e) {
final JWaitDialog jwd = new JWaitDialog(
MainComponent.dummyOwnerJFrame,
"Service Catalogue Plugin - Adding "
+ typeToPreview.getTypeName(),
"<html><center>Please wait for selected "
+ typeToPreview.getTypeName()
+ " details to be fetched from the Service Catalogue<br>"
+ "and to be added into the Service Panel.</center></html>");
new Thread("Adding all operations of " + typeToPreview.getTypeName()
+ " to the Service Panel") {
public void run() {
// if it is the expanded that we are looking at, need to extract
// the 'associated' object
ResourceLink resourceLink = (potentialObjectToPreview instanceof LoadingExpandedResource ? ((LoadingExpandedResource) potentialObjectToPreview)
.getAssociatedObj()
: potentialObjectToPreview);
JComponent insertionOutcome = Integration
.insertAllOperationsIntoServicePanel(resourceLink);
jwd.waitFinished(insertionOutcome);
// Switch to Design Perspective
switchToDesignPerspective();
}
}.start();
// NB! The modal dialog window needs to be made visible after
// the background
// process (i.e. adding a processor) has already been started!
jwd.setVisible(true);
}
};
this.addToWorkflowDiagramAction = new AbstractAction(
"Add to workflow",
ResourceManager
.getImageIcon(ResourceManager.ADD_PROCESSOR_TO_WORKFLOW_ICON)) {
// Tooltip
{
this.putValue(SHORT_DESCRIPTION, "<html>Insert selected "
+ typeToPreview.getTypeName()
+ " into the current workflow</html>");
}
public void actionPerformed(ActionEvent e) {
final JWaitDialog jwd = new JWaitDialog(
MainComponent.dummyOwnerJFrame,
"Service Catalogue Plugin - Adding "
+ typeToPreview.getTypeName(),
"<html><center>Please wait for selected "
+ typeToPreview.getTypeName()
+ " details to be fetched from the Service Catalogue<br>"
+ "and to be added into the current workflow.</center></html>");
new Thread("Adding " + typeToPreview.getTypeName()
+ " into workflow") {
public void run() {
// if it is the expanded that we are looking at, need to extract
// the 'associated' object
ResourceLink processorResourceToAdd = (potentialObjectToPreview instanceof LoadingExpandedResource ? ((LoadingExpandedResource) potentialObjectToPreview)
.getAssociatedObj()
: potentialObjectToPreview);
JComponent insertionOutcome = Integration
.insertProcessorIntoCurrentWorkflow(processorResourceToAdd);
jwd.waitFinished(insertionOutcome);
// Switch to Design Perspective
switchToDesignPerspective();
}
}.start();
// NB! The modal dialog window needs to be made visible after
// the background
// process (i.e. adding a processor) has already been started!
jwd.setVisible(true);
}
};
this.openInBioCatalogueAction = new AbstractAction(
"Open in the Service Catalogue",
ResourceManager
.getImageIcon(ResourceManager.OPEN_IN_BIOCATALOGUE_ICON)) {
// Tooltip
{
this.putValue(SHORT_DESCRIPTION, "<html>View selected "
+ typeToPreview.getTypeName()
+ " on the Service Catalogue Web site.<br>"
+ "This will open your standard Web browser.</html>");
}
public void actionPerformed(ActionEvent e) {
String hrefString = potentialObjectToPreview.getHref();
try {
Desktop.getDesktop().browse(new URI(hrefString));
}
catch (Exception ex) {
logger.error("Failed while trying to open the URL in a standard browser; URL was: " +
hrefString + "\nException was: " + ex + "\n" + ex.getStackTrace());
};
}
};
this.doHealthCheckAction = new AbstractAction(
"Check monitoring status",
ResourceManager
.getImageIcon(ResourceManager.EXECUTE_HEALTH_CHECK_ICON)) {
// Tooltip
{
this
.putValue(
SHORT_DESCRIPTION,
"<html>Fetch the latest monitoring data for selected "
+ typeToPreview.getTypeName()
+ ".<br>"
+ "Data will be obtained from the Service Catalogue and displayed in a popup window.</html>");
}
public void actionPerformed(ActionEvent e) {
// if it is the expanded that we are looking at, need to extract
// the 'associated' object
ResourceLink resourceForHealthCheck = (potentialObjectToPreview instanceof LoadingExpandedResource ? ((LoadingExpandedResource) potentialObjectToPreview)
.getAssociatedObj()
: potentialObjectToPreview);
ServiceHealthChecker.checkResource(resourceForHealthCheck);
}
};
tbSelectedItemActions = new JToolBar(JToolBar.HORIZONTAL);
tbSelectedItemActions.setBorderPainted(true);
tbSelectedItemActions.setBorder(BorderFactory.createEmptyBorder(5, 5,
5, 3));
tbSelectedItemActions.setFloatable(false);
if (typeToPreview.isSuitableForAddingToServicePanel()) {
tbSelectedItemActions.add(addToServicePanelAction);
}
if (typeToPreview.isSuitableForAddingAllToServicePanel()) {
tbSelectedItemActions.add(addAllOperationsToServicePanelAction);
}
if (typeToPreview.isSuitableForAddingToWorkflowDiagram()) {
tbSelectedItemActions.add(addToWorkflowDiagramAction);
}
if (typeToPreview.isSuitableForHealthCheck()) {
tbSelectedItemActions.add(doHealthCheckAction);
}
tbSelectedItemActions.add(openInBioCatalogueAction);
// *** Prepare search results status panel ***
GridBagConstraints c = new GridBagConstraints();
jpSearchStatus = new JPanel(new GridBagLayout());
c.anchor = GridBagConstraints.WEST;
c.weightx = 0;
jpSearchStatus.add(tbSelectedItemActions, c);
jlSearchStatus = new JLabel();
jlSearchStatus.setIconTextGap(20);
c.weightx = 1.0;
c.insets = new Insets(0, 20, 0, 0);
jpSearchStatus.add(jlSearchStatus, c);
if (parentMainSearchResultsPanel.getFilterTreePaneFor(typeToPreview) != null) {
Dimension preferredSize = new Dimension(200,
parentMainSearchResultsPanel.getFilterTreePaneFor(
typeToPreview).getTreeToolbarPreferredSize().height);
// HACK: due to concurrency issues, sometimes this doesn't work
// correctly -
// to rectify the problem using the hard-coded value that was
// correct at
// the time of coding...
if (preferredSize.height < 30) {
preferredSize.height = 33;
}
jpSearchStatus.setPreferredSize(preferredSize);
}
// *** Create list to hold search results and wrap it into a scroll pane
// ***
resultsListingModel = new DefaultListModel();
jlResultsListing = new JList(resultsListingModel);
jlResultsListing.setDoubleBuffered(true);
jlResultsListing.setCellRenderer(listingCellRenderer);
jlResultsListing.addMouseListener(this);
jlResultsListing.addMouseMotionListener(this);
jlResultsListing.setBackground(thisPanel.getBackground());
jlResultsListing.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
jlResultsListing.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
// update value to be used in contextual menu click handler
// to act on the just-selected entry
potentialObjectToPreview = getResourceSelectedInJList();
if (potentialObjectToPreview != null) {
// only enable actions in the menu if the list entry
// that is being
// clicked on is beyond the initial 'loading' state
boolean shown = !isListEntryOnlyWithInitialDetails(potentialObjectToPreview);
boolean shownAndNotArchived = shown && !isArchived(potentialObjectToPreview);
addToServicePanelAction
.setEnabled(shownAndNotArchived);
addAllOperationsToServicePanelAction
.setEnabled(shownAndNotArchived && !(potentialObjectToPreview instanceof RestMethod));
addToWorkflowDiagramAction
.setEnabled(shownAndNotArchived);
openInBioCatalogueAction
.setEnabled(shown);
doHealthCheckAction
.setEnabled(shown);
return;
}
}
// disable actions if nothing is selected in the list or if
// selection is still "adjusting"
addToServicePanelAction.setEnabled(false);
addAllOperationsToServicePanelAction.setEnabled(false);
addToWorkflowDiagramAction.setEnabled(false);
openInBioCatalogueAction.setEnabled(false);
doHealthCheckAction.setEnabled(false);
}
});
spResultsListing = new JScrollPane(jlResultsListing);
spResultsListing.getVerticalScrollBar().addAdjustmentListener(
new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
if (!e.getValueIsAdjusting()) {
// load missing details on adjusting the scroll bar
//
// only start loading more results in case if the
// value is "not adjusting" -
// this means that the mouse has been released and
// is not dragging the scroll bar
// any more, so effectively the user has stopped
// scrolling
checkAllEntriesInTheVisiblePartOfJListAreLoaded();
}
}
});
// tie components to the class panel itself
this.resetSearchResultsListing(true);
// *** Create CONTEXTUAL MENU ***
contextualMenu = new JPopupMenu();
if (typeToPreview.isSuitableForAddingToServicePanel()) {
contextualMenu.add(addToServicePanelAction);
contextualMenu.add(addAllOperationsToServicePanelAction);
}
if (typeToPreview.isSuitableForAddingToWorkflowDiagram()) {
contextualMenu.add(addToWorkflowDiagramAction);
}
if (typeToPreview.isSuitableForHealthCheck()) {
contextualMenu.add(doHealthCheckAction);
}
contextualMenu.add(openInBioCatalogueAction);
}
/**
* Allows to set the search status by supplying the message to display.
*/
protected void setSearchStatusText(final String statusString,
final boolean spinnerActive) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
jlSearchStatus
.setIcon(spinnerActive ? ResourceManager
.getImageIcon(ResourceManager.BAR_LOADER_ORANGE)
: null);
jlSearchStatus.setText(statusString);
jlSearchStatus.setToolTipText("<html>"
+ Util.ensureLineLengthWithinString(statusString,
SEARCH_STATUS_TOOLTIP_LINE_LENGTH, false)
+ "</html>");
}
});
}
/**
* This helper method is used to initialise this panel. Also invoked when
* search results need to be cleared.
*
* @param showSuggestion
* <code>true</code> should be used on first load of the panel -
* in that case a suggestion would be displayed to perform a
* search, tag search or start directly with filtering;<br/>
* <code>false</code> to be used when resetting the panel after
* perfoming the search, but not finding any results.
*/
public void resetSearchResultsListing(boolean showSuggestion) {
setSearchStatusText("No searches were made yet", false);
String labelText = "<html><center>"
+ (showSuggestion ? "You can find "
+ this.typeToPreview.getCollectionName()
+ " by typing a search query."
+ (this.typeToPreview.isSuitableForFiltering() ? "<br><br>Alternatively, you can select some filters from the tree on the left."
: "")
: "There are no "
+ this.typeToPreview.getCollectionName()
+ " that match your search criteria<br><br>"
+ "Please try making the search query shorter or selecting fewer filters")
+ "</center></html>";
JLabel jlMainLabel = new JLabel(labelText, JLabel.CENTER);
jlMainLabel.setFont(jlMainLabel.getFont().deriveFont(Font.PLAIN, 16));
jlMainLabel.setBorder(BorderFactory.createEtchedBorder());
this.removeAll();
this.setLayout(new BorderLayout(0, 0));
this.add(jpSearchStatus, BorderLayout.NORTH);
this.add(jlMainLabel, BorderLayout.CENTER);
this.validate();
// disable the toolbar actions
this.addToServicePanelAction.setEnabled(false);
this.addToWorkflowDiagramAction.setEnabled(false);
this.openInBioCatalogueAction.setEnabled(false);
this.doHealthCheckAction.setEnabled(false);
this.addAllOperationsToServicePanelAction.setEnabled(false);
}
/**
* Statistics will be rendered along with the collection of found items.
*
* @param searchInstance
* SearchInstance containing search results to render.
*/
public void renderResults(SearchInstance searchInstance) {
// make the current search instance available globally within this class
this.searchInstance = searchInstance;
// stop spinner icon on the tab that is populated and add number of
// results
parentMainSearchResultsPanel.setDefaultIconForTab(typeToPreview);
parentMainSearchResultsPanel.setDefaultTitleForTabWithSuffix(
typeToPreview, " ("
+ searchInstance.getSearchResults()
.getTotalMatchingItemCount() + ")");
// if nothing was found - display notification and finish result
// processing
if (searchInstance.getSearchResults().getTotalMatchingItemCount() == 0) {
resetSearchResultsListing(false);
// must happen after resetting the listing, as it replaces the
// default status text
setSearchStatusText("No results found for "
+ searchInstance.getDescriptionStringForSearchStatus(),
false);
return;
}
// populate results
if (searchInstance.getSearchResults().getTotalMatchingItemCount() > 0) {
// populate the list box with users
List<? extends ResourceLink> foundItems = searchInstance
.getSearchResults().getFoundItems();
for (ResourceLink item : foundItems) {
resultsListingModel.addElement(item);
}
}
// update the UI once contents are ready
thisPanel.removeAll();
thisPanel.setLayout(new BorderLayout(0, 0));
thisPanel.add(jpSearchStatus, BorderLayout.NORTH);
thisPanel.add(spResultsListing, BorderLayout.CENTER);
thisPanel.repaint();
// automatically start loading details for the first section of result
// listing
SwingUtilities.invokeLater(new Runnable() {
public void run() {
checkAllEntriesInTheVisiblePartOfJListAreLoaded();
}
});
// *** Also update status text ***
setSearchStatusText("Search results for "
+ searchInstance.getDescriptionStringForSearchStatus(), false);
}
/**
* Check if details are fetched for all result entries that are currently
* visible in the JList.
*
* If some are not yet loaded, identifies the page in the index of
* corresponding resources to fetch details.
*
* When done, recursively calls itself again to verify that no more entries
* need further details loaded.
*/
private void checkAllEntriesInTheVisiblePartOfJListAreLoaded() {
int firstVisibleIndex = jlResultsListing.getFirstVisibleIndex();
if (firstVisibleIndex >= 0) {
int lastVisibleIndex = jlResultsListing.getLastVisibleIndex();
final int firstNotFetchedMatchingItemIndex = searchInstance
.getSearchResults().getFirstMatchingItemIndexNotYetFetched(
firstVisibleIndex, lastVisibleIndex);
final int pageToFetchNumber = searchInstance.getSearchResults()
.getMatchingItemPageNumberFor(
firstNotFetchedMatchingItemIndex);
// check if found a valid page to load
if (pageToFetchNumber != -1) {
int numberOfResourcesPerPageForThisResourceType = searchInstance
.getSearchResults().getTypeOfResourcesInTheResultSet()
.getApiResourceCountPerIndexPage();
int firstListIndexToLoad = searchInstance.getSearchResults()
.getFirstItemIndexOn(pageToFetchNumber); // first
// element
// on the
// page that
// is about
// to be
// loaded
int countToLoad = Math.min(
numberOfResourcesPerPageForThisResourceType, // if the
// last
// page
// isn't
// full,
// need
// to
// mark
// less
// items
// than
// the
// full
// page
resultsListingModel.getSize() - firstListIndexToLoad);
// mark the next "page" of items in the JList as "loading" -
// but also mark them in the SearchResults backing list, so
// that next calls to this listener are aware of the previous
// items that were marked as "loading"
for (int i = firstListIndexToLoad; i < firstListIndexToLoad
+ countToLoad; i++) {
((LoadingResource) searchInstance.getSearchResults()
.getFoundItems().get(i)).setLoading(true);
}
// update the UI to show 'loading' state on relevant entries
renderFurtherResults(searchInstance, firstListIndexToLoad,
countToLoad);
// now start loading data for the 'loading' entries
final CountDownLatch latch = new CountDownLatch(1);
new Thread("Search via the API") {
public void run() {
try {
searchInstance.fetchMoreResults(
parentMainSearchResultsPanel, latch,
thisPanel, pageToFetchNumber);
} catch (Exception e) {
logger.error("Error while searching via the Service Catalogue API", e);
}
}
}.start();
// wait for the previous portion of results to load, then fetch
// the next portion
new Thread(
"Fetch more another page of details for search results") {
public void run() {
try {
latch.await();
checkAllEntriesInTheVisiblePartOfJListAreLoaded();
} catch (InterruptedException e) {
logger
.error(
"Failed to wait for the previous page of results to load to check if "
+ "another one needs loading as well. Details in the attache exception.",
e);
}
}
}.start();
}
}
}
/**
* Tests whether {@link ResourceLink} object corresponding to an entry in
* the search results list is in the state where only the first (initial)
* fragment of data was loaded (through BioCatalogue LITE JSON API) that
* contains just the title + URL of the resource.
*
* @param resource
* @return
*/
private boolean isListEntryOnlyWithInitialDetails(ResourceLink resource) {
return (resource instanceof LoadingResource);
}
private boolean isArchived(ResourceLink resource) {
if (listingCellRenderer instanceof ExpandableOnDemandLoadedListCellRenderer) {
ExpandableOnDemandLoadedListCellRenderer r = (ExpandableOnDemandLoadedListCellRenderer) listingCellRenderer;
return r.shouldBeHidden(resource);
}
return false;
}
// ***** Callbacks for MouseListener *****
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) { /* NOT IN USE */
}
public void mouseExited(MouseEvent e) { /* NOT IN USE */
}
public void mousePressed(MouseEvent e) {
// checked in both mousePressed() & mouseReleased() for cross-platform
// operation
maybeShowPopupMenu(e);
}
public void mouseReleased(MouseEvent e) {
// checked in both mousePressed() & mouseReleased() for cross-platform
// operation
maybeShowPopupMenu(e);
}
// ***** Callbacks for MouseMotionListener *****
public void mouseMoved(MouseEvent e) {
}
public void mouseDragged(MouseEvent e) { /* do nothing */
}
/**
* Gets the selected object from the specified list. Used for previewing
* items through double-clicks and contextual menu.
*
* @return <code>null</code> if no selection in the list,
* <code>ResourceLink</code> object that is currently selected
* otherwise.
*/
private ResourceLink getResourceSelectedInJList() {
return (jlResultsListing.getSelectedIndex() == -1 ? null
: (ResourceLink) jlResultsListing.getSelectedValue());
}
private void maybeShowPopupMenu(MouseEvent e) {
if (e.getSource().equals(jlResultsListing) && e.isPopupTrigger()
&& jlResultsListing.locationToIndex(e.getPoint()) != -1) {
// select the entry in the list that triggered the event to show
// this popup menu
jlResultsListing.setSelectedIndex(jlResultsListing
.locationToIndex(e.getPoint()));
// update value to be used in contextual menu click handler to act
// on the just-selected entry
potentialObjectToPreview = getResourceSelectedInJList();
// show the contextual menu
this.contextualMenu.show(e.getComponent(), e.getX(), e.getY());
}
}
// *** Callbacks for SearchResultsRenderer ***
public void renderInitialResults(final SearchInstance si) {
// NB! critical to have UI update done within the invokeLater()
// method - this is to prevent UI from 'flashing' and to
// avoid concurrency-related errors
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// make sure to remove any old results from the list model!
resultsListingModel.clear();
// display the partial search results
logger.debug("Started rendering initial search results for "
+ si.getResourceTypeToSearchFor().getCollectionName());
renderResults(si);
logger.debug("Finished rendering initial search results for "
+ si.getResourceTypeToSearchFor().getCollectionName());
}
});
}
public void renderFurtherResults(SearchInstance si, int startIndex,
int count) {
renderFurtherResults(si, startIndex, count, false);
}
public void renderFurtherResults(final SearchInstance si,
final int startIndex, final int count,
final boolean disableListDataListeners) {
logger.debug("Started rendering further search results for "
+ si.getResourceTypeToSearchFor().getCollectionName());
// NB! very important to remove all listeners here, so that the JList
// won't "freeze"
// on updating the components
ListDataListener[] listeners = null;
if (disableListDataListeners) {
listeners = resultsListingModel.getListDataListeners();
for (ListDataListener listener : listeners) {
resultsListingModel.removeListDataListener(listener);
}
}
for (int i = startIndex; i < startIndex + count
&& i < resultsListingModel.getSize(); i++) {
resultsListingModel.set(i, searchInstance.getSearchResults()
.getFoundItems().get(i));
}
// reset all listeners in case they were removed
if (disableListDataListeners) {
for (ListDataListener listener : listeners) {
resultsListingModel.addListDataListener(listener);
}
}
// NB! critical to have UI update done within the invokeLater()
// method - this is to prevent UI from 'flashing' and to
// avoid some weird errors
SwingUtilities.invokeLater(new Runnable() {
public void run() {
jlResultsListing.validate();
jlResultsListing.repaint();
logger.debug("Finished rendering further search results for "
+ si.getResourceTypeToSearchFor().getCollectionName());
}
});
}
private void switchToDesignPerspective() {
if (designPerspective == null) {
for (PerspectiveSPI perspective : Workbench.getInstance()
.getPerspectives().getPerspectives()) {
if (perspective.getText().equalsIgnoreCase("design")) {
designPerspective = perspective;
break;
}
}
}
if (designPerspective != null) {
ModelMap.getInstance().setModel(
ModelMapConstants.CURRENT_PERSPECTIVE, designPerspective);
}
}
}