/******************************************************************************* | |
* Copyright (c) 2005, 2012 IBM Corporation and others. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License 2.0 | |
* which accompanies this distribution, and is available at | |
* https://www.eclipse.org/legal/epl-2.0/ | |
* | |
* SPDX-License-Identifier: EPL-2.0 | |
* | |
* Contributors: | |
* IBM Corporation - initial API and implementation | |
*******************************************************************************/ | |
package org.eclipse.bpel.ui.util; | |
import org.eclipse.bpel.ui.Messages; | |
import org.eclipse.swt.SWT; | |
import org.eclipse.swt.accessibility.ACC; | |
import org.eclipse.swt.accessibility.Accessible; | |
import org.eclipse.swt.accessibility.AccessibleAdapter; | |
import org.eclipse.swt.accessibility.AccessibleControlAdapter; | |
import org.eclipse.swt.accessibility.AccessibleControlEvent; | |
import org.eclipse.swt.accessibility.AccessibleEvent; | |
import org.eclipse.swt.events.FocusEvent; | |
import org.eclipse.swt.events.FocusListener; | |
import org.eclipse.swt.events.SelectionListener; | |
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.widgets.Canvas; | |
import org.eclipse.swt.widgets.Display; | |
import org.eclipse.swt.widgets.Event; | |
import org.eclipse.swt.widgets.Listener; | |
import org.eclipse.swt.widgets.ScrollBar; | |
import org.eclipse.swt.widgets.Table; | |
import org.eclipse.swt.widgets.TableColumn; | |
import org.eclipse.swt.widgets.TableItem; | |
import org.eclipse.swt.widgets.TypedListener; | |
/** | |
* A modification of the SWT TableCursor class to better handle empty tables and dynamic tables | |
*/ | |
public class TableCursor extends Canvas { | |
Table table; | |
int row = -1, column = 0; // having row negative will end up hiding the cursor | |
Listener tableListener, resizeListener; | |
boolean progVisible = false; | |
boolean userVisible = true; | |
boolean hasFocus = false; | |
AccessibleAdapter accessAdapter = null; | |
Listener selectionAccessListener = null; | |
Listener focusAccessListener = null; | |
AccessibleControlAdapter accessControlAdapter = null; | |
boolean hasAccessibility = false; | |
public TableCursor(Table parent, int style) { | |
super(parent, style); | |
table = parent; | |
Listener listener = new Listener() { | |
public void handleEvent(Event event) { | |
switch (event.type) { | |
case SWT.Dispose : | |
dispose(event); | |
break; | |
case SWT.KeyDown : | |
keyDown(event); | |
break; | |
case SWT.Paint : | |
paint(event); | |
break; | |
case SWT.Traverse : | |
traverse(event); | |
break; | |
} | |
} | |
}; | |
addListener(SWT.Dispose, listener); | |
addListener(SWT.KeyDown, listener); | |
addListener(SWT.Paint, listener); | |
addListener(SWT.Traverse, listener); | |
tableListener = new Listener() { | |
public void handleEvent(Event event) { | |
switch (event.type) { | |
case SWT.MouseDown : | |
tableMouseDown(event); | |
break; | |
case SWT.FocusIn : | |
tableFocusIn(event); | |
break; | |
} | |
} | |
}; | |
table.addListener(SWT.FocusIn, tableListener); | |
table.addListener(SWT.MouseDown, tableListener); | |
resizeListener = new Listener() { | |
public void handleEvent(Event event) { | |
resize(); | |
} | |
}; | |
ScrollBar hBar = table.getHorizontalBar(); | |
if (hBar != null) | |
hBar.addListener(SWT.Selection, resizeListener); | |
ScrollBar vBar = table.getVerticalBar(); | |
if (vBar != null) | |
vBar.addListener(SWT.Selection, resizeListener); | |
this.addFocusListener(new FocusListener() { | |
public void focusGained(FocusEvent e) { | |
hasFocus = true; | |
redraw(); | |
} | |
public void focusLost(FocusEvent e) { | |
hasFocus = false; | |
redraw(); | |
}}); | |
refresh(); | |
initAccessible(); | |
} | |
/** | |
* this is called whenever the content of the table has changed, it | |
* will reconcile the cursor and any listeners that we need | |
*/ | |
public void refresh() { | |
// the number of rows and columns could have changed, ideally we should track | |
// which columns have listeners but for the now, we'll just remove and readd | |
int columns = table.getColumnCount(); | |
for (int i = 0; i < columns; i++) { | |
TableColumn column = table.getColumn(i); | |
column.removeListener(SWT.Resize, resizeListener); | |
} | |
for (int i = 0; i < columns; i++) { | |
TableColumn column = table.getColumn(i); | |
column.addListener(SWT.Resize, resizeListener); | |
} | |
// reset the row and column to be a valid one | |
//boolean repaint = false; | |
if (row >= table.getItemCount()) { | |
row = table.getItemCount()-1; | |
} | |
if (column >= columns) { | |
column = columns - 1; | |
} | |
// check to see what the selection is, and reset the tablecursor to | |
// a valid row/column in that selection | |
TableItem[] selection = table.getSelection(); | |
if (selection.length == 0) { | |
row = -1; | |
} | |
else { | |
// // there is a selection, so make sure our table cursor is in that selection | |
// // range | |
// int min = 999999; | |
// int max = -1; | |
// for (int i = 0; i < selection.length; i++) { | |
// int temp = table.indexOf(selection[i]); | |
// min = Math.min(temp, min); | |
// max = Math.max(temp, max); | |
// } | |
// if (row > max || row < min) | |
// row = min; | |
} | |
setSelection(row, column); | |
} | |
public void addSelectionListener(SelectionListener listener) { | |
checkWidget(); | |
if (listener == null) | |
SWT.error(SWT.ERROR_NULL_ARGUMENT); | |
TypedListener typedListener = new TypedListener(listener); | |
addListener(SWT.Selection, typedListener); | |
addListener(SWT.DefaultSelection, typedListener); | |
} | |
void dispose(Event event) { | |
Display display = getDisplay(); | |
display.asyncExec(new Runnable() { | |
public void run() { | |
if (table.isDisposed()) | |
return; | |
table.removeListener(SWT.FocusIn, tableListener); | |
table.removeListener(SWT.MouseDown, tableListener); | |
int columns = table.getColumnCount(); | |
for (int i = 0; i < columns; i++) { | |
TableColumn column = table.getColumn(i); | |
column.removeListener(SWT.Resize, resizeListener); | |
} | |
ScrollBar hBar = table.getHorizontalBar(); | |
if (hBar != null) { | |
hBar.removeListener(SWT.Selection, resizeListener); | |
} | |
ScrollBar vBar = table.getVerticalBar(); | |
if (vBar != null) { | |
vBar.removeListener(SWT.Selection, resizeListener); | |
} | |
} | |
}); | |
} | |
void keyDown(Event event) { | |
switch (event.character) { | |
case SWT.CR : | |
notifyListeners(SWT.DefaultSelection, new Event()); | |
return; | |
} | |
switch (event.keyCode) { | |
case SWT.ARROW_UP : | |
if (column < 0) | |
column = 0; | |
setRowColumn(row - 1, column, true); | |
break; | |
case SWT.ARROW_DOWN : | |
if (column < 0) | |
column = 0; | |
setRowColumn(row + 1, column, true); | |
break; | |
case SWT.ARROW_LEFT : | |
case SWT.ARROW_RIGHT : | |
{ | |
if (column < 0) | |
column = 0; | |
int leadKey = (getStyle() & SWT.RIGHT_TO_LEFT) != 0 ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT; | |
if (event.keyCode == leadKey) { | |
setRowColumn(row, column - 1, true); | |
} else { | |
setRowColumn(row, column + 1, true); | |
} | |
break; | |
} | |
case SWT.HOME : | |
if (column < 0) | |
column = 0; | |
setRowColumn(0, column, true); | |
break; | |
case SWT.END : | |
{ | |
if (column < 0) | |
column = 0; | |
int row = table.getItemCount() - 1; | |
setRowColumn(row, column, true); | |
break; | |
} | |
} | |
} | |
void paint(Event event) { | |
GC gc = event.gc; | |
Display display = getDisplay(); | |
gc.setBackground(display.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT)); | |
gc.setForeground(display.getSystemColor(SWT.COLOR_LIST_SELECTION)); | |
gc.fillRectangle(event.x, event.y, event.width, event.height); | |
TableItem item = null; | |
if (row >= 0) | |
item = table.getItem(row); | |
int x = 0, y = 0; | |
Point size = getSize(); | |
if (item != null) { | |
Image image = item.getImage(column); | |
if (image != null) { | |
Rectangle imageSize = image.getBounds(); | |
int imageY = y + (int) (((float)size.y - (float)imageSize.height) / 2.0); | |
gc.drawImage(image, x, imageY); | |
x += imageSize.width; | |
} | |
x += (column == 0) ? 2 : 6; | |
int textY = y + (int) (((float)size.y - (float)gc.getFontMetrics().getHeight()) / 2.0); | |
gc.drawString(item.getText(column), x, textY); | |
} | |
if (isFocusControl()) { | |
gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK)); | |
gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE)); | |
gc.drawFocus(0, 0, size.x, size.y); | |
} | |
} | |
void tableFocusIn(Event event) { | |
if (isDisposed()) | |
return; | |
if (isVisible()) { | |
setFocus(); | |
redraw(); | |
} | |
} | |
void tableMouseDown(Event event) { | |
event.doit = true; | |
if (isDisposed() || !isVisible()) | |
return; | |
Point pt = new Point(event.x, event.y); | |
Rectangle clientRect = table.getClientArea(); | |
int columns = table.getColumnCount(); | |
int start = table.getTopIndex(); | |
int end = table.getItemCount(); | |
for (int row = start; row < end; row++) { | |
TableItem item = table.getItem(row); | |
for (int column = 0; column < columns; column++) { | |
Rectangle rect = item.getBounds(column); | |
if (rect.y > clientRect.y + clientRect.height) | |
return; | |
if (rect.contains(pt)) { | |
setRowColumn(row, column, true); | |
//setFocus(); | |
return; | |
} | |
} | |
} | |
} | |
void traverse(Event event) { | |
switch (event.detail) { | |
case SWT.TRAVERSE_ARROW_NEXT : | |
case SWT.TRAVERSE_ARROW_PREVIOUS : | |
case SWT.TRAVERSE_RETURN : | |
event.doit = false; | |
return; | |
} | |
event.doit = true; | |
} | |
void setRowColumn(int row, int column, boolean notify) { | |
if (0 <= row && row < table.getItemCount()) { | |
if (0 <= column && column < table.getColumnCount()) { | |
this.row = row; | |
this.column = column; | |
TableItem item = table.getItem(row); | |
table.showItem(item); | |
setBounds(item.getBounds(column)); | |
// redraw(); | |
if (notify) { | |
notifyListeners(SWT.Selection, new Event()); | |
} | |
} | |
} | |
redraw(); | |
updateVisible(); | |
} | |
@Override | |
public void setVisible(boolean visible) { | |
checkWidget(); | |
userVisible = visible; | |
resize(); | |
} | |
void resize() { | |
if (row >= 0 && row < table.getItemCount()) { | |
TableItem item = table.getItem(row); | |
setBounds(item.getBounds(column)); | |
} | |
updateVisible(); | |
} | |
void updateVisible() { | |
progVisible = false; | |
if (0 <= row && row < table.getItemCount()) { | |
if (0 <= column && column < table.getColumnCount()) { | |
progVisible = true; | |
} | |
} | |
super.setVisible(progVisible && userVisible); | |
} | |
public int getColumn() { | |
checkWidget(); | |
return column; | |
} | |
public TableItem getRow() { | |
checkWidget(); | |
if (table.getItemCount() == 0) | |
return null; | |
return table.getItem(row); | |
} | |
public void setSelection(int row, int column) { | |
checkWidget(); | |
setRowColumn(row, column, false); | |
} | |
public void setSelection(TableItem row, int column) { | |
checkWidget(); | |
setRowColumn(table.indexOf(row), column, false); | |
} | |
private void initAccessible() { | |
final Accessible accessible = getAccessible(); | |
if (accessAdapter == null) { | |
accessAdapter = new AccessibleAdapter() { | |
@Override | |
public void getName(AccessibleEvent e) { | |
String name = null; | |
TableItem item = null; | |
TableColumn[] tableColumns = table.getColumns(); | |
TableColumn thisCol = null; | |
if (row >= 0 && row < table.getItemCount() && column >= 0 && column < table.getColumnCount()) { | |
item = table.getItem(row); | |
if (column >= 0 && column < tableColumns.length) { | |
thisCol = tableColumns[column]; | |
} | |
} | |
if (item != null) { | |
if (thisCol != null) | |
name = thisCol.getText(); | |
if (name != null && name.length() > 0) | |
name = name + "=" + item.getText(column); //$NON-NLS-1$ | |
else | |
name = item.getText(column); | |
} | |
e.result = name; | |
} | |
@Override | |
public void getHelp(AccessibleEvent e) { | |
String help = null; | |
e.result = help; | |
} | |
@Override | |
public void getKeyboardShortcut(AccessibleEvent e) { | |
} | |
}; | |
accessControlAdapter = new AccessibleControlAdapter() { | |
@Override | |
public void getChildAtPoint(AccessibleControlEvent e) { | |
Point testPoint = toControl(new Point(e.x, e.y)); | |
int childID = ACC.CHILDID_NONE; | |
if (childID == ACC.CHILDID_NONE) { | |
Rectangle location = getBounds(); | |
location.height = location.height - getClientArea().height; | |
if (location.contains(testPoint)) { | |
childID = ACC.CHILDID_SELF; | |
} | |
} | |
e.childID = childID; | |
} | |
@Override | |
public void getLocation(AccessibleControlEvent e) { | |
Rectangle location = null; | |
int childID = e.childID; | |
if (childID == ACC.CHILDID_SELF) { | |
location = getBounds(); | |
} | |
if (location != null) { | |
Point pt = toDisplay(new Point(location.x, location.y)); | |
e.x = pt.x; | |
e.y = pt.y; | |
e.width = location.width; | |
e.height = location.height; | |
} | |
} | |
@Override | |
public void getChildCount(AccessibleControlEvent e) { | |
e.detail = 0; | |
} | |
@Override | |
public void getDefaultAction(AccessibleControlEvent e) { | |
String action = Messages.TableCursor_ScreenReader_Cell_Action1; | |
e.result = action; | |
} | |
@Override | |
public void getFocus(AccessibleControlEvent e) { | |
int childID = ACC.CHILDID_NONE; | |
if (isFocusControl()) { | |
childID = ACC.CHILDID_SELF; | |
} | |
e.childID = childID; | |
} | |
@Override | |
public void getRole(AccessibleControlEvent e) { | |
int role = 0; | |
int childID = e.childID; | |
if (childID == ACC.CHILDID_SELF) | |
role = ACC.ROLE_LISTITEM; | |
e.detail = role; | |
} | |
@Override | |
public void getSelection(AccessibleControlEvent e) { | |
e.childID = ACC.CHILDID_NONE; | |
} | |
@Override | |
public void getState(AccessibleControlEvent e) { | |
int state = 0; | |
int childID = e.childID; | |
if (childID == ACC.CHILDID_SELF) { | |
state = ACC.STATE_SELECTABLE; | |
if (isFocusControl()) { | |
state |= ACC.STATE_FOCUSABLE; | |
if (TableCursor.this.hasFocus) { | |
state += ACC.STATE_FOCUSED | ACC.STATE_SELECTED; | |
} | |
} | |
} | |
e.detail = state; | |
} | |
@Override | |
public void getChildren(AccessibleControlEvent e) { | |
e.children = null; | |
} | |
}; | |
selectionAccessListener = new Listener() { | |
public void handleEvent(Event event) { | |
accessible.setFocus(ACC.CHILDID_SELF); | |
} | |
}; | |
focusAccessListener = new Listener() { | |
public void handleEvent(Event event) { | |
accessible.setFocus(ACC.CHILDID_SELF); | |
} | |
}; | |
} | |
if (hasAccessibility) { | |
accessible.removeAccessibleListener(accessAdapter); | |
accessible.removeAccessibleControlListener(accessControlAdapter); | |
removeListener(SWT.Selection, selectionAccessListener); | |
removeListener(SWT.FocusIn, focusAccessListener); | |
hasAccessibility = false; | |
} | |
if (hasAccessibility == false) { | |
accessible.addAccessibleListener(accessAdapter); | |
accessible.addAccessibleControlListener(accessControlAdapter); | |
addListener(SWT.Selection, selectionAccessListener); | |
addListener(SWT.FocusIn, focusAccessListener); | |
hasAccessibility = true; | |
} | |
} | |
} | |