001: /*
002: * uDig - User Friendly Desktop Internet GIS client http://udig.refractions.net (C) 2004,
003: * Refractions Research Inc. This library is free software; you can redistribute it and/or modify it
004: * under the terms of the GNU Lesser General Public License as published by the Free Software
005: * Foundation; version 2.1 of the License. This library is distributed in the hope that it will be
006: * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
007: * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
008: */
009: package net.refractions.udig.project.ui.internal;
010:
011: import java.util.ArrayList;
012: import java.util.HashMap;
013: import java.util.Iterator;
014: import java.util.List;
015: import java.util.Map;
016:
017: import net.refractions.udig.core.internal.ExtensionPointList;
018: import net.refractions.udig.internal.ui.UiPlugin;
019: import net.refractions.udig.project.internal.EditManager;
020: import net.refractions.udig.project.internal.ProjectPackage;
021: import net.refractions.udig.project.internal.ProjectPlugin;
022: import net.refractions.udig.project.preferences.PreferenceConstants;
023: import net.refractions.udig.project.ui.IUDIGDialogPage;
024: import net.refractions.udig.project.ui.IUDIGView;
025: import net.refractions.udig.project.ui.internal.tool.ToolContext;
026: import net.refractions.udig.project.ui.internal.tool.impl.ToolContextImpl;
027: import net.refractions.udig.ui.PlatformGIS;
028:
029: import org.eclipse.core.runtime.IConfigurationElement;
030: import org.eclipse.emf.common.notify.Notification;
031: import org.eclipse.emf.common.notify.impl.AdapterImpl;
032: import org.eclipse.jface.action.Action;
033: import org.eclipse.jface.action.ActionContributionItem;
034: import org.eclipse.jface.action.GroupMarker;
035: import org.eclipse.jface.action.IAction;
036: import org.eclipse.jface.action.IContributionItem;
037: import org.eclipse.jface.action.MenuManager;
038: import org.eclipse.jface.dialogs.Dialog;
039: import org.eclipse.jface.viewers.ISelection;
040: import org.eclipse.jface.viewers.IStructuredSelection;
041: import org.eclipse.swt.SWT;
042: import org.eclipse.swt.layout.GridData;
043: import org.eclipse.swt.widgets.Composite;
044: import org.eclipse.swt.widgets.Control;
045: import org.eclipse.swt.widgets.Event;
046: import org.eclipse.swt.widgets.Shell;
047: import org.eclipse.ui.IPartListener2;
048: import org.eclipse.ui.IViewReference;
049: import org.eclipse.ui.IWorkbenchPage;
050: import org.eclipse.ui.IWorkbenchPart;
051: import org.eclipse.ui.IWorkbenchPartReference;
052: import org.eclipse.ui.PlatformUI;
053: import org.eclipse.ui.preferences.ScopedPreferenceStore;
054: import org.geotools.feature.Feature;
055: import org.geotools.feature.FeatureType;
056:
057: /**
058: * Processes the feature editor extension points and creates the menu items.
059: *
060: * @author jeichar
061: * @since 0.9.0
062: */
063: public class FeatureEditorExtensionProcessor {
064:
065: FeatureEditorViewpartListener partListener;
066:
067: Map<FeatureType, EditActionContribution> selectedEditors = new HashMap<FeatureType, EditActionContribution>();
068:
069: List<FeatureEditorLoader> editorLoaders = new ArrayList<FeatureEditorLoader>();
070:
071: static final String CURRENT_LOADER_ID = "FeatureEditorCurrentLoader"; //$NON-NLS-1$
072:
073: private static final String FEATURE_EDITOR_ID = "net.refractions.udig.project.ui.featureEditor"; //$NON-NLS-1$
074:
075: private boolean running;
076:
077: /**
078: * Construct <code>FeatureEditorExtensionProcessor</code>.
079: */
080: public FeatureEditorExtensionProcessor() {
081:
082: List<IConfigurationElement> list = ExtensionPointList
083: .getExtensionPointList(FEATURE_EDITOR_ID);
084: for (IConfigurationElement element : list) {
085: FeatureEditorLoader loader = new FeatureEditorLoader(this ,
086: element);
087: ScopedPreferenceStore preferences = ProjectPlugin
088: .getPlugin().getPreferenceStore();
089: if (loader.id
090: .equals(preferences
091: .getString(PreferenceConstants.P_DEFAULT_FEATURE_EDITOR)))
092: editorLoaders.add(0, loader);
093: else
094: editorLoaders.add(loader);
095: }
096: }
097:
098: /**
099: * Creates the edit feature action
100: *
101: * @return the edit feature action
102: */
103: public IContributionItem getEditFeatureAction(
104: final ISelection selection) {
105: if (selection.isEmpty())
106: return new GroupMarker("editAction"); //$NON-NLS-1$
107:
108: if (!(selection instanceof IStructuredSelection))
109: return new GroupMarker("editAction"); //$NON-NLS-1$
110: IStructuredSelection structuredSelection = (IStructuredSelection) selection;
111: if (!sameFeatureTypes(structuredSelection))
112: return new GroupMarker("editAction"); //$NON-NLS-1$
113:
114: Feature feature = (Feature) structuredSelection
115: .getFirstElement();
116: IContributionItem item = selectedEditors.get(feature
117: .getFeatureType());
118: if (item != null)
119: return item;
120:
121: FeatureEditorLoader loader = getClosestMatch(selection);
122: if (loader != null)
123: return createEditAction(loader, selection, feature);
124:
125: return new GroupMarker("editAction"); //$NON-NLS-1$
126: }
127:
128: /**
129: * returns the feature editor that is customized closest for the feature(s) in the selection
130: *
131: * @param selection a selection that contains 1 or more features.
132: * @return the feature editor that is customized closest for the feature(s) in the selection.
133: */
134: public FeatureEditorLoader getClosestMatch(ISelection selection) {
135: int bestValue = Integer.MAX_VALUE;
136: FeatureEditorLoader best = null;
137: for (FeatureEditorLoader loader : editorLoaders) {
138: int value = loader.match(selection);
139: if (value == -1)
140: continue;
141: if (value == 0)
142: return loader;
143: if (value < bestValue) {
144: bestValue = value;
145: best = loader;
146: }
147: }
148:
149: return best;
150: }
151:
152: /**
153: * Creates the edit menu item.
154: *
155: * @param loader
156: * @param selection
157: * @param feature
158: * @return
159: */
160: EditActionContribution createEditAction(
161: final FeatureEditorLoader loader,
162: final ISelection selection, Feature feature) {
163: EditActionContribution item;
164: item = new EditActionContribution(new EditAction(loader,
165: selection));
166: selectedEditors.put(feature.getFeatureType(), item);
167: return item;
168: }
169:
170: static class EditAction extends Action {
171: private ISelection selection;
172: private final FeatureEditorLoader loader;
173:
174: public EditAction(FeatureEditorLoader loader,
175: ISelection selection) {
176: super (Messages.FeatureEditorExtensionProcessor_editMenu,
177: loader.icon);
178: this .selection = selection;
179: this .loader = loader;
180: setId(loader.id);
181: }
182:
183: @Override
184: public void runWithEvent(Event event) {
185: loader.open(event.display, selection);
186: }
187:
188: public void setSelection(ISelection selection) {
189: this .selection = selection;
190: }
191: }
192:
193: static class EditActionContribution extends ActionContributionItem {
194:
195: private EditAction editAction;
196:
197: public EditActionContribution(IAction action) {
198: super (action);
199: editAction = (EditAction) action;
200: }
201:
202: public void setSelection(ISelection selection) {
203: editAction.setSelection(selection);
204: }
205:
206: }
207:
208: boolean sameFeatureTypes(IStructuredSelection structuredSelection) {
209: FeatureType type = null;
210: for (Iterator iter = structuredSelection.iterator(); iter
211: .hasNext();) {
212: Object obj = iter.next();
213: if (!(obj instanceof Feature))
214: return false;
215:
216: Feature feature = (Feature) obj;
217: if (type == null)
218: type = feature.getFeatureType();
219: else if (!type.equals(feature.getFeatureType()))
220: return false;
221: }
222: return true;
223: }
224:
225: /**
226: * Creates the edit/editWith menu items if the current selection is a feature.
227: *
228: * @return the edit/editWith menu items if the current selection is a feature.
229: */
230: public IContributionItem getEditWithFeatureMenu(ISelection selection) {
231: if (selection.isEmpty())
232: return new GroupMarker("editWithMenu"); //$NON-NLS-1$
233: MenuManager editWithMenu = new MenuManager(
234: Messages.FeatureEditorExtensionProcessor_editWithMenu);
235: for (FeatureEditorLoader loader : editorLoaders) {
236: IAction editorAction = loader.getAction(selection);
237: if (editorAction != null)
238: editWithMenu.add(editorAction);
239: }
240: editWithMenu.setVisible(true);
241: if (editWithMenu.getItems().length == 0)
242: return new GroupMarker("editWithMenu"); //$NON-NLS-1$
243: return editWithMenu;
244: }
245:
246: /**
247: * Creates and registers the Workbench part listener with the workbench window.
248: */
249: public void startPartListener() {
250: if (partListener == null) {
251: if (partListener == null) {
252: partListener = new FeatureEditorViewpartListener();
253: }
254: }
255: try {
256: PlatformUI.getWorkbench().getActiveWorkbenchWindow()
257: .getPartService().addPartListener(partListener);
258: running = true;
259: } catch (Exception e) {
260: ProjectUIPlugin.log(null, e);
261: }
262: }
263:
264: /**
265: * De-registers the Workbench part listener with the workbench window.
266: */
267: public void stopPartListener() {
268: try {
269: PlatformUI.getWorkbench().getActiveWorkbenchWindow()
270: .getPartService().addPartListener(partListener);
271: } catch (Exception e) {
272: // do nothing
273: }
274: running = false;
275: }
276:
277: static class FeatureEditorViewpartListener extends AdapterImpl
278: implements IPartListener2 {
279:
280: ToolContext currentContext;
281:
282: private List<IUDIGView> views = new ArrayList<IUDIGView>();
283:
284: FeatureEditorViewpartListener() {
285: IWorkbenchPage page = PlatformUI.getWorkbench()
286: .getActiveWorkbenchWindow().getActivePage();
287: IViewReference[] refs = page.getViewReferences();
288:
289: for (IViewReference reference : refs) {
290: IWorkbenchPart part = reference.getPart(false);
291: if (page.isPartVisible(part)
292: && part instanceof IUDIGView)
293: views.add((IUDIGView) part);
294: }
295: }
296:
297: /**
298: * @see org.eclipse.ui.IPartListener2#partActivated(org.eclipse.ui.IWorkbenchPartReference)
299: */
300: public void partActivated(IWorkbenchPartReference partRef) {
301: partVisible(partRef);
302:
303: }
304:
305: /**
306: * @see org.eclipse.ui.IPartListener2#partBroughtToTop(org.eclipse.ui.IWorkbenchPartReference)
307: */
308: public void partBroughtToTop(IWorkbenchPartReference partRef) {
309: partVisible(partRef);
310: }
311:
312: /**
313: * @see org.eclipse.ui.IPartListener2#partClosed(org.eclipse.ui.IWorkbenchPartReference)
314: */
315: public void partClosed(IWorkbenchPartReference partRef) {
316: if (partRef.getPart(false) instanceof IUDIGView) {
317: views.remove(partRef.getPart(false));
318: } else if (partRef.getPart(false) instanceof MapEditor) {
319:
320: MapEditor editor = (MapEditor) partRef.getPart(false);
321: synchronized (this ) {
322:
323: if (currentContext == null
324: || currentContext.getMapInternal() != editor
325: .getMap())
326: return;
327:
328: currentContext.getEditManagerInternal().eAdapters()
329: .remove(this );
330: currentContext = null;
331: }
332: for (IUDIGView view : views) {
333: view.setContext(null);
334: }
335: }
336: }
337:
338: /**
339: * @see org.eclipse.ui.IPartListener2#partDeactivated(org.eclipse.ui.IWorkbenchPartReference)
340: */
341: public void partDeactivated(IWorkbenchPartReference partRef) {
342: // do nothing
343: }
344:
345: /**
346: * @see org.eclipse.ui.IPartListener2#partOpened(org.eclipse.ui.IWorkbenchPartReference)
347: */
348: public synchronized void partOpened(
349: IWorkbenchPartReference partRef) {
350: partVisible(partRef);
351: }
352:
353: /**
354: * @see org.eclipse.ui.IPartListener2#partHidden(org.eclipse.ui.IWorkbenchPartReference)
355: */
356: public void partHidden(IWorkbenchPartReference partRef) {
357: // do nothing
358: }
359:
360: /**
361: * @see org.eclipse.ui.IPartListener2#partVisible(org.eclipse.ui.IWorkbenchPartReference)
362: */
363: @SuppressWarnings("unchecked")
364: public void partVisible(IWorkbenchPartReference partRef) {
365: if (partRef.getPart(false) instanceof IUDIGView) {
366: IUDIGView udigview = (IUDIGView) partRef.getPart(false);
367: if (!views.contains(udigview))
368: views.add(udigview);
369: Feature editFeature;
370: ToolContext copy;
371: synchronized (this ) {
372: if (!validateContext(currentContext))
373: return;
374: copy = currentContext.copy();
375: editFeature = currentContext.getEditManager()
376: .getEditFeature();
377: }
378: try {
379: udigview.setContext(copy);
380: if (editFeature != null)
381: udigview.editFeatureChanged(editFeature);
382: } catch (Throwable e) {
383: UiPlugin.log(udigview + " threw an exception", e); //$NON-NLS-1$
384: }
385:
386: } else if (partRef.getPart(false) instanceof MapEditor) {
387:
388: MapEditor editor = (MapEditor) partRef.getPart(false);
389: synchronized (this ) {
390: if (currentContext != null
391: && currentContext.getMapInternal() == editor
392: .getMap())
393: return;
394: if (currentContext != null)
395: currentContext.getEditManagerInternal()
396: .eAdapters().remove(this );
397:
398: currentContext = new ToolContextImpl();
399: currentContext.setMapInternal(editor.getMap());
400: currentContext.setRenderManagerInternal(editor
401: .getMap().getRenderManagerInternal());
402: currentContext.getEditManagerInternal().eAdapters()
403: .add(this );
404: for (IUDIGView view : views) {
405: try {
406: view.setContext(currentContext);
407: } catch (Throwable e) {
408: UiPlugin.log(
409: view + " threw an exception", e); //$NON-NLS-1$
410: }
411: }
412: }
413: }
414: }
415:
416: private boolean validateContext(ToolContext currentContext2) {
417: if (currentContext2 == null)
418: return false;
419: if (currentContext2.getMap() == null)
420: return false;
421: if (currentContext2.getViewportModel() == null)
422: return false;
423: if (currentContext2.getRenderManager() == null)
424: return false;
425: if (currentContext2.getDisplay() == null)
426: return false;
427: if (currentContext2.getEditManager() == null)
428: return false;
429:
430: return true;
431: }
432:
433: /**
434: * @see org.eclipse.ui.IPartListener2#partInputChanged(org.eclipse.ui.IWorkbenchPartReference)
435: */
436: public void partInputChanged(IWorkbenchPartReference partRef) {
437: // do nothing
438: }
439:
440: /**
441: * @see org.eclipse.emf.common.notify.impl.AdapterImpl#notifyChanged(org.eclipse.emf.common.notify.Notification)
442: */
443: public void notifyChanged(final Notification msg) {
444: if (msg.getNotifier() instanceof EditManager) {
445: if (msg.getFeatureID(EditManager.class) == ProjectPackage.EDIT_MANAGER__EDIT_FEATURE) {
446: PlatformGIS.syncInDisplayThread(new Runnable() {
447: public void run() {
448: updateEditFeatureViews(msg);
449: }
450: });
451: }
452: }
453: }
454:
455: private void updateEditFeatureViews(Notification msg) {
456: Feature newFeature = (Feature) msg.getNewValue();
457: for (IUDIGView view : views) {
458: try {
459: view.editFeatureChanged(newFeature);
460: } catch (Throwable e) {
461: UiPlugin.log(view + " threw an exception", e); //$NON-NLS-1$
462: }
463: }
464: }
465:
466: }
467:
468: static class EditorDialog extends Dialog {
469:
470: private IUDIGDialogPage page;
471:
472: /**
473: * Construct <code>EditorDialog</code>.
474: *
475: * @param parentShell
476: */
477: protected EditorDialog(Shell parentShell, IUDIGDialogPage page) {
478: super (parentShell);
479: this .page = page;
480: }
481:
482: /**
483: * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
484: */
485: protected Control createDialogArea(Composite parent) {
486: Composite composite = (Composite) super
487: .createDialogArea(parent);
488: page.createControl(composite);
489: GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
490: composite.setLayoutData(data);
491: page.getControl().setLayoutData(data);
492: return composite;
493: }
494:
495: }
496:
497: /**
498: * Indicates whether the listener has been added to the workbench
499: *
500: * @return
501: */
502: public boolean isRunning() {
503: return running;
504: }
505:
506: public FeatureEditorLoader[] getEditorLoaders() {
507: return editorLoaders.toArray(new FeatureEditorLoader[0]);
508: }
509:
510: }
|