| /******************************************************************************* |
| * Copyright (c) 2004, 2005 IBM Corporation and others. All rights reserved. This program and the |
| * accompanying materials are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html Contributors: IBM Corporation - Initial API and |
| * implementation Jens Lukowski/Innoopract - initial renaming/restructuring |
| *******************************************************************************/ |
| package org.eclipse.wst.common.ui.internal.viewers; |
| |
| import org.eclipse.jface.viewers.CellEditor; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jface.viewers.StructuredViewer; |
| import org.eclipse.jface.viewers.TableTreeViewer; |
| import org.eclipse.jface.viewers.TableViewer; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.TableCursor; |
| import org.eclipse.swt.custom.TableTreeItem; |
| import org.eclipse.swt.events.FocusAdapter; |
| import org.eclipse.swt.events.FocusEvent; |
| import org.eclipse.swt.events.KeyAdapter; |
| import org.eclipse.swt.events.KeyEvent; |
| import org.eclipse.swt.events.MouseAdapter; |
| import org.eclipse.swt.events.MouseEvent; |
| import org.eclipse.swt.events.PaintEvent; |
| import org.eclipse.swt.events.PaintListener; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.Table; |
| import org.eclipse.swt.widgets.TableItem; |
| |
| /** |
| * Adds a TableCursor to a StructuredViewer - for keyboard navigation of the table The intent of |
| * this class is to provide the standard listeners for using F2 to activate cell editors. Due to a |
| * current bug in the TableCursor, TableViewers using this class must make a call similar to the |
| * TableNavigator method moveCellEditorsAbove(cellEditors) whenever a setCellEditors call is made in |
| * the StructuredViewer. This is so that the cell editor control shows up above the table cursor |
| * control. |
| */ |
| |
| public class TableNavigator extends TableCursor { |
| private static final String TABLETREEITEM_ID = "TableTreeItemID"; |
| |
| final Table table; |
| |
| public TableNavigator(Table table, StructuredViewer viewer) { |
| super(table, SWT.NONE); |
| this.table = table; |
| final Table currentTable = table; |
| final StructuredViewer sViewer = viewer; |
| |
| // Linux index out of bounds fix. See defect 253429, 253433, and more |
| setVisible(false); |
| |
| addPaintListener(viewer); |
| addKeyListeners(viewer); |
| addMouseListeners(viewer); |
| addSelectionListener(new SelectionAdapter() { |
| /** |
| * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(SelectionEvent) |
| */ |
| public void widgetSelected(SelectionEvent e) { |
| super.widgetSelected(e); |
| if (sViewer instanceof TableTreeViewer) { |
| TableTreeItem tableTreeItem = (TableTreeItem) getRow().getData(TABLETREEITEM_ID); |
| StructuredSelection selection = new StructuredSelection(tableTreeItem.getData()); |
| sViewer.setSelection(selection, true); |
| } |
| } |
| |
| }); |
| addFocusListener(new FocusAdapter() { |
| public void focusGained(FocusEvent e) { |
| // if e.source is not a child of the table then set selection - this is for tab into viewer |
| Object eventSource = e.getSource(); |
| if (eventSource instanceof Control) { |
| if (!isChild(currentTable, (Control) eventSource)) { |
| if (currentTable.getItemCount() > 0 && currentTable.getSelectionCount() <= 0) { |
| if (sViewer instanceof TableTreeViewer) { |
| TableTreeItem tableTreeItem = (TableTreeItem) getRow().getData(TABLETREEITEM_ID); |
| StructuredSelection selection = new StructuredSelection(tableTreeItem.getData()); |
| sViewer.setSelection(selection, true); |
| } else { |
| currentTable.setSelection(0); |
| setSelection(0, 0); |
| } |
| } |
| } else { |
| if (currentTable.getItems().length > 0) { |
| // cursor can end up on a non-existent table row |
| // currently no way to get the current table cursor row |
| // so for now just catch the exception since it doesn't |
| // cause any side effects. |
| try { |
| setVisible(true); |
| } catch (Exception ee) { |
| currentTable.setSelection(0); |
| setSelection(0, 0); |
| } |
| } else // do not show table cursor if there are no elements in the table - avoid repaint |
| { |
| setVisible(false); |
| } |
| } |
| } |
| } |
| |
| protected boolean isChild(Control parent, Control child) { |
| Control tempChild = child; |
| while (tempChild != null) { |
| if (tempChild == parent) { |
| return true; |
| } |
| tempChild = tempChild.getParent(); |
| } |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.swt.events.FocusAdapter#focusLost(FocusEvent) |
| */ |
| public void focusLost(FocusEvent e) { |
| // Set the table navigator to be not visible if the the table |
| // is not in focus and a child of the table is not in focus |
| // note that we do this asynchronously so we don't mess up the |
| // current focus handling. |
| Display.getDefault().asyncExec(new Runnable() { |
| /** |
| * @see java.lang.Runnable#run() |
| */ |
| public void run() { |
| if (currentTable != null && !currentTable.isDisposed() |
| && !currentTable.isFocusControl() |
| && !isChild(currentTable, Display.getDefault().getFocusControl())) { |
| setVisible(false); |
| } |
| } |
| }); |
| } |
| |
| }); |
| |
| table.addFocusListener(new FocusAdapter() { |
| /** |
| * @see org.eclipse.swt.events.FocusListener#focusGained(FocusEvent) |
| */ |
| public void focusGained(FocusEvent e) { |
| // only display navigator if there are items in the table |
| // and if the focus wasn't gained from our own table navigator |
| // (ie focus came from outside) |
| if (currentTable.getItemCount() > 0 && (Display.getDefault().getFocusControl() != null) |
| && !Display.getDefault().getFocusControl().equals(TableNavigator.this)) { |
| // note that we do this asynchronously so we don't mess up the |
| // current focus handling. |
| Display.getDefault().asyncExec(new Runnable() { |
| /** |
| * @see java.lang.Runnable#run() |
| */ |
| public void run() { |
| if (!isVisible()) { |
| try { |
| setVisible(true); |
| setFocus(); |
| } catch (Exception e) { |
| // catch IllegalArgumentExceptions here - index out of bounds on tableviewer |
| if (currentTable.getItemCount() > 0) { |
| currentTable.setSelection(0); |
| setSelection(0, 0); |
| } else // do not show table cursor if there are no elements in the table - avoid repaint |
| { |
| setVisible(false); |
| } |
| } |
| } |
| } |
| }); |
| } |
| } |
| }); |
| } |
| |
| public Table getTable() { |
| return table; |
| } |
| |
| public void addPaintListener(StructuredViewer viewer) { |
| |
| addPaintListener(new PaintListener() { |
| public void paintControl(PaintEvent e) { |
| TableItem[] selection = table.getSelection(); |
| final TableItem row = (selection.length == 0) ? table.getItem(table.getTopIndex()) |
| : selection[0]; |
| final String cellText = row.getText(getColumn()); |
| final Image cellImage = row.getImage(getColumn()); |
| final int col = getColumn(); |
| |
| Display.getCurrent().asyncExec(new Runnable() { |
| public void run() { |
| if (!row.isDisposed()) { |
| String newText = row.getText(getColumn()); |
| if (!newText.equals(cellText) || !(row.getImage(col) == cellImage)) { |
| redraw(); |
| } |
| } |
| } |
| }); |
| } |
| }); |
| } |
| |
| public SelectionKeyAdapter getKeyAdapter(StructuredViewer viewer) { |
| if (keyAdapter == null) { |
| return new SelectionKeyAdapter(viewer); |
| } else |
| return keyAdapter; |
| } |
| |
| public void setKeyAdapter(SelectionKeyAdapter kAdapter) { |
| keyAdapter = kAdapter; |
| } |
| |
| protected SelectionKeyAdapter keyAdapter = null; |
| |
| public class SelectionKeyAdapter extends KeyAdapter { |
| StructuredViewer structuredViewer; |
| |
| public SelectionKeyAdapter(StructuredViewer viewer) { |
| super(); |
| this.structuredViewer = viewer; |
| } |
| |
| int lastKeyPressed = -1; // used to cache the last key for key combos |
| |
| public void keyPressed(KeyEvent e) { |
| TableItem row = getRow(); |
| int column = getColumn(); |
| |
| // hack to emulate SHIFT+F10 popup menu - otherwise table cursor |
| // obscures the table popup mechanism and it doesn't work. |
| if (lastKeyPressed == SWT.SHIFT && e.keyCode == SWT.F10) { |
| Menu popup = getTable().getMenu(); |
| popup.setVisible(true); |
| } |
| lastKeyPressed = e.keyCode; |
| |
| //jvh - look for + or - key |
| // column == 0 |
| if (row.getData(TABLETREEITEM_ID) instanceof TableTreeItem) { |
| if (column == 0 && e.character == '+') { |
| TableTreeItem tableTreeItem = (TableTreeItem) row.getData(TABLETREEITEM_ID); |
| ((TableTreeViewer) structuredViewer).setExpandedState(tableTreeItem.getData(), true); |
| refresh(); |
| } else if (column == 0 && e.character == '-') { |
| TableTreeItem tableTreeItem = (TableTreeItem) row.getData(TABLETREEITEM_ID); |
| ((TableTreeViewer) structuredViewer).setExpandedState(tableTreeItem.getData(), false); |
| refresh(); |
| } |
| } |
| // use F2 to invoke editing for a cell |
| if (e.keyCode == SWT.F2) { |
| if (structuredViewer instanceof TableViewer) { |
| ((TableViewer) structuredViewer).editElement(row.getData(), column); |
| } else if (structuredViewer instanceof TableTreeViewer) { |
| TableTreeItem tableTreeItem = (TableTreeItem) row.getData(TABLETREEITEM_ID); |
| ((TableTreeViewer) structuredViewer).editElement(tableTreeItem.getData(), column); |
| } |
| } |
| } |
| } |
| |
| public void addKeyListeners(StructuredViewer viewer) { |
| final StructuredViewer structuredViewer = viewer; |
| |
| addKeyListener(getKeyAdapter(structuredViewer)); |
| } |
| |
| public void addMouseListeners(StructuredViewer viewer) { |
| final StructuredViewer structuredViewer = viewer; |
| |
| addMouseListener(new MouseAdapter() { |
| |
| public void mouseUp(MouseEvent e) { |
| TableItem row = getRow(); |
| int column = getColumn(); |
| |
| // use mouse button 1 to invoke editing for a cell |
| if (e.button == 1) { |
| if (structuredViewer instanceof TableViewer) { |
| ((TableViewer) structuredViewer).editElement(row.getData(), column); |
| } else if (structuredViewer instanceof TableTreeViewer && column == 1) { |
| TableTreeItem tableTreeItem = (TableTreeItem) row.getData(TABLETREEITEM_ID); |
| ((TableTreeViewer) structuredViewer).editElement(tableTreeItem.getData(), column); |
| } |
| |
| if (structuredViewer instanceof TableTreeViewer |
| && row.getData(TABLETREEITEM_ID) instanceof TableTreeItem) { |
| if (column == 0) { |
| TableTreeItem tableTreeItem = (TableTreeItem) row.getData(TABLETREEITEM_ID); |
| boolean expandState = tableTreeItem.getExpanded(); |
| ((TableTreeViewer) structuredViewer).setExpandedState(tableTreeItem.getData(), |
| !expandState); |
| refresh(); |
| } |
| } |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Ensure that cell editor control shows up above the table cursor control. Should be called |
| * whenever the table viewer makes a new call to setCellEditors i.e. in constructor and in |
| * refreshCellEditors |
| * |
| * @param - array of cell editors for the StructuredViewer |
| */ |
| |
| public void moveCellEditorsAbove(CellEditor[] editorArray) { |
| for (int i = 0; i < editorArray.length; i++) { |
| CellEditor cEd = editorArray[i]; |
| if (cEd != null && cEd.getControl() != null) { |
| cEd.getControl().moveAbove(null); |
| } |
| } |
| } |
| |
| public void refresh() { |
| Display.getCurrent().asyncExec(new Runnable() { |
| public void run() { |
| if (!isDisposed() && isVisible()) |
| redraw(); |
| } |
| }); |
| } |
| } |