/******************************************************************************* * Copyright 2011 Google Inc. All Rights Reserved. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * 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.google.gwt.eclipse.oophm.views.hierarchical; import com.google.gdt.eclipse.core.browser.BrowserMenuPopulator; import com.google.gwt.eclipse.core.GWTPlugin; import com.google.gwt.eclipse.oophm.model.IWebAppDebugModelListener; import com.google.gwt.eclipse.oophm.model.LaunchConfiguration; import com.google.gwt.eclipse.oophm.model.WebAppDebugModelEvent; import com.google.gwt.eclipse.oophm.model.WebAppDebugModelListenerAdapter; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Menu; import java.util.List; /** * Displays a filtered list of launch URLs for a given launch configuration. This pane is displayed * when a launch configuration is selected in the hierarchical view. */ public class LaunchConfigurationContent extends Composite { /* * All methods must be called on the UI thread, otherwise there needs to be locking on the viewer * . */ private static class ContentProvider implements ITreeContentProvider { private final IWebAppDebugModelListener launchUrlsChangedListener = new WebAppDebugModelListenerAdapter() { @Override public void launchConfigurationLaunchUrlsChanged( WebAppDebugModelEvent<LaunchConfiguration> e) { if (e.getElement().equals(launchConfigurationModelObject)) { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { updateLaunchUrlsOrLoadingOnViewer(); } }); } } }; private TreeViewer viewer; private LaunchConfiguration launchConfigurationModelObject; private final Label launchUrlsCaptionLabel; /* * TODO: The launchUrlsCaptionLabel is leaking the abstraction of the provider being separate * from the viewer (this label counts as part of the "viewer" since it is updated along with * viewer updates). But a fix is overly complicated for the current usage of this class. */ private ContentProvider(Label launchUrlsCaptionLabel) { this.launchUrlsCaptionLabel = launchUrlsCaptionLabel; } @Override public void dispose() {} @Override public Object[] getChildren(Object parentElement) { if (!(parentElement instanceof LaunchConfiguration)) { return null; } LaunchConfiguration parentLaunchConfiguration = (LaunchConfiguration) parentElement; if (!parentLaunchConfiguration.isServing()) { return new String[] {"Waiting for launch URLs..."}; } else { List<String> launchUrls = parentLaunchConfiguration.getLaunchUrls(); return launchUrls.toArray(new String[launchUrls.size()]); } } @Override public Object[] getElements(Object inputElement) { return getChildren(inputElement); } @Override public Object getParent(Object element) { return element != launchConfigurationModelObject ? launchConfigurationModelObject : null; } @Override public boolean hasChildren(Object element) { return element == launchConfigurationModelObject; } @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { this.launchConfigurationModelObject = (LaunchConfiguration) newInput; this.viewer = (TreeViewer) viewer; if (oldInput != null) { ((LaunchConfiguration) oldInput).getModel().removeWebAppDebugModelListener( launchUrlsChangedListener); } if (newInput != null) { ((LaunchConfiguration) newInput).getModel().addWebAppDebugModelListener( launchUrlsChangedListener); updateLaunchUrlsOrLoadingOnViewer(); } } private void updateLaunchUrlsOrLoadingOnViewer() { if (viewer != null) { boolean isServing = launchConfigurationModelObject.isServing(); viewer.getTree().setEnabled(isServing); if (isServing) { launchUrlsCaptionLabel .setText("Double-click to open a URL or right-click for more options."); launchUrlsCaptionLabel.getParent().layout(); } else { launchUrlsCaptionLabel.setText("Development mode is loading..."); } viewer.refresh(); } } } private static class LabelProvider implements ILabelProvider { @Override public void addListener(ILabelProviderListener listener) {} @Override public void dispose() {} @Override public Image getImage(Object element) { return null; } @Override public String getText(Object element) { return (String) element; } @Override public boolean isLabelProperty(Object element, String property) { return false; } @Override public void removeListener(ILabelProviderListener listener) {} } private final Clipboard clipboard = new Clipboard(Display.getCurrent()); private final TextTransfer textTransferInstance = TextTransfer.getInstance(); private final LaunchConfiguration launchConfiguration; private TreeViewer viewer; private BrowserMenuPopulator browserMenuPopulator = new BrowserMenuPopulator( new BrowserMenuPopulator.DefaultBrowserProvider() { private final String ATTR_PREVIOUS_BROWSER = GWTPlugin.PLUGIN_ID + ".oophm." + LaunchConfigurationContent.class.getSimpleName(); @Override public String getDefaultBrowserName() { return GWTPlugin.getDefault().getPreferenceStore().getString(ATTR_PREVIOUS_BROWSER); } @Override public void setDefaultBrowserName(String browserName) { GWTPlugin.getDefault().getPreferenceStore().setValue(ATTR_PREVIOUS_BROWSER, browserName); } }); public LaunchConfigurationContent(final Composite parent, LaunchConfiguration launchConfigurationModelObject) { super(parent, SWT.NONE); this.launchConfiguration = launchConfigurationModelObject; GridLayout layout = new GridLayout(); setLayout(layout); setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); // if the parent of launchUrlsCaptionLabel changes, then update the code // in the isServing condition in updateLaunchUrlsOrLoadingOnViewer Label launchUrlsCaptionLabel = new Label(this, SWT.WRAP); GridData labelLayoutData = new GridData(SWT.FILL, SWT.CENTER, true, false); labelLayoutData.horizontalIndent = 2; labelLayoutData.verticalIndent = 2; launchUrlsCaptionLabel.setLayoutData(labelLayoutData); createViewer(launchUrlsCaptionLabel); createContextMenu(); } private void copySelectionToClipboard() { String url = getSelectedUrl(); if (url != null) { clipboard.setContents(new Object[] {url}, new Transfer[] {textTransferInstance}); } } private void createContextMenu() { MenuManager menuMgr = new MenuManager("#PopupMenu"); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { @Override public void menuAboutToShow(IMenuManager manager) { populateBrowserActions(launchConfiguration, manager); manager.add(new Separator()); manager.add(new Action("&Copy") { @Override public void run() { copySelectionToClipboard(); } }); } }); Menu menu = menuMgr.createContextMenu(viewer.getControl()); viewer.getControl().setMenu(menu); } private void createViewer(Label launchUrlsCaptionLabel) { viewer = new TreeViewer(this, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER); viewer.getTree().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); viewer.setLabelProvider(new LabelProvider()); viewer.setContentProvider(new ContentProvider(launchUrlsCaptionLabel)); viewer.setInput(launchConfiguration); viewer.addDoubleClickListener(new IDoubleClickListener() { @Override public void doubleClick(DoubleClickEvent event) { browserMenuPopulator.openDefaultBrowser(getProject(launchConfiguration), getSelectedUrl()); } }); } private String getSelectedUrl() { StructuredSelection selection = (StructuredSelection) viewer.getSelection(); return selection != null && !selection.isEmpty() ? (String) selection.getFirstElement() : null; } private void populateBrowserActions(LaunchConfiguration launchConfig, IMenuManager manager) { browserMenuPopulator.populate(getProject(launchConfig), manager, getSelectedUrl()); } private IProject getProject(LaunchConfiguration launchConfig) { if (launchConfig == null) { return null; } ILaunch launch = launchConfig.getLaunch(); if (launch == null) { return null; } ILaunchConfiguration lc = launch.getLaunchConfiguration(); if (lc == null) { return null; } IJavaProject project; try { project = JavaRuntime.getJavaProject(lc); } catch (CoreException e) { return null; } if (project == null) { return null; } return project.getProject(); } }