What I'd like to do is be able to tab between elements in table.
I currently am creating my table like this.
this.tableViewer =
new TableViewer(parent , SWT.FULL_SELECTION);
tableViewer.setUseHashlookup(true);
table = tableViewer.getTable();
GridData gridData = new GridData(GridData.FILL_BOTH);
gridData.grabExcessVerticalSpace = true;
table.setLayoutData(gridData);
table.setLinesVisible(true);
table.setHeaderVisible(true);
...
/** Create the Cell Editor Array - will hold all columns **/
editors = new CellEditor[table.getColumnCount()];
/** Cell Editor Row 1 **/
/** Set the column properties **/
tableViewer.setColumnProperties(columnNames);
/** Assign the cell editors to the viewer **/
tableViewer.setCellEditors(editors);
/** Set the cell modifier for the viewer **/
tableViewer.setCellModifier(new MyCellModifier(this));
//Create the Table Viewer
/** Table Viewer Content and Label Provider **/
tableViewer.setContentProvider(new MyContentProvider(this));
tableViewer.setLabelProvider(new MyLabelProvider());
But I'm not sure how to set up the tabulation. Everything else works as far as editing columns, showing data, etc. Just stuck on this last part.
If I've missed obvious documentation or javadocs - my apologies and even pointing to those would be great.
Although the solution thehiatus posted is very low level and will probably work (I haven't tested it), JFace gives you a framework for this specific problem. See the org.eclipse.jface.viewers.TableViewerFocusCellManager along with org.eclipse.jface.viewers.CellNavigationStrategy classes to solve this problem.
I think by default tab does not jump from cell to cell in an swt table. Instead it traverses to the next control. So you'll also need to tell it not to traverse when tab is pressed
KeyListener keyListener = new KeyLisener()
{
public void keyPressed(KeyEvent evt)
{
if (evt.keyCode == SWT.TAB)
{
// There are numerous setSelection methods. I'll leave this to you.
tableViewer.getTable().setSelection(...)
}
}
public void keyReleased(KeyEvent evt){}
}
TraverseListener traverseListener = new TraverseListener()
{
public void keyTraversed(TraverseEvent evt)
{
if (evt.keyCode == SWT.TAB)
evt.doit = false;
}
}
tableViewer.getTable().addKeyListener(keyListener);
tableViewer.getTable().addTraverseListener(traverseListener);
Also, as derBiggi suggested, the listeners need to be added to the Table object, not the TableViewer.
I couldn't get the desired behavior with a TraverseListener (it would not traverse within the table), and I had trouble getting it to work with a FocusCellManager and CellNavigationStrategy. I finally found this solution that enables me to tab from column to column within a row and automatically activate the editor.
Viewer viewer = ...
TableViewerFocusCellManager focusCellManager =
new TableViewerFocusCellManager(
viewer,
new FocusCellHighlighter(viewer) {});
ColumnViewerEditorActivationStrategy editorActivationStrategy =
new ColumnViewerEditorActivationStrategy(viewer) {
#Override
protected boolean isEditorActivationEvent(
ColumnViewerEditorActivationEvent event) {
ViewerCell cell = (ViewerCell) event.getSource();
return cell.getColumnIndex() == 1 || cell.getColumnIndex() == 2;
}
};
TableViewerEditor.create(viewer, focusCellManager, editorActivationStrategy,
TableViewerEditor.TABBING_HORIZONTAL);
You need to add a KeyListener and set the selection or focus to the next cell:
tableViewer.getTable().addKeyListener(new KeyListener(){
public void keyPressed(KeyEvent e) {
System.out.println("Key Pressed");
if (e.keycode == SWT.TAB)
{
System.out.println("Detected TAB key");
// set table viewer selection
}
}
public void keyReleased(KeyEvent e) {
System.out.println("Key Released");
}}
);
I also had to implement tabbing between elements in a table. We use Grid from Nebula as the table.
Firstly, I had to suppress tabbing the focus preventing it from moving out of the table.
and then I added a Key Listener which moves the focus/selection to the next cell:
I also made my own algorithm to move the selection one cell to the right and when at the end of the row, move it to the beginning of the next row. When end of table is reached, the selection moves back to the first cell in the table.
This solved the problem for me.
Related
I am working on an Eclipse product which will open a wizard which content depends on previous user selection. I created a few WizardPages which read global parameters and then show the appropriate content.
But I am struggling with the newest page which should show a table which contains an arbitrary number of columns with an arbitrary number of rows.
Because the content of the table depends on user inputs on previous pages the creation of the Page is not done in public void createControl(Composite parent). In this function I create a global composite which will host the table which is created when the page gets visible because at that moment the needed information are available.
The number and titles of columns depend on user input just like the number of rows.
I added an example where the table should have 3 columns and ~15 rows. Every cell except the ones of the last column contain a checkbox. The cells of the last column contain text. But as you can see the most of the WizardPage is grey and on the bottom there is a small area where you can see the table. It is looking like there is some kind of overlay. Additionally the whole table looks like it is shifted to the left as u can only see two columns on the example picture.
If I add all the content in a ScrolledComposite not even the little area is visible. (Later I will change to ScrolledComposite because the table can get very big.) I debugged the project and I can see that the page is correctly build and the composite composite hosts only the table and no other elements. All column titles and number of rows created are correct but I am not able to display the table correctly.
I am thankful for every advice.
Code below:
public class selectionListPage extends CoevolutionDecisionPage implements SelectionListener {
private ScrolledComposite scrolledComposite;
private Composite composite;
private Table table;
private HashMap<String, String> parameters;
public selectionListPage(String name, HashMap<String, String> parameters) {
super(name);
this.setTitle(name);
this.parameters = parameters;
}
#Override
public void createControl(Composite parent) {
composite = new Composite(parent, SWT.NULL);
composite.setLayout(new GridLayout());
setControl(composite);
}
#Override
public void setVisible(boolean visible) {
super.setVisible(visible);
if (visible) {
onEnterPage();
}
}
public void onEnterPage() {
CoevolutionWizard wizard = (CoevolutionWizard)getWizard();
// this list will contain all titles for all columns
ArrayList<String> titles = new ArrayList<String> ();
/* read titles from wizard */
// create table for data
table = new Table(composite, SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION);
table.setLinesVisible (true);
table.setHeaderVisible (true);
table.setVisible(true);
// set layout data so that all cells are equal
GridData data = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
table.setLayoutData(data);
for (int i = 0; i < titles.size(); i++) {
TableColumn column = new TableColumn (table, SWT.NONE);
column.setText (titles.get(i));
}
// add table column for attributes
TableColumn column = new TableColumn (table, SWT.NONE);
column.setText ("Attribute");
// this list will contain all relevant elements for all inspected elements
ArrayList<ArrayList<String>> allElements = new ArrayList<ArrayList<String>> ();
/* read data from wizard */
// this counter indicates which column is default value for items
int elementCount = 0;
// every element has an own element of nested elements
for (ArrayList<String> list : allElements) {
// iterate over all nested elements
for (String element : list) {
// each row has one tableItem which contains all cells and meta data about items
TableItem item = new TableItem (table, SWT.NONE);
// for every column a cell will be created
for (int j = 0; j < titles.size()-1; j++) {
// create a checkbutton
Button checkButton = new Button(table, SWT.CHECK);
TableEditor editor = new TableEditor(table);
// pack button for correct display size and set listener
checkButton.pack();
checkButton.addSelectionListener(this);
// set size and alignment options for this cell
editor.minimumWidth = checkButton.getSize().x;
editor.horizontalAlignment = SWT.CENTER;
// add button to meta data of table item
item.setData("checkButton" + j, checkButton);
// add element to table
editor.setEditor(checkButton, item, j);
}
// add text
item.setText (titles.size(), titles.get(titles.size()-1));
// add additional meta data
item.setData("name", titles.get(titles.size()-1));
item.setData("default", titles.get(elementCount));
}
elementCount++;
}
for (int j = 0; j < titles.size(); j++) {
table.getColumn(j).pack();
}
composite.pack();
// proceeding from this page is possible at every moment
this.setPageComplete(true);
}
#Override
public void widgetSelected(SelectionEvent e) {
System.out.println("click");
}
Edit: Added code of CoevolutionDecisionPage
/**
* Superclass for all decision pages of the refactoring wizard.
*/
public abstract class CoevolutionDecisionPage extends WizardPage {
/**
* Creates a decision page for the refactoring wizard.
*
* #param refElem
* Refactoring element.
* #param decision
* Decision to show
*/
public CoevolutionDecisionPage(String name) {
super(name);
this.setPageComplete(false);
}
/**
* Is called as soon as data to execute the query delivering elements to
* decide is available. This is important because a decision can depend on
* previous decisions.
*/
abstract public void updateData();
}
Edit2: I did not solve the problem but I think I have some relevant information for the solution (at least I hope so).
At a moment of frustration I changed the Layout from the composite parent inside of createControl(Composite parent) to FillLayout and then the Wizard was divided into as many equal "columns" as I have WizardPages. Each column contains the correct WizardPage and displays it at the right time. So FillLayout behaves correct. But of course this is not a long term solution but allowed me to work on other tasks. And it allows me to verify the correct creation of the table.
So I added an other WizardPage after the page that is not working. This page only contains a number of labels (it is the last page and shows a summary). But this page shows the same strange behavior (if parent layout is not set to FillLayout) like my selectionListPage: It is "overlayed".
So I assume that the problem resides in one of the previous pages. In total I have four WizardPages:
Welcome page which shows some information (only text) which are read from "global" parameters.
A page where the user has to type names into an arbitrary number of textfields (which are created and deleted on the fly so that only one empty textfield is present every moment)
The described selectionListPage which contains a table.
The summary page.
The first to pages behave like expected but the other two are not displayed probably.
You need to update the composite layout, call
composite.layout(true, true);
just before the call to composite.pack() at the end of onEnterPage.
I have a table displayed in my Java GUI, which the user can add rows to by clicking an 'Add' button. The cells in the row that is added to the table are all editable by default, and the user can select each row/ cell as they wish.
I now want to add the functionality to remove a row from the table, but I can't seem to find the correct way to do this with a DefaultTableModel data type.
I have added the following code to the action listener for my 'remove row' button:
removeBtn.addActionListener(new ActionListener(){
public void removeRow(){
DefaultTableModel model = (DefaultTableModel)jEntityFilterTable.getModel();
model.removeRow();
}
});
However, the removeRow() method requires a parameter of type int (the index number of the row I want to remove). How can I get the 'selected row' from the DefaultTableModel? There doesn't appear to be a method that allows you to do this...
You can obtain the index from the table.
removeBtn.addActionListener(new ActionListener(){
public void removeRow(){
int selRow = jEntityFilterTable.getSelectedRow();
if(selRow != -1) {
DefaultTableModel model = (DefaultTableModel)jEntityFilterTable.getModel();
model.removeRow(selRow);
}
}
});
I'm building and app with multiple JTables and I need to detect when cell value change occurs so I can update it in the database. I tried TableModelListener and overriding tableChanged, but it fires only when I click away (click on another row) after I have edited a cell.
Any other way to do this?
You can implement the CellEditorListener interface, as shown in this example. Note that JTable itself is a CellEditorListener.
It may also be convenient to terminate the edit when focus is lost, as shown here:
table.putClientProperty("terminateEditOnFocusLost", true);
More Swing client properties may be found here.
I'm agreeing with #mKorbel - unless all your input is checkboxes and dropdowns, you're going to want to wait until the cell editing is stopped (you don't want to commit to the database every time a letter is typed in a textbox).
If the problem is that it's not committing after focus has gone to another component, add a FocusListener that stops editing the table when focus is lost on the table:
Example:
final JTable table = new JTable();
table.addFocusListener(new FocusAdapter() {
#Override
public void focusLost(FocusEvent e) {
TableCellEditor tce = table.getCellEditor();
if(tce != null)
tce.stopCellEditing();
}
});
I use the enter key so everytime a user hit enter the cell will update.
DefaultTableModel dtm = new DefaultTableModel(data, columnNames);
JTable table = new JTable(dtm);
table.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
int row = table.getSelectedRow();
int column = table.getSelectedColumn();
// resul is the new value to insert in the DB
String resul = table.getValueAt(row, column).toString();
// id is the primary key of my DB
String id = table.getValueAt(row, 0).toString();
// update is my method to update. Update needs the id for
// the where clausule. resul is the value that will receive
// the cell and you need column to tell what to update.
update(id, resul, column);
}
}
});
This is also handy if you want to stop the editing on an event handler from selection change or save button.
if (table.isEditing())
table.getCellEditor().stopCellEditing();
I'm trying to have a JTable edition working like in excel: if I start editing a cell, the cell becomes empty for receiving the new entry. The goal is to avoid the user having to delete the existing cell content first.
I can already detect cell start/stop edition by adding a PropertyChangeListener working as follow:
public void propertyChange(PropertyChangeEvent e)
{
// A cell has started/stopped editing
if ("tableCellEditor".equals(e.getPropertyName()))
{
if (table.isEditing())
processEditingStarted();
else
processEditingStopped();
}
}
Trying to clear the table model at processEditingStarted() does not work with the code bellow:
private void processEditingStarted()
{
SwingUtilities.invokeLater( this );
}
public void run()
{
row = table.convertRowIndexToModel( table.getEditingRow() );
column = table.convertColumnIndexToModel( table.getEditingColumn() );
oldValue = table.getModel().getValueAt(row, column);
newValue = null;
table.getModel().setValueAt(null, row, column);
table.updateUI();
}
However the editor content keeps the former value.
Does someone has clues to hide the current value of the cell during edition?
Optionnaly it should be able to restore the former cell content if the user did not actually modify the value.
I"ve never seen a spreadsheet remove data when you start editing a cell.
Table Select All Editor shows a different approach.
I did it like this. First I am using the event keyReleased and then getting the row and column number on which I am working and then setting the value at that row. Code goes like this.
private void purchases_TBLKeyReleased(java.awt.event.KeyEvent evt) {
int rowWorking = purchases_TBL.getSelectedRow();
int columnWorking = purchases_TBL.getSelectedColumn();
if(columnWorking==3){
model.setValueAt(null, rowWorking, columnWorking);
}
}
This makes the third column of the table null as soon as I focus move on to that using keyboard.
Note: The same piece of code can be placed in MouseClicked event.
Qt solution is a single call to resizeColumnsToContent(), in .NET one can use TextRenderer.MeasureText(), JTable could use AUTO_RESIZE_ALL_COLUMNS.
In SWT, is there a way to programmaticaly resize columns after populating them?
Calling computeSize(SWT.DEFAULT, SWT.DEFAULT) returns the same value thus disregarding character left overs in columns.
TableColumn has setWidth(), but how do I obtain the size hint for the current content taking into account font face, etc?
Solved with:
private static void resizeColumn(TableColumn tableColumn_)
{
tableColumn_.pack();
}
private static void resizeTable(Table table_)
{
for (TableColumn tc : table.getColumns())
resizeColumn(tc);
}
In many cases, the table entries change at run-time to reflect changes in the data model. Adding entry to the data model requires to resize columns as well, but in my case calling .pack() after the modification of the model does not solved completly the problem. In particolar with decorations the last entry is never resized. This seams to be due to async table viewer update. This snipped solved my problem:
public class LabelDecoratorProvider extends DecoratingStyledCellLabelProvider {
public LabelDecoratorProvider(IStyledLabelProvider labelProvider,
ILabelDecorator decorator, IDecorationContext decorationContext) {
super(labelProvider, decorator, decorationContext);
}
#Override
public void update(ViewerCell cell) {
super.update(cell);
if (TableViewer.class.isInstance(getViewer())) {
TableViewer tableViewer = ((TableViewer)getViewer());
Table table = tableViewer.getTable();
for (int i = 0, n = table.getColumnCount(); i < n; i++)
table.getColumn(i).pack();
}
}
}