/* * 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.designer.ui.common.InternalUiConstants.Widgets; import org.teiid.designer.ui.common.util.UiUtil; import org.teiid.designer.ui.common.util.WidgetFactory; import org.teiid.designer.ui.common.util.WidgetUtil; /** * @since 8.0 */ public class TablePanel extends AbstractVerticalButtonPanel implements Widgets { public static interface Constants { int ITEMS_ORDERED = 1; int ITEMS_EDITABLE = 1 << 1; int ITEMS_COMMONLY_ALL_SELECTED = 1 << 2; int DOWN = 1; int UP = -1; } protected IListPanelController ctrlr; protected Button addButton, editButton, removeButton, upButton, downButton, selectAllButton, deselectAllButton; protected WrappingLabel messageLabel; protected boolean enabled; protected ListenerList checkStateListeners; protected ISelectionChangedListener tableSelectionListener; boolean editEnabled; /** * @param name * @param parent * @param style * @param gridStyle * @param span * @since 4.2 */ public TablePanel( Composite parent, String title, IListPanelController controller, int style, int itemStyle ) { this(parent, title, controller, style, itemStyle, GridData.FILL_BOTH); } public TablePanel( Composite parent, String title, IListPanelController controller, int style, int itemStyle, int span ) { super(title, parent, style); this.enabled = super.getEnabled(); // to initialize current enabled state constructEditPanel(controller, itemStyle, span); } /** * @since 4.0 */ protected void constructEditPanel( final IListPanelController controller, final int itemStyle, final int gridStyle ) { CoreArgCheck.isNotNull(controller); this.ctrlr = controller; final TableViewer viewer = getTableViewer(); // 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 ) { // defect 15983 - allow double-clicks even if VDB is read-only // note that we can't just call editButton.isEnabled() -- the javadocs // for that state that all ancestors must be enabled, too, which is // not the case here: TablePanel itself is disabled... if (editEnabled) { 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(); } /** * @see org.teiid.designer.ui.common.widget.AbstractVerticalButtonPanel#createViewer(org.eclipse.swt.widgets.Composite, int) * @since 4.2 */ @Override protected Viewer createViewer( Composite parent, int style ) { messageLabel = WidgetFactory.createWrappingLabel(parent, GridData.BEGINNING, 2); messageLabel.setVisible(false); tableSelectionListener = new ISelectionChangedListener() { @Override public void selectionChanged( final SelectionChangedEvent event ) { itemsSelected((IStructuredSelection)event.getSelection()); } }; // Add single-column table (i.e, a list w/ icons) if (WidgetUtil.hasState(style, SWT.CHECK)) { final CheckTablePanelViewer viewer = new CheckTablePanelViewer(parent, style); viewer.addSelectionChangedListener(tableSelectionListener); // 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 TableViewer viewer = new TableViewer(parent, style); viewer.addSelectionChangedListener(tableSelectionListener); return viewer; } /** * @since 4.0 */ public TableViewer getTableViewer() { return (TableViewer)getViewer(); } /** * This method must be called if any TableColumns are added to the table after this object is constructed. * * @since 4.2 */ public void resetSelectionListener() { getViewer().removeSelectionChangedListener(tableSelectionListener); getViewer().addSelectionChangedListener(tableSelectionListener); } /** * @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 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); } } /** * Set the message label for this table * * @param message * @since 4.2 */ public void setMessage( final String message ) { messageLabel.setText(message); messageLabel.setVisible(true); } public void deleteMessageLabel() { // delete the message label: messageLabel.dispose(); } /** * 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); // defect 15983 - allow double-clicks even if VDB is read-only editEnabled = count == 1; if (this.editButton != null) { editButton.setEnabled(editEnabled); } 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(); 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() { } /** * @since 4.0 */ void upButtonSelected() { this.ctrlr.upButtonSelected(getSelection()); moveItems(Constants.UP); } /** * 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 ) { 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); } }); } } } /** * @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.2 */ private class CheckTablePanelViewer extends CheckboxTableViewer { /** * @since 4.2 */ CheckTablePanelViewer( final Composite parent, final int style ) { super(new Table(parent, style | SWT.MULTI)); } /** * 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); } } }