package org.sigmah.client.ui.view.project.treegrid; /* * #%L * Sigmah * %% * Copyright (C) 2010 - 2016 URD * %% * 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/gpl-3.0.html>. * #L% */ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Set; import org.sigmah.client.i18n.I18N; import org.sigmah.client.ui.notif.N10N; import org.sigmah.client.ui.presenter.project.treegrid.ProjectsListWidget; import org.sigmah.client.ui.presenter.project.treegrid.ProjectsListWidget.HandlerProvider; import org.sigmah.client.ui.res.icon.IconImageBundle; import org.sigmah.client.ui.res.icon.dashboard.DashboardImageBundle; import org.sigmah.client.ui.res.icon.dashboard.funding.FundingIconProvider; import org.sigmah.client.ui.res.icon.dashboard.funding.FundingIconProvider.IconSize; import org.sigmah.client.ui.res.icon.project.category.CategoryIconProvider; import org.sigmah.client.ui.view.base.AbstractView; import org.sigmah.client.ui.widget.RatioBar; import org.sigmah.client.ui.widget.button.Button; import org.sigmah.client.ui.widget.form.Forms; import org.sigmah.client.ui.widget.form.ProjectModelTypeField; import org.sigmah.client.ui.widget.panel.Panels; import org.sigmah.client.util.DateUtils; import org.sigmah.client.util.NumberUtils; import org.sigmah.shared.dto.ProjectDTO; import org.sigmah.shared.dto.category.CategoryElementDTO; import org.sigmah.shared.dto.referential.BudgetSubFieldType; import org.sigmah.shared.dto.referential.ProjectModelType; import com.extjs.gxt.ui.client.GXT; import com.extjs.gxt.ui.client.Style.SortDir; import com.extjs.gxt.ui.client.data.Loader; import com.extjs.gxt.ui.client.data.ModelData; import com.extjs.gxt.ui.client.data.SortInfo; import com.extjs.gxt.ui.client.event.BaseEvent; import com.extjs.gxt.ui.client.event.ButtonEvent; import com.extjs.gxt.ui.client.event.Events; import com.extjs.gxt.ui.client.event.GridEvent; import com.extjs.gxt.ui.client.event.Listener; import com.extjs.gxt.ui.client.event.MenuEvent; import com.extjs.gxt.ui.client.event.SelectionListener; import com.extjs.gxt.ui.client.store.ListStore; import com.extjs.gxt.ui.client.store.StoreFilter; import com.extjs.gxt.ui.client.store.TreeStore; import com.extjs.gxt.ui.client.util.DelayedTask; import com.extjs.gxt.ui.client.util.Margins; import com.extjs.gxt.ui.client.widget.ContentPanel; import com.extjs.gxt.ui.client.widget.Html; import com.extjs.gxt.ui.client.widget.Label; import com.extjs.gxt.ui.client.widget.LayoutContainer; import com.extjs.gxt.ui.client.widget.WidgetComponent; import com.extjs.gxt.ui.client.widget.form.Field; import com.extjs.gxt.ui.client.widget.grid.ColumnConfig; import com.extjs.gxt.ui.client.widget.grid.ColumnData; import com.extjs.gxt.ui.client.widget.grid.ColumnHeader; import com.extjs.gxt.ui.client.widget.grid.ColumnHeader.Head; import com.extjs.gxt.ui.client.widget.grid.ColumnModel; import com.extjs.gxt.ui.client.widget.grid.Grid; import com.extjs.gxt.ui.client.widget.grid.GridCellRenderer; import com.extjs.gxt.ui.client.widget.grid.filters.DateFilter; import com.extjs.gxt.ui.client.widget.grid.filters.Filter; import com.extjs.gxt.ui.client.widget.grid.filters.GridFilters; import com.extjs.gxt.ui.client.widget.grid.filters.ListFilter; import com.extjs.gxt.ui.client.widget.grid.filters.NumericFilter; import com.extjs.gxt.ui.client.widget.grid.filters.StringFilter; import com.extjs.gxt.ui.client.widget.layout.FlowData; import com.extjs.gxt.ui.client.widget.layout.FlowLayout; import com.extjs.gxt.ui.client.widget.menu.CheckMenuItem; import com.extjs.gxt.ui.client.widget.menu.Menu; import com.extjs.gxt.ui.client.widget.menu.SeparatorMenuItem; import com.extjs.gxt.ui.client.widget.toolbar.FillToolItem; import com.extjs.gxt.ui.client.widget.toolbar.SeparatorToolItem; import com.extjs.gxt.ui.client.widget.toolbar.ToolBar; import com.extjs.gxt.ui.client.widget.treegrid.TreeGrid; import com.extjs.gxt.ui.client.widget.treegrid.WidgetTreeGridCellRenderer; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.i18n.client.DateTimeFormat; import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.Widget; import com.allen_sauer.gwt.log.client.Log; /** * {@link ProjectsListWidget} corresponding view implementation. * * @author Denis Colliot (dcolliot@ideia.fr) */ public class ProjectsListView extends AbstractView implements ProjectsListWidget.View { // CSS style names. private static final String STYLE_FILTERED_COLUMN_HEADER = "filtered-column-header"; private static final String STYLE_FLEXIBILITY_ELEMENT_LABEL = "flexibility-element-label"; private static final String STYLE_PROJECT_STARRED_ICON = "project-starred-icon"; private static final String STYLE_PROJECT_REFRESH_BUTTON = "project-refresh-button"; private static final String STYLE_IMPORTANT_LABEL = "important-label"; private static final String STYLE_PROJECT_GRID_NODE = "project-grid-node"; private static final String STYLE_PROJECT_GRID_LEAF = "project-grid-leaf"; private static final String STYLE_PROJECT_GRID_CODE_ICON = "project-grid-code-icon"; private static final String STYLE_PROJECT_GRID_CODE = "project-grid-code"; private static final String PROJECT_CODE_COLUMN_ID="projectCodeColumnId_"; /** * HTML double spaces characters. */ private static final String DOUBLE_SPACES = "  "; /** * Refresh time format. */ private static final DateTimeFormat REFRESH_TIME_FORMAT = DateTimeFormat.getFormat("HH:mm"); private ContentPanel projectTreePanel; private TreeGrid<ProjectDTO> projectTreeGrid; private Button filterButton; private GridFilters gridFilters; private ToolBar toolbar; private SeparatorToolItem refreshSeparator; private Button refreshButton; private Label refreshDateLabel; private Button exportButton; private ProjectModelTypeField projectModelTypeField; // Specific Handlers provided by presenter. private HandlerProvider handlerProvider; private TreeGridEventHandler<ProjectDTO> treeHandler; /** * Initializes the widget view. */ @Override public void initialize() { // Store. final TreeStore<ProjectDTO> projectStore = new TreeStore<ProjectDTO>(); projectStore.setMonitorChanges(true); // Default sort order of the projects grid. projectStore.setSortInfo(new SortInfo(ProjectDTO.NAME, SortDir.ASC)); // Grid. projectTreeGrid = new TreeGrid<ProjectDTO>(projectStore, buildProjectGridColumnModel()); projectTreeGrid.setBorders(true); projectTreeGrid.getStyle().setNodeOpenIcon(null); projectTreeGrid.getStyle().setNodeCloseIcon(null); projectTreeGrid.getStyle().setLeafIcon(null); projectTreeGrid.setAutoExpandColumn(ProjectDTO.FULL_NAME); projectTreeGrid.setTrackMouseOver(false); projectTreeGrid.setAutoExpand(true); // Apply grid filters gridFilters = new GridFilters() { private CheckMenuItem checkItem; private SeparatorMenuItem seperator; private Menu filterMenuO; private StoreFilter<ModelData> currentFilterO; private DelayedTask deferredUpdateO = new DelayedTask(new Listener<BaseEvent>() { @Override public void handleEvent(BaseEvent be) { reload(); } }); @Override protected void onContextMenu(GridEvent<?> be) { final int column = be.getColIndex(); if (seperator == null) { seperator = new SeparatorMenuItem(); } seperator.removeFromParent(); if (checkItem == null) { checkItem = new CheckMenuItem(getMessages().getFilterText()); checkItem.addListener(Events.CheckChange, new Listener<MenuEvent>() { @Override public void handleEvent(MenuEvent me) { onCheckChange(me); } }); checkItem.addListener(Events.BeforeCheckChange, new Listener<MenuEvent>() { @Override public void handleEvent(MenuEvent me) { onBeforeCheck(me); } }); } checkItem.removeFromParent(); checkItem.setData("index", column); final Filter f = getFilter(grid.getColumnModel().getColumn(column).getDataIndex()); if (f != null) { checkItem.show(); filterMenuO = f.getMenu(); checkItem.setChecked(f.isActive(), true); checkItem.setSubMenu(filterMenuO); final Menu menu = be.getMenu(); menu.add(seperator); if (columnModel.getDataIndex(column).equals(TIME_COLUMN)) { checkItem.setText(I18N.CONSTANTS.closedProjectsFilterText()); } else { checkItem.setText(GXT.MESSAGES.gridFilters_filterText()); } menu.add(checkItem); } } @Override protected void onStateChange(Filter filter) { if (checkItem != null && checkItem.isAttached()) { checkItem.setChecked(filter.isActive(), true); } if ((isAutoReload() || isLocal())) { deferredUpdateO.delay(getUpdateBuffer()); } updateColumnHeadings(); } @Override protected void reload() { if (isLocal()) { if (currentFilterO != null) { getStore().removeFilter(currentFilterO); } currentFilterO = getModelFilter(); // BUGFIX #742 : Verifying that grid is not null to avoid a NPE in getStore(). if(currentFilterO != null && grid != null) { getStore().addFilter(currentFilterO); if (!getStore().isFiltered()) { getStore().applyFilters(""); } } } else { deferredUpdateO.cancel(); final Loader<?> l = getLoader(getStore()); if (l != null) { l.load(); } } } @Override public void updateColumnHeadings() { // BUGFIX #742 : Verifying that grid is not null to avoid a NPE when the user cannot access projects. if(grid == null || grid.getColumnModel() == null) { return; } final int cols = grid.getColumnModel().getColumnCount(); for (int i = 0; i < cols; i++) { final ColumnConfig config = grid.getColumnModel().getColumn(i); if (config.isHidden()) { continue; } final ColumnHeader header = grid.getView().getHeader(); if (header == null) { continue; } final Head h = header.getHead(i); if (h != null && h.isRendered()) { final Filter f = getFilter(config.getDataIndex()); if (f != null) { h.el().setStyleName(STYLE_FILTERED_COLUMN_HEADER, f.isActive()); } } } } }; projectTreeGrid.addPlugin(createGridFilters()); // Top panel final HTML headLabel = new HTML(DOUBLE_SPACES + I18N.CONSTANTS.projectTypeFilter() + I18N.CONSTANTS.form_label_separator() + DOUBLE_SPACES); headLabel.addStyleName(STYLE_FLEXIBILITY_ELEMENT_LABEL); // Expand all button. final Button expandButton = Forms.button("", IconImageBundle.ICONS.expand(), new SelectionListener<ButtonEvent>() { @Override public void componentSelected(ButtonEvent ce) { projectTreeGrid.expandAll(); } }); // Collapse all button. final Button collapseButton = Forms.button("", IconImageBundle.ICONS.collapse(), new SelectionListener<ButtonEvent>() { @Override public void componentSelected(ButtonEvent ce) { projectTreeGrid.collapseAll(); } }); // Filter button. filterButton = new Button(I18N.CONSTANTS.filter(), IconImageBundle.ICONS.filter()); // Refresh button. refreshButton = new Button(I18N.CONSTANTS.refreshProjectList(), IconImageBundle.ICONS.refresh()); refreshButton.setToolTip(I18N.CONSTANTS.refreshProjectListDetails()); refreshButton.addStyleName(STYLE_PROJECT_REFRESH_BUTTON); // Refresh date. refreshDateLabel = new Label(); refreshSeparator = new SeparatorToolItem(); toolbar = new ToolBar(); toolbar.add(expandButton); toolbar.add(collapseButton); toolbar.add(new SeparatorToolItem()); toolbar.add(filterButton); toolbar.add(new WidgetComponent(headLabel)); projectModelTypeField = new ProjectModelTypeField(null, false); toolbar.add(projectModelTypeField); projectModelTypeField.setValue(ProjectModelType.NGO); // Default selected option. // Preparing 'export' functionality. toolbar.add(new FillToolItem()); exportButton = new Button(I18N.CONSTANTS.exportAll(), IconImageBundle.ICONS.excel()); // Panel projectTreePanel = Panels.content(I18N.CONSTANTS.projects()); projectTreePanel.setTopComponent(toolbar); } /** * {@inheritDoc} */ @Override public Widget asWidget() { return projectTreePanel; } /** * {@inheritDoc} */ @Override public void setHandlerProvider(final HandlerProvider handlerProvider) { this.handlerProvider = handlerProvider; } /** * {@inheritDoc} */ @Override public void updateAccessibilityState(final boolean authorized) { projectTreePanel.removeAll(); if (authorized) { projectTreePanel.add(projectTreeGrid); } else { final HTML insufficient = new HTML(I18N.CONSTANTS.permViewProjectsInsufficient()); insufficient.addStyleName(STYLE_IMPORTANT_LABEL); projectTreePanel.add(insufficient); } } /** * {@inheritDoc} */ @Override public ContentPanel getProjectsPanel() { return projectTreePanel; } /** * {@inheritDoc} */ @Override public GridFilters getGridFilters() { return gridFilters; } /** * {@inheritDoc} */ @Override public Field<ProjectModelType> getProjectModelTypeField() { return projectModelTypeField; } /** * {@inheritDoc} */ @Override public void updateRefreshingDate(final Date date) { if (date == null) { return; } refreshDateLabel.setHtml('(' + REFRESH_TIME_FORMAT.format(date) + ')'); } /** * {@inheritDoc} */ @Override public Button getRefreshButton() { return refreshButton; } /** * {@inheritDoc} */ @Override public Button getFilterButton() { return filterButton; } /** * {@inheritDoc} */ @Override public TreeGrid<ProjectDTO> getTreeGrid() { return projectTreeGrid; } /** * {@inheritDoc} */ @Override public TreeStore<ProjectDTO> getStore() { return projectTreeGrid.getTreeStore(); } /** * {@inheritDoc} */ @Override public void setTreeGridEventHandler(final TreeGridEventHandler<ProjectDTO> handler) { this.treeHandler = handler; } /** * {@inheritDoc} */ @Override public void updateToolbar(final boolean refresh, final boolean export) { if (refresh) { // Inserts 'refresh' functionality. toolbar.insert(refreshSeparator, 0); toolbar.insert(refreshDateLabel, 0); toolbar.insert(refreshButton, 0); } else if (refreshSeparator.isAttached()) { // Removes 'refresh' functionality. toolbar.remove(refreshSeparator); toolbar.remove(refreshDateLabel); toolbar.remove(refreshButton); } if (export) { // Inserts 'export' functionality. toolbar.add(exportButton); } else if (exportButton.isAttached()) { // Removes 'export' functionality. toolbar.remove(exportButton); } } /** * {@inheritDoc} */ @Override public Button getExportButton() { return exportButton; } /** * {@inheritDoc} */ @Override public void syncSize() { projectTreeGrid.syncSize(); } // --------------------------------------------------------------------------------------------------------- // // UTILITY METHODS. // // --------------------------------------------------------------------------------------------------------- /** * Builds and returns the columns model for the projects tree grid. * * @return The project tree grid columns model. */ private ColumnModel buildProjectGridColumnModel() { final DateTimeFormat format = DateUtils.DATE_SHORT; // Starred icon final ColumnConfig starredIconColumn = new ColumnConfig("starred", "", 30); starredIconColumn.setRenderer(new GridCellRenderer<ProjectDTO>() { @Override public Object render(final ProjectDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<ProjectDTO> store, final Grid<ProjectDTO> grid) { // A star icon final Image icon; if (handlerProvider.isFavoriteProject(model)) { // Favorite project (filled star). icon = DashboardImageBundle.ICONS.star().createImage(); icon.setTitle(I18N.CONSTANTS.projectStarred_tooltip_on()); } else { // Non-favorite project (empty star). icon = DashboardImageBundle.ICONS.emptyStar().createImage(); icon.setTitle(I18N.CONSTANTS.projectStarred_tooltip_off()); } // Star icon click-handler icon.addClickHandler(new ClickHandler() { @Override public void onClick(final ClickEvent event) { handlerProvider.onStarIconClicked(model); } }); icon.addStyleName(STYLE_PROJECT_STARRED_ICON); return icon; } }); // Code final ColumnConfig codeColumn = new ColumnConfig(ProjectDTO.NAME, I18N.CONSTANTS.projectName(), 110); codeColumn.setRenderer(new WidgetTreeGridCellRenderer<ProjectDTO>() { @Override public Widget getWidget(final ProjectDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<ProjectDTO> store, final Grid<ProjectDTO> grid) { final Anchor nameLink = new Anchor((String) model.get(property)); nameLink.ensureDebugId(nameLink.getElement(),PROJECT_CODE_COLUMN_ID+model.getId()); if (!model.isLeaf()) { nameLink.setStyleName(STYLE_PROJECT_GRID_NODE ); } else { nameLink.setStyleName(STYLE_PROJECT_GRID_LEAF); } nameLink.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { if (treeHandler == null) { N10N.warn("Not implemented yet."); return; } treeHandler.onRowClickEvent(model); } }); final com.google.gwt.user.client.ui.Grid panel = new com.google.gwt.user.client.ui.Grid(1, 2); panel.setCellPadding(0); panel.setCellSpacing(0); panel.setWidget(0, 0, FundingIconProvider.getProjectTypeIcon(handlerProvider.getProjectModelType(model), IconSize.SMALL_MEDIUM).createImage()); panel.getCellFormatter().addStyleName(0, 0, STYLE_PROJECT_GRID_CODE_ICON); nameLink.getElement().getId(); nameLink.getElement().setId(PROJECT_CODE_COLUMN_ID+"-4"+model.getId()); panel.setWidget(0, 1, nameLink); panel.getCellFormatter().addStyleName(0, 1, STYLE_PROJECT_GRID_CODE); //panel.getElement().setId(PROJECT_CODE_COLUMN_ID+model.getId()); return panel; } }); // Title final ColumnConfig titleColumn = new ColumnConfig(ProjectDTO.FULL_NAME, I18N.CONSTANTS.projectFullName(), 230); titleColumn.setRenderer(new GridCellRenderer<ProjectDTO>() { @Override public Object render(final ProjectDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<ProjectDTO> store, final Grid<ProjectDTO> grid) { String title = (String) model.get(property); if (model.getParent() != null) { title = DOUBLE_SPACES + DOUBLE_SPACES + title; } return createProjectGridText(model, title); } }); // Current phase final ColumnConfig currentPhaseName = new ColumnConfig(ProjectDTO.CURRENT_PHASE_NAME, I18N.CONSTANTS.projectActivePhase(), 150); currentPhaseName.setRenderer(new GridCellRenderer<ProjectDTO>() { @Override public Object render(final ProjectDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<ProjectDTO> store, final Grid<ProjectDTO> grid) { return createProjectGridText(model, (String) model.get(property)); } }); // Org Unit final ColumnConfig orgUnitColumn = new ColumnConfig(ProjectDTO.ORG_UNIT_NAME, I18N.CONSTANTS.orgunit(), 150); orgUnitColumn.setRenderer(new GridCellRenderer<ProjectDTO>() { @Override public Object render(final ProjectDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<ProjectDTO> store, final Grid<ProjectDTO> grid) { return createProjectGridText(model, (String) model.get(property)); } }); // Ratio budget final ColumnConfig spentBudgetColumn = new ColumnConfig(ProjectDTO.SPEND_BUDGET, I18N.CONSTANTS.projectSpendBudget(), 100); spentBudgetColumn.setRenderer(new GridCellRenderer<ProjectDTO>() { @Override public Object render(final ProjectDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<ProjectDTO> store, final Grid<ProjectDTO> grid) { final String ratioDividendLabel; if (model.getRatioDividendType() != null) { ratioDividendLabel = BudgetSubFieldType.getName(model.getRatioDividendType()); } else { ratioDividendLabel = model.getRatioDividendLabel(); } final String ratioDivisorLabel; if (model.getRatioDivisorType() != null) { ratioDivisorLabel = BudgetSubFieldType.getName(model.getRatioDivisorType()); } else { ratioDivisorLabel = model.getRatioDivisorLabel(); } final String titleRatioLabel = '(' + ratioDividendLabel + '/' + ratioDivisorLabel + ')'; if (model.getRatioDividendValue() != null && model.getRatioDivisorValue() != null) { return new RatioBar(NumberUtils.ratio(model.getRatioDividendValue(), model.getRatioDivisorValue()), titleRatioLabel); } else { return new RatioBar(0.0); } } }); // Planned budget final ColumnConfig plannedBudgetColumn = new ColumnConfig(ProjectDTO.PLANNED_BUDGET, I18N.CONSTANTS.projectPlannedBudget(), 75); plannedBudgetColumn.setHidden(true); // Spend budget final ColumnConfig spendBudgetColumn = new ColumnConfig(ProjectDTO.SPEND_BUDGET, I18N.CONSTANTS.projectSpendBudget(), 75); spendBudgetColumn.setHidden(true); // Received budget final ColumnConfig receivedBudgetColumn = new ColumnConfig(ProjectDTO.RECEIVED_BUDGET, I18N.CONSTANTS.projectReceivedBudget(), 75); receivedBudgetColumn.setHidden(true); // Time final ColumnConfig timeColumn = new ColumnConfig(TIME_COLUMN, I18N.CONSTANTS.projectTime(), 100); timeColumn.setRenderer(new GridCellRenderer<ProjectDTO>() { @Override public Object render(final ProjectDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<ProjectDTO> store, final Grid<ProjectDTO> grid) { if (!model.isClosed()) { return new RatioBar(model.getElapsedTime()); } else { return new Label(I18N.CONSTANTS.projectClosedLabel()); } } }); // Start date final ColumnConfig startDateColumn = new ColumnConfig(ProjectDTO.START_DATE, I18N.CONSTANTS.projectStartDate(), 75); startDateColumn.setHidden(true); startDateColumn.setDateTimeFormat(format); startDateColumn.setRenderer(new GridCellRenderer<ProjectDTO>() { @Override public Object render(final ProjectDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<ProjectDTO> store, final Grid<ProjectDTO> grid) { final Date d = (Date) model.get(property); return createProjectGridText(model, d != null ? format.format(d) : ""); } }); // End date final ColumnConfig endDateColumn = new ColumnConfig(ProjectDTO.END_DATE, I18N.CONSTANTS.projectEndDate(), 75); endDateColumn.setDateTimeFormat(format); endDateColumn.setHidden(true); endDateColumn.setRenderer(new GridCellRenderer<ProjectDTO>() { @Override public Object render(final ProjectDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<ProjectDTO> store, final Grid<ProjectDTO> grid) { final Date d = (Date) model.get(property); return createProjectGridText(model, d != null ? format.format(d) : ""); } }); // Close date final ColumnConfig closeDateColumn = new ColumnConfig(ProjectDTO.CLOSE_DATE, I18N.CONSTANTS.projectClosedDate(), 75); closeDateColumn.setDateTimeFormat(format); closeDateColumn.setHidden(true); closeDateColumn.setRenderer(new GridCellRenderer<ProjectDTO>() { @Override public Object render(final ProjectDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<ProjectDTO> store, final Grid<ProjectDTO> grid) { final Date d = (Date) model.get(property); return createProjectGridText(model, d != null ? format.format(d) : ""); } }); // Activity final ColumnConfig activityColumn = new ColumnConfig(ProjectDTO.ACTIVITY_ADVANCEMENT, I18N.CONSTANTS.logFrameActivity(), 100); activityColumn.setSortable(false); activityColumn.setRenderer(new GridCellRenderer<ProjectDTO>() { @Override public Object render(final ProjectDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<ProjectDTO> store, final Grid<ProjectDTO> grid) { return new RatioBar(model.getActivityAdvancement() != null ? model.getActivityAdvancement() : 0); } }); // Category final ColumnConfig categoryColumn = new ColumnConfig(ProjectDTO.CATEGORY_ELEMENTS, I18N.CONSTANTS.category(), 150); categoryColumn.setSortable(false); categoryColumn.setRenderer(new GridCellRenderer<ProjectDTO>() { @Override public Object render(final ProjectDTO model, final String property, final ColumnData config, final int rowIndex, final int colIndex, final ListStore<ProjectDTO> store, final Grid<ProjectDTO> grid) { final Set<CategoryElementDTO> elements = model.getCategoryElements(); final LayoutContainer panel = new LayoutContainer(); panel.setLayout(new FlowLayout()); final FlowData data = new FlowData(new Margins(0, 5, 0, 0)); if (elements != null) { for (final CategoryElementDTO element : elements) { panel.add(CategoryIconProvider.getIcon(element), data); } } return panel; } }); return new ColumnModel(Arrays.asList(starredIconColumn, codeColumn, titleColumn, currentPhaseName, orgUnitColumn, spentBudgetColumn, plannedBudgetColumn, spendBudgetColumn, receivedBudgetColumn, startDateColumn, endDateColumn, closeDateColumn, timeColumn, activityColumn, categoryColumn)); } /** * Creates a text widget for the projects grid. * * @param model * The project model. * @param content * The text content. * @return The built widget. */ private static Widget createProjectGridText(final ProjectDTO model, final String content) { final Html label = new Html(content); if (!model.isLeaf()) { label.addStyleName(STYLE_PROJECT_GRID_NODE); } else { label.addStyleName(STYLE_PROJECT_GRID_LEAF); } return label; } /** * Grid filters for projects' TreeGrid. */ private GridFilters createGridFilters() { gridFilters.setLocal(true); // Data index of each filter should be identical with column id in ColumnConfig of TreeGrid. // Common filters gridFilters.addFilter(new StringFilter(ProjectDTO.NAME)); gridFilters.addFilter(new StringFilter(ProjectDTO.FULL_NAME)); gridFilters.addFilter(new StringFilter(ProjectDTO.CURRENT_PHASE_NAME)); gridFilters.addFilter(new StringFilter(ProjectDTO.ORG_UNIT_NAME)); gridFilters.addFilter(new NumericFilter(ProjectDTO.SPEND_BUDGET)); gridFilters.addFilter(new NumericFilter(ProjectDTO.RECEIVED_BUDGET)); gridFilters.addFilter(new NumericFilter(ProjectDTO.PLANNED_BUDGET)); gridFilters.addFilter(new DateFilter(ProjectDTO.START_DATE)); gridFilters.addFilter(new DateFilter(ProjectDTO.END_DATE)); gridFilters.addFilter(new DateFilter(ProjectDTO.CLOSE_DATE)); // Custom filter for category elements' list final ListFilter categoryListFilter = new ListFilter(ProjectDTO.CATEGORY_ELEMENTS, new ListStore<ModelData>()) { @SuppressWarnings("unchecked") @Override public boolean validateModel(final ModelData model) { final Set<CategoryElementDTO> elementsPerProject = getModelValue(model); final List<String> filterLabels = (ArrayList<String>) getValue(); if (elementsPerProject != null) { String label = null; for (final CategoryElementDTO element : elementsPerProject) { label = element.getLabel() + " (" + element.getParentCategoryDTO().getLabel() + ")"; if (filterLabels.contains(label)) { return true; } } } return filterLabels.size() == 0; } }; categoryListFilter.setDisplayProperty(CATEGORY_FILTER); gridFilters.addFilter(categoryListFilter); gridFilters.addFilter(new ClosedFilter(TIME_COLUMN)); return gridFilters; } }