/** * Copyright (c) 2009 Bestsolution.at and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * Tom Schindl<tom.schindl@bestsolution.at> - Initial API and implementation */ package org.eclipse.emf.example.databinding.project.ui.rcp.views; import java.text.DateFormat; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.commands.IExecutionListener; import org.eclipse.core.commands.NotHandledException; import org.eclipse.core.databinding.ObservablesManager; import org.eclipse.core.databinding.observable.IObservable; import org.eclipse.core.databinding.observable.list.IObservableList; import org.eclipse.core.databinding.observable.map.IMapChangeListener; import org.eclipse.core.databinding.observable.map.IObservableMap; import org.eclipse.core.databinding.observable.map.MapChangeEvent; import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; import org.eclipse.core.databinding.observable.set.IObservableSet; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.observable.value.IValueChangeListener; import org.eclipse.core.databinding.observable.value.ValueChangeEvent; import org.eclipse.core.databinding.observable.value.WritableValue; import org.eclipse.jface.action.GroupMarker; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider; import org.eclipse.jface.databinding.viewers.TreeStructureAdvisor; import org.eclipse.jface.databinding.viewers.ViewerProperties; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.ColumnViewer; import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.LabelProviderChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.StyledCellLabelProvider; import org.eclipse.jface.viewers.StyledString; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.jface.window.ToolTip; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.program.Program; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.ui.IViewSite; import org.eclipse.ui.commands.ICommandService; import org.eclipse.ui.forms.IFormColors; import org.eclipse.ui.forms.events.HyperlinkEvent; import org.eclipse.ui.forms.events.IHyperlinkListener; import org.eclipse.ui.forms.widgets.Form; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.services.ISourceProviderService; import org.eclipse.emf.databinding.EMFProperties; import org.eclipse.emf.databinding.FeaturePath; import org.eclipse.emf.databinding.IEMFListProperty; import org.eclipse.emf.example.databinding.project.ui.rcp.Activator; import org.eclipse.emf.example.databinding.project.ui.rcp.ResourceProvider; import org.eclipse.emf.example.databinding.project.ui.rcp.databinding.Base64ToImageConverter; import org.eclipse.emf.example.databinding.project.ui.rcp.handlers.CreateSublevelProjectHandler; import org.eclipse.emf.example.databinding.project.ui.rcp.handlers.CreateToplevelProjectHandler; import org.eclipse.emf.examples.databinding.project.core.model.project.CommitterShip; import org.eclipse.emf.examples.databinding.project.core.model.project.Foundation; import org.eclipse.emf.examples.databinding.project.core.model.project.Person; import org.eclipse.emf.examples.databinding.project.core.model.project.Project; import org.eclipse.emf.examples.databinding.project.core.model.project.ProjectPackage; /** * Part responsible for rendering the project tree */ public class ProjectExplorerPart { private final TreeViewer viewer; private FormToolkit toolkit; private final Image projectImage; private final Image committerImage; private final IObservableValue master = new WritableValue(); private final IExecutionListener paramListener; private final IViewSite site; /** * Create a new project explorer * @param site the view site * @param parent the parent control to renderer on * @param toolkit the toolkit * @param foundation the foundation instance * @param manager manager for observables to avoid leaks */ public ProjectExplorerPart(IViewSite site, Composite parent, FormToolkit toolkit, Foundation foundation, ObservablesManager manager) { this.toolkit = toolkit; this.site = site; ImageDescriptor desc = Activator.imageDescriptorFromPlugin("org.eclipse.ui", "icons/full/obj16/generic_elements.gif"); if (desc != null) { projectImage = desc.createImage(); } else { projectImage = null; } desc = Activator.imageDescriptorFromPlugin("org.eclipse.ui", "icons/full/obj16/signed_yes_tbl.gif"); if (desc != null) { committerImage = desc.createImage(); } else { committerImage = null; } viewer = init(parent, foundation); paramListener = new CommandListener(); ICommandService service = (ICommandService)site.getService(ICommandService.class); service.addExecutionListener(paramListener); } private TreeViewer init(Composite parent, Foundation foundation) { TreeViewer viewer = new TreeViewer(parent); ObservableListTreeContentProvider cp = new ObservableListTreeContentProvider(new TreeFactoryImpl(), new TreeStructureAdvisorImpl()); viewer.setContentProvider(cp); IObservableSet set = cp.getKnownElements(); IObservableMap[] map = new IObservableMap [4]; map[0] = EMFProperties.value(ProjectPackage.Literals.PROJECT__SHORTNAME).observeDetail(set); map[1] = EMFProperties.value(ProjectPackage.Literals.PROJECT__COMMITTERS).observeDetail(set); map[2] = EMFProperties.value( FeaturePath.fromList(ProjectPackage.Literals.COMMITTER_SHIP__PERSON, ProjectPackage.Literals.PERSON__FIRSTNAME)).observeDetail(set); map[3] = EMFProperties.value( FeaturePath.fromList(ProjectPackage.Literals.COMMITTER_SHIP__PERSON, ProjectPackage.Literals.PERSON__LASTNAME)).observeDetail(set); viewer.setLabelProvider(new TreeLabelProviderImpl(map)); IEMFListProperty projects = EMFProperties.list(ProjectPackage.Literals.FOUNDATION__PROJECTS); viewer.setInput(projects.observe(foundation)); MenuManager mgr = new MenuManager(); mgr.add(new GroupMarker("additions")); viewer.getControl().setMenu(mgr.createContextMenu(viewer.getControl())); site.registerContextMenu(Activator.PLUGIN_ID + ".projectexp", mgr, viewer); site.setSelectionProvider(viewer); ColumnViewerToolTipSupportImpl.enableFor(viewer, toolkit); IObservableValue treeObs = ViewerProperties.singleSelection().observe(viewer); treeObs.addValueChangeListener(new IValueChangeListener() { public void handleValueChange(ValueChangeEvent event) { ISourceProviderService s = (ISourceProviderService)site.getService(ISourceProviderService.class); ResourceProvider p = (ResourceProvider)s.getSourceProvider(ResourceProvider.MODEL_RESOURCE_NAME); if (event.diff.getNewValue() instanceof Project) { Project pr = (Project)event.diff.getNewValue(); master.setValue(pr); p.setCommitter(null); p.setProject(pr); } else if (event.diff.getNewValue() != null) { CommitterShip cm = (CommitterShip)event.diff.getNewValue(); master.setValue(cm.getProject()); p.setCommitter(cm); p.setProject(cm.getProject()); } else { p.setCommitter(null); p.setProject(null); } } }); return viewer; } /** * @return observable of the current active project in the tree */ public IObservableValue getProjectObservable() { return master; } private static class TreeFactoryImpl implements IObservableFactory { private IEMFListProperty multi = EMFProperties.multiList( ProjectPackage.Literals.PROJECT__SUBPROJECTS, ProjectPackage.Literals.PROJECT__COMMITTERS); public IObservable createObservable(final Object target) { if (target instanceof IObservableList) { return (IObservable)target; } else if (target instanceof Project) { return multi.observe(target); } return null; } } private static class TreeStructureAdvisorImpl extends TreeStructureAdvisor { @Override public Object getParent(Object element) { if (element instanceof Project) { return ((Project)element).getParent(); } return null; } @Override public Boolean hasChildren(Object element) { if (element instanceof Project && (((Project)element).getCommitters().size() > 0 || ((Project)element).getSubprojects().size() > 0)) { return Boolean.TRUE; } return super.hasChildren(element); } } private class TreeLabelProviderImpl extends StyledCellLabelProvider { private IMapChangeListener mapChangeListener = new IMapChangeListener() { public void handleMapChange(MapChangeEvent event) { Set< ? > affectedElements = event.diff.getChangedKeys(); if (!affectedElements.isEmpty()) { LabelProviderChangedEvent newEvent = new LabelProviderChangedEvent(TreeLabelProviderImpl.this, affectedElements.toArray()); fireLabelProviderChanged(newEvent); } } }; public TreeLabelProviderImpl(IObservableMap... attributeMaps) { for (int i = 0; i < attributeMaps.length; i++) { attributeMaps[i].addMapChangeListener(mapChangeListener); } } @Override public String getToolTipText(Object element) { return "#dummy#"; } @Override public void update(ViewerCell cell) { if (cell.getElement() instanceof Project) { Project p = (Project)cell.getElement(); StyledString styledString = new StyledString(p.getShortname() != null ? p.getShortname() : "*noname*", null); String decoration = " (" + p.getCommitters().size() + " Committers)"; styledString.append(decoration, StyledString.COUNTER_STYLER); cell.setText(styledString.getString()); cell.setImage(projectImage); cell.setStyleRanges(styledString.getStyleRanges()); } else if (cell.getElement() instanceof CommitterShip) { Person p = ((CommitterShip)cell.getElement()).getPerson(); String value = "*noname*"; if (p != null) { value = p.getLastname() + ", " + p.getFirstname(); } StyledString styledString = new StyledString(value, null); cell.setText(styledString.getString()); cell.setForeground(cell.getControl().getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY)); cell.setImage(committerImage); cell.setStyleRanges(styledString.getStyleRanges()); } } } private static class ColumnViewerToolTipSupportImpl extends ColumnViewerToolTipSupport { private FormToolkit toolkit; protected ColumnViewerToolTipSupportImpl(FormToolkit toolkit, ColumnViewer viewer, int style) { super(viewer, style, false); setHideOnMouseDown(false); this.toolkit = toolkit; } @Override protected Composite createViewerToolTipContentArea(Event event, ViewerCell cell, Composite parent) { if (cell.getElement() instanceof Project) { final Project p = (Project)cell.getElement(); StringBuilder b = new StringBuilder(); for (Person lead : p.getProjectleads()) { if (b.length() > 0) { b.append(", "); } b.append(lead.getFirstname() + " " + lead.getLastname()); } Form form = toolkit.createForm(parent); toolkit.decorateFormHeading(form); form.setText(p.getShortname() + " - " + p.getLongname()); Composite body = form.getBody(); body.setLayout(new GridLayout(2, false)); toolkit.createLabel(body, "Project creation:").setForeground(toolkit.getColors().getColor(IFormColors.TITLE)); toolkit.createLabel(body, p.getStart() != null ? DateFormat.getDateInstance().format(p.getStart()) : ""); toolkit.createLabel(body, "Project lead:").setForeground(toolkit.getColors().getColor(IFormColors.TITLE)); toolkit.createLabel(body, b.toString()); toolkit.createLabel(body, "Committers:").setForeground(toolkit.getColors().getColor(IFormColors.TITLE)); toolkit.createLabel(body, p.getCommitters().size() + ""); toolkit.createLabel(body, "Mailing-List:").setForeground(toolkit.getColors().getColor(IFormColors.TITLE)); if (p.getDevmail() != null) { toolkit.createHyperlink(body, p.getDevmail(), SWT.NONE).addHyperlinkListener(new IHyperlinkListener() { public void linkExited(HyperlinkEvent e) { // Ignore } public void linkEntered(HyperlinkEvent e) { // Ignore } public void linkActivated(HyperlinkEvent e) { Program.launch(p.getDevmail()); } }); } else { toolkit.createLabel(body, "-"); } toolkit.createLabel(body, "Homepage:").setForeground(toolkit.getColors().getColor(IFormColors.TITLE)); if (p.getHomepage() != null) { toolkit.createHyperlink(body, p.getHomepage(), SWT.NONE).addHyperlinkListener(new IHyperlinkListener() { public void linkExited(HyperlinkEvent e) { // Ignore } public void linkEntered(HyperlinkEvent e) { // Ignore } public void linkActivated(HyperlinkEvent e) { Program.launch(p.getHomepage()); } }); } else { toolkit.createLabel(body, "-"); } return form; } else { final CommitterShip committership = (CommitterShip)cell.getElement(); Form form = toolkit.createForm(parent); toolkit.decorateFormHeading(form); form.setText(committership.getPerson().getFirstname() + ", " + committership.getPerson().getLastname()); Composite formBody = form.getBody(); formBody.setLayout(new GridLayout(2, false)); GridData gd = new GridData(); final Label l = toolkit.createLabel(formBody, ""); l.setLayoutData(gd); Base64ToImageConverter c = new Base64ToImageConverter(form.getDisplay()); l.setImage((Image)c.convert(committership.getPerson().getImage())); l.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { if (l.getImage() != null) l.getImage().dispose(); } }); Composite body = toolkit.createComposite(formBody); body.setLayout(new GridLayout(2, false)); toolkit.createLabel(body, "Start:").setForeground(toolkit.getColors().getColor(IFormColors.TITLE)); toolkit.createLabel(body, committership.getStart() != null ? DateFormat.getDateInstance().format(committership.getStart()) : ""); toolkit.createLabel(body, "End:").setForeground(toolkit.getColors().getColor(IFormColors.TITLE)); toolkit.createLabel(body, committership.getEnd() != null ? DateFormat.getDateInstance().format(committership.getEnd()) : ""); toolkit.createLabel(body, "E-Mail:").setForeground(toolkit.getColors().getColor(IFormColors.TITLE)); if (committership.getPerson().getEmail() != null) { toolkit.createHyperlink(body, committership.getPerson().getEmail(), SWT.NONE).addHyperlinkListener(new IHyperlinkListener() { public void linkExited(HyperlinkEvent e) { // Ignore } public void linkEntered(HyperlinkEvent e) { // Ignore } public void linkActivated(HyperlinkEvent e) { Program.launch(committership.getPerson().getEmail()); } }); } else { toolkit.createLabel(body, ""); } return form; } } static void enableFor(ColumnViewer viewer, FormToolkit toolkit) { new ColumnViewerToolTipSupportImpl(toolkit, viewer, ToolTip.NO_RECREATE); } } /** * Set the focus */ public void setFocus() { viewer.getControl().setFocus(); if (viewer.getTree().getItemCount() > 0) { viewer.setSelection(new StructuredSelection(viewer.getTree().getItem(0).getData())); } } /** * Dispose all resources and remove all listeners */ public void dispose() { ICommandService service = (ICommandService)site.getService(ICommandService.class); service.removeExecutionListener(paramListener); if (projectImage != null) { projectImage.dispose(); } if (committerImage != null) { committerImage.dispose(); } } /** * @return the current selected committer */ public CommitterShip getCommitter() { Object obj = ((IStructuredSelection)viewer.getSelection()).getFirstElement(); return (CommitterShip)((obj instanceof CommitterShip) ? obj : null); } private class CommandListener implements IExecutionListener { public void preExecute(String commandId, ExecutionEvent event) { // Ignore } public void postExecuteSuccess(String commandId, Object returnValue) { if (site.getPage() != null && site.getPage().getActivePart() != null && site.getPage().getActivePart().getSite() == site) { if (commandId.equals(CreateToplevelProjectHandler.ID) && returnValue != null) { viewer.setSelection(new StructuredSelection(returnValue)); } else if (commandId.equals(CreateSublevelProjectHandler.ID) && returnValue != null) { Project project = (Project)returnValue; List<Project> path = new ArrayList<Project>(); path.add(project); while ((project = project.getParent()) != null) { path.add(0, project); } viewer.expandToLevel(new TreePath(path.toArray()), 0); viewer.setSelection(new TreeSelection(new TreePath(path.toArray()))); } } } public void postExecuteFailure(String commandId, ExecutionException exception) { // Ignore } public void notHandled(String commandId, NotHandledException exception) { // Ignore } } }