edu.nrao.dss.client.widget.explorers.Explorer.java Source code

Java tutorial

Introduction

Here is the source code for edu.nrao.dss.client.widget.explorers.Explorer.java

Source

// Copyright (C) 2011 Associated Universities, Inc. Washington DC, USA.
// 
// 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 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, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// 
// Correspondence concerning GBT software should be addressed as follows:
//       GBT Operations
//       National Radio Astronomy Observatory
//       P. O. Box 2
//       Green Bank, WV 24944-0002 USA

package edu.nrao.dss.client.widget.explorers;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import com.extjs.gxt.ui.client.GXT;
import com.extjs.gxt.ui.client.Style.Scroll;
import com.extjs.gxt.ui.client.Style.SelectionMode;
import com.extjs.gxt.ui.client.core.El;
import com.extjs.gxt.ui.client.data.BaseModelData;
import com.extjs.gxt.ui.client.data.BasePagingLoadResult;
import com.extjs.gxt.ui.client.data.BasePagingLoader;
import com.extjs.gxt.ui.client.data.DataField;
import com.extjs.gxt.ui.client.data.DataReader;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.data.ModelType;
import com.extjs.gxt.ui.client.data.PagingLoader;
import com.extjs.gxt.ui.client.event.ButtonEvent;
import com.extjs.gxt.ui.client.event.ComponentEvent;
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.KeyListener;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.MenuEvent;
import com.extjs.gxt.ui.client.event.SelectionChangedEvent;
import com.extjs.gxt.ui.client.event.SelectionChangedListener;
import com.extjs.gxt.ui.client.event.SelectionListener;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.store.Record;
import com.extjs.gxt.ui.client.store.Store;
import com.extjs.gxt.ui.client.store.StoreEvent;
import com.extjs.gxt.ui.client.store.StoreListener;
import com.extjs.gxt.ui.client.util.Margins;
import com.extjs.gxt.ui.client.widget.Component;
import com.extjs.gxt.ui.client.widget.Container;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.Dialog;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.Text;
import com.extjs.gxt.ui.client.widget.WidgetComponent;
import com.extjs.gxt.ui.client.widget.button.Button;
import com.extjs.gxt.ui.client.widget.button.SplitButton;
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.SimpleComboBox;
import com.extjs.gxt.ui.client.widget.form.TextField;
import com.extjs.gxt.ui.client.widget.form.ComboBox.TriggerAction;
import com.extjs.gxt.ui.client.widget.grid.CellEditor;
import com.extjs.gxt.ui.client.widget.grid.CheckColumnConfig;
import com.extjs.gxt.ui.client.widget.grid.ColumnConfig;
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.GridSelectionModel;
import com.extjs.gxt.ui.client.widget.layout.FitLayout;
import com.extjs.gxt.ui.client.widget.layout.RowData;
import com.extjs.gxt.ui.client.widget.menu.CheckMenuItem;
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.FillToolItem;
import com.extjs.gxt.ui.client.widget.toolbar.LabelToolItem;
import com.extjs.gxt.ui.client.widget.toolbar.PagingToolBar;
import com.extjs.gxt.ui.client.widget.toolbar.SeparatorToolItem;
import com.extjs.gxt.ui.client.widget.toolbar.ToolBar;
import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.user.client.Window;

import edu.nrao.dss.client.data.OptionsFilter;
import edu.nrao.dss.client.util.DynamicHttpProxy;
import edu.nrao.dss.client.util.JSONCallbackAdapter;
import edu.nrao.dss.client.util.JSONRequest;
import edu.nrao.dss.client.util.PagingJsonReader;
import edu.nrao.dss.client.widget.FilterItem;
import edu.nrao.dss.client.widget.FilterMenu;

public class Explorer extends ContentPanel {

    public Explorer(String url, String defaultArgs, ModelType mType) {
        rootURL = url;
        initialArgs = defaultArgs;
        modelType = mType;
        defaultDate = "";
        pagingToolBar = null;
    }

    public Explorer(String url, String defaultArgs, ModelType mType, PagingToolBar ptb) {
        rootURL = url;
        initialArgs = defaultArgs;
        modelType = mType;
        defaultDate = "";
        pagingToolBar = ptb;
    }

    @SuppressWarnings("unchecked")
    protected void initLayout(ColumnModel cm, Boolean createToolBar) {

        setHeaderVisible(false);
        setLayout(new FitLayout());
        setCommitState(false);
        //setAutoHeight(true);
        setScrollMode(Scroll.NONE);

        RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, rootURL + initialArgs);

        DataReader reader = new PagingJsonReader<BasePagingLoadResult>(modelType);
        proxy = new DynamicHttpProxy<BasePagingLoadResult<BaseModelData>>(builder);
        loader = new BasePagingLoader<BasePagingLoadResult<BaseModelData>>(proxy, reader);
        loader.setRemoteSort(true);

        store = new ListStore<BaseModelData>(loader);

        setGrid(new EditorGrid<BaseModelData>(store, cm));
        //grid.setAutoHeight(true);
        grid.setHeight(875);

        GridSelectionModel<BaseModelData> selectionModel = new GridSelectionModel<BaseModelData>();
        selectionModel.setSelectionMode(SelectionMode.MULTI);
        grid.setSelectionModel(selectionModel);
        addPlugins();
        add(grid);
        grid.setBorders(true);

        if (createToolBar) {
            initToolBar();
        }
        columnConfForm = new ColumnConfigForm(this);
        filterComboForm = new FilterComboForm(this);
        undoredo = new UndoRedo(this);

        initListeners();

        if (this.loadDataInitially) {
            loadData();
        }
    }

    protected void addPlugins() {
        for (CheckColumnConfig cb : checkBoxes) {
            cb.setFiresEvents(true);
            grid.addPlugin(cb);
        }
    }

    public void loadData() {
        loader.load(0, getPageSize());
    }

    private void initListeners() {

        undoredo.initListeners();

        store.addStoreListener(new StoreListener<BaseModelData>() {
            @Override
            public void storeUpdate(StoreEvent<BaseModelData> se) {
                save(se.getModel());
            }
        });
    }

    private void save(ModelData model) {
        if (!isCommitState()) {
            return;
        }
        ArrayList<String> keys = new ArrayList<String>();
        ArrayList<String> values = new ArrayList<String>();

        keys.add("_method");
        values.add("put");

        for (String name : model.getPropertyNames()) {
            Object value = model.get(name);
            if (value != null) {
                keys.add(name);
                values.add(value.toString());
            }
        }
        JSONRequest.post(rootURL + "/" + ((Number) model.get("id")).intValue(), keys.toArray(new String[] {}),
                values.toArray(new String[] {}), new JSONCallbackAdapter());
        updateObservers();
    }

    // to be implemented by children
    public void registerObservers() {

    }

    // to be implemented by children
    public void updateObservers() {

    }

    // to be implemented by children
    public void viewObject() {
        //return grid.getSelectionModel().getSelectedItem();
    }

    // to be implemented by children
    public void actionOnObject() {
        //return grid.getSelectionModel().getSelectedItem();
    }

    private void initToolBar() {
        if (pagingToolBar == null) {
            pagingToolBar = new PagingToolBar(50);
        }

        final TextField<String> pages = new TextField<String>();
        pages.setWidth(30);
        pages.setValue("50");
        pages.addKeyListener(new KeyListener() {
            public void componentKeyPress(ComponentEvent e) {
                if (e.getKeyCode() == 13) {
                    int page_size = Integer.valueOf(pages.getValue()).intValue();
                    pagingToolBar.setPageSize(page_size);
                    setPageSize(page_size);
                    loadData();
                }
            }
        });
        pages.setTitle("Page Size");
        pagingToolBar.add(pages);
        setBottomComponent(pagingToolBar);
        pagingToolBar.bind(loader);

        toolBar = new ToolBar();
        if (createFilterToolBar) {
            LayoutContainer toolBars = new LayoutContainer();
            setTopComponent(toolBars);
            filterToolBar = new ToolBar();
            toolBars.add(toolBar);
            toolBars.add(filterToolBar);
            initFilterToolBar();
        } else {
            setTopComponent(toolBar);
        }

        if (showColumnsMenu) {
            columnsItem = new Button("Columns");
            columnsItem.setToolTip("Manage column configurations.");
            columnsItem.setMenu(initColumnsMenu());
            toolBar.add(columnsItem);
        }

        viewItem = new Button("View");
        toolBar.add(viewItem);
        viewItem.setToolTip("View selected row.");
        viewItem.addSelectionListener(new SelectionListener<ButtonEvent>() {
            @Override
            public void componentSelected(ButtonEvent ce) {
                viewObject();
            }
        });
        // hide this button by default
        viewItem.setVisible(false);

        addItem = new Button("Add");
        toolBar.add(addItem);
        addItem.setToolTip("Add a new row.");
        addItem.addSelectionListener(new SelectionListener<ButtonEvent>() {
            @Override
            public void componentSelected(ButtonEvent ce) {
                HashMap<String, Object> fields = new HashMap<String, Object>();
                if (defaultDate != "") {
                    fields.put("date", defaultDate);
                }
                addRecord(fields);
            }
        });

        final Button duplicateItem = new Button("Duplicate");
        toolBar.add(duplicateItem);
        duplicateItem.setToolTip("Copy a row.");
        duplicateItem.setEnabled(false);
        duplicateItem.addSelectionListener(new SelectionListener<ButtonEvent>() {
            @Override
            public void componentSelected(ButtonEvent be) {
                HashMap<String, Object> fields = new HashMap<String, Object>(
                        grid.getSelectionModel().getSelectedItem().getProperties());
                fields.put("pcode", fields.get("pcode") + " - 2");
                addRecord(fields);
                grid.getView().refresh(true);
            }
        });

        removeDialog = new Dialog();
        removeDialog.setHeading("Confirmation");
        removeDialog.addText("Remove record?");
        removeDialog.setButtons(Dialog.YESNO);
        removeDialog.setHideOnButtonClick(true);
        setRemoveApproval(removeDialog.getButtonById(Dialog.YES));
        getRemoveApproval().addSelectionListener(new SelectionListener<ButtonEvent>() {
            @Override
            public void componentSelected(ButtonEvent ce) {
                Double id = grid.getSelectionModel().getSelectedItem().get("id");
                JSONRequest.delete(rootURL + "/" + id.intValue(), new JSONCallbackAdapter() {
                    public void onSuccess(JSONObject json) {
                        store.remove(grid.getSelectionModel().getSelectedItem());
                    }
                });
                updateObservers();
            }
        });
        removeDialog.hide();

        removeItem = new Button("Delete");
        toolBar.add(removeItem);
        removeItem.setToolTip("Delete a row.");
        // make it so that children can override this behavior
        setRemoveItemListener();

        columnEditItem = new ToggleButton("Column Edit");
        columnEditItem.setToolTip(
                "Click to enable column edit mode which will repeat the last edit for all the selected rows.");
        toolBar.add(columnEditItem);

        // add a generic button that can be changed for whatever purpose a child class may have for it
        actionItem = new Button("Action");
        toolBar.add(actionItem);
        //actionItem.setToolTip("view row.");
        actionItem.addSelectionListener(new SelectionListener<ButtonEvent>() {
            @Override
            public void componentSelected(ButtonEvent ce) {
                actionOnObject();
            }
        });
        // hide this button by default
        actionItem.setVisible(false);

        toolBar.add(new SeparatorToolItem());

        toolBar.add(new FillToolItem());
        toolBar.add(new SeparatorToolItem());

        undoItem = new Button();
        undoItem.setToolTip("Undo the last edit.");
        undoItem.disable();
        undoItem.setIcon(GXT.IMAGES.paging_toolbar_prev());
        toolBar.add(undoItem);

        undoItem.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent ce) {
                undoredo.doUndo();
            }

        });

        redoItem = new Button();
        redoItem.setToolTip("Redo the last undo.");
        redoItem.disable();
        redoItem.setIcon(GXT.IMAGES.paging_toolbar_next());
        toolBar.add(redoItem);

        redoItem.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent ce) {
                undoredo.doRedo();
            }

        });

        toolBar.add(new SeparatorToolItem());

        cancelItem = new Button("Cancel");
        cancelItem.setToolTip(
                "Cancel changes.  Note: modified fields are indicated with a red trangle in the upper left corn of the cell.");
        toolBar.add(cancelItem);

        //  Reject outstanding changes
        cancelItem.addSelectionListener(new SelectionListener<ButtonEvent>() {

            @Override
            public void componentSelected(ButtonEvent ce) {
                store.rejectChanges();
                undoredo.reset();
            }

        });

        setSaveItem(new Button("Save"));
        getSaveItem().setToolTip(
                "Save changes.  Note: modified fields are indicated with a red trangle in the upper left corn of the cell.");
        toolBar.add(getSaveItem());

        // Commit outstanding changes to the server.
        getSaveItem().addSelectionListener(new SelectionListener<ButtonEvent>() {
            @Override
            public void componentSelected(ButtonEvent be) {
                setCommitState(true);
                store.commitChanges();
                setCommitState(false);
                //loadData();
                //grid.getView().refresh(true);

                undoredo.reset();
            }
        });

        // Enable the "Duplicate" button only if there is a selection.
        grid.getSelectionModel().addSelectionChangedListener(new SelectionChangedListener<BaseModelData>() {
            @Override
            public void selectionChanged(SelectionChangedEvent<BaseModelData> se) {
                duplicateItem.setEnabled(!grid.getSelectionModel().getSelectedItems().isEmpty());
            }
        });
    }

    private void initFilterToolBar() {
        filter = new FilterItem(Explorer.this, false);
        filterToolBar.add(filter.getTextField());

        for (SimpleComboBox<String> f : advancedFilters) {
            filterToolBar.add(new SeparatorToolItem());
            //  Karen wants to take out the labels on the advanced filters and
            //  use the tool tips for now.
            //filterToolBar.add(new LabelToolItem(f.getTitle()));
            filterToolBar.add(f);
        }
        filterToolBar.add(new SeparatorToolItem());
        Button reset = new Button("Reset");
        reset.setToolTip("Resets filter drop down menus.");
        reset.addSelectionListener(new SelectionListener<ButtonEvent>() {
            @Override
            public void componentSelected(ButtonEvent ce) {
                for (SimpleComboBox<String> f : advancedFilters) {
                    f.reset();
                }
                filter.getTextField().setValue("");
            }
        });
        filterToolBar.add(reset);
        filterToolBar.add(new SeparatorToolItem());
        if (filterAction != null) {
            filterToolBar.add(filterAction);
            filterAction.setToolTip("Execute filter combination.");
        }
    }

    private Menu initColumnsMenu() {
        final Menu menu = new Menu();
        MenuItem saveConfig = new MenuItem("Save Column Combination");
        saveConfig.addSelectionListener(new SelectionListener<MenuEvent>() {

            public void componentSelected(MenuEvent ce) {
                com.extjs.gxt.ui.client.widget.Window w = columnConfForm.getWindow();
                columnConfForm.show();
                w.show();
            }

        });

        menu.add(saveConfig);
        MenuItem removeConfigs = new MenuItem("Remove Checked Items");
        removeConfigs.addSelectionListener(new SelectionListener<MenuEvent>() {

            @Override
            public void componentSelected(MenuEvent ce) {
                for (Component mi : menu.getItems()) {
                    //  Doing this the Python way. ;)
                    try {
                        final ColumnConfigMenuItem cmi = (ColumnConfigMenuItem) mi;
                        if (cmi.isChecked()) {
                            HashMap<String, Object> data = new HashMap<String, Object>();
                            data.put("method_", "DELETE");
                            JSONRequest.post("/scheduler/configurations/explorer/columnConfigs/" + cmi.config_id,
                                    data, new JSONCallbackAdapter() {
                                        public void onSuccess(JSONObject json) {
                                            menu.remove(cmi);
                                        }
                                    });
                        }
                    } catch (ClassCastException e) {

                    }

                }
            }

        });
        menu.add(removeConfigs);
        menu.add(new SeparatorMenuItem());

        MenuItem all = new MenuItem("Restore All");
        all.addSelectionListener(new SelectionListener<MenuEvent>() {

            @Override
            public void componentSelected(MenuEvent ce) {
                for (ColumnConfig cc : grid.getColumnModel().getColumns()) {
                    cc.setHidden(false);
                }
                grid.getView().refresh(true);
            }

        });
        menu.add(all);
        menu.add(new SeparatorMenuItem());

        HashMap<String, Object> data = new HashMap<String, Object>();
        data.put("explorer", rootURL);
        // Get save configurations from the server and populate them as menu items
        JSONRequest.get("/scheduler/configurations/explorer/columnConfigs", data, new JSONCallbackAdapter() {
            public void onSuccess(JSONObject json) {
                JSONArray configs = json.get("configs").isArray();
                for (int i = 0; i < configs.size(); ++i) {
                    JSONArray config = configs.get(i).isArray();
                    String config_id = config.get(1).isNumber().toString();
                    ColumnConfigMenuItem mi = new ColumnConfigMenuItem(grid, config.get(0).isString().stringValue(),
                            config_id);
                    menu.add(mi);
                    columnConfigIds.add(config_id);
                }
            }
        });
        return menu;
    }

    protected FilterMenu initFilterMenu() {
        filterMenu = new FilterMenu();
        MenuItem saveCombos = new MenuItem("Save Filter Combination");
        saveCombos.addSelectionListener(new SelectionListener<MenuEvent>() {

            public void componentSelected(MenuEvent ce) {
                com.extjs.gxt.ui.client.widget.Window w = filterComboForm.getWindow();
                filterComboForm.show();
                w.show();
            }

        });

        filterMenu.add(saveCombos);
        MenuItem removeCombos = new MenuItem("Remove Checked Items");
        removeCombos.addSelectionListener(new SelectionListener<MenuEvent>() {

            @Override
            public void componentSelected(MenuEvent ce) {
                for (Component mi : filterMenu.getItems()) {
                    //  Doing this the Python way. ;)
                    try {
                        final FilterComboMenuItem cmi = (FilterComboMenuItem) mi;
                        if (cmi.isChecked()) {
                            HashMap<String, Object> data = new HashMap<String, Object>();
                            data.put("method_", "DELETE");
                            JSONRequest.post("/scheduler/configurations/explorer/filterCombos/" + cmi.combo_id,
                                    data, new JSONCallbackAdapter() {
                                        public void onSuccess(JSONObject json) {
                                            filterMenu.remove(cmi);
                                        }
                                    });
                        }
                    } catch (ClassCastException e) {

                    }

                }
            }

        });
        filterMenu.add(removeCombos);
        filterMenu.add(new SeparatorMenuItem());

        // We'll need to pass in this object as to the FilterComboMenuItem constructor below
        // so do this to make sure it will be in scope. 
        final Explorer e = this;
        HashMap<String, Object> data = new HashMap<String, Object>();
        data.put("explorer", rootURL);
        // Get save configurations from the server and populate them as menu items
        JSONRequest.get("/scheduler/configurations/explorer/filterCombos", data, new JSONCallbackAdapter() {
            public void onSuccess(JSONObject json) {
                JSONArray configs = json.get("configs").isArray();
                for (int i = 0; i < configs.size(); ++i) {
                    JSONArray config = configs.get(i).isArray();
                    String filter_id = config.get(1).isNumber().toString();
                    FilterComboMenuItem mi = new FilterComboMenuItem(e, config.get(0).isString().stringValue(),
                            filter_id);
                    filterMenu.add(mi);
                    filterComboIds.add(filter_id);
                }
            }
        });
        return filterMenu;
    }

    protected void initOptionsFilter() {
        optionsFilter = new OptionsFilter();
        toolBar.insert(new SeparatorToolItem(), 6);
        toolBar.insert(optionsFilter.getSemester(), 7);
        toolBar.insert(optionsFilter.getEnabled(), 8);
        toolBar.insert(optionsFilter.getNotComplete(), 9);

    }

    protected void setRemoveItemListener() {
        removeItem.addSelectionListener(new SelectionListener<ButtonEvent>() {
            @Override
            public void componentSelected(ButtonEvent be) {
                removeDialog.show();
            }
        });
    }

    public void addRecordInterface(HashMap<String, Object> fields) {
        addRecord(fields);
    }

    protected void addRecord(HashMap<String, Object> fields) {
        JSONRequest.post(rootURL, fields, new JSONCallbackAdapter() {
            @Override
            public void onSuccess(JSONObject json) {
                BaseModelData model = new BaseModelData();
                for (int i = 0; i < modelType.getFieldCount(); ++i) {
                    DataField field = modelType.getField(i);
                    String fName = field.getName();
                    if (json.containsKey(fName)) {
                        // Set model value dependent on data type
                        JSONValue value = json.get(fName);
                        if (value.isNumber() != null) {
                            double numValue = value.isNumber().doubleValue();
                            // Note: we're treating even integer values like doubles here.  Doesn't appear
                            // to be an issue.  1.0 is displayed as 1 anyways.
                            model.set(fName, numValue);
                        } else if (value.isBoolean() != null) {
                            model.set(fName, value.isBoolean().booleanValue());
                        } else if (value.isString() != null) {
                            model.set(fName, value.isString().stringValue());
                        } else if (value.isNull() != null) {
                            // not raising an error seems to be fine.
                        } else {
                            Window.alert("unknown JSON value type");
                        }
                    }
                }
                grid.stopEditing();
                store.insert(model, 0);
                //grid.getView().refresh(true);
                grid.getSelectionModel().select(model, false);
            }
        });
    }

    protected SimpleComboBox<String> initCombo(String title, String[] options, int width) {
        SimpleComboBox<String> filter = new SimpleComboBox<String>();
        filter.setTriggerAction(TriggerAction.ALL);
        filter.setWidth(width);
        filter.setEmptyText(title);
        filter.setTitle(title);
        for (String o : options) {
            filter.add(o);
        }
        return filter;
    }

    protected CellEditor initCombo(String[] options) {
        final SimpleComboBox<String> combo = new SimpleComboBox<String>();
        combo.setForceSelection(true);
        combo.setTriggerAction(TriggerAction.ALL);
        for (String o : options) {
            combo.add(o);
        }

        CellEditor editor = new CellEditor(combo) {
            @Override
            public Object preProcessValue(Object value) {
                if (value == null) {
                    return value;
                }
                return combo.findModel(value.toString());
            }

            @Override
            public Object postProcessValue(Object value) {
                if (value == null) {
                    return value;
                }
                return ((ModelData) value).get("value");
            }
        };
        return editor;
    }

    public DynamicHttpProxy<BasePagingLoadResult<BaseModelData>> getProxy() {
        return proxy;
    }

    public boolean getLoadDataInitially() {
        return loadDataInitially;
    }

    public void setLoadDataInitially(boolean state) {
        loadDataInitially = state;
    }

    public String getRootURL() {
        return rootURL;
    }

    public void setRootURL(String rurl) {
        rootURL = rurl;
        RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, rootURL);
        proxy.setBuilder(builder);
    }

    public ToggleButton getColumnEditItem() {
        return columnEditItem;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public int getPageSize() {
        return pageSize;
    }

    public Button getColumnsItem() {
        return columnsItem;
    }

    public SplitButton getFilterAction() {
        return filterAction;
    }

    public FilterMenu getFilterMenu() {
        return filterMenu;
    }

    public List<SimpleComboBox<String>> getAdvancedFilters() {
        return advancedFilters;
    }

    public void setCommitState(boolean commitState) {
        this.commitState = commitState;
    }

    public boolean isCommitState() {
        return commitState;
    }

    public void setShowColumnsMenu(boolean state) {
        showColumnsMenu = state;
    }

    public void setCreateFilterToolBar(boolean state) {
        createFilterToolBar = state;
    }

    public void setGrid(EditorGrid<BaseModelData> grid) {
        this.grid = grid;
    }

    public EditorGrid<BaseModelData> getGrid() {
        return grid;
    }

    public void setSaveItem(Button saveItem) {
        this.saveItem = saveItem;
    }

    public Button getSaveItem() {
        return saveItem;
    }

    public void setRemoveApproval(Button removeApproval) {
        this.removeApproval = removeApproval;
    }

    public Button getRemoveApproval() {
        return removeApproval;
    }

    /** Provides basic spreadsheet-like functionality. */
    private EditorGrid<BaseModelData> grid;

    /** Use store.add() to remember dynamically created records. */
    protected ListStore<BaseModelData> store;

    /** Flag for enforcing saves only on Save button press. **/
    private boolean commitState;
    private int pageSize = 50;
    private ModelType modelType;
    private ColumnConfigForm columnConfForm;
    private FilterComboForm filterComboForm;
    private UndoRedo undoredo;
    private Button columnsItem;
    private boolean showColumnsMenu = true;
    private FilterMenu filterMenu;
    private boolean createFilterToolBar = true;
    private ToggleButton columnEditItem;

    public List<String> filterComboIds = new ArrayList<String>();
    public List<String> columnConfigIds = new ArrayList<String>();

    protected List<CheckColumnConfig> checkBoxes = new ArrayList<CheckColumnConfig>();

    /** Use loader.load() to refresh with the list of records on the server. */
    protected PagingLoader<BasePagingLoadResult<BaseModelData>> loader;

    protected DynamicHttpProxy<BasePagingLoadResult<BaseModelData>> proxy;

    protected String rootURL;
    protected String initialArgs;
    protected boolean loadDataInitially = true;

    protected List<SimpleComboBox<String>> advancedFilters = new ArrayList<SimpleComboBox<String>>();

    protected SplitButton filterAction;
    private Button saveItem;
    protected Button cancelItem;
    protected Button undoItem;
    protected Button redoItem;
    protected Button viewItem;
    protected Button addItem;
    protected Button removeItem;
    private Button removeApproval;
    protected Dialog removeDialog;
    protected Button actionItem;
    protected ToolBar toolBar;
    protected ToolBar filterToolBar;
    protected PagingToolBar pagingToolBar;
    protected OptionsFilter optionsFilter;

    protected FilterItem filter;

    // Note: really should be grabing this list from the server, but it's static 
    // enough to hard code here.
    protected String[] semesters = new String[] { "12B", "12A", "11B", "11A", "10C", "10B", "10A", "09C", "09B",
            "09A", "08C", "08B", "08A", "07C", "07B", "07A", "06C", "06B", "06A", "05C", "05B", "05A", "04A" };

    protected String defaultDate;
}