ca.qc.cegepoutaouais.tge.pige.client.ui.view.LoanManagementView.java Source code

Java tutorial

Introduction

Here is the source code for ca.qc.cegepoutaouais.tge.pige.client.ui.view.LoanManagementView.java

Source

/*
 * Copyright 2010, 2011 Renaud Brub
 *
 * This file is part of PIGE.
 *
 * PIGE 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.
 *
 * PIGE 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 PIGE.  If not, see <http://www.gnu.org/licenses/>.
 */
package ca.qc.cegepoutaouais.tge.pige.client.ui.view;

import ca.qc.cegepoutaouais.tge.pige.client.HTMLLoanFormatter;
import ca.qc.cegepoutaouais.tge.pige.client.LoanStatus;
import ca.qc.cegepoutaouais.tge.pige.client.LoanStatus.LoanStatusModel;
import ca.qc.cegepoutaouais.tge.pige.client.services.ManagementServiceAsync;
import ca.qc.cegepoutaouais.tge.pige.client.ui.AppEvents;
import ca.qc.cegepoutaouais.tge.pige.client.ui.AppWidgets;
import ca.qc.cegepoutaouais.tge.pige.client.ui.widget.LoanAdvancedSearchBox;
import ca.qc.cegepoutaouais.tge.pige.client.ui.widget.LoanManagementSearchBox;
import ca.qc.cegepoutaouais.tge.pige.client.ui.widget.LoanStateFilterBox;
import ca.qc.cegepoutaouais.tge.pige.client.PIGE;
import ca.qc.cegepoutaouais.tge.pige.client.PigeUtil;
import ca.qc.cegepoutaouais.tge.pige.client.PrintViewer;
import ca.qc.cegepoutaouais.tge.pige.client.resources.Resources;
import ca.qc.cegepoutaouais.tge.pige.client.ui.dialog.AbstractCEVDialog.CEV;
import ca.qc.cegepoutaouais.tge.pige.client.ui.dialog.ItemCEVDialog;
import ca.qc.cegepoutaouais.tge.pige.client.ui.widget.AbstractFieldListener;
import ca.qc.cegepoutaouais.tge.pige.client.ui.widget.AbstractFieldListener.FieldUpdater;
import ca.qc.cegepoutaouais.tge.pige.client.ui.widget.StatusBar;
import ca.qc.cegepoutaouais.tge.pige.dao.pojos.Item;
import ca.qc.cegepoutaouais.tge.pige.dao.pojos.Loan;
import ca.qc.cegepoutaouais.tge.pige.dao.pojos.User;
import com.extjs.gxt.ui.client.Registry;
import com.extjs.gxt.ui.client.core.Template;
import com.extjs.gxt.ui.client.data.BaseFilterConfig;
import com.extjs.gxt.ui.client.data.BaseListFilterConfig;
import com.extjs.gxt.ui.client.data.BasePagingLoadConfig;
import com.extjs.gxt.ui.client.data.BasePagingLoader;
import com.extjs.gxt.ui.client.data.BaseStringFilterConfig;
import com.extjs.gxt.ui.client.data.BeanModel;
import com.extjs.gxt.ui.client.data.BeanModelReader;
import com.extjs.gxt.ui.client.data.FilterConfig;
import com.extjs.gxt.ui.client.data.LoadConfig;
import com.extjs.gxt.ui.client.data.LoadEvent;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.data.PagingLoadConfig;
import com.extjs.gxt.ui.client.data.PagingLoadResult;
import com.extjs.gxt.ui.client.data.RpcProxy;
import com.extjs.gxt.ui.client.event.ButtonEvent;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.EventType;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.FieldEvent;
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.MessageBoxEvent;
import com.extjs.gxt.ui.client.event.SelectionListener;
import com.extjs.gxt.ui.client.mvc.AppEvent;
import com.extjs.gxt.ui.client.mvc.Controller;
import com.extjs.gxt.ui.client.store.GroupingStore;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.store.Store;
import com.extjs.gxt.ui.client.store.StoreSorter;
import com.extjs.gxt.ui.client.util.Params;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.Html;
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;
import com.extjs.gxt.ui.client.widget.button.ButtonBar;
import com.extjs.gxt.ui.client.widget.button.ToggleButton;
import com.extjs.gxt.ui.client.widget.form.CheckBox;
import com.extjs.gxt.ui.client.widget.form.ComboBox;
import com.extjs.gxt.ui.client.widget.form.ComboBox.TriggerAction;
import com.extjs.gxt.ui.client.widget.form.DateField;
import com.extjs.gxt.ui.client.widget.form.NumberField;
import com.extjs.gxt.ui.client.widget.form.TextField;
import com.extjs.gxt.ui.client.widget.grid.CellEditor;
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.ColumnModel;
import com.extjs.gxt.ui.client.widget.grid.EditorGrid;
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.GridGroupRenderer;
import com.extjs.gxt.ui.client.widget.grid.GroupColumnData;
import com.extjs.gxt.ui.client.widget.grid.GroupingView;
import com.extjs.gxt.ui.client.widget.grid.filters.DateFilter;
import com.extjs.gxt.ui.client.widget.grid.filters.GridFilters;
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.RowData;
import com.extjs.gxt.ui.client.widget.layout.RowLayout;
import com.extjs.gxt.ui.client.widget.menu.AdapterMenuItem;
import com.extjs.gxt.ui.client.widget.menu.HeaderMenuItem;
import com.extjs.gxt.ui.client.widget.menu.Menu;
import com.extjs.gxt.ui.client.widget.menu.MenuItem;
import com.extjs.gxt.ui.client.widget.menu.SeparatorMenuItem;
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.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.Widget;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

/**
 * Vue permettant d'effectuer la gestions des emprunts.
 * Cette mme vue peut tre visualise de trois diffrentes manires.
 * Ces diffrentes perspectives permettent chacune d'effectuer une tche
 * de faon optimale (Prt, retour, recherche).
 *
 * @author Renaud Brub
 */
public class LoanManagementView extends BaseView {

    private static Integer buttonGroupId = 0;
    private final String BUTTON_GROUP_TAG = "lmbg-";

    private class ApprovalButtonGridCellRenderer implements GridCellRenderer<BeanModel> {

        public ApprovalButtonGridCellRenderer() {
        }

        @Override
        public Object render(final BeanModel model, String property, ColumnData config, final int rowIndex,
                final int colIndex, ListStore<BeanModel> store, Grid<BeanModel> grid) {

            String groupId = (++buttonGroupId).toString();
            String toggleGroup = BUTTON_GROUP_TAG + groupId;

            final Loan loan = model.getBean();
            ButtonBar buttonBar = new ButtonBar();
            final ToggleButton acceptBtn = new ToggleButton(messages.acceptedAbbr());
            final ToggleButton refuseBtn = new ToggleButton(messages.refusedAbbr());
            final ToggleButton postponeBtn = new ToggleButton(messages.postponeAbbr());

            acceptBtn.setWidth(50);
            refuseBtn.setWidth(50);
            postponeBtn.setWidth(50);
            acceptBtn.setHeight(20);
            refuseBtn.setHeight(20);
            postponeBtn.setHeight(20);
            acceptBtn.setToggleGroup(toggleGroup);
            refuseBtn.setToggleGroup(toggleGroup);
            postponeBtn.setToggleGroup(toggleGroup);

            if (loan.getStatus().equals(LoanStatus.STATUS_POSTPONED)) {
                postponeBtn.toggle(Boolean.TRUE);
            }

            acceptBtn.addListener(Events.Toggle, new Listener<ButtonEvent>() {

                @Override
                public void handleEvent(ButtonEvent event) {
                    if (acceptBtn.isPressed()) {
                        loan.setStatus(LoanStatus.STATUS_LENT);
                    } else {
                        loan.setStatus(LoanStatus.STATUS_PENDING);
                    }
                    loan.setStartDate(new Date());
                    setLoanDirty(model, Boolean.TRUE);
                }
            });
            refuseBtn.addListener(Events.Toggle, new Listener<ButtonEvent>() {

                @Override
                public void handleEvent(ButtonEvent event) {
                    if (refuseBtn.isPressed()) {
                        loan.setStatus(LoanStatus.STATUS_REFUSED);
                    } else {
                        loan.setStatus(LoanStatus.STATUS_PENDING);
                    }
                    loan.setStartDate(new Date());
                    setLoanDirty(model, Boolean.TRUE);
                }
            });
            postponeBtn.addListener(Events.Toggle, new Listener<ButtonEvent>() {

                @Override
                public void handleEvent(ButtonEvent event) {
                    if (postponeBtn.isPressed()) {
                        loan.setStatus(LoanStatus.STATUS_POSTPONED);
                    } else {
                        loan.setStatus(LoanStatus.STATUS_PENDING);
                    }
                    loan.setStartDate(new Date());
                    setLoanDirty(model, Boolean.TRUE);
                }
            });

            buttonBar.add(acceptBtn);
            buttonBar.add(refuseBtn);
            buttonBar.add(postponeBtn);
            buttonBar.setWidth(grid.getColumnModel().getColumnWidth(colIndex) - 10);

            return buttonBar;
        }
    };

    private class ReturnButtonGridCellRenderer implements GridCellRenderer<BeanModel> {

        public ReturnButtonGridCellRenderer() {
        }

        @Override
        public Object render(final BeanModel model, String property, ColumnData config, final int rowIndex,
                final int colIndex, ListStore<BeanModel> store, Grid<BeanModel> grid) {

            final Loan loan = model.getBean();
            ButtonBar buttonBar = new ButtonBar();
            final ToggleButton returnBtn = new ToggleButton(messages.returnAbbr());
            returnBtn.setWidth(50);
            returnBtn.setHeight(20);
            returnBtn.addListener(Events.Toggle, new Listener<ButtonEvent>() {

                @Override
                public void handleEvent(ButtonEvent event) {
                    if (returnBtn.isPressed()) {
                        loan.setStatus(LoanStatus.STATUS_RETURNED);
                    } else {
                        loan.setStatus(LoanStatus.STATUS_LENT);
                    }
                    setLoanDirty(model, Boolean.TRUE);
                }
            });

            buttonBar.add(returnBtn);
            buttonBar.setWidth(grid.getColumnModel().getColumnWidth(colIndex) - 10);
            return buttonBar;
        }
    };

    class LoanStatusChooser extends ComboBox<LoanStatusModel> {
        public LoanStatusChooser() {
            ListStore<LoanStatusModel> listStore = new ListStore();
            listStore.add(LoanStatus.getInstance().getModelList());
            listStore.remove(listStore.findModel("value", LoanStatus.STATUS_SAVED));
            listStore.remove(listStore.findModel("value", LoanStatus.STATUS_LATE));
            setStore(listStore);
            setWidth(100);
            setDisplayField("label");
            setTriggerAction(TriggerAction.ALL);
            setEditable(Boolean.FALSE);
            setForceSelection(Boolean.TRUE);
        }
    }

    private interface IStoreUpdater {
        public void updateStore(ListStore store, BeanModel model);
    }

    private class ApprovalStoreUpdater implements IStoreUpdater {

        @Override
        public void updateStore(ListStore store, BeanModel model) {
            if (store != null && model != null) {
                Loan loan = model.getBean();
                String status = loan.getStatus();
                if (status.equals(LoanStatus.STATUS_PENDING)) {
                    return;
                }
                if (status.equals(LoanStatus.STATUS_POSTPONED) && showPostponedLoanCbx.getValue()) {
                    return;
                }
                store.remove(model);
            }
        }
    }

    private class ReturnStoreUpdater implements IStoreUpdater {

        @Override
        public void updateStore(ListStore store, BeanModel model) {
            if (store != null && model != null) {
                Loan loan = model.getBean();
                if (loan.getStatus().equals(LoanStatus.STATUS_RETURNED)) {
                    store.remove(model);
                }
            }
        }
    }

    // 1.5.1
    private final class FieldListener extends AbstractFieldListener {

        public FieldListener(Menu rootMenu, FieldUpdater updater) {
            super(rootMenu, updater);
        }

        @Override
        protected void fireModelsUpdate() {
            if (selectedLoans == null || selectedLoans.isEmpty()) {
                MessageBox.info(messages.information(), messages.noLoanSelected(), null);
                return;
            }
            //super.fireModelsUpdate();
            doFireModelsUpdate();
        }

        @Override
        protected void doFireModelsUpdate() {
            List<BeanModel> models = selectedLoans;
            for (BeanModel bm : models) {
                bm.set(updater.getProperty(), updater.getValue());
                setLoanDirty(bm, Boolean.TRUE);
                loanStore.update(bm);
            }
            updater.getField().clear();
            rootMenu.hide();
        }
    }

    private int PAGE_SIZE = 25;
    private int SEARCH_PAGE_SIZE = 50;
    final String DATE_CHANGED = "dateChanged";

    private LayoutContainer toolBarPanel = null;
    private ToolBar mainTB = null;
    private ToolBar secondaryTB = null;
    private CheckBox showPostponedLoanCbx = null;
    private Button updateBtn = null;
    private Button searchBtn = null;
    private Button resetBtn = null;
    private Button printBtn = null;
    // 1.5.1
    private Button batchEditBtn = null;
    private MenuItem clearSelectedLoansMenuItem = null;

    private LoanStateFilterBox loanStateFilterBox = null;
    private LoanAdvancedSearchBox loanAdvancedSearchBox = null;
    private LoanManagementSearchBox loanManagementSearchBox = null;
    private List<FilterConfig> searchConfigList = null;
    private List<FilterConfig> filterConfigList = null;
    private List<FilterConfig> userConfigList = null;

    private RpcProxy<PagingLoadResult<Loan>> proxy = null;
    private BasePagingLoader<PagingLoadResult<ModelData>> storeLoader = null;
    private BasePagingLoadConfig loadConfigs = null;

    private Grid<BeanModel> grid = null;
    private GroupingStore<BeanModel> loanStore = null;
    private ColumnModel columnModel = null;
    private PagingToolBar pagingTB = null;
    final StatusBar statusBar = Registry.get(AppWidgets.APP_STATUS_BAR);

    private List<BeanModel> dirtyLoans = new ArrayList<BeanModel>();
    private IStoreUpdater storeUpdater = null;

    private BeanModel selectedLoan = null;
    private String descTemplateStr = "<div class=\"pige-description-column  {icon} \">"
            + "<span class=\"pige-description-column-text \">{desc}</span>" + "</div>";
    private Boolean isPrintButtonPresent = Boolean.FALSE;

    // 1.5.1
    private List<BeanModel> selectedLoans = new LinkedList<BeanModel>();

    public LoanManagementView(Controller controller) {
        super(controller, "");
    }

    @Override
    protected void handleEvent(AppEvent event) {
        super.handleEvent(event);
        EventType type = event.getType();
        if (type == AppEvents.LOAN_APPROVAL_VIEW_EVENT) {
            setApprovalMode();
        } else if (type == AppEvents.LOAN_RETURN_VIEW_EVENT) {
            setReturnMode();
        } else if (type == AppEvents.LOAN_SEARCH_VIEW_EVENT) {
            setSearchMode();
        }

        // 1.5.1
        selectedLoans.clear();

        onActivateView();
    }

    @Override
    protected void initialize() {
        super.initialize();

        proxy = new RpcProxy<PagingLoadResult<Loan>>() {

            @Override
            protected void load(Object loadConfig, final AsyncCallback<PagingLoadResult<Loan>> callback) {

                statusBar.setBusyText(messages.loading());

                // Obtenir le nombre de rle demand  partir du service RPC.
                ManagementServiceAsync rpcService = Registry.get(PIGE.MANAGEMENT_SERVICE);
                rpcService.getLoans((PagingLoadConfig) loadConfig, new AsyncCallback<PagingLoadResult<Loan>>() {

                    @Override
                    public void onSuccess(PagingLoadResult result) {
                        statusBar.getStatus().clearStatus(messages.loadDone());
                        callback.onSuccess(result);
                    }

                    @Override
                    public void onFailure(Throwable caught) {
                        callback.onFailure(caught);
                    }
                }); // getLoans()
            } // load()
        }; // new RpcProxy

        loadConfigs = new BasePagingLoadConfig();
        storeLoader = new BasePagingLoader<PagingLoadResult<ModelData>>(proxy, new BeanModelReader()) {

            @Override
            protected LoadConfig newLoadConfig() {
                return loadConfigs;
            }
        };
        storeLoader.setRemoteSort(false);

        loanStore = new GroupingStore<BeanModel>(storeLoader) {

            /**
             * Permet de retirer les emprunts sauvegards de la liste  afficher.
             */
            @Override
            protected void onLoad(LoadEvent event) {
                PigeUtil.filterSavedLoans(event);
                selectedLoans.clear();// 1.5.1
                super.onLoad(event);
            }
        };

        pagingTB = new PagingToolBar(PAGE_SIZE);
        pagingTB.bind(storeLoader);

        List<ColumnConfig> configs = new ArrayList<ColumnConfig>();
        // 1.5.1
        ColumnConfig selectorCC = new ColumnConfig(null, "", 25);
        selectorCC.setRenderer(new GridCellRenderer() {
            @Override
            public Object render(final ModelData model, String property, ColumnData config, int rowIndex,
                    int colIndex, ListStore store, Grid grid) {
                final CheckBox cbox = new CheckBox();
                cbox.addListener(Events.Change, new Listener<FieldEvent>() {
                    @Override
                    public void handleEvent(FieldEvent be) {
                        BeanModel bm = (BeanModel) model;
                        if (cbox.getValue() && !selectedLoans.contains(bm)) {
                            selectedLoans.add(bm);
                        } else if (!cbox.getValue()) {
                            selectedLoans.remove(bm);
                        }
                    }
                });
                BeanModel bm = (BeanModel) model;
                if (selectedLoans.contains(bm)) {
                    cbox.setValue(Boolean.TRUE);
                }
                return cbox;
            }
        });
        ColumnConfig ownerCC = new ColumnConfig(Loan.USER_REF, "user", 50);
        ColumnConfig statusCC = new ColumnConfig(Loan.STATUS_REF,
                messages.accepted() + " / " + messages.refused() + " / " + messages.postponed(), 150);
        ColumnConfig quantityCC = new ColumnConfig(Loan.QUANTITY_REF, messages.quantity(), 50);
        NumberFormat nf = NumberFormat.getFormat(PIGE.QUANTITY_FORMAT);
        quantityCC.setNumberFormat(nf);

        ColumnConfig descriptionCC = new ColumnConfig(Loan.DESCRIPTION_REF, messages.description(), 250);
        final Template descTemplate = new Template(descTemplateStr);
        descTemplate.compile();
        descriptionCC.setRenderer(new GridCellRenderer() {

            @Override
            public Object render(ModelData model, String property, ColumnData config, int rowIndex, int colIndex,
                    ListStore store, Grid grid) {
                Loan loan = (Loan) ((BeanModel) model).getBean();
                Params p = new Params();
                p.set("desc", model.get(property));
                if (loan.getItemId() != null) {
                    p.set("icon", "pige-icon-visible");
                } else {
                    p.set("icon", "pige-icon-hidden");
                }
                return descTemplate.applyTemplate(p);
            }
        });

        ColumnConfig startDateCC = new ColumnConfig(Loan.START_DATE_REF, messages.loanDate(), 100);
        DateTimeFormat dtf = DateTimeFormat.getFormat(PIGE.TERM_DATE_FORMAT);
        startDateCC.setDateTimeFormat(dtf);
        ColumnConfig termCC = new ColumnConfig(Loan.TERM_REF, messages.term(), 100);
        termCC.setDateTimeFormat(dtf);
        ColumnConfig tagCC = new ColumnConfig(Loan.TAG_REF, messages.tag(), 100);
        ColumnConfig noteCC = new ColumnConfig(Loan.NOTE_REF, messages.note(), 100);

        // 1.5.1
        configs.add(selectorCC);

        configs.add(ownerCC);
        configs.add(statusCC);
        configs.add(quantityCC);
        configs.add(descriptionCC);
        configs.add(startDateCC);
        configs.add(termCC);
        configs.add(tagCC);
        configs.add(noteCC);

        columnModel = new ColumnModel(configs);
        GroupingView view = new GroupingView();
        view.setShowGroupedColumn(false);
        view.setForceFit(true);
        view.setGroupRenderer(new GridGroupRenderer() {

            @Override
            public String render(GroupColumnData data) {
                BeanModel model = (BeanModel) loanStore.getAt(data.startRow);
                Loan loan = model.getBean();
                User user = loan.getUser();
                String id = (user.getLoanNo() != null ? user.getLoanNo() : user.getIdentifier());
                return "[" + id + "] " + user.getLastname() + ", " + user.getFirstname();
            }
        });
        loanStore.setStoreSorter(new StoreSorter<BeanModel>() {

            @Override
            public int compare(Store<BeanModel> store, BeanModel m1, BeanModel m2, String property) {
                if (property.equals(User.LOAN_NO_REF)) {
                    Loan l1 = m1.getBean();
                    Loan l2 = m2.getBean();
                    User u1 = l1.getUser();
                    User u2 = l2.getUser();
                    return u1.getLoanNo().compareTo(u2.getLoanNo());
                } else {
                    return super.compare(store, m1, m2, property);
                }
            }
        });
        loanStore.groupBy(Loan.USER_REF);

        GridFilters filters = new GridFilters();
        filters.setLocal(true);

        NumericFilter qtyFilter = new NumericFilter(Loan.QUANTITY_REF);
        StringFilter descFilter = new StringFilter(Loan.DESCRIPTION_REF);
        DateFilter startDateFilter = new DateFilter(Loan.START_DATE_REF);
        DateFilter termFilter = new DateFilter(Loan.TERM_REF);
        StringFilter tagFilter = new StringFilter(Loan.TAG_REF);
        StringFilter noteFilter = new StringFilter(Loan.NOTE_REF);

        filters.addFilter(qtyFilter);
        filters.addFilter(descFilter);
        filters.addFilter(startDateFilter);
        filters.addFilter(termFilter);
        filters.addFilter(tagFilter);
        filters.addFilter(noteFilter);

        grid = new EditorGrid<BeanModel>(loanStore, columnModel);
        grid.setView(view);
        grid.setLoadMask(true);
        grid.setBorders(false);
        grid.setAutoExpandColumn(Loan.DESCRIPTION_REF);
        grid.getView().setAutoFill(true);
        grid.getView().setEmptyText(messages.noLoanToShow());
        grid.setTrackMouseOver(true);
        grid.addListener(Events.AfterEdit, new GridEditListener());
        grid.addPlugin(filters);
        grid.setContextMenu(createContextMenu());

        grid.addListener(Events.ContextMenu, new Listener<GridEvent>() {

            @Override
            public void handleEvent(GridEvent be) {
                selectedLoan = (BeanModel) be.getModel();
            }
        });

        toolBarPanel = new LayoutContainer();
        toolBarPanel.setLayout(new RowLayout());
        mainTB = new ToolBar();
        secondaryTB = new ToolBar();
        toolBarPanel.add(mainTB, new RowData(1, -1));
        contentPanel.setLayout(new RowLayout());
        contentPanel.setBottomComponent(pagingTB);
        RowData data = new RowData(1, -1);
        contentPanel.add(toolBarPanel, data);
        data = new RowData(1, 1);
        contentPanel.add(grid, data);

        ContentPanel panel = Registry.get(AppWidgets.APP_CONTENT_PANEL);
        panel.add(contentPanel);
        panel.layout();

        searchConfigList = new ArrayList<FilterConfig>();
        filterConfigList = new ArrayList<FilterConfig>();
        loadConfigs.set(PIGE.SEARCH_CONFIGS, searchConfigList);
        loadConfigs.set(PIGE.FILTER_CONFIGS, filterConfigList);

        userConfigList = new ArrayList<FilterConfig>();
    }

    private void setApprovalMode() {
        setViewTitle(messages.loanApprobation());
        configureCellEditors(true);
        columnModel.setEditor(columnModel.getIndexById(Loan.STATUS_REF), null);
        configureToolBar();
        toolBarPanel.remove(secondaryTB);

        resetLoadConfigs();

        BaseFilterConfig params = new BaseListFilterConfig();
        params.setField("params");
        params.set(PIGE.USER_CONFIGS, userConfigList);
        searchConfigList.add(params);

        FilterConfig fc = new BaseStringFilterConfig();
        fc.setField(Loan.STATUS_REF);
        fc.setValue(LoanStatus.STATUS_PENDING);
        filterConfigList.add(fc);

        if (loanManagementSearchBox == null) {
            loanManagementSearchBox = new LoanManagementSearchBox(userConfigList) {

                @Override
                public void load() {
                    storeLoader.load(0, PAGE_SIZE);
                }
            };
        } else {
            loanManagementSearchBox.reset();
        }

        mainTB.add(PigeUtil.createVerticalSeparator());
        mainTB.add(loanManagementSearchBox);
        mainTB.add(PigeUtil.createVerticalSeparator());
        showPostponedLoanCbx = new CheckBox();
        showPostponedLoanCbx.setStyleAttribute("margin-left", "5px");
        Html postponedCbxLabel = new Html(messages.showPostponedLoans());
        postponedCbxLabel.setStyleName("pige-tb-cb-label");
        final FilterConfig postponedLoanStatusFC = new BaseStringFilterConfig();
        postponedLoanStatusFC.setField(Loan.STATUS_REF);
        postponedLoanStatusFC.setValue(LoanStatus.STATUS_POSTPONED);
        showPostponedLoanCbx.addListener(Events.Change, new Listener<FieldEvent>() {

            @Override
            public void handleEvent(FieldEvent event) {
                if (showPostponedLoanCbx.getValue()) {
                    filterConfigList.add(postponedLoanStatusFC);
                } else {
                    filterConfigList.remove(postponedLoanStatusFC);
                }
                storeLoader.load(0, PAGE_SIZE);
            }
        });
        mainTB.add(showPostponedLoanCbx);
        mainTB.add(postponedCbxLabel);
        mainTB.add(new SeparatorToolItem());
        mainTB.add(batchEditBtn);
        mainTB.add(new SeparatorToolItem());
        mainTB.add(printBtn);
        mainTB.add(new SeparatorToolItem());
        mainTB.layout();

        storeUpdater = new ApprovalStoreUpdater();
        ColumnConfig cc = columnModel.getColumnById(Loan.STATUS_REF);
        cc.setRenderer(new ApprovalButtonGridCellRenderer());
        columnModel.setColumnHeader(columnModel.getIndexById(Loan.STATUS_REF),
                messages.accepted() + " / " + messages.refused() + " / " + messages.postponed());

        pagingTB.setPageSize(PAGE_SIZE);
        storeLoader.load(0, PAGE_SIZE);
    }

    private void setReturnMode() {
        setViewTitle(messages.loansReturn());
        configureCellEditors(true);
        columnModel.setEditor(columnModel.getIndexById(Loan.STATUS_REF), null);
        configureToolBar();
        toolBarPanel.remove(secondaryTB);

        resetLoadConfigs();

        BaseFilterConfig params = new BaseListFilterConfig();
        params.setField("params");
        params.set(PIGE.USER_CONFIGS, userConfigList);
        searchConfigList.add(params);

        FilterConfig fc = new BaseStringFilterConfig();
        fc.setField(Loan.STATUS_REF);
        fc.setValue(LoanStatus.STATUS_LENT);
        filterConfigList.add(fc);

        fc = new BaseStringFilterConfig();
        fc.setField(Loan.STATUS_REF);
        fc.setValue(LoanStatus.STATUS_LATE);
        filterConfigList.add(fc);

        if (loanManagementSearchBox == null) {
            loanManagementSearchBox = new LoanManagementSearchBox(userConfigList) {

                @Override
                public void load() {
                    storeLoader.load(0, PAGE_SIZE);
                }
            };
        } else {
            loanManagementSearchBox.reset();
        }

        mainTB.add(PigeUtil.createVerticalSeparator());
        mainTB.add(loanManagementSearchBox);
        mainTB.add(new SeparatorToolItem());
        mainTB.add(batchEditBtn);
        mainTB.add(new SeparatorToolItem());
        mainTB.add(printBtn);
        mainTB.add(new SeparatorToolItem());
        mainTB.layout();

        storeUpdater = new ReturnStoreUpdater();
        ColumnConfig cc = columnModel.getColumnById(Loan.STATUS_REF);
        cc.setRenderer(new ReturnButtonGridCellRenderer());
        columnModel.setColumnHeader(columnModel.getIndexById(Loan.STATUS_REF), messages.returned());

        pagingTB.setPageSize(PAGE_SIZE);
        loanStore.removeAll();
    }

    private void setSearchMode() {
        setViewTitle(messages.search());
        configureCellEditors(Boolean.TRUE);
        configureToolBar();

        toolBarPanel.add(secondaryTB, new RowData(1, -1));
        updateBtn.setVisible(Boolean.TRUE);

        resetLoadConfigs();

        if (loanAdvancedSearchBox == null) {
            loanAdvancedSearchBox = new LoanAdvancedSearchBox(null, searchConfigList) {

                @Override
                public void load() {
                    storeLoader.load(0, SEARCH_PAGE_SIZE);
                }
            };
        }

        mainTB.add(PigeUtil.createVerticalSeparator());
        mainTB.add(loanAdvancedSearchBox);
        mainTB.layout();

        if (loanStateFilterBox == null) {
            loanStateFilterBox = new LoanStateFilterBox(storeLoader, filterConfigList) {

                @Override
                public void load() {
                    storeLoader.load(0, SEARCH_PAGE_SIZE);
                    contentPanel.layout();
                }
            };
            secondaryTB.add(loanStateFilterBox);
        }

        if (searchBtn == null) {
            searchBtn = new Button(messages.doSearch());
            searchBtn.setWidth(100);
            searchBtn.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.searchIcon()));
            searchBtn.addSelectionListener(loanAdvancedSearchBox.getStartSearchListener());
            secondaryTB.add(PigeUtil.createVerticalSeparator());
            secondaryTB.add(searchBtn);
        }
        if (resetBtn == null) {
            resetBtn = new Button(messages.reset());
            resetBtn.setWidth(100);
            resetBtn.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.redXIcon()));
            resetBtn.addSelectionListener(loanAdvancedSearchBox.getResetSearchListener());
            resetBtn.addSelectionListener(new SelectionListener<ButtonEvent>() {

                @Override
                public void componentSelected(ButtonEvent event) {
                    loanStateFilterBox.resetStates();
                }
            });
            secondaryTB.add(PigeUtil.createVerticalSeparator());
            secondaryTB.add(resetBtn);
            secondaryTB.add(PigeUtil.createVerticalSeparator());
        }

        if (!isPrintButtonPresent) {
            secondaryTB.add(batchEditBtn);
            secondaryTB.add(new SeparatorToolItem());
            secondaryTB.add(printBtn);
            secondaryTB.add(new SeparatorToolItem());
            isPrintButtonPresent = Boolean.TRUE;
        }

        storeUpdater = null;
        ColumnConfig cc = columnModel.getColumnById(Loan.STATUS_REF);
        cc.setRenderer(new GridCellRenderer<BeanModel>() {

            @Override
            public Object render(BeanModel model, String property, ColumnData config, int rowIndex, int colIndex,
                    ListStore<BeanModel> store, Grid<BeanModel> grid) {
                return (String) LoanStatus.getInstance().get((String) model.get(property));
            }
        });

        loanStateFilterBox.resetStates();

        columnModel.setColumnHeader(columnModel.getIndexById(Loan.STATUS_REF), messages.status());
        pagingTB.setPageSize(SEARCH_PAGE_SIZE);
        loanStore.removeAll();
    }

    private void resetLoadConfigs() {
        this.searchConfigList.clear();
        this.filterConfigList.clear();
    }

    private void setLoanDirty(BeanModel bm, Boolean dirty) {
        if (bm != null) {
            if (dirty && !dirtyLoans.contains(bm)) {
                dirtyLoans.add(bm);
            } else if (!dirty && dirtyLoans.contains(bm)) {
                dirtyLoans.remove(bm);
            }
        }
    }

    private class GridEditListener implements Listener<GridEvent> {

        @Override
        public void handleEvent(GridEvent event) {
            setLoanDirty((BeanModel) event.getModel(), Boolean.TRUE);
        }
    }

    private void configureToolBar() {
        mainTB.removeAll();
        updateBtn = new Button(messages.update());
        updateBtn.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.saveIcon()));
        updateBtn.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent event) {

                final StatusBar sb = Registry.get(AppWidgets.APP_STATUS_BAR);

                if (dirtyLoans.size() == 0) {
                    sb.setTimedText(messages.noUpdateToDo());
                    return;
                }

                List<Loan> loans = new ArrayList();
                Loan loan = null;
                for (BeanModel bm : dirtyLoans) {
                    loan = bm.getBean();
                    // Si on modifie un emprunt en retard (exemple: on change la
                    // date de retour), on remet cet emprunt dans l'tat 'Prt'.
                    if (loan.getStatus().equals(LoanStatus.STATUS_LATE)) {
                        loan.setStatus(LoanStatus.STATUS_LENT);
                    }
                    loans.add(loan);
                }

                ManagementServiceAsync rpcService = Registry.get(PIGE.MANAGEMENT_SERVICE);
                rpcService.updateLoans(loans, new AsyncCallback() {

                    @Override
                    public void onSuccess(Object o) {
                        StatusBar sb = Registry.get(AppWidgets.APP_STATUS_BAR);
                        sb.setTimedText(messages.updateSuccessful());
                        for (BeanModel bm : dirtyLoans) {
                            if (storeUpdater != null) {
                                storeUpdater.updateStore(loanStore, bm);
                            }
                        }
                        dirtyLoans.clear();
                    }

                    @Override
                    public void onFailure(Throwable caught) {
                        PIGE.handleException(caught);
                    }

                });
            }
        });
        updateBtn.setVisible(Boolean.TRUE);
        mainTB.add(updateBtn);
        mainTB.layout();

        printBtn = new Button(messages.print() + PIGE.DIALOG_OPENER_INDICATOR);
        printBtn.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.printerIcon()));
        printBtn.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent ce) {
                printSearchResult();
            }
        });

        batchEditBtn = new Button(messages.batchEdit());
        batchEditBtn.setIcon(AbstractImagePrototype.create(Resources.INSTANCE.batchEditIcon()));
        Menu batchEditMenu = new Menu();
        batchEditBtn.setMenu(batchEditMenu);
        configureBatchEditMenu(batchEditMenu, batchEditMenu);
    }

    private void configureCellEditors(Boolean isEditable) {
        NumberFormat nf = NumberFormat.getFormat(PIGE.QUANTITY_FORMAT);
        NumberField numberFieldEditor = new NumberField();
        numberFieldEditor.setPropertyEditorType(Integer.class);
        numberFieldEditor.setFormat(nf);
        numberFieldEditor.setMinValue(new Integer(0));
        numberFieldEditor.setMaxLength(6);
        columnModel.setEditor(columnModel.getIndexById(Loan.QUANTITY_REF),
                isEditable ? new CellEditor(numberFieldEditor) : null);

        TextField<String> descriptionEditor = new TextField<String>();
        descriptionEditor.setMaxLength(100);
        columnModel.setEditor(columnModel.getIndexById(Loan.DESCRIPTION_REF),
                isEditable ? new CellEditor(descriptionEditor) : null);

        DateTimeFormat dtf = DateTimeFormat.getFormat(PIGE.TERM_DATE_FORMAT);
        DateField dateField = new DateField();
        dateField.setMinValue(new Date());
        dateField.setEditable(false);
        dateField.setFormatValue(true);
        dateField.getPropertyEditor().setFormat(dtf);
        CellEditor termDateEditor = new CellEditor(dateField);
        columnModel.setEditor(columnModel.getIndexById(Loan.TERM_REF), isEditable ? termDateEditor : null);

        TextField<String> tagEditor = new TextField<String>();
        tagEditor.setMaxLength(50);
        columnModel.setEditor(columnModel.getIndexById(Loan.TAG_REF),
                isEditable ? new CellEditor(tagEditor) : null);

        TextField<String> noteEditor = new TextField<String>();
        noteEditor.setMaxLength(150);
        columnModel.setEditor(columnModel.getIndexById(Loan.NOTE_REF),
                isEditable ? new CellEditor(noteEditor) : null);

        LoanStatusChooser LoanStatusEditor = new LoanStatusChooser();
        CellEditor editor = new CellEditor(LoanStatusEditor) {

            @Override
            public Object preProcessValue(Object value) {
                List<LoanStatusModel> list = LoanStatus.getInstance().getModelList();
                for (LoanStatusModel model : list) {
                    if (model.getValue().equals(value)) {
                        return model;
                    }
                }
                return null;
            }

            @Override
            public Object postProcessValue(Object value) {
                LoanStatusModel model = (LoanStatusModel) value;
                return model.getValue();
            }

        };
        columnModel.setEditor(columnModel.getIndexById(Loan.STATUS_REF), isEditable ? editor : null);
    }

    protected BeanModel getSelectedLoan() {
        return selectedLoan;
    }

    protected Boolean validateLoanSelection() {
        if (selectedLoan != null) {
            return Boolean.TRUE;
        }
        MessageBox.info(messages.information(), messages.noLoanSelected(), null);
        return Boolean.FALSE;
    }

    protected void viewLoanItem() {
        if (!validateLoanSelection()) {
            return;
        }
        Loan loan = (Loan) getSelectedLoan().getBean();
        Integer itemId = loan.getItemId();
        if (itemId == null) {
            MessageBox.info(messages.information(), messages.noAssociatedItem(), null);
            return;
        }
        doViewLoanItem(itemId);
    }

    protected void doViewLoanItem(Integer itemId) {
        PIGE.__assert(itemId != null, "itemId == null", this.getClass(), "doViewLoanItem");
        ManagementServiceAsync rpcService = (ManagementServiceAsync) Registry.get(PIGE.MANAGEMENT_SERVICE);
        rpcService.getItemById(itemId, new AsyncCallback<Item>() {

            @Override
            public void onSuccess(Item item) {
                if (item != null) {
                    ItemCEVDialog dialog = new ItemCEVDialog(CEV.VIEW);
                    dialog.setData("item", item);
                    dialog.showDialog();
                }
            }

            @Override
            public void onFailure(Throwable caught) {
                PIGE.handleException(caught);
            }
        });
    }

    protected void unbindItem() {
        if (!validateLoanSelection()) {
            return;
        }
        final Loan loan = (Loan) getSelectedLoan().getBean();
        Integer itemId = loan.getItemId();
        if (itemId == null) {
            MessageBox.info(messages.information(), messages.noAssociatedItem(), null);
            return;
        }
        Listener listener = new Listener<MessageBoxEvent>() {
            @Override
            public void handleEvent(MessageBoxEvent event) {
                if (event.getButtonClicked().getHtml().equals(messages.yes())) {
                    doUnbindItem(loan);
                }
            }
        };
        MessageBox.confirm(messages.warning(), messages.unbindLoanItemConfirmation(), listener)
                .setIcon(MessageBox.WARNING);
    }

    protected void doUnbindItem(final Loan loan) {
        PIGE.__assert(loan != null, "loan == null", getClass(), "doUnbindItem");
        loan.setItemId(null);
        ManagementServiceAsync rpcService = (ManagementServiceAsync) Registry.get(PIGE.MANAGEMENT_SERVICE);
        rpcService.updateLoans(Arrays.asList(loan), new AsyncCallback() {

            @Override
            public void onSuccess(Object o) {
                loanStore.update(getSelectedLoan());
            }

            @Override
            public void onFailure(Throwable caught) {
                PIGE.handleException(caught);
            }
        });
    }

    protected Menu createContextMenu() {
        Menu contextMenu = new Menu();
        contextMenu.add(new HeaderMenuItem(messages.loanMenu()));
        MenuItem viewItemMenu = new MenuItem(messages.viewItem());
        viewItemMenu.addSelectionListener(new SelectionListener<MenuEvent>() {

            @Override
            public void componentSelected(MenuEvent e) {
                viewLoanItem();
            }
        });
        MenuItem unbindItemMenu = new MenuItem(messages.unbind());
        unbindItemMenu.addSelectionListener(new SelectionListener<MenuEvent>() {

            @Override
            public void componentSelected(MenuEvent e) {
                unbindItem();
            }
        });
        MenuItem printMenu = new MenuItem(messages.print());
        printMenu.addSelectionListener(new SelectionListener<MenuEvent>() {

            @Override
            public void componentSelected(MenuEvent e) {
                printSearchResult();
            }
        });

        // 1.5.1
        MenuItem batchEditMenuItem = new MenuItem(messages.batchEdit());
        Menu batchEditMenu = new Menu();
        batchEditMenuItem.setSubMenu(batchEditMenu);
        configureBatchEditMenu(batchEditMenu, contextMenu);

        contextMenu.add(viewItemMenu);
        contextMenu.add(unbindItemMenu);
        contextMenu.add(batchEditMenuItem);
        contextMenu.add(createClearSelectedLoansMenuItem());
        contextMenu.add(new SeparatorMenuItem());
        contextMenu.add(printMenu);
        return contextMenu;
    }

    private void printSearchResult() {
        if (loanStore.getCount() <= 0) {
            MessageBox.info(messages.information(), messages.noDataToPrint(), null);
            return;
        }
        doPrintSearchResult();
    }

    private void doPrintSearchResult() {
        BasePagingLoadConfig config = new BasePagingLoadConfig();
        config.setProperties(loadConfigs.getProperties());
        config.setLimit(0); // Pas de limite.
        config.setOffset(0);
        final MessageBox progressBar = MessageBox.wait(messages.fetchingData(), null, null);
        ManagementServiceAsync rpcService = (ManagementServiceAsync) Registry.get(PIGE.MANAGEMENT_SERVICE);
        rpcService.getLoans(config, new AsyncCallback<PagingLoadResult<Loan>>() {

            @Override
            public void onSuccess(PagingLoadResult<Loan> result) {
                HTMLLoanFormatter formatter = new HTMLLoanFormatter();
                formatter.setTitle(messages.loan() + "s - " + messages.searchResults());
                String data = formatter.format(result.getData());
                PrintViewer viewer = new PrintViewer();
                viewer.setContent(data);
                progressBar.close();
                viewer.show();
            }

            @Override
            public void onFailure(Throwable caught) {
                PIGE.handleException(caught);
            }
        });
    }

    // 1.5.1
    protected void configureBatchEditMenu(Menu menu, final Menu rootMenu) {

        MenuItem statusEditMenuItem = new MenuItem(messages.status());
        MenuItem quantityEditMenuItem = new MenuItem(messages.quantity());
        MenuItem descriptionEditMenuItem = new MenuItem(messages.description());
        MenuItem dueDateEditMenuItem = new MenuItem(messages.term());
        MenuItem tagEditMenuItem = new MenuItem(messages.tag());
        MenuItem noteEditMenuItem = new MenuItem(messages.note());

        menu.add(quantityEditMenuItem);

        // Doit seulement tre visible dans la vue Recherche.
        if (getViewTitle().equals(messages.search())) {
            menu.add(statusEditMenuItem);
        }

        menu.add(descriptionEditMenuItem);
        menu.add(dueDateEditMenuItem);
        menu.add(tagEditMenuItem);
        menu.add(noteEditMenuItem);

        menu.add(createClearSelectedLoansMenuItem());

        // Proprit quantit.
        NumberField quantityNF = new NumberField();
        quantityNF.setFormat(NumberFormat.getFormat(PIGE.QUANTITY_FORMAT));
        quantityNF.setPropertyEditorType(Integer.class);
        quantityNF.setAllowDecimals(Boolean.FALSE);
        quantityNF.setAllowNegative(Boolean.FALSE);
        quantityNF.setMaxLength(9);
        quantityNF.addListener(Events.KeyUp,
                new FieldListener(rootMenu, new FieldUpdater(Loan.QUANTITY_REF, quantityNF)));
        AdapterMenuItem quantityTFMenuAdapter = new AdapterMenuItem(quantityNF);
        quantityTFMenuAdapter.setManageFocus(true);
        quantityTFMenuAdapter.setCanActivate(true);
        Menu quantityEditMenu = new Menu();
        quantityEditMenu.add(quantityTFMenuAdapter);
        quantityEditMenuItem.setSubMenu(quantityEditMenu);

        // Proprit tat
        ListStore statusListStore = new ListStore<LoanStatusModel>();
        statusListStore.add(LoanStatus.getInstance().getModelList());
        ComboBox statusCBx = new ComboBox<LoanStatusModel>();
        statusCBx.setStore(statusListStore);
        statusCBx.setForceSelection(true);
        statusCBx.setEditable(false);
        statusCBx.setDisplayField("label");
        statusCBx.setTriggerAction(TriggerAction.ALL);
        statusCBx.addListener(Events.Select,
                new FieldListener(rootMenu, new FieldUpdater(Loan.STATUS_REF, statusCBx)));
        AdapterMenuItem statusTFMenuAdapter = new AdapterMenuItem(statusCBx);
        statusTFMenuAdapter.setManageFocus(true);
        statusTFMenuAdapter.setCanActivate(true);
        Menu statusEditMenu = new Menu();
        statusEditMenu.add(statusTFMenuAdapter);
        statusEditMenuItem.setSubMenu(statusEditMenu);

        // Proprit description.
        final TextField descriptionTF = new TextField<String>();
        descriptionTF.addListener(Events.KeyUp,
                new FieldListener(rootMenu, new FieldUpdater(Loan.DESCRIPTION_REF, descriptionTF)));
        AdapterMenuItem descriptionTFMenuAdapter = new AdapterMenuItem(descriptionTF);
        descriptionTFMenuAdapter.setManageFocus(true);
        descriptionTFMenuAdapter.setCanActivate(true);
        Menu descriptionEditMenu = new Menu();
        descriptionEditMenu.add(descriptionTFMenuAdapter);
        descriptionEditMenuItem.setSubMenu(descriptionEditMenu);

        // Proprit chance.
        DateTimeFormat dtf = DateTimeFormat.getFormat(PIGE.TERM_DATE_FORMAT);
        final DateField dateField = new DateField();
        dateField.setMinValue(new Date());
        dateField.setEditable(false);
        dateField.setFormatValue(true);
        dateField.getPropertyEditor().setFormat(dtf);
        dateField.getDatePicker().addListener(Events.Select,
                new FieldListener(rootMenu, new FieldUpdater(Loan.TERM_REF, dateField)));
        AdapterMenuItem dueDateTFMenuAdapter = new AdapterMenuItem(dateField);
        dueDateTFMenuAdapter.setManageFocus(true);
        dueDateTFMenuAdapter.setCanActivate(true);
        Menu dueDateEditMenu = new Menu();
        dueDateEditMenu.add(dueDateTFMenuAdapter);
        dueDateEditMenuItem.setSubMenu(dueDateEditMenu);

        // Proprit libell.
        final TextField tagTF = new TextField<String>();
        tagTF.addListener(Events.KeyUp, new FieldListener(rootMenu, new FieldUpdater(Loan.TAG_REF, tagTF)));
        AdapterMenuItem tagTFMenuAdapter = new AdapterMenuItem(tagTF);
        tagTFMenuAdapter.setManageFocus(true);
        tagTFMenuAdapter.setCanActivate(true);
        Menu tagEditMenu = new Menu();
        tagEditMenu.add(tagTFMenuAdapter);
        tagEditMenuItem.setSubMenu(tagEditMenu);

        // Proprit note.
        final TextField noteTF = new TextField<String>();
        noteTF.addListener(Events.KeyUp, new FieldListener(rootMenu, new FieldUpdater(Loan.NOTE_REF, noteTF)));
        AdapterMenuItem noteTFMenuAdapter = new AdapterMenuItem(noteTF);
        noteTFMenuAdapter.setManageFocus(true);
        noteTFMenuAdapter.setCanActivate(true);
        Menu noteEditMenu = new Menu();
        noteEditMenu.add(noteTFMenuAdapter);
        noteEditMenuItem.setSubMenu(noteEditMenu);

    }

    // 1.5.1
    private void uncheckAll() {
        for (BeanModel bm : selectedLoans) {
            int idx = grid.getStore().indexOf(bm);
            Widget w = grid.getView().getWidget(idx, 0);
            if (w != null && w instanceof CheckBox) {
                ((CheckBox) w).setValue(false);
            }
        }
        selectedLoans.clear();
    }

    // 1.5.1
    private MenuItem createClearSelectedLoansMenuItem() {
        MenuItem menuItem = new MenuItem(messages.clearSelection());
        menuItem.addSelectionListener(new SelectionListener<MenuEvent>() {

            @Override
            public void componentSelected(MenuEvent event) {
                uncheckAll();
            }
        });
        return menuItem;
    }

}