package com.constellio.app.ui.pages.search; import com.constellio.app.ui.application.ConstellioUI; import com.constellio.app.ui.entities.FacetVO; import com.constellio.app.ui.entities.FacetValueVO; import com.constellio.app.ui.entities.MetadataVO; import com.constellio.app.ui.framework.buttons.BaseButton; import com.constellio.app.ui.framework.buttons.LabelsButton.RecordSelector; import com.constellio.app.ui.framework.buttons.SelectDeselectAllButton; import com.constellio.app.ui.framework.buttons.WindowButton; import com.constellio.app.ui.framework.components.*; import com.constellio.app.ui.framework.components.ReportViewer.DownloadStreamResource; import com.constellio.app.ui.framework.components.fields.BaseTextField; import com.constellio.app.ui.framework.containers.SearchResultContainer; import com.constellio.app.ui.framework.containers.SearchResultVOLazyContainer; import com.constellio.app.ui.pages.base.BaseViewImpl; import com.constellio.app.ui.pages.search.SearchPresenter.SortOrder; import com.constellio.data.utils.KeySetMap; import com.jensjansson.pagedtable.PagedTable.PagedTableChangeEvent; import com.vaadin.data.Property; import com.vaadin.data.Property.ValueChangeEvent; import com.vaadin.data.Property.ValueChangeListener; import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; import com.vaadin.ui.AbstractSelect.ItemCaptionMode; import com.vaadin.ui.*; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.Table.ColumnHeaderMode; import com.vaadin.ui.themes.ValoTheme; import java.util.Arrays; import java.util.List; import java.util.Set; import static com.constellio.app.ui.i18n.i18n.$; public abstract class SearchViewImpl<T extends SearchPresenter<? extends SearchView>> extends BaseViewImpl implements SearchView, RecordSelector { public static final String SUGGESTION_STYLE = "spell-checker-suggestion"; public static final String FACET_BOX_STYLE = "facet-box"; public static final String FACET_TITLE_STYLE = "facet-title"; public static final String SORT_BOX_STYLE = "sort-box"; public static final String SORT_TITLE_STYLE = "sort-title"; public static final String SAVE_SEARCH = "save-search"; protected T presenter; private CssLayout suggestions; private VerticalLayout summary; private VerticalLayout resultsArea; private VerticalLayout facetsArea; private SearchResultTable results; private SelectDeselectAllButton selectDeselectAllButton; private Button addToSelectionButton; @Override protected boolean isFullWidthIfActionMenuAbsent() { return true; } @Override protected String getTitle() { return null; } @Override protected void initBeforeCreateComponents(ViewChangeEvent event) { presenter.resetFacetSelection(); presenter.forRequestParameters(event.getParameters()); } @Override protected Component buildMainComponent(ViewChangeEvent event) { VerticalLayout layout = new VerticalLayout(buildSearchUI(), buildResultsUI()); layout.addStyleName("search-main-container"); layout.setSpacing(true); if (presenter.mustDisplayResults()) { refreshSearchResultsAndFacets(false); } return layout; } @Override public void setSearchExpression(String expression) { ConstellioUI.getCurrent().getHeader().setSearchExpression(expression); } public void refreshSearchResultsAndFacets(boolean temporarySave) { refreshSearchResults(temporarySave); refreshFacets(); } @Override public void refreshSearchResultsAndFacets() { refreshSearchResults(true); refreshFacets(); } @Override public void refreshSearchResults(boolean temporarySave) { if (temporarySave) { presenter.saveTemporarySearch(false); } suggestions.removeAllComponents(); buildSuggestions(); results = buildResultTable(); summary.removeAllComponents(); summary.addComponent(buildSummary(results)); if (isDetailedView()) { resultsArea.removeAllComponents(); resultsArea.addComponents(results, ((SearchResultDetailedTable) results).createControls()); ((SearchResultDetailedTable) results).setItemsPerPageValue(presenter.getSelectedPageLength()); } else { resultsArea.removeAllComponents(); resultsArea.addComponent(results); } } private boolean isDetailedView() { return !SearchResultsViewMode.TABLE.equals(presenter.getResultsViewMode()); } @Override public void refreshFacets() { facetsArea.removeAllComponents(); addFacetComponents(facetsArea); presenter.setPageNumber(1); } @Override public List<String> getSelectedRecordIds() { return results.getSelectedRecordIds(); } @Override public List<String> getUnselectedRecordIds() { return results.getUnselectedRecordIds(); } protected abstract Component buildSearchUI(); protected Component buildSummary(SearchResultTable results) { List<Component> actions = Arrays.asList( buildSelectAllButton(), buildAddToSelectionButton(), buildSavedSearchButton(), (Component) new ReportSelector(presenter)); Component zipButton = new Link($("ReportViewer.download", "(zip)"), new DownloadStreamResource(presenter.getZippedContents(), presenter.getZippedContentsFilename())); zipButton.addStyleName(ValoTheme.BUTTON_LINK); return results.createSummary(actions, zipButton); } private Component buildResultsUI() { suggestions = new CssLayout(); suggestions.addStyleName("spell-checker"); summary = new VerticalLayout(); summary.addStyleName("search-result-summary"); resultsArea = new VerticalLayout(); resultsArea.addStyleName("search-result-area"); // resultsArea.setWidth("100%"); resultsArea.setSpacing(true); facetsArea = new VerticalLayout(); facetsArea.addStyleName("search-result-facets"); facetsArea.setWidth("300px"); facetsArea.setSpacing(true); HorizontalLayout body = new HorizontalLayout(resultsArea, facetsArea); body.addStyleName("search-result-and-facets-container"); body.setWidth("100%"); body.setExpandRatio(resultsArea, 1); body.setSpacing(true); VerticalLayout main = new VerticalLayout(suggestions, summary, body); main.addStyleName("suggestions-summary-results-facets"); main.setWidth("100%"); main.setSpacing(true); return main; } protected SearchResultTable buildResultTable() { return buildDetailedResultsTable(); } protected SearchResultTable buildDetailedResultsTable() { SearchResultContainer container = buildResultContainer(); SearchResultDetailedTable srTable = new SearchResultDetailedTable(container); int totalResults = container.size(); int totalAmountOfPages = srTable.getTotalAmountOfPages(); int currentPage = presenter.getPageNumber(); int selectedPageLength = presenter.getSelectedPageLength(); if (selectedPageLength == 0) { selectedPageLength = Math.min(totalResults, SearchResultDetailedTable.DEFAULT_PAGE_LENGTH); } presenter.setSelectedPageLength(selectedPageLength); srTable.setPageLength(selectedPageLength); srTable.setItemsPerPageValue(selectedPageLength); srTable.setCurrentPage(currentPage); srTable.addListener(new SearchResultDetailedTable.PageChangeListener() { public void pageChanged(PagedTableChangeEvent event) { presenter.setPageNumber(event.getCurrentPage()); presenter.saveTemporarySearch(false); if (selectDeselectAllButton != null) { selectDeselectAllButton.setSelectAllMode(true); } } }); srTable.getItemsPerPageField().addValueChangeListener(new ValueChangeListener() { @Override public void valueChange(Property.ValueChangeEvent event) { presenter.setSelectedPageLength((int) event.getProperty().getValue()); } }); srTable.setWidth("100%"); return srTable; } protected SearchResultContainer buildResultContainer() { RecordDisplayFactory displayFactory = new RecordDisplayFactory(getSessionContext().getCurrentUser()); SearchResultVOLazyContainer results = new SearchResultVOLazyContainer(presenter.getSearchResults()); return new SearchResultContainer(results, displayFactory); } @SuppressWarnings("unchecked") private void buildSuggestions() { if (!presenter.mustDisplaySuggestions()) { suggestions.setVisible(false); return; } Label caption = new Label($("SearchView.spellChecker")); caption.addStyleName(ValoTheme.LABEL_BOLD); suggestions.addComponent(caption); List<String> foundSuggestions = presenter.getSuggestions(); for (final String suggestion : foundSuggestions) { Button activateSuggestion = new Button(suggestion); activateSuggestion.addStyleName(ValoTheme.BUTTON_LINK); activateSuggestion.addStyleName(SUGGESTION_STYLE); activateSuggestion.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { presenter.suggestionSelected(suggestion); } }); suggestions.addComponent(activateSuggestion); } suggestions.setVisible(true); } private Component buildSortComponent() { Label sortBy = new Label($("SearchView.sortBy")); sortBy.addStyleName(ValoTheme.LABEL_BOLD); sortBy.addStyleName(SORT_TITLE_STYLE); final ComboBox criterion = new ComboBox(); criterion.setItemCaptionMode(ItemCaptionMode.EXPLICIT); criterion.setWidth("100%"); List<MetadataVO> sortableMetadata = presenter.getMetadataAllowedInSort(); for (MetadataVO metadata : sortableMetadata) { criterion.addItem(metadata.getCode()); criterion.setItemCaption(metadata.getCode(), metadata.getLabel()); } // criterion.setPageLength(criterion.size()); criterion.setValue(presenter.getSortCriterionValueAmong(sortableMetadata)); final OptionGroup order = new OptionGroup(); order.addItem(SortOrder.ASCENDING); order.setItemCaption(SortOrder.ASCENDING, $("SearchView.sortAsc")); order.addItem(SortOrder.DESCENDING); order.setItemCaption(SortOrder.DESCENDING, $("SearchView.sortDesc")); order.setValue(presenter.getSortOrder()); ValueChangeListener listener = new ValueChangeListener() { @Override public void valueChange(ValueChangeEvent event) { String sortCriterion = (String) criterion.getValue(); SortOrder sortOrder = (SortOrder) order.getValue(); presenter.sortCriterionSelected(sortCriterion, sortOrder); } }; criterion.addValueChangeListener(listener); order.addValueChangeListener(listener); VerticalLayout inner = new VerticalLayout(criterion, order); inner.setWidth("100%"); inner.addStyleName("sort-box-content"); VerticalLayout layout = new VerticalLayout(sortBy, inner); layout.setWidth("95%"); layout.addStyleName(SORT_BOX_STYLE); return layout; } @SuppressWarnings("unchecked") private void addFacetComponents(ComponentContainer container) { container.addComponent(buildSortComponent()); List<FacetVO> facets = presenter.getFacets(); KeySetMap<String, String> facetSelections = presenter.getFacetSelections(); for (FacetVO facet : facets) { container.addComponent(buildFacetComponent(facet, facetSelections.get(facet.getId()))); } } private Component buildFacetComponent(final FacetVO facet, Set<String> selectedFacetValues) { CheckBox deselect = new CheckBox(); deselect.setValue(!selectedFacetValues.isEmpty()); deselect.setEnabled(!selectedFacetValues.isEmpty()); deselect.addValueChangeListener(new ValueChangeListener() { @Override public void valueChange(ValueChangeEvent event) { presenter.facetDeselected(facet.getId()); } }); Label title = new Label(facet.getLabel()); title.addStyleName(ValoTheme.LABEL_BOLD); final Button toggle = new Button(facet.isOpen() ? "—" : "+"); toggle.addStyleName(ValoTheme.BUTTON_TINY); toggle.addStyleName(ValoTheme.BUTTON_BORDERLESS); HorizontalLayout captionBar = new HorizontalLayout(deselect, title, toggle); captionBar.setComponentAlignment(deselect, Alignment.MIDDLE_LEFT); captionBar.setComponentAlignment(title, Alignment.MIDDLE_LEFT); captionBar.setComponentAlignment(toggle, Alignment.MIDDLE_RIGHT); captionBar.setExpandRatio(title, 1); captionBar.setWidth("100%"); captionBar.addStyleName(FACET_TITLE_STYLE); VerticalLayout layout = new VerticalLayout(captionBar); layout.setWidth("95%"); final Table table = new Table(); table.setColumnHeaderMode(ColumnHeaderMode.HIDDEN); table.addContainerProperty("value", Component.class, null); table.setWidth("100%"); List<FacetValueVO> values = facet.getValues(); for (final FacetValueVO facetValue : values) { final CheckBox checkBox = new CheckBox(); if (selectedFacetValues.contains(facetValue.getValue())) { checkBox.setValue(true); } checkBox.addValueChangeListener(new ValueChangeListener() { @Override public void valueChange(ValueChangeEvent event) { if (checkBox.getValue()) { presenter.facetValueSelected(facetValue.getFacetId(), facetValue.getValue()); } else { presenter.facetValueDeselected(facetValue.getFacetId(), facetValue.getValue()); } } }); String caption = facetValue.getLabel(); caption += " (" + facetValue.getCount() + ")"; checkBox.setCaption(caption); @SuppressWarnings("unchecked") Property<Component> value = (Property<Component>) table.addItem(checkBox).getItemProperty("value"); value.setValue(checkBox); } table.setPageLength(Math.min(facet.getValuesPerPage(), values.size())); table.setVisible(facet.isOpen()); toggle.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { if (toggle.getCaption().equals("+")) { toggle.setCaption("—"); table.setVisible(true); presenter.facetOpened(facet.getId()); } else { toggle.setCaption("+"); table.setVisible(false); presenter.facetClosed(facet.getId()); } } }); layout.addComponent(table); layout.setVisible(!facet.getValues().isEmpty()); layout.addStyleName(FACET_BOX_STYLE); return layout; } protected Button buildSelectAllButton() { String selectAllCaption; String deselectAllCaption; if (isDetailedView()) { selectAllCaption = $("SearchView.selectCurrentPage"); deselectAllCaption = $("SearchView.deselectCurrentPage"); } else { selectAllCaption = $("SearchView.selectRange"); deselectAllCaption = $("SearchView.selectRange"); } selectDeselectAllButton = new SelectDeselectAllButton(selectAllCaption, deselectAllCaption) { @Override protected void onSelectAll(ClickEvent event) { if (isDetailedView()) { ((SearchResultDetailedTable) results).selectCurrentPage(); } else { ((SearchResultSimpleTable) results).askSelectionRange(); } } @Override protected void onDeselectAll(ClickEvent event) { if (isDetailedView()) { ((SearchResultDetailedTable) results).deselectCurrentPage(); } else { ((SearchResultSimpleTable) results).askSelectionRange(); } } }; selectDeselectAllButton.addStyleName(ValoTheme.BUTTON_LINK); return selectDeselectAllButton; } protected Button buildAddToSelectionButton() { addToSelectionButton = new BaseButton($("SearchView.addToSelection")) { @Override protected void buttonClick(ClickEvent event) { presenter.addToSelectionButtonClicked(); } }; addToSelectionButton.addStyleName(ValoTheme.BUTTON_LINK); return addToSelectionButton; } protected Button buildSavedSearchButton() { WindowButton button = new WindowButton($("SearchView.saveSearch"), $("SearchView.saveSearch")) { @Override protected Component buildWindowContent() { final TextField titleField = new BaseTextField(); titleField.setCaption($("SearchView.savedSearch.title")); titleField.setRequired(true); titleField.setId("title"); titleField.addStyleName("title"); final CheckBox publicField = new CheckBox(); publicField.setCaption($("SearchView.savedSearch.public")); publicField.setRequired(true); publicField.setId("public"); publicField.addStyleName("public"); BaseButton saveSearchButton = new BaseButton($("SearchView.savedSearch.save")) { @Override protected void buttonClick(ClickEvent event) { if (presenter.saveSearch(titleField.getValue(), (publicField.getValue()))) { getWindow().close(); } } }; saveSearchButton.addStyleName(ValoTheme.BUTTON_PRIMARY); BaseButton cancelButton = new BaseButton($("cancel")) { @Override protected void buttonClick(ClickEvent event) { getWindow().close(); } }; HorizontalLayout horizontalLayout = new HorizontalLayout(); horizontalLayout.setSpacing(true); horizontalLayout.addComponents(saveSearchButton, cancelButton); VerticalLayout verticalLayout = new VerticalLayout(); verticalLayout.addComponents(titleField, publicField, horizontalLayout); verticalLayout.setSpacing(true); return verticalLayout; } }; button.addStyleName(ValoTheme.BUTTON_LINK); button.addStyleName(SAVE_SEARCH); return button; } public boolean isSelectAllMode() { return selectDeselectAllButton == null? null:selectDeselectAllButton.isSelectAllMode(); } }