/*
* Copyright (C) 2012 Jan Pokorsky
*
* 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
package cz.cas.lib.proarc.webapp.client.widget;
import com.smartgwt.client.data.Criteria;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.i18n.SmartGwtMessages;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.types.SelectionStyle;
import com.smartgwt.client.types.SelectionType;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.IconButton;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.FormItemIfFunction;
import com.smartgwt.client.widgets.form.events.SubmitValuesEvent;
import com.smartgwt.client.widgets.form.events.SubmitValuesHandler;
import com.smartgwt.client.widgets.form.fields.FormItem;
import com.smartgwt.client.widgets.form.fields.RadioGroupItem;
import com.smartgwt.client.widgets.form.fields.SelectItem;
import com.smartgwt.client.widgets.form.fields.SpacerItem;
import com.smartgwt.client.widgets.form.fields.SubmitItem;
import com.smartgwt.client.widgets.form.fields.TextItem;
import com.smartgwt.client.widgets.form.validator.LengthRangeValidator;
import com.smartgwt.client.widgets.form.validator.RequiredIfFunction;
import com.smartgwt.client.widgets.form.validator.RequiredIfValidator;
import com.smartgwt.client.widgets.grid.CellFormatter;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.events.DataArrivedEvent;
import com.smartgwt.client.widgets.grid.events.DataArrivedHandler;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.toolbar.ToolStrip;
import cz.cas.lib.proarc.webapp.client.ClientMessages;
import cz.cas.lib.proarc.webapp.client.ClientUtils;
import cz.cas.lib.proarc.webapp.client.Editor;
import cz.cas.lib.proarc.webapp.client.action.Actions;
import cz.cas.lib.proarc.webapp.client.action.RefreshAction;
import cz.cas.lib.proarc.webapp.client.action.Selectable;
import cz.cas.lib.proarc.webapp.client.ds.SearchDataSource;
import cz.cas.lib.proarc.webapp.shared.rest.DigitalObjectResourceApi;
import cz.cas.lib.proarc.webapp.shared.rest.DigitalObjectResourceApi.SearchType;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.logging.Logger;
/**
* Shows list of digital objects.
*
* @author Jan Pokorsky
*/
public final class DigitalObjectSearchView implements Selectable<Record>, RefreshAction.Refreshable {
private static final Logger LOG = Logger.getLogger(DigitalObjectSearchView.class.getName());
private static final String FILTER_LAST_CREATED = SearchType.LAST_CREATED.toString();
private static final String FILTER_LAST_MODIFIED = SearchType.LAST_MODIFIED.toString();
private static final String FILTER_PHRASE = SearchType.PHRASE.toString();
private static final String FILTER_QUERY = SearchType.QUERY.toString();
private final DynamicForm filters;
private final Canvas rootWidget;
private final ListGrid foundGrid;
private final ClientMessages i18n;
private final SmartGwtMessages i18nSmartGwt;
private final ToolStrip toolbar;
public DigitalObjectSearchView(ClientMessages i18n) {
this.i18n = i18n;
this.i18nSmartGwt = ClientUtils.createSmartGwtMessages();
foundGrid = createList();
filters = createFilter();
VLayout vLayout = new VLayout();
vLayout.addMember(filters);
toolbar = createToolbar();
vLayout.addMember(toolbar);
vLayout.addMember(foundGrid);
rootWidget = vLayout;
}
private ListGrid createList() {
final ListGrid grid = new ListGrid();
grid.addDataArrivedHandler(new DataArrivedHandler() {
@Override
public void onDataArrived(DataArrivedEvent event) {
int startRow = event.getStartRow();
int endRow = event.getEndRow();
if (startRow == 0 && endRow >= 0) {
grid.focus();
grid.selectSingleRecord(0);
} else if (endRow < 0) {
grid.deselectAllRecords();
}
}
});
grid.setSelectionType(SelectionStyle.SINGLE);
grid.setCanSort(false);
grid.setDataSource(SearchDataSource.getInstance());
ListGridField label = new ListGridField(SearchDataSource.FIELD_LABEL,
i18n.DigitalObjectSearchView_ListHeaderLabel_Title());
ListGridField model = new ListGridField(SearchDataSource.FIELD_MODEL,
i18n.DigitalObjectSearchView_ListHeaderModel_Title(), 150);
model.setAlign(Alignment.CENTER);
ListGridField pid = new ListGridField(SearchDataSource.FIELD_PID,
i18n.DigitalObjectSearchView_ListHeaderPid_Title(), 100);
pid.setAlign(Alignment.CENTER);
ListGridField created = new ListGridField(SearchDataSource.FIELD_CREATED,
i18n.DigitalObjectSearchView_ListHeaderCreated_Title(), 100);
created.setAlign(Alignment.CENTER);
ListGridField modified = new ListGridField(SearchDataSource.FIELD_MODIFIED,
i18n.DigitalObjectSearchView_ListHeaderModified_Title(), 100);
modified.setAlign(Alignment.CENTER);
ListGridField owner = new ListGridField(SearchDataSource.FIELD_OWNER,
i18n.DigitalObjectSearchView_ListHeaderOwner_Title(), 100);
ListGridField state = new ListGridField(SearchDataSource.FIELD_STATE,
i18n.DigitalObjectSearchView_ListHeaderState_Title(), 100);
state.setHidden(true);
ListGridField export = new ListGridField(SearchDataSource.FIELD_EXPORT,
i18n.DigitalObjectSearchView_ListHeaderExport_Title(), 100);
export.setCellFormatter(new CellFormatter() {
@Override
public String format(Object value, ListGridRecord record, int rowNum, int colNum) {
return value == null || "0".equals(value)
? i18nSmartGwt.dialog_NoButtonTitle()
: i18nSmartGwt.dialog_YesButtonTitle();
}
});
grid.setFields(label, model, pid, created, modified, owner, state, export);
grid.setContextMenu(Actions.createMenu());
return grid;
}
private ToolStrip createToolbar() {
ToolStrip toolbar = Actions.createToolStrip();
IconButton btnFilter = new IconButton();
btnFilter.setActionType(SelectionType.CHECKBOX);
btnFilter.setIcon("[SKIN]/actions/filter.png");
btnFilter.setTitle(i18n.DigitalObjectSearchView_FilterButton_Title());
btnFilter.setTooltip(i18n.DigitalObjectSearchView_FilterButton_Hint());
btnFilter.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if (filters.isVisible()) {
filters.hide();
} else {
filters.show();
}
}
});
btnFilter.setSelected(filters.isVisible());
toolbar.addMember(btnFilter);
return toolbar;
}
private DynamicForm createFilter() {
DynamicForm form = new DynamicForm();
form.setBrowserSpellCheck(false);
form.setValidateOnExit(true);
form.setSaveOnEnter(true);
form.setAutoHeight();
form.setWidth100();
form.setNumCols(3);
final RadioGroupItem filterType = new RadioGroupItem(DigitalObjectResourceApi.SEARCH_TYPE_PARAM);
filterType.setVertical(false);
filterType.setShowTitle(false);
filterType.setWrap(false);
// setRedrawOnChange(true) enforces evaluation of other FormItem.setShowIfCondition
filterType.setRedrawOnChange(true);
filterType.setColSpan(2);
final LinkedHashMap<String, String> filterMap = new LinkedHashMap<String, String>();
filterMap.put(FILTER_LAST_CREATED, i18n.DigitalObjectSearchView_FilterGroupLastCreated_Title());
filterMap.put(FILTER_LAST_MODIFIED, i18n.DigitalObjectSearchView_FilterGroupLastModified_Title());
if (!Editor.getInstance().hasPermission("proarc.permission.repository.search.groupOwner")) {
filterMap.put(FILTER_PHRASE, i18n.DigitalObjectSearchView_FilterGroupPhrase_Title());
}
filterMap.put(FILTER_QUERY, i18n.DigitalObjectSearchView_FilterGroupAdvanced_Title());
filterType.setValueMap(filterMap);
filterType.setValue(FILTER_LAST_CREATED);
FormItemIfFunction showIfAdvanced = new StringMatchFunction(filterType, FILTER_QUERY);
FormItemIfFunction showIfPhrase = new StringMatchFunction(filterType, FILTER_PHRASE);
final TextItem phrase = createAdvancedItem(DigitalObjectResourceApi.SEARCH_PHRASE_PARAM,
i18n.DigitalObjectSearchView_FilterPhrase_Title(), showIfPhrase);
phrase.setValidators(new RequiredIfValidator(new RequiredIfFunction() {
@Override
public boolean execute(FormItem formItem, Object value) {
return FILTER_PHRASE.equals(filterType.getValueAsString());
}
}), new LengthRangeValidator() {{
setMax(1000);
}});
SubmitItem submit = new SubmitItem("search", i18n.DigitalObjectSearchView_FilterSearchButton_Title());
form.setFields(filterType, new SpacerItem() {{setWidth("100%");}},
phrase,
createAdvancedItem(DigitalObjectResourceApi.SEARCH_QUERY_TITLE_PARAM,
i18n.DigitalObjectSearchView_FilterAdvancedTitle_Title(), showIfAdvanced),
createAdvancedItem(DigitalObjectResourceApi.SEARCH_QUERY_IDENTIFIER_PARAM,
i18n.DigitalObjectSearchView_FilterAdvancedIdentifier_Title(), showIfAdvanced),
createAdvancedItem(DigitalObjectResourceApi.SEARCH_QUERY_CREATOR_PARAM,
i18n.DigitalObjectSearchView_FilterAdvancedCreator_Title(), showIfAdvanced),
createAdvancedItem(DigitalObjectResourceApi.SEARCH_QUERY_LABEL_PARAM,
i18n.DigitalObjectSearchView_FilterAdvancedLabel_Title(), showIfAdvanced),
createAdvancedItem(DigitalObjectResourceApi.SEARCH_OWNER_PARAM,
i18n.DigitalObjectSearchView_FilterAdvancedOwner_Title(), showIfAdvanced),
createModelItem(i18n.DigitalObjectSearchView_FilterAdvancedModel_Title(),
new StringMatchFunction(filterType, FILTER_LAST_CREATED, FILTER_LAST_MODIFIED, FILTER_QUERY)),
submit);
form.addSubmitValuesHandler(new SubmitValuesHandler() {
@Override
public void onSubmitValues(SubmitValuesEvent event) {
if (filters.validate(false)) {
filter();
}
}
});
return form;
}
private static TextItem createAdvancedItem(String name, String title, FormItemIfFunction showIf) {
TextItem item = new TextItem(name, title);
if (showIf != null) {
item.setShowIfCondition(showIf);
}
item.setWidth("100%");
item.setValidators(new LengthRangeValidator() {{ setMax(1000); }});
return item;
}
private SelectItem createModelItem(String title, FormItemIfFunction showIf) {
SelectItem item = new SelectItem(DigitalObjectResourceApi.SEARCH_QUERY_MODEL_PARAM, title);
item.setWidth(300);
item.setAllowEmptyValue(true);
item.setEmptyDisplayValue("<i>" + i18nSmartGwt.filterBuilder_matchAllTitle() + "</i>");
// see setFilterModel
// item.setValue("model:periodical");
// item.setDefaultToFirstOption(true);
if (showIf != null) {
item.setShowIfCondition(showIf);
}
return item;
}
public Canvas asWidget() {
return rootWidget;
}
public ListGrid getGrid() {
return foundGrid;
}
public ToolStrip getToolbar() {
return toolbar;
}
public void onShow() {
// fetch values after fetching models (setFilterModel, setModels).
}
public void setFilterModel(Object modelId) {
FormItem filterModel = filters.getField(DigitalObjectResourceApi.SEARCH_QUERY_MODEL_PARAM);
filterModel.setValue(modelId);
}
public void setModels(LinkedHashMap<?, ?> valueMap) {
ListGridField field = foundGrid.getField(SearchDataSource.FIELD_MODEL);
field.setValueMap(valueMap);
FormItem filterModel = filters.getField(DigitalObjectResourceApi.SEARCH_QUERY_MODEL_PARAM);
filterModel.setValueMap(valueMap);
}
@Override
public Record[] getSelection() {
ListGridRecord[] selections = foundGrid.getSelectedRecords();
return selections;
}
@Override
public void refresh() {
foundGrid.invalidateCache();
filter();
}
private void filter() {
Criteria valuesAsCriteria = filters.getValuesAsCriteria();
foundGrid.deselectAllRecords();
foundGrid.fetchData(valuesAsCriteria);
}
private static final class StringMatchFunction implements FormItemIfFunction {
private final HashSet<String> patterns;
private final FormItem item2query;
public StringMatchFunction(FormItem item2query, String... patterns) {
this.patterns = new HashSet<String>(Arrays.asList(patterns));
this.item2query = item2query;
}
@Override
public boolean execute(FormItem item, Object value, DynamicForm form) {
Object itemValue = item2query.getValue();
return itemValue != null && patterns.contains(itemValue.toString());
}
}
}