/* * RHQ Management Platform * Copyright (C) 2005-2012 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.coregui.client.searchbar; import java.util.List; import com.google.gwt.user.client.rpc.AsyncCallback; import com.smartgwt.client.data.DataSource; import com.smartgwt.client.data.fields.DataSourceTextField; import com.smartgwt.client.widgets.form.fields.events.KeyUpEvent; import com.smartgwt.client.widgets.grid.HoverCustomizer; import com.smartgwt.client.widgets.grid.ListGrid; import com.smartgwt.client.widgets.grid.ListGridRecord; import com.smartgwt.client.widgets.grid.events.RecordClickEvent; import org.rhq.core.domain.search.SearchSubsystem; import org.rhq.core.domain.search.SearchSuggestion; import org.rhq.coregui.client.CoreGUI; import org.rhq.coregui.client.util.Log; /** * This is the general search strategy implementation used for most searches. * These strategy classes operate by providing the standard functionality * needed by searches as defined by the super class {@link AbstractSearchStrategy} * This strategy works in conjunction with the {@link FavoritesSearchStrategy} for * saved searches. * @author Mike Thompson */ public class BasicSearchStrategy extends AbstractSearchStrategy { private boolean isSearchInProgress = false; private String pendingSearchExpression = null; public BasicSearchStrategy(EnhancedSearchBar searchBar) { super(searchBar); } @Override public String format(Object value, ListGridRecord record, int rowNum, int colNum) { String name = record.getAttribute(ATTR_NAME); String kind = record.getAttribute(ATTR_KIND); String style = "font-variant: small-caps; font-weight: bold; font-size: 11px; float: left; margin-left: 2px; width: 50px;"; StringBuilder sb = new StringBuilder(); sb.append("<div style='height:20px;width:400px;float:left;white-space:nowrap;overflow:hidden;' >"); String color = (kind.equals(SearchSuggestion.Kind.GlobalSavedSearch.getDisplayName()) || kind .equals(SearchSuggestion.Kind.UserSavedSearch.getDisplayName())) ? "color:green;" : "color:grey"; sb.append("<span style='" + style + color + "' >"); sb.append(kind); sb.append("</span>"); sb.append("<span style='" + style + "' >"); sb.append(name); sb.append("</span>"); sb.append("</div>"); return sb.toString(); } @Override public int getCellHeight() { return 20; } /** * Executed when this field is clicked on. Note that if {@link * com.smartgwt.client.widgets.grid.ListGrid#addRecordClickHandler ListGrid.recordClick} is also defined, it will be fired * for fields that define a recordClick handler if the field-level handler returns true. Call {@link com.smartgwt.client.widgets.grid.events.RecordClickEvent#cancel()} from within {@link com.smartgwt.client.widgets.grid.events.RecordClickHandler#onRecordClick} to prevent the * grid-level handler from firing. * * @param event the event */ @Override public void onRecordClick(RecordClickEvent event) { String kind = event.getRecord().getAttribute(ATTR_KIND); String searchExpression; if (kind.equals("SAVED") || kind.equals("GLOBAL")) { searchExpression = event.getRecord().getAttribute(ATTR_PATTERN); } else { searchExpression = event.getRecord().getAttribute(ATTR_NAME); } searchBar.getSearchTextItem().focusInItem(); if (!(null == searchExpression || searchExpression.isEmpty())) { searchBar.getSearchTextItem().setValue(searchExpression); doSearch(searchExpression); } } @Override public void searchFocusHandler() { String searchExpression = searchBar.getSearchTextItem().getValueAsString(); doSearch(searchExpression); } @Override public void searchKeyUpHandler(KeyUpEvent keyUpEvent) { String searchExpression = searchBar.getSearchTextItem().getValueAsString(); doSearch(searchExpression); } @Override public void searchReturnKeyHandler(KeyUpEvent keyUpEvent) { searchBar.getPickListGrid().hide(); } private void doSearch(String searchExpression) { if (isSearchInProgress) { pendingSearchExpression = (null == searchExpression) ? "" : searchExpression; return; } isSearchInProgress = true; if (null == searchExpression || searchExpression.isEmpty()) { getSearchSuggestions(SearchSubsystem.RESOURCE, null, 0); } else { getSearchSuggestions(SearchSubsystem.RESOURCE, searchBar.getSearchTextItem().getValueAsString(), searchBar .getSearchTextItem().getValueAsString().length()); } } private void getSearchSuggestions(final SearchSubsystem searchSubsystem, final String expression, int caretPosition) { final long suggestStart = System.currentTimeMillis(); searchService.getTabAwareSuggestions(searchSubsystem, expression, caretPosition, null, new AsyncCallback<List<SearchSuggestion>>() { @Override public void onSuccess(List<SearchSuggestion> results) { try { ListGrid searchBarPickListGrid = searchBar.getPickListGrid(); DataSource ds = searchBarPickListGrid.getDataSource(); // create the datasource if needed if (null == ds) { ds = new DataSource(); ds.setClientOnly(true); DataSourceTextField idField = new DataSourceTextField(ATTR_ID, "Id"); idField.setPrimaryKey(true); idField.setCanView(false); DataSourceTextField valueField = new DataSourceTextField(ATTR_VALUE, "Value"); ds.setFields(idField, valueField); searchBarPickListGrid.setDataSource(ds); searchBarPickListGrid.getField(ATTR_VALUE).setShowHover(true); searchBarPickListGrid.getField(ATTR_VALUE).setHoverCustomizer(new HoverCustomizer() { public String hoverHTML(Object value, ListGridRecord record, int rowNum, int colNum) { if (null == record) { return ""; } String kind = record.getAttribute(ATTR_KIND); if (kind.equals("SAVED") || kind.equals("GLOBAL")) { String pattern = record.getAttribute(ATTR_PATTERN); if (!(null == pattern || pattern.isEmpty())) { return pattern; } } return ""; } }); } else { ds.invalidateCache(); } for (SearchSuggestion searchSuggestion : results) { Log.debug("search tab aware Suggestions: " + searchSuggestion.getKind() + ", " + searchSuggestion.getValue() + ", " + searchSuggestion.getLabel()); ListGridRecord record = new ListGridRecord(); record.setAttribute(ATTR_ID, searchSuggestion.getValue()); if (null != searchSuggestion.getKind()) { record.setAttribute(ATTR_KIND, searchSuggestion.getKind().getDisplayName()); } record.setAttribute(ATTR_NAME, searchSuggestion.getLabel()); record.setAttribute(ATTR_VALUE, searchSuggestion.getValue()); String pattern = searchSuggestion.getOptional(); record.setAttribute(ATTR_PATTERN, (null == pattern) ? "" : pattern); ds.addData(record); } try { searchBarPickListGrid.setData(new ListGridRecord[] {}); searchBarPickListGrid.fetchData(); } catch (Exception e) { Log.info("Caught exception on fetchData: " + e); } long suggestFetchTime = System.currentTimeMillis() - suggestStart; Log.debug(results.size() + " suggestions searches fetched in: " + suggestFetchTime + "ms"); } finally { // By the time we're done it's possible the results are already obsolete. If so, kick // off another search. Log.debug("Search End"); if (null == pendingSearchExpression) { isSearchInProgress = false; } else { Log.debug("Performing pending search [" + pendingSearchExpression + "]"); String searchExpression = pendingSearchExpression; pendingSearchExpression = null; isSearchInProgress = false; doSearch(searchExpression); } } } @Override public void onFailure(Throwable caught) { isSearchInProgress = false; pendingSearchExpression = null; CoreGUI.getErrorHandler().handleError(MSG.view_searchBar_suggest_failSuggest(), caught); } }); } }