Java tutorial
/******************************************************************************* * Copyright 2014 Celartem, Inc., dba LizardTech * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.lizardtech.expresszip.vaadin; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import org.vaadin.vol.Layer; import org.vaadin.vol.ProjectedLayer; import org.vaadin.vol.WebMapServiceLayer; import com.lizardtech.expresszip.model.ExpressZipLayer; import com.lizardtech.expresszip.model.Filter; import com.lizardtech.expresszip.model.Filter.AxisFilters; import com.lizardtech.expresszip.model.Filter.FilterObject; import com.lizardtech.expresszip.model.MapModel; import com.lizardtech.expresszip.ui.FindLayersView; import com.lizardtech.expresszip.vaadin.ExpressZipButton.Style; import com.vaadin.data.Property; import com.vaadin.data.Property.ValueChangeEvent; import com.vaadin.data.Property.ValueChangeListener; import com.vaadin.data.util.BeanItemContainer; import com.vaadin.event.Action; import com.vaadin.event.ShortcutAction.KeyCode; import com.vaadin.terminal.Sizeable; import com.vaadin.terminal.ThemeResource; import com.vaadin.ui.AbstractSelect.Filtering; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.CheckBox; import com.vaadin.ui.ComboBox; import com.vaadin.ui.CssLayout; import com.vaadin.ui.CustomComponent; import com.vaadin.ui.Embedded; import com.vaadin.ui.GridLayout; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.Panel; import com.vaadin.ui.Table; import com.vaadin.ui.TextField; import com.vaadin.ui.TreeTable; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.Window; import com.vaadin.ui.themes.BaseTheme; public class FindLayersViewComponent extends CustomComponent implements FindLayersView, Button.ClickListener, Action.Handler { private static final long serialVersionUID = 912537433588738669L; private static final String RENDER = "Render"; private static final String EXPORT = "Export"; private static final String BASEMAP = "Basemap"; private ExpressZipButton btnBack; private ExpressZipButton btnNext; private ExpressZipButton btnAddFilter; private TreeTable treeTable; private AxisSelected axisSelectedListener; private FilterListeners filterButtonListener; private HashMap<Button, FilterObject> hshFilterButtons; private Window wndAddFilter = null; private HorizontalLayout hznCriteria; private CssLayout cssLayers; private ComboBox basemapSelector; private CheckBox includeBasemap; private AxisFilters filteringOn; private Filter filter; private List<FindLayersViewListener> listeners; private MapModel mapModel; private TreeTable popupTable; private ValueChangeListener popupSelectionListener; // Actions for the context menu private static final Action ACTION_ZOOM = new Action("Zoom to"); private static final Action SELECT_ALL = new Action("Select All"); private static final Action SELECT_NONE = new Action("Clear selection"); private static final Action EXPORT_ALL = new Action("Export all"); private static final Action EXPORT_NONE = new Action("Export none"); private static final Action[] ACTIONS = new Action[] { ACTION_ZOOM, EXPORT_ALL, EXPORT_NONE, SELECT_ALL, SELECT_NONE }; public FindLayersViewComponent() { treeTable = new ExpressZipTreeTable(); popupTable = new ExpressZipTreeTable(); configureTable(treeTable); popupSelectionListener = new Property.ValueChangeListener() { private static final long serialVersionUID = 625365970493526725L; @Override public void valueChange(ValueChangeEvent event) { // the current popup selection Set<ExpressZipLayer> popupSelection = (Set<ExpressZipLayer>) event.getProperty().getValue(); // get the tree's current selection HashSet<ExpressZipLayer> treeSelection = new HashSet<ExpressZipLayer>( (Set<ExpressZipLayer>) treeTable.getValue()); // remove all items in common with popup treeSelection.removeAll(popupTable.getItemIds()); // set the treeTable selection to the union Set<ExpressZipLayer> unionSelection = new HashSet<ExpressZipLayer>(); unionSelection.addAll(popupSelection); unionSelection.addAll(treeSelection); treeTable.setValue(unionSelection); } }; popupTable.addListener(popupSelectionListener); treeTable.addListener(new Property.ValueChangeListener() { private static final long serialVersionUID = 6236114836521221107L; @Override public void valueChange(ValueChangeEvent event) { Set<ExpressZipLayer> highlightedLayers = (Set<ExpressZipLayer>) event.getProperty().getValue(); for (FindLayersViewListener listener : listeners) { listener.layerHighlightedEvent(highlightedLayers); } // reset selection of popup table popupTable.removeListener(popupSelectionListener); // intersection of treeTable's selection and popupTable items Set<ExpressZipLayer> popupSelection = new HashSet<ExpressZipLayer>(); popupSelection.addAll(highlightedLayers); popupSelection.retainAll(popupTable.getItemIds()); popupTable.setValue(popupSelection); popupTable.addListener(popupSelectionListener); } }); configureTable(popupTable); filter = new Filter(this); filterButtonListener = new FilterListeners(); axisSelectedListener = new AxisSelected(); listeners = new ArrayList<FindLayersViewListener>(); btnNext = new ExpressZipButton("Next", Style.STEP); btnBack = new ExpressZipButton("Back", Style.STEP); btnAddFilter = new ExpressZipButton("Add Filter", Style.ACTION); btnAddFilter.addStyleName("filter-flow"); hshFilterButtons = new HashMap<Button, FilterObject>(); cssLayers = new CssLayout(); basemapSelector = new ComboBox(); basemapSelector.setWidth(100.0f, UNITS_PERCENTAGE); basemapSelector.setTextInputAllowed(false); basemapSelector.setImmediate(true); basemapSelector.setNullSelectionAllowed(false); basemapSelector.addListener(new Property.ValueChangeListener() { private static final long serialVersionUID = -7358667131762099215L; @Override public void valueChange(ValueChangeEvent event) { ProjectedLayer l = (ProjectedLayer) basemapSelector.getValue(); boolean enableCheckbox = false; if (l instanceof WebMapServiceLayer) { for (ExpressZipLayer local : mapModel.getLocalBaseLayers()) { if (l.toString().equals(local.getName())) { enableCheckbox = true; break; } } } includeBasemap.setEnabled(enableCheckbox); if (!enableCheckbox) { includeBasemap.setValue(false); } if (mapModel.getBaseLayerTerms(l) != null && !mapModel.getBaseLayerTermsAccepted(l)) { final Window modal = new Window("Terms of Use"); modal.setModal(true); modal.setClosable(false); modal.setResizable(false); modal.getContent().setSizeUndefined(); // trick to size around content Button bOK = new ExpressZipButton("OK", Style.ACTION, new ClickListener() { private static final long serialVersionUID = -2872178665349848542L; @Override public void buttonClick(ClickEvent event) { ProjectedLayer l = (ProjectedLayer) basemapSelector.getValue(); mapModel.setBaseLayerTermsAccepted(l); for (FindLayersViewListener listener : listeners) listener.baseMapChanged(l); ((ExpressZipWindow) getApplication().getMainWindow()).removeWindow(modal); } }); Button bCancel = new ExpressZipButton("Cancel", Style.ACTION, new ClickListener() { private static final long serialVersionUID = -3044064554876422836L; @Override public void buttonClick(ClickEvent event) { basemapSelector.select(mapModel.getBaseLayers().get(0)); ((ExpressZipWindow) getApplication().getMainWindow()).removeWindow(modal); } }); HorizontalLayout buttons = new HorizontalLayout(); buttons.setSpacing(true); buttons.addComponent(bOK); buttons.addComponent(bCancel); Label termsText = new Label(mapModel.getBaseLayerTerms(l)); termsText.setContentMode(Label.CONTENT_XHTML); VerticalLayout vlay = new VerticalLayout(); vlay.addComponent(termsText); vlay.addComponent(buttons); vlay.setComponentAlignment(buttons, Alignment.MIDDLE_RIGHT); vlay.setWidth(400, UNITS_PIXELS); modal.getContent().addComponent(vlay); ((ExpressZipWindow) getApplication().getMainWindow()).addWindow(modal); } else { for (FindLayersViewListener listener : listeners) listener.baseMapChanged(l); } } }); includeBasemap = new CheckBox(); includeBasemap.setDescription("Include this basemap in the exported image."); includeBasemap.setWidth(64f, UNITS_PIXELS); HorizontalLayout basemapLayout = new HorizontalLayout(); basemapLayout.setWidth(100f, UNITS_PERCENTAGE); VerticalLayout layout = new VerticalLayout(); layout.setMargin(true); layout.setSizeFull(); setSizeFull(); Label step = new Label("Step 1: Select Layers"); step.addStyleName("step"); layout.addComponent(step); layout.addComponent(treeTable); layout.setSpacing(true); treeTable.setSizeFull(); layout.setExpandRatio(treeTable, 1f); layout.addComponent(new Panel(BASEMAP, basemapLayout)); basemapLayout.addComponent(basemapSelector); basemapLayout.setExpandRatio(basemapSelector, 1f); basemapLayout.addComponent(includeBasemap); layout.addComponent(cssLayers); cssLayers.addComponent(btnAddFilter); HorizontalLayout backSubmitLayout = new HorizontalLayout(); backSubmitLayout.setWidth("100%"); backSubmitLayout.addComponent(btnBack); backSubmitLayout.addComponent(btnNext); backSubmitLayout.setComponentAlignment(btnBack, Alignment.BOTTOM_LEFT); backSubmitLayout.setComponentAlignment(btnNext, Alignment.BOTTOM_RIGHT); VerticalLayout navLayout = new VerticalLayout(); navLayout.addComponent(backSubmitLayout); navLayout.setSpacing(true); ThemeResource banner = new ThemeResource("img/ProgressBar1.png"); navLayout.addComponent(new Embedded(null, banner)); layout.addComponent(navLayout); layout.setComponentAlignment(navLayout, Alignment.BOTTOM_CENTER); btnNext.addListener(this); btnNext.setEnabled(false); btnBack.setEnabled(false); // always disabled btnAddFilter.addListener(this); layout.addStyleName("findlayers"); setCompositionRoot(layout); } public TreeTable getPopupTable() { return popupTable; } // FindLayersView implementations public void populateLayers(ExpressZipLayer rootLayer) { if (rootLayer != null) { addChildren(rootLayer); } } @Override public void addListener(FindLayersViewListener listener) { listeners.add(listener); } @Override public void buttonClick(ClickEvent event) { if (event.getButton() == btnNext) { // For 'Next' being pressed String localBaseLayerName = null; if (includeBasemap.booleanValue()) { ProjectedLayer basemap = (ProjectedLayer) basemapSelector.getValue(); if (basemap instanceof WebMapServiceLayer) { localBaseLayerName = ((WebMapServiceLayer) basemap).getDisplayName(); } } for (FindLayersViewListener l : listeners) { l.setIncludedLocalBaseMap(localBaseLayerName); } ((ExpressZipWindow) getApplication().getMainWindow()).advanceToNext(); } else if (event.getButton() == btnAddFilter) { // For 'Add Filter' // This window is already created and setup, add it and return ExpressZipWindow parent = (ExpressZipWindow) getApplication().getMainWindow(); if (wndAddFilter != null) { parent.addWindow(wndAddFilter); return; } wndAddFilter = new Window("Add Filter"); wndAddFilter.setModal(true); int height = 250; int width = 400; wndAddFilter.setWidth(width + "px"); wndAddFilter.setHeight(height + "px"); // Center the window based on browser width/height wndAddFilter.setPositionX((parent.getBrowserWindowWidth() - width) / 2); wndAddFilter.setPositionY((parent.getBrowserWindowHeight() - height) / 2); setupAddFilterWindow(wndAddFilter); parent.addWindow(wndAddFilter); } } private void setupAddFilterWindow(Window window) { // General variables // Layouts GridLayout mainLayout = new GridLayout(1, 3); HorizontalLayout axisLayout = new HorizontalLayout(); HorizontalLayout criteriaLayout = new HorizontalLayout(); HorizontalLayout buttonLayout = new HorizontalLayout(); hznCriteria = criteriaLayout; // Buttons ExpressZipButton btnAdd = new ExpressZipButton("Add", Style.ACTION); btnAdd.setClickShortcut(KeyCode.ENTER); btnAdd.addStyleName("primary"); ExpressZipButton btnCancel = new ExpressZipButton("Cancel", Style.ACTION); // Fields ComboBox cmbAxis = new ComboBox(); cmbAxis.setTextInputAllowed(false); cmbAxis.setNullSelectionAllowed(false); // Labels Label lblAxis = new Label("Axis"); btnAdd.addListener(filterButtonListener); btnCancel.addListener(filterButtonListener); for (Filter.AxisFilters f : Filter.axisArray) { cmbAxis.addItem(filter.getNameOfFilter(f)); } cmbAxis.setImmediate(true); cmbAxis.addListener(axisSelectedListener); cmbAxis.setValue(filter.getNameOfFilter(Filter.axisArray[0])); mainLayout.addComponent(axisLayout, 0, 0); mainLayout.addComponent(criteriaLayout, 0, 1); mainLayout.addComponent(buttonLayout, 0, 2); mainLayout.setSpacing(true); axisLayout.setSpacing(true); axisLayout.addComponent(lblAxis); axisLayout.addComponent(cmbAxis); axisLayout.setExpandRatio(lblAxis, .2f); axisLayout.setExpandRatio(cmbAxis, .8f); axisLayout.setComponentAlignment(lblAxis, Alignment.MIDDLE_LEFT); axisLayout.setComponentAlignment(cmbAxis, Alignment.MIDDLE_LEFT); axisLayout.setSizeFull(); criteriaLayout.setSizeFull(); buttonLayout.setSpacing(true); buttonLayout.addComponent(btnAdd); buttonLayout.addComponent(btnCancel); buttonLayout.setComponentAlignment(btnAdd, Alignment.BOTTOM_RIGHT); buttonLayout.setComponentAlignment(btnCancel, Alignment.BOTTOM_RIGHT); buttonLayout.setExpandRatio(btnAdd, 1f); buttonLayout.setExpandRatio(btnCancel, 0f); buttonLayout.setSizeFull(); mainLayout.setRowExpandRatio(0, 1f); mainLayout.setRowExpandRatio(1, 1f); mainLayout.setRowExpandRatio(2, 1f); mainLayout.setSizeFull(); window.addComponent(mainLayout); window.getContent().setSizeFull(); } @Override public void setupMap(MapModel model) { this.mapModel = model; List<ProjectedLayer> baseLayers = model.getBaseLayers(); BeanItemContainer<Layer> container = new BeanItemContainer<Layer>(Layer.class, baseLayers); basemapSelector.setContainerDataSource(container); if (baseLayers.size() > 0) basemapSelector.select(baseLayers.get(0)); refreshMap(); } private void refreshMap() { Set<ExpressZipLayer> selected = (Set<ExpressZipLayer>) treeTable.getValue(); treeTable.removeAllItems(); populateLayers(mapModel.getRootRasterLayer()); treeTable.setValue(selected); for (ExpressZipLayer layer : mapModel.getChosenLayers()) { resetParentCheckBoxes(layer, true); } } public class FilterListeners implements Button.ClickListener { private static final long serialVersionUID = -7059229918698817362L; FilterObject fo = null; Button btnFilter; @Override public void buttonClick(ClickEvent event) { String buttCaption = event.getButton().getCaption(); if (buttCaption != null && buttCaption.compareTo("Add") == 0) { Label lblFilter = null; switch (filteringOn) { case LAYER_NAME: lblFilter = new Label("Name:" + axisSelectedListener.getTxtString()); fo = new FilterObject(AxisFilters.LAYER_NAME, axisSelectedListener.getCmbNamOperation(), axisSelectedListener.getTxtString()); break; case KEYWORD: lblFilter = new Label("Keywd:" + axisSelectedListener.getTxtKeyword()); fo = new FilterObject(AxisFilters.KEYWORD, axisSelectedListener.getCmbKeyOperation(), axisSelectedListener.getTxtKeyword()); break; case LOCATION: lblFilter = new Label("Loc:" + axisSelectedListener.getTxtLocation()); fo = new FilterObject(AxisFilters.LOCATION, axisSelectedListener.getCmbLocOp(), axisSelectedListener.getTxtLocation()); break; case SRS: lblFilter = new Label("SRS:" + axisSelectedListener.getTxtEPSG()); fo = new FilterObject(AxisFilters.SRS, axisSelectedListener.getTxtEPSG()); break; } filter.addFilter(fo); btnFilter = new Button(); btnFilter.addListener(filterButtonListener); btnFilter.setIcon(new ThemeResource("img/LayerLayerRendered16px.png")); btnFilter.setStyleName(BaseTheme.BUTTON_LINK); HorizontalLayout filterdiv = new HorizontalLayout(); filterdiv.addStyleName("filter-flow"); filterdiv.addComponent(btnFilter); filterdiv.setSizeUndefined(); lblFilter.setWidth(Sizeable.SIZE_UNDEFINED, 0); filterdiv.addComponent(lblFilter); cssLayers.addComponent(filterdiv); hshFilterButtons.put(btnFilter, fo); ((ExpressZipWindow) getApplication().getMainWindow()).removeWindow(wndAddFilter); // close the window applyFilters(); } else if (buttCaption != null && buttCaption.compareTo("Cancel") == 0) { ((ExpressZipWindow) getApplication().getMainWindow()).removeWindow(wndAddFilter); } else { // This is for clicking on filters currently in the app filter.delFilter(hshFilterButtons.get(event.getButton()), mapModel); hshFilterButtons.remove(event.getButton()); cssLayers.removeComponent(event.getButton().getParent()); applyFilters(); } } } public class AxisSelected implements Property.ValueChangeListener { private static final long serialVersionUID = -521006477002348622L; // Layer name private ComboBox cmbNameOperation = null; private TextField txtString = null; // Keyword private ComboBox cmbKeyOperation = null; private TextField txtKeyword = null; // Location private ComboBox cmbLocOp = null; private TextField txtLocation = null; // SRS private TextField txtEPSG = null; @Override public void valueChange(ValueChangeEvent event) { switch (filter.getFilterByName((String) event.getProperty().getValue())) { case LAYER_NAME: filteringOn = AxisFilters.LAYER_NAME; hznCriteria.removeAllComponents(); if (cmbNameOperation == null && txtString == null) { cmbNameOperation = new ComboBox(); cmbNameOperation.setTextInputAllowed(false); cmbNameOperation.setNullSelectionAllowed(false); txtString = new TextField(); cmbNameOperation.addItem("contains"); cmbNameOperation.addItem("starts with"); cmbNameOperation.addItem("ends with"); cmbNameOperation.addItem("does not contain"); cmbNameOperation.select("contains"); // default txtString.setWidth("100%"); txtString.setInputPrompt("e.g. Columbia"); } hznCriteria.setSpacing(true); hznCriteria.addComponent(cmbNameOperation); hznCriteria.addComponent(txtString); hznCriteria.setExpandRatio(txtString, 1f); break; case KEYWORD: filteringOn = AxisFilters.KEYWORD; hznCriteria.removeAllComponents(); if (cmbKeyOperation == null && txtKeyword == null) { cmbKeyOperation = new ComboBox(); cmbKeyOperation.setTextInputAllowed(false); cmbKeyOperation.setNullSelectionAllowed(false); txtKeyword = new TextField(); cmbKeyOperation.addItem("contains"); cmbKeyOperation.addItem("starts with"); cmbKeyOperation.addItem("ends with"); cmbKeyOperation.addItem("does not contain"); cmbKeyOperation.select("contains"); // default txtKeyword.setWidth("100%"); txtKeyword.setInputPrompt("e.g. Paris"); } hznCriteria.setSpacing(true); hznCriteria.addComponent(cmbKeyOperation); hznCriteria.addComponent(txtKeyword); hznCriteria.setExpandRatio(txtKeyword, 1f); break; case LOCATION: filteringOn = AxisFilters.LOCATION; hznCriteria.removeAllComponents(); if (cmbLocOp == null && txtLocation == null) { txtLocation = new TextField(); cmbLocOp = new ComboBox(); cmbLocOp.setTextInputAllowed(false); cmbLocOp.setNullSelectionAllowed(false); cmbLocOp.addItem("inside"); cmbLocOp.addItem("outside"); cmbLocOp.select("inside"); // default txtLocation.setInputPrompt("e.g. Washington"); txtLocation.setWidth("100%"); } hznCriteria.setSpacing(true); hznCriteria.addComponent(cmbLocOp); hznCriteria.addComponent(txtLocation); hznCriteria.setExpandRatio(txtLocation, 1f); break; case SRS: filteringOn = AxisFilters.SRS; hznCriteria.removeAllComponents(); if (txtEPSG == null) { txtEPSG = new TextField(); } hznCriteria.addComponent(txtEPSG); hznCriteria.setComponentAlignment(txtEPSG, Alignment.TOP_CENTER); break; } } public String getCmbNamOperation() { return cmbNameOperation.getValue().toString(); } public String getTxtString() { return txtString.getValue().toString(); } public String getCmbKeyOperation() { return cmbKeyOperation.getValue().toString(); } public String getTxtKeyword() { return txtKeyword.getValue().toString(); } public String getTxtLocation() { return txtLocation.getValue().toString(); } public String getTxtEPSG() { return txtEPSG.getValue().toString(); } public String getCmbLocOp() { return cmbLocOp.getValue().toString(); } } public void applyFilters() { filter.filterLayers(mapModel); refreshMap(); for (FindLayersViewListener listener : listeners) { listener.filtersChangedEvent(); } } /* * Returns the set of available actions */ @Override public Action[] getActions(Object target, Object sender) { return ACTIONS; } /* * Handle actions */ @Override public void handleAction(Action action, Object sender, Object layer) { if (sender instanceof Table) { Table table = (Table) sender; if (action == ACTION_ZOOM) { table.select(layer); for (FindLayersViewListener listener : listeners) { listener.zoomToLayersEvent((Set<ExpressZipLayer>) table.getValue()); } } else if (action == SELECT_ALL || action == SELECT_NONE) { for (Object itemId : table.getItemIds()) { if (action == SELECT_ALL) table.select(itemId); else table.unselect(itemId); } } else if (action == EXPORT_ALL || action == EXPORT_NONE) { for (Object itemId : table.getItemIds()) { IndeterminateCheckBox cb = (IndeterminateCheckBox) table.getContainerProperty(itemId, EXPORT) .getValue(); if ((cb.isOn() && action == EXPORT_NONE) || (!cb.isOn() && action == EXPORT_ALL)) { cb.click(); } } } } } public void configureTable(Table table) { table.addContainerProperty(ExpressZipTreeTable.LAYER, ExpressZipLayer.class, null); table.addContainerProperty(EXPORT, IndeterminateCheckBox.class, null); table.addContainerProperty(RENDER, RenderButton.class, null); table.setColumnHeaders(new String[] { ExpressZipTreeTable.LAYER, "", "" }); table.setColumnExpandRatio(ExpressZipTreeTable.LAYER, 1); // Add actions (context menu) table.addActionHandler(this); } private void addChildren(ExpressZipLayer parent) { for (final ExpressZipLayer child : parent.getChildren()) { if (child.filterAllows()) { // Don't show basemaps in the list of available layers. boolean skip_localBaseMap = false; for (Object itemId : basemapSelector.getItemIds()) { if (child.getName().equalsIgnoreCase(itemId.toString())) { skip_localBaseMap = true; break; } } if (skip_localBaseMap) continue; addLayerItem(child, treeTable); treeTable.setChildrenAllowed(parent, true); treeTable.setChildrenAllowed(child, false); if (parent.filterAllows()) { treeTable.setParent(child, parent); // set indeterminate on parent if appropriate if (child.isChosen() && !parent.isChosen()) { Property export = treeTable.getContainerProperty(parent, EXPORT); if (export != null) { IndeterminateCheckBox cb = (IndeterminateCheckBox) export.getValue(); cb.setState(IndeterminateCheckBox.INDETERMINATE); } } } else { treeTable.setParent(child, mapModel.getRootRasterLayer()); } } addChildren(child); } } public void addLayerItem(final ExpressZipLayer layer, final TreeTable table) { final IndeterminateCheckBox exportCheckBox = new IndeterminateCheckBox(); if (layer.isChosen()) { exportCheckBox.setState(IndeterminateCheckBox.ON); } final RenderButton renderLayerButton = new RenderButton(layer.isRendered()); table.addItem(new Object[] { layer, exportCheckBox, renderLayerButton }, layer); table.setChildrenAllowed(layer, false); exportCheckBox.addListener(new Button.ClickListener() { private static final long serialVersionUID = -1903183530883807821L; @Override public void buttonClick(ClickEvent event) { if (exportCheckBox.isOff() || exportCheckBox.isIndeterminate()) { // choosing a parent clears all children clearChildCheckBoxes(layer); exportCheckBox.setState(IndeterminateCheckBox.ON); } else if (hasChildrenChosen(layer)) { // un-choosing a parent will keep the choose-state of any children exportCheckBox.setState(IndeterminateCheckBox.INDETERMINATE); } else { exportCheckBox.setState(IndeterminateCheckBox.OFF); } // synchronize popup and treetable states int exportState = exportCheckBox.getState(); if (treeTable.containsId(layer)) ((IndeterminateCheckBox) treeTable.getContainerProperty(layer, EXPORT).getValue()) .setState(exportState); if (popupTable.containsId(layer)) ((IndeterminateCheckBox) popupTable.getContainerProperty(layer, EXPORT).getValue()) .setState(exportState); notifyLayerExport(layer, exportState == IndeterminateCheckBox.ON); resetParentCheckBoxes(layer, exportState == IndeterminateCheckBox.ON); btnNext.setEnabled(!mapModel.getChosenLayers().isEmpty()); } }); renderLayerButton.addListener(new ClickListener() { private static final long serialVersionUID = -462470414717524928L; @Override public void buttonClick(ClickEvent event) { layer.setRendered(!layer.isRendered()); // toggle rendered state of layer // toggle the treeTable's item as necessary RenderButton treeRenderButton = (RenderButton) treeTable.getContainerProperty(layer, RENDER) .getValue(); treeRenderButton.setState(layer.isRendered()); // toggle the popupTable's item as necessary if (popupTable.containsId(layer)) { RenderButton popupRenderButton = (RenderButton) popupTable.getContainerProperty(layer, RENDER) .getValue(); popupRenderButton.setState(layer.isRendered()); } for (FindLayersViewListener listener : listeners) { listener.renderLayerEvent(layer, layer.isRendered()); } } }); } protected void resetParentCheckBoxes(ExpressZipLayer layer, boolean export) { ExpressZipLayer parent = layer.getParent(); if (parent != null) { IndeterminateCheckBox treeCB = null; IndeterminateCheckBox popupCB = null; if (treeTable.containsId(parent)) treeCB = (IndeterminateCheckBox) treeTable.getContainerProperty(parent, EXPORT).getValue(); if (popupTable.containsId(parent)) popupCB = (IndeterminateCheckBox) popupTable.getContainerProperty(parent, EXPORT).getValue(); if (export) { if (treeCB != null && treeCB.isOff()) treeCB.setState(IndeterminateCheckBox.INDETERMINATE); if (popupCB != null && popupCB.isOff()) popupCB.setState(IndeterminateCheckBox.INDETERMINATE); } else if (!hasChildrenChosen(parent)) { if (treeCB != null && treeCB.isIndeterminate()) treeCB.setState(IndeterminateCheckBox.OFF); if (popupCB != null && popupCB.isIndeterminate()) popupCB.setState(IndeterminateCheckBox.OFF); } resetParentCheckBoxes(parent, export); } } public void showRenderedLayers(List<ExpressZipLayer> renderedLayers) { for (ExpressZipLayer layer : renderedLayers) { if (treeTable.getItem(layer) != null) { RenderButton cb = (RenderButton) treeTable.getContainerProperty(layer, RENDER).getValue(); cb.setState(layer.isRendered()); } } } private void clearChildCheckBoxes(ExpressZipLayer parent) { for (ExpressZipLayer child : parent.getChildren()) { if (treeTable.containsId(child)) ((IndeterminateCheckBox) treeTable.getContainerProperty(child, EXPORT).getValue()) .setState(IndeterminateCheckBox.OFF); if (popupTable.containsId(child)) ((IndeterminateCheckBox) popupTable.getContainerProperty(child, EXPORT).getValue()) .setState(IndeterminateCheckBox.OFF); notifyLayerExport(child, false); clearChildCheckBoxes(child); } } private boolean hasChildrenChosen(ExpressZipLayer layer) { for (ExpressZipLayer l : layer.getChildren()) { if (l.isChosen() || hasChildrenChosen(l)) return true; } return false; } private void notifyLayerExport(ExpressZipLayer layer, boolean export) { boolean highlighted = treeTable.isSelected(layer); for (FindLayersViewListener listener : listeners) { listener.chooseLayerForExportEvent(layer, export, highlighted); } } public class RenderButton extends Button { private static final long serialVersionUID = 2390825473656198993L; public RenderButton(boolean b) { super(); setImmediate(true); setStyleName(BaseTheme.BUTTON_LINK); setState(b); setDescription("Preview the layer"); } public void setState(boolean rendered) { if (rendered) { setIcon(new ThemeResource("img/LayerLayerRendered16px.png")); } else { setIcon(new ThemeResource("img/LayerRenderLayer16px.png")); } } } public class IndeterminateCheckBox extends Button { private static final long serialVersionUID = 1178313040045502351L; public static final int OFF = 1; public static final int ON = 2; public static final int INDETERMINATE = 3; private int state; public IndeterminateCheckBox() { super(); setImmediate(true); setStyleName(BaseTheme.BUTTON_LINK); setState(OFF); setDescription("Select the layer for export"); } public boolean isOff() { return (state == OFF); } public boolean isOn() { return (state == ON); } public boolean isIndeterminate() { return (state == INDETERMINATE); } public int getState() { return state; } public void setState(int state) { this.state = state; switch (state) { case OFF: setIcon(new ThemeResource("img/LayerChooseLayer16px.png")); break; case ON: setIcon(new ThemeResource("img/LayerChosen16px.png")); break; case INDETERMINATE: setIcon(new ThemeResource("img/LayerChildrenChosen16px.png")); break; default: break; } } public void toggle() { if (state == INDETERMINATE || state == OFF) { setState(ON); } else { setState(OFF); } } } }