/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.ui.common.widget; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.jface.util.SafeRunnable; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; import org.teiid.core.designer.util.CoreArgCheck; import org.teiid.core.designer.util.CoreStringUtil; import org.teiid.designer.ui.common.InternalUiConstants.Widgets; import org.teiid.designer.ui.common.util.UiUtil; import org.teiid.designer.ui.common.util.WidgetUtil; /** * Must be added to a container that has a {@link org.eclipse.swt.layout.GridLayout}. * * @since 8.0 */ public class ListPanel extends AbstractVerticalButtonPanel implements CoreStringUtil.Constants, Widgets { public static interface Constants { int NONE = 0; int ITEMS_ORDERED = 1; int ITEMS_EDITABLE = 1 << 1; int ITEMS_COMMONLY_ALL_SELECTED = 1 << 2; int DOWN = 1; int UP = -1; } private IListPanelController ctrlr; Button addButton, editButton, removeButton, upButton, downButton, selectAllButton, deselectAllButton; private boolean enabled; private ListenerList checkStateListeners; /** * @param parent * @since 4.0 */ public ListPanel( final Composite parent, final String title, final IListPanelController controller ) { this(parent, title, controller, SWT.NONE); } /** * @param parent * @param style * @since 4.0 */ public ListPanel( final Composite parent, final String title, final IListPanelController controller, final int style ) { this(parent, title, controller, style, Constants.NONE); } /** * @param parent * @param style * @since 4.0 */ public ListPanel( final Composite parent, final String title, final IListPanelController controller, final int style, final int itemStyle ) { this(parent, title, controller, style, itemStyle, GridData.FILL_BOTH); } /** * @param parent * @param style * @since 4.0 */ public ListPanel( final Composite parent, final String title, final IListPanelController controller, final int style, final int itemStyle, final int gridStyle ) { this(parent, title, controller, style, itemStyle, gridStyle, (java.util.List)null); } /** * @param parent * @param style * @since 4.0 */ public ListPanel( final Composite parent, final String title, final IListPanelController controller, final int style, final int itemStyle, final java.util.List items ) { this(parent, title, controller, style, itemStyle, GridData.FILL_BOTH, items); } /** * @param parent * @param style * @since 4.0 */ public ListPanel( final Composite parent, final String title, final IListPanelController controller, final int style, final int itemStyle, final int gridStyle, final java.util.List items ) { super(title, parent, style); this.enabled = super.getEnabled(); // to initialize current enabled state constructListEditPanel(controller, itemStyle, gridStyle, items); } /** * @since 4.0 */ protected void constructListEditPanel( final IListPanelController controller, final int itemStyle, final int gridStyle, final java.util.List items ) { CoreArgCheck.isNotNull(controller); this.ctrlr = controller; final TableViewer viewer = getTableViewer(); // Initialize table if items available if (items != null && !items.isEmpty()) { viewer.add(items.toArray()); } // Add vertical button bar final int style = viewer.getControl().getStyle(); if ((style & SWT.READ_ONLY) == 0) { // Add buttons to button bar this.addButton = addButton(ADD_BUTTON); this.addButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected( final SelectionEvent event ) { addButtonSelected(); } }); } if ((itemStyle & Constants.ITEMS_EDITABLE) != 0) { this.editButton = addButton(EDIT_BUTTON); this.editButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected( final SelectionEvent event ) { editButtonSelected(); } }); // Add double-click listener to list that auto-edits if editing enabled viewer.addDoubleClickListener(new IDoubleClickListener() { @Override public void doubleClick( final DoubleClickEvent event ) { if (ListPanel.this.editButton.isEnabled()) { editButtonSelected(); } } }); } if ((style & SWT.READ_ONLY) == 0) { this.removeButton = addButton(REMOVE_BUTTON); this.removeButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected( final SelectionEvent event ) { removeButtonSelected(); } }); } if ((itemStyle & Constants.ITEMS_ORDERED) != 0) { this.upButton = addButton(UP_BUTTON); this.upButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected( final SelectionEvent event ) { upButtonSelected(); } }); this.downButton = addButton(DOWN_BUTTON); this.downButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected( final SelectionEvent event ) { downButtonSelected(); } }); } if ((itemStyle & Constants.ITEMS_COMMONLY_ALL_SELECTED) != 0) { this.selectAllButton = addButton(SELECT_ALL_BUTTON); this.selectAllButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected( final SelectionEvent event ) { selectAllButtonSelected(); } }); this.deselectAllButton = addButton(DESELECT_ALL_BUTTON); this.deselectAllButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected( final SelectionEvent event ) { deselectAllButtonSelected(); } }); } // Initialize buttons updateButtons(); } /** * <p> * </p> * * @since 4.0 */ public TableViewer getTableViewer() { return (TableViewer)getViewer(); } /** * Adds the specified listener to those receiving events. Only if the viewer is a checkbox viewer is the listener added. * * @param theListener the listener being added */ public void addCheckStateListener( ICheckStateListener theListener ) { // NO_UCD (Indicates this is ignored by unused code detection tool) if (getViewer() instanceof CheckboxTableViewer) { if (this.checkStateListeners == null) { checkStateListeners = new ListenerList(ListenerList.IDENTITY); } this.checkStateListeners.add(theListener); } } /** * Removes the specified listener to those receiving events. * * @param theListener the listener being added */ public void removeCheckStateListener( ICheckStateListener theListener ) { if (this.checkStateListeners != null) { this.checkStateListeners.remove(theListener); } } /** * Notifies registered listeners. * * @param theEvent the event being processed * @since 4.2 */ void notifyCheckStateListeners( final CheckStateChangedEvent theEvent ) { if (this.checkStateListeners != null) { Object[] array = checkStateListeners.getListeners(); for (int i = 0; i < array.length; i++) { final ICheckStateListener l = (ICheckStateListener)array[i]; SafeRunner.run(new SafeRunnable() { @Override public void run() { l.checkStateChanged(theEvent); } @Override public void handleException( Throwable theEvent ) { super.handleException(theEvent); // if an unexpected exception happens remove listener to make sure the workbench keeps running. removeCheckStateListener(l); } }); } } } /** * @see org.teiid.designer.ui.common.widget.AbstractVerticalButtonPanel#createViewer(org.eclipse.swt.widgets.Composite, int) * @since 4.0 */ @Override protected Viewer createViewer( final Composite parent, final int style ) { // Add single-column table (i.e, a list w/ icons) if (WidgetUtil.hasState(style, SWT.CHECK)) { final CheckListPanelViewer viewer = new CheckListPanelViewer(parent, style); viewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged( final SelectionChangedEvent event ) { itemsSelected((IStructuredSelection)event.getSelection()); } }); // when the panel is disabled it's scrollbars also disable. this is bad since the user can't see // all the items in the panel then. so when the component does disable just undo the checked state // being set. viewer.addCheckStateListenerAccess(new ICheckStateListener() { @Override public void checkStateChanged( CheckStateChangedEvent theEvent ) { // undo the check if (!getEnabled() && theEvent.getSource() != this) { viewer.setChecked(theEvent.getElement(), !theEvent.getChecked()); } else { notifyCheckStateListeners(theEvent); } } }); return viewer; } final ListPanelViewer viewer = new ListPanelViewer(parent, style); viewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged( final SelectionChangedEvent event ) { itemsSelected((IStructuredSelection)event.getSelection()); } }); return viewer; } /** * @since 4.0 */ public void addItem( final Object item ) { addItems(new Object[] {item}); } /** * @since 4.0 */ public void addItems( final Object[] items ) { if (items.length > 0) { final TableViewer viewer = getTableViewer(); viewer.add(items); viewer.setSelection(new StructuredSelection(items)); updateButtons(); } } /** * @since 4.0 */ public void clear() { final TableViewer viewer = getTableViewer(); for (Object obj = viewer.getElementAt(0); obj != null; obj = viewer.getElementAt(0)) { viewer.remove(obj); } } /** * @since 4.0 */ public boolean contains( final Object element ) { CoreArgCheck.isNotNull(element); final TableItem[] items = getTableViewer().getTable().getItems(); for (int ndx = items.length; --ndx >= 0;) { if (element.equals(items[ndx].getData())) { return true; } } return false; } /** * @see org.eclipse.swt.widgets.Control#getEnabled() * @since 4.2 */ @Override public boolean getEnabled() { return this.enabled; } /** * @see org.eclipse.swt.widgets.Control#isEnabled() * @since 4.2 */ @Override public boolean isEnabled() { return this.enabled && getParent().isEnabled(); } /** * When disabling the list the list actually remains enabled. This is done because once disabled it's scrollbars are also * disabled so the list won't scroll. If a checkbox tree viewer is used the checkbox is still enabled but if the user clicks * it to change it's state then the state is toggled back to its original state. Keep this in mind if you add a checkbox state * listener. You will always have to check the enabled state. * * @see org.eclipse.swt.widgets.Control#setEnabled(boolean) * @since 4.2 */ @Override public void setEnabled( boolean theEnableFlag ) { // don't call super since disabling will also disable the scrollbars in the list this.enabled = theEnableFlag; // set button state updateButtons(); // change color to match enabled or disabled color int colorCode = (theEnableFlag ? SWT.COLOR_WHITE : SWT.COLOR_WIDGET_BACKGROUND); getViewer().getControl().setBackground(UiUtil.getSystemColor(colorCode)); if (!theEnableFlag) { getTableViewer().setCellEditors(null); } } /** * Get the specified button from this panel. Works only for the standard button types. * * @param buttonNameConstant see InternalUiConstants.Widgets * @return the button specified, or null if the button could not be found, or was not created in this instance of the panel. * @since 4.2 */ public Button getButton( String buttonNameConstant ) { if (ADD_BUTTON.equals(buttonNameConstant)) { return addButton; } else if (DESELECT_ALL_BUTTON.equals(buttonNameConstant)) { return deselectAllButton; } else if (DOWN_BUTTON.equals(buttonNameConstant)) { return downButton; } else if (EDIT_BUTTON.equals(buttonNameConstant)) { return editButton; } else if (REMOVE_BUTTON.equals(buttonNameConstant)) { return removeButton; } else if (SELECT_ALL_BUTTON.equals(buttonNameConstant)) { return selectAllButton; } else if (UP_BUTTON.equals(buttonNameConstant)) { return upButton; } return null; } /** * @since 4.0 */ @Override public Button addButton( final String name ) { final Button button = super.addButton(name); button.moveBelow(this.addButton); return button; } /** * @since 4.0 */ public void updateButtons() { final TableViewer viewer = getTableViewer(); final IStructuredSelection selection = getSelection(); final int count = selection.size(); final int itemCount = viewer.getTable().getItemCount(); final boolean itemsExist = (itemCount > 0); if (this.editButton != null) { this.editButton.setEnabled(getEnabled() && (count == 1)); } final boolean itemsSelected = (count > 0); if (this.removeButton != null) { this.removeButton.setEnabled(getEnabled() && itemsSelected); } if (this.upButton != null) { this.upButton.setEnabled(getEnabled() && itemsSelected && selection.getFirstElement() != viewer.getElementAt(0)); if (itemsExist && itemsSelected) { final Object lastSelectedObj = selection.toArray()[count - 1]; final Object lastObj = viewer.getElementAt(itemCount - 1); this.downButton.setEnabled(getEnabled() && (lastSelectedObj != lastObj)); } else { this.downButton.setEnabled(false); } } if (this.selectAllButton != null) { this.selectAllButton.setEnabled(getEnabled() && itemsExist && count < itemCount); this.deselectAllButton.setEnabled(getEnabled() && itemsExist && itemsSelected); } if (this.addButton != null) { this.addButton.setEnabled(getEnabled()); } } /** * @since 4.0 */ void addButtonSelected() { final Object[] items = this.ctrlr.addButtonSelected(); if( items != null ) { addItems(items); } } /** * @since 4.0 */ void deselectAllButtonSelected() { getViewer().setSelection(new StructuredSelection()); } /** * @since 4.0 */ void downButtonSelected() { this.ctrlr.downButtonSelected(getSelection()); moveItems(Constants.DOWN); } /** * @since 4.0 */ void editButtonSelected() { final Object item = this.ctrlr.editButtonSelected(getSelection()); if (item != null) { final TableViewer viewer = getTableViewer(); viewer.getTable().getSelection()[0].setData(item); viewer.update(item, null); itemsSelected(getSelection()); } } /** * @since 4.0 */ void itemsSelected( final IStructuredSelection selection ) { this.ctrlr.itemsSelected(selection); updateButtons(); } /** * @since 4.0 */ void removeButtonSelected() { final Object[] items = this.ctrlr.removeButtonSelected(getSelection()); if (items.length > 0) { getTableViewer().remove(items); updateButtons(); } } /** * @since 4.0 */ void selectAllButtonSelected() { final ListPanelViewer viewer = (ListPanelViewer)getViewer(); viewer.setSelection(new StructuredSelection(viewer.getRawChildren(viewer.getInput()))); } /** * @since 4.0 */ void upButtonSelected() { this.ctrlr.upButtonSelected(getSelection()); moveItems(Constants.UP); } /** * @since 4.0 */ private void moveItems( final int direction ) { final TableViewer viewer = getTableViewer(); final Table table = viewer.getTable(); final int[] rows = table.getSelectionIndices(); final Object[] items = new Object[rows.length * 2]; for (int ndx = 0; ndx < rows.length; ++ndx) { final int rowsNdx = (direction < 0 ? ndx : rows.length - ndx - 1); final int row = rows[rowsNdx]; final TableItem srcTableItem = table.getItem(row); final TableItem destTableItem = table.getItem(row + direction); final Object srcItem = srcTableItem.getData(); final Object destItem = destTableItem.getData(); destTableItem.setData(srcItem); srcTableItem.setData(destItem); final int itemsNdx = ndx * 2; items[itemsNdx] = srcItem; items[itemsNdx + 1] = destItem; rows[rowsNdx] += direction; } viewer.update(items, null); table.setSelection(rows); updateButtons(); } /** * @since 4.0 */ private class ListPanelViewer extends TableViewer { /** * @since 4.0 */ ListPanelViewer( final Composite parent, final int style ) { super(parent, style); } /** * Overridden to handle lack of content provider * * @see org.eclipse.jface.viewers.StructuredViewer#getRawChildren(java.lang.Object) * @since 4.0 */ @Override protected Object[] getRawChildren( final Object parent ) { Object[] children = super.getRawChildren(parent); final Table table = getTable(); final int count = table.getItemCount(); if (children.length == 0 && count > 0) { children = new Object[count]; final TableItem[] items = table.getItems(); for (int ndx = 0; ndx < items.length; ++ndx) { children[ndx] = items[ndx].getData(); } } return children; } } /** * @since 4.2 */ class CheckListPanelViewer extends CheckboxTableViewer { /** * @since 4.2 */ CheckListPanelViewer( final Composite parent, final int style ) { super(new Table(parent, style)); } /** * This is an unsupported operation. Use * * @see org.eclipse.jface.viewers.CheckboxTableViewer#addCheckStateListener(org.eclipse.jface.viewers.ICheckStateListener) * @since 4.2 */ @Override public void addCheckStateListener( ICheckStateListener theListener ) { throw new UnsupportedOperationException("Use " + ListPanel.class + ".addCheckStateListener(ICheckStateListener)"); //$NON-NLS-1$ //$NON-NLS-2$ } /** * Gives access to registering a listener. * * @param theListener the listener being registered * @since 4.2 */ void addCheckStateListenerAccess( ICheckStateListener theListener ) { super.addCheckStateListener(theListener); } /** * Overridden to handle lack of content provider * * @see org.eclipse.jface.viewers.StructuredViewer#getRawChildren(java.lang.Object) * @since 4.0 */ @Override protected Object[] getRawChildren( final Object parent ) { Object[] children = super.getRawChildren(parent); final Table table = getTable(); final int count = table.getItemCount(); if (children.length == 0 && count > 0) { children = new Object[count]; final TableItem[] items = table.getItems(); for (int ndx = 0; ndx < items.length; ++ndx) { children[ndx] = items[ndx].getData(); } } return children; } } }