diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java index 0c1036c5f286ed79f431b4532e70d8352ba1a16f..6f6906437f842e77f9449d5173864aa056b7ed12 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java @@ -78,6 +78,16 @@ public abstract class Dict public static final String ENTITY_OPERATIONS = "entity_operations"; + // + // Table Modifications + // + + public static final String TABLE_MODIFICATIONS = "table_modifications"; + + public static final String TABLE_MODIFICATIONS_INFO_TITLE = "table_modifications_info_title"; + + public static final String TABLE_MODIFICATIONS_INFO_TEXT = "table_modifications_info_text"; + // // Field // diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractBrowserGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractBrowserGrid.java index e084122ff9855798d30f577fe442c1e3ee3d0d9e..a727294bb59388b1aadcbf97e93ac1f2ce9dbbcd 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractBrowserGrid.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractBrowserGrid.java @@ -51,7 +51,6 @@ import com.extjs.gxt.ui.client.widget.ContentPanel; import com.extjs.gxt.ui.client.widget.Dialog; import com.extjs.gxt.ui.client.widget.Info; import com.extjs.gxt.ui.client.widget.InfoConfig; -import com.extjs.gxt.ui.client.widget.Label; import com.extjs.gxt.ui.client.widget.LayoutContainer; import com.extjs.gxt.ui.client.widget.MessageBox; import com.extjs.gxt.ui.client.widget.button.Button; @@ -69,17 +68,14 @@ import com.extjs.gxt.ui.client.widget.menu.Menu; import com.extjs.gxt.ui.client.widget.toolbar.PagingToolBar; import com.extjs.gxt.ui.client.widget.toolbar.SeparatorToolItem; import com.extjs.gxt.ui.client.widget.toolbar.ToolBar; -import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.AbstractImagePrototype; import ch.systemsx.cisd.common.shared.basic.utils.StringUtils; import ch.systemsx.cisd.openbis.generic.client.web.client.ICommonClientServiceAsync; import ch.systemsx.cisd.openbis.generic.client.web.client.application.AbstractAsyncCallback; import ch.systemsx.cisd.openbis.generic.client.web.client.application.Dict; import ch.systemsx.cisd.openbis.generic.client.web.client.application.GenericConstants; -import ch.systemsx.cisd.openbis.generic.client.web.client.application.IGenericImageBundle; import ch.systemsx.cisd.openbis.generic.client.web.client.application.IViewContext; import ch.systemsx.cisd.openbis.generic.client.web.client.application.VoidAsyncCallback; import ch.systemsx.cisd.openbis.generic.client.web.client.application.framework.AbstractTabItemFactory; @@ -102,6 +98,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.specific.GridCustomColumnDefinition; import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.grid.BrowserGridPagingToolBar.PagingToolBarButtonKind; import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.grid.expressions.filter.FilterToolbar; +import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.grid.modifications.ModificationsToolbar; import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.widget.IDataRefreshCallback; import ch.systemsx.cisd.openbis.generic.client.web.client.application.util.GWTUtils; import ch.systemsx.cisd.openbis.generic.client.web.client.application.util.GWTUtils.DisplayInfoTime; @@ -220,6 +217,9 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod private final ToolBar modificationsToolbar; + @SuppressWarnings("unused") + private final TableModificationsManager tableModificationsManager; + private final IDisplayTypeIDGenerator displayTypeIDGenerator; // --------- private non-final fields @@ -247,9 +247,6 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod this(viewContext, gridId, false, displayTypeIDGenerator); } - private static final IGenericImageBundle IMAGE_BUNDLE = GWT - .<IGenericImageBundle> create(IGenericImageBundle.class); - /** * @param refreshAutomatically should the data be automatically loaded when the grid is rendered * for the first time? @@ -279,30 +276,8 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod pagingToolbar.bind(pagingLoader); this.filterToolbar = new FilterToolbar<T>(viewContext, gridId, this, createApplyFiltersDelagator()); - this.modificationsToolbar = new ToolBar(); - modificationsToolbar.add(new Label("Table Modifications:")); - AbstractImagePrototype confirmIcon = - AbstractImagePrototype.create(IMAGE_BUNDLE.getConfirmIcon()); - AbstractImagePrototype cancelIcon = - AbstractImagePrototype.create(IMAGE_BUNDLE.getCancelIcon()); - modificationsToolbar.add(new Button("Save", confirmIcon, - new SelectionListener<ButtonEvent>() - { - @Override - public void componentSelected(ButtonEvent be) - { - asActionInvoker().saveModifications(); - } - })); - modificationsToolbar.add(new Button("Cancel", cancelIcon, - new SelectionListener<ButtonEvent>() - { - @Override - public void componentSelected(ButtonEvent be) - { - asActionInvoker().cancelModifications(); - } - })); + this.tableModificationsManager = new TableModificationsManager(); + this.modificationsToolbar = new ModificationsToolbar(viewContext, asActionInvoker()); this.contentPanel = createEmptyContentPanel(); bottomToolbars = createBottomToolbars(contentPanel, pagingToolbar); @@ -1008,15 +983,6 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod }; } - private void clearModifications() - { - finishedModifications = 0; - failedModifications.clear(); - modificationsByModel.clear(); - hideModificationsBar(); - refresh(); - } - protected void showFiltersBar() { // always show filters under modifications @@ -1029,9 +995,8 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod { if (bottomToolbars.getItems().contains(modificationsToolbar) == false) { - GWTUtils.displayInfo( - "Table Modification Mode On", - "Use 'Table Modification' toolbar below the table to <b>save</b> or <b>cancel</b> the changes made in table cells.", + GWTUtils.displayInfo(viewContext.getMessage(Dict.TABLE_MODIFICATIONS_INFO_TITLE), + viewContext.getMessage(Dict.TABLE_MODIFICATIONS_INFO_TEXT), DisplayInfoTime.LONG); bottomToolbars.insert(modificationsToolbar, 0); bottomToolbars.layout(); @@ -1860,6 +1825,126 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod return null; } + // this should be the only place where we create the grid column model. + private static ColumnModel createColumnModel(List<ColumnConfig> columConfigs) + { + return new ColumnModel(columConfigs); + } + + protected ColumnModel getFullColumnModel() + { + return fullColumnModel; + } + + private static final class ExportEntitiesCallback extends AbstractAsyncCallback<String> + { + public ExportEntitiesCallback(final IViewContext<ICommonClientServiceAsync> viewContext) + { + super(viewContext); + } + + @Override + protected void process(final String exportDataKey) + { + final URLMethodWithParameters methodWithParameters = + new URLMethodWithParameters( + GenericConstants.FILE_EXPORTER_DOWNLOAD_SERVLET_NAME); + methodWithParameters.addParameter(GenericConstants.EXPORT_CRITERIA_KEY_PARAMETER, + exportDataKey); + WindowUtils.openWindow(methodWithParameters.toString()); + } + + } + + // creates a map to quickly find a definition by its identifier + private static <T> Map<String, IColumnDefinition<T>> asColumnIdMap( + final Set<IColumnDefinition<T>> defs) + { + final Map<String, IColumnDefinition<T>> map = new HashMap<String, IColumnDefinition<T>>(); + for (final IColumnDefinition<T> def : defs) + { + map.put(def.getIdentifier(), def); + } + return map; + } + + /** + * @param availableColumns map of all available columns definitions. + * @param columnModel describes the visual properties of the columns. Connected with + * availableColumns by column id. + * @return list of columns definitions for those columns which are currently shown + */ + private static <T/* column definition */> List<T> getVisibleColumns( + final Map<String, T> availableColumns, final ColumnModel columnModel) + { + final List<T> selectedColumnDefs = new ArrayList<T>(); + final int columnCount = columnModel.getColumnCount(); + for (int i = 0; i < columnCount; i++) + { + if (columnModel.isHidden(i) == false) + { + final String columnId = columnModel.getColumnId(i); + selectedColumnDefs.add(availableColumns.get(columnId)); + } + } + return selectedColumnDefs; + } + + /** Creates callback that refreshes the grid. */ + protected final AbstractAsyncCallback<Void> createRefreshCallback( + IBrowserGridActionInvoker invoker) + { + return new RefreshCallback(viewContext, invoker); + } + + /** Callback that refreshes the grid. */ + private static final class RefreshCallback extends AbstractAsyncCallback<Void> + { + private final IBrowserGridActionInvoker invoker; + + public RefreshCallback(IViewContext<?> viewContext, IBrowserGridActionInvoker invoker) + { + super(viewContext); + this.invoker = invoker; + } + + @Override + protected void process(Void result) + { + invoker.refresh(); + } + } + + /** {@link SelectionListener} that creates a dialog with selected data items. */ + protected abstract class AbstractCreateDialogListener extends SelectionListener<ButtonEvent> + { + @Override + public void componentSelected(ButtonEvent ce) + { + List<T> data = getSelectedBaseObjects(); + IBrowserGridActionInvoker invoker = asActionInvoker(); + if (validateSelectedData(data)) + { + createDialog(data, invoker).show(); + } + } + + /** + * If specified data is valid returns true, otherwise returns false. Dialog will be shown + * only if this method returns true. Default implementation always returns true. + */ + protected boolean validateSelectedData(List<T> data) + { + return true; + } + + protected abstract Dialog createDialog(List<T> data, IBrowserGridActionInvoker invoker); + } + + // + // Table Modifications + // + public interface IModification { String getColumnID(); @@ -1891,6 +1976,13 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod } } + private static class TableModificationsManager + { + + } + + // TODO 2011-08-06, Piotr Buczek: refactor the code - move to TableModificationsManager + private final Map<M, String> failedModifications = new HashMap<M, String>(); private int finishedModifications = 0; @@ -1898,6 +1990,15 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod private final Map<M, List<IModification>> modificationsByModel = new LinkedHashMap<M, List<IModification>>(); + private void clearModifications() + { + finishedModifications = 0; + failedModifications.clear(); + modificationsByModel.clear(); + hideModificationsBar(); + refresh(); + } + /** Handle cell editing event. */ private void handleEditingEvent(M model, String columnID, String newValueOrNull) { @@ -1919,21 +2020,6 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod } - /** - * Creates a callback object which invokes {@link #refresh()} after server-side editing action - * took place. - */ - protected AsyncCallback<Void> createPostEditingRefreshCallback() - { - return createPostEditingCallback(new IDelegatedAction() - { - public void execute() - { - asActionInvoker().refresh(); - } - }); - } - protected AsyncCallback<EntityPropertyUpdatesResult> createApplyModificationsCallback( final M model, final List<IModification> modifications) { @@ -2007,6 +2093,21 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod return result.toString(); } + /** + * Creates a callback object which invokes {@link #refresh()} after server-side editing action + * took place. + */ + protected AsyncCallback<Void> createPostEditingRefreshCallback() + { + return createPostEditingCallback(new IDelegatedAction() + { + public void execute() + { + asActionInvoker().refresh(); + } + }); + } + /** * Creates a callback object which invokes specified refresh action after server-side editing * action took place. @@ -2029,120 +2130,4 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod }; } - // this should be the only place where we create the grid column model. - private static ColumnModel createColumnModel(List<ColumnConfig> columConfigs) - { - return new ColumnModel(columConfigs); - } - - protected ColumnModel getFullColumnModel() - { - return fullColumnModel; - } - - private static final class ExportEntitiesCallback extends AbstractAsyncCallback<String> - { - public ExportEntitiesCallback(final IViewContext<ICommonClientServiceAsync> viewContext) - { - super(viewContext); - } - - @Override - protected void process(final String exportDataKey) - { - final URLMethodWithParameters methodWithParameters = - new URLMethodWithParameters( - GenericConstants.FILE_EXPORTER_DOWNLOAD_SERVLET_NAME); - methodWithParameters.addParameter(GenericConstants.EXPORT_CRITERIA_KEY_PARAMETER, - exportDataKey); - WindowUtils.openWindow(methodWithParameters.toString()); - } - - } - - // creates a map to quickly find a definition by its identifier - private static <T> Map<String, IColumnDefinition<T>> asColumnIdMap( - final Set<IColumnDefinition<T>> defs) - { - final Map<String, IColumnDefinition<T>> map = new HashMap<String, IColumnDefinition<T>>(); - for (final IColumnDefinition<T> def : defs) - { - map.put(def.getIdentifier(), def); - } - return map; - } - - /** - * @param availableColumns map of all available columns definitions. - * @param columnModel describes the visual properties of the columns. Connected with - * availableColumns by column id. - * @return list of columns definitions for those columns which are currently shown - */ - private static <T/* column definition */> List<T> getVisibleColumns( - final Map<String, T> availableColumns, final ColumnModel columnModel) - { - final List<T> selectedColumnDefs = new ArrayList<T>(); - final int columnCount = columnModel.getColumnCount(); - for (int i = 0; i < columnCount; i++) - { - if (columnModel.isHidden(i) == false) - { - final String columnId = columnModel.getColumnId(i); - selectedColumnDefs.add(availableColumns.get(columnId)); - } - } - return selectedColumnDefs; - } - - /** Creates callback that refreshes the grid. */ - protected final AbstractAsyncCallback<Void> createRefreshCallback( - IBrowserGridActionInvoker invoker) - { - return new RefreshCallback(viewContext, invoker); - } - - /** Callback that refreshes the grid. */ - private static final class RefreshCallback extends AbstractAsyncCallback<Void> - { - private final IBrowserGridActionInvoker invoker; - - public RefreshCallback(IViewContext<?> viewContext, IBrowserGridActionInvoker invoker) - { - super(viewContext); - this.invoker = invoker; - } - - @Override - protected void process(Void result) - { - invoker.refresh(); - } - } - - /** {@link SelectionListener} that creates a dialog with selected data items. */ - protected abstract class AbstractCreateDialogListener extends SelectionListener<ButtonEvent> - { - @Override - public void componentSelected(ButtonEvent ce) - { - List<T> data = getSelectedBaseObjects(); - IBrowserGridActionInvoker invoker = asActionInvoker(); - if (validateSelectedData(data)) - { - createDialog(data, invoker).show(); - } - } - - /** - * If specified data is valid returns true, otherwise returns false. Dialog will be shown - * only if this method returns true. Default implementation always returns true. - */ - protected boolean validateSelectedData(List<T> data) - { - return true; - } - - protected abstract Dialog createDialog(List<T> data, IBrowserGridActionInvoker invoker); - } - } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/modifications/ModificationsToolbar.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/modifications/ModificationsToolbar.java new file mode 100644 index 0000000000000000000000000000000000000000..9b1379a7da36ba5fa28ae47d6aa7b2e278833012 --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/modifications/ModificationsToolbar.java @@ -0,0 +1,78 @@ +/* + * Copyright 2011 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.grid.modifications; + +import com.extjs.gxt.ui.client.event.ButtonEvent; +import com.extjs.gxt.ui.client.event.SelectionListener; +import com.extjs.gxt.ui.client.widget.Label; +import com.extjs.gxt.ui.client.widget.button.Button; +import com.extjs.gxt.ui.client.widget.toolbar.ToolBar; +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.ui.AbstractImagePrototype; + +import ch.systemsx.cisd.openbis.generic.client.web.client.application.Dict; +import ch.systemsx.cisd.openbis.generic.client.web.client.application.IGenericImageBundle; +import ch.systemsx.cisd.openbis.generic.client.web.client.application.IViewContext; +import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.grid.IBrowserGridActionInvoker; +import ch.systemsx.cisd.openbis.generic.client.web.client.application.util.IMessageProvider; + +/** + * Toolbar for handling table modifications. + * + * @author Piotr Buczek + */ +public class ModificationsToolbar extends ToolBar +{ + private static final IGenericImageBundle IMAGE_BUNDLE = GWT + .<IGenericImageBundle> create(IGenericImageBundle.class); + + private final IMessageProvider messageProvider; + + @SuppressWarnings("unused") + private final IViewContext<?> viewContext; // TODO remove if not used + + public ModificationsToolbar(final IViewContext<?> viewContext, + final IBrowserGridActionInvoker browserActionInvoker) + { + this.viewContext = viewContext; + this.messageProvider = viewContext; + + add(new Label(messageProvider.getMessage(Dict.TABLE_MODIFICATIONS))); + + final AbstractImagePrototype confirmIcon = + AbstractImagePrototype.create(IMAGE_BUNDLE.getConfirmIcon()); + final AbstractImagePrototype cancelIcon = + AbstractImagePrototype.create(IMAGE_BUNDLE.getCancelIcon()); + add(new Button("Save", confirmIcon, new SelectionListener<ButtonEvent>() + { + @Override + public void componentSelected(ButtonEvent be) + { + browserActionInvoker.saveModifications(); + } + })); + add(new Button("Cancel", cancelIcon, new SelectionListener<ButtonEvent>() + { + @Override + public void componentSelected(ButtonEvent be) + { + browserActionInvoker.cancelModifications(); + } + })); + } + +}