To disable vertical scroll bar i used the following syntax
table.getHorizontalBar().setEnabled(false);
But it is not working. It is ruining my application ui. How can i disable it?
Use option SWT.NO_SCROLL and SWT.V_SCROLL while constructing the table as following:
new Table (shell, SWT.NO_SCROLL | SWT.V_SCROLL);
You can't prevent the Table from showing its scrollbars if it wants to. However, if you give the table the space it requires, it should not have to display any scrollbars.
Note:
You can simply use SWT.NO_SCROLL in the constructor but if you want to update it later it won't be possible.
ScrollBar / Table position synchronization in Java SWT turorial
Probably, if you you want to disable a scrollbar, it means you want to synchronize the content with something else. Here is example how to do it with two tables, where content of main table called tableExcel is mirrored in extra table called tableRow (please, be aware the code may not be perfect, because I am beginner):
*Note: the code is not complete & shows only key things (beginners, please use Window Builder and then edit/add code):
1) As ankur.trapasiya mentioned, do this:
tableRow = new Table(sashFormExcel, SWT.BORDER | SWT.VIRTUAL | SWT.NO_SCROLL);
2) And (SWT.VIRTUAL will be required to do the workaround, and to load tables fast, it will load what you see & where you scroll)
tableExcel = new Table(sashFormExcel, SWT.BORDER | SWT.VIRTUAL);
3) Because I used sash-form for better navigation, insert this above point 1:
SashForm sashFormExcel = new SashForm(sashForm_Main, SWT.NONE);
4) Then implement this (after point 3):
synchronizer synchronize = new synchronizer(tableRow, tableExcel); tableExcel.getVerticalBar().addSelectionListener(synchronize);
5) Add this class (will synchronize the tables content position):
class synchronizer implements SelectionListener
{
Table t1, t2;
public synchronizer(Table tableRow, Table tableMain)
{
t1 = tableRow;
t2 = tableMain;
}
#Override
public void widgetSelected(SelectionEvent e) {
t1.setTopIndex(t2.getTopIndex());
}
#Override
public void widgetDefaultSelected(SelectionEvent e) {
// TODO Auto-generated method stub
}
}
6) And after point 2, add this (it will make to load main table fast, and will also synchronize your table where scroll bar is disabled):
tableExcel.addListener( SWT.SetData, new Listener() {
public void handleEvent( Event event ) {
TableItem item = (TableItem) event.item;
int index = event.index;
int page = index / PAGE_SIZE;
int start = page * PAGE_SIZE;
int end = start + PAGE_SIZE;
end = Math.min (end, virtTableExcel.size());
for (int i = start; i < end; i++) {
item = tableExcel.getItem (i);
item.setText (virtTableExcel.get(i));
tableRow.getItem(i).setText(Integer.toString(i));
}
}
});
And on top of code, add this:
private static Table tableExcel;
ArrayList<String[]> virtTableExcel = new ArrayList<String[]>();
final int PAGE_SIZE = 64;
private Table tableRow;
As it is mentioned in point 7, you take data from array list virtTableExcel, but to trigger point 6, use this (somewhere in code, after you generated virtTableExcel with data), where rowMax is integer which is equal to virtTableExcel.size(); :tableExcel.setItemCount(rowMax);
tableRow.setItemCount(rowMax);
Please, don't blame me for code, I used it from my application. But, all these bits might be useful for other beginners.
What work for me:
When declaring the TableViewer I do this:
this.viewer.getTable().getHorizontalBar().setVisible(false);
this.viewer.getTable().pack();
this.viewer.getTable().getParent().layout();
this.viewer.getTable().getParent().getParent().layout();
and when i am changing the viewer input i also call the three last lines.
I got this solution from: https://bugs.eclipse.org/bugs/show_bug.cgi?id=304128
Try
table.getHorizontalBar().setVisible(false);
Is't simple:
leftTable.getVerticalBar().addListener(SWT.Selection, event -> {
rightTableViewer.getTable().setTopIndex(leftTableViewer.getTable().getTopIndex());
});
and vice versa.
Related
We have fairly complicated model in a JTable. In new development I noticed that GUI does not refresh when I call fireTableChanged(...) for individual cells.
So, my question is:
What do I put into TableModelEvent - model row id or view row id?
Looking into JTable code (I have jdk1.8.0_202):
public class JTable extends JComponent implements TableModelListener, Scrollable,
...
public void tableChanged(TableModelEvent e) {
...
int modelColumn = e.getColumn();
int start = e.getFirstRow();
int end = e.getLastRow();
Rectangle dirtyRegion;
if (modelColumn == TableModelEvent.ALL_COLUMNS) {
// 1 or more rows changed
dirtyRegion = new Rectangle(0, start * getRowHeight(),
getColumnModel().getTotalColumnWidth(), 0);
}
else {
// A cell or column of cells has changed.
// Unlike the rest of the methods in the JTable, the TableModelEvent
// uses the coordinate system of the model instead of the view.
// This is the only place in the JTable where this "reverse mapping"
// is used.
int column = convertColumnIndexToView(modelColumn);
dirtyRegion = getCellRect(start, column, false);
}
I see that in order to calculate dirty region, it converts column index, but does not do the same for row.
What does "reverse mapping" comment mean?
Looks like a bug in Swing to me.
What do you think?
UPDATE
My code is simple:
model.fireTableChanged(new TableModelEvent(model, rowNumber, rowNumber, columnNumber));
GUI does NOT refresh the cell.
UPDATE2
The issue is in my model which is too complicated to post it here. :(
I cannot blame JTable. It is designed this way. The only possible addition to it is RowSorter, and in there it does correct conversion:
private void repaintSortedRows(ModelChange change) {
...
int modelIndex = change.startModelIndex;
while (modelIndex <= change.endModelIndex) {
int viewIndex = convertRowIndexToView(modelIndex++);
if (viewIndex != -1) {
Rectangle dirty = getCellRect(viewIndex, columnViewIndex,
false);
int x = dirty.x;
int w = dirty.width;
if (eventColumn == TableModelEvent.ALL_COLUMNS) {
x = 0;
w = getWidth();
}
repaint(x, dirty.y, w, dirty.height);
}
}
}
Thanks everybody. Sorry for disturbance.
My code is simple:
model.fireTableChanged(new TableModelEvent(model, rowNumber, rowNumber, columnNumber));
That is not how you change data in a JTable. You should NOT be invoking that method directly. It is the responsibility of the TableModel to invoke that method when data is changed.
The point of using a TableModelListener is to listen for changes in the TableModel. You only need to implement the listener if you want to do special processing AFTER the data has changed as I demonstrated in the link I provided in my comment.
If you have data in an existing cell and you want to change its value then can do something like:
model.setValueAt("new value", 0, 0);
If you want to add a new row of data you use:
model.addRow(...);
The point is all changes should be done via the TableModel.
Note the JTable also has a convenience setValueAt(...) method which will invoke the model for you.
I would like to use column percentage sizing to force the table to take on the width of the parent.
This does not work when I hide column(s) by default because the setColumnPercentageSizing() method does not seem to exclude hidden columns and does not correctly calculate the width.
Is there an easy way to adjust this in my code?
Example:
public void example(){
createGlazedListsGridLayer();
autoResizeColumns();
nattable.configure();
}
public GlazedListsGridLayer createGlazedListsGridLayer(){
SortedList<T> sortedList = new SortedList<>(eventList, null);
this.bodyDataProvider = new ListDataProvider<>(sortedList,
columnPropertyAccessor);
this.bodyDataLayer = new DataLayer(this.bodyDataProvider);
ColumnHideShowLayer columnHideShowLayer = new
ColumnHideShowLayer(bodyDataLayer);
// In this example, hide the first column
columnHideShowLayer.hideColumnPositions(Lists.newArrayList(0));
this.bodyLayerStack = new DefaultBodyLayerStack(new
GlazedListsEventLayer<>(columnHideShowLayer, eventList));
//...etc
}
protected void autoResizeColumns() {
glazedListsGridLayer.getBodyDataLayer().setColumnPercentageSizing(true);
nattable.addConfiguration(new DefaultNatTableStyleConfiguration() {
{
cellPainter = new LineBorderDecorator(new TextPainter(false,
true, 5, true));
}
});
}
UPDATE
It's not ideal but this is the closest I could get to it
public void adjustColumnWidth() {
getBodyDataLayer().setColumnPercentageSizing(false);
// Avoid the first column since it's hidden
for (int x = 1; x <= numColumns; x++) {
getBodyDataLayer().setColumnWidthByPosition(x,
getParent().getSize().x / numColumns, true);
}
}
UPDATE 2
Here are a couple of different things I tried in various combinations. None of them seem to keep the column hidden after a table is dynamically populated with data.
protected void enableAutoResizeColumns() {
getBodyDataLayer().setColumnPercentageSizing(true);
getBodyDataLayer().setDefaultColumnWidthByPosition(0, 0);
getBodyDataLayer().setColumnWidthByPosition(0, 0);
getBodyDataLayer().setColumnWidthPercentageByPosition(0, 0);
getNatTable().addConfiguration(new
DefaultNatTableStyleConfiguration() {
{
cellPainter = new LineBorderDecorator(new TextPainter
(false, true, 5, true));
}
});
}
Currently there is no solution for that. The reason for this is that the column widths are calculated in the DataLayer. The ColumnHideShowLayer sits on top of it and simply hides columns. It doesn't communicate back to the DataLayer that something is hidden.
In the end the ColumnHideShowLayer would need to re-trigger percentage size calculation based on the hidden state. But there is currently no API for that.
Feel free to create an enhancement ticket and provide a patch if you have an idea how to solve it.
well i am making this system that has a table, and i have to put buttons in the last column. i've been researching but all the codes i saw are really confusing. there is one tho, but there are still some parts that i didn't understand. here's the site where i got it http://www.javaquery.com/2013/05/how-to-implement-jbutton-in-jtable.html
String[] InvoiceArray = new String[20];
//Declare above variable globally. Used by two-three methods. Change variable name as per your need.
/*
* import the ButtonColumn class if you are not working in IDE
* I used formWindowOpened event to load content in Jtable but you can use other event.
* All you need is put the code with in that event.
*/
private void formWindowOpened(java.awt.event.WindowEvent evt) {
Object[][] rowData = new Object[4][2]; // 4: is number of row ; 2: is number of column
Object columnNames[] = {"Invoice No", "View Report"}; // Name of columns
for (int i = 0; i < 4; i++) {
InvoiceArray[i] = i + "-2345";
rowData[i][0] = i + "-2345";
rowData[i][1] = "View Order " + i; // Can change the text of button.
}
DefaultTableModel tm = new DefaultTableModel(rowData, columnNames);
jTable1.setModel(tm);
ButtonColumn buttonColumn = new ButtonColumn(jTable1, showOrder, 1); // 1: is column number. column count starts with 0,1,2...
}
what's the InvoiceArray for? and should i make the showOrder from the last line? and also, i didn't understand the code he posted on how to make a listener on it. here it is:
Action showOrder = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
//JTable table = (JTable) e.getSource(); // If you have multiple component following the ActionEvent
int modelRow = Integer.valueOf(e.getActionCommand());
if (InvoiceArray[modelRow] != null) {
/* We are placing invoice no in array
* And track the button click index
* And fetch index in invoice no
*/
System.out.println("Your Invoice No:" + InvoiceArray[modelRow]);
} else {
JOptionPane.showMessageDialog(rootPane, "No records found!");
}
}
};
i know there are some explanations already. i understand some of them but not all. just a simplier way to add jbutton on jtable and also listeners for the jbutton. thank you so much
just a simplier way to add jbutton on jtable and also listeners for the jbutton.
There is no simple way. You need to understand how renderers and editors work in a JTable. Read the section from the Swing tutorial on Concepts: Renderers and Editors for the basics.
Then you can check out Table Button Column which does the hard work for you. You only need to provide the Action to be invoked when you click on the button.
what's the InvoiceArray for?
It is used to load data into the JTable. This is basic usage of a JTable and has absolutely nothing to do with adding a button to a column of the table.
After the data is loaded you should forget about the invoiceArray. The Action you write should access the data via the TableModel or the JTable.
Is it possible to control whether a column should be available in a column control popup menu? I'm aware of toggling (Disable/enable using CheckBoxList) and gray-out the column. But I do not want column entry in popup menu as The column is must-have column in Jtable. I'm using the JXTable. Anyone that have any hints?
A TableColumnExt has a property hideable which effectly disables the hiding. It is still shown in the popup and you can toggle the checkbox (that's a bug, just filed - the menu item should be disabled ;), but at least the column isn't hidden. To work around the bug, you can implement a custom column control (as Robin correctly suggested) which doesn't add the checkbox, something like:
JXTable table = new JXTable(new AncientSwingTeam());
// here the hideable property is configured manually,
// in production code you'll probably have a custom ColumnFactory
// doing it based on some data state
table.getColumnExt(0).setHideable(false);
ColumnControlButton columnControl = new ColumnControlButton(table) {
#Override
protected ColumnVisibilityAction createColumnVisibilityAction(
TableColumn column) {
if (column instanceof TableColumnExt
&& !((TableColumnExt) column).isHideable())
return null;
return super.createColumnVisibilityAction(column);
}
};
table.setColumnControl(columnControl);
table.setColumnControlVisible(true);
As to not including the menu item: when introducing the hideable property, we decided to go for keeping the item in the list but disable it because users might get confused not seeing all columns in the control. So once the bug will be fixed (just done, committed as of revision #4315), I would recommend to remove the custom column control again. Just my 2 euro-cents, though :-)
ColumnControlButton#createColumnVisibilityAction looks like the method you are looking for. According to the documentation:
Creates and returns a ColumnVisibilityAction for the given TableColumn. The return value might be null, f.i. if the column should not be allowed to be toggled
you can return null for your case.
You should be able to plug this in by using the JXTable#setColumnControl method.
First way:
myTable().getColumnExt(_column_number_).setHideable(false);
This works smooth but has one UI drawback: text in menu is gray and thick is black - bad user experience.
So try to fix it, text will be gray and thick won't be here:
public class MyTable extends JXTable
{
public MyTable(AbstractTableModel model)
{
//first two columns won't be hiddeable
ColumnControlButton controlButton = new ColumnControlButton(this)
{
#Override
protected ColumnControlPopup createColumnControlPopup()
{
return (new NFColumnControlPopup());
}
class NFColumnControlPopup extends DefaultColumnControlPopup
{
#Override
public void addVisibilityActionItems(List<? extends AbstractActionExt> actions)
{
for(int i = 0; i < actions.size(); i++)
{
AbstractActionExt action = actions.get(i);
JCheckBoxMenuItem chk = new JCheckBoxMenuItem(action);
//Disabling unwanted items but they will be still shown for smooth user experience
if(i == 0 || i == 1)
{
chk.setEnabled(false);
chk.setSelected(false);
//chk.setIcon(new ImageIcon(Icons.class.getResource("check.png")));
}
else
{
chk.setSelected(true);
}
chk.addItemListener(action);
super.addItem(chk);
}
}
}
};
this.setColumnControl(controlButton);
}
}
and if you need to hide controls for "show horizontal scrollbar", "pack" and "pack all" add into code:
//remove items for horizontal scrollbar, pack and packall
this.getActionMap().remove("column.horizontalScroll");
this.getActionMap().remove("column.packAll");
this.getActionMap().remove("column.packSelected");
right after calling super(model)
I have window that shows log events in table.
For example, user is reading text in some row of table. When new logs come, they are added in the beginning of the table, and the row, the user was reading moves down. I have a requirement to prevent JScrollPane to scroll when new rows are added at the beginning of the table. I tried diffrent things but nothing helped. Can someone advice me how to implement this?
Thanks in advance!
Tricky task :-) The moving out happens because by default the scroll position is not adjusted in any way: adding rows above simply keeps the vertical scroll position which then points to a different row as before.
What's needed:
keep track of the visibleRect/last row before the insert
listen to model changes of type insert
calculate the new visible rect such that the old last row is scrolled back into the view
trigger the scroll
It's tricky because we need to listen to model changes. That listener is a neighbour of the modelListener registered internally by the table which updates itself on changes. So we need to be sure to act after the internal changes are done and at the same time use information before the internal update.
A dirty solution - depending on the usual sequence of swing listener notification which is last-added-first-notified (beware: DONT in production code! It will break easily, f.i. when changing the LAF) - is to gather the before-state when notified and adjust the scroll position wrapped in an invokeLater:
final DefaultTableModel model = new DefaultTableModel(20, 2);
for (int i = 0; i < model.getRowCount(); i++) {
model.setValueAt(i, i, 0);
}
final JTable table = new JTable(model);
Action action = new AbstractAction("add row at 0") {
#Override
public void actionPerformed(ActionEvent e) {
model.insertRow(0, new Object[] {});
}
};
JXFrame frame = wrapWithScrollingInFrame(table, "modify scroll");
TableModelListener l = new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
if (!TableUtilities.isInsert(e)) return;
final Rectangle r = table.getVisibleRect();
final int lastRow = table.rowAtPoint(
new Point(0, r.y + r.height - 5));
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
// assuming
// a) the insert was exactly 1 row
// b) was _before_ the old last row in view-coordinates
Rectangle cell = table.getCellRect(lastRow + 1, 0, true);
cell.translate(0, -r.height + cell.height);
cell.height = r.height;
table.scrollRectToVisible(cell);
}
});
}
};
model.addTableModelListener(l);
The appropriate way of getting the before-state cleanly depends on your exact context. You can keep track of the first/last row during the lifetime of the table, updating them in (one or all)
a ListSelectionListener to the row selectionModel (if the row the user is reading is always selected)
a ChangeListener to the vertical scrollBar
a ComponentListener to scrollPane's size changes