/******************************************************************************* * Copyright 2013 Geoscience Australia * * 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 au.gov.ga.earthsci.discovery.ui; import gov.nasa.worldwind.SceneController; import gov.nasa.worldwind.View; import gov.nasa.worldwind.WorldWindow; import gov.nasa.worldwind.avlist.AVKey; import gov.nasa.worldwind.geom.LatLon; import gov.nasa.worldwind.geom.Position; import gov.nasa.worldwind.layers.RenderableLayer; import gov.nasa.worldwind.view.orbit.OrbitView; import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.inject.Inject; import javax.inject.Named; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.e4.core.contexts.ContextInjectionFactory; import org.eclipse.e4.core.contexts.IEclipseContext; import org.eclipse.e4.core.di.annotations.Optional; import org.eclipse.e4.core.services.events.IEventBroker; import org.eclipse.e4.ui.di.Focus; import org.eclipse.e4.ui.model.application.ui.basic.MPart; import org.eclipse.e4.ui.services.IServiceConstants; import org.eclipse.e4.ui.workbench.UIEvents; import org.eclipse.e4.ui.workbench.modeling.ESelectionService; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ColumnViewer; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.events.MouseTrackAdapter; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.TraverseEvent; import org.eclipse.swt.events.TraverseListener; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.Text; import au.gov.ga.earthsci.application.Activator; import au.gov.ga.earthsci.application.parts.globe.GlobeSceneController; import au.gov.ga.earthsci.common.ui.dialogs.StackTraceDialog; import au.gov.ga.earthsci.common.ui.information.IInformationProvider; import au.gov.ga.earthsci.common.ui.information.InformationProviderHoverInformationControlManager; import au.gov.ga.earthsci.common.ui.widgets.PageLinks; import au.gov.ga.earthsci.common.ui.widgets.PageListener; import au.gov.ga.earthsci.discovery.DiscoveryParameters; import au.gov.ga.earthsci.discovery.DiscoveryServiceManager; import au.gov.ga.earthsci.discovery.IDiscovery; import au.gov.ga.earthsci.discovery.IDiscoveryListener; import au.gov.ga.earthsci.discovery.IDiscoveryResult; import au.gov.ga.earthsci.discovery.IDiscoveryResultHandler; import au.gov.ga.earthsci.discovery.IDiscoveryResultLabelProvider; import au.gov.ga.earthsci.discovery.IDiscoveryService; import au.gov.ga.earthsci.discovery.ui.handler.ServicesHandler; import au.gov.ga.earthsci.discovery.ui.handler.ViewOnGlobeHandler; import au.gov.ga.earthsci.worldwind.common.WorldWindowRegistry; import au.gov.ga.earthsci.worldwind.common.layers.Bounds; import au.gov.ga.earthsci.worldwind.common.retrieve.SectorPolyline; import au.gov.ga.earthsci.worldwind.common.view.orbit.FlyToOrbitViewAnimator; import au.gov.ga.earthsci.worldwind.common.view.orbit.FlyToSectorAnimator; /** * The Discovery UI part. * * @author Michael de Hoog (michael.dehoog@ga.gov.au) */ public class DiscoveryPart implements IDiscoveryListener, PageListener { @Inject @Optional @Named(IServiceConstants.ACTIVE_SHELL) private Shell shell; @Inject private IEclipseContext context; @Inject private ESelectionService selectionService; @Inject private IEventBroker eventBroker; private Text searchText; private Button searchButton; private SashForm resultsSashForm; private TableViewer discoveriesViewer; private Composite resultsComposite; private Composite resultsViewerComposite; private TableViewer resultsViewer; private PageLinks pageLinks; private final List<IDiscovery> currentDiscoveries = new ArrayList<IDiscovery>(); private final DiscoveryResultContentProvider discoveryContentProvider = new DiscoveryResultContentProvider(); private IDiscoveryResult lastResultMouseOver; private RenderableLayer mouseOverLayer; private GlobeSceneController mouseOverSceneController; private IDiscoveryResult selectedResult; private boolean viewOnGlobe; @PostConstruct public void init(final Composite parent, final MPart part) { context.set(DiscoveryPart.class, this); viewOnGlobe = ViewOnGlobeHandler.isViewOnGlobe(part); parent.setLayout(new GridLayout(1, true)); Composite searchComposite = new Composite(parent, SWT.NONE); searchComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); searchComposite.setLayout(createGridLayout(2, 0)); searchText = new Text(searchComposite, SWT.SEARCH); searchText.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false)); searchText.setMessage(Messages.DiscoveryPart_SearchPlaceholder); searchText.addTraverseListener(new TraverseListener() { @Override public void keyTraversed(TraverseEvent e) { if (e.detail == SWT.TRAVERSE_RETURN) { performSearch(); } } }); searchButton = new Button(searchComposite, SWT.PUSH); searchButton.setLayoutData(new GridData(GridData.END, GridData.CENTER, false, false)); searchButton.setText(Messages.DiscoveryPart_Search); searchButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { performSearch(); } }); searchText.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { searchButton.setEnabled(searchText.getText().length() != 0); } }); searchButton.setEnabled(false); Composite resultsRootComposite = new Composite(parent, SWT.NONE); resultsRootComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); resultsRootComposite.setLayout(createGridLayout(1, 0)); resultsSashForm = new SashForm(resultsRootComposite, SWT.VERTICAL | SWT.SMOOTH); resultsSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); discoveriesViewer = new TableViewer(resultsSashForm, SWT.BORDER); discoveriesViewer.setContentProvider(new ArrayContentProvider()); discoveriesViewer.setLabelProvider(new DiscoveryLabelProvider()); discoveriesViewer.setComparator(new DiscoveryComparator()); resultsComposite = new Composite(resultsSashForm, SWT.NONE); resultsComposite.setLayout(createGridLayout(1, 0)); resultsViewerComposite = new Composite(resultsComposite, SWT.NONE); resultsViewerComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); resultsViewerComposite.setLayout(new FillLayout()); createResultsViewer(); pageLinks = new PageLinks(resultsComposite, SWT.NONE); pageLinks.setPageCount(20); pageLinks.setSelectedPage(4); pageLinks.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false)); pageLinks.addPageListener(this); pageLinks.setVisible(false); ((GridData) pageLinks.getLayoutData()).exclude = true; resultsSashForm.setWeights(new int[] { 2, 8 }); new TableViewerSelectionHelper<IDiscovery>(discoveriesViewer, IDiscovery.class) { @Override protected void itemSelected(IDiscovery selection) { discoverySelected(selection); } @Override protected void itemDefaultSelected(IDiscovery selection) { discoveryDefaultSelected(selection); } }; } @PreDestroy public void dispose() { displaySectorAndZoom(null, false); } @Focus private void setFocus() { searchText.setFocus(); } public void setViewOnGlobe(boolean viewOnGlobe) { this.viewOnGlobe = viewOnGlobe; resultSelected(selectedResult); } private void performSearch() { for (IDiscovery discovery : currentDiscoveries) { discovery.cancel(); } currentDiscoveries.clear(); discoverySelected(null); if (searchText.getText().length() > 0) { for (IDiscoveryService service : DiscoveryServiceManager.getServices()) { if (service.isEnabled()) { DiscoveryParameters parameters = new DiscoveryParameters(); parameters.setQuery(searchText.getText()); IDiscovery discovery = service.createDiscovery(parameters); if (discovery != null) { currentDiscoveries.add(discovery); discovery.addListener(DiscoveryPart.this); discovery.start(); } } } if (currentDiscoveries.isEmpty()) { if (MessageDialog.openQuestion(shell, Messages.DiscoveryPart_NoServicesDialogTitle, Messages.DiscoveryPart_NoServicesDialogMessage)) { new ServicesHandler().execute(shell); } } } discoveriesViewer.setInput(currentDiscoveries); resultSelected(null); eventBroker.send(UIEvents.REQUEST_ENABLEMENT_UPDATE_TOPIC, UIEvents.ALL_ELEMENT_ID); } public boolean canClearResults() { return searchText.getText().length() > 0 || !currentDiscoveries.isEmpty(); } public void clearResults() { searchText.setText(""); //$NON-NLS-1$ performSearch(); } private void createResultsViewer() { if (resultsViewer != null) { resultsViewer.getControl().dispose(); } resultsViewer = new TableViewer(resultsViewerComposite, SWT.BORDER | SWT.FULL_SELECTION); resultsViewer.setContentProvider(discoveryContentProvider); resultsViewer.getTable().setLinesVisible(true); resultsViewerComposite.layout(); //keep the column width in sync with the table width final TableColumn resultsColumn = new TableColumn(resultsViewer.getTable(), SWT.LEFT); Listener resizeListener = new Listener() { @Override public void handleEvent(Event event) { resultsColumn.setWidth(resultsViewer.getTable().getClientArea().width); } }; resultsViewer.getControl().addListener(SWT.Resize, resizeListener); resultsViewer.getControl().addListener(SWT.Paint, resizeListener); new TableViewerSelectionHelper<IDiscoveryResult>(resultsViewer, IDiscoveryResult.class) { @Override protected void itemSelected(IDiscoveryResult selection) { resultSelected(selection); } @Override protected void itemDefaultSelected(IDiscoveryResult selection) { resultDefaultSelected(selection); } }; resultsViewer.getTable().addMouseMoveListener(new MouseMoveListener() { @Override public void mouseMove(MouseEvent e) { IDiscoveryResult result = null; ViewerCell cell = resultsViewer.getCell(new Point(e.x, e.y)); if (cell != null) { result = (IDiscoveryResult) cell.getElement(); } resultMouseOver(result); } }); resultsViewer.getTable().addMouseTrackListener(new MouseTrackAdapter() { @Override public void mouseExit(MouseEvent e) { resultMouseOver(null); } }); } private void discoverySelected(IDiscovery discovery) { createResultsViewer(); boolean pageLinksVisible = false; if (discovery != null) { pageLinksVisible = discovery.getPageSize() != 0; if (pageLinksVisible) { int resultCount = discovery.getResultCount(); if (resultCount == IDiscovery.UNKNOWN) { pageLinks.setPageCount(PageLinks.UNKNOWN_PAGE_COUNT); } else { int pageCount = ((resultCount - 1) / discovery.getPageSize()) + 1; pageLinks.setPageCount(pageCount); } pageLinks.setSelectedPage(0); pageChanged(0); } IDiscoveryResultLabelProvider labelProvider = discovery.getLabelProvider(); resultsViewer.setLabelProvider(new DiscoveryResultLabelProvider(labelProvider)); //enable focusable tooltips IInformationProvider provider = new DiscoveryResultInformationProvider(resultsViewer, labelProvider); IInformationControlCreator creator = new DiscoveryResultInformationControlCreator(); InformationProviderHoverInformationControlManager.install(resultsViewer.getControl(), provider, creator); } pageLinks.setVisible(pageLinksVisible); ((GridData) pageLinks.getLayoutData()).exclude = !pageLinksVisible; resultsComposite.layout(); resultsViewer.setInput(discovery); } private void discoveryDefaultSelected(IDiscovery discovery) { if (discovery.getError() != null) { Throwable e = discovery.getError(); IStatus status = new Status(IStatus.ERROR, Activator.getBundleName(), e.getLocalizedMessage(), e); StackTraceDialog.openError(shell, Messages.DiscoveryPart_Error, null, status); } } private static GridLayout createGridLayout(int numColumns, int margins) { GridLayout layout = new GridLayout(numColumns, false); layout.marginWidth = layout.marginHeight = margins; return layout; } @Override public void resultCountChanged(final IDiscovery discovery) { final ColumnViewer viewer = discoveriesViewer; if (!viewer.getControl().isDisposed()) { viewer.getControl().getDisplay().asyncExec(new Runnable() { @Override public void run() { if (!viewer.getControl().isDisposed()) { viewer.refresh(discovery); } } }); } } @Override public void resultAdded(IDiscovery discovery, IDiscoveryResult result) { } @Override public void pageChanged(int page) { discoveryContentProvider.setPage(page); } private void resultSelected(IDiscoveryResult result) { this.selectedResult = result; selectionService.setSelection(result); displaySectorAndZoom(viewOnGlobe ? result : null, true); } private void resultDefaultSelected(IDiscoveryResult result) { IDiscoveryResultHandler handler = result.getDiscovery().getService().getProvider().getHandler(); ContextInjectionFactory.inject(handler, context); handler.open(result); } private void resultMouseOver(IDiscoveryResult result) { } private void displaySectorAndZoom(IDiscoveryResult result, boolean zoom) { if (lastResultMouseOver != result) { lastResultMouseOver = result; if (mouseOverLayer == null) { mouseOverLayer = new RenderableLayer(); } Bounds bounds = null; mouseOverLayer.removeAllRenderables(); if (result != null) { bounds = result.getBounds(); if (bounds != null) { mouseOverLayer.addRenderable(new SectorPolyline(bounds.toSector())); } } WorldWindow wwd = WorldWindowRegistry.INSTANCE.getActive(); if (wwd != null) { SceneController sceneController = wwd.getSceneController(); if (bounds != null && sceneController instanceof GlobeSceneController) { GlobeSceneController gsc = (GlobeSceneController) sceneController; if (mouseOverSceneController != gsc) { if (mouseOverSceneController != null) { mouseOverSceneController.getPostLayers().remove(mouseOverLayer); } mouseOverSceneController = gsc; mouseOverSceneController.getPostLayers().add(mouseOverLayer); } } else if (mouseOverSceneController != null) { mouseOverSceneController.getPostLayers().remove(mouseOverLayer); mouseOverSceneController = null; } wwd.redraw(); if (zoom) { View view = WorldWindowRegistry.INSTANCE.getActiveView(); if (view instanceof OrbitView && bounds != null) { OrbitView orbitView = (OrbitView) view; Position center = orbitView.getCenterPosition(); Position newCenter; if (bounds.contains(center) && bounds.deltaLatitude.degrees > 90 && bounds.deltaLongitude.degrees > 90) { newCenter = center; } else { newCenter = bounds.center; } LatLon endVisibleDelta = new LatLon(bounds.deltaLatitude, bounds.deltaLongitude); FlyToOrbitViewAnimator animator = FlyToSectorAnimator.createScaledFlyToSectorAnimator(orbitView, center, newCenter, orbitView.getHeading(), orbitView.getPitch(), orbitView.getZoom(), endVisibleDelta, 10); orbitView.stopAnimations(); orbitView.stopMovement(); orbitView.addAnimator(animator); orbitView.firePropertyChange(AVKey.VIEW, null, orbitView); } } } } } }