package au.com.vaadinutils.crud; import java.lang.annotation.Annotation; import java.util.Collection; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.vaadin.data.Container; import com.vaadin.data.Container.Filter; import com.vaadin.data.Container.Filterable; import com.vaadin.data.util.converter.Converter; import com.vaadin.event.FieldEvents.TextChangeEvent; import com.vaadin.event.FieldEvents.TextChangeListener; import com.vaadin.event.ItemClickEvent; import com.vaadin.event.ItemClickEvent.ItemClickListener; import com.vaadin.event.dd.DropHandler; import com.vaadin.shared.MouseEventDetails.MouseButton; 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.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.Table.ColumnGenerator; import com.vaadin.ui.Table.TableDragMode; 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.fields.SelectionListener; import au.com.vaadinutils.listener.ClickEventLogged; import au.com.vaadinutils.menu.Menu; import au.com.vaadinutils.menu.Menus; public abstract class SearchableSelectableEntityTable<E> extends VerticalLayout { private static final long serialVersionUID = 1L; @SuppressWarnings("unused") private static Logger logger = LogManager.getLogger(); protected TextField searchField = new TextField(); private AbstractLayout advancedSearchLayout; private AbstractLayout searchBar; private Button advancedSearchCheckbox; public boolean advancedSearchOn = false; protected SelectableEntityTable<E> selectableTable; protected Container.Filterable container; private String uniqueId; private String filterString = ""; public SearchableSelectableEntityTable(String uniqueId) { this.uniqueId = uniqueId; container = getContainer(); selectableTable = new SelectableEntityTable<E>(container, getHeadingPropertySet(), uniqueId); selectableTable.setSizeFull(); this.setSizeFull(); if (!getSecurityManager().canUserView()) { this.setSizeFull(); this.addComponent(new Label("Sorry, you do not have permission to access " + getTitle())); return; } searchBar = buildSearchBar(); final String titleText = getTitle(); if (titleText != null && !titleText.isEmpty()) { Label title = new Label(getTitle()); title.setStyleName(Reindeer.LABEL_H1); this.addComponent(title); } this.addComponent(searchBar); this.addComponent(selectableTable); this.setExpandRatio(selectableTable, 1); addRightClickSelect(); triggerFilter(); } /** * Adds a listener to select the right clicked item in the table. This is * needed by ContextMenus. */ private void addRightClickSelect() { selectableTable.addItemClickListener(new ItemClickListener() { private static final long serialVersionUID = 1L; @Override @SuppressWarnings("deprecation") public void itemClick(ItemClickEvent event) { if (event.getButton() == MouseButton.RIGHT) { selectableTable.setValue(event.getItemId()); } } }); } abstract public HeadingPropertySet getHeadingPropertySet(); abstract public Filterable 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) { selectableTable.addGeneratedColumn(id, generatedColumn); } protected AbstractLayout buildSearchBar() { VerticalLayout layout = new VerticalLayout(); layout.setWidth(100, Unit.PERCENTAGE); searchField.setWidth(100, Unit.PERCENTAGE); searchBar = layout; HorizontalLayout basicSearchLayout = new HorizontalLayout(); basicSearchLayout.setWidth(100, Unit.PERCENTAGE); basicSearchLayout.setSpacing(true); layout.addComponent(basicSearchLayout); AbstractLayout advancedSearch = buildAdvancedSearch(); if (advancedSearch != null) { basicSearchLayout.addComponent(advancedSearchCheckbox); } 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 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; } @SuppressWarnings("deprecation") public void disableSelectable() { selectableTable.disableSelectable(); } /** * Filtering * * @return */ private Button createClearButton() { 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) { advancedSearchCheckbox = new Button("Advanced"); advancedSearchOn = false; advancedSearchCheckbox.setImmediate(true); advancedSearchCheckbox.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) { advancedSearchCheckbox.removeStyleName(ValoTheme.BUTTON_FRIENDLY); } else { advancedSearchCheckbox.setStyleName(ValoTheme.BUTTON_FRIENDLY); } } }); searchBar.addComponent(advancedSearchLayout); advancedSearchLayout.setVisible(false); } return advancedSearchLayout; } protected AbstractLayout getAdvancedSearchLayout() { return null; } /** * call this method to cause filters to be applied */ public void triggerFilter() { triggerFilter(searchField.getValue()); } protected void triggerFilter(String searchText) { // boolean advancedSearchActive = advancedSearchCheckbox != null && // advancedSearchCheckbox.getValue(); 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) { /* Reset the filter for the Entity Container. */ resetFilters(); container.addContainerFilter(filter); } public String getSearchFieldText() { return filterString; } /** * create a filter for the text supplied, the text is as entered in the text * search bar. * * @param string * @return */ abstract protected Filter getContainerFilter(String filterString, boolean advancedSearchActive); /** * called when the advancedFilters layout should clear it's values */ protected void clearAdvancedFilters() { } public Collection<Long> getSelectedIds() { return selectableTable.getSelectedIds(); } @SuppressWarnings("deprecation") public void addSelectionListener(SelectionListener listener) { selectableTable.addSelectionListener(listener); } public void addItemClickListener(ItemClickListener object) { selectableTable.addItemClickListener(object); } public void removeAllContainerFilters() { container.removeAllContainerFilters(); } public void addContainerFilter(Filter filter) { container.addContainerFilter(filter); } public void setConverter(String propertyId, Converter<String, ?> converter) { selectableTable.setConverter(propertyId, converter); } @SuppressWarnings("deprecation") public void setSelected(Collection<Long> ids) { selectableTable.setSelectedValue(ids); } @SuppressWarnings("deprecation") public void setMultiSelect(boolean b) { selectableTable.setMultiSelect(true); } public void setDragMode(TableDragMode mode) { selectableTable.setDragMode(mode); } public void setDropHandler(DropHandler dropHandler) { selectableTable.setDropHandler(dropHandler); } @SuppressWarnings("deprecation") public void deselectAll() { selectableTable.deselectAll(); } @SuppressWarnings("deprecation") public Object getSelectedItems() { return selectableTable.getSelectedItems(); } public void setSearchFilterText(String text) { searchField.setValue(text); triggerFilter(text); } public SelectableEntityTable<E> getSelectableTable() { return new SelectableEntityTable<E>(container, getHeadingPropertySet(), uniqueId); } public AbstractLayout getSearchBar() { return searchBar; } public SelectableEntityTable<E> getTable() { return selectableTable; } @SuppressWarnings("deprecation") public void selectAll() { selectableTable.selectAll(); } public void setColumnReorderingAllowed(boolean columnReorderingAllowed) { selectableTable.setColumnReorderingAllowed(true); } public boolean isColumnReorderingAllowed() { return selectableTable.isColumnReorderingAllowed(); } public void refresh() { selectableTable.refreshRowCache(); } }