package org.nightlabs.jfire.personrelation.issuetracking.trade.ui.view; import java.util.Collections; import javax.jdo.FetchPlan; import javax.jdo.JDOHelper; import javax.security.auth.login.LoginException; import org.apache.log4j.Logger; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.TreeItem; import org.nightlabs.base.ui.notification.NotificationAdapterJob; import org.nightlabs.base.ui.notification.SelectionManager; import org.nightlabs.base.ui.resource.SharedImages; import org.nightlabs.jdo.NLJDOHelper; import org.nightlabs.jdo.ObjectID; import org.nightlabs.jfire.base.JFireEjb3Factory; import org.nightlabs.jfire.base.login.ui.Login; import org.nightlabs.jfire.person.Person; import org.nightlabs.jfire.personrelation.PersonRelation; import org.nightlabs.jfire.personrelation.issuetracking.trade.ui.Activator; import org.nightlabs.jfire.personrelation.issuetracking.trade.ui.action.CreateIssueCommentAction; import org.nightlabs.jfire.personrelation.issuetracking.trade.ui.action.CreateOrLinkIssueAction; import org.nightlabs.jfire.personrelation.issuetracking.trade.ui.action.CreatePersonRelationAction; import org.nightlabs.jfire.personrelation.issuetracking.trade.ui.action.DeletePersonRelationAction; import org.nightlabs.jfire.personrelation.issuetracking.trade.ui.action.OpenIssueEditorAction; import org.nightlabs.jfire.personrelation.issuetracking.trade.ui.resource.Messages; import org.nightlabs.jfire.personrelation.issuetracking.trade.ui.tree.IssueCommentPersonRelationTreeLabelProviderDelegate; import org.nightlabs.jfire.personrelation.issuetracking.trade.ui.tree.IssueDescriptionPersonRelationTreeLabelProviderDelegate; import org.nightlabs.jfire.personrelation.issuetracking.trade.ui.tree.IssueLinkPersonRelationTreeLabelProviderDelegate; import org.nightlabs.jfire.personrelation.issuetracking.trade.ui.tree.IssuePersonRelationTreeControllerDelegate; import org.nightlabs.jfire.personrelation.ui.AbstractPersonRelationTreeView; import org.nightlabs.jfire.personrelation.ui.tree.PersonRelationTree; import org.nightlabs.jfire.personrelation.ui.tree.PersonRelationTreeNode; import org.nightlabs.jfire.personrelation.ui.tree.PersonRelationTreeUtil; import org.nightlabs.jfire.prop.id.PropertySetID; import org.nightlabs.jfire.trade.LegalEntity; import org.nightlabs.jfire.trade.TradeManagerRemote; import org.nightlabs.jfire.trade.dao.LegalEntityDAO; import org.nightlabs.jfire.trade.ui.TradePlugin; import org.nightlabs.jfire.transfer.id.AnchorID; import org.nightlabs.notification.NotificationEvent; import org.nightlabs.notification.NotificationListener; /** * This is shall always stand to be JFire's original {@link PersonRelationIssueTreeView}, with all its original conceptual behaviours intact. * There is an extended version of this, the {@link HierarchicalPersonRelationIssueTreeView} which serves as a platform used as a sandbox to test the new ideas * and consolidate our experiences from the development of BEHR's specific {@link PersonRelationTree}. * * Since 2010.03.17, we have restored the original behaviours of this {@link PersonRelationIssueTreeView} to the pre-BEHR era. Kai. * TODO In the jfire_1.0 branch, the original behaviours of the PersonRelationIssueTreeView was (unconstitutionally) changed when working on the BEHR project. Restore it too! * * @author Marco หงุ่ยตระ�ูล-Schulze - marco at nightlabs dot de * @author khaireel (at) nightlabs (dot) de */ public class PersonRelationIssueTreeView extends AbstractPersonRelationTreeView<PersonRelationTreeNode, PersonRelationTree<PersonRelationTreeNode>> { private static final Logger logger = Logger.getLogger(PersonRelationIssueTreeView.class); public static final String ID_VIEW = PersonRelationIssueTreeView.class.getName(); // The currentPersonID that has focus. protected PropertySetID currentPersonID = null; /** * Creates a new instance of the PersonRelationTree, and initialises it as completely as possible; i.e. supply the * delegates it requires. */ @Override protected PersonRelationTree<PersonRelationTreeNode> createAndInitPersonRelationTree(Composite parent) { PersonRelationTree<PersonRelationTreeNode> personRelationTree = new PersonRelationTree<PersonRelationTreeNode>(parent, false, // without restoring the tree's collapse state true, // with context menu(s) true // with the drill-down adapter. ); // Delegate controller and label-providers for handling Issues in the PersonRelationTree. personRelationTree.getPersonRelationTreeController().addPersonRelationTreeControllerDelegate(new IssuePersonRelationTreeControllerDelegate()); personRelationTree.addPersonRelationTreeLabelProviderDelegate(new IssueLinkPersonRelationTreeLabelProviderDelegate()); personRelationTree.addPersonRelationTreeLabelProviderDelegate(new IssueDescriptionPersonRelationTreeLabelProviderDelegate()); personRelationTree.addPersonRelationTreeLabelProviderDelegate(new IssueCommentPersonRelationTreeLabelProviderDelegate()); return personRelationTree; } /** * Set up the ORDERED set of context-menus into the {@link PersonRelationTree}. */ @Override protected void registerContextMenuContibutions(PersonRelationTree<PersonRelationTreeNode> personRelationTree) { personRelationTree.addContextMenuContribution(new SelectBusinessPartnerTreeItemAction("Focus trade on this business partner")); personRelationTree.addContextMenuContribution(this, new CreatePersonRelationAction(), "Create new person relation", SharedImages.getSharedImageDescriptor(Activator.getDefault(), CreatePersonRelationAction.class)); personRelationTree.addContextMenuContribution(this, new DeletePersonRelationAction(), "Delete person relation", SharedImages.getSharedImageDescriptor(Activator.getDefault(), DeletePersonRelationAction.class)); personRelationTree.addContextMenuContribution(new OpenIssueEditorAction()); personRelationTree.addContextMenuContribution(this, new CreateOrLinkIssueAction(), "Create new or link existing issue", SharedImages.getSharedImageDescriptor(Activator.getDefault(), CreateOrLinkIssueAction.class)); personRelationTree.addContextMenuContribution(this, new CreateIssueCommentAction(), "Create new issue comment", SharedImages.getSharedImageDescriptor(Activator.getDefault(), CreateIssueCommentAction.class)); } /** * Initialises all other listeners that the {@link PersonRelationTree} requires for its fundamental operational behaviour. */ @Override protected void initPersonRelationTreeListeners(PersonRelationTree<PersonRelationTreeNode> personRelationTree) { // Notifies other view(s) that may wish to react upon the current selection in the tree, in the TradePlugin.ZONE_SALE. personRelationTree.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { if (event.getSelection().isEmpty()) return; Object selectedElement = getPersonRelationTree().getFirstSelectedElement(); if (selectedElement instanceof PersonRelationTreeNode) { PersonRelationTreeNode node = (PersonRelationTreeNode) selectedElement; if (logger.isInfoEnabled() && node != null) { logger.info(PersonRelationTreeUtil.showObjectIDs("propertySetIDsToRoot", node.getPropertySetIDsToRoot(), 5)); //$NON-NLS-1$ } if (node != null) { // Kai: It is possible that the selected treeNode does not contain a Person-related object (e.g. Issue, IssueComment, etc.). // But we may be interested of the Person-related object to which the selected treeNode belongs to. // --> Thus, in this case, we traverse up the parent until we get to a node representing a Person-related object. // --> This iterative traversal always have a base case, since the root node(s) in the PersonRelationTree is always a Person-related object. node = traverseUpUntilPerson(node); PropertySetID personID = node.getPropertySetID(); if (personID != null) SelectionManager.sharedInstance().notify(new NotificationEvent(this, TradePlugin.ZONE_SALE, personID, Person.class)); } } } }); // Automatically expand the root node. personRelationTree.getTree().addListener(SWT.SetData, new Listener() { @Override public void handleEvent(Event event) { if (!(event.item instanceof TreeItem)) return; TreeItem treeItem = (TreeItem) event.item; if (treeItem == null || !(treeItem.getData() instanceof PersonRelationTreeNode)) return; PersonRelationTreeNode node = (PersonRelationTreeNode) treeItem.getData(); if (node == null) return; treeItem.setExpanded(node.getParent() == null || node.getParent().getParent() == null); // ... phew... bloody lazy tree nodes..... } }); } /** * Creates a NotificationListener that defines the behaviour of this View with respect to whatever Perspective. */ @Override protected NotificationListener createAndRegisterNotificationListenerLegalEntitySelected(PersonRelationTree<PersonRelationTreeNode> personRelationTree) { final NotificationListener notificationListener = new NotificationAdapterJob(Messages.getString("org.nightlabs.jfire.personrelation.issuetracking.trade.ui.PersonRelationIssueTreeView.selectLegalEntityJob.title")) //$NON-NLS-1$ { public void notify(org.nightlabs.notification.NotificationEvent notificationEvent) { Object subject = notificationEvent.getFirstSubject(); PropertySetID personID = null; if ( !(subject instanceof PropertySetID) ) { AnchorID legalEntityID = (AnchorID) notificationEvent.getFirstSubject(); LegalEntity legalEntity = null; if (legalEntityID != null) { legalEntity = LegalEntityDAO.sharedInstance().getLegalEntity( legalEntityID, new String[] { FetchPlan.DEFAULT, LegalEntity.FETCH_GROUP_PERSON }, NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT, getProgressMonitor() ); } personID = (PropertySetID) (legalEntity == null ? null : JDOHelper.getObjectId(legalEntity.getPerson())); } else personID = (PropertySetID) subject; // Ensures that we don't unnecessarily retrieve the relationRootNodes for one that has already been retrieved and on display. if (personID != null && (currentPersonID == null || currentPersonID != personID)) { currentPersonID = personID; getPersonRelationTree().getDisplay().asyncExec(new Runnable() { public void run() { if (!getPersonRelationTree().isDisposed()) getPersonRelationTree().setInputPersonIDs(Collections.singleton(currentPersonID)); } }); } } }; SelectionManager.sharedInstance().addNotificationListener( TradePlugin.ZONE_SALE, LegalEntity.class, notificationListener ); personRelationTree.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent event) { SelectionManager.sharedInstance().removeNotificationListener( TradePlugin.ZONE_SALE, LegalEntity.class, notificationListener ); } }); return notificationListener; } /** * Given a set of {@link PropertySetID}s, invoke the {@link TradeManagerRemote} to retrieve the respective * {@link AnchorID}s, and subsequently triggers a {@link NotificationEvent} through the {@link TradePlugin}.ZONE_SALE (for example), * by passing the {@link AnchorID}s in order of appearance from the input {@link PropertySetID}s. */ @Override protected void prepareAndHandleNotification(PropertySetID... propertySetIDs) { try { TradeManagerRemote tm = JFireEjb3Factory.getRemoteBean(TradeManagerRemote.class, Login.getLogin().getInitialContextProperties()); AnchorID[] anchorIDs = new AnchorID[propertySetIDs.length]; for (int i=0; i<propertySetIDs.length; i++) { LegalEntity le = propertySetIDs[i] == null ? null : tm.getLegalEntityForPerson(propertySetIDs[i], new String[] {FetchPlan.DEFAULT}, NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT); anchorIDs[i] = (AnchorID) (le == null ? null : JDOHelper.getObjectId(le)); } SelectionManager.sharedInstance().notify( new NotificationEvent(this, TradePlugin.ZONE_SALE, anchorIDs, new Class<?>[] {LegalEntity.class})); } catch (LoginException e) { throw new RuntimeException(e); } } /** * Traverses up the tree in search for the first node containing a representation of a Person-related object. * @return the first node it finds containing a representation of a Person-related object. */ protected PersonRelationTreeNode traverseUpUntilPerson(PersonRelationTreeNode node) { ObjectID jdoObjectID = node.getJdoObjectID(); Object jdoObject = node.getJdoObject(); if (jdoObject != null && jdoObjectID != null) { // Base case. if (jdoObjectID instanceof PropertySetID || jdoObject instanceof PersonRelation) return node; // Iterative case. return traverseUpUntilPerson(node.getParent()); } return node; } }