So here's my problem. I have developed a plugin for intellij which contains a tool window, and a part of it contains a tabbed pane with a JXTreeTable like this
Looks great and works as expected until I start clicking on the rows. I'll explain the issue through screenshots.
Row selected
Move mouse pointer to next row
Select Next row
Repeat above procedure for the next couple of rows and I end with this!
urghh..puke
Now If I move my mouse pointer back up those again, The colors come back like magic!
Continue moving mouse back up along the rows..
Also at any point if my mouse pointer leaves the column..The view is back to normal again (except for the selected row of course)
My Look and Feel is IntelliJ Dark Look and Feel - com.intellij.ide.ui.laf.IntelliJLaf , which I found out using System.out.println(UIManager.getLookAndFeel())
If I change my look and feel programmatically to something else, This issue disappears. But i'm not in a position to alter the default look and feel so I have to fix this issue (or bug?).
I've used a hack to apply a different look and feel just for my JXTreeTable view component, and it worked great until.. I open another project on a new window (this creates a new instance of my tool window for the new project) and the hack this time around works against me, setting the look and feel properly for the new project instance, but resetting the look and feel back to default for my first project instance
Oh and i'm using the DefaultTreeCellRenderer for my treetable.
EDIT: I implemented a custom cell renderer for the treetable, setting it to transparent when not selected (like MadProgrammer suggested), but the issue is still there.
public class CustomCellRenderer extends JLabel
implements TreeCellRenderer {
#Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
if (selected){
setBackground(Color.BLUE);
setForeground(Color.WHITE);
}else{
setForeground(Color.BLACK);
setBackground(Color.WHITE);
setOpaque(false);
}
setText(value != null ? value.toString() : "<null>");
return this;
}
}
There is a slight change in the results now. lol
Compare with 4. above
EDIT 02:
So I decided to completely disable cell selection for my treetable using treeTable.setFocusable(false). Well guess what? Now when I click on a row nothing happens. No cell selections or colour changes. But once I move my mouse pointer to the next row as shown above (step 2), yup same story over again. Looks like it's related to a mouse listener somewhere? idk.. This is driving me nuts. Some help would be highly appreciated!
EDIT 03:
This seems to be the LAF that's giving me trouble
com.intellij.ide.ui.laf.darcula.DarculaLaf
source: https://github.com/JetBrains/intellij-community/blob/master/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLaf.java
EDIT 04
So I gave up SwingX and tried to implement a JTreeTable component from scratch, to see if this would fix the issue. I managed to put together an implementation using online source code. The results are interesting.
As you can see the issue is still there but unlike before, it does not propagate from cell to cell. So basically, it only shows on the current selected cell.
Anyway, the implementation I used, contains a MyTreeTable class which extends a JTreeTable class. The complete source code can be found here
If I instantiate JTreeTable instead of MyTreeTable the cell rendering issue disappears, but so does the collapse/expand feature of the JTreeTable. It basically turns into a flat tree. (useless!) So whatever that's causing the issue,
Is in the MyTreeTable class
Provides the collapse/expand behavior
to the tree component
after a bit of debugging I narrowed it down to the following method. If I remove this method from the MyTreeTable class, I can't expand/collapse the treetable, once it's included the cell rendering issue comes back.
public boolean editCellAt(int row, int column, EventObject e){
if(e instanceof MouseEvent){
MouseEvent me = (MouseEvent)e;
// If the modifiers are not 0 (or the left mouse button),
// tree may try and toggle the selection, and table
// will then try and toggle, resulting in the
// selection remaining the same. To avoid this, we
// only dispatch when the modifiers are 0 (or the left mouse
// button).
if(me.getModifiers()==0 ||
me.getModifiers()==InputEvent.BUTTON1_MASK){
for(int counter = getColumnCount()-1; counter>= 0;
counter--){
if(getColumnClass(counter)== TreeTableModel.class){
MouseEvent newME = new MouseEvent
(tree, me.getID(),
me.getWhen(), me.getModifiers(),
me.getX()-getCellRect(0, counter, true).x,
me.getY(), me.getClickCount(),
me.isPopupTrigger());
tree.dispatchEvent(newME);
break;
}
}
}
return false;
}
return super.editCellAt(row, column, e);
}
Anyway, this looks like a bug from the intellij side because it's look and feel specific and exists to some degree in both SwingX and a standard library JTreeTable implementation. So maybe i'm approaching this totally wrong. But still if there is a possible fix, please share your ideas. I need this issue sorted out. thanks
Related
I recently added Drag and Drop to my SWT TableViewer. That works well.
I am able to drag one Element from the source TableViewer to the target TableViewer and I can work with that.
Now comes my problem. I only want to be able to drag an element on another element. In other words I only want to be able to drop my element on another element and not in between two rows.
In the picture you can see what I mean. When dragging between two rows the bold line shows up. And I dont want that to happen. It should only highlight rows and the bold line should never appear.
Currently I'm working with validateDrop(...) so the drop doesn't work when I release there, but that's not a clean solution.
I hope you can help me.
Drag and Drop Example:
Assuming you are using a drop target listener that extends ViewerDropAdapter override determineLocation and always return LOCATION_ON:
#Override
protected int determineLocation(DropTargetEvent event)
{
return LOCATION_ON;
}
I have a JXTable where the users need to introduce data, then save it. Only the thing is, the user has to deselect the last edited cell before saving it. If they don't, the data of that cell isn't saved.
The only thing I thought of is to change the current selection automatically just before saving. This is what i tried :
table.getSelectionModel().setSelectionInterval(0, 0);
table.getColumnModel().getSelectionModel().setSelectionInterval(0, 0);
OR
table.getSelectionModel().setLeadSelectionIndex(0);
table.getColumnModel().getSelectionModel().setLeadSelectionIndex(0);
None of both seem to work yet these are the only two methods I found to do this.
Can anyone please tell me how to do this properly or propose an alternative to also let it save the data from that last cell?
I am assuming the user clicks on another component (JButton) when he wishes to save data. If you have a reference to the JXTable when that event happens you could add the following piece of code there:
if (table.isEditing()) {
table.getCellEditor().stopCellEditing();
}
The stopCellEditing() should save the state of the model and allow you to save all the contents, including the currently selected / edited cell.
EDIT: As kleopatra pointed out, the default (and better!) way to handle this is through the client property of JTable component:
table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
For JXTable this should already be set though, which indicates that the way your UI handling of the save functionality works does not include moving the focus away from the table. So in essence you'd be better off changing the focus when your 'save' event is being fired.
I'm trying to find a way to detect changes in which column the user selected in a JTable. I did some poking around and it appears that you need to somehow use a TableColumnModelListener in order to detect the changes, but that doesn't seem to fire an event when you change the column you have selected.
You need to add a ListSelectionListener instead. That will capture selection events. Here are some Swing tutorials that go further in depth:
http://download.oracle.com/javase/tutorial/uiswing/events/listselectionlistener.html
http://download.oracle.com/javase/tutorial/uiswing/components/table.html#selection
From what I read, I think you need to add a MouseListener to your table, which for example in mouseClicked will get the row and column using the following code, below:
table.addMouseListener(new MouseListener()
{
#Override
public void mouseClicked(MouseEvent e)
{
Point pnt = evt.getPoint();
int row = table.rowAtPoint(pnt);
int col = table.columnAtPoint(pnt);
}
}
It should work great for you I have used similar thing myself before.
BTW it look similar to the problem I found on coderanch, link:
http://www.coderanch.com/t/332737/GUI/java/detect-single-click-any-cell
Good luck, Boro
If by "change" you mean changing the value of a cell then you can use an AbstractTableModel and implement the fireTableCellUpdated method
I've been tasked with doing refactoring to a Java Swing application we have here that's pretty crufty. My job is to clean up the code and make it more dynamic as we're going to start using it with a second project and it needs to be able to adjust appropriately. There is a small portion of one window that contains
A button
A JFormattedTextField that is used to select dates.
A 3X4 table of JLabels that display data.
The person who originally wrote this simply used a GridBagLayout JPanel and then hardcoded everything, including the table's header and row label's and left empty JLabel's in the dynamic data position. When the dynamic information is received setText is called with the text data. Part of my refactoring will cause the entire table to be dynamic in dimension as well as content so I decided to make the table a sub-panel with a GridLayout and dynamically set the contents and dimensions with this piece of code:
public void updateInfoPanel(ArrayList rows) {
System.out.println("Updating Info Panel!");
//genericInfo is the new sub panel in question.
genericInfo.removeAll();
GridLayout layout = new GridLayout();
layout.setColumns(rows.get(0).length);
layout.setRows(rows.size());
genericInfo.setLayout(layout);
for(String[] row : rows) {
for(String element : row) {
genericInfo.add(new Label(element));
}
}
}
I have verified that this is only ever getting called one time per window creation but the entire window is now incredibly sluggish. It can take >5 seconds to respond to clicks in other parts of the frame that used to respond in fractions of a second. Does anyone know what would cause this? Is there something about GridLayouts that I don't understand?
Try calling this code on the EDT.
No it appears you understand GridLayouts. The problem is elsewhere, look at other code that you might have changed, and do some profiling to determine the true source of the slowdown.
I have a simple JTable, there are two columns that matter: quantity and value (Integers). Each time user enters a new row or updates one, each rows value must be multiplied by quantity, results sumed together and the result sum displayed in a JLabel outside the JTable. Looks pretty simple. Except that I have no idea what event should I look for. Something like "cell value changed"? When I right click on the JTable in NetBeans I see no such event or dont recognize it ;) Anyway, before I come up with some weird noobish solution I thought I might ask here what's the proper way to do it :)
you should add a TableModelListener as described here.
also, in your listener once you've updated the value of the other cell values programatically you will need to call model.fireTableCellUpdated to let swing know about the changes
Finally I managed to find how to do it in NetBeans with all the code protection, et cetera. It's right click on JTable in Design View, Properties, then Code tab, and then add your code in Pre-Adding Code section (code evaluated before table is added to container or something like that).
The exact code which works for me is this:
table.getModel().addTableModelListener(
new TableModelListener()
{
public void tableChanged(TableModelEvent evt)
{
// here goes your code "on cell update"
}
});
I am aware that Tom, above, suggested never calling getModel() but I'm too new to Java to understand why (care to explain, please..?) :) and it's just an example anyway, I'm adding this answer just to show how to do it in NetBeans (thanks pstanton for answering what to do). Because I found so many people asking this in internet and no real answers (apart from "copy your protected code out of NetBeans protected area and then customize your table).