0001: /*
0002: * uDig - User Friendly Desktop Internet GIS client
0003: * http://udig.refractions.net
0004: * (C) 2004, Refractions Research Inc.
0005: *
0006: * This library is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU Lesser General Public
0008: * License as published by the Free Software Foundation;
0009: * version 2.1 of the License.
0010: *
0011: * This library is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * Lesser General Public License for more details.
0015: *
0016: */
0017: package net.refractions.udig.tool.select;
0018:
0019: import java.io.IOException;
0020: import java.util.ArrayList;
0021: import java.util.Collections;
0022: import java.util.List;
0023: import java.util.Set;
0024: import java.util.concurrent.CopyOnWriteArraySet;
0025:
0026: import net.refractions.udig.core.IBlockingProvider;
0027: import net.refractions.udig.core.IProvider;
0028: import net.refractions.udig.core.StaticBlockingProvider;
0029: import net.refractions.udig.project.EditManagerEvent;
0030: import net.refractions.udig.project.IEditManagerListener;
0031: import net.refractions.udig.project.ILayer;
0032: import net.refractions.udig.project.ILayerListener;
0033: import net.refractions.udig.project.IMapCompositionListener;
0034: import net.refractions.udig.project.LayerEvent;
0035: import net.refractions.udig.project.MapCompositionEvent;
0036: import net.refractions.udig.project.command.AbstractCommand;
0037: import net.refractions.udig.project.command.CompositeCommand;
0038: import net.refractions.udig.project.command.UndoableCommand;
0039: import net.refractions.udig.project.command.UndoableComposite;
0040: import net.refractions.udig.project.command.UndoableMapCommand;
0041: import net.refractions.udig.project.command.factory.EditCommandFactory;
0042: import net.refractions.udig.project.command.factory.SelectionCommandFactory;
0043: import net.refractions.udig.project.command.provider.FIDFeatureProvider;
0044: import net.refractions.udig.project.internal.Layer;
0045: import net.refractions.udig.project.internal.Map;
0046: import net.refractions.udig.project.internal.commands.edit.DeleteFeatureCommand;
0047: import net.refractions.udig.project.internal.commands.edit.DeleteManyFeaturesCommand;
0048: import net.refractions.udig.project.ui.ApplicationGIS;
0049: import net.refractions.udig.project.ui.IUDIGView;
0050: import net.refractions.udig.project.ui.internal.AdaptingFilter;
0051: import net.refractions.udig.project.ui.internal.MapEditor;
0052: import net.refractions.udig.project.ui.internal.tool.display.ToolManager;
0053: import net.refractions.udig.project.ui.tool.IToolContext;
0054: import net.refractions.udig.project.ui.tool.ToolsConstants;
0055: import net.refractions.udig.tool.select.internal.Messages;
0056: import net.refractions.udig.tool.select.internal.ZoomSelection;
0057: import net.refractions.udig.ui.FeatureTableControl;
0058: import net.refractions.udig.ui.IFeatureTableLoadingListener;
0059: import net.refractions.udig.ui.PlatformGIS;
0060: import net.refractions.udig.ui.ProgressManager;
0061:
0062: import org.eclipse.core.runtime.IProgressMonitor;
0063: import org.eclipse.core.runtime.ISafeRunnable;
0064: import org.eclipse.jface.action.Action;
0065: import org.eclipse.jface.action.GroupMarker;
0066: import org.eclipse.jface.action.IAction;
0067: import org.eclipse.jface.action.IMenuListener;
0068: import org.eclipse.jface.action.IMenuManager;
0069: import org.eclipse.jface.action.IStatusLineManager;
0070: import org.eclipse.jface.action.IToolBarManager;
0071: import org.eclipse.jface.action.MenuManager;
0072: import org.eclipse.jface.resource.ImageDescriptor;
0073: import org.eclipse.jface.viewers.ICellEditorListener;
0074: import org.eclipse.jface.viewers.ISelection;
0075: import org.eclipse.jface.viewers.ISelectionChangedListener;
0076: import org.eclipse.jface.viewers.ISelectionProvider;
0077: import org.eclipse.jface.viewers.IStructuredSelection;
0078: import org.eclipse.jface.viewers.SelectionChangedEvent;
0079: import org.eclipse.jface.viewers.StructuredSelection;
0080: import org.eclipse.swt.SWT;
0081: import org.eclipse.swt.graphics.Color;
0082: import org.eclipse.swt.layout.FormAttachment;
0083: import org.eclipse.swt.layout.FormData;
0084: import org.eclipse.swt.layout.FormLayout;
0085: import org.eclipse.swt.widgets.Button;
0086: import org.eclipse.swt.widgets.Combo;
0087: import org.eclipse.swt.widgets.Composite;
0088: import org.eclipse.swt.widgets.Display;
0089: import org.eclipse.swt.widgets.Event;
0090: import org.eclipse.swt.widgets.Item;
0091: import org.eclipse.swt.widgets.Label;
0092: import org.eclipse.swt.widgets.Listener;
0093: import org.eclipse.swt.widgets.TableItem;
0094: import org.eclipse.swt.widgets.Text;
0095: import org.eclipse.ui.IKeyBindingService;
0096: import org.eclipse.ui.IPartListener2;
0097: import org.eclipse.ui.ISelectionListener;
0098: import org.eclipse.ui.IWorkbenchActionConstants;
0099: import org.eclipse.ui.IWorkbenchPage;
0100: import org.eclipse.ui.IWorkbenchPart;
0101: import org.eclipse.ui.IWorkbenchPartReference;
0102: import org.eclipse.ui.PlatformUI;
0103: import org.eclipse.ui.actions.ActionFactory;
0104: import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
0105: import org.eclipse.ui.part.ViewPart;
0106: import org.eclipse.ui.plugin.AbstractUIPlugin;
0107: import org.geotools.data.DefaultQuery;
0108: import org.geotools.data.FeatureEvent;
0109: import org.geotools.data.FeatureSource;
0110: import org.geotools.data.FeatureStore;
0111: import org.geotools.data.Query;
0112: import org.geotools.feature.AttributeType;
0113: import org.geotools.feature.Feature;
0114: import org.geotools.feature.FeatureCollection;
0115: import org.geotools.feature.FeatureType;
0116: import org.geotools.feature.GeometryAttributeType;
0117: import org.geotools.filter.FidFilter;
0118: import org.geotools.filter.Filter;
0119: import org.geotools.filter.FilterFactory;
0120: import org.geotools.filter.FilterFactoryFinder;
0121: import org.geotools.filter.FilterType;
0122: import org.geotools.filter.GeometryFilter;
0123: import org.geotools.filter.IllegalFilterException;
0124:
0125: import com.vividsolutions.jts.geom.Envelope;
0126:
0127: /**
0128: * Table view for selected Layer, may choose
0129: * to display FeatureSource with out supporting selection
0130: * in the future.
0131: * </p>
0132: * Currently this is a playground using the FeatureTable
0133: * to look at a FeautreSource, syncing up the slection
0134: * with the Layer's filter will come next.
0135: * </p>
0136: * <p>
0137: * Long term responsibilities include:
0138: * <ul>
0139: * <li>Access to a Filter editor for selection specification
0140: * <li>Allowing in view edits
0141: * <li>Real random access for shapefile allowing the table view
0142: * everyone expects (tm)
0143: * </ul>
0144: * @author Jody Garnett, Refractions Research, Inc.
0145: * @since 0.6
0146: */
0147: public class TableView extends ViewPart implements ISelectionProvider,
0148: IUDIGView {
0149:
0150: private static final String INITIAL_TEXT = Messages.TableView_search;
0151:
0152: protected static final String ANY = Messages.TableView_search_any;
0153:
0154: /** Used to show the current feature source */
0155: FeatureTableControl table;
0156:
0157: /** Current editor */
0158: MapEditor currentEditor;
0159:
0160: /** Current layer under study */
0161: Layer layer;
0162:
0163: /** Used to display status messages when no content is available */
0164: Text text;
0165:
0166: /** Toolbar entry used to turn on selection mode */
0167: private IAction select;
0168:
0169: private ISelectionListener workbenchSelectionListener;
0170:
0171: /**
0172: * page that the part listener and the workbenchSelectionListener are listening to.
0173: */
0174: private IWorkbenchPage page;
0175:
0176: /**
0177: * Listener that deactivates/reactivates view when it is hidden/shown
0178: */
0179: private IPartListener2 activePartListener;
0180:
0181: private MenuManager contextMenu;
0182:
0183: /**
0184: * Indicates whether the view is visible and therefore is active
0185: */
0186: private volatile boolean active = false;
0187:
0188: /**
0189: * Indicates that the features in the view need to be reloaded when the view is visible again.
0190: */
0191: protected volatile boolean reloadNeeded = false;
0192:
0193: /**
0194: * Indicates that the selection filter has changed while inactive
0195: */
0196: protected volatile boolean filterChange = false;
0197:
0198: /**
0199: * Indicates that the a feature is being updated by the table view so
0200: * it is not necessary to load the change indicated by the feature event.
0201: */
0202: protected volatile boolean editing = false;
0203:
0204: /**
0205: * Indicates that the selection of the table is being updated by the view because
0206: * the filter has been updated on the layer.
0207: */
0208: private volatile boolean updatingSelection = false;
0209:
0210: /**
0211: * Indicates that the view is updating the layer's filter because the selection on the table has changed.
0212: */
0213: protected volatile boolean updatingLayerFilter;
0214:
0215: private PromoteSelectionAction promoteSelection;
0216:
0217: private Label featuresSelected;
0218:
0219: private Combo attributeCombo;
0220:
0221: private IAction zoom;
0222: private IAction deleteAction;
0223:
0224: private Button selectAllCheck;
0225:
0226: /**
0227: * Construct <code>SelectView</code>.
0228: * <p>
0229: * Don't do setup here - there is an init method you can override that has
0230: * access to configuration and stuff.
0231: * </p>
0232: */
0233: public TableView() {
0234: super ();
0235: }
0236:
0237: @Override
0238: public void createPartControl(Composite parent) {
0239: active = true;
0240:
0241: featuresSelected = new Label(parent, SWT.NONE);
0242: featuresSelected
0243: .setText(Messages.TableView_featureSelected + 0);
0244:
0245: attributeCombo = new Combo(parent, SWT.DROP_DOWN
0246: | SWT.READ_ONLY);
0247: attributeCombo.setItems(new String[] { ANY });
0248: attributeCombo.select(0);
0249: attributeCombo.setEnabled(false);
0250:
0251: text = new Text(parent, SWT.BORDER);
0252: text.setEnabled(false);
0253:
0254: text.setText(INITIAL_TEXT);
0255: final Color systemColor = text.getDisplay().getSystemColor(
0256: SWT.COLOR_DARK_GRAY);
0257: text.setForeground(systemColor);
0258: text.setEditable(true);
0259: text.addListener(SWT.MouseDown, new Listener() {
0260: public void handleEvent(Event e) {
0261: if (text.getForeground().equals(systemColor)) {
0262: text.setForeground(e.display
0263: .getSystemColor(SWT.COLOR_BLACK));
0264: text.setText(""); //$NON-NLS-1$
0265: }
0266: ApplicationGIS.getToolManager().unregisterActions(
0267: TableView.this );
0268: }
0269: });
0270: text.addListener(SWT.DefaultSelection, new Listener() {
0271: public void handleEvent(Event e) {
0272: if (text.getText().trim().length() == 0) {
0273: text.setText(INITIAL_TEXT);
0274: text.setForeground(systemColor);
0275: }
0276:
0277: String[] attsToSearch;
0278: String item = attributeCombo.getItem(attributeCombo
0279: .getSelectionIndex());
0280: if (item.equals(ANY)) {
0281: attsToSearch = FeatureTableControl.ALL;
0282: } else {
0283: attsToSearch = new String[] { item };
0284: }
0285: boolean selectAll = selectAllCheck.getSelection();
0286: table.select(text.getText().trim(), attsToSearch,
0287: selectAll);
0288: ApplicationGIS.getToolManager().unregisterActions(
0289: TableView.this );
0290: }
0291: });
0292: IProvider<IProgressMonitor> provider = new IProvider<IProgressMonitor>() {
0293:
0294: public IProgressMonitor get() {
0295: IStatusLineManager statusLineManager = getViewSite()
0296: .getActionBars().getStatusLineManager();
0297: statusLineManager.setCancelEnabled(true);
0298: return statusLineManager.getProgressMonitor();
0299: }
0300:
0301: };
0302: selectAllCheck = new Button(parent, SWT.CHECK);
0303: selectAllCheck.setText(Messages.TableView_allCheckText);
0304: selectAllCheck.setToolTipText(Messages.TableView_allToolTip);
0305: selectAllCheck.setEnabled(false);
0306:
0307: table = new FeatureTableControl(provider);
0308: table.createTableControl(parent);
0309: table.addLoadingListener(new IFeatureTableLoadingListener() {
0310:
0311: public void loadingStarted(IProgressMonitor monitor) {
0312: text.setEnabled(false);
0313: selectAllCheck.setEnabled(false);
0314: attributeCombo.setEnabled(false);
0315: }
0316:
0317: public void loadingStopped(boolean canceled) {
0318: text.setEnabled(true);
0319: selectAllCheck.setEnabled(true);
0320: attributeCombo.setEnabled(true);
0321: }
0322:
0323: });
0324: layoutComponents(parent);
0325:
0326: // Create menu and toolbars
0327: createActions();
0328: createMenu();
0329: createContextMenu();
0330: createToolbar();
0331: hookGlobalActions();
0332:
0333: // restore state (from previous session)
0334:
0335: page = getSite().getPage();
0336:
0337: if (page.getActiveEditor() instanceof MapEditor) {
0338: editorActivated((MapEditor) page.getActiveEditor());
0339: }
0340:
0341: addTableSelectionListener();
0342: addSelectionListener();
0343: addPageListener();
0344:
0345: //provide workbench selections
0346: getSite().setSelectionProvider(this );
0347:
0348: ApplicationGIS.getToolManager().registerActionsWithPart(this );
0349: }
0350:
0351: private void addTableSelectionListener() {
0352: table
0353: .addSelectionChangedListener(new ISelectionChangedListener() {
0354:
0355: public void selectionChanged(
0356: SelectionChangedEvent event) {
0357: if (event.getSource() == table)
0358: featuresSelected
0359: .setText(Messages.TableView_featureSelected
0360: + table.getSelectionCount());
0361: if (updatingSelection) {
0362: updatingSelection = false;
0363: return;
0364: }
0365:
0366: ISelection selection = getSelection();
0367: if (selection.isEmpty()) {
0368: updateLayerFilter(Filter.ALL);
0369: } else if (selection instanceof IStructuredSelection) {
0370: IStructuredSelection structuredSelection = (IStructuredSelection) selection;
0371: Filter firstElement = (Filter) structuredSelection
0372: .getFirstElement();
0373: updateLayerFilter(firstElement);
0374: }
0375:
0376: }
0377:
0378: private void updateLayerFilter(Filter filter) {
0379: updatingLayerFilter = true;
0380: UndoableMapCommand createSelectCommand = SelectionCommandFactory
0381: .getInstance().createSelectCommand(
0382: layer, filter);
0383: layer.getMap().sendCommandSync(
0384: createSelectCommand);
0385: updatingLayerFilter = false;
0386:
0387: setZoomToSelectionToolEnablement();
0388: }
0389:
0390: });
0391: }
0392:
0393: private void layoutComponents(Composite parent) {
0394: FormLayout layout = new FormLayout();
0395: layout.marginHeight = 0;
0396: layout.marginWidth = 0;
0397: layout.spacing = 0;
0398: parent.setLayout(layout);
0399:
0400: FormData dLabel = new FormData(); // bind to left & text
0401: dLabel.left = new FormAttachment(0, 5);
0402: dLabel.top = new FormAttachment(attributeCombo, 2);
0403: dLabel.right = new FormAttachment(100, -5);
0404: featuresSelected.setLayoutData(dLabel);
0405:
0406: FormData dCombo = new FormData(); // bind to label and text
0407: dCombo.left = new FormAttachment(0, 5);
0408: dCombo.top = new FormAttachment(selectAllCheck, 0, SWT.CENTER);
0409: dCombo.right = new FormAttachment(30, -5);
0410: attributeCombo.setLayoutData(dCombo);
0411:
0412: FormData dText = new FormData(); // bind to label and text
0413: dText.left = new FormAttachment(attributeCombo);
0414: dText.top = new FormAttachment(selectAllCheck, 0, SWT.CENTER);
0415: dText.right = new FormAttachment(95, -5);
0416: text.setLayoutData(dText);
0417:
0418: FormData dCheck = new FormData(); // bind to top, label, bbox
0419: dCheck.top = new FormAttachment(2);
0420: dCheck.left = new FormAttachment(text, 5);
0421: dCheck.right = new FormAttachment(100, -5);
0422: selectAllCheck.setLayoutData(dCheck);
0423:
0424: FormData dContents = new FormData(100, 100); // text & bottom
0425: dContents.right = new FormAttachment(100); // bind to right of form
0426: dContents.left = new FormAttachment(0); // bind to left of form
0427: dContents.top = new FormAttachment(featuresSelected, 2); // attach with 2 pixel offset
0428: dContents.bottom = new FormAttachment(100); // bind to bottom of form
0429: table.getControl().setLayoutData(dContents);
0430: }
0431:
0432: /**
0433: * Adds a post selection listener that listens to the workbench's selection for maps, layers or MapEditor.
0434: */
0435: private void addSelectionListener() {
0436: workbenchSelectionListener = new ISelectionListener() {
0437: public void selectionChanged(IWorkbenchPart part,
0438: ISelection selection) {
0439: if (part instanceof MapEditor) {
0440: editorActivated((MapEditor) part);
0441: return;
0442: }
0443: if (!(selection instanceof IStructuredSelection))
0444: return;
0445:
0446: Object selected = ((IStructuredSelection) selection)
0447: .getFirstElement();
0448:
0449: final Layer selectedLayer;
0450: // this is horribly inelegant. is there not some other way?
0451: if (selected instanceof Map) {
0452: selectedLayer = ((Map) selected)
0453: .getEditManagerInternal()
0454: .getSelectedLayer();
0455: } else if (selected instanceof Layer) {
0456: selectedLayer = (Layer) selected;
0457: } else if (selected instanceof AdaptingFilter) {
0458: AdaptingFilter adaptingFilter = (AdaptingFilter) selected;
0459: selectedLayer = (Layer) adaptingFilter
0460: .getAdapter(Layer.class);
0461: } else {
0462: return;
0463: }
0464:
0465: if (selectedLayer != null) {
0466: PlatformGIS.run(new ISafeRunnable() {
0467:
0468: public void handleException(Throwable exception) {
0469: SelectPlugin.log(
0470: "error selecting layer", exception); //$NON-NLS-1$
0471: }
0472:
0473: public void run() throws Exception {
0474: layerSelected(selectedLayer);
0475: }
0476:
0477: });
0478: }
0479: }
0480: };
0481: // listen to selections
0482: page.addPostSelectionListener(workbenchSelectionListener);
0483: }
0484:
0485: /**
0486: * Adds a listener to {@link #page} that deactivates the view when part is hidden and reactivates it when it is made
0487: * visible. This is to prevent a bunch of featurestore accesses when view is not visible.
0488: */
0489: private void addPageListener() {
0490: activePartListener = new IPartListener2() {
0491: public void partActivated(IWorkbenchPartReference partRef) {
0492: }
0493:
0494: public void partBroughtToTop(IWorkbenchPartReference partRef) {
0495: }
0496:
0497: public void partClosed(IWorkbenchPartReference partRef) {
0498: }
0499:
0500: public void partDeactivated(IWorkbenchPartReference partRef) {
0501: }
0502:
0503: public void partOpened(IWorkbenchPartReference partRef) {
0504: }
0505:
0506: public void partHidden(IWorkbenchPartReference partRef) {
0507: if (partRef.getPart(false) == TableView.this )
0508: deactivate();
0509: }
0510:
0511: public void partVisible(IWorkbenchPartReference partRef) {
0512: if (partRef.getPart(false) == TableView.this )
0513: activate();
0514: }
0515:
0516: public void partInputChanged(IWorkbenchPartReference partRef) {
0517: }
0518: };
0519: // listen for editor changes
0520: page.addPartListener(activePartListener);
0521: }
0522:
0523: protected void activate() {
0524: if (active)
0525: return;
0526:
0527: PlatformGIS.run(new ISafeRunnable() {
0528:
0529: public void handleException(Throwable exception) {
0530: SelectPlugin.log("error activating table", exception); //$NON-NLS-1$
0531: }
0532:
0533: public void run() throws Exception {
0534: active = true;
0535: if (reloadNeeded)
0536: reloadFeatures(layer);
0537: if (!updates.isEmpty())
0538: updateTable(layer);
0539: if (filterChange)
0540: updateSelection(layer);
0541: }
0542:
0543: });
0544: }
0545:
0546: protected void deactivate() {
0547: active = false;
0548: }
0549:
0550: private void hookGlobalActions() {
0551: IKeyBindingService service = getSite().getKeyBindingService();
0552: IAction action = deleteAction;
0553: getViewSite().getActionBars().setGlobalActionHandler(
0554: ActionFactory.DELETE.getId(), action);
0555: service.registerAction(action);
0556:
0557: }
0558:
0559: /**
0560: * Create actions, linking view to current map.
0561: */
0562: private void createActions() {
0563: select = ApplicationGIS.getToolManager().createToolAction(
0564: BBoxSelection.ID, ToolsConstants.SELECTION_CATEGORY);
0565: ImageDescriptor icon = AbstractUIPlugin
0566: .imageDescriptorFromPlugin(SelectPlugin.ID,
0567: "icons/eview16/select_view.gif"); //$NON-NLS-1$
0568: select.setImageDescriptor(icon);
0569:
0570: this .promoteSelection = new PromoteSelectionAction();
0571:
0572: zoom = ((ToolManager) ApplicationGIS.getToolManager())
0573: .createToolAction(ZoomSelection.ID,
0574: ToolsConstants.ZOOM_CATEGORY);
0575: icon = AbstractUIPlugin.imageDescriptorFromPlugin(
0576: SelectPlugin.ID, "icons/elcl16/zoom_select_co.png"); //$NON-NLS-1$
0577: zoom.setImageDescriptor(icon);
0578:
0579: deleteAction = new DeleteAction();
0580: }
0581:
0582: /* Called when a Layer is selected - will need to check if we care */
0583: void layerSelected(ILayer selected) {
0584: if (layer == selected)
0585: return; // we already know
0586:
0587: if (layer != null) {
0588: layer.removeListener(layerListener);
0589: if (layer.getMap() != null) {
0590: layer.getMap().removeMapCompositionListener(
0591: compositionListener);
0592: layer.getMap().getEditManager().removeListener(
0593: editManagerListener);
0594: }
0595: }
0596:
0597: if (selected == null
0598: || !selected.hasResource(FeatureSource.class)
0599: || selected.getMap() == null) {
0600: if (currentEditor != null) {
0601: currentEditor.getMap().getEditManager().addListener(
0602: editManagerListener);
0603: }
0604: layer = null;
0605: filterChange = false;
0606: reloadNeeded = false;
0607: table.getControl().getDisplay().asyncExec(new Runnable() {
0608: public void run() {
0609: table.clear();
0610: table.message(Messages.TableView_noFeatureWarning);
0611: }
0612: });
0613: return;
0614: }
0615:
0616: layer = (Layer) selected;
0617: layer.addListener(layerListener);
0618: layer.getMap().addMapCompositionListener(compositionListener);
0619: layer.getMap().getEditManager()
0620: .addListener(editManagerListener);
0621:
0622: if (!active) {
0623: filterChange = true;
0624: reloadNeeded = true;
0625: return;
0626: }
0627:
0628: setZoomToSelectionToolEnablement();
0629: reloadFeatures(layer);
0630: updateSelection(layer);
0631: }
0632:
0633: private void setZoomToSelectionToolEnablement() {
0634: final boolean enabled;
0635:
0636: if (layer.getMap() == ApplicationGIS.getActiveMap()
0637: && layer.getFilter() != Filter.ALL)
0638: enabled = true;
0639: else
0640: enabled = false;
0641:
0642: table.getControl().getDisplay().asyncExec(new Runnable() {
0643: public void run() {
0644: zoom.setEnabled(enabled);
0645: }
0646: });
0647: }
0648:
0649: /**
0650: * The list of updates that have occurred but have not yet been applied to the FeatureTable
0651: */
0652: private List<FeatureEvent> updates = Collections
0653: .synchronizedList(new ArrayList<FeatureEvent>());
0654: private ILayerListener layerListener = new ILayerListener() {
0655:
0656: public void refresh(LayerEvent event) {
0657: final ILayer notifierLayer = event.getSource();
0658: assert layer == notifierLayer;
0659:
0660: switch (event.getType()) {
0661: case EDIT_EVENT:
0662: if (!editing) {
0663: if (event.getNewValue() == null)
0664: return;
0665:
0666: updates.add((FeatureEvent) event.getNewValue());
0667: if (active) {
0668: updateTable(notifierLayer);
0669: }
0670: }
0671: break;
0672: case FILTER:
0673: if (active)
0674: updateSelection(notifierLayer);
0675: else {
0676: filterChange = true;
0677: }
0678: break;
0679: }
0680:
0681: }
0682:
0683: };
0684:
0685: private IMapCompositionListener compositionListener = new IMapCompositionListener() {
0686:
0687: public void changed(MapCompositionEvent event) {
0688:
0689: if (event.getType() == MapCompositionEvent.EventType.REMOVED) {
0690: if (event.getLayer() == layer) {
0691: layerSelected(null);
0692: }
0693: } else if (event.getType() == MapCompositionEvent.EventType.MANY_REMOVED) {
0694: if (((List) event.getNewValue()).contains(layer))
0695: layerSelected(null);
0696: }
0697: }
0698:
0699: };
0700:
0701: private IEditManagerListener editManagerListener = new IEditManagerListener() {
0702:
0703: public void changed(EditManagerEvent event) {
0704: assert layer == null
0705: || (layer != null && layer.getMap() == event
0706: .getSource().getMap());
0707:
0708: switch (event.getType()) {
0709: case EditManagerEvent.POST_ROLLBACK:
0710: if (!active) {
0711: reloadNeeded = true;
0712: return;
0713: }
0714: reloadFeatures(layer);
0715: break;
0716: case EditManagerEvent.SELECTED_LAYER:
0717: layerSelected((ILayer) event.getNewValue());
0718: break;
0719: default:
0720: break;
0721: }
0722: }
0723:
0724: };
0725: private Set<ISelectionChangedListener> selectionChangeListeners = new CopyOnWriteArraySet<ISelectionChangedListener>();
0726:
0727: private IToolContext currentContext;
0728:
0729: /**
0730: * Watch current editor to indicate current selectable layers.
0731: *
0732: * @param editor
0733: */
0734: protected void editorActivated(MapEditor editor) {
0735: if (currentEditor == editor)
0736: return;
0737:
0738: currentEditor = editor;
0739: if (editor == null) {
0740: select.setEnabled(false);
0741: table.clear();
0742: table.update();
0743: return;
0744: }
0745:
0746: Map map = currentEditor.getMap();
0747: final ILayer selectedLayer = map.getEditManager()
0748: .getSelectedLayer();
0749: // layerSelecte returns if layer==newLayer. If both are null we still want to listen to map for a
0750: // layer being added (and selected).
0751: if (selectedLayer == null && layer == null) {
0752: map.getEditManager().addListener(editManagerListener);
0753: return;
0754: }
0755:
0756: PlatformGIS.run(new ISafeRunnable() {
0757:
0758: public void handleException(Throwable exception) {
0759: SelectPlugin.log("error selecting layer", exception); //$NON-NLS-1$
0760: }
0761:
0762: public void run() throws Exception {
0763: layerSelected(selectedLayer);
0764: }
0765:
0766: });
0767: if (selectedLayer != null)
0768: select.setEnabled(true);
0769: }
0770:
0771: private void createToolbar() {
0772: IToolBarManager toolbar = getViewSite().getActionBars()
0773: .getToolBarManager();
0774: toolbar.add(select);
0775: toolbar.add(zoom);
0776: toolbar.add(promoteSelection);
0777: }
0778:
0779: private void createContextMenu() {
0780: contextMenu = new MenuManager();
0781:
0782: contextMenu.setRemoveAllWhenShown(true);
0783: contextMenu.addMenuListener(new IMenuListener() {
0784:
0785: public void menuAboutToShow(IMenuManager mgr) {
0786: contextMenu.add(deleteAction);
0787: contextMenu.add(zoom);
0788: contextMenu.add(promoteSelection);
0789: contextMenu.add(new GroupMarker(
0790: IWorkbenchActionConstants.MB_ADDITIONS));
0791: contextMenu.add(ApplicationGIS.getToolManager()
0792: .createOperationsContextMenu(
0793: table.getSelection()));
0794: }
0795:
0796: });
0797:
0798: // Create menu.
0799: table.setMenuManager(contextMenu);
0800: }
0801:
0802: private void createMenu() {
0803: // create view menu, consider sync
0804: }
0805:
0806: @Override
0807: public void setFocus() {
0808: hookGlobalActions();
0809: // select your "main" control
0810: if (table.getControl() != null)
0811: table.getControl().setFocus();
0812: }
0813:
0814: @Override
0815: public void dispose() {
0816: if (table != null)
0817: table.dispose();
0818: super .dispose();
0819: if (activePartListener != null)
0820: page.removePartListener(activePartListener);
0821: if (workbenchSelectionListener != null)
0822: page
0823: .removePostSelectionListener(this .workbenchSelectionListener);
0824: if (layer != null && layerListener != null)
0825: layer.removeListener(layerListener);
0826: }
0827:
0828: protected void updateSelection(final ILayer notifierLayer) {
0829: if (!active) {
0830: filterChange = true;
0831: return;
0832: }
0833: if (updatingLayerFilter)
0834: return;
0835: try {
0836: final FeatureSource featureSource = notifierLayer
0837: .getResource(FeatureSource.class, null);
0838: filterChange = false;
0839: Display.getDefault().asyncExec(new Runnable() {
0840: public void run() {
0841: updatingSelection = true;
0842: if (updatingLayerFilter)
0843: return;
0844: Filter filter = notifierLayer.getFilter();
0845: if (filter == Filter.ALL) {
0846: table.setSelection(new StructuredSelection());
0847: return;
0848: }
0849: AdaptingFilter adaptingFilter = new AdaptingFilter(
0850: filter);
0851: adaptingFilter.addAdapter(featureSource);
0852: table.setSelection(new StructuredSelection(
0853: adaptingFilter));
0854: }
0855: });
0856: } catch (IOException e) {
0857: SelectPlugin.log("", e); //$NON-NLS-1$
0858: }
0859: }
0860:
0861: protected void updateTable(final ILayer notifierLayer) {
0862: try {
0863: // Envelope indicating the bounds of the added features from all the updates currently available in the
0864: // updates field;
0865: Envelope addedBounds = null;
0866: // Envelope indicating the bounds of the modified features from all the updates currently available in the
0867: // updates field;
0868: Envelope modifiedBounds = null;
0869:
0870: synchronized (updates) {
0871: for (FeatureEvent event : updates) {
0872: Envelope bounds = event.getBounds();
0873: switch (event.getEventType()) {
0874: case FeatureEvent.FEATURES_ADDED:
0875: if (addedBounds == null) {
0876: addedBounds = new Envelope(bounds);
0877: } else {
0878: addedBounds.expandToInclude(bounds);
0879: }
0880: break;
0881: case FeatureEvent.FEATURES_REMOVED:
0882: // With current Event API there is no way to know what was removed
0883: reloadNeeded = true;
0884: if (active)
0885: reloadFeatures(notifierLayer);
0886: return;
0887:
0888: case FeatureEvent.FEATURES_CHANGED:
0889: if (event.getBounds() == null)
0890: return;
0891: if (modifiedBounds == null) {
0892: modifiedBounds = new Envelope(bounds);
0893: } else {
0894: modifiedBounds.expandToInclude(bounds);
0895: }
0896:
0897: break;
0898:
0899: default:
0900: break;
0901: }
0902: }
0903: updates.clear();
0904: }
0905: FeatureSource source = notifierLayer.getResource(
0906: FeatureSource.class, ProgressManager.instance()
0907: .get());
0908: FeatureType schema = source.getSchema();
0909:
0910: FilterFactory fac = FilterFactoryFinder
0911: .createFilterFactory();
0912: final List<String> queryAtts = obtainQueryAttributesForFeatureTable(schema);
0913: final DefaultQuery query = new DefaultQuery(schema
0914: .getTypeName(), Filter.ALL, queryAtts
0915: .toArray(new String[0]));
0916:
0917: // add new features
0918: if (addedBounds != null) {
0919: GeometryFilter bboxFilter = fac
0920: .createGeometryFilter(FilterType.GEOMETRY_BBOX);
0921: bboxFilter.addLeftGeometry(fac
0922: .createBBoxExpression(addedBounds));
0923: bboxFilter.addRightGeometry(fac
0924: .createAttributeExpression(schema
0925: .getDefaultGeometry().getName()));
0926:
0927: query.setFilter(bboxFilter);
0928: FeatureCollection features = source.getFeatures(query);
0929: this .table.update(features);
0930: }
0931: // update modified features
0932: if (modifiedBounds != null) {
0933: GeometryFilter bboxFilter = fac
0934: .createGeometryFilter(FilterType.GEOMETRY_BBOX);
0935: bboxFilter.addLeftGeometry(fac
0936: .createBBoxExpression(modifiedBounds));
0937: bboxFilter.addRightGeometry(fac
0938: .createAttributeExpression(schema
0939: .getDefaultGeometry().getName()));
0940:
0941: query.setFilter(bboxFilter);
0942: FeatureCollection features = source.getFeatures(query);
0943: this .table.update(features);
0944: }
0945:
0946: } catch (IOException e) {
0947: if (active) {
0948: reloadFeatures(notifierLayer);
0949: } else {
0950: reloadNeeded = true;
0951: }
0952: } catch (IllegalFilterException e) {
0953: if (active) {
0954: reloadFeatures(notifierLayer);
0955: } else {
0956: reloadNeeded = true;
0957: }
0958: }
0959: }
0960:
0961: protected void reloadFeatures(final ILayer notifierLayer) {
0962: try {
0963: reloadNeeded = false;
0964: updates.clear();
0965:
0966: // icon = getLayerIcon()
0967: final FeatureTypeCellModifier featureTypeCellModifier = new FeatureTypeCellModifier(
0968: notifierLayer) {
0969: @Override
0970: public Object getValue(Object element, String property) {
0971: ApplicationGIS.getToolManager().unregisterActions(
0972: TableView.this );
0973:
0974: return super .getValue(element, property);
0975: }
0976:
0977: @Override
0978: protected void makeModification(Feature feature,
0979: ILayer layer, String property, Object value,
0980: Item item) {
0981: TableItem tableItem = (TableItem) item;
0982: int columnIndex = feature.getFeatureType().find(
0983: property);
0984: tableItem
0985: .setText(columnIndex + 1, value.toString());
0986:
0987: UndoableComposite composite = new UndoableComposite();
0988: composite.getCommands().add(
0989: new SetEditingFlag(true));
0990:
0991: composite.getCommands().add(
0992: EditCommandFactory.getInstance()
0993: .createSetAttributeCommand(feature,
0994: layer, property, value));
0995: composite.getFinalizerCommands().add(
0996: new SetEditingFlag(false));
0997: layer.getMap().sendCommandASync(composite);
0998: }
0999: };
1000: final FeatureType schema = notifierLayer.getSchema();
1001: final FeatureSource featureSource = notifierLayer
1002: .getResource(FeatureSource.class, null);
1003: final List<String> queryAtts = obtainQueryAttributesForFeatureTable(schema);
1004:
1005: final Query query = new DefaultQuery(schema.getTypeName(),
1006: Filter.NONE, queryAtts.toArray(new String[0]));
1007:
1008: final FeatureCollection features = featureSource
1009: .getFeatures(query);
1010: // final FeatureCollection features = featureSource.getFeatures();
1011:
1012: Display.getDefault().asyncExec(new Runnable() {
1013: public void run() {
1014: // we don't need to display the geometries, that's what the map is for.
1015: queryAtts.add(0, ANY);
1016: attributeCombo.setItems(queryAtts
1017: .toArray(new String[0]));
1018: attributeCombo.select(0);
1019:
1020: AdaptableFeatureCollection adaptableCollection = new AdaptableFeatureCollection(
1021: features);
1022:
1023: if (featureSource instanceof FeatureStore)
1024: enableEditing(featureTypeCellModifier, query,
1025: adaptableCollection);
1026:
1027: table.setFeatures(adaptableCollection);
1028: }
1029:
1030: private void enableEditing(
1031: final FeatureTypeCellModifier featureTypeCellModifier,
1032: final Query query,
1033: AdaptableFeatureCollection adaptableCollection) {
1034: adaptableCollection
1035: .addAdapter(featureTypeCellModifier);
1036: ICellEditorListener[] keyBindingActivators = new ICellEditorListener[query
1037: .getPropertyNames().length];
1038: for (int i = 0; i < keyBindingActivators.length; i++) {
1039: keyBindingActivators[i] = new ICellEditorListener() {
1040: public void applyEditorValue() {
1041: ApplicationGIS.getToolManager()
1042: .registerActionsWithPart(
1043: TableView.this );
1044: }
1045:
1046: public void cancelEditor() {
1047: applyEditorValue();
1048: }
1049:
1050: public void editorValueChanged(
1051: boolean oldValidState,
1052: boolean newValidState) {
1053:
1054: }
1055:
1056: };
1057: }
1058: adaptableCollection
1059: .addAdapter(keyBindingActivators);
1060: }
1061: });
1062: } catch (final IOException e) {
1063: Display.getDefault().asyncExec(new Runnable() {
1064: public void run() {
1065: table.message(e.getMessage());
1066: }
1067: });
1068: }
1069: }
1070:
1071: private List<String> obtainQueryAttributesForFeatureTable(
1072: final FeatureType schema) {
1073: final List<String> queryAtts = new ArrayList<String>();
1074:
1075: for (int i = 0; i < schema.getAttributeCount(); i++) {
1076: AttributeType attr = schema.getAttributeType(i);
1077: if (!(attr instanceof GeometryAttributeType)) {
1078: queryAtts.add(attr.getName());
1079: }
1080: }
1081: return queryAtts;
1082: }
1083:
1084: public void addSelectionChangedListener(
1085: ISelectionChangedListener listener) {
1086: selectionChangeListeners.add(listener);
1087: }
1088:
1089: public ISelection getSelection() {
1090: FidFilter firstElement = getFilter();
1091: if (firstElement == null)
1092: return new StructuredSelection();
1093: return new StructuredSelection(new AdaptingFilter(firstElement,
1094: layer));
1095: }
1096:
1097: private FidFilter getFilter() {
1098: IStructuredSelection selection = (IStructuredSelection) table
1099: .getSelection();
1100: if (selection.isEmpty())
1101: return null;
1102:
1103: FidFilter firstElement = (FidFilter) selection
1104: .getFirstElement();
1105: return firstElement;
1106: }
1107:
1108: public void removeSelectionChangedListener(
1109: ISelectionChangedListener listener) {
1110: selectionChangeListeners.add(listener);
1111: }
1112:
1113: public void setSelection(ISelection selection) {
1114: table.setSelection(selection);
1115: }
1116:
1117: private class PromoteSelectionAction extends Action {
1118:
1119: public PromoteSelectionAction() {
1120: setText(Messages.TableView_promote_text);
1121: setToolTipText(Messages.TableView_promote_tooltip);
1122: setImageDescriptor(AbstractUIPlugin
1123: .imageDescriptorFromPlugin(SelectPlugin.ID,
1124: "icons/elcl16/promote_selection_co.gif")); //$NON-NLS-1$
1125: }
1126:
1127: @Override
1128: public void run() {
1129: table.promoteSelection();
1130: }
1131:
1132: }
1133:
1134: private class DeleteAction extends Action {
1135: public DeleteAction() {
1136: setActionDefinitionId("org.eclipse.ui.edit.delete"); //$NON-NLS-1$
1137: IWorkbenchAction actionTemplate = ActionFactory.DELETE
1138: .create(PlatformUI.getWorkbench()
1139: .getActiveWorkbenchWindow());
1140: setText(actionTemplate.getText());
1141: setToolTipText(actionTemplate.getToolTipText());
1142: setImageDescriptor(actionTemplate.getImageDescriptor());
1143: setDescription(actionTemplate.getDescription());
1144: setDisabledImageDescriptor(actionTemplate
1145: .getDisabledImageDescriptor());
1146: }
1147:
1148: @Override
1149: public void run() {
1150: IStructuredSelection selection = ((IStructuredSelection) table
1151: .getSelection());
1152: if (selection == null || selection.isEmpty()
1153: || table.getSelectionCount() == 0)
1154: return;
1155:
1156: FidFilter filter = (FidFilter) selection.getFirstElement();
1157:
1158: CompositeCommand composite;
1159: if (table.getSelectionCount() == 1) {
1160: composite = deleteFeature();
1161: } else {
1162: composite = deleteManyFeatures(filter);
1163: }
1164:
1165: layer.getMap().sendCommandASync(composite);
1166: }
1167:
1168: private CompositeCommand deleteFeature() {
1169: CompositeCommand composite = new CompositeCommand();
1170: composite.setName(Messages.TableView_compositeName);
1171: composite.getCommands().add(new SetEditingFlag(true));
1172: DeleteFromTableCommand deleteFromTableCommand = new DeleteFromTableCommand(
1173: layer);
1174: composite.getCommands().add(deleteFromTableCommand);
1175: IBlockingProvider<ILayer> layerProvider = new StaticBlockingProvider<ILayer>(
1176: layer);
1177: composite.getCommands().add(
1178: new DeleteFeatureCommand(deleteFromTableCommand,
1179: layerProvider));
1180: composite.getFinalizerCommands().add(
1181: new SetEditingFlag(false));
1182: return composite;
1183: }
1184:
1185: private CompositeCommand deleteManyFeatures(FidFilter filter) {
1186: CompositeCommand composite = new CompositeCommand();
1187: composite.setName(Messages.TableView_compositeName);
1188: composite.getCommands().add(new SetEditingFlag(true));
1189: composite.getCommands().add(
1190: new DeleteManyFeaturesCommand(layer, filter));
1191: composite.getCommands().add(
1192: new DeleteFromTableCommand(layer));
1193: composite.getFinalizerCommands().add(
1194: new SetEditingFlag(false));
1195: return composite;
1196: }
1197: }
1198:
1199: private class DeleteFromTableCommand extends AbstractCommand
1200: implements UndoableCommand, IBlockingProvider<Feature> {
1201:
1202: private FeatureCollection deletedFeatures;
1203: private Layer layer;
1204:
1205: public DeleteFromTableCommand(Layer layer) {
1206: this .layer = layer;
1207: }
1208:
1209: public void rollback(IProgressMonitor monitor) throws Exception {
1210: table.update(deletedFeatures);
1211: }
1212:
1213: public String getName() {
1214: return Messages.TableView_deleteCommandName;
1215: }
1216:
1217: public void run(IProgressMonitor monitor) throws Exception {
1218: deletedFeatures = table.deleteSelection();
1219: }
1220:
1221: public Feature get(IProgressMonitor monitor) throws IOException {
1222:
1223: IBlockingProvider<ILayer> layerProvider = new StaticBlockingProvider<ILayer>(
1224: layer);
1225: String featureID = deletedFeatures.features().next()
1226: .getID();
1227: IBlockingProvider<Feature> featureProvider = new FIDFeatureProvider(
1228: featureID, layerProvider);
1229:
1230: return featureProvider.get(monitor);
1231: }
1232:
1233: }
1234:
1235: private class SetEditingFlag extends AbstractCommand implements
1236: UndoableCommand {
1237: boolean oldState;
1238: final boolean newState;
1239:
1240: public SetEditingFlag(boolean newState) {
1241: this .newState = newState;
1242: }
1243:
1244: public void rollback(IProgressMonitor monitor) throws Exception {
1245: editing = oldState;
1246: }
1247:
1248: public String getName() {
1249: return "Set Editing Flag"; //$NON-NLS-1$
1250: }
1251:
1252: public void run(IProgressMonitor monitor) throws Exception {
1253: oldState = editing;
1254: editing = newState;
1255: }
1256:
1257: }
1258:
1259: public void editFeatureChanged(Feature feature) {
1260: if (feature == null) {
1261: return;
1262: }
1263: if (getContext().getEditManager().getSelectedLayer() != layer
1264: || updatingLayerFilter)
1265: return;
1266:
1267: if (table.getSelectionCount() == 1
1268: && getFilter().getFids()[0].equals(feature.getID()))
1269: return;
1270:
1271: updatingLayerFilter = true;
1272: try {
1273: StructuredSelection structuredSelection;
1274: if (feature != null)
1275: structuredSelection = new StructuredSelection(feature);
1276: else
1277: structuredSelection = new StructuredSelection();
1278:
1279: setSelection(structuredSelection);
1280: } finally {
1281: updatingLayerFilter = false;
1282: }
1283: }
1284:
1285: public IToolContext getContext() {
1286: return currentContext;
1287: }
1288:
1289: public void setContext(IToolContext newContext) {
1290: this.currentContext = newContext;
1291: }
1292: }
|