I want to pass the focus to 1st row of JTable. I can't use editCellAt(row,column) method because I don't want to allow user to change content of table. changeSelection(row,column,false, false) method also does not work properly.
Please suggest me some way to transfer focus to first row.
I have textfield and table. Initially my control is at textfield. If I press ENTER,control should go at first row of table. I have provided the code below. Please help me out.
private void txtRawMaterial(java.awt.event.KeyEvent evt) //txtRawMaterial is textfield
{
if (evt.getKeyCode()==KeyEvent.VK_ENTER)
{
if(table.getRowCount()>0) //table is jTable
{
table.requestFocus();
}
}
}
The two concepts of focus (of the JTable in its role as component) and selection (of rows/columns) are not necessarily related, if you want both you'll have to invoke separate methods to reach both:
table.requestFocus();
table.changeSelection(0, 0, false, false);
Also note that the focused cell is not a focusOwner in the usual sense. Instead, it is the lead of both column- and rowSelection models that typically has a visual clue like a thicker/different border than the others.
Related
I am building a JTreeTable. I found some starter code and have come pretty far. In the end my goal is to be able to have different data at different levels like a hierarchical list.
Currently, I have it working with data at different levels. However, I am running up against a wall when it comes to changing the columns as a next goal. From where I currently stand I have 3 more milestones:
Show different set of columns for different levels
Ability to adjust column widths for different levels
Ensure the JTree part of the table always stays to left
I am getting close to closing out this task but again stuck at the first of these 3.
Since creating a JTreeTable is complex, the minimum example leverages several class listed below in the image:
I am happy to post the code to any of those classes but I also did not want clog the question with useless code. First let me show the functionality I want.
The first image is when the top level is selected and the second image is when the second level is selected. Notice how the columns are different. That is what I want to happen in my application.
Top level selected:
Second level selected:
So one way I tried to solve this problem, is when the list selection is changed inside this section of code:
ListSelectionListener listener = (ListSelectionEvent e) -> {
TreeTableModelAdapter adapter = (TreeTableModelAdapter) JTreeTable.this.getModel();
//Need to see why this breaks.
JTreeTable.this.getTableHeader().setColumnModel(adapter.getColumnModel());
};
this.getSelectionModel().addListSelectionListener(listener);
This code is in the initialization of the JTreeTable. I have tried setting the column model on both the TableHeader and the table as well. Below is what happens then when I select a row:
The columns just disappear on me. The creation of the column model is happening in the TreeTableModelAdapter class with the following method:
public TableColumnModel getColumnModel(){
DefaultTableColumnModel model = new DefaultTableColumnModel();
for(int i=0;i<getColumnCount();i++){
TableColumn column = new TableColumn();
column.setIdentifier(getColumnName(i));
model.addColumn(column);
}
return model;
}
Any direction would be very helpful. Again happy to post any code you think could be helpful to answer the question. Just put a comment in and I will add it right away.
I will add the milestones as I find them in case this helps others, but for now this question is answered.
Milestone 1
I was actually able to solve the first milestone. The key is to trigger the creation of the columns of the column model, not to create a new column model. Below is the code for when the row selection is changed:
//Change columns depending on row
ListSelectionListener listener = (ListSelectionEvent e) -> {
createDefaultColumnsFromModel();
};
this.getSelectionModel().addListSelectionListener(listener);
This code creates the columns based on the row selected in the JTree part of the JTreeTable. The TreeTableModelAdapter implements the getColumnCount() and getColumnName() methods by also passing the selected row in the JTree to the JTreeTableModel so that the columns and their names are dynamically retrieved based on a particular node in the JTree. The key for this for me was trigger those to be called again to update the JTreeTable.
Milestone 2
Adjusting column widths based on the data level proved to be much more difficult than I had originally anticipated. In order to retain the cells state when the column model changed I had to disconnect the painting of the cells from it. This is a hairy process because this is done inside BasicTableUI and the method that gets the rectangle of the cell is private. So I had to subclass it, overload the paint() method and create my own methods that get called inside the paint method. There was a lot of copy pasting so that I could call normally private methods. I just renamed them and referenced these methods instead. The way the ui class was designed did not make it very flexible. Below is 2 images where I am selecting different levels and the columns are obviously different widths at different levels.
Milestone 3
I was able to make this work by keeping track of the view in the model. This seems very dirty to me as the model should separated from the view. Since the tree column's class is unique, I just returned the right class if that column was the first in the view.
The one problem I have with this technique is that I get unexpected behavior where the value returned is not consistent. I attempted to resolve this by overriding JTree.covertValueToText(). Since a JTree only expects 1 value and depending on the sequence of columns in the view this value could change. So in overriding this method I check the stored index for the JTree column's value. Again this causes the unexpected behavior. I will update the post if I find the fix.
In my JTable, I want to edit all relevant cells like in Excel.
I implemented that ENTER stops editing the cell and transfers focus to the next cell. However, when I hit ENTER in the last cell, the focus cycle makes me jump to the fist cell. But I want to continue outside the table and set focus to a JButton.
How can I break this cycle?
JTable is not a spreadsheet, but setCellSelectionEnabled() should allow you to proceed. Use setDefaultButton() as required, for example.
Addendum: In addition to setDefaultButton(), you can evoke any desired action in your custom Action, as shown here.
Ok so I think the title is all I've got to ask here.
My AbstractTableModel works fine, when I want to add an empty row, I use the following code to do so:
public void addRow() {
Object[][] oldData = data;
data = new Object[oldData.length + 1][3];
// Copy old data to new data
for (int x = 0; x < oldData.length; x++) {
data[x] = oldData[x];
}
// Append new row
data[oldData.length] = new Object[] {"", "", false};
fireTableRowsInserted(data.length - 2, data.length - 1);
}
Now, since I'm showing an empty row, I want the user to edit it, and I assume that the user will. Now, how do I make sure that the data is saved in my array as the user makes changes? Or, if that's not possible, what better alternative is there?
Ok so I should probably explain what I want to do:
I'm loading contents from a file, and displaying as a table. Now, the user might add new rows to the table on clicking the Add Row button. This will add 1 empty row per click. (In this image above is an instance when the button is pressed twice.
Now, I want that the user can edit the cells, and then maybe delete some rows (maybe) but on clicking the Save Database button, the updated data in the table is stores.
You may get a better understanding by comparing several approaches:
In this example, either a background thread or a button can add a new row to a table. The background thread sequences instances of Runnable on the EDT via invokeLater(). It is conceptually easier to understand, but it is also easier to get wrong.
SwingWorker encapsulates sequencing access to shared data. The API and tutorial show the basic approach of updating a component's model using publish()/process(). This preferred mechanism is more robust and scalable.
In this more advanced example, a Swing Timer paces an Executor that controls a series of SwingWorker instances.
In all cases, the tables remain functional. You might use any as a basis for an sscce.
Addendum: Could you explain a little more about the strategies you suggested?
I've updated the list of examples and suggested some things to look for in context.
Whenever there is a lostFocus inside a JTable, i need to capture the existing cell's row and column.
However, the condition below is always false because the source is always either a JTextField or a JComboBox.
public void focusLost(FocusEvent e) {
int row, col;
Object source = e.getSource();
if(((Component) source).getParent() instanceof JTable_Ext){ //<-- always false
table = (JTable_Ext) ((Component) source).getParent();
row = table.getSelectedRow();
col = table.getSelectedColumn();
}
To mitigate the above, i remember the row and col during FocusGained (as class level variable). The problem is, if the user click very fast all over the place within the JTable, somehow the row and column information will be out of sync.
Is there a way to get the Row and Col during FocusLost? if not, is there a better way of doing this?
Well, there is the oppositeComponent. The weird thing is, if this listener is attached to the table, the documentation tells that the table should be the "source" component (because it is a FocusLost event and the component that lost the focus is the table itself).
http://download.oracle.com/javase/1.4.2/docs/api/java/awt/event/FocusEvent.html#getOppositeComponent%28%29
Could you just keep a record of row & column using
table.getSelectionModel().addListSelectionListener(...);
table.getColumnModel().getSelectionModel().addListSelectionListener(...);
So every time the use clicks update it, not just on focus events?
If you just want to save the data that was entered in the cell (without hitting return), then you don't need to do anything. The updated information is contained within TableModel of the JTable.
Otherwise you can take a look at .tableChanged() and the associated TableModelEvent, which gives you the last row/column modified. You could keep a variable that is always updated to the latest event row/column. I guess that if you change a cell number without hitting return, it nonetheless registers as an event.
However, the condition below is always false because the source is always either a JTextField or a JComboBox.
This implies that the focusLost event is being generated when you begin editing a cell. So the question is why are you doing this? I think you need to state your actual requirement, because you attempted solution does not seem appropriate.
The desired behavior is akin to the mirrored text editing field provided in Excel when a given cell is selected, allowing more space to view the contents of the cell. I have a JTable with 5 columns and n rows. Column 2 holds expressions that can be arbitrarily long, thus I'd like to provide a separate JTextField to work with for editing the contents of the expression cell per row. The other fields are directly editable in the table. When the user clicks on a field in column 2, however, I want to send them to the text field. Any contents preexisting in the cell should be appear in the text field and additional edits in the text field should be mirrored in the table cell. Likewise, if someone double-clicks on the cell and edits it directly, I want those changes reflected in the text field. Thus, the user can choose to edit in either space and both are updated. Ideally, they are updated per keystroke, but update upon hitting return is acceptable.
So, far I've got the JTable, TableModel, TableModelListener, JTextField, ListSelectionListener, and AbstractAction, working together to provide most of the functionality described above. I'm missing the reflection of direct table cell edits to the text field and per-keystoke updates.
Are their ideas on how best to construct this behavior?
Well, if you want to get data from the table to the cell then you add the code to your TableModel's setValueAt() function, which should run when the user changes the content in an editable cell. I don't think that will update per-keystroke though.
If you want to move data from the textbox to the table cell use code like this
myJTextField.getDocument().addDocumentListener(new MyDocumentListener());
Where MyDocumentListener is an implementation of the javax.swing.event.DocumentListener interface
That will get you per-keystroke updates from the box to the table. But for the other way around it's a bit trickier.
There are two ways you might be able to go about doing it
1) Add a key listener to the table, and when the user starts typing check to see what table element is active, and intercept keystrokes as they type. That's kind of messy, though.
2) Another option might be to try to grab or replace the component that the table is using to actually let the user make the changes. I think that JTable actually allows you to change the editor component if you dig around.