package net.sourceforge.pmd.eclipse.ui.preferences.br; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import net.sourceforge.pmd.eclipse.runtime.preferences.IPreferences; import net.sourceforge.pmd.eclipse.ui.AbstractColumnDescriptor; import net.sourceforge.pmd.eclipse.ui.ColumnDescriptor; import net.sourceforge.pmd.eclipse.ui.ModifyListener; import net.sourceforge.pmd.eclipse.ui.PMDUiConstants; import net.sourceforge.pmd.eclipse.ui.nls.StringKeys; import net.sourceforge.pmd.eclipse.ui.preferences.editors.SWTUtil; import net.sourceforge.pmd.eclipse.ui.views.ChangeRecord; import net.sourceforge.pmd.eclipse.util.ResourceManager; import org.eclipse.jface.viewers.CheckboxTreeViewer; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.dialogs.ContainerCheckedTreeViewer; /** * Tree table support, everything non-Rule related. * * @author Brian Remedios */ public abstract class AbstractTreeTableManager <T extends Object> extends AbstractTableManager<T> { protected ContainerCheckedTreeViewer treeViewer; private Button selectAllButton; private Button unSelectAllButton; private ModifyListener modifyListener; private Label activeCountLabel; private Label activeCountIssue; private ChangeRecord<T> changes; private Map<Integer, List<Listener>> paintListeners = new HashMap<Integer, List<Listener>>(); public AbstractTreeTableManager(String theWidgetId, IPreferences thePreferences, ColumnDescriptor[] theColumns) { super(theWidgetId, thePreferences, theColumns); } protected static ColumnWidthAdapter adapterFor(final TreeColumn column) { return new ColumnWidthAdapter() { public int width() { return column.getWidth(); } public void width(int newWidth) { column.setWidth(newWidth); } public Display display() { return column.getDisplay(); } public void setData(String key, Object value) { column.setData(key, value); } public Object getData(String key) { return column.getData(key); } }; } public Tree getControl() { return treeViewer.getTree(); } protected String idFor(Object column) { return ((TreeColumn)column).getToolTipText(); } protected void removed(Collection<T> items) { if (changes == null) changes = new ChangeRecord<T>(); changes.removed(items); updateCheckControls(); } protected void added(T item) { if (changes == null) changes = new ChangeRecord<T>(); changes.added(item); updateCheckControls(); } protected Map<Integer, List<Listener>> paintListeners() { return paintListeners; } protected void createCheckBoxColumn(Tree tree) { TreeColumn tc = new TreeColumn(tree, 0); Image image = new Image(Display.getCurrent(), 15, 15); GC gc = new GC(image); Point textExtent = gc.textExtent("m"); gc.dispose(); image.dispose(); tc.setWidth(textExtent.x * 4); tc.setResizable(true); tc.pack(); tc.addListener(SWT.Selection, new Listener() { public void handleEvent(Event e) { sortByCheckedItems(); } }); } /** * Remove all rows, columns, and column painters in preparation * for new columns. * * @return Tree */ protected Tree cleanupRuleTree() { Tree tree = treeViewer.getTree(); tree.clearAll(true); for(;tree.getColumns().length>0;) { // TODO also dispose any heading icons? tree.getColumns()[0].dispose(); } // ensure we don't have any previous per-column painters left over for (Map.Entry<Integer, List<Listener>> entry : paintListeners.entrySet()) { int eventCode = entry.getKey().intValue(); List<Listener> listeners = entry.getValue(); for (Listener listener : listeners) { tree.removeListener(eventCode, listener); } listeners.clear(); } return tree; } protected abstract boolean isQualifiedItem(Object item); protected abstract void saveItemSelections(); public int activeItemCount() { Object[] checkedItems = treeViewer.getCheckedElements(); int count = 0; for (Object item : checkedItems) { if (isQualifiedItem(item)) count++; } return count; } protected CheckboxTreeViewer treeViewer() { return treeViewer; } public ChangeRecord<T> changes() { return changes; } protected Button newImageButton(Composite parent, String imageId, String toolTipId) { Button button = new Button(parent, SWT.PUSH | SWT.LEFT); button.setImage(ResourceManager.imageFor(imageId)); button.setToolTipText(getMessage(toolTipId)); button.setEnabled(true); return button; } /** * * @param parent Composite * @return Button */ protected Button buildSelectAllButton(Composite parent) { Button button = newImageButton(parent, PMDUiConstants.ICON_BUTTON_CHECK_ALL, StringKeys.PREF_RULESET_BUTTON_CHECK_ALL); button.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { setAllItemsActive(); } }); return button; } /** * * @param parent Composite * @return Button */ protected Button buildUnselectAllButton(Composite parent) { Button button = newImageButton(parent, PMDUiConstants.ICON_BUTTON_UNCHECK_ALL, StringKeys.PREF_RULESET_BUTTON_UNCHECK_ALL); button.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { preferences.getActiveRuleNames().clear(); treeViewer().setCheckedElements(new Object[0]); setModified(); updateCheckControls(); } }); return button; } protected abstract String nameFor(Object treeItemData); /** * @param item TreeItem * @param checked boolean */ private void check(TreeItem item, boolean checked) { item.setChecked(checked); Object itemData = item.getData(); if (itemData == null || itemData instanceof RuleGroup) return; String name = nameFor(itemData); isActive(name, checked); updateCheckControls(); setModified(); } protected abstract void selectedItems(Object[] items); protected abstract void updateTooltipFor(TreeItem item, int columnIndex); // TODO move to util public static int columnIndexAt(TreeItem item, int xPosition) { TreeColumn[] cols = item.getParent().getColumns(); Rectangle bounds; for(int i = 0; i < cols.length; i++){ bounds = item.getBounds(i); if (bounds.x < xPosition && xPosition < (bounds.x + bounds.width)) { return i; } } return -1; } protected void buildTreeViewer(Composite parent) { int treeStyle = SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION | SWT.CHECK; treeViewer = new ContainerCheckedTreeViewer(parent, treeStyle); final Tree tree = treeViewer.getTree(); tree.setLinesVisible(true); tree.setHeaderVisible(true); treeViewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { IStructuredSelection selection = (IStructuredSelection)event.getSelection(); selectedItems(selection.toArray()); } }); addDeleteListener(tree); tree.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent ev) { if (ev.character == ' ') { toggleSelectedItems(); } } }); tree.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { if (event.detail == SWT.CHECK) { TreeItem item = (TreeItem) event.item; boolean checked = item.getChecked(); checkItems(item, checked); checkPath(item.getParentItem(), checked, false); } // if (!checkedRules.isEmpty()) System.out.println(checkedRules.iterator().next()); } }); tree.addListener(SWT.MouseMove, new Listener() { public void handleEvent(Event event) { Point point = new Point(event.x, event.y); TreeItem item = tree.getItem(point); if (item != null) { int columnIndex = columnIndexAt(item, event.x); updateTooltipFor(item, columnIndex); } } }); setupMenusFor(tree); } protected int headerHeightFor(Control control) { return ((Tree)control).getHeaderHeight(); } protected void setMenu(Control control, Menu menu) { ((Tree)control).setMenu(menu); } protected Rectangle clientAreaFor(Control control) { return ((Tree)control).getClientArea(); } public void updated(Object item) { treeViewer.update(item, null); } /** * Method checkPath. * @param item TreeItem * @param checked boolean * @param grayed boolean */ protected void checkPath(TreeItem item, boolean checked, boolean grayed) { if (item == null) return; if (grayed) { checked = true; } else { int index = 0; TreeItem[] items = item.getItems(); while (index < items.length) { TreeItem child = items[index]; if (child.getGrayed() || checked != child.getChecked()) { checked = grayed = true; break; } index++; } } check(item, checked); item.setGrayed(grayed); checkPath(item.getParentItem(), checked, grayed); } /** * @param item TreeItem * @param checked boolean */ protected void checkItems(TreeItem item, boolean checked) { item.setGrayed(false); check(item, checked); TreeItem[] items = item.getItems(); for (TreeItem item2 : items) { checkItems(item2, checked); } updateCheckControls(); } private void toggleSelectedItems() { // TODO System.out.println("TODO: toggle selected items"); } protected void buildActiveCountWidgets(Composite parent) { activeCountLabel = new Label(parent, 0); activeCountLabel.setText("---"); GridData data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER, GridData.CENTER, true, false, 1, 1); activeCountLabel.setAlignment(SWT.RIGHT); activeCountLabel.setLayoutData(data); activeCountIssue = new Label(parent, 0); data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER, GridData.CENTER, true, false, 1, 1); activeCountIssue.setAlignment(SWT.RIGHT); activeCountIssue.setLayoutData(data); } protected void activeCountDetails(String msg, Image image) { if (activeCountLabel == null) return; activeCountLabel.setText(msg); activeCountIssue.setImage(image); activeCountIssue.getParent().pack(); activeCountLabel.getParent().pack(); // handle changing string length } protected void buildCheckButtons(Composite parent) { selectAllButton = buildSelectAllButton(parent); unSelectAllButton = buildUnselectAllButton(parent); } protected ColumnWidthAdapter columnAdapterFor(ColumnDescriptor desc) { TreeColumn column = columnFor(desc); return adapterFor(column); } protected void redrawTable() { redrawTable("-", -1); } protected void redrawTable(String sortColumnLabel, int sortDir) { TreeColumn sortColumn = columnFor(sortColumnLabel); treeViewer().getTree().setSortColumn(sortColumn); treeViewer().getTree().setSortDirection(sortDir); } private TreeColumn columnFor(String tooltipText) { for (TreeColumn column : treeViewer().getTree().getColumns()) { if (String.valueOf(column.getToolTipText()).equals(tooltipText)) return column; } return null; } private TreeColumn columnFor(ColumnDescriptor desc) { for (TreeColumn column : treeViewer().getTree().getColumns()) { if ((column.getData(AbstractColumnDescriptor.DescriptorKey)) == desc) return column; } return null; } protected void formatValueOn(StringBuilder target, Object value, Class<?> datatype) { ValueFormatter formatter = FormatManager.formatterFor(datatype); if (formatter != null) { formatter.format(value, target); return; } target.append(value); // should not get here..breakpoint here } // protected Button buildSortByCheckedItemsButton(Composite parent) { // Button button = new Button(parent, SWT.PUSH | SWT.LEFT); // button.setToolTipText("Sort by checked items"); // button.setImage(ResourceManager.imageFor(PMDUiConstants.ICON_BUTTON_SORT_CHECKED)); // // button.addSelectionListener(new SelectionAdapter() { // public void widgetSelected(SelectionEvent event) { // sortByCheckedItems(); // } // }); // // return button; // } protected abstract void setAllItemsActive(); protected abstract void sortByCheckedItems(); public void modifyListener(ModifyListener theListener) { modifyListener = theListener; } protected void setModified() { if (modifyListener != null) modifyListener.setModified(); } protected void updateButtonsFor(int selections, int totalSelections) { SWTUtil.setEnabled(selectAllButton, selections < totalSelections); SWTUtil.setEnabled(unSelectAllButton, selections > 0); } protected abstract void updateCheckControls(); /** * Refresh the list */ protected void refresh() { try { treeViewer().getControl().setRedraw(false); treeViewer().refresh(); } catch (ClassCastException e) { plugin.logError("Ignoring exception while refreshing table", e); } finally { treeViewer().getControl().setRedraw(true); } } }