Position a Table Row in the Center of my Window using SWT - java

I have a large table created with Java SWT which I can sort columnwise in various ways. But whenever I sort a column, the table starts displaying the 1st row and I could not find a way to move the table to display a specific row, e.g. the last selected row.
Any idea is appreciated!
Gerald

You can use `Table.setTopIndex" to set the row shown at the top of the table window. So to centre a row you might use something like:
static void showCentredRow(Table table, int rowToShow)
{
Rectangle clientArea = table.getClientArea();
int itemHeight = table.getItemHeight();
int visibleRows = clientArea.height / itemHeight;
int topRow = Math.max(rowToShow - (visibleRows / 2), 0);
table.setTopIndex(topRow);
}

Related

Set columnwidth of a table in XWPFTableCell (docx)

I'm generating a docx file with apache-poi. In the wordfile, I add tables, whose columns have a width I would like to see fixed.
Currently, I use the technique described here: http://apache-poi.1045710.n5.nabble.com/Is-there-a-way-to-set-the-width-of-a-column-in-XWPFTableCell-td5711491.html
Basically, this entails setting
cell.getCTTc().addNewTcPr().addNewTcW().setW(BigInteger.valueOf(cols[j]));
on each cell of that column.
The problem is that while the file opens perfectly in MS Word, open office interprets the values I set to the columnwidth differently. Whereas MS Word apparantly assumes 20-th of a point as units, open office seems to use points instead and therefore all columns are 20 times wider when I open the generated document in OO.
Usually when I see something weird in the generated output, I unpack the docx file, see what the value should be and change my code. But open office does not seem to be able to save to docx, so I can't change the value in OO save it back and see if Word still interprets the document correctly in order to find a cross-application solution.
Any idea how I set the width of the table column so that both OO and MS Wordt interprets it the same?
Don't touch single cells.
Add a GRID:
XWPFDocument doc = new XWPFDocument();
XWPFTable table = doc.createTable(1,2);
table.getCTTbl().addNewTblGrid().addNewGridCol().setW(BigInteger.valueOf(6000));
table.getCTTbl().getTblGrid().addNewGridCol().setW(BigInteger.valueOf(2000));
table.getRow(0).getCell(0).setText("1A");
table.getRow(0).getCell(1).setText("1B");
XWPFTableRow newrow = table.createRow();
newrow.getCell(0).setText("2A");
newrow.getCell(1).setText("2B");
The grid sets widths for entire columns. You don't need to do any cycles to set a width for every cell. It works in LibreOffice and GoogleDocs.
To watch the seted width in MS Word too, you may set widths of cells in the first row:
widthCellsAcrossRow(table, 0, 0, 4000);
widthCellsAcrossRow(table, 0, 0, 5000);
private static void widthCellsAcrossRow (XWPFTable table, int rowNum, int colNum, int width) {
XWPFTableCell cell = table.getRow(rowNum).getCell(colNum);
if (cell.getCTTc().getTcPr() == null)
cell.getCTTc().addNewTcPr();
if (cell.getCTTc().getTcPr().getTcW()==null)
cell.getCTTc().getTcPr().addNewTcW();
cell.getCTTc().getTcPr().getTcW().setW(BigInteger.valueOf((long) width));
}
Answer extracted from question:
It was recently pointed out to me that LibreOffice is able to save to docx. By changing the generated file and saving it back and decompiling the result, I have been able to resolve the issue.
Key is to put an explicit width to the table itself first. Word doesn't seem to care about its presence, and OpenOffice/LibreOffice are able to render the table correctly.
So, after creation of the table, I did as follows:
CTTblWidth width = table.getCTTbl().addNewTblPr().addNewTblW();
width.setType(STTblWidth.DXA);
width.setW(BigInteger.valueOf(9072));
Upon creation of the table, the layout is set to "auto" by default hence the width of the cell will always increase to follow the length of the string. As per OpenXML markup, it look's like
w:tblPr
w:tblLayout w:type="auto"
the solution is to set the layout to fixed and set the individual column length
w:tblPr
w:tblLayout w:type="fixed"
Here's the poi code for setting table layout:
XWPFTable table = document.createTable();
CTTblLayoutType type = table.getCTTbl().getTblPr().addNewTblLayout();
type.setType(STTblLayoutType.FIXED);
Here's how to set the individual width:
int[] cols = {
4896,
1872,
4032,
1728,
1440
};
for (int i = 0; i < table.getNumberOfRows(); i++) {
XWPFTableRow row = table.getRow(i);
int numCells = row.getTableCells().size();
for (int j = 0; j < numCells; j++) {
XWPFTableCell cell = row.getCell(j);
CTTblWidth cellWidth = cell.getCTTc().addNewTcPr().addNewTcW();
CTTcPr pr = cell.getCTTc().addNewTcPr();
pr.addNewNoWrap();
cellWidth.setW(BigInteger.valueOf(cols[j]));
}
}
column lengths are in twentieths of a point (dxa) or 1/1440 inch.
This is a major and very tricky element. I solved it using this own generic method of setting the widths of a table cell.
private static void setTableColumnWidths(XWPFTable table) {
table.getCTTbl().addNewTblGrid().addNewGridCol().setW(BigInteger.valueOf(2000));
table.getCTTbl().getTblGrid().addNewGridCol().setW(BigInteger.valueOf(3200));
table.getCTTbl().getTblGrid().addNewGridCol().setW(BigInteger.valueOf(1000));
table.getCTTbl().getTblGrid().addNewGridCol().setW(BigInteger.valueOf(1000));
table.getCTTbl().getTblGrid().addNewGridCol().setW(BigInteger.valueOf(1105));
table.getCTTbl().getTblGrid().addNewGridCol().setW(BigInteger.valueOf(1105));
}
Based on other answers...
public static void setTableColumnsWidth(XWPFTable table, long... widths) {
CTTblGrid grid = table.getCTTbl().addNewTblGrid();
for (long w : widths) {
grid.addNewGridCol().setW(BigInteger.valueOf(w));
}
}
Usage:
setTableColumnsWidth(table, 1440, 2700, 3000, 1440);

Create FormLayout rows and columns dynamically (at runtime)

Hello guys and ladies,
as I let the Eclipse WindowBuilder create me a JPanel with a FormLayout, I wanted to make this creation to be dynamical, because the program I'm writing needs it that way in order to avoid 1000 row long from. I used the following code:
JPanel pData = new JPanel();
pData.setBounds(10, 232, 381, 163);
FormLayout fLayout= new FormLayout(new ColumnSpec[]{}, new RowSpec[]{});
int numCols = 5;
int numRows = 10;
for(int i=1;i<=numCols;i+=2)
{
fLayout.insertColumn(i, FormFactory.RELATED_GAP_COLSPEC);
fLayout.insertColumn(i+1, FormFactory.DEFAULT_COLSPEC);
}
for(int j=1;j<=numRows;j+=2)
{
fLayout.insertRow(j, FormFactory.RELATED_GAP_ROWSPEC);
fLayout.insertRow(j+1, FormFactory.DEFAULT_ROWSPEC);
}
pData.setLayout(fLayout);
getContentPane().add(pData);
But starting the program, I get a stack of errors starting with:
"The column index 1 must be in the range [1, 0]"
Changing the index in the for-loop(s) simply changes the number in the middle of this error text, but the rest stays the same.
What am I doing wrong? Is it even possible to create a FormLayout dynamically? I'd really appreciate your help!
Additional Information:
The reason I'm using a FormLayout is the fact, the columns have different sizes. I know GridBagLayout can do so as well, but it needs many more lines and numbers to have the same result concerning insets and position. But if it's the only sensible alternative, I'll accept it ... as long as it's dynamical ;-)
It has to do with how the "insertRow()/insertColumn()" work. To insert something you must already have row/columns to insert in between. You should instead use the ".appendRow()/.appendColumn()" which just add a new row or column at the bottom of any existing rows or to the right of any existing columns.
EX:
int numCols = 2;
int numRows = 10;
for(int i=1;i<=numCols;i++)
{
fLayout.appendColumn(FormFactory.RELATED_GAP_COLSPEC);
fLayout.appendColumn(FormFactory.DEFAULT_COLSPEC );
}
for(int j=1;j<=numRows;j++)
{
fLayout.appendRow(FormFactory.RELATED_GAP_ROWSPEC);
fLayout.appendRow(FormFactory.DEFAULT_ROWSPEC);;
}
this.setLayout(fLayout);
This would add 4 columns(2 default and 2 related gaps) and 4 rows(2 default and 2 related gaps) to whatever already exists.

Retrieving JTable line content after user sorted content by clicking on column

I have a pane with two tables A and B. When a row is selected in A, the content of B should be upated.
My code detects row selections in A fine. But, when the user clicks on the column header to sort rows, it does not seem like this is taken into account in A's table model.
So, I can get the selected row number (which is correct considering the sorting), but when I try to retrieve row field content from A using its table model, it gives me the values as if rows had not been sorted.
How can I retrieve the content of the selected line from the selected row number?
Without any code, it is hard to say for sure what your problem is. However, it sounds like you are mixing up the row indices between the view and the model. You must be very clear about what co-ordinate system you are referring to (view or model) when you have a row number. See the JTable API for the convertRowIndexToModel and convertRowIndexToView methods.
You probably need something like this:
JTable table = ...;
TableModel model = ...;
int viewRow = table.getSelectedRow();
int modelRow = table.convertRowIndexToModel(viewRow);
int viewColumn = table.getSelectedColumn();
int modelColumn = table.convertColumnIndexToModel(viewColumn);
Object cell = model.getValueAt( modelRow, modelColumn );

How can I create a JTable where the first column is always in the JScrollPane viewport?

What's the best way to set up a table in a JScrollPane such that the first column is always on the screen in the same position regardless of horizontal scrolling and overlaps columns that pass underneath?
When the scrollbar is at the farthest left, the columns look normal, but as the user scrolls to the right, the secondary columns (2 and on) move underneath the first until the last column appears on the far right of the viewport?
I found a sample taken from Eckstein's "Java Swing" book that sort of does this, but it doesn't allow resizing of the first column. I was thinking of some scheme where one JPanel held a horizontal struct and a table holding the secondary columns and another JPanel which floated over them (fixed regardless of scrolling). The struct would be to keep the viewport range constant as the first column floated around. Ideally I could do it with two tables using the same model, but I'm not sure if the whole idea is a naive solution.
I'd ideally like a setup where multiple tables are on the same scrollpane vertically, where all their first columns are aligned and move together and there are just little horizontal gaps between the individual tables.
Fixed Column Table does most of what you need.
It does not support resizing the fixed column so you would need to add code like:
MouseAdapter ma = new MouseAdapter()
{
TableColumn column;
int columnWidth;
int pressedX;
public void mousePressed(MouseEvent e)
{
JTableHeader header = (JTableHeader)e.getComponent();
TableColumnModel tcm = header.getColumnModel();
int columnIndex = tcm.getColumnIndexAtX( e.getX() );
Cursor cursor = header.getCursor();
if (columnIndex == tcm.getColumnCount() - 1
&& cursor == Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR))
{
column = tcm.getColumn( columnIndex );
columnWidth = column.getWidth();
pressedX = e.getX();
header.addMouseMotionListener( this );
}
}
public void mouseReleased(MouseEvent e)
{
JTableHeader header = (JTableHeader)e.getComponent();
header.removeMouseMotionListener( this );
}
public void mouseDragged(MouseEvent e)
{
int width = columnWidth - pressedX + e.getX();
column.setPreferredWidth( width );
JTableHeader header = (JTableHeader)e.getComponent();
JTable table = header.getTable();
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = (JScrollPane)table.getParent().getParent();
scrollPane.revalidate();
}
};
JTable fixed = fixedColumnTable.getFixedTable();
fixed.getTableHeader().addMouseListener( ma );
JScrollPane has an area specifically for this, the row header (see the diagram in the API:)
All you need to do is:
- create an extra JTable for this fixed area
- hook it up to the first column of your data model
- set it as the row header
- and in the main table omit or remove the first column of data.
When the scrollpane scrolls up and down both tables will scroll in sync with no added code. When the scrollpane scrolls horizontally the row header is always kept visible and only the main table scrolls.
For most cases the only added code you'll need is for the column resizing, like camickr's example.
Check out this class, extracted from http://fahdshariff.blogspot.sg/2010/02/freezing-columns-in-jtable.html
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableModel;
public class FrozenTablePane extends JScrollPane{
public FrozenTablePane(JTable table, int colsToFreeze){
super(table);
TableModel model = table.getModel();
//create a frozen model
TableModel frozenModel = new DefaultTableModel(
model.getRowCount(),
colsToFreeze);
//populate the frozen model
for (int i = 0; i < model.getRowCount(); i++) {
for (int j = 0; j < colsToFreeze; j++) {
String value = (String) model.getValueAt(i, j);
frozenModel.setValueAt(value, i, j);
}
}
//create frozen table
JTable frozenTable = new JTable(frozenModel);
//remove the frozen columns from the original table
for (int j = 0; j < colsToFreeze; j++) {
table.removeColumn(table.getColumnModel().getColumn(0));
} table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
//format the frozen table
JTableHeader header = table.getTableHeader();
frozenTable.setBackground(header.getBackground());
frozenTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
frozenTable.setEnabled(false);
//set frozen table as row header view
JViewport viewport = new JViewport();
viewport.setView(frozenTable);
viewport.setPreferredSize(frozenTable.getPreferredSize());
setRowHeaderView(viewport);
setCorner(JScrollPane.UPPER_LEFT_CORNER,
frozenTable.getTableHeader());
}
}
Thereafter, just call the constructor method:
JTable table = new JTable( <yourData>, <yourColumns> );
FrozenTablePane frozenPane = new FrozenTablePane(table,1);//where 1 is the number of freezed column(s)
I think you are on the right track. What you conceptually have is a table with a 'header column' for each row. I would use two tables - one has the 'leftmost' column in and the other has all the others. Then I would present these in a JSplitPane with the 'leftmost column' table on the left and the rest on the right. There would be a single vertical scrollbar that controlled the y offset of both tables, and a single horizontal scrollbar controlling the right hand pane (only).
You can also use the advanced features of JScrollPane to set a 'header' component on the left of the main scroll area. I've never done it, but you might be able to use that as the 'headers' of your rows.

Auto adjust the height of rows in a JTable

In a JTable, how can I make some rows automatically increase height to show the complete multiline text inside? This is how it is displayed at the moment:
I do not want to set the height for all rows, but only for the ones which have multiline text.
The only way to know the row height for sure is to render each cell to determine the rendered height. After your table is populated with data you can do:
private void updateRowHeights()
{
for (int row = 0; row < table.getRowCount(); row++)
{
int rowHeight = table.getRowHeight();
for (int column = 0; column < table.getColumnCount(); column++)
{
Component comp = table.prepareRenderer(table.getCellRenderer(row, column), row, column);
rowHeight = Math.max(rowHeight, comp.getPreferredSize().height);
}
table.setRowHeight(row, rowHeight);
}
}
If only the first column can contain multiple line you can optimize the above code for that column only.
Camickr's solution did not work for me at all. My data model was dynamic though - it changed all the time. I guess the mentioned solution works for static data, like coming from an array.
I had JPanel for cell renderer component and it's preferred size wasn't set correctly after using prepareRenderer(...). The size was set correctly after the containing window was already visible and did repaint (2 times in fact after some unspecified, though short time). How could I call updateRowHeights() method shown above then and where would I do this? If I called it in (overriden) Table.paint() it obviously caused infinite repaints. It took me 2 days. Literally. The solution that works for me is this one (this is the cell renderer I used for my column):
public class GlasscubesMessagesTableCellRenderer extends MyJPanelComponent implements TableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
//this method updates GUI components of my JPanel based on the model data
updateData(value);
//this sets the component's width to the column width (therwise my JPanel would not properly fill the width - I am not sure if you want this)
setSize(table.getColumnModel().getColumn(column).getWidth(), (int) getPreferredSize().getHeight());
//I used to have revalidate() call here, but it has proven redundant
int height = getHeight();
// the only thing that prevents infinite cell repaints is this
// condition
if (table.getRowHeight(row) != height){
table.setRowHeight(row, height);
}
return this;
}
}
You must iterate over each row, get the bounding box for each element and adjust the height accordingly. There is no code support for this in the standard JTable (see this article for a solution for Java ... 1.3.1 =8*O).

Categories

Resources