package net.sourceforge.pmd.eclipse.ui.preferences.br; import java.util.HashSet; import java.util.Set; import net.sourceforge.pmd.eclipse.plugin.PMDPlugin; import net.sourceforge.pmd.eclipse.runtime.preferences.IPreferences; import net.sourceforge.pmd.eclipse.runtime.preferences.impl.PreferenceUIStore; import net.sourceforge.pmd.eclipse.ui.ColumnDescriptor; 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.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.TableColumn; /** * Core table behaviour including menus and support for hiding/showing columns * * @author Brian Remedios */ public abstract class AbstractTableManager<T extends Object> implements SortListener { private final String widgetId; // for saving preference values protected boolean sortDescending; protected TableColumn sortColumn; protected Object columnSorter; // cast to concrete type in subclass protected IPreferences preferences; protected Menu headerMenu; protected Menu tableMenu; protected final ColumnDescriptor[] availableColumns; // columns shown in the rule treetable in the desired order private final Set<ColumnDescriptor> hiddenColumns = new HashSet<ColumnDescriptor>(); protected static PMDPlugin plugin = PMDPlugin.getDefault(); public AbstractTableManager(String theWidgetId, IPreferences thePreferences, ColumnDescriptor[] theColumns) { super(); widgetId = theWidgetId; preferences = thePreferences; availableColumns = theColumns; loadHiddenColumns(); } protected static class WidthChangeThread extends Thread { private final int startWidth; private final int endWidth; private final ColumnWidthAdapter column; protected WidthChangeThread(int start, int end, ColumnWidthAdapter theColumn) { super(); startWidth = start; endWidth = end; column = theColumn; } private void delay(int mSec) { try { Thread.sleep(mSec); } catch (Exception ex) { } } protected void setWidth(final int width) { column.display().syncExec(new Runnable() { public void run() { // delay(10); column.width(width); } }); } public void run() { if (endWidth > startWidth) { for (int i = startWidth; i <= endWidth; i++ ) { setWidth(i); } } else { for (int i = startWidth; i >= endWidth; i-- ) { setWidth(i); } } } } protected interface ColumnWidthAdapter { // unifies table and tree column behaviour as one type int width(); void width(int newWidth); Display display(); void setData(String key, Object value); Object getData(String key); } protected static ColumnWidthAdapter adapterFor(final TableColumn 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); } }; } protected abstract ColumnWidthAdapter columnAdapterFor(ColumnDescriptor desc); protected void setupMenusFor(final Control control) { final Display display = control.getDisplay(); Shell shell = control.getShell(); setupMenus(shell); control.addListener(SWT.MenuDetect, new Listener() { public void handleEvent(Event event) { Point pt = display.map(null, control, new Point(event.x, event.y)); Rectangle clientArea = clientAreaFor(control); boolean isHeader = clientArea.y <= pt.y && pt.y < (clientArea.y + headerHeightFor(control)); if (!isHeader) adjustTableMenuOptions(); setMenu(control, isHeader ? headerMenu : tableMenu); } }); addDisposeListener(control); } protected abstract Rectangle clientAreaFor(Control tableOrTreeControl); protected abstract int headerHeightFor(Control tableOrTreeControl); protected abstract void setMenu(Control contro, Menu menu); protected abstract void saveItemSelections(); public void saveUIState() { saveItemSelections(); } protected void setupMenus(Shell shell) { headerMenu = new Menu(shell, SWT.POP_UP); addHeaderSelectionOptions(headerMenu); tableMenu = new Menu(shell, SWT.POP_UP); addTableSelectionOptions(tableMenu); } public void setTableMenu(Menu tableMenu) { this.tableMenu = tableMenu; } protected void addDeleteListener(Control control) { control.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent ev) { if (ev.character == SWT.DEL) { removeSelectedItems(); } } }); } protected void addHeaderSelectionOptions(Menu menu) { for (ColumnDescriptor desc : availableColumns) { MenuItem columnItem = new MenuItem(menu, SWT.CHECK); columnItem.setSelection(!isHidden(desc)); columnItem.setText(desc.label()); final ColumnDescriptor columnDesc = desc; columnItem.addSelectionListener( new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { toggleColumnVisiblity(columnDesc); } } ); } } protected abstract void removeSelectedItems(); protected void addTableSelectionOptions(Menu menu) { // subclasses to provide this } protected void adjustTableMenuOptions() { // subclasses to provide this } protected void addDisposeListener(Control control) { control.addListener(SWT.Dispose, new Listener() { public void handleEvent(Event event) { headerMenu.dispose(); tableMenu.dispose(); } }); } public void visible(ColumnDescriptor column, boolean show) { if (show) { show(column); } else { hide(column); } } protected void show(ColumnDescriptor desc) { hiddenColumns.remove(desc); ColumnWidthAdapter cwa = columnAdapterFor(desc); Object widthData = cwa.getData("restoredWidth"); int width = widthData == null ? desc.defaultWidth() : ((Integer)widthData).intValue(); WidthChangeThread t = new WidthChangeThread(0, width, cwa); t.start(); } protected void hide(ColumnDescriptor desc) { hiddenColumns.add(desc); ColumnWidthAdapter cwa = columnAdapterFor(desc); cwa.setData("restoredWidth", Integer.valueOf( cwa.width() )); WidthChangeThread t = new WidthChangeThread(cwa.width(), 0, cwa); t.start(); } protected boolean isHidden(ColumnDescriptor desc) { return hiddenColumns.contains(desc); } protected boolean isActive(String item) { return preferences.isActive(item); } protected void isActive(String item, boolean flag) { preferences.isActive(item, flag); } protected void toggleColumnVisiblity(ColumnDescriptor desc) { if (hiddenColumns.contains(desc)) { show(desc); } else { hide(desc); } storeHiddenColumns(); // redrawTable(); } public void sortBy(Object accessor, Object context) { if (columnSorter == accessor) { sortDescending = !sortDescending; } else { columnSorter = accessor; } sortColumn = (TableColumn)context; redrawTable(idFor(context), getSortDirection()); } public int getSortDirection() { return sortDescending ? SWT.DOWN : SWT.UP; } public void setSortDirection(TableColumn column, Object accessor, int direction) { if (column != null && accessor != null) { sortDescending = direction == SWT.DOWN; sortColumn = column; columnSorter = accessor; redrawTable(idFor(sortColumn), direction); } } protected abstract String idFor(Object column); protected abstract void redrawTable(String columnId, int sortDirection); private void storeHiddenColumns() { Set<String> columnIds = new HashSet<String>(hiddenColumns.size()); for (ColumnDescriptor desc : hiddenColumns) { columnIds.add(desc.id()); } PreferenceUIStore.instance.hiddenColumnIds(columnIds); } private void loadHiddenColumns() { for (String columnId : PreferenceUIStore.instance.hiddenColumnIds() ) { for (ColumnDescriptor desc : availableColumns) { if (desc.id().equals(columnId)) { hiddenColumns.add(desc); } } } } /** * Helper method to shorten message access * @param key a message key * @return requested message */ protected String getMessage(String key) { return PMDPlugin.getDefault().getStringTable().getString(key); } }