package org.nightlabs.jfire.issuetracking.trade.ui.issuelink; import java.util.Collection; import javax.jdo.FetchPlan; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.jface.action.Action; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.forms.editor.IFormPage; import org.eclipse.ui.forms.widgets.ExpandableComposite; import org.nightlabs.base.ui.composite.XComposite; import org.nightlabs.base.ui.composite.XComposite.LayoutMode; import org.nightlabs.base.ui.editor.ToolBarSectionPart; import org.nightlabs.base.ui.job.Job; import org.nightlabs.base.ui.notification.NotificationAdapterJob; import org.nightlabs.base.ui.resource.SharedImages; import org.nightlabs.base.ui.wizard.DynamicPathWizardDialog; import org.nightlabs.jdo.NLJDOHelper; import org.nightlabs.jfire.base.jdo.GlobalJDOManagerProvider; import org.nightlabs.jfire.base.jdo.notification.JDOLifecycleEvent; import org.nightlabs.jfire.base.jdo.notification.JDOLifecycleListener; import org.nightlabs.jfire.base.ui.jdo.notification.JDOLifecycleAdapterJob; import org.nightlabs.jfire.issue.Issue; import org.nightlabs.jfire.issue.IssueLink; import org.nightlabs.jfire.issue.dao.IssueLinkDAO; import org.nightlabs.jfire.issue.id.IssueID; import org.nightlabs.jfire.issue.id.IssueLinkID; import org.nightlabs.jfire.issuetracking.trade.ui.resource.Messages; import org.nightlabs.jfire.issuetracking.ui.issue.IssueTable; import org.nightlabs.jfire.issuetracking.ui.issuelink.attach.AttachIssueToObjectWizard; import org.nightlabs.jfire.jdo.notification.DirtyObjectID; import org.nightlabs.jfire.jdo.notification.IJDOLifecycleListenerFilter; import org.nightlabs.jfire.jdo.notification.JDOLifecycleState; import org.nightlabs.jfire.jdo.notification.SimpleLifecycleListenerFilter; import org.nightlabs.jfire.table.config.IColumnConfiguration; import org.nightlabs.jfire.trade.ArticleContainer; import org.nightlabs.notification.NotificationEvent; import org.nightlabs.notification.NotificationListener; import org.nightlabs.progress.ProgressMonitor; import org.nightlabs.progress.SubProgressMonitor; /** * This is the related Section called from the {@link ShowLinkedIssuePage}, containing the {@link IssueTable} that displays * the {@link Issue}s linked to the controllerObject, which in this case refers to the {@link ArticleContainer}. * * @author Chairat Kongarayawetchakun - chairat at nightlabs dot de * @author Khaireel Mohamed - khaireel at nightlabs dot de */ public class ShowLinkedIssueSection extends ToolBarSectionPart { private ShowLinkedIssuePageController controller; private IFormPage page; private IssueTable issueTable; private IColumnConfiguration columnConfiguration; private Job loadColumnConfigurationJob; private AddIssueLinkAction addIssueLinkAction; private IssueLinkActionRemove issueLinkActionRemove; /** * Creates a new instance of the ShowLinkedIssueSection. */ public ShowLinkedIssueSection(IFormPage page, Composite parent, final ShowLinkedIssuePageController controller) { super(page, parent, ExpandableComposite.EXPANDED | ExpandableComposite.TITLE_BAR, Messages.getString("org.nightlabs.jfire.issuetracking.trade.ui.issuelink.ShowLinkedIssueSection.title")); //$NON-NLS-1$ this.controller = controller; this.page = page; getSection().setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); getSection().setLayout(new GridLayout()); final XComposite client = new XComposite(getSection(), SWT.NONE, LayoutMode.TIGHT_WRAPPER); client.getGridLayout().numColumns = 1; issueTable = new IssueTable(client, SWT.NONE, false); issueTable.setLayoutData(new GridData(GridData.FILL_BOTH)); issueTable.addSelectionChangedListener(new ISelectionChangedListener(){ @Override public void selectionChanged(SelectionChangedEvent event) { handleRemoveActionButton(); } }); loadColumnConfigurationJob = new Job("Load Column Configuration") { @Override protected IStatus run(ProgressMonitor monitor) throws Exception { columnConfiguration = IssueTable.getDefaultColumnConfiguration(monitor); if (!client.isDisposed()) { client.getDisplay().asyncExec(new Runnable() { @Override public void run() { issueTable.setIssueTableConfigurations(columnConfiguration); issueTable.layout(true, true); } }); } return Status.OK_STATUS; } }; loadColumnConfigurationJob.schedule(); // See notes in [Observation and strategy, 23.06.2009] in ShowLinkedIssuePageController. GlobalJDOManagerProvider.sharedInstance().getLifecycleManager().addLifecycleListener(issueLinksLifeCycleListener); GlobalJDOManagerProvider.sharedInstance().getLifecycleManager().addNotificationListener(Issue.class, issueChangeNotificationListener); issueTable.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent event) { GlobalJDOManagerProvider.sharedInstance().getLifecycleManager().removeLifecycleListener(issueLinksLifeCycleListener); GlobalJDOManagerProvider.sharedInstance().getLifecycleManager().removeNotificationListener(Issue.class, issueChangeNotificationListener); } }); // The Action buttons to perform the interface for adding and removing IssueLinks. addIssueLinkAction = new AddIssueLinkAction(); getToolBarManager().add( addIssueLinkAction ); issueLinkActionRemove = new IssueLinkActionRemove(issueTable, controller, page); getToolBarManager().add( issueLinkActionRemove ); updateToolBarManager(); getSection().setClient(client); } /** * Makes the Action button enabled if and only if there is at least one selected item from the {@link IssueTable}. */ protected void handleRemoveActionButton() { assert issueLinkActionRemove != null; issueLinkActionRemove.setEnabled( issueTable.getSelectionIndex() >= 0 ); } // -----------------------------------------------------------------------------------------------------------------------------------| // ---[ Lifecycle and Notification listeners: IssueLinks and Issues ] ----------------------------------------------------------------| // -----------------------------------------------------------------------------------------------------------------------------------| // We require the explicit listener here, to monitor any additions or removal of IssueLinks. // See notes in [Observation and strategy, 23.06.2009] in ShowLinkedIssuePageController. private JDOLifecycleListener issueLinksLifeCycleListener = new JDOLifecycleAdapterJob(Messages.getString("org.nightlabs.jfire.issuetracking.trade.ui.issuelink.ShowLinkedIssueSection.job.name.checkingIssueLinks")) { //$NON-NLS-1$ private IJDOLifecycleListenerFilter filter = new SimpleLifecycleListenerFilter( IssueLink.class, false, JDOLifecycleState.NEW, JDOLifecycleState.DELETED ); @Override public IJDOLifecycleListenerFilter getJDOLifecycleListenerFilter() { return filter; } @Override public void notify(JDOLifecycleEvent event) { ProgressMonitor monitor = getProgressMonitor(); monitor.beginTask(Messages.getString("org.nightlabs.jfire.issuetracking.trade.ui.issuelink.ShowLinkedIssueSection.task.updatingIssueLinks"), 100); //$NON-NLS-1$ try { // Perform the sifting here. for (DirtyObjectID dirtyObjectID : event.getDirtyObjectIDs()) { IssueLink issueLink = IssueLinkDAO.sharedInstance().getIssueLink( (IssueLinkID)dirtyObjectID.getObjectID(), new String[] {FetchPlan.DEFAULT, IssueLink.FETCH_GROUP_LINKED_OBJECT}, // <-- Seems enough without having to load IssueType. NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT, new SubProgressMonitor(monitor, 10) ); // If any single one of the DirtyObjects within the carrier points to the <linkedObject> known by // the controller, then we stop the loop, and perform a single doLoad(). JDOLifecycleState lState = dirtyObjectID.getLifecycleState(); // <-- Bad... how does one deal with deleted objects? I need to access the linkedObject from the deleted IssueLink. Check with jdoPreDelete()? if ( lState.equals(JDOLifecycleState.DELETED) || issueLink.getLinkedObjectID().equals( controller.getArticleContainerID() ) ) { controller.doLoad(new SubProgressMonitor(monitor, 70)); break; } } } finally { monitor.done(); } } }; // And now, we require the implicit listener for that handles the Issue items in the IssueTable. private NotificationListener issueChangeNotificationListener = new NotificationAdapterJob() { @Override public void notify(final NotificationEvent event) { final ProgressMonitor monitor = getProgressMonitor(); monitor.beginTask(Messages.getString("org.nightlabs.jfire.issuetracking.trade.ui.issuelink.ShowLinkedIssueSection.task.updatingIssue"), 100); //$NON-NLS-1$ try { if (!getSection().isDisposed()) { getSection().getDisplay().asyncExec(new Runnable(){ @Override public void run() { // Check to see if any of the dirty Issues notified belong in our table. // If so, refresh the entry. Collection<Issue> issues = issueTable.getElements(); if (issues.isEmpty()) return; Collection<IssueID> issueIDs = NLJDOHelper.getObjectIDList(issues); for (Object obj : event.getSubjects()) { if (obj == null) continue; DirtyObjectID dirtyObjectID = (DirtyObjectID) obj; if (dirtyObjectID.getLifecycleState().equals( JDOLifecycleState.DIRTY ) && issueIDs.contains(dirtyObjectID.getObjectID())) { controller.doLoad(new SubProgressMonitor(monitor, 90)); break; } } } }); } } finally { monitor.done(); } } }; // -----------------------------------------------------------------------------------------------------------------------------------| // ---[ Routines to handle the IssueTable contents ] ---------------------------------------------------------------------------------| // -----------------------------------------------------------------------------------------------------------------------------------| /** * Sets the {@link Issue}s linked to the {@link ArticleContainer}, and updates the remove-Action-button. */ public void setLinkedIssues(final Collection<Issue> issues) { if (loadColumnConfigurationJob.getResult() == null){ // job is not finished loadColumnConfigurationJob.addJobChangeListener(new JobChangeAdapter(){ @Override public void done(IJobChangeEvent event) { if (Status.OK_STATUS == event.getResult()){ getSection().getShell().getDisplay().asyncExec(new Runnable() { @Override public void run() { setLinkedIssues(issues); } }); } super.done(event); } }); }else{ issueTable.setInput(issues); handleRemoveActionButton(); } } /** * Sets the {@link Issue}s linked to the {@link ArticleContainer}, and updates the remove-Action-button, and * highlights the {@link Issue} identified by selectedIssue. */ public void setLinkedIssues(Collection<Issue> issues, Issue selectedIssue) { setLinkedIssues(issues); if (selectedIssue != null) highlightIssueEntry(selectedIssue); } /** * @return the {@link IssueTable} from this Section. */ public IssueTable getIssueTable() { return issueTable; } /** * Highlights the entry in the {@link IssueTable} matching the given {@link Issue}. * Table will contain no highlight if no match is found. * FIXME Something is still wrong here... Wont highlight the latest entry. */ protected void highlightIssueEntry(Issue issue) { int index = -1; issueTable.getTableViewer().getTable().setSelection(index); Collection<Issue> issues = issueTable.getElements(); for(Issue issueElem : issues) { index++; if ( issueElem.equals(issue) ) { issueTable.getTableViewer().getTable().setSelection(index); break; } } } // -----------------------------------------------------------------------------------------------------------------------------------| /** * Handles the action to add a new {@link IssueLink} to an {@link Issue}. */ private class AddIssueLinkAction extends Action { public AddIssueLinkAction() { setId(AddIssueLinkAction.class.getName()); setImageDescriptor(SharedImages.ADD_16x16); setToolTipText(Messages.getString("org.nightlabs.jfire.issuetracking.trade.ui.issuelink.ShowLinkedIssueSection.addIssueLinkAction.tooltip")); //$NON-NLS-1$ setText(Messages.getString("org.nightlabs.jfire.issuetracking.trade.ui.issuelink.ShowLinkedIssueSection.addIssueLinkAction.text")); //$NON-NLS-1$ } @Override public void run() { Object linkedObject = controller.getArticleContainer(); AttachIssueToObjectWizard attachIssueToObjectWizard = new AttachIssueToObjectWizard(linkedObject); DynamicPathWizardDialog dialog = new DynamicPathWizardDialog(getSection().getShell(), attachIssueToObjectWizard) { @Override protected Point getInitialSize() { return new Point(convertHorizontalDLUsToPixels(600), convertVerticalDLUsToPixels(450)); } }; dialog.open(); // Update the table in the Section. if (dialog.getReturnCode() == Window.OK) { // != Window.CANCEL) { ((ShowLinkedIssuePage)page).setHighlightIssueEntry( attachIssueToObjectWizard.getSelectedIssue() ); // <-- Attempt to highlight the latest entry in the table. // controller.doLoad(new NullProgressMonitor()); // <-- This shall be delegated to the issueLinksLifeCycleListener. } } } }