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();
+                }
+            }));
+    }
+
+}