package org.nightlabs.jfire.issuetracking.ui.issue; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; import java.util.List; import javax.jdo.FetchPlan; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableLayout; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.nightlabs.base.ui.editor.Editor2PerspectiveRegistry; import org.nightlabs.base.ui.labelprovider.ColumnSpanLabelProvider; import org.nightlabs.base.ui.layout.WeightedTableLayout; import org.nightlabs.base.ui.table.AbstractTableComposite; import org.nightlabs.jdo.NLJDOHelper; import org.nightlabs.jfire.base.ui.config.ConfigUtil; import org.nightlabs.jfire.issue.Issue; import org.nightlabs.jfire.issue.config.IssueTableConfigModule; import org.nightlabs.jfire.issue.id.IssueID; import org.nightlabs.jfire.issuetracking.ui.issue.editor.IssueEditor; import org.nightlabs.jfire.issuetracking.ui.issue.editor.IssueEditorInput; import org.nightlabs.jfire.table.config.ColumnDescriptor; import org.nightlabs.jfire.table.config.IColumnConfiguration; import org.nightlabs.jfire.table.config.IColumnContentDescriptor; import org.nightlabs.jfire.table.config.IColumnDescriptor; import org.nightlabs.progress.NullProgressMonitor; import org.nightlabs.progress.ProgressMonitor; /** * The table used for listing {@link Issue} elements. * * @author Chairat Kongarayawetchakun - chairat[at]nightlabs[dot]de * @author Khaireel Mohamed - khaireel at nightlabs dot de * @author marco schulze - marco at nightlabs dot de */ public class IssueTable extends AbstractTableComposite<Issue> { // There now exists an IssueTableConfigModule, which configures the columns of the IssueTable. // In that same config-module, there also exists all the necessary fetch-groups from which we // would need, based on the ColumnDescriptors that we have configured to want to use. private List<? extends IColumnContentDescriptor> columnContentDescriptors = null; private ConfigurableIssueTableLabelProvider issueTableLabelProvider; /** * Loads default column configuration which earlier was loaded directly in deprecated IssueTable constructor. * * @param monitor The {@link ProgressMonitor} to use * @return loaded column configuration */ public static IColumnConfiguration getDefaultColumnConfiguration(ProgressMonitor monitor){ IssueTableConfigModule issueTableCfMod = ConfigUtil.getUserCfMod( IssueTableConfigModule.class, new String[] {FetchPlan.DEFAULT, IssueTableConfigModule.FETCH_GROUP_COLUMNDESCRIPTORS, ColumnDescriptor.FETCH_GROUP_COL_FIELD_NAMES, ColumnDescriptor.FETCH_GROUP_COL_NAME, ColumnDescriptor.FETCH_GROUP_COL_TOOLTIP_DESCRIPTION, ColumnDescriptor.FETCH_GROUP_COL_FETCH_GROUPS}, NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT, monitor); monitor.done(); return issueTableCfMod; } /** * Constructs the issue table. * * @param parent - the parent composite for holding this table * @param style - SWT style constant * * @deprecated should not be used anymore, use {@link #IssueTable(Composite, int, boolean)} instead * and use {@link #setIssueTableConfigurations(IssueTableConfigModule)}. * This constructor uses Server communication to load the {@link IssueTableConfigModule}. */ @Deprecated public IssueTable(Composite parent, int style) { // this(parent, style, true); // <-- OLD default, single constructor for the IssueTable. // Since 2010.04.26: // The default constructor of this IssueTable will call upon the (default) IssueTableConfigModule, and sets its table // layout and contents according to the instructions contained within the configuration's specifications. this(parent, style, false); // The setup --> According to the criteria defined in the (default) IssueTableConfigModule. // Currently, as of 2011.09.30, the default IssueTable has 10 columns. IssueTableConfigModule issueTableCfMod = ConfigUtil.getUserCfMod( IssueTableConfigModule.class, new String[] {FetchPlan.DEFAULT, IssueTableConfigModule.FETCH_GROUP_COLUMNDESCRIPTORS, ColumnDescriptor.FETCH_GROUP_COL_FIELD_NAMES, ColumnDescriptor.FETCH_GROUP_COL_NAME, ColumnDescriptor.FETCH_GROUP_COL_TOOLTIP_DESCRIPTION, ColumnDescriptor.FETCH_GROUP_COL_FETCH_GROUPS}, NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT, new NullProgressMonitor()); setIssueTableConfigurations(issueTableCfMod); } /** * An alternative constructor that allows for independent initialisation of the IssueTable. */ public IssueTable(Composite parent, int style, boolean isInitTable) { super(parent, style, isInitTable); getTableViewer().setComparator(new ViewerComparator() { @Override public void sort(Viewer viewer, Object[] elements) { Arrays.sort(elements, new Comparator<Object>() { public int compare(Object object1, Object object2) { return -((Issue)object1).getCreateTimestamp().compareTo(((Issue)object2).getCreateTimestamp()); } }); } }); // Reorganised: From the original codes, the following lines were previously inside the createTableColumns() method. // TODO Consider before forward-porting to trunk: Use the new context-menu framework, which handles events such as this more effectively. @Kai addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(DoubleClickEvent e) { if (isTableInWizard) return; StructuredSelection s = (StructuredSelection)e.getSelection(); if (s.isEmpty()) return; Issue issue = (Issue)s.getFirstElement(); 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); } } }); } /** * This assumes that the super class's initTable() routine has been earlier by-passed during the construction of this * {@link IssueTable}, and based on the given issueTableColumnConfiguration, we will proceed to configure the appearance (and later, * the contents) of what the table is allowed to display in its columns and fields. */ public void setIssueTableConfigurations(IColumnConfiguration issueTableColumnConfiguration) { // Keep a reference of the ColumnDescriptors from the config module. columnContentDescriptors = issueTableColumnConfiguration.getColumnDescriptors(); // After cleaning up and refactorisation, we are again able to call the super class's initTable() method // here, which basically performs the following two things: // 1. Creates the columns. // 2. Sets up the label-provider. initTable(); // Since we now have set up what the table-columns can display, we can happily set it's fetch-group references. fetchGroups = issueTableColumnConfiguration.getAllColumnFetchGroups(); } // @Override // protected void createTableColumns(TableViewer tableViewer, Table table) { // // Guard. // if (columnContentDescriptors == null) // return; // // // Go through the list, in order of the intended appearance. // TableLayout layout = new TableLayout(); // for (IColumnDescriptor columnDescriptor : columnContentDescriptors) { // TableColumn tc = new TableColumn(table, columnDescriptor.getStyle()); // tc.setMoveable(columnDescriptor.isMovable()); // tc.setResizable(columnDescriptor.isResizable()); // tc.setText(columnDescriptor.getName()); // tc.setToolTipText(columnDescriptor.getTooltipDescription()); // layout.addColumnData(new ColumnWeightData(columnDescriptor.getWeight())); // } // // // And finally... // table.setLayout(layout); // } @Override protected void createTableColumns(TableViewer tableViewer, Table table) { // Guard. if (columnContentDescriptors == null) return; if (table == null || table.isDisposed()) return; // Go through the list, in order of the intended appearance. int[] weights = new int[columnContentDescriptors.size()]; for (int i = 0; i<columnContentDescriptors.size(); i++) { IColumnDescriptor columnDescriptor = columnContentDescriptors.get(i); TableColumn tc = new TableColumn(table, columnDescriptor.getStyle()); tc.setMoveable(columnDescriptor.isMovable()); tc.setResizable(columnDescriptor.isResizable()); tc.setText(columnDescriptor.getName()); tc.setToolTipText(columnDescriptor.getTooltipDescription()); weights[i] = columnDescriptor.getWeight(); // layout.addColumnData(new ColumnWeightData(columnDescriptor.getWeight())); } // And finally... TableLayout layout = new WeightedTableLayout(weights); table.setLayout(layout); } public static final String SCOPE = "org.nightlabs.jfire.trade.ui.TradePlugin" + "#ZONE_SALE"; @Override protected void setTableProvider(TableViewer tableViewer) { // Guard. if (columnContentDescriptors == null) return; if (tableViewer.getTable().isDisposed()) return; // Only because of the scope there existed a dependency to org.nightlabs.jfire.trade.ui.TradePlugin although not needed at all // changed the CONSTANT to local SCOPE and left same value because of compatibility. If value is unimported it can be changed. Daniel // issueTableLabelProvider = new ConfigurableIssueTableLabelProvider(tableViewer, this, columnContentDescriptors, TradePlugin.ZONE_SALE); issueTableLabelProvider = new ConfigurableIssueTableLabelProvider(tableViewer, this, columnContentDescriptors, SCOPE); tableViewer.setContentProvider(new ArrayContentProvider()); tableViewer.setLabelProvider(issueTableLabelProvider); } private boolean isTableInWizard = false; // <-- Erh?? Why should this be here?? public void setIsTableInWizard(boolean isTableInWizard) { this.isTableInWizard = isTableInWizard; } // The fetch-groups required to display the contents of the columns in this table should be set // based on the information conceived in the IColumnConfiguration (the config-module). private String[] fetchGroups; public String[] getIssueTableFetchGroups() { return fetchGroups; } @Override public void setInput(Object input) { // For placement settings: Determine the maximum number of IssueMarkers per Issue // OR do we need to refactor this (ask the server) when refactoring this whole search stuff to SWT.VIRTUAL? int maxIssueMarkerCountPerIssue = -1; if (input instanceof Collection<?> && issueTableLabelProvider instanceof ConfigurableIssueTableLabelProvider) { // We might not want to display markers in the new configurable table, where the markers have not been duly detached. if (((ConfigurableIssueTableLabelProvider) issueTableLabelProvider).isFieldNameInConfiguration(Issue.FieldName.issueMarkers)) { for (Object o : ((Collection<?>)input)) if (o instanceof Issue) maxIssueMarkerCountPerIssue = Math.max(maxIssueMarkerCountPerIssue, ((Issue)o).getIssueMarkers().size()); ((ConfigurableIssueTableLabelProvider) issueTableLabelProvider).setMaxIssueMarkerCountPerIssue(maxIssueMarkerCountPerIssue); } } super.setInput(input); } // ---[ Proposed additional helper methods ]----------------------------------------------------------------------| // --->> Which perhaps can be upgraded into the super class? /** * @return the ObjectIDs of all the elements in this table. Note that it is possible that the Collection is empty. */ public Collection<IssueID> getElementsObjectIDs() { return NLJDOHelper.getObjectIDList( getElements() ); } /** * Performs an O(n) search in the currecnt Collection based on the given ObjectID. * @return the element from this table that matches the given ObjectID. Returns null if no matching element is found. */ public Issue getElementByID(IssueID objectID) { Collection<Issue> issues = getElements(); if (issues == null || issues.isEmpty()) return null; Collection<IssueID> issueIDs = NLJDOHelper.getObjectIDList(issues); Iterator<Issue> issueIter = issues.iterator(); for (IssueID issueID : issueIDs) { Issue issue = issueIter.next(); if (objectID.equals(issueID)) return issue; } return null; } /** * Removes an element from this table given its matching ObjectID. Performs a linear search here. * @return the element that was removed from the table. Returns null if no matching element is found. */ public Issue removeElementByID(IssueID objectID) { Issue issue = getElementByID(objectID); if (issue != null) { Collection<Issue> issues = getElements(); issues.remove(issue); this.setInput(issues); } return issue; } }