Scrolling SWT Table programmatically - swt

How to do vertical scroll of SWT table programatically?
I'm implementing search function on the table. When an item was found then it will be scrolled to the item found.

There are several methods you might want to try:
Table.showItem(TableItem)
Table.showSelection()
Table.showColumn(TableColumn)
Table.setTopIndex(int)
Other than that, I suggest using a TableViewer from JFace. Then you'd scroll to an item with this method:
TableViewer.reveal(Object)

My full time job is to develop SWT (on Linux), I hope to be able to provide a comprehensive answer:
From a SWT code point of view (at least on on GTK), there are only 3 Table functions that affect scrolling via an internal native call gtk_tree_view_scroll_to_*()
setTopIndex();
showSelection();
showColumn();
To explain what they do & how to use them:
Vertical Scrolling
This is done by setting focus or selecting a particular table item.
For Table:
setTopIndex(int) // Btw for Tree it's setTopItem(..)
showSelection() // which is also reached via setSelection().
setTopIndex(int) moves the view programatically to the desired position.
Below is a modified version of [Snippet52][1] that performs the desired job:
public static void main (String [] args) {
Display display = new Display ();
Shell shell = new Shell (display);
Table table = new Table (shell, SWT.BORDER | SWT.MULTI);
Rectangle clientArea = shell.getClientArea ();
table.setBounds (clientArea.x, clientArea.y, 200, 200);
for (int i=0; i<128; i++) {
TableItem item = new TableItem (table, SWT.NONE);
item.setText ("Item " + i);
}
table.setTopIndex(95); // <<<< This is the interesting line.
shell.pack ();
shell.open ();
while (!shell.isDisposed ()) {
if (!display.readAndDispatch ()) display.sleep ();
}
}
showSelection() on the other hand scrolls the view to the currently selected item. This method is also called by various setSelection(..) methods.
I.e setSelection(..) is typically used to scroll to the desired item and set keyboard focus on it. This is useful if you search for an item in a tree and would like user input (e.g 'enter') to act upon the item that you found. Snippet52 (mentioned above) performs this task.
Now it's worth noting that setSelection(..) doesn't trigger selectionListeners(...), so calling this method wouldn't invoke the associated action.
Horizontal Scrolling
This is done by focusing on a particular column via 'showColumn()'.
Below is a sample snippet that creates some rows & columns and then
scrolls to the last column.
public static void main (String [] args) {
Display display = new Display ();
Shell shell = new Shell (display);
Table table = new Table (shell, SWT.BORDER | SWT.MULTI);
table.setHeaderVisible (true);
Rectangle clientArea = shell.getClientArea ();
table.setBounds (clientArea.x, clientArea.y, 100, 100);
String[] titles = {"First", "Second", "Third", "Fourth", "Fifth"};
for (int i=0; i<titles.length; i++) {
TableColumn column = new TableColumn (table, SWT.NONE);
column.setText (titles [i]);
}
for (int i=0; i<128; i++) {
TableItem item = new TableItem (table, SWT.NONE);
item.setText (new String [] {"" + i, ""+i, ""+i, ""+i});
}
for (int i=0; i<titles.length; i++) {
table.getColumn (i).pack ();
}
shell.pack ();
shell.open ();
display.asyncExec(
// Sometimes table column sizes are computed later at runtime,
// to get around it, set the column index after initialization.
() -> table.showColumn(table.getColumn(4))
);
while (!shell.isDisposed ()) {
if (!display.readAndDispatch ()) display.sleep ();
}
display.dispose ();
}
Note on Trees and Lists
Internally in SWT, Tree/Table/List all use the same native 'Tree' widget.
The above examples can be used for Lists and Tables as well, with the difference:
in Tree, setTopIndex(..) is setTopItem(..).
Lists don't have columns, so showColumn() is not applicable.
Let me know if you have further questions.

I don't really know what you need the search for, but you might also consider filtering the table to get to your desired element(kind of like a quick search).
Check it out:
http://eclipsesource.com/blogs/2012/10/26/filtering-tables-in-swtjface/
Hope it helps, cheers!

Related

Is there way to auto resize percentage columns when column group is collapsed/expaned in NatTable

I found ResizeColumnHideShowLayer class at nattable version 1.6.
(about https://bugs.eclipse.org/bugs/show_bug.cgi?id=521486)
That is work fine for normal column headers only.
But, if I collapse a column group, no adjust size to fit window. (no increasing column size)
How can I solve the problem?
Is there way to resize other columns to fit window automatically increase?
Thank you.
Currently not because the ColumnGroupExpandCollapseLayer is taking care of hiding collapsed columns.
I found solution by myself!
It works fine very well. :-)
I was run based on NatTable v1.6 version.(downloaded yesterday)
I think this is a basic feature, so I hope this feature will be included in the next NatTable version.
In narrow tables, behavior that collapsing column group means that may be someone want to view other column data more widely.
Overview (Problem screen and solved screen)
I explain using two application(before, after) screen shot.
Refer to bottom image if you want understand my issue easily at once.
Problem screen
enter image description here
Improved screen
enter image description here
Solution summary :
Add event listener to ColumnGroupExpandCollapseLayer.
      (HideColumnPositionsEvent, ShowColumnPositionsEvent)
Handle above events.
      Get column indices which is hidden by collapsed
      Execute MultiColumnHideCommand with the indices
Layer structure of my test code
↑ ViewportLayer (top layer)
| SelectionLayer
| ColumnGroupExpandCollapseLayer
| ResizeColumnHideShowLayer
| ColumnGroupReorderLayer
| ColumnReorderLayer
| DataLayer (base layer)
Implementation code is below:
void method() {
...
columnGroupExpandCollapseLayer.addLayerListener(new ILayerListener() {
#Override
public void handleLayerEvent(ILayerEvent event) {
boolean doRedraw = false;
//It works for HideColumnPositionsEvent and ShowColumnPositionsEvent
// triggered by ColumnGroupExpandCollapseCommandHandler
if (event instanceof HideColumnPositionsEvent) {
HideColumnPositionsEvent hideEvent = (HideColumnPositionsEvent)event;
Collection<Range> columnPositionRanges = hideEvent.getColumnPositionRanges();
Collection<Integer> convertIntegerCollection = convertIntegerCollection(columnPositionRanges);
int[] positions = convertIntPrimitiveArray(convertIntegerCollection);
//Execute command to hide columns that was hidden by collapsed column group.
MultiColumnHideCommand multiColumnHideCommand = new MultiColumnHideCommand(resizeColumnHideShowLayer, positions);
resizeColumnHideShowLayer.doCommand(multiColumnHideCommand);
doRedraw = true;
}else if (event instanceof ShowColumnPositionsEvent) {//called by ColumnGroupCollapsedCollapseCommandHandler
ShowColumnPositionsEvent showEvent = (ShowColumnPositionsEvent)event;
Collection<Range> columnPositionRanges = showEvent.getColumnPositionRanges();
Collection<Integer> positions = convertIntegerCollection(columnPositionRanges);
//Execute command to show columns that was hidden by expanded column group.
MultiColumnShowCommand multiColumnShowCommand = new MultiColumnShowCommand(positions);
resizeColumnHideShowLayer.doCommand(multiColumnShowCommand);
//Set whether or not to redraw table
doRedraw = true;
}
if (doRedraw) {
natTable.redraw();
}
}
/**
* Merge position values within several Ranges to Integer collection
*/
private Collection<Integer> convertIntegerCollection(Collection<Range> rangeCollection) {
Iterator<Range> rangeIterator = rangeCollection.iterator();
Set<Integer> mergedPositionSet = new HashSet<Integer>();
while (rangeIterator.hasNext()) {
Range range = rangeIterator.next();
mergedPositionSet.addAll(range.getMembers());
}
return mergedPositionSet;
}
/**
* Convert Integer wrapper object to primitive value
*/
private int [] convertIntPrimitiveArray(Collection<Integer> integerCollection) {
Integer [] integers = (Integer [])integerCollection.toArray(new Integer[integerCollection.size()]);
int [] positionPrimitives = new int[integers.length];
for (int i = 0 ; i < integers.length ; i++) {
positionPrimitives[i] = integers[i].intValue();
}
return positionPrimitives;
}
});
}

Change value of widget inside GWT flex table when other widget's value changes?

Ok, so I have a pretty specific and to me quite complicated issue, as I'm a GWT newbie.
I have a GWT flex table, which I use to dynamically add rows, whose cells contain GWT widgets. The row number changes, but the number of columns in static, always 6. Each row contains a cell with a remove button and five cells each with their own textbox.
What I need to do is somehow code a kind of relationship between the textbox in cell 6 of one row and the textbox in cell 5 in the next row (and vice versa).
To illustrate: when something changes in the textbox at [1,6] the content of textbox at [2,5] needs to be overwritten with the same value. If the textbox at [2,5] changes the textbox at [1,6] needs to change as well. I cannot use a button to commit the changes, it needs to happen via onValueChange or Blur or something similar, which doesn't require the user to perform a specific action.
My problem stems mostly from trying to figure out how to address specific cells in the flex table and their content. For the remove button the solution was easy enough with a click event handler, but for this issue I just can't seem to be able to come up with a solution.
Sadly I also cannot provide any of the code which I have up until now, since it's a business secret. I can only give a broad description of the problem like the one above.
EDIT:
Actually, it's probably more a problem of not having much code in terms of this specific problem.
What I have is a flex table, which has initially only the header row. Upon clicking a button below this table the addNewField() method is called, which just contains the creation, setting of default values and adding of the text fields into a new row.
addNewField() {
int rows = flextable.getRowCount();
Button removeBtn = new Button("x");
removeBtn.getElement().setId(Integer.toString(rows));
//then the button's event handler
TextBox name = new TextBox();
name.setText("something");
flextable.setWidget(rows, 0, "name");
//repeat 4 more times with incrementing columns for the other widgets
}
This way I add entire rows of editable TextBoxes. What I need is a way to influence the values of the 6th column TextBox of a chosen row and the 5th column TextBox of chosen row + 1.
EDIT2: I've tried the dirty option just to see how it would go and somehow the compare inside the if breaks the app. The compiler detects a nullpointerexception and I can't even debug it with breakpoints because it fails to compile and won't start. I can't figure out why though. I threw the code directly into the event for testing purposes, so pardon the ugliness.
TextBox bis = new TextBox();
bis.setText(rows + ":10:00");
subs.setWidget(rows, 5, bis);
bis.addValueChangeHandler(new ValueChangeHandler<String>()
{
#Override
public void onValueChange(ValueChangeEvent<String> event)
{
allRows: for (int i = 0; i < subs.getRowCount(); i++)
{
for (int j = 0; j < subs.getCellCount(i); j++)
{
if ( subs.getWidget(i, j) == bis )
{
TextBox widgetAtColumnSix = ((TextBox) subs.getWidget(i, 5));
String text = widgetAtColumnSix.getText();
TextBox widgetAtColumnFiveRowPlusOne = ((TextBox) subs.getWidget(i + 1, 4));
widgetAtColumnFiveRowPlusOne.setText(text);
break allRows;
}
}
}
}
});
EDIT: Since you edited your question and you dont want to use EventBus you could iterate over your FlexTable and set your TextBox value depending on your current rowIndex and cellIndex... Its not nice but it should work:
public class CellWidget extends Composite {
private TextBox nameBox;
public CellWidget() {
FlowPanel flowPanel = new FlowPanel();
Button deleteButton = new Button("x");
nameBox = new TextBox();
nameBox.addValueChangeHandler(new ValueChangeHandler<String>() {
#Override
public void onValueChange(ValueChangeEvent<String> event) {
notifiyTextBox(CellWidget.this, event.getValue());
}
});
flowPanel.add(nameBox);
flowPanel.add(deleteButton);
initWidget(flowPanel);
}
public void setText(String text) {
nameBox.setText(text);
}
}
public void notifiyTextBox(CellWidget source, String string) {
rows: for (int i = 0; i < flextable.getRowCount(); i++) {
columns: for (int j = 0; j < flextable.getCellCount(i); j++) {
if (flextable.getWidget(i, j) == source) {
CellWidget widgetAtColumnSix = ((CellWidget) flextable.getWidget(i, 5));
widgetAtColumnSix.setText(string);
CellWidget widgetAtColumnFiveRowPlusOne = ((CellWidget) flextable.getWidget(i + 1, 4));
widgetAtColumnFiveRowPlusOne.setText(string);
break rows;
}
}
}
}
I still would recommend using an eventbus. To make it even more convenient there is the GWT Event Binder lib, which makes using events a breeze.
So when you change a value in your textbox[2,5] it also fires your CustomEvent. All Widgets, that need to change their textbox value just need to catch...

SWT - TableViewer - Refreshing Selection

I have a button that runs a method. The method gets the selected Rows in the table and adds them to an arraylist. This works well the first time executed. But if the user selected the wrong row, they will be able to re select a different row and add that selection data to the arraylist.
But with my current the code, it does not matter what row the user selects the second time the first selected data is always added to the arraylist. It is like the selection needs to be reset or refreshed before the user selects the new row.
Button Code
Button pdfButton = new Button(composite, SWT.PUSH);
pdfButton.setText("Get Plotter List");
pdfButton.setEnabled(true);
pdfButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
getPlotterSelection();
}
});
Method Code
public void getPlotterSelection() {
selectedPlotters.clear(); <-- Clearing the ArrayList
int[] row = viewer.getTable().getSelectionIndices(); <-- Getting Current Selections
Arrays.sort(row);
if (row.length > 0) {
for(int i = row.length-1; i >= 0; i--){
PrinterProfile pp = new PrinterProfile(aa.get(i).getPrinterName(), aa.get(i).getProfileName());
selectedPlotters.add(pp);
}
}
viewer.getTable().deselectAll();
}
As I am writing this, I think maybe the problem is in the getSelectionIndices(). It seems to be getting the number of rows selected, but not the actual row number
Edit
The problem was in my logic. I was getting the correct Indices but using i varaible in the for loop to get the value.
for(int i = row.length-1; i >= 0; i--){
PrinterProfile pp = new PrinterProfile(aa.get(i).getPrinterName(), aa.get(i).getProfileName());
changed it to
aa.get(row[i].getPrinterName(), etc...
and it works like I thought it would
Since you are already using a TableViewer, why not get the selection from it?
IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
YourObject[] array = (YourObject[])selection.toArray();
Then you can iterate over the array and add them to your ArrayList.

Can user add new Sections in a view?

I am using eclipse 3.6 and developing RCP application with java 6.
I am using the Section and trying to let the use able to add new n-sections. I need the text in the field after that.
Now the User can see a section. I need that he is able to add a n-sections and then to write text in stopRouteStreet-field. I would like to read all the n Text written in this field.
Any idea how to do this?.
Here is my code
Section sectionStop = toolkit.createSection(form.getBody(), Section.DESCRIPTION|Section.TWISTIE|Section.TITLE_BAR);
td = new TableWrapData(TableWrapData.FILL);
td.colspan = 2;
sectionStop.setLayoutData(td);
sectionStop.addExpansionListener(new ExpansionAdapter() {
public void expansionStateChanged(ExpansionEvent e) {
form.reflow(true);
}
});
sectionStop.setText(Messages.SearchMapView_endPoint); //$NON-NLS-1$
Composite sectionClientStop = toolkit.createComposite(sectionStop);
sectionClientStop.setLayout(new GridLayout());
final Composite stopComposite = toolkit.createComposite(sectionClientStop, SWT.NONE);
final GridLayout gridLayoutStop = new GridLayout();
gridLayoutStop.numColumns = 2;
stopComposite.setLayout(gridLayoutStop);
toolkit.createLabel(stopComposite, Messages.SearchMapView_Street);
stopRouteStreet = toolkit.createText(stopComposite, "", SWT.BORDER); //$NON-NLS-1$
sectionStop.setClient(sectionClientStop);
You need a global variable (a HashMap would do), that saves a mapping between each newly created Section and the Text control.
// define global field
HashMap <Section, Text> dynamicControls = new HashMap <Section, Text> ();
// after you create the text field, save the newly created Text field
....
...
dynamicControls.put(section, text);
// Later when you need to read the values in all the text fields
for(Section s: dynamicControls.keySet()){
Text textField = dynamicControls.get(s);
System.out.println(textField.getText());
}

Editable SWT table

How to edit SWT table Values without Using Mouse Listeners?
Do the TableEditor snippets in the below link help?
SWT Snippets
The first example in the TableEditor section uses a SelectionListener on the table (unlike the second example which uses a MouseDown event you mentioned you don't want)
You could perhaps make use of the TraverseListener or KeyListener too to help you achieve what you want.
final int EDITABLECOLUMN = 1;
tblProvisionInfo.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
// Clean up any previous editor control
final TableEditor editor = new TableEditor(tblProvisionInfo);
// The editor must have the same size as the cell and must
// not be any smaller than 50 pixels.
editor.horizontalAlignment = SWT.LEFT;
editor.grabHorizontal = true;
editor.minimumWidth = 50;
Control oldEditor = editor.getEditor();
if (oldEditor != null)
oldEditor.dispose();
// Identify the selected row
TableItem item = (TableItem) e.item;
if (item == null)
return;
// The control that will be the editor must be a child of the
// Table
Text newEditor = new Text(tblProvisionInfo, SWT.NONE);
newEditor.setText(item.getText(EDITABLECOLUMN));
newEditor.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent me) {
Text text = (Text) editor.getEditor();
editor.getItem()
.setText(EDITABLECOLUMN, text.getText());
}
});
newEditor.selectAll();
newEditor.setFocus();
editor.setEditor(newEditor, item, EDITABLECOLUMN);
}
});
Here tblProvision is the name of your table. you can just now edit Your table by clicking on it. I have Declare EDITABLECOLUMN. this is the column that u want to edit.
If you can use JFace as well and not just pain SWT, have a look at the JFace Snippets, especially
Snippet036FocusBorderCellHighlighter - Demonstrates keyboard navigation by highlighting the currently selected cell with a focus border showing once more the flexibility of the new cell navigation support
Snippet034CellEditorPerRowNewAPI - Demonstrates different CellEditor-Types in one COLUMN with 3.3-API of JFace-Viewers
You can get or set the value of a item, for example:
Table table = new Table(parent, SWT.NONE);
TableItem item = new TableItem(table, SWT.NONE);
item.setText("My new Text");
I suggest you to us TableViewer, it is very powerful table which it you can use databinding very easy too.