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.
Related
I have a button with a ClickListener event and a method inside. The method is responsible for adding a new row in Vaadin GirdLayout once a button is clicked.
private GridLayout buildDeductionsGrid(){
GridLayout deductionsGrid = new GridLayout(13, 8);
deductionsGrid.setSpacing(true);
deductionsGrid.setWidth("50%");
addDeductionsGridLabelsAndFields(deductionsGrid);
return deductionsGrid;
}
This method creates a grid for rows to be inserted.
private void addDeductionsGridLabelsAndFields(GridLayout deductionsGrid) {
int rowIndex = 1;
btnAddDeductionRow.addClickListener((Button.ClickListener) event -> addNewDeductionRow(deductionsGrid, rowIndex));
}
rowIndex as it's name suggests is index of a row which needs to be incremented in addNewDeductionRow method. It increments without a problem the first time the button is clicked but it doesn't save the incrementation so when the button is clicked the second time, it tries to add a row at the same place when the button was clicked first.
private void addNewDeductionRow(GridLayout deductionsGrid, int rowIndex) {
String cbValue = deductionTypeDropdown.getValue().toString().toLowerCase();
for (DeductionsGridRow deductionsGridRow : deductionsGridRows) {
if (deductionsGridRow.getDeductionType().getName().toLowerCase().equals(cbValue)) {
deductionsGrid.addComponent(deductionsGridRow.getGridRowLabel(), 0, rowIndex);
deductionsGrid.addComponent(new Label("Amount"), 1, rowIndex);
deductionsGrid.addComponent(deductionsGridRow.getAmount(), 2, rowIndex);
}
}
rowIndex++;
}
This method does what it says - adds a new row.
Note: deductionsGrid and rowIndex are marked as final; I am using Vaadin 7.6.2
It seems your always passing 1 as the rowIndex into your function. The incrementation inside the function does not change the value outside of the function (and even if it would, 'outside' it is just another variable with local scope)
Maybe try to determine the rowIndex dynamically using GridLayout#getRows.
EDIT after comment
If the getRows() function always returns the predetermined grid size, then maybe the GridLayout itself is the problem here. It seems that it is intended to be used for laying out given content. When you are creating/changing content dynamically a list or table or some iterating component may be the right tool here
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 am using gwt2.3
My celltable contains 10 rows, 5 columns.
All cells in 1st row is empty,editable.
Whenever user clicks on column cell lets say 1st row X 3rd column
then user will edit that cell say "xyz".
after that when user click on button: "update column cell" then xyz value set to all cell present in that column.
I am using different celltype in celltable.
How to set/update all cell value in that particular column/page whose 1st cell is edited
Any help or guidance in this matter would be appreciated
Create a FieldUpdater in order to push back the changes to your Domain object. Then in the onClick callback of your button update your List with the values from the first row.
For example for an arbitrary TextInputColumn which takes a MyDTO class (can be any domain object) as the value type you can define following FieldUpdater:
myColumn.setFieldUpdater(new FieldUpdater() {
#Override
public void update(int index, MyDTO object, String value) {
// Push the changes into the MyDTO. At this point, you could send an
// asynchronous request to the server to update the database.
object.someField = value;
// Redraw the table with the new data.
table.redraw();
}
});
You have to set such a FieldUpdater for all 5 columns. (someField is the field in your DTO which you want to update).
Now in the onClick() callback of the button you have to update the actual list. Looks something like that:
update_column_cell.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
//Supose listDataProvider is the instance of your DataSource for your CellTable
List<MyDTO> list = listDataProvider.getList();
// get cell values for the first row (this is for one cell)
newSomeField = list.get(0).someField;
newSomeField2 = list.get(0).someField2;
for (int i = 1;i<list.size();i++) {
MyDTO dto = list.get(i);
if (newSomeField != null && newSomeField.isNotEmpty()) {
dto.someField = newSomeField;
}
if (newSomeField2 != null && newSomeField2.isNotEmpty()) {
dto.someField2 = newSomeField2;
}
}
}
})
This example only handles two fields of your DTO. You will probably have to extend it to cover all 5 fields you are showing as columns in your CellTable
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();
}
}
}
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.