package org.nightlabs.jfire.issuetracking.ui.issuelink.person; import java.awt.Dimension; import java.io.ByteArrayInputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Set; import javax.jdo.FetchPlan; import javax.jdo.JDOHelper; import org.apache.log4j.Logger; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.viewers.ColumnViewer; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableLayout; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.ui.IWorkbenchPartSite; import org.eclipse.ui.part.DrillDownAdapter; import org.nightlabs.base.ui.editor.Editor2PerspectiveRegistry; import org.nightlabs.base.ui.job.Job; import org.nightlabs.base.ui.labelprovider.ColumnSpanLabelProvider; import org.nightlabs.base.ui.notification.NotificationAdapterJob; import org.nightlabs.base.ui.tree.AbstractTreeComposite; import org.nightlabs.base.ui.tree.TreeContentProvider; import org.nightlabs.jdo.NLJDOHelper; import org.nightlabs.jdo.ObjectID; import org.nightlabs.jfire.base.JFireEjb3Factory; import org.nightlabs.jfire.base.jdo.notification.JDOLifecycleManager; import org.nightlabs.jfire.base.login.ui.Login; import org.nightlabs.jfire.issue.Issue; import org.nightlabs.jfire.issue.IssueComment; import org.nightlabs.jfire.issue.IssueDescription; import org.nightlabs.jfire.issue.IssueLink; import org.nightlabs.jfire.issue.IssueManagerRemote; import org.nightlabs.jfire.issue.dao.IssueLinkDAO; import org.nightlabs.jfire.issue.id.IssueID; import org.nightlabs.jfire.issue.issuemarker.IssueMarker; import org.nightlabs.jfire.issue.issuemarker.id.IssueMarkerID; import org.nightlabs.jfire.issuetracking.ui.issue.editor.IssueEditor; import org.nightlabs.jfire.issuetracking.ui.issue.editor.IssueEditorInput; import org.nightlabs.jfire.issuetracking.ui.resource.Messages; import org.nightlabs.jfire.person.Person; import org.nightlabs.notification.NotificationEvent; import org.nightlabs.notification.NotificationListener; import org.nightlabs.progress.ProgressMonitor; import org.nightlabs.progress.SubProgressMonitor; /** * @author Fitas Amine - fitas at nightlabs dot de * */ public class PersonIssueLinkTreeComposite extends AbstractTreeComposite implements IIssueLinkSelection { /** * LOG4J logger used by this class */ private static final Logger logger = Logger.getLogger(PersonIssueLinkTreeComposite .class); private IssueLinkTreeNode rootlegalEntityIssuesLinkNode = null; private static final Object[] EMPTY_DATA = new Object[]{}; // private LegalEntity partner = null; private Person person = null; private Object selectedNode = null; private DrillDownAdapter drillDownAdapter; private IWorkbenchPartSite site; private Collection<IssueMarker> issueMarkers; private Collection<Image> iconImages = new ArrayList<Image>(); private static final String[] FETCH_GROUPS_ISSUESLINK = new String[] { FetchPlan.DEFAULT, IssueLink.FETCH_GROUP_ISSUE, Issue.FETCH_GROUP_ISSUE_TYPE, Issue.FETCH_GROUP_SUBJECT, Issue.FETCH_GROUP_DESCRIPTION, Issue.FETCH_GROUP_ISSUE_COMMENTS, Issue.FETCH_GROUP_ISSUE_MARKERS, Issue.FETCH_GROUP_ISSUE_LINKS, Issue.FETCH_GROUP_DESCRIPTION, IssueMarker.FETCH_GROUP_NAME, IssueMarker.FETCH_GROUP_ICON_16X16_DATA, IssueComment.FETCH_GROUP_TEXT}; public PersonIssueLinkTreeComposite(Composite parent, int style, IWorkbenchPartSite site) { super(parent, style); this.site = site; init(); JDOLifecycleManager.sharedInstance().addNotificationListener(Issue.class, issueChangeListener); addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent event) { JDOLifecycleManager.sharedInstance().removeNotificationListener( Issue.class, issueChangeListener); } }); getTreeViewer().addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { // if the selection is empty clear the label if(event.getSelection().isEmpty()) return; StructuredSelection s = (StructuredSelection)event.getSelection(); selectedNode = s.getFirstElement(); } }); // open up the Issue in the Issue Editor. getTreeViewer().addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(DoubleClickEvent e) { StructuredSelection s = (StructuredSelection)e.getSelection(); if (s.isEmpty()) return; Object o = s.getFirstElement(); if (o instanceof IssueLink) { Issue issue = ((IssueLink)o).getIssue(); IssueEditorInput issueEditorInput = new IssueEditorInput(IssueID.create(issue.getOrganisationID(), issue.getIssueID())); try { Editor2PerspectiveRegistry.sharedInstance().openEditor(issueEditorInput, IssueEditor.EDITOR_ID); } catch (Exception e1) { throw new RuntimeException(e1); } } } } ); drillDownAdapter = new DrillDownAdapter(getTreeViewer()); getTreeViewer().addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { }}); // Load the IssueMarkers references. Job job = new Job(Messages.getString(Messages.getString("org.nightlabs.jfire.issuetracking.ui.issuelink.person.PersonIssueLinkTreeComposite.job.loadIssueMarkers"))) { //$NON-NLS-1$ @Override protected IStatus run(ProgressMonitor monitor) throws Exception { IssueManagerRemote imr = null; try { imr = JFireEjb3Factory.getRemoteBean(IssueManagerRemote.class, Login.getLogin().getInitialContextProperties()); } catch (Exception e) { throw new RuntimeException(e); } Set<IssueMarkerID> issueMarkerIDs = imr.getIssueMarkerIDs(); issueMarkers = imr.getIssueMarkers( issueMarkerIDs, new String[] {FetchPlan.DEFAULT, IssueMarker.FETCH_GROUP_NAME, IssueMarker.FETCH_GROUP_DESCRIPTION, IssueMarker.FETCH_GROUP_ICON_16X16_DATA}, NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT ); Display.getDefault().asyncExec(new Runnable() { @Override public void run() { createContextMenu(); } }); return Status.OK_STATUS; } }; job.setPriority(Job.SHORT); job.schedule(); } public Object getSelectedNode() { return selectedNode; } protected void fillContextMenu(IMenuManager manager) { drillDownAdapter.addNavigationActions(manager); // Other plug-ins can contribute their actions here Set<IssueMarker>selectedIssueMarkers = null; // get the already selected Issue Markers if(selectedNode!= null && selectedNode instanceof IssueLink) { Issue issue = ((IssueLink)selectedNode).getIssue(); selectedIssueMarkers = issue.getIssueMarkers(); } for(IssueMarker issueMarker:issueMarkers) { AddIssueMarkerMenuAction addIssueMarkerMenuAction = new AddIssueMarkerMenuAction(); addIssueMarkerMenuAction.init(this, issueMarker); manager.add(addIssueMarkerMenuAction); if(selectedIssueMarkers!= null) addIssueMarkerMenuAction.setChecked(selectedIssueMarkers.contains(issueMarker)); } } private void createContextMenu() { MenuManager menuMgr = new MenuManager(Messages.getString("org.nightlabs.jfire.issuetracking.ui.issuelink.person.PersonIssueLinkTreeComposite.menu.markers")); //$NON-NLS-1$ menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { fillContextMenu(manager); } }); Menu menu = menuMgr.createContextMenu(getTreeViewer().getControl()); getTreeViewer().getControl().setMenu(menu); site.registerContextMenu(menuMgr, getTreeViewer()); } private NotificationListener issueChangeListener = new NotificationAdapterJob() { public void notify(NotificationEvent evt) { logger.info("changeListener got notified with event "+evt); //$NON-NLS-1$ ProgressMonitor monitor = getProgressMonitor(); monitor.beginTask(Messages.getString("org.nightlabs.jfire.issuetracking.ui.issuelink.person.PersonIssueLinkTreeComposite.task.refreshNodes"), 100); //$NON-NLS-1$ setRootNode(person, new SubProgressMonitor(monitor, 100));} }; private class ContentProvider extends TreeContentProvider { public Object[] getElements(Object inputElement) { return getChildren(inputElement); } /** * @see org.nightlabs.base.ui.tree.TreeContentProvider#getChildren(java.lang.Object) */ @Override public Object[] getChildren(Object parentElement) { if (parentElement instanceof IssueLinkTreeNode) return ((IssueLinkTreeNode)parentElement).getChildNodes(); if (parentElement instanceof IssueLink) { final List<IssueComment> commentsList = ((IssueLink)parentElement).getIssue().getComments(); Collections.sort(commentsList, new Comparator<IssueComment>() { public int compare(IssueComment o1, IssueComment o2) { // chronological Order return o1.getCreateTimestamp().compareTo(o2.getCreateTimestamp()); } }); // for empty string description no need to add the desc node if(((IssueLink)parentElement).getIssue().getDescription().getText().isEmpty()) return commentsList.toArray(); // concatnate the Comments nodes with the Desc Object[] arrayDesc = new Object[] {((IssueLink)parentElement).getIssue().getDescription()}; return concat(arrayDesc,commentsList.toArray()); } return EMPTY_DATA; } protected Object[] concat(Object[] object, Object[] more) { Object[] both = new Object[object.length + more.length]; System.arraycopy(object, 0, both, 0, object.length); System.arraycopy(more, 0, both, object.length, more.length); return both; } /** * @see org.nightlabs.base.ui.tree.TreeContentProvider#hasChildren(java.lang.Object) */ @Override public boolean hasChildren(Object element) { if (element instanceof IssueLinkTreeNode) return ((IssueLinkTreeNode)element).hasChildren(); if (element instanceof IssueLink) return ((IssueLink)element).getIssue().getComments().size() > 0|| !((IssueLink)element).getIssue().getDescription().getText().isEmpty(); if (element instanceof IssueComment||element instanceof IssueDescription) return false; else return true; } @Override public void dispose() { for (Image image : iconImages) image.dispose(); iconImages.clear(); } } private static Dimension ISSUE_MARKER_IMAGE_DIMENSION = new Dimension(16, 16); private class LabelProvider extends ColumnSpanLabelProvider { public LabelProvider(ColumnViewer columnViewer) { super(columnViewer); } // extends TableLabelProvider { @Override public String getColumnText(Object element, int columnIndex) { if(columnIndex == 0) return getText(element); else return ""; //$NON-NLS-1$ } @Override public Image getColumnImage(Object element, int columnIndex) { // if (element instanceof IssueLinkTreeNode && columnIndex==0) // return ((IssueLinkTreeNode)element).getIcon(); // if (element instanceof IssueLink&&columnIndex == 1) // return getCombiIssueMarkerImage(((IssueLink)element).getIssue()); return null; } protected Image getCombiIssueMarkerImage(Issue issue) { int maxIssueMarkerCountPerIssue; if (issue.getIssueMarkers().size() > 0) maxIssueMarkerCountPerIssue = issue.getIssueMarkers().size(); else return null; Image combinedIcon = new Image(getDisplay(), ISSUE_MARKER_IMAGE_DIMENSION.width * maxIssueMarkerCountPerIssue + maxIssueMarkerCountPerIssue - 1, ISSUE_MARKER_IMAGE_DIMENSION.height); // FIXME TODO RAP commented because of incompatibility with RAP // GC gc = new GC(combinedIcon); GC gc = new GC(combinedIcon.getDevice()); Image icon = null; int i = 0; try { for(IssueMarker issueMarker:issue.getIssueMarkers()) { if (issueMarker.getIcon16x16Data() != null) { ByteArrayInputStream in = new ByteArrayInputStream(issueMarker.getIcon16x16Data()); icon = new Image(getDisplay(), in); gc.drawImage(icon, ISSUE_MARKER_IMAGE_DIMENSION.width * i + i, 0); iconImages.add(icon); } i++; } } finally { gc.dispose(); } iconImages.add(combinedIcon); return combinedIcon; } /** * @see org.nightlabs.base.ui.table.TableLabelProvider#getText(java.lang.Object) */ public String getText(Object element) { if (element instanceof IssueLinkTreeNode) return ((IssueLinkTreeNode)element).getName(); if (element instanceof IssueLink) // return String.format(Messages.getString("org.nightlabs.jfire.issuetracking.trade.ui.issuelink.person.PersonIssueLinkTreeComposite.2"), //$NON-NLS-1$ // ((IssueLink)element).getIssue().getIssueIDAsString(), // ((IssueLink)element).getIssue().getSubject().getText()); return ((IssueLink)element).getIssue().getSubject().getText(); if (element instanceof IssueComment) return ((IssueComment)element).getText(); if (element instanceof IssueDescription) return ((IssueDescription)element).getText(); return ""; //$NON-NLS-1$ } @Override protected int[][] getColumnSpan(Object element) { // TODO Auto-generated method stub return null; } @Override public void update(ViewerCell cell) { // TODO Auto-generated method stub } } public void setRootNode(Person person, ProgressMonitor monitor) { this.person = person; final ObjectID personID = (ObjectID) JDOHelper.getObjectId(person); // <-- FIXME Problem here triggered from the IssueTracking perspective... final Collection<IssueLink> links = IssueLinkDAO.sharedInstance().getIssueLinks( personID, FETCH_GROUPS_ISSUESLINK, NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT, new SubProgressMonitor(monitor, 80) ); this.rootlegalEntityIssuesLinkNode = new IssueLinkTreeNode(Messages.getString("org.nightlabs.jfire.issuetracking.ui.issuelink.person.PersonIssueLinkTreeComposite.node.name"), null) //$NON-NLS-1$ { @Override public Object[] getChildNodes() { return links.toArray(); } @Override public boolean hasChildren() { return !links.isEmpty(); } }; Display.getDefault().asyncExec(new Runnable() { public void run() { getTreeViewer().setInput(rootlegalEntityIssuesLinkNode); if(selectedNode != null) getTreeViewer().expandToLevel(selectedNode,1); else getTreeViewer().expandToLevel(1); } }); monitor.worked(20); } /** * @see org.nightlabs.base.ui.tree.AbstractTreeComposite#setTreeProvider(org.eclipse.jface.viewers.TreeViewer) */ @Override public void setTreeProvider(TreeViewer treeViewer) { treeViewer.setContentProvider(new ContentProvider()); treeViewer.setLabelProvider(new LabelProvider(treeViewer)); if (rootlegalEntityIssuesLinkNode != null) { treeViewer.setInput(rootlegalEntityIssuesLinkNode); } } @Override public void createTreeColumns(Tree tree) { TreeColumn column1 = new TreeColumn(tree, SWT.LEFT); TreeColumn column2 = new TreeColumn(tree, SWT.LEFT); TableLayout l = new TableLayout(); l.addColumnData(new ColumnWeightData(3)); l.addColumnData(new ColumnWeightData(1)); tree.setLayout(l); getTree().setHeaderVisible(false); } @Override public IssueLink getSelectedIssueLink() { if (getSelectedNode() instanceof IssueLink) { return (IssueLink) getSelectedNode(); } return null; } }