I am working with a JTreeTable as described in an old article at: Sun Developer Network
Here are the same files but slightly adjusted for java 6:
http://edtaylor80.angelfire.com
If you run this little example program you will find that selection works as expected to start with, entire rows are selected when you click a random cell. This behaviour is desired. However, as soon as a node is expanded the behaviour changes, now it is only possible to select a row by clicking at the actucal node (name). I still want to be able to select an entire row by clicking a random cell. How can I modify the source code to accomplish this?
Before you click into the first column to open a node, there is no cell editor for the JTable. Once you perform that operation, the table has an active cell editor, which is an instance of the AbstractCellEditor that comes as part of the JTreeTable example source. In the implementation therein, you will find this:
public boolean shouldSelectCell(EventObject anEvent) { return false; }
This gets called by the BasicTableUI when it determines whether to adjust selection or not. As you can see, it will always return false. This is why, once you open a node, you will see this odd selection behavior.
While on the topic of tree tables, I recommend that you check out NetBeans' Outline. It is an easy to use implementation, much less convoluted than the JTreeTable example from Sun. You can find links and a demo in this post.
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.
For some reason, even though JList is, by default, a multi-select list, when one clicks another option, any choices you previously clicked get erased. Also, for some reason, one would have to hold down Ctrl and click, to make otherSelection
I tried to override this, by making custom ListModel that has selectedIndices stored to it, and adding MouseListener to my JList that, when activated, will append list.getSelectedIndices() to listModel.selectedIndices. However, there is unexpected problem with this approach: when stress-testing my solution (I made damn sure I was working out all of the inevitable programming mistakes before proceeding!), I found out that, for example, I could have the second list option selected, but click on the first, and list.getSelectedIndices() would return the index for the second option (and thus, my algorithm would fail).
Is there any way to make it so that every click would actually add (or remove) the index of the clicked list option to list.getSelectedIndices(), without my workaround?
Here is JAR file for testing (DEBUG == true so debug output is visible from command line).
I use JCheckBox as the ListCellRenderer
Then maybe you should be using a single column JTable with Boolean values. The state of the checkbox will be toggled every time you click on the cell.
When using a DefaultCellEditor in a JTable, there seem to be two different editing modes:
Single clicking on a cell does not display the editor component, but (for String/Object classes) one can still type in the selected cell and thereby edit its value.
Double clicking on a cell displays the editor component and one can edit the selected cell's value as expected.
Why is this? One would think that editing a JTable cell's value would always involve the same UI behavior.
Technically, double clicking a cell and typing in a cell (which supports text editing) essentially boils down to the same thing. You can actually double click a text editable cell to start the editing process
Cell editability comes to down to two factors, the result of TableModel#isCellEditable and TableCellEditor#isCellEditable, when these two methods return true a cell can be placed into edit mode.
A text editable cell is, generally, a special case, where the cell can be edited by typing in the cell while it has focus and double clicking (and pressing F2 in most cases).
The case for providing more than one mechanism for initializing the editing process will come down to decisions made over usability and existing conventions across multiple platforms. It's likely that some attempt was made to mix expectations where possible so users of different platforms could feel more comfortable with the process, but this is simply speculation.
The problem is, even amongst users of a single platform, there are different expectations about how something like this works, so rather the supply a single trigger, the system has been designed to allow for multiple triggers, where it would be applicable.
For example, it wouldn't make sense for a cell containing an image to allow the user to edit the cell simply by typing in it.
Updated
If you take a look at the isCellEdtiable method for DefaultCellEditor...
public boolean isCellEditable(EventObject anEvent) {
if (anEvent instanceof MouseEvent) {
return ((MouseEvent)anEvent).getClickCount() >= clickCountToStart;
}
return true;
}
You will note that the only event that actually stops a cell from entering edit mode is a MouseEvent, but only when the number of mouse clicks is less than clickCountToStart, which is set to 2 when using a JTextField as the editor, otherwise it's 1
I'm currently learning the adf framework, and while doing this I found myself in a situation I am not able to solve.
I have a tree component that works fine. I also have different forms, corresponding to different levels of the tree. Rather than having all of the forms visible at one time, I would like to only show the one that corresponds to the selected item in the tree.
To solve this, I created af:switcher, created facets and moved the forms there. Here is where I am lost, how do I tell the switcher to change the form? I tried to link them together using the facetName on the switcher, but no success. I suspect I did not link the right thing there, but I could not find anything helpful from the tree either! I assume it has something to do with the selectionListener and a bean, but I could not figure out a way to do this. Any clues?
See Sample #50 of http://www.oracle.com/technetwork/developer-tools/adf/learnmore/index-101235.html#CodeCornerSamples
Frank
I've got a blocking problem with the sorting functionality of a JTable; this made stall the development of the spare time open source project for 4 months now. Hope to be pointed into the right direction here.
Context: I'm working on extending the functionality of the ps3mediaserver to add a media library with pms-mlx. The UI of the media server has been done using swing.
Problem: When clicking on a column header in the JTable, a seemingly random column gets sorted instead of the one having been clicked.
Current implementation: Here's the description of the different components and classes being used for the implementation:
ETable: As alternate row colours aren't supported by default in the JTable, I've switched to the ETable extending the JTable. Source comes from here
FileDisplayTable: This is the class creating the table. In the init() method, the sorting is being enabled with 'table.setAutoCreateRowSorter(true);'
FileDisplayTableCellRenderer: Exists to always align cell content on the left
FileDisplayTableColumnModel: Does some mapping between internal types and column names
FileDisplayTableAdapter: This class implements com.jgoodies.binding.adapter.AbstractTableAdapter to map the objects with the table columns.
Possible solutions:
Preferably, I'd like to keep the current implementation and figure out how to correct the sorting, but I doubt someone can help me out with that!? Additionally their are some bits of code I had to add because of strange behaviours; they're commented in the code
The alternate option would be to change the JTable for another control altogether. I've made some research but didn't find the solution I was hoping for. The constraints are that
it must be embeddable in a swing UI
preferably it should support data bindings
support alternate row colours
row sorting
At some point it will be possible to open an editing dialogue, where the content of the row has to be retrieved, can be edited and when saved the row has to be updated.
Before reworking the entire thing I'd like to be sure the component will be able to handle all I want to do with it.
I'm more used to create GUIs using .NET in Visual Studio. It's quite different and a lot more difficult to do the same with swing. Please show me I'm wrong :)
[edit] If someone is willing to reproduce the problem, either get the source or the binaries, launch the application, navigate to the media library tab. In the Genral section import some videos by adding some video files. Go to the library section, click on apply to refresh the list and try to sort the table.
It may be useful to know that JTable columns can be dragged by the user. As a result, the view (JTable or a subclass) and model (an implementation of TableModel) may have different column numbers. Similarly, a RowSorter may affect the order or number of rows in the view as compared to the model. The related conversion methods are mentioned in How to Use Tables: Sorting and Filtering. In particular: "When using a sorter, always remember to translate cell coordinates."
Addendum: As an alternative, consider org.netbeans.swing.etable.ETable or it's subclass org.netbeans.swing.outline.Outline, depicted here.