org.bonitasoft.console.client.view.labels.LabelAssociationWidget.java Source code

Java tutorial

Introduction

Here is the source code for org.bonitasoft.console.client.view.labels.LabelAssociationWidget.java

Source

/**
 * Copyright (C) 2009 BonitaSoft S.A.
 * BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2.0 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.bonitasoft.console.client.view.labels;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import org.bonitasoft.console.client.CSSClassManager;
import org.bonitasoft.console.client.cases.CaseItem;
import org.bonitasoft.console.client.cases.CaseUUID;
import org.bonitasoft.console.client.controller.LabelAssociationController;
import org.bonitasoft.console.client.i18n.LocaleUtil;
import org.bonitasoft.console.client.labels.LabelModel;
import org.bonitasoft.console.client.labels.LabelUUID;
import org.bonitasoft.console.client.model.cases.CaseDataSource;
import org.bonitasoft.console.client.model.cases.CaseSelection;
import org.bonitasoft.console.client.model.labels.LabelDataSource;
import org.bonitasoft.console.client.view.BonitaPanel;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ScrollPanel;

/**
 * @author Nicolas Chabanoles
 * 
 */
public class LabelAssociationWidget extends BonitaPanel {

    private static final String PARTIALLY_CHECKED_TRISTATECHECKBOX = "b-tristatecheckbox-partially-checked";

    private static final String CHECKED_TRISTATECHECKBOX = "b-tristatecheckbox-fully-checked";

    private static final String EMPTY_TRISTATECHECKBOX = "b-tristatecheckbox";

    private static final HashSet<LabelUUID> myLabelsToAddToCaseSelection = new HashSet<LabelUUID>();
    private static final HashSet<LabelUUID> myLabelsToRemoveFromCaseSelection = new HashSet<LabelUUID>();

    private enum TriState {
        EMPTY, PARTIAL, FULL;
    }

    /*
     * A ClickHandler implementation that will manage the binding between the
     * myCases and the labels.
     */
    private class LabelClickHandler implements ClickHandler {

        private LabelUUID myLabelUUID;
        private TriState myInitialState;
        private TriState myCurrentState;
        private Image myImage;
        private String myInitialImageStyle;

        /**
         * 
         * Default constructor.
         * 
         * @param aLabelUUID
         * @param anInitialState
         * @param anImage
         */
        public LabelClickHandler(final LabelUUID aLabelUUID, final TriState anInitialState, Image anImage) {
            myLabelUUID = aLabelUUID;
            myInitialState = anInitialState;
            myCurrentState = anInitialState;
            switch (myInitialState) {
            case EMPTY:
                myInitialImageStyle = EMPTY_TRISTATECHECKBOX;
                break;
            case PARTIAL:
                myInitialImageStyle = PARTIALLY_CHECKED_TRISTATECHECKBOX;
                break;
            case FULL:
                myInitialImageStyle = CHECKED_TRISTATECHECKBOX;
                break;
            default:
                break;
            }
            myImage = anImage;
        }

        /*
         * (non-Javadoc)
         * 
         * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt
         * .event.dom.client.ClickEvent)
         */
        public void onClick(ClickEvent aEvent) {
            /*
             * Default behavior is to round from EMPTY to PARTIAL and then to FULL.
             * The transition from EMPTY to PARTIAL is equivalent to cancel changes.
             */
            if (myCaseSelection.getSize() > 0) {
                switch (myCurrentState) {
                case EMPTY:
                    addLabelToCaseSelection(myLabelUUID);
                    break;
                case PARTIAL:
                    addLabelToCaseSelection(myLabelUUID);
                    break;
                case FULL:
                    removeLabelFromCaseSelection(myLabelUUID);
                    break;
                default:
                    break;
                }
                // Update the current state.
                if (myCurrentState == TriState.FULL) {
                    myCurrentState = TriState.EMPTY;
                    myImage.setStylePrimaryName(EMPTY_TRISTATECHECKBOX);
                } else {
                    if (myCurrentState == TriState.EMPTY && myInitialState == TriState.PARTIAL) {
                        // Transition from EMPTY to PARTIAL
                        myCurrentState = myInitialState;
                        myImage.setStylePrimaryName(myInitialImageStyle);
                    } else {
                        // Transition from PARTIAL or EMPTY to FULL
                        myCurrentState = TriState.FULL;
                        myImage.setStylePrimaryName(CHECKED_TRISTATECHECKBOX);
                    }
                }
                if (myCurrentState == myInitialState) {
                    cancelModifications(myLabelUUID);
                }
            }
        }

        private void cancelModifications(LabelUUID aLabelUUID) {
            myLabelsToAddToCaseSelection.remove(aLabelUUID);
            myLabelsToRemoveFromCaseSelection.remove(aLabelUUID);
        }

        private void addLabelToCaseSelection(LabelUUID aLabelUUID) {
            myLabelsToAddToCaseSelection.add(aLabelUUID);
        }

        private void removeLabelFromCaseSelection(LabelUUID aLabelUUID) {
            myLabelsToRemoveFromCaseSelection.add(aLabelUUID);
        }
    }

    /*
     * Capture the events coming from the mouse to dynamically update the css
     * style of table entries.
     */
    private class MouseHandler implements MouseOverHandler, MouseOutHandler {
        private static final String DEPENDENT_STYLENAME_SELECTED_ITEM = "selected";
        private int myRow;

        public MouseHandler(int aRow) {
            myRow = aRow;
        }

        public void onMouseOut(MouseOutEvent aEvent) {
            myLabelSelection.getRowFormatter().removeStyleName(myRow, DEPENDENT_STYLENAME_SELECTED_ITEM);
        }

        public void onMouseOver(MouseOverEvent aEvent) {
            myLabelSelection.getRowFormatter().addStyleName(myRow, DEPENDENT_STYLENAME_SELECTED_ITEM);
        }
    }

    private CaseSelection myCaseSelection;
    private LabelDataSource myLabelDataSource;
    LabelAssociationController myController;

    ScrollPanel theScrollPanel = new ScrollPanel();
    // Create the panel for the list of labels.
    FlexTable myLabelSelection = new FlexTable();
    protected final Label myApplyLabel = new Label(constants.apply());

    private CaseDataSource myCaseDataSource;

    /**
     * Default constructor.
     * 
     * @param aController
     * @param aCaseDataSource
     * 
     * @param aCaseDataSource
     * 
     * @param aCaseSelection
     * @param aLabelDataSource
     */
    public LabelAssociationWidget(LabelAssociationController aController, CaseDataSource aCaseDataSource,
            CaseSelection aCaseSelection, LabelDataSource aLabelDataSource) {
        super();
        myController = aController;
        myCaseDataSource = aCaseDataSource;
        myCaseSelection = aCaseSelection;
        myLabelDataSource = aLabelDataSource;

        myApplyLabel.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent aEvent) {
                applyChanges();
            }
        });

        // Update the UI.
        myLabelSelection.setStyleName("label_selection");

        theScrollPanel.add(myLabelSelection);
        update();

        this.initWidget(theScrollPanel);
    }

    /**
     * Update the UI.
     */
    private void update() {
        // first of all clean the panel
        myLabelSelection.clear();

        final List<LabelModel> theLabels = myLabelDataSource.getAllLabels();

        // CheckBox theCheckBox;
        Image theSelectorImage;
        LabelModel theLabelModel;
        Image theLabelIcon;
        Label theLabel;
        MouseHandler theMouseHandler;
        LabelClickHandler theClickHandler;
        int theNbSelectedCaseHavingCurrentLabel;
        int theNbCaseSelected = myCaseSelection.getSelectedItems().size();
        int row = 0;
        // Create an entry for each label.
        for (int i = 0; i < theLabels.size(); i++) {
            theLabelModel = theLabels.get(i);
            if (theLabelModel.isAssignableByUser()) {
                // The state of the checkbox depends on the number of
                // occurrences.
                // If the label is associated to all cases it must be
                // selected.
                // If the label is not associated to any case it must NOT be
                // selected.
                // If it is associated at least one but not all cases, it must
                // be
                // "partially" selected.

                theSelectorImage = new Image(PICTURE_PLACE_HOLDER);
                theNbSelectedCaseHavingCurrentLabel = countSelectedCases(theLabelModel.getUUID()); // myCaseSelection.getSelectedCaseCount(theLabelModel);

                if (theNbSelectedCaseHavingCurrentLabel == 0 || theNbCaseSelected == 0) {
                    // Empty checkbox
                    theSelectorImage.setStylePrimaryName(EMPTY_TRISTATECHECKBOX);
                    theClickHandler = new LabelClickHandler(theLabelModel.getUUID(), TriState.EMPTY,
                            theSelectorImage);
                    theSelectorImage.addClickHandler(theClickHandler);
                } else {
                    if (theNbSelectedCaseHavingCurrentLabel == theNbCaseSelected) {
                        // Checkbox fully checked
                        theSelectorImage.setStylePrimaryName(CHECKED_TRISTATECHECKBOX);
                        theClickHandler = new LabelClickHandler(theLabelModel.getUUID(), TriState.FULL,
                                theSelectorImage);
                        theSelectorImage.addClickHandler(theClickHandler);
                    } else {
                        // Checkbox partially checked
                        theSelectorImage.setStylePrimaryName(PARTIALLY_CHECKED_TRISTATECHECKBOX);
                        theClickHandler = new LabelClickHandler(theLabelModel.getUUID(), TriState.PARTIAL,
                                theSelectorImage);
                        theSelectorImage.addClickHandler(theClickHandler);
                    }
                }

                theLabel = new Label(LocaleUtil.translate(theLabelModel.getUUID()));
                theMouseHandler = new MouseHandler(row);
                theLabel.addMouseOverHandler(theMouseHandler);
                theLabel.addMouseOutHandler(theMouseHandler);
                // Bind the same click handler as the checkbox, to allow the
                // user to click on the label or the check box to achieve the
                // same result.
                theLabel.addClickHandler(theClickHandler);

                theLabelIcon = new Image(PICTURE_PLACE_HOLDER);
                if (theLabelModel.getIconCSSStyle() != null
                        && theLabelModel.getIconCSSStyle().trim().length() > 0) {
                    theLabelIcon.setStylePrimaryName(theLabelModel.getIconCSSStyle());
                } else {
                    theLabelIcon.setWidth("60%");
                }
                theLabelIcon.addMouseOverHandler(theMouseHandler);
                theLabelIcon.addMouseOutHandler(theMouseHandler);
                // Bind the same click handler as the checkbox, to allow the
                // user to click on the label or the check box to achieve the
                // same result.
                theLabelIcon.addClickHandler(theClickHandler);

                myLabelSelection.setWidget(row, 0, theSelectorImage);
                myLabelSelection.setWidget(row, 1, theLabel);
                myLabelSelection.setWidget(row, 2, theLabelIcon);

                myLabelSelection.getRowFormatter().setStyleName(row, CSSClassManager.POPUP_MENU_ENTRY);
                myLabelSelection.getFlexCellFormatter().setColSpan(row, 0, 1);
                row++;
            }
        }
        if (row > 0) {
            myLabelSelection.getRowFormatter().setStyleName(row, "menu_separator");
            myLabelSelection.getFlexCellFormatter().setColSpan(row, 0, 3);
            row++;

            myLabelSelection.setWidget(row, 0, myApplyLabel);
            myLabelSelection.getFlexCellFormatter().setColSpan(row, 0, 3);
            myLabelSelection.getFlexCellFormatter().setHorizontalAlignment(row, 0,
                    HasHorizontalAlignment.ALIGN_CENTER);
            myLabelSelection.getRowFormatter().setStyleName(row, CSSClassManager.POPUP_MENU_ENTRY);

            theMouseHandler = new MouseHandler(row);
            myApplyLabel.addMouseOverHandler(theMouseHandler);
            myApplyLabel.addMouseOutHandler(theMouseHandler);
        }
    }

    private int countSelectedCases(LabelUUID aLabelUUID) {
        int theResult = 0;
        ArrayList<CaseUUID> theCaseUUIDs = myCaseSelection.getSelectedItems();
        for (CaseUUID theCaseUUID : theCaseUUIDs) {
            CaseItem theCaseItem = myCaseDataSource.getItem(theCaseUUID);
            if (theCaseItem.getLabels().contains(aLabelUUID)) {
                theResult += 1;
            }
        }
        return theResult;
    }

    /**
     * Apply the chnages on data source.
     */
    void applyChanges() {
        if (myCaseSelection.getSize() > 0) {
            myController.applyChanges(myLabelsToAddToCaseSelection, myLabelsToRemoveFromCaseSelection,
                    new HashSet<CaseUUID>(myCaseSelection.getSelectedItems()));
        }
    }

    /*
     * Reset the state of the widget and update the UI.
     */
    private void reset() {
        myLabelsToAddToCaseSelection.clear();
        myLabelsToRemoveFromCaseSelection.clear();
        update();
    }

    @Override
    protected void onAttach() {
        super.onAttach();
        reset();
    }

}