/** * Copyright (C) 2008-2010, Squale Project - http://www.squale.org * * This file is part of Squale. * * Squale is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or any later version. * * Squale 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 Lesser General Public License * along with Squale. If not, see <http://www.gnu.org/licenses/>. */ /** * */ package org.squale.gwt.distributionmap.widget; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import org.squale.gwt.distributionmap.widget.bundle.DMMessages; import org.squale.gwt.distributionmap.widget.bundle.DMResources; import org.squale.gwt.distributionmap.widget.data.Parent; import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.Widget; /** * <p> * This is the Distribution Map widget class. * </p> * <p> * The aim of a DMap is to show the distribution of a property of an element across its parents. <br> * A DMap displays a collection of these elements as little boxes, grouped into big boxes which are the parents of these * elements. <br> * Each little box has a color which depends on the value of the property that the DMap shows. * </p> * <p> * When building data to give to the DMap widget, the elements are referred as children (Child class) and the theirs * parents as parent (Parent class). * </p> * * @author Fabrice BELLINGARD */ public class DistributionMap extends Composite { /** * Callback of the DMap used when receiving data from the server */ final private AsyncCallback<ArrayList<Parent>> callback = new AsyncCallback<ArrayList<Parent>>() { public void onSuccess( ArrayList<Parent> result ) { if ( result.isEmpty() ) { displayErrorMessage(); } else { Collections.sort( result, parentComparator ); displayBoxes( result ); } } public void onFailure( Throwable caught ) { displayErrorMessage(); } }; /** * Comparator to sort parents by size, in order to print them properly in HTML */ final private Comparator<Parent> parentComparator = new Comparator<Parent>() { public int compare( Parent p1, Parent p2 ) { return p1.getChildren().size() - p2.getChildren().size(); } }; /** * Various resources used by the DMap, like images or CSS style sheets */ final public static DMResources resources = GWT.create( DMResources.class ); /** * Internationalized messages */ final public static DMMessages messages = GWT.create( DMMessages.class ); /** * Main panel where the boxes will be placed */ final private FlowPanel mainPanel = new FlowPanel(); /** * A temporary widget that shows a turning wheel to indicate that the widget is waiting for the servers's response */ final private Widget loadingLabel = new HTML(); /** * Label displayed when an error occurs */ final private Widget errorLabel = new HTML( messages.errorDisplayingDMap() ); /** * The popup panel used to display the details of each element box */ final private PopupPanel detailPopup = new PopupPanel( true ); /** * The message to display for big boxes (parents) */ private String bigBoxDetailPopupMessage; /** * The message to display for small boxes (children) */ private String smallBoxDetailPopupMessage; /** * The optional URL used to create a link from small boxes to a resources that gives details about the elements */ private String detailURL = ""; /** * Boolean that tells if the widget tries to optimize the big boxes sizes */ private boolean layoutOptimized; /** * Default constructor. <br> * The principle is to create the widget with this constructor, then to call {@link #startLoading()}, and finally to * call the RPC service that will generate the data (and give it the DMap callback). */ public DistributionMap() { resources.css().ensureInjected(); loadingLabel.setStylePrimaryName( resources.css().loadingLabel() ); mainPanel.setStylePrimaryName( resources.css().distributionMap() ); initWidget( mainPanel ); } /** * Display the turning wheel in the DMap widget */ public void startLoading() { mainPanel.clear(); mainPanel.add( loadingLabel ); } /** * Display the error label */ public void displayErrorMessage() { mainPanel.clear(); mainPanel.add( errorLabel ); } /** * Gives the callback used by the DMap to display the data * * @return the callback */ public AsyncCallback<ArrayList<Parent>> getCallback() { return callback; } /** * Displays the data returned from the server into a distribution map. It removes any loading label if necessary. * * @param parents the Parent-Child tree to display as a DMap */ private void displayBoxes( ArrayList<Parent> parents ) { mainPanel.clear(); for ( Parent parent : parents ) { mainPanel.add( new BigBox( this, parent ) ); } } /** * Moves the current detail popup to the specified position. * * @param xPosition X position * @param yPosition Y position */ void updateDetailPopup( int xPosition, int yPosition ) { detailPopup.setPopupPosition( xPosition + 1 + Window.getScrollLeft(), yPosition + 1 + Window.getScrollTop() ); } /** * Hides the small box detail popup. */ void hideDetailPopupForSmallBox() { detailPopup.setWidget( new HTML( bigBoxDetailPopupMessage ) ); } /** * Shows the small box detail popup with the given message and moves it to the given position. * * @param message the message to display * @param xPosition X position * @param yPosition Y position */ void showDetailPopupForSmallBox( String message, int xPosition, int yPosition ) { smallBoxDetailPopupMessage = message; detailPopup.setWidget( new HTML( smallBoxDetailPopupMessage ) ); detailPopup.show(); updateDetailPopup( xPosition, yPosition ); } /** * Hides the big box detail popup. */ void hideDetailPopupForBigBox() { detailPopup.hide(); } /** * Shows the bi box detail popup with the given message and moves it to the given position. * * @param message the message to display * @param xPosition X position * @param yPosition Y position */ void showDetailPopupForBigBox( String message, int xPosition, int yPosition ) { bigBoxDetailPopupMessage = message; detailPopup.setWidget( new HTML( bigBoxDetailPopupMessage ) ); detailPopup.show(); updateDetailPopup( xPosition, yPosition ); } /** * Returns the URL used to display details about elements. This URL may be an empty string if nothing was * configured. * * @return the detail URL */ public String getDetailURL() { return detailURL; } /** * Sets the URL used to display details about elements. * * @param detailURL the detail URL to set */ public void setDetailURL( String detailURL ) { this.detailURL = detailURL; } /** * Tells the widget tries to optimize or not the big boxes sizes. * * @param layoutOptimized set to true if the layout should be optimized */ public void setLayoutOptimized( boolean layoutOptimized ) { this.layoutOptimized = layoutOptimized; } /** * Tells if the widget tries to optimize the big boxes sizes. * * @return true if the layout is optimized */ public boolean isLayoutOptimized() { return layoutOptimized; } }