package org.sigmah.client.ui.view.project; /* * #%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 com.extjs.gxt.ui.client.GXT; import java.util.LinkedHashMap; import java.util.Map; import org.sigmah.client.i18n.I18N; import org.sigmah.client.page.Page; import org.sigmah.client.ui.presenter.project.ProjectPresenter; import org.sigmah.client.ui.presenter.project.ProjectPresenter.ExportActionHandler; import org.sigmah.client.ui.res.icon.IconImageBundle; 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.view.base.AbstractView; import org.sigmah.client.ui.widget.SubMenuWidget; import org.sigmah.client.ui.widget.SubMenuWidget.Orientation; import org.sigmah.client.ui.widget.button.Button; import org.sigmah.client.ui.widget.form.FormPanel; import org.sigmah.client.ui.widget.form.Forms; import org.sigmah.client.ui.widget.layout.Layouts; import org.sigmah.client.ui.widget.layout.Layouts.Margin; import org.sigmah.client.ui.widget.panel.Panels; import org.sigmah.client.util.ClientUtils; import org.sigmah.shared.dto.AmendmentDTO; import org.sigmah.shared.dto.referential.ProjectModelType; import org.sigmah.shared.util.Pair; import com.extjs.gxt.ui.client.Style.LayoutRegion; import com.extjs.gxt.ui.client.core.XTemplate; import com.extjs.gxt.ui.client.event.ButtonEvent; import com.extjs.gxt.ui.client.event.SelectionListener; import com.extjs.gxt.ui.client.store.ListStore; import com.extjs.gxt.ui.client.widget.Component; import com.extjs.gxt.ui.client.widget.ContentPanel; import com.extjs.gxt.ui.client.widget.Dialog; import com.extjs.gxt.ui.client.widget.LayoutContainer; import com.extjs.gxt.ui.client.widget.MessageBox; import com.extjs.gxt.ui.client.widget.MessageBox.MessageBoxType; import com.extjs.gxt.ui.client.widget.Window; import com.extjs.gxt.ui.client.widget.form.CheckBox; import com.extjs.gxt.ui.client.widget.form.CheckBoxGroup; import com.extjs.gxt.ui.client.widget.form.ComboBox; import com.extjs.gxt.ui.client.widget.layout.FitLayout; import com.extjs.gxt.ui.client.widget.tips.ToolTipConfig; import com.google.gwt.dom.client.Style.Float; import com.google.gwt.user.client.ui.AbstractImagePrototype; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.HTMLTable; import com.google.gwt.user.client.ui.Widget; import com.google.inject.Singleton; import java.util.List; import org.sigmah.shared.dto.referential.AmendmentState; import org.sigmah.shared.dto.referential.CoreVersionAction; import org.sigmah.shared.dto.referential.CoreVersionActionType; /** * {@link ProjectPresenter} view. * * @author Denis Colliot (dcolliot@ideia.fr) */ @Singleton public class ProjectView extends AbstractView implements ProjectPresenter.View { /** * Project title max <em>displayed</em> length. */ private static final int PROJECT_TITLE_MAX_LENGTH = 110; /** * Amendments panel width. */ private static final float AMENDMENTS_PANEL_WIDTH = 250f; // CSS style names. public static final String STYLE_HEADER_BANNER = "banner"; public static final String STYLE_HEADER_BANNER_LOGO = "banner-logo"; public static final String STYLE_HEADER_BANNER_FLEX = "banner-flex"; // Grids coordinates. private static final Pair<Integer, Integer> HEADER_BANNER_LOGO_CELL = new Pair<Integer, Integer>(0, 0); private static final Pair<Integer, Integer> HEADER_BANNER_WIDGET_CELL = new Pair<Integer, Integer>(0, 1); // UI widgets. private ContentPanel projectBannerPanel; private Grid projectBannerGrid; private ContentPanel coreVersionPanel; private FlexTable coreVersionTable; private Button lockProjectCoreButton; private Button unlockProjectCoreButton; private Button validateVersionButton; private Button backToWorkingVersionButton; private ComboBox<CoreVersionAction> coreVersionActionComboBox; private SubMenuWidget subMenu; private LayoutContainer subViewPlaceHolder; private Button exportButton; private Button deleteButton; /** * {@inheritDoc} */ @Override public void initialize() { // -- // Banner container. // -- final LayoutContainer headerContainer = Layouts.border(); headerContainer.add(createProjectBannerPanel(), Layouts.borderLayoutData(LayoutRegion.CENTER, Margin.RIGHT)); headerContainer.add(createAmendmentsPanel(), Layouts.borderLayoutData(LayoutRegion.EAST, AMENDMENTS_PANEL_WIDTH)); // -- // Menu container. // -- final Map<Page, String> linksMap = new LinkedHashMap<Page, String>(); linksMap.put(Page.PROJECT_DASHBOARD, I18N.CONSTANTS.projectTabDashboard()); linksMap.put(Page.PROJECT_DETAILS, I18N.CONSTANTS.projectDetails()); linksMap.put(Page.PROJECT_LOGFRAME, I18N.CONSTANTS.projectTabLogFrame()); linksMap.put(Page.PROJECT_INDICATORS_MANAGEMENT, I18N.CONSTANTS.projectTabIndicators()); linksMap.put(Page.PROJECT_INDICATORS_MAP, I18N.CONSTANTS.projectTabMap()); linksMap.put(Page.PROJECT_INDICATORS_ENTRIES, I18N.CONSTANTS.projectTabDataEntry()); linksMap.put(Page.PROJECT_TEAM_MEMBERS, I18N.CONSTANTS.projectTabTeamMembers()); linksMap.put(Page.PROJECT_CALENDAR, I18N.CONSTANTS.projectTabCalendar()); linksMap.put(Page.PROJECT_REPORTS, I18N.CONSTANTS.projectTabReports()); final Grid buttonsGrid = new Grid(1, 2); exportButton = Forms.button(I18N.CONSTANTS.export(), IconImageBundle.ICONS.excel()); deleteButton = Forms.button(I18N.CONSTANTS.deleteProjectAnchor(), IconImageBundle.ICONS.remove()); buttonsGrid.setWidget(0, 0, exportButton); buttonsGrid.setWidget(0, 1, deleteButton); buttonsGrid.getElement().getStyle().setFloat(Float.RIGHT); subMenu = new SubMenuWidget(Orientation.HORIZONTAL, linksMap); subMenu.asWidget().getElement().getStyle().setFloat(Float.LEFT); final FlowPanel menuContainer = new FlowPanel(); menuContainer.add(subMenu.asWidget()); menuContainer.add(buttonsGrid); // -- // Center container. // -- subViewPlaceHolder = Layouts.fit(); // -- // Main layout. // -- final LayoutContainer mainContainer = Layouts.vBox(); mainContainer.add(headerContainer, Layouts.vBoxData(Margin.BOTTOM)); mainContainer.add(menuContainer); add(mainContainer, Layouts.borderLayoutData(LayoutRegion.NORTH, Layouts.BANNER_PANEL_HEIGHT, Margin.BOTTOM)); add(subViewPlaceHolder); } /** * {@inheritDoc} */ @Override public LayoutContainer getPlaceHolder() { return subViewPlaceHolder; } /** * {@inheritDoc} */ @Override public SubMenuWidget getSubMenuWidget() { return subMenu; } /** * {@inheritDoc} */ @Override public void setProjectTitle(final String projectName, final String projectFullName) { final StringBuilder builder = new StringBuilder(); builder.append(I18N.CONSTANTS.projectMainTabTitle()).append(' ').append(projectName); if (ClientUtils.isNotBlank(projectFullName)) { builder.append(" (").append(ClientUtils.abbreviate(projectFullName, PROJECT_TITLE_MAX_LENGTH)).append(')'); } // Panel header title. projectBannerPanel.setHeadingText(builder.toString()); // Tool tip configuration. final ToolTipConfig projectBannerToolTipConfig = new ToolTipConfig(builder.toString()); projectBannerToolTipConfig.setMaxWidth(500); projectBannerPanel.setToolTip(projectBannerToolTipConfig); } /** * {@inheritDoc} */ @Override public void setProjectLogo(final ProjectModelType projectType) { final AbstractImagePrototype projectIcon = FundingIconProvider.getProjectTypeIcon(projectType, IconSize.LARGE); projectBannerGrid.setWidget(HEADER_BANNER_LOGO_CELL.left, HEADER_BANNER_LOGO_CELL.right, projectIcon.createImage()); } /** * {@inheritDoc} */ @Override public void setProjectBanner(final Widget bannerWidget) { projectBannerGrid.setWidget(HEADER_BANNER_WIDGET_CELL.left, HEADER_BANNER_WIDGET_CELL.right, bannerWidget); } /** * {@inheritDoc} */ @Override public HTMLTable buildBannerTable(final int rows, final int cols) { final Grid gridLayout = new Grid(rows, cols); gridLayout.addStyleName(ProjectView.STYLE_HEADER_BANNER_FLEX); gridLayout.setCellPadding(0); gridLayout.setCellSpacing(0); gridLayout.setWidth("100%"); gridLayout.setHeight("100%"); for (int i = 0; i < gridLayout.getColumnCount() - 1; i++) { gridLayout.getColumnFormatter().setWidth(i, "325px"); } return gridLayout; } /** * {@inheritDoc} */ @Override public ContentPanel getProjectCoreVersionPanel() { return coreVersionPanel; } /** * {@inheritDoc} */ @Override public Button getLockProjectCoreButton() { return lockProjectCoreButton; } /** * {@inheritDoc} */ @Override public Button getUnlockProjectCoreButton() { return unlockProjectCoreButton; } /** * {@inheritDoc} */ @Override public Button getValidateVersionButton() { return validateVersionButton; } /** * {@inheritDoc} */ @Override public Button getBackToWorkingVersionButton() { return backToWorkingVersionButton; } /** * {@inheritDoc} */ @Override public ComboBox<CoreVersionAction> getCoreVersionActionComboBox() { return coreVersionActionComboBox; } /** * {@inheritDoc} */ @Override public Button getExportButton() { return exportButton; } /** * {@inheritDoc} */ @Override public Button getDeleteButton() { return deleteButton; } /** * {@inheritDoc} */ @Override public ContentPanel getProjectBannerPanel() { return projectBannerPanel; } /** * {@inheritDoc} */ @Override public void buildExportDialog(final ExportActionHandler handler) { final Window w = new Window(); w.setPlain(true); w.setModal(true); w.setBlinkModal(true); w.setLayout(new FitLayout()); w.setSize(350, 180); w.setHeadingHtml(I18N.CONSTANTS.exportData()); final FormPanel panel = Forms.panel(); final CheckBox synthesisBox = Forms.checkbox(I18N.CONSTANTS.projectSynthesis(), Boolean.TRUE); synthesisBox.setEnabled(false); final CheckBox indicatorBox = Forms.checkbox(I18N.CONSTANTS.flexibleElementIndicatorsList()); final CheckBox logFrameBox = Forms.checkbox(I18N.CONSTANTS.logFrame()); final CheckBox contactsBox = Forms.checkbox(I18N.CONSTANTS.contacts()); final CheckBoxGroup options = Forms.checkBoxGroup(I18N.CONSTANTS.exportOptions(), com.extjs.gxt.ui.client.Style.Orientation.VERTICAL, synthesisBox, logFrameBox, indicatorBox, contactsBox); panel.add(options); final Button export = Forms.button(I18N.CONSTANTS.export()); panel.getButtonBar().add(export); export.addSelectionListener(new SelectionListener<ButtonEvent>() { @Override public void componentSelected(final ButtonEvent ce) { if (handler != null) { handler.onExportProject(indicatorBox, logFrameBox, contactsBox); } w.hide(); } }); w.add(panel); w.show(); } /** * {@inheritDoc} */ @Override public void buildCreateCoreVersionDialog(final SelectionListener<ButtonEvent> callback) { final MessageBox box = new MessageBox(); box.setTitleHtml(I18N.CONSTANTS.projectCoreValidateVersion()); box.setMessage(I18N.CONSTANTS.projectCoreNewVersion()); box.setType(MessageBoxType.PROMPT); // No automatic button. box.setButtons(""); final Dialog dialog = box.getDialog(); final SelectionListener<ButtonEvent> hideDialog = new SelectionListener<ButtonEvent>() { @Override public void componentSelected(ButtonEvent ce) { dialog.hide(ce.getButton()); } }; // Create core version button. final Button okButton = new Button(I18N.CONSTANTS.projectCoreNewVersionButton()); okButton.addSelectionListener(hideDialog); okButton.addSelectionListener(new SelectionListener<ButtonEvent>() { @Override public void componentSelected(ButtonEvent ce) { ce.setSource(box.getTextBox().getValue()); callback.handleEvent(ce); } }); // Cancel button. final Button cancelButton = new Button(GXT.MESSAGES.messageBox_cancel()); cancelButton.addSelectionListener(hideDialog); dialog.addButton(cancelButton); dialog.addButton(okButton); box.show(); } /** * Changes the current view to match the given state. * * @param state Current project core version state. * @param coreVersionWasModified State of the current project core version (modified or not). */ @Override public void setProjectCoreVersionState(AmendmentState state, boolean coreVersionWasModified) { coreVersionTable.clear(); coreVersionTable.removeAllRows(); final boolean locked = state == AmendmentState.LOCKED; final boolean ended = state == AmendmentState.PROJECT_ENDED; if(state == AmendmentState.DRAFT || locked || ended) { validateVersionButton.setEnabled(locked && !ended); lockProjectCoreButton.setEnabled(!ended); unlockProjectCoreButton.setEnabled(true); coreVersionTable.insertRow(0); coreVersionTable.insertRow(0); coreVersionTable.addCell(0); coreVersionTable.addCell(0); coreVersionTable.addCell(1); coreVersionTable.getFlexCellFormatter().setWidth(0, 0, "50%"); coreVersionTable.getFlexCellFormatter().setWidth(0, 1, "50%"); coreVersionTable.getFlexCellFormatter().setColSpan(1, 0, 2); coreVersionTable.setWidget(0, 0, locked && !ended ? unlockProjectCoreButton : lockProjectCoreButton); coreVersionTable.setWidget(0, 1, validateVersionButton); coreVersionTable.setWidget(1, 0, coreVersionActionComboBox); } else { coreVersionTable.setWidget(0, 0, backToWorkingVersionButton); coreVersionTable.setWidget(1, 0, coreVersionActionComboBox); } coreVersionPanel.layout(true); } /** * Displays the given core versions in the core version combo box. * * @param coreVersions List of project core versions to display. * @param coreVersionWasModified State of the current project core version (modified or not). * @param canRenameVersion <code>true</code> to add the "Rename core version" menu item. */ @Override public void setProjectCoreVersions(List<AmendmentDTO> coreVersions, boolean coreVersionWasModified, boolean canRenameVersion) { final ListStore<CoreVersionAction> store = coreVersionActionComboBox.getStore(); store.removeAll(); // Add compare and rename actions. coreVersionActionComboBox.getStore().add(CoreVersionEntry.create(I18N.CONSTANTS.amendmentCompare(), CoreVersionActionType.FUNCTION_COMPARE)); if(canRenameVersion) { coreVersionActionComboBox.getStore().add(CoreVersionEntry.create(I18N.CONSTANTS.amendmentRename(), CoreVersionActionType.FUNCTION_RENAME)); } // Add the separator if needed. if(!coreVersions.isEmpty()) { // Displays the name of the last core version if the project core has not been modified. final AmendmentDTO lastCoreVersion = coreVersions.get(coreVersions.size() - 1); coreVersionActionComboBox.setEmptyText(!coreVersionWasModified ? lastCoreVersion.getVersion() + ". " + lastCoreVersion.getName() : ""); // Add a separator between the actions and the core versions. store.add(CoreVersionEntry.createSeparator()); store.add(CoreVersionEntry.createComment(I18N.CONSTANTS.projectCoreDisplayVersion())); // Add the core versions to the store. for(final AmendmentDTO coreVersion : coreVersions) { coreVersion.set(CoreVersionEntry.TYPE, CoreVersionActionType.CORE_VERSION.name()); store.add(coreVersion); } } else { coreVersionActionComboBox.setEmptyText(I18N.CONSTANTS.projectCoreNoValidated()); } } /** * Creates the project banner panel. * * @return The project banner panel. */ private Component createProjectBannerPanel() { // Main panel. projectBannerPanel = Panels.content(I18N.CONSTANTS.loading()); // Temporary title. // Main grid. projectBannerGrid = new Grid(1, 2); projectBannerGrid.addStyleName(STYLE_HEADER_BANNER); projectBannerGrid.setCellPadding(0); projectBannerGrid.setCellSpacing(0); projectBannerGrid.setWidth("100%"); projectBannerGrid.setHeight("100%"); // Logo cell style. projectBannerGrid.getCellFormatter().setStyleName(HEADER_BANNER_LOGO_CELL.left, HEADER_BANNER_LOGO_CELL.right, STYLE_HEADER_BANNER_LOGO); projectBannerPanel.add(projectBannerGrid); return projectBannerPanel; } /** * Creates the amendments panel. * * @return The amendments panel component. */ private Component createAmendmentsPanel() { // Lock project core version button. lockProjectCoreButton = Forms.button(I18N.CONSTANTS.projectCorelockButton(), IconImageBundle.ICONS.lock()); // Unlock project core version button. unlockProjectCoreButton = Forms.button(I18N.CONSTANTS.projectCoreUnlockButton(), IconImageBundle.ICONS.unlock()); // Validate project core version button. validateVersionButton = Forms.button(I18N.CONSTANTS.projectCoreValidateVersion(), IconImageBundle.ICONS.validate()); // Back to working version button. backToWorkingVersionButton = Forms.button(I18N.CONSTANTS.projectCoreBackToWorkingVersion(), IconImageBundle.ICONS.amendment()); // Core version and actions combo box. coreVersionActionComboBox = new ComboBox<CoreVersionAction>(); coreVersionActionComboBox.setDisplayField("name"); coreVersionActionComboBox.setValueField("id"); coreVersionActionComboBox.setStore(new ListStore<CoreVersionAction>()); coreVersionActionComboBox.setTemplate(XTemplate.create(getTemplate())); coreVersionActionComboBox.setItemSelector(".x-combo-list-item"); coreVersionActionComboBox.setTriggerAction(ComboBox.TriggerAction.ALL); // Define a width of 100% to all buttons. expandWidth(lockProjectCoreButton, unlockProjectCoreButton, validateVersionButton, backToWorkingVersionButton, coreVersionActionComboBox); // Layout. coreVersionTable = new FlexTable(); coreVersionTable.setCellSpacing(6); // Content panel. coreVersionPanel = Panels.content(I18N.CONSTANTS.projectCoreBoxTitle()); coreVersionPanel.setIcon(IconImageBundle.ICONS.DNABrownRed()); coreVersionPanel.add(coreVersionTable); return coreVersionPanel; } private void expandWidth(Component... components) { for(final Component component : components) { component.setWidth("100%"); } } /** * Creates the XTemplate for the project core version combo box. * @return The XTemplate used by the project core version combo box. */ private native String getTemplate() /*-{ return [ '<tpl for=".">', '<tpl if="entry == "CORE_VERSION"">', '<div class="x-combo-list-item" style="position: relative"><span style="font-weight: bold">{version}.</span> {name} <div style="color: #CCC; width: 60px; position: absolute; right: 0; top: 2px">{history_date:date("M/d/yy")}</div></div>', '</tpl>', '<tpl if="entry == "FUNCTION_COMPARE" || entry == "FUNCTION_RENAME"">', '<div class="x-combo-list-item">{name}</div>', '</tpl>', '<tpl if="entry == "SEPARATOR"">', '<div class="x-combo-list-item x-combo-unselectable" style="padding: 0; margin: 0"><hr style="border: 0; border-bottom: 1px solid #CCC"/></div>', '</tpl>', '<tpl if="entry == "COMMENT"">', '<div class="x-combo-list-item x-combo-unselectable" style="font-style: italic; color: #CCC; padding: 2px">{name}</div>', '</tpl>', '</tpl>' ].join(""); }-*/; }