package au.com.vaadinutils.crud; import java.lang.annotation.Annotation; import java.util.Collection; import org.vaadin.teemusa.gridextensions.refresher.GridRefresher; import com.vaadin.data.Container.Filter; import com.vaadin.data.Container.Filterable; import com.vaadin.data.Container.Indexed; import com.vaadin.data.sort.Sort; import com.vaadin.data.util.GeneratedPropertyContainer; import com.vaadin.data.util.converter.Converter; import com.vaadin.event.ContextClickEvent.ContextClickListener; import com.vaadin.event.FieldEvents.TextChangeEvent; import com.vaadin.event.FieldEvents.TextChangeListener; import com.vaadin.event.ItemClickEvent.ItemClickListener; import com.vaadin.event.SelectionEvent.SelectionListener; import com.vaadin.ui.AbstractLayout; import com.vaadin.ui.AbstractTextField.TextChangeEventMode; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.CustomComponent; import com.vaadin.ui.Grid; import com.vaadin.ui.Grid.CellDescriptionGenerator; import com.vaadin.ui.Grid.CellStyleGenerator; import com.vaadin.ui.Grid.RowStyleGenerator; import com.vaadin.ui.Grid.SelectionMode; import com.vaadin.ui.Grid.SelectionModel; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.Table.ColumnGenerator; import com.vaadin.ui.TextField; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.themes.Reindeer; import com.vaadin.ui.themes.ValoTheme; import au.com.vaadinutils.crud.security.SecurityManagerFactoryProxy; import au.com.vaadinutils.listener.ClickEventLogged; import au.com.vaadinutils.menu.Menu; import au.com.vaadinutils.menu.Menus; public abstract class SearchableGrid<E, T extends Indexed & Filterable> extends CustomComponent { private static final long serialVersionUID = 1L; private boolean initialised; private TextField searchField = new TextField(); private AbstractLayout advancedSearchLayout; private AbstractLayout searchBar; private Button advancedSearchButton; private boolean advancedSearchOn = false; private Grid grid; private T container; private String filterString = ""; private GridHeadingPropertySet headingPropertySet; public SearchableGrid(String uniqueId) { if (!getSecurityManager().canUserView()) { this.setSizeFull(); this.setCompositionRoot(new Label("Sorry, you do not have permission to access " + getTitle())); return; } container = getContainer(); grid = new Grid(new GeneratedPropertyContainer(container)); grid.setSizeFull(); searchBar = buildSearchBar(); final VerticalLayout layout = new VerticalLayout(); layout.setSizeFull(); addTitle(layout); layout.addComponent(searchBar); layout.addComponent(grid); layout.setExpandRatio(grid, 1); this.setCompositionRoot(layout); headingPropertySet = getHeadingPropertySet(); headingPropertySet.setDeferLoadSettings(true); headingPropertySet.applyToGrid(grid, uniqueId); } private void addTitle(final VerticalLayout layout) { final String titleText = getTitle(); if (titleText != null && !titleText.isEmpty()) { final Label titleLabel = new Label(getTitle()); titleLabel.setStyleName(setTitleStyleName()); layout.addComponent(titleLabel); } } @Override public void beforeClientResponse(boolean initial) { super.beforeClientResponse(initial); if (!initialised) { headingPropertySet.applySettingsToColumns(); initialised = true; } } abstract public GridHeadingPropertySet getHeadingPropertySet(); abstract public T getContainer(); protected String getTitle() { Annotation annotation = this.getClass().getAnnotation(Menu.class); if (annotation instanceof Menu) { return ((Menu) annotation).display(); } annotation = this.getClass().getAnnotation(Menus.class); if (annotation instanceof Menus) { return ((Menus) annotation).menus()[0].display(); } return "Override getTitle() to set a custom title."; } private CrudSecurityManager getSecurityManager() { return SecurityManagerFactoryProxy.getSecurityManager(this.getClass()); } public void addGeneratedColumn(Object id, ColumnGenerator generatedColumn) { // grid.addGeneratedColumn(id, generatedColumn); } protected AbstractLayout buildSearchBar() { final VerticalLayout layout = new VerticalLayout(); layout.setWidth(100, Unit.PERCENTAGE); searchField.setWidth(100, Unit.PERCENTAGE); searchBar = layout; final HorizontalLayout basicSearchLayout = new HorizontalLayout(); basicSearchLayout.setWidth(100, Unit.PERCENTAGE); basicSearchLayout.setSpacing(true); layout.addComponent(basicSearchLayout); final AbstractLayout advancedSearch = buildAdvancedSearch(); if (advancedSearch != null) { basicSearchLayout.addComponent(advancedSearchButton); } searchField.setInputPrompt("Search"); searchField.setId("searchField"); searchField.setTextChangeEventMode(TextChangeEventMode.LAZY); searchField.setImmediate(true); searchField.addTextChangeListener(new TextChangeListener() { private static final long serialVersionUID = 1L; @Override public void textChange(final TextChangeEvent event) { filterString = event.getText().trim(); triggerFilter(filterString); } }); // clear button final Button clear = createClearButton(); basicSearchLayout.addComponent(clear); basicSearchLayout.setComponentAlignment(clear, Alignment.MIDDLE_CENTER); basicSearchLayout.addComponent(searchField); basicSearchLayout.setExpandRatio(searchField, 1.0f); basicSearchLayout.setSpacing(true); searchField.focus(); return layout; } private Button createClearButton() { final Button clear = new Button("X"); clear.setStyleName(Reindeer.BUTTON_SMALL); clear.setImmediate(true); clear.addClickListener(new ClickEventLogged.ClickListener() { private static final long serialVersionUID = 1L; @Override public void clicked(ClickEvent event) { searchField.setValue(""); clearAdvancedFilters(); triggerFilter(); } }); return clear; } private AbstractLayout buildAdvancedSearch() { advancedSearchLayout = getAdvancedSearchLayout(); if (advancedSearchLayout != null) { advancedSearchButton = new Button(getAdvancedCaption()); advancedSearchOn = false; advancedSearchButton.setImmediate(true); advancedSearchButton.addClickListener(new ClickListener() { private static final long serialVersionUID = 7777043506655571664L; @Override public void buttonClick(ClickEvent event) { clearAdvancedFilters(); advancedSearchOn = !advancedSearchOn; advancedSearchLayout.setVisible(advancedSearchOn); if (!advancedSearchOn) { triggerFilter(); } if (!advancedSearchOn) { advancedSearchButton.setCaption(getAdvancedCaption()); advancedSearchButton.removeStyleName(ValoTheme.BUTTON_FRIENDLY); } else { advancedSearchButton.setCaption(getBasicCaption()); advancedSearchButton.setStyleName(ValoTheme.BUTTON_FRIENDLY); } } }); searchBar.addComponent(advancedSearchLayout); advancedSearchLayout.setVisible(false); } return advancedSearchLayout; } protected AbstractLayout getAdvancedSearchLayout() { return null; } public void triggerFilter() { triggerFilter(searchField.getValue()); } protected void triggerFilter(String searchText) { boolean advancedSearchActive = advancedSearchOn; Filter filter = getContainerFilter(searchText, advancedSearchActive); if (filter == null) { resetFilters(); } else { applyFilter(filter); } } protected void resetFilters() { container.removeAllContainerFilters(); } protected void applyFilter(Filter filter) { resetFilters(); container.addContainerFilter(filter); } public String getSearchFieldText() { return filterString; } abstract protected Filter getContainerFilter(String filterString, boolean advancedSearchActive); protected void clearAdvancedFilters() { } public void addItemClickListener(ItemClickListener listener) { grid.addItemClickListener(listener); } public void addSelectionListener(SelectionListener listener) { grid.addSelectionListener(listener); } public void addStyleName(String style) { grid.addStyleName(style); } public void removeAllContainerFilters() { container.removeAllContainerFilters(); } public void addContainerFilter(Filter filter) { container.addContainerFilter(filter); } public boolean select(Object itemId) { return grid.select(itemId); } public void deselectAll() { grid.deselectAll(); } public SelectionModel getSelectionModel() { return grid.getSelectionModel(); } public SelectionModel setSelectionMode(SelectionMode selectionMode) { return grid.setSelectionMode(selectionMode); } public Object getSelectedRow() { return grid.getSelectedRow(); } public Collection<Object> getSelectedRows() { return grid.getSelectedRows(); } public void setSearchFilterText(String text) { searchField.setValue(text); triggerFilter(text); } public AbstractLayout getSearchBar() { return searchBar; } public Grid getGrid() { return grid; } public void setColumnReorderingAllowed(boolean columnReorderingAllowed) { grid.setColumnReorderingAllowed(true); } public boolean isColumnReorderingAllowed() { return grid.isColumnReorderingAllowed(); } public void setConverter(String propertyId, Converter<String, ?> converter) { grid.getColumn(propertyId).setConverter(converter); } public void refresh(final Object itemId) { GridRefresher.extend(grid).refresh(itemId); } public void refresh() { final GridRefresher refresher = GridRefresher.extend(grid); for (Object itemId : grid.getContainerDataSource().getItemIds()) { refresher.refresh(itemId); } } @Override public void addContextClickListener(ContextClickListener listener) { grid.addContextClickListener(listener); } protected String getAdvancedCaption() { return "Advanced"; } protected String getBasicCaption() { return "Basic"; } public void setCellDescriptionGenerator(CellDescriptionGenerator generator) { grid.setCellDescriptionGenerator(generator); } public void setCellStyleGenerator(CellStyleGenerator generator) { grid.setCellStyleGenerator(generator); } public void setRowStyleGenerator(RowStyleGenerator generator) { grid.setRowStyleGenerator(generator); } public String setTitleStyleName() { return ""; } public void sort(final Sort sort) { grid.sort(sort); } }