edu.caltech.ipac.firefly.ui.imageGrid.BasicImageGrid.java Source code

Java tutorial

Introduction

Here is the source code for edu.caltech.ipac.firefly.ui.imageGrid.BasicImageGrid.java

Source

/*
 * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt
 */
package edu.caltech.ipac.firefly.ui.imageGrid;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DoubleClickEvent;
import com.google.gwt.event.dom.client.DoubleClickHandler;
import com.google.gwt.event.dom.client.ErrorEvent;
import com.google.gwt.event.dom.client.ErrorHandler;
import com.google.gwt.event.dom.client.LoadEvent;
import com.google.gwt.event.dom.client.LoadHandler;
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.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import edu.caltech.ipac.firefly.data.DownloadRequest;
import edu.caltech.ipac.firefly.data.Param;
import edu.caltech.ipac.firefly.data.table.DataSet;
import edu.caltech.ipac.firefly.data.table.TableData;
import edu.caltech.ipac.firefly.data.table.TableMeta;
import edu.caltech.ipac.firefly.ui.GwtUtil;
import edu.caltech.ipac.firefly.ui.PopupPane;
import edu.caltech.ipac.firefly.ui.PopupType;
import edu.caltech.ipac.firefly.ui.VisibleListener;
import edu.caltech.ipac.firefly.ui.creator.PreviewImageGridCreator;
import edu.caltech.ipac.firefly.ui.creator.drawing.DatasetDrawingLayerProvider;
import edu.caltech.ipac.firefly.ui.creator.drawing.DrawingLayerProvider;
import edu.caltech.ipac.firefly.ui.previews.AbstractPreviewData;
import edu.caltech.ipac.firefly.ui.table.TablePanel;
import edu.caltech.ipac.firefly.ui.table.EventHub;
import edu.caltech.ipac.firefly.util.event.Name;
import edu.caltech.ipac.firefly.util.event.WebEvent;
import edu.caltech.ipac.firefly.util.event.WebEventListener;
import edu.caltech.ipac.firefly.util.event.WebEventManager;
import edu.caltech.ipac.firefly.visualize.ActiveTarget;
import edu.caltech.ipac.firefly.visualize.AllPlots;
import edu.caltech.ipac.firefly.visualize.MiniPlotWidget;
import edu.caltech.ipac.firefly.visualize.PlotWidgetGroup;
import edu.caltech.ipac.firefly.visualize.Vis;
import edu.caltech.ipac.firefly.visualize.WebPlot;
import edu.caltech.ipac.firefly.visualize.WebPlotRequest;
import edu.caltech.ipac.firefly.visualize.WebPlotView;
import edu.caltech.ipac.firefly.visualize.draw.CatalogDisplay;
import edu.caltech.ipac.firefly.visualize.draw.DataConnectionDisplay;
import edu.caltech.ipac.visualize.plot.RangeValues;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by IntelliJ IDEA.
 * User: tlau
 * Date: Aug 9, 2010
 * Time: 4:29:19 PM
 * $Id: BasicImageGrid.java,v 1.133 2012/12/22 01:20:04 tlau Exp $
 */
public class BasicImageGrid extends ScrollPanel implements VisibleListener {

    public static final Name ON_SELECTED_PLOT_CHANGE = new Name("BasicImageGrid.ON_SELECTED_PLOT_CHANGE",
            "When the new plot is selected (highlighted).");

    public static final Name ON_CHECKED_PLOT_CHANGE = new Name("BasicImageGrid.ON_CHECKED_PLOT_CHANGE",
            "When a plot is checked or unchecked.");

    public static final Name ON_ALL_CHECKED_PLOT_CHANGE = new Name("BasicImageGrid.ON_ALL_CHECKED_PLOT_CHANGE",
            "When all plots are checked or unchecked. Data is the boolean, true for all checked, false for all unchecked");

    public static final Name ON_ALL_PLOTS_DONE = new Name("BasicImageGrid.ON_ALL_PLOTS_DONE",
            "When all plots are done.");
    private static int INIT_SIZE = 3;

    //IPAC TABLE HEADERS FOR IMAGE GRID LAYOUT OPTIONS
    public static String DATA_TYPE_COLUMN = "DATA_TYPE_COLUMN";
    public static String DESCRIPTION_COLUMN = "DESCRIPTION_COLUMN";
    public static String GROUPING_COLUMN = "GROUPING_COLUMN";
    public static String EVENTWORKER_COLUMN = "EVENTWORKER_COLUMN";
    public static String ALL_EVENTWORKER_COLUMN = "ALL_EVENTWORKER_COLUMN";
    public static String FULL_SIZE_URL_COLUMN = "FULL_SIZE_URL_COLUMN";
    public static String GROUPING = "GROUPING";
    public static String GRID_BACKGROUND = "GRID_BACKGROUND";
    public static String INFO = "INFO";
    public static String JPEG_SELECTION_HILITE = "JPEG_SELECTION_HILITE";
    public static String JPEG_SELECTION_DOUBLE_CLICK = "JPEG_SELECTION_DOUBLE_CLICK";
    public static String SOURCE_ID_COLUMN = "SOURCE_ID_COLUMN";
    public static String THUMBNAIL_URL_COLUMN = "THUMBNAIL_URL_COLUMN";
    public static String COLUMNS = "COLUMNS";

    //GROUPING PARAMETERS
    private static String show_in_a_row = "show_in_a_row";
    private static String show_label = "show_label";
    private static String groupBy = "groupBy";
    private static String keywords = "keywords";

    private static String TYPE_FITS = "fits";
    private static String TYPE_REQ = "req";
    private static String TYPE_JPEG = "jpg";
    private static final String PARAM_SEP = "&";
    private static final String GRID_BACKGROUND_DEFAULT_COLOR = "#f4f4f4";
    private static final int MPW_HEADER_HEIGHT = 24;
    private static final String EMPTY_GRID = "There are no data to display";
    private FlowPanel mainPanel = new FlowPanel();
    private Map<String, ImageGridWidgetGroup> imageGridWidgetGroups = new LinkedHashMap<String, ImageGridWidgetGroup>();
    private Map<String, Integer> rowWidths = new HashMap<String, Integer>();
    private int columns = 1; //disable if columns<=1
    private int mouseOverAt = 0;
    private int currentRow = 0;
    private int nextMpw = 0;
    private int currentViewHeight = 0;
    private int currentViewWidth = 0;
    private int currentScrollX = 0;
    private int currentScrollY = 0;
    private boolean updateDisplay = false;
    private String dataTypeColumnKeyword = null;
    private String thumbnailUrlColumnKeyword = null;
    private String thumbnailTitleColumnKeyword = null;
    private String eventWorkerKeyword = null;
    private String allEventWorkerKeyword = null;
    private String groupingColumnKeyword = null;
    private String fullSizeUrlColumnKeyword = null;
    private String sourceIdColumnKeyword = null;
    private boolean groupingByColumns = false;
    private boolean groupingInRow = false;
    private Boolean lockrelated = null;
    private Map<String, String> titleDescMap = null;
    private List<String> plotEWList = null;
    private String info = null;
    private String groupingShowLabel = "OFF"; //ON, OFF, TOP
    private LinkedHashMap<String, String> groupingParams = new LinkedHashMap<String, String>(0);
    private ImageGridPanelHandler imageGridPanelHandler = new ImageGridPanelHandler();
    private List<TableData.Row> rowValues = null;
    private ArrayList<MiniPlotWidget> mpwList = new ArrayList<MiniPlotWidget>(INIT_SIZE);
    private EventHub hub = null;
    private final PreviewImageGridCreator.ImageGridPreviewData _previewData;
    private HashMap<VerticalPanel, MiniPlotWidget> panelMap = new HashMap<VerticalPanel, MiniPlotWidget>();
    private boolean checkingEnabled = false;
    private boolean suspendEvents = false;
    private boolean onlyShowingFilteredResults = false;
    private int plottingCnt = 0;
    private int successPlots = 0;
    private int failurePlots = 0;

    private WebEventManager eventManager = new WebEventManager();
    private PlotWidgetEventListener plotWidgetEventListener;
    private PostPlotWidgetEventListener postPlotWidgetEventListener;
    private MiniPlotWidget.PlotError plotError = null;
    private int lastThumbnailHeight = 0;
    private String _plotGroup = null;
    //private MiniPlotWidget _currentMpw = null;
    private TablePanel tablePanel = null;

    public BasicImageGrid(AbstractPreviewData previewData) {
        super.setSize("100%", "100%");
        _previewData = (PreviewImageGridCreator.ImageGridPreviewData) previewData;
        mainPanel.setSize("100%", "100%");
        super.add(mainPanel);
        GwtUtil.setStyle(mainPanel, "backgroundColor", GRID_BACKGROUND_DEFAULT_COLOR);
        GwtUtil.setStyle(this, "backgroundColor", GRID_BACKGROUND_DEFAULT_COLOR);

        setPlotWidgetGroupValue(_previewData.getPlotWidgetGroup());

        for (int i = 0; i < INIT_SIZE; i++) {
            createAndAddMiniPlotWidget();
        }

        getPlotWidgetGroup().setImageGrid(this);
        getPlotWidgetGroup().setGridPopoutColumns(_previewData.getGridPopoutCols());
        getPlotWidgetGroup().setGridPopoutZoomType(_previewData.getGridPopoutZoomtype());

        Window.addResizeHandler(new ResizeHandler() {
            public void onResize(ResizeEvent event) {
                resize();
            }
        });
        plotError = new MiniPlotWidget.PlotError() {
            public void onError(WebPlot wp, String briefDesc, String desc, String details, Exception e) {

            }
        };
    }

    /**
     * This method is called immediately after a widget becomes attached to the
     * browser's document.
     */
    @Override
    protected void onLoad() {
        super.onLoad();
        addPlotWidgetEventListeners();
    }

    /**
     * This method is called immediately before a widget will be detached from the
     * browser's document.
     */
    @Override
    protected void onUnload() {
        removePlotWidgetEventListeners();
        super.onUnload();
    }

    public WebEventManager getEventManager() {
        return eventManager;
    }

    private void handlePlotEvent(WebEvent ev) {
        if (suspendEvents)
            return;
        if (ev.getName().equals(Name.FITS_VIEWER_CHANGE)) {
            MiniPlotWidget mpw = (MiniPlotWidget) ev.getData();
            for (int i = 0; i < nextMpw; i++) {
                if (mpwList.get(i).equals(mpw)) {
                    eventManager.fireEvent(new WebEvent(this, ON_SELECTED_PLOT_CHANGE));
                }
            }
        } else if (ev.getName().equals(Name.CHECKED_PLOT_CHANGE)) {
            eventManager.fireEvent(new WebEvent(this, ON_CHECKED_PLOT_CHANGE, false));
        } else if (ev.getName().equals(Name.ALL_CHECKED_PLOT_CHANGE)) {
            eventManager.fireEvent(new WebEvent(this, ON_ALL_CHECKED_PLOT_CHANGE, ev.getData()));
        }
    }

    private void handlePostPlotEvent(WebEvent ev) {
        if (suspendEvents)
            return;
        if (ev.getName().equals(Name.ALL_PLOT_TASKS_COMPLETE)) {
            resize();
        }
    }

    private void addPlotWidgetEventListeners() {
        plotWidgetEventListener = new PlotWidgetEventListener();
        postPlotWidgetEventListener = new PostPlotWidgetEventListener();
        AllPlots.getInstance().addListener(Name.FITS_VIEWER_CHANGE, plotWidgetEventListener);
        AllPlots.getInstance().addListener(Name.CHECKED_PLOT_CHANGE, plotWidgetEventListener);
        AllPlots.getInstance().addListener(Name.ALL_CHECKED_PLOT_CHANGE, plotWidgetEventListener);
        AllPlots.getInstance().addListener(Name.ALL_PLOT_TASKS_COMPLETE, postPlotWidgetEventListener);
    }

    private void removePlotWidgetEventListeners() {
        AllPlots.getInstance().removeListener(Name.FITS_VIEWER_CHANGE, plotWidgetEventListener);
        AllPlots.getInstance().removeListener(Name.CHECKED_PLOT_CHANGE, plotWidgetEventListener);
        AllPlots.getInstance().removeListener(Name.ALL_CHECKED_PLOT_CHANGE, plotWidgetEventListener);
    }

    /**
     * Select the plot widget in the cell with the given index
     *
     * @param idx cell index
     */
    public void setSelectedPlotIdx(int idx) {
        if (nextMpw > 0) {
            if (idx >= 0 && idx < nextMpw) {
                suspendEvents = true;
                AllPlots.getInstance().setSelectedMPW(mpwList.get(idx));
                suspendEvents = false;
            } else {
                throw new ArrayIndexOutOfBoundsException(idx + " must be from 0 to " + (nextMpw - 1));
            }
        }
    }

    /**
     * Get index of the cell with selected plot widget
     *
     * @return cell index or -1 if no plot widget is selected
     */
    public int getSelectedPlotIdx() {
        MiniPlotWidget selectedMpw = AllPlots.getInstance().getMiniPlotWidget();
        if (selectedMpw != null) {
            for (int i = 0; i < nextMpw; i++) {
                if (selectedMpw.equals(mpwList.get(i))) {
                    return i;
                }
            }
        }
        return -1;
    }

    /**
     * Check or uncheck all plot widgets
     *
     * @param check if true check, otherwise uncheck
     */
    public void setAllChecked(boolean check) {
        if (checkingEnabled && nextMpw > 0) {
            suspendEvents = true;
            mpwList.get(0).getGroup().setAllChecked(check);
            suspendEvents = false;
        }
    }

    /**
     * Returns true if all plot widgets are checked
     */
    public boolean isAllChecked() {
        boolean allChecked = false;
        if (checkingEnabled && nextMpw > 0) {
            allChecked = mpwList.get(0).getGroup().isAllChecked();
        }
        return allChecked;
    }

    /**
     * Check the plot widgets with the given indexes
     *
     * @param checked array list with indexes of the checked plots
     */
    public void setCheckedPlotIdx(List<Integer> checked) {
        if (checkingEnabled && nextMpw > 0) {
            suspendEvents = true;
            for (int i = 0; i < nextMpw; i++) {
                boolean doCheck = checked.contains(i);
                if (mpwList.get(i).isChecked() != doCheck) {
                    mpwList.get(i).setChecked(doCheck);
                }
            }
            suspendEvents = false;
        }
    }

    /**
     * Get indexes of the cells with checked plot widget
     *
     * @return array list with indexes of the checked plots
     */
    public ArrayList<Integer> getCheckedPlotIdx() {
        ArrayList<Integer> checked = new ArrayList<Integer>();
        if (checkingEnabled) {
            for (int i = 0; i < nextMpw; i++) {
                if (mpwList.get(i).isChecked()) {
                    checked.add(i);
                }
            }
        }
        return checked;
    }

    public void bind(EventHub hub) {
        this.hub = hub;
        //        setupCatalog();
    }

    public void setEnableChecking(boolean checkingEnabled) {
        for (MiniPlotWidget mpw : mpwList) {
            if (mpw.getEnableChecking() != checkingEnabled) {
                mpw.setEnableChecking(checkingEnabled);
            }
        }
        this.checkingEnabled = checkingEnabled;

        PlotWidgetGroup group = getPlotWidgetGroup();
        group.setEnableChecking(checkingEnabled);
    }

    public void setEnablePdfDownload(boolean b) {
        PlotWidgetGroup group = getPlotWidgetGroup();
        group.setEnablePdfDownload(b);
    }

    public void setOnlyShowingFilteredResults(boolean b) {
        onlyShowingFilteredResults = b;
    }

    public void setShowDrawingLayers(boolean b) {
        getPlotWidgetGroup().setShowDrawingLayers(b);
    }

    public String getPlotWidgetGroupValue() {
        return _plotGroup;
    }

    public void setPlotWidgetGroupValue(String s) {
        if (s != null) {
            _plotGroup = s;
        }
    }

    private PlotWidgetGroup getPlotWidgetGroup() {
        return mpwList.get(0).getGroup();
    }

    private boolean hasValidImageGridPreviewData() {
        return (_previewData != null && _previewData instanceof PreviewImageGridCreator.ImageGridPreviewData);
    }

    private void empty() {
        showMessage(EMPTY_GRID);
    }

    private boolean getLockRelated() {
        if (lockrelated == null) {
            if (hasValidImageGridPreviewData()) {
                lockrelated = _previewData.getLockRelated();
            } else {
                lockrelated = false;
            }
        }
        return lockrelated;
    }

    private List<String> getPlotEventWorkerIDList() {
        if (plotEWList == null) {
            if (hasValidImageGridPreviewData()) {
                plotEWList = _previewData.getPlotEventWorkerList();
            } else {
                plotEWList = null;
            }
        }
        return plotEWList;
    }

    private void setupCatalog() {
        if (hub != null) {
            Vis.init(new Vis.InitComplete() {
                public void done() {
                    setupDrawing();
                }
            });
        }
    }

    private void setupDrawing() {
        Vis.init(new Vis.InitComplete() {
            public void done() {
                if (hub != null) {
                    List<WebPlotView> pvList = new ArrayList<WebPlotView>(mpwList.size());
                    for (MiniPlotWidget mpw : mpwList) {
                        pvList.add(mpw.getOps().getPlotView());
                    }
                    CatalogDisplay catDis = hub.getCatalogDisplay();
                    catDis.addPlotViewList(pvList);

                    DataConnectionDisplay dc = hub.getDataConnectionDisplay();
                    if (getPlotEventWorkerIDList() != null) {
                        for (WebPlotView pv : pvList) {
                            dc.addPlotView(pv, getPlotEventWorkerIDList());
                        }
                    }
                }
            }
        });
    }

    private void setupEventWorker(final String ew, final String all, final WebPlotView pv, final WebPlot plot) {
        Vis.init(new Vis.InitComplete() {
            public void done() {
                if (hub != null) {
                    ArrayList<String> eventWorkerIDList = new ArrayList<String>();
                    List allEWorkers = Arrays.asList(all.split(","));
                    for (DrawingLayerProvider p : hub.getDrawingProviders()) {
                        if (allEWorkers.contains(p.getID())) {
                            eventWorkerIDList.add(p.getID());
                        }
                    }

                    DataConnectionDisplay dc = hub.getDataConnectionDisplay();
                    if (getPlotEventWorkerIDList() != null) {
                        dc.addPlotView(pv, eventWorkerIDList);
                    }

                    for (DrawingLayerProvider p : hub.getDrawingProviders()) {
                        if (eventWorkerIDList.contains(p.getID())) {
                            if (p instanceof DatasetDrawingLayerProvider) {
                                Map<String, String> params = ((DatasetDrawingLayerProvider) p).getParams();
                                params.put("subsize", plot.getAttribute("REQUESTED_SIZE").toString());
                                params.put("UserTargetWorldPt",
                                        ((ActiveTarget.PosEntry) plot.getAttribute("FIXED_TARGET")).getPt()
                                                .toString());
                                p.activate(null, params);
                            }
                        }
                    }
                }
            }
        });
    }

    public void updateDisplay() {
        updateDisplay = true; // set value for a state-machine: inform object to get ready to update display.
    }

    public void clearTable() {
        saveScrollPosition();
        mainPanel.clear();
        if (imageGridWidgetGroups.size() > 0) {
            for (ImageGridWidgetGroup g : imageGridWidgetGroups.values()) {
                g.clear();
            }
        }
        imageGridWidgetGroups.clear();
        rebuildGroups();
        // disable popouts
        MiniPlotWidget mpw;
        for (int i = 0; i < mpwList.size(); i++) {
            mpw = mpwList.get(i);
            if (mpw != null) {
                AllPlots.getInstance().setStatus(mpw, AllPlots.PopoutStatus.Disabled);
            }
        }
        AllPlots.getInstance().clearSelectedMPW();
        nextMpw = 0;
    }

    public ComplexPanel getMainPanel() {
        return mainPanel;
    }

    public void addRows(ArrayList<WebPlotRequest> reqList) {
        renderRows(reqList, false);
    }

    public void addRows(DataSet data) {
        if (data == null)
            return;
        loadTableMeta(data);
        renderRows(data, false);
    }

    public void loadTable(ArrayList<WebPlotRequest> reqList) {
        if (reqList == null)
            return;
        renderRows(reqList, true);
    }

    public void loadTable(DataSet data) {
        if (onlyShowingFilteredResults) {
            //todo: find better way to detect filtered results.
            if (hub != null && tablePanel == null) {
                tablePanel = hub.getActiveTable();
                //return;
            }
        }

        if (!updateDisplay)
            return;

        if (data == null) {
            if (mainPanel.getWidgetCount() == 1 && mainPanel.getWidget(0) instanceof HTML
                    && ((HTML) mainPanel.getWidget(0)).getText().equals(EMPTY_GRID)) {
                return;
            }
            empty();
            return;
        }
        if (hub != null) {
            tablePanel = hub.getActiveTable();
            hub.getCatalogDisplay().beginBulkUpdate();
        }
        loadTableMeta(data);
        renderRows(data, true);
        if (hub != null) {
            hub.getCatalogDisplay().endBulkUpdate();
        }

        updateDisplay = false; // set value for a state-machine: inform object display updated.
    }

    public DownloadRequest getDownloadRequest() {
        DownloadRequest retval = tablePanel.getDownloadRequest();

        if (retval == null) {
            for (TablePanel panel : hub.getTables()) {
                retval = panel.getDownloadRequest();
                if (retval != null) {
                    break;
                }
            }
        }
        return retval;
    }

    protected void loadTableMeta(DataSet data) {
        if (data == null) {
            return;
        }

        TableMeta meta = data.getMeta();
        thumbnailUrlColumnKeyword = meta.getAttribute(THUMBNAIL_URL_COLUMN);
        thumbnailTitleColumnKeyword = meta.getAttribute(DESCRIPTION_COLUMN);
        fullSizeUrlColumnKeyword = meta.getAttribute(FULL_SIZE_URL_COLUMN);
        sourceIdColumnKeyword = meta.getAttribute(SOURCE_ID_COLUMN);
        dataTypeColumnKeyword = meta.getAttribute(DATA_TYPE_COLUMN);
        groupingColumnKeyword = meta.getAttribute(GROUPING_COLUMN);
        eventWorkerKeyword = meta.getAttribute(EVENTWORKER_COLUMN);
        allEventWorkerKeyword = meta.getAttribute(ALL_EVENTWORKER_COLUMN);

        info = meta.getAttribute(INFO);

        boolean jpegSelectionHiLite;
        boolean jpegSelectionDClick;
        String attribute = meta.getAttribute(JPEG_SELECTION_HILITE);
        if (attribute != null) {
            jpegSelectionHiLite = parseBoolean(attribute);
        } else {
            jpegSelectionHiLite = false;
        }

        attribute = meta.getAttribute(JPEG_SELECTION_DOUBLE_CLICK);
        if (attribute != null) {
            jpegSelectionDClick = parseBoolean(attribute);
        } else {
            jpegSelectionDClick = false;
        }

        attribute = meta.getAttribute(COLUMNS);
        if (attribute != null) {
            columns = Integer.valueOf(attribute);
            groupingByColumns = true;
        } else {
            columns = 1;
            groupingByColumns = false;
        }

        attribute = meta.getAttribute(GRID_BACKGROUND);
        if (attribute != null && attribute.length() > 0) {
            GwtUtil.setStyle(mainPanel, "backgroundColor", attribute);
            GwtUtil.setStyle(this, "backgroundColor", attribute);
            if (imageGridPanelHandler != null) {
                imageGridPanelHandler.setBackground(attribute);
            }
        }
        if (imageGridPanelHandler != null) {
            imageGridPanelHandler.enableSelectionHiLite(jpegSelectionHiLite);
            imageGridPanelHandler.enableSelectionDoubleClick(jpegSelectionDClick);
        }
        Map metas = meta.getAttributes();
        String key;
        for (Object o : metas.keySet()) {
            key = o.toString();
            if (key.startsWith("TitleDesc.")) {
                if (titleDescMap == null) {
                    titleDescMap = new HashMap<String, String>();
                }
                titleDescMap.put(key, metas.get(key).toString());
            }
        }
        parseGroupingMeta(meta.getAttribute(GROUPING));
    }

    public void resize() {
        if (requiresResize()) {

            for (ImageGridWidgetGroup g : imageGridWidgetGroups.values()) {
                g.resize();
            }
        }
    }

    public boolean isGroupingInRow() {
        return groupingInRow;
    }

    public boolean isGroupingByColumns() {
        return groupingByColumns;
    }

    public String getGroupingShowLabel() {
        return groupingShowLabel;
    }

    private boolean requiresResize() {
        return (columns > 1);
    }

    private void parseGroupingMeta(String str) {
        if (str == null)
            return;
        String[] params = str.split(PARAM_SEP);

        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                Param p = Param.parse(params[i]);
                groupingParams.put(p.getName(), p.getValue());
            }
        }

        rebuildGroups();
    }

    private void rebuildGroups() {
        if (groupingParams.containsKey(keywords)) {
            String groups[] = groupingParams.get(keywords).split(",");
            if (groups != null) {
                for (String group : groups) {
                    //getWidgetList(group);
                }
            }
        }
        if (groupingParams.containsKey(show_in_a_row)) {
            groupingInRow = parseBoolean(groupingParams.get(show_in_a_row));
        }
        if (groupingParams.containsKey(show_label)) {
            groupingShowLabel = groupingParams.get(show_label).trim().toUpperCase();
        }
    }

    protected void renderRows(ArrayList<WebPlotRequest> reqList, boolean clearTable) {
        if (clearTable)
            clearTable();
        plottingCnt = 0;
        successPlots = 0;
        failurePlots = 0;
        clearMaxRowWidth();

        for (WebPlotRequest req : reqList) {
            addWebPlotRequestWidget(getMiniPlotWidget(), req);
        }
        addStuff();
    }

    protected void renderRows(DataSet data, boolean clearTable) {
        String type;
        if (clearTable)
            clearTable();
        rowValues = new ArrayList<TableData.Row>(data.getModel().getRows());

        if (rowValues.size() == 0) {
            String text = (info != null && info.length() > 0) ? info : "Image data not found!";
            showMessage(text);
        } else {
            plottingCnt = 0;
            successPlots = 0;
            failurePlots = 0;
            clearMaxRowWidth();

            MiniPlotWidget mpw;
            for (TableData.Row row : rowValues) {
                type = getThumbnailDataType(row);

                if (type.equals(TYPE_FITS)) {
                    addMiniPlotWidget(getMiniPlotWidget(), row);
                } else if (type.equals(TYPE_JPEG)) {
                    addWidget(makeCellWidget(row), row);
                } else if (type.equals(TYPE_REQ)) {
                    addWebPlotRequestWidget(getMiniPlotWidget(), row);
                }
            }
            addStuff();
        }
    }

    protected void addStuff() {
        for (ImageGridWidgetGroup group : imageGridWidgetGroups.values()) {
            group.populate();
        }
    }

    protected ThumbnailWidget makeCellWidget(TableData.Row row) {
        String background = GRID_BACKGROUND;
        if (imageGridPanelHandler != null)
            background = imageGridPanelHandler.getBackground();
        if (requiresResize()) {
            return new ThumbnailWidget(getThumbnailDesc(row), getThumbnailUrl(row), background, requiresResize(),
                    columns);
        } else {
            return new ThumbnailWidget(getThumbnailDesc(row), getThumbnailUrl(row), background);
        }
    }

    protected void handleDoubleClick(TableData.Row row) {
        VerticalPanel vp = new VerticalPanel();
        final Image previewImage = new Image(getFullSizeUrl(row));
        final HTML caption = new HTML(getPopUpCaption(row));
        String title = getThumbnailDesc(row).replace("<em>", "").replace("</em>", "");

        caption.setWidth("320px");

        previewImage.addLoadHandler(new LoadHandler() {
            public void onLoad(LoadEvent ev) {
                caption.setWidth(previewImage.getWidth() + "px");
            }
        });
        GwtUtil.setStyle(vp, "margin", "8px");

        vp.setCellHorizontalAlignment(previewImage, HasHorizontalAlignment.ALIGN_CENTER);
        vp.setCellVerticalAlignment(previewImage, HasVerticalAlignment.ALIGN_MIDDLE);
        vp.add(previewImage);
        vp.add(caption);

        PopupPane popupPane = new PopupPane(title, vp, PopupType.STANDARD, false, false);

        popupPane.show();
    }

    protected int getResizedWidth() {
        int px = 64; // min width should be no less than min plot width
        Widget parent = mainPanel;

        if (parent != null) {
            px = parent.getOffsetWidth() / columns;
            px = px > 74 ? px - 10 : 64;
        }
        return px;
    }

    private void showMessage(String text) {
        HTML msg = new HTML(text);

        GwtUtil.setStyle(msg, "padding", "8px");
        GwtUtil.setStyle(msg, "fontSize", "120%");
        GwtUtil.setStyle(msg, "textAlign", "center");
        GwtUtil.setStyle(msg, "fontWeight", "bold");

        mainPanel.add(msg);

    }

    private void saveScrollPosition() {
        Element body = getElement();
        int scrollX = body.getScrollLeft();
        int scrollY = body.getScrollTop();
        int viewWidth = DOM.getElementPropertyInt(body, "clientWidth");
        int viewHeight = DOM.getElementPropertyInt(body, "clientHeight");

        if (currentViewHeight != viewHeight) {
            currentViewHeight = viewHeight;
        }

        if (currentViewWidth != viewWidth) {
            currentViewWidth = viewWidth;
        }

        if (currentScrollX != scrollX) {
            currentScrollX = scrollX;
        }

        if (currentScrollY != scrollY) {
            currentScrollY = scrollY;
        }

        if (lockrelated != null && mpwList.size() > 0) {
            lockrelated = mpwList.get(0).getGroup().getLockRelated();
        }
    }

    private void restoreScrollPositions() {
        Element body = getElement();
        int scrollX = body.getScrollLeft();
        int scrollY = body.getScrollTop();
        int viewWidth = DOM.getElementPropertyInt(body, "clientWidth");
        int viewHeight = DOM.getElementPropertyInt(body, "clientHeight");
        if (currentViewHeight == viewHeight && currentViewWidth == viewWidth) {
            if (currentScrollX != scrollX) {
                setHorizontalScrollPosition(currentScrollX);
            }

            if (currentScrollY != scrollY) {
                setScrollPosition(currentScrollY);
            }
        }
    }

    private MiniPlotWidget getMiniPlotWidget() {
        return getMiniPlotWidget(nextMpw++);
    }

    private MiniPlotWidget getMiniPlotWidget(int index) {

        if (index >= mpwList.size()) {
            for (int i = mpwList.size(); i <= index; i++) {
                createAndAddMiniPlotWidget();
            }
            //            setupCatalog();
        }

        return mpwList.get(index);
    }

    private void createAndAddMiniPlotWidget() {
        String groupName = getPlotWidgetGroupValue();
        if (groupName == null)
            groupName = "GridGroup:" + this.hashCode();
        MiniPlotWidget mpw = new MiniPlotWidget(groupName);
        mpw.setErrorDisplayHandler(plotError);
        mpwList.add(mpw);
        mpw.setEnableChecking(checkingEnabled);
        mpw.setBoxSelection(true);

    }

    private String getWidgetGroupKey(String title) {
        String key = "";
        if (groupingParams.containsKey(keywords)) {

            for (String k : imageGridWidgetGroups.keySet()) {
                if (title.contains(k)) {
                    key = k;
                    break;
                }
            }
        } else if (groupingParams.containsKey(groupBy)) {
            String param = groupingParams.get(groupBy);

            if (param.equals("firstWord")) {
                key = title.substring(0, title.indexOf(" "));
            } else if (param.equals("alphabeticPrefix ")) {
                key = title.substring(0, title.indexOf(" "));
            } else if (param.equals("numericPrefix")) {
                key = title.substring(0, title.indexOf(" "));
            } else if (param.equals("wholeWord")) {
                key = title;
            }
        } else if (groupingColumnKeyword != null) {
            key = title;
        }
        return key;
    }

    private void addWidget(Widget widget, String title) {
        String key = getWidgetGroupKey(title);
        if (!imageGridWidgetGroups.containsKey(key)) {
            String desc = null;
            String titleDesc = "TitleDesc." + title;
            if (titleDescMap != null && titleDescMap.size() > 0) {
                desc = titleDescMap.get(titleDesc);
            }
            if (desc == null && hasValidImageGridPreviewData()) {
                //empty string returned if _previewData does not contain specific key
                desc = _previewData.getTitleDesc(titleDesc);
            }
            imageGridWidgetGroups.put(key, new ImageGridWidgetGroup(key, desc));
        }
        imageGridWidgetGroups.get(key).addWidget(widget);
        /*List<Widget> list = getWidgetList(getWidgetGroupKey(title));
        list.add(widget);
        GwtUtil.setStyle(widget, "float", "left");
        GwtUtil.setStyle(widget, "cssFloat", "left");
        GwtUtil.setStyle(widget, "styleFloat", "left");*/
    }

    private void addWidget(ThumbnailWidget widget, TableData.Row row) {
        widget.addClickHandler(imageGridPanelHandler);
        widget.addDoubleClickHandler(imageGridPanelHandler);
        //widget.addErrorHandler(imageGridPanelHandler);
        widget.addMouseOutHandler(imageGridPanelHandler);
        widget.addMouseOverHandler(imageGridPanelHandler);
        addWidget(widget, getGroupingTag(row));
        addToMaxRowWidth(getGroupingTag(row), 128, 10);
        widget.addStyleName("shadow");
        GwtUtil.setStyles(widget, "margin", "2px", "background", "#E5E5E5 url(images/bg_boxgradient.png) repeat-x");
    }

    private void addMiniPlotWidget(MiniPlotWidget mpw, TableData.Row row) {
        float zoomLevel = 1.0F;

        String title = getThumbnailDesc(row);
        String url = getThumbnailUrl(row);

        if (row.getValue("initial_zoom_level") != null)
            zoomLevel = (Float) row.getValue("initial_zoom_level");

        WebPlotRequest req = WebPlotRequest.makeURLPlotRequest(url);

        req.setInitialZoomLevel(zoomLevel);
        req.setInitialRangeValues(new RangeValues());

        addWebPlotRequestWidget(mpw, req, title, getGroupingTag(row), getColumnValue(row, eventWorkerKeyword),
                getColumnValue(row, allEventWorkerKeyword));
    }

    private void addWebPlotRequestWidget(MiniPlotWidget mpw, WebPlotRequest req) {
        addWebPlotRequestWidget(mpw, req, null, null, null, null);
    }

    private void addWebPlotRequestWidget(MiniPlotWidget mpw, TableData.Row row) {
        WebPlotRequest req = WebPlotRequest.parse(getWebPlotRequest(row));

        if (hasValidImageGridPreviewData()) {
            Integer zoomToWidth = _previewData.getZoomToWidth();
            if (zoomToWidth != null && zoomToWidth > 0) {
                req.setZoomToWidth(zoomToWidth);
            }
        }
        String title = getThumbnailDesc(row);
        addWebPlotRequestWidget(mpw, req, title, getGroupingTag(row), getColumnValue(row, eventWorkerKeyword),
                getColumnValue(row, allEventWorkerKeyword));
    }

    private void addWebPlotRequestWidget(MiniPlotWidget mpw, WebPlotRequest req, String title, String groupTag,
            String eventWorkId, String allEventWorkers) {
        int minWidth = 64;
        int minHeight = 64;

        VerticalPanel panel = new GroupedVerticalPanel(groupTag);

        addWidget(panel, groupTag);

        if (title != null)
            req.setTitle(title);

        mpw.setMinSize(minWidth, minHeight);

        mpw.getOps().plot(req, false, new WebPlotCallback(mpw, panel, title, req, eventWorkId, allEventWorkers));
        plottingCnt++;
        //        batch.add(req,mpw,new WebPlotCallback(mpw, panel, title));
        //        if (batch.size()>=4) batch.fire();
        panelMap.put(panel, mpw);
    }

    private void fireBatch() {

    }

    private String getGroupingTag(TableData.Row row) {
        String retval = null;

        if (groupingColumnKeyword != null)
            retval = String.valueOf(row.getValue(groupingColumnKeyword));
        else {
            retval = getWidgetGroupKey(getThumbnailDesc(row));
        }

        return retval;
    }

    private String getColumnValue(TableData.Row row, String columnKeyword) {
        String retval = null;

        if (columnKeyword != null)
            retval = String.valueOf(row.getValue(columnKeyword));
        return retval;
    }

    private String getThumbnailUrl(TableData.Row row) {
        String s = String.valueOf(row.getValue(thumbnailUrlColumnKeyword));
        if (!s.startsWith("http")) {
            s = GWT.getModuleBaseURL() + s;
        }
        return s;
    }

    private String getThumbnailDataType(TableData.Row row) {
        return String.valueOf(row.getValue(dataTypeColumnKeyword));
    }

    private String getThumbnailDesc(TableData.Row row) {
        return String.valueOf(row.getValue(thumbnailTitleColumnKeyword));
    }

    private String getFullSizeUrl(TableData.Row row) {
        return String.valueOf(row.getValue(fullSizeUrlColumnKeyword));
    }

    private String getSourceId(TableData.Row row) {
        String retval = String.valueOf(row.getValue(sourceIdColumnKeyword));
        if (retval.equals("null"))
            retval = "";
        return retval;
    }

    private String getWebPlotRequest(TableData.Row row) {
        return String.valueOf(row.getValue(thumbnailUrlColumnKeyword));
    }

    private void plotSuccess(WebPlot plot) {
    }

    private TableData.Row getRowValue(int row) {
        if (rowValues.size() <= row) {
            return null;
        }
        return rowValues.get(row);
    }

    private TableData.Row getRow(Widget widget) {
        int idx = findIndex(widget);
        return getRowValue(idx);
    }

    private int getMouseOverAt() {
        return this.mouseOverAt;
    }

    private void setMouseOverAt(Widget widget) {
        this.mouseOverAt = findIndex(widget);
    }

    private int getCurrentRow() {
        return this.currentRow;
    }

    private void setCurrentRow(Widget widget) {
        setCurrentRow(findIndex(widget));
    }

    private void setCurrentRow(int cur) {
        if (cur != this.currentRow) {
            this.currentRow = cur;
            //if (dataSet.getSelected().size()>0) {
            //    dataSet.deselectAll();
            //}
            //dataSet.select(this.currentRow );
            //todo: fireTablePreviewEventHubEvent(EventHub.ON_ROWHIGHLIGHT_CHANGE);
        }
    }

    // --------------- Define PopUp URL and title ---------------
    private String getPopUpCaption(TableData.Row row) {
        String caption = getSourceId(row);

        if (caption.length() > 0)
            caption += ": ";
        caption += getThumbnailDesc(row);
        return caption;
    }

    // --------------- find widget index ---------------
    private static int findIndex(Widget cell) {
        int retval = ((ComplexPanel) cell.getParent()).getWidgetIndex(cell);
        Widget w;
        for (int i = 0; i < ((ComplexPanel) cell.getParent()).getWidgetIndex(cell); i++) {
            w = ((ComplexPanel) cell.getParent()).getWidget(i);
            if (w instanceof HTML)
                retval--;
        }
        return retval;
    }

    private boolean parseBoolean(String s) {
        String value = s.toLowerCase().trim();
        return value.equals("t") || value.equals("true");
    }

    public void onShow() {
        for (MiniPlotWidget mpw : mpwList) {
            mpw.setActive(true);
        }
        if (mpwList.size() > 0) {
            mpwList.get(0).getGroup().setFloatingToolbarShowing(true);
        }
    }

    public void onHide() {
        for (MiniPlotWidget mpw : mpwList) {
            mpw.setActive(false);
        }
        if (mpwList.size() > 0) {
            mpwList.get(0).getGroup().setFloatingToolbarShowing(false);
        }
    }

    // MaxRowWidth methods
    private void addToMaxRowWidth(String groupTag, int width, int offset) {
        if (!rowWidths.containsKey(groupTag)) {
            rowWidths.put(groupTag, width + offset);
        } else
            rowWidths.put(groupTag, rowWidths.get(groupTag) + width + offset);
    }

    private void addToMaxRowWidth(String groupTag, int width) {
        addToMaxRowWidth(groupTag, width, 10);
    }

    private void clearMaxRowWidth() {
        if (rowWidths != null) {
            rowWidths.clear();
        }
    }

    private int getMaxRowWidth() {
        int retval = 0;
        for (int i : rowWidths.values()) {
            if (i > retval)
                retval = i;
        }
        return retval;
    }

    private void applyMaxRowWidth() {
        int px = getMaxRowWidth();
        if (px > 0) {
            Widget w = getMainPanel();
            GwtUtil.setStyle(w, "maxWidth", Integer.toString(px) + "px");
            GwtUtil.setStyle(w, "width", Integer.toString(px) + "px");
        }
    }

    private void addLineBreak() {
        Widget itemW = new HTML();
        GwtUtil.setStyle(itemW, "clear", "both");
        getMainPanel().add(itemW);
    }

    private static void setFloatLeft(Widget w) {
        GwtUtil.setStyle(w, "float", "left");
        GwtUtil.setStyle(w, "cssFloat", "left");
        GwtUtil.setStyle(w, "styleFloat", "left");
    }

    //---- ImageGridPanelHandler class ----
    private class ImageGridPanelHandler
            implements ClickHandler, DoubleClickHandler, ErrorHandler, MouseOverHandler, MouseOutHandler {
        private boolean doHilite = true;
        private boolean doDoubleClick = true;
        private String hiliteBackground = "#bde";
        private String hiliteBorder = "#abc";
        private String normalBackground = GRID_BACKGROUND_DEFAULT_COLOR;

        public void enableSelectionHiLite(boolean enable) {
            doHilite = enable;
        }

        public void enableSelectionDoubleClick(boolean enable) {
            doDoubleClick = enable;
        }

        public String getBackground() {

            return normalBackground;
        }

        public void setBackground(String rgb) {
            normalBackground = rgb;
        }

        public void setHiLiteBackground(String rgb) {
            hiliteBackground = rgb;
        }

        public void setHiLiteBorder(String rgb) {
            hiliteBorder = rgb;
        }

        private Timer mouseOverTimer = new Timer() {
            public void run() {
                BasicImageGrid.this.setCurrentRow(BasicImageGrid.this.getMouseOverAt());
            }
        };

        public void onDoubleClick(DoubleClickEvent event) {
            TableData.Row row = BasicImageGrid.this.getRow((Widget) event.getSource());
            handleDoubleClick(row);
        }

        public void onClick(ClickEvent event) {
            if (doDoubleClick) {
                Object source = event.getSource();
                BasicImageGrid.this.setCurrentRow((Widget) source);
            }
        }

        public void onMouseOver(MouseOverEvent event) {
            Widget widget = (Widget) event.getSource();
            if (doHilite) {
                GwtUtil.setStyle(widget, "backgroundColor", hiliteBackground);
                GwtUtil.setStyle(widget, "border", "1px solid " + hiliteBorder);
            }
            BasicImageGrid.this.setMouseOverAt(widget);
            mouseOverTimer.schedule(1500);
        }

        public void onMouseOut(MouseOutEvent event) {
            Widget widget = (Widget) event.getSource();
            if (doHilite) {
                GwtUtil.setStyle(widget, "backgroundColor", normalBackground);
                GwtUtil.setStyle(widget, "border", "1px solid " + normalBackground);
            }
            mouseOverTimer.cancel();
        }

        public void onError(ErrorEvent ev) {
            Object src = ev.getSource();

            if (src instanceof Image) {
                Image image = (Image) src;
                image.setUrl("images/blank_image_icon.png");
            }
        }
    }

    //---- ImageGridWidgetGroup class ----
    private class ImageGridWidgetGroup {
        List<Widget> widgets = null;
        String groupTitle = null, groupDesc = null;

        public ImageGridWidgetGroup(String title, String desc) {
            this.groupTitle = title;
            this.groupDesc = desc;
        }

        public List<Widget> getWidgetsAsList() {
            if (widgets == null)
                widgets = new ArrayList<Widget>();
            return widgets;
        }

        public void addWidget(Widget w) {
            if (widgets == null)
                widgets = new ArrayList<Widget>();
            widgets.add(w);
        }

        public void clear() {
            widgets.clear();
        }

        public int populate() {
            int px, counter = 0;
            Widget label, desc;

            if (!getGroupingShowLabel().equals("OFF")) { //add group title
                label = new HTML(groupTitle);
                GwtUtil.setStyle(label, "padding", "4px");
                GwtUtil.setStyle(label, "fontSize", "120%");
                GwtUtil.setStyle(label, "textShadow", "#888 0px 2px 2px");
                GwtUtil.setStyle(label, "textAlign", "left");
                GwtUtil.setStyle(label, "fontWeight", "bold");
                setFloatLeft(label);
                getMainPanel().add(label);
                if (groupDesc != null) {
                    if (!getGroupingShowLabel().equals("TOP"))
                        addLineBreak();
                    desc = new HTML(groupDesc);
                    GwtUtil.setStyle(desc, "padding", "4px");
                    GwtUtil.setStyle(desc, "fontSize", "100%");
                    GwtUtil.setStyle(desc, "textAlign", "left");
                    setFloatLeft(desc);
                    getMainPanel().add(desc);
                }
            }

            if (getGroupingShowLabel().equals("TOP"))
                addLineBreak();

            px = getResizedWidth() - 8;
            for (Widget w : widgets) {
                if (requiresResize()) {
                    GwtUtil.setStyle(w, "maxWidth", Integer.toString(px) + "px");
                    GwtUtil.setStyle(w, "width", Integer.toString(px) + "px");
                }
                setFloatLeft(w);
                getMainPanel().add(w);
                counter++;
                if (columns > 1 && counter % columns == 0) {
                    addLineBreak();
                }
            }
            addLineBreak();

            return counter;
        }

        public void resize() {
            int px = getResizedWidth();
            for (Widget w : widgets) {
                if (!(w instanceof HTML)) {
                    VerticalPanel vp = ((VerticalPanel) w);
                    GwtUtil.setStyle(vp, "maxWidth", Integer.toString(px) + "px");
                    GwtUtil.setStyle(vp, "width", Integer.toString(px) + "px");
                    GwtUtil.setStyle(vp, "clear", "none");

                    for (Iterator<Widget> i = vp.iterator(); i.hasNext();) {
                        Widget c = i.next();
                        try {
                            if (panelMap.containsKey(vp)) {
                                int height = panelMap.get(vp).getCurrentPlot().getScreenHeight()
                                        + MPW_HEADER_HEIGHT;
                                int width = px - 8;
                                vp.clear();
                                vp.add(panelMap.get(vp).makeFixedSizeContainer(width, height, false));
                                vp.addStyleName("shadow");
                                GwtUtil.setStyles(vp, "margin", "2px", "background",
                                        "#E5E5E5 url(images/bg_boxgradient.png) repeat-x");
                            } else {
                                GwtUtil.setStyle(c, "maxWidth", Integer.toString(px) + "px");
                                GwtUtil.setStyle(c, "width", Integer.toString(px) + "px");
                            }
                        } catch (NullPointerException e) {
                            //todo: handle null pointer exception
                        }
                    }
                }
            }
        }
    }

    //---- WebPlotCallback class ----
    private class WebPlotCallback implements AsyncCallback<WebPlot> {
        MiniPlotWidget _mpw = null;
        String _title = null;
        VerticalPanel _panel = null;
        String _eventWorkerId = null;
        String _allEventWorkers = null;
        WebPlotRequest _req = null;

        public WebPlotCallback(MiniPlotWidget mpw, VerticalPanel panel, String title, WebPlotRequest req,
                String eventWorkerId, String allEventWorkers) {
            _mpw = mpw;
            _panel = panel;
            _title = title;
            _req = req;
            _eventWorkerId = eventWorkerId;
            _allEventWorkers = allEventWorkers;
        }

        public void onFailure(Throwable caught) {
            plotDone();
            updatePlotWidgetGroupStatus(false);
            int width = 128;
            int height = 128;
            if (_previewData.getHideFailurePlot())
                return;

            if (requiresResize()) {
                width = getResizedWidth() - 4;
                height = lastThumbnailHeight;
            } else if (_req.getZoomToWidth() > 0) {
                width = _req.getZoomToWidth();
                height = _req.getZoomToWidth();
            }
            _panel.add(_mpw.makeFailureMessage("No Image returned.", width, height + MPW_HEADER_HEIGHT, false));
            _panel.addStyleName("shadow");
            GwtUtil.setStyles(_panel, "margin", "2px", "background",
                    "#E5E5E5 url(images/bg_boxgradient.png) repeat-x");

            updateMaxRowWidth(width);
        }

        public void onSuccess(WebPlot plot) {
            if (plot != null && _panel != null && _mpw != null) {
                int width = plot.getScreenWidth();
                int height = plot.getScreenHeight();

                if (requiresResize()) {
                    width = getResizedWidth() - 4;
                } else if (_req.getZoomToWidth() > 0) {
                    width = _req.getZoomToWidth();
                    height = _req.getZoomToWidth();
                }
                if (_previewData != null)
                    _previewData.postPlot(_mpw, plot);
                _panel.add(_mpw.makeFixedSizeContainer(width, height + MPW_HEADER_HEIGHT, false));
                _panel.addStyleName("shadow");
                GwtUtil.setStyles(_panel, "margin", "2px", "background",
                        "#E5E5E5 url(images/bg_boxgradient.png) repeat-x");
                //                if (mpwList.lastIndexOf(_mpw)==(mpwList.size()-1)) {
                //                }
                if ((_eventWorkerId != null || _allEventWorkers != null) && hub != null) {
                    setupEventWorker(_eventWorkerId, _allEventWorkers, _mpw.getPlotView(), plot);
                }
                //                setupDrawing(_mpw.getPlotView());
                _mpw.getGroup().enableFloatVisBar(BasicImageGrid.this);
                AllPlots.getInstance().setStatus(_mpw, AllPlots.PopoutStatus.Enabled);

                lastThumbnailHeight = height;
                plotDone();
                updateMaxRowWidth(width);
                updatePlotWidgetGroupStatus(true);
            }
        }

        private void updatePlotWidgetGroupStatus(boolean success) {
            if (!_previewData.getUpdatePlotWidgetGroupStatus())
                return;

            StringBuffer buffer = new StringBuffer();

            if (success)
                successPlots++;
            else
                failurePlots++;
            buffer.append("Success: ");
            buffer.append(successPlots);
            buffer.append(", Failure: ");
            buffer.append(failurePlots);

            _mpw.getGroup().updateFloatingStatus(new String(buffer), BasicImageGrid.this);
        }

        private void updateMaxRowWidth(int width) {
            if (!(_panel instanceof GroupedVerticalPanel))
                return;

            addToMaxRowWidth(((GroupedVerticalPanel) _panel).getGroupTag(), width);

            if (plottingCnt == 0 && isGroupingInRow()) {
                applyMaxRowWidth();
                clearMaxRowWidth();
            }
        }

        private void plotDone() {
            plottingCnt--;
            if (plottingCnt == 0) {
                restoreScrollPositions();
                _mpw.getGroup().setLockRelated(getLockRelated());
                setupCatalog();
                eventManager.fireEvent(new WebEvent(this, ON_ALL_PLOTS_DONE));
                // the following line causes first plot to be selected, which is not always right
                // for example, when a row (a plot) is selected from coverage, we want to preserve the selection
                // AllPlots.getInstance().setSelectedWidget(AllPlots.getInstance().getActiveList().get(0));
                resize();
            }
        }
    }

    //---- PlotWidgetEventListener class ----
    private class PlotWidgetEventListener implements WebEventListener {
        public void eventNotify(WebEvent ev) {
            handlePlotEvent(ev);
        }
    }

    //---- PostPlotWidgetEventListener class ----
    private class PostPlotWidgetEventListener implements WebEventListener {
        public void eventNotify(WebEvent ev) {
            handlePostPlotEvent(ev);
        }
    }

    //---- GroupedVerticalPanel class ----
    private static class GroupedVerticalPanel extends VerticalPanel {
        String groupTag = null;

        public GroupedVerticalPanel(String groupTag) {
            super();
            this.groupTag = groupTag;
        }

        public String getGroupTag() {
            return groupTag;
        }
    }

    private class EventWorkerResolver {
        static final String KEYWORD = "col_keyword";
        static final String MAPPING = "col_mapping";

        String methodTag = null;
        HashMap<String, String> map = null;

        public EventWorkerResolver(String meta) {
            parseMetaData(meta);
        }

        private void parseMetaData(String meta) {
            String key, value;
            for (String tag : meta.split("&")) {
                key = getKey(tag, "=");
                value = getValue(tag, "=");
                if (key.equals("method"))
                    methodTag = value;
                else if (key.equals("map")) {
                    map = new HashMap<String, String>();
                    for (String duo : value.split(";")) {
                        map.put(getKey(duo, ";"), getValue(duo, ";"));
                    }
                }
            }

        }

        private String getKey(String tag, String delimiter) {
            return tag.split(delimiter)[0].trim();
        }

        private String getValue(String tag, String delimiter) {
            return tag.split(delimiter)[1].trim();
        }

        public String[] ResolveEventWorkerId(TableData.Row row) {
            if (methodTag.equals(KEYWORD)) {
                return null;
            } else if (methodTag.equals(MAPPING)) {
                return null;
            }
            return null;
        }
    }
}