I have to disable right click on JTableHeader so that user cannot right click over column header and drag to reposition the columns. Do we have any trick to do that? Just to mention left mouse button click works good but when user does RMB and drags the column, the column is moved and is repainted over the other columns when mouse is released.
Any help is appreciatted.
Triggering column drag/resizing with the right button clearly is a bug IMO.
A whacky workaround is to hook into the mouse/Motion/Listener installed by the uidelegate and silently eat all events which are not the left button. Something like (note: a more robust install of this wrapping listener which would survive a LAF switch is outlined in a recent answer):
public static class BugHook implements MouseListener, MouseMotionListener {
private JTableHeader header;
private MouseListener mouseDelegate;
private MouseMotionListener motionDelegate;
public BugHook(JTableHeader header) {
this.header = header;
MouseListener[] ls = header.getMouseListeners();
for (int i = 0; i < ls.length; i++) {
header.removeMouseListener(ls[i]);
String name = ls[i].getClass().getName();
if (name.contains("TableHeaderUI")) {
this.mouseDelegate = ls[i];
ls[i] = this;
}
}
for (MouseListener l : ls) {
header.addMouseListener(l);
}
MouseMotionListener[] motionLs = header.getMouseMotionListeners();
for (int i = 0; i < motionLs.length; i++) {
header.removeMouseMotionListener(motionLs[i]);
String name = motionLs[i].getClass().getName();
if (name.contains("TableHeaderUI")) {
this.motionDelegate = motionLs[i];
motionLs[i] = this;
}
}
for (MouseMotionListener l : motionLs) {
header.addMouseMotionListener(l);
}
}
// methods delegation left buttons only
#Override
public void mousePressed(MouseEvent e) {
if (!SwingUtilities.isLeftMouseButton(e)) return;
mouseDelegate.mousePressed(e);
}
#Override
public void mouseDragged(MouseEvent e) {
if (!SwingUtilities.isLeftMouseButton(e)) return;
motionDelegate.mouseDragged(e);
}
#Override
public void mouseReleased(MouseEvent e) {
if (!SwingUtilities.isLeftMouseButton(e)) return;
mouseDelegate.mouseReleased(e);
}
/// ---------- methods delegating always
#Override
public void mouseClicked(MouseEvent e) {
mouseDelegate.mouseClicked(e);
}
#Override
public void mouseEntered(MouseEvent e) {
mouseDelegate.mouseEntered(e);
}
#Override
public void mouseExited(MouseEvent e) {
mouseDelegate.mouseExited(e);
}
#Override
public void mouseMoved(MouseEvent e) {
motionDelegate.mouseMoved(e);
}
}
I tried with Java versions 1.7.0_11 and 1.6.0_38 and doing this:
table.getTableHeader().setReorderingAllowed(false);
will lock the columns in place. Are you perhaps using older Java version or doing the disabling some other way?
Related
I have:
class extended from JFrame;
an list of JTextField's elements - JTextField[] pix.
When clicking on pix[i] - JFrame must iconified and next click at any point of screen must changes exactly that textField (pix[i]) without any influence on another textFields, then frame must normalized and any mouseClicks after that (not on textField) couldn't influenced on that elements.
Clicks outside of JFrame processed with jnativehook library.
That part of code here:
for (int i = 0; i < pix.length; i++){
int tmp = i;
pix[i].addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
setState(Frame.ICONIFIED);
GlobalScreen.addNativeMouseListener(new NativeMouseAdapter(){
public void nativeMouseClicked(NativeMouseEvent e){
pixelChoose(pix[tmp]);
setState(Frame.NORMAL);
}
});
}
});
P.S.: I've tried to use
GlobalScreen.removeNativeMouseListener(new NativeMouseAdapter() {
public void nativeMouseClicked(NativeMouseEvent e) {
}
});
but don't actually know how to use this correctly.
P.S.[2]: if you have another solution of that question, you are welcome to type it into the answers - it will be great :>
EDIT!
I was buisy and now I'm here with solution:
NativeMouseAdapter adapter = new NativeMouseAdapter(){
public void nativeMouseClicked(NativeMouseEvent e){
pixelChoose(pix[tmp]);
setState(Frame.NORMAL);
GlobalScreen.removeNativeMouseListener(this);
}
};
MouseListener listener = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
setState(Frame.ICONIFIED);
GlobalScreen.addNativeMouseListener(adapter);
}
};
pix[i].addMouseListener(listener);
Add (after the setState) code to remove the MouseListener.
setState(Frame.NORMAL);
for (int i = 0; i < pix.length; i++){
pix[i].removeMouseListener(MouseAdapter::this);
}
pix must be effectively final, and I hope MouseAdapter::this works for the anonymous MouseListener.
MouseAdapter::this fails
Instead of
pix[i].addMouseListener(new MouseAdapter() {
hold the MouseListener in its own variable:
MouseListener cat = new MouseAdapter() { ... };
pix[i].addMouseListener(cat);
And later do in the inner callback do
pix[i].removeMouseListener(cat);
In the code where you create the mouse listener, you need to keep a reference.
NativeMouseAdapter adapter = new NativeMouseAdapter(){
public void nativeMouseClicked(NativeMouseEvent e){
pixelChoose(pix[tmp]);
setState(Frame.NORMAL);
}
}
GlobalScreen.addNativeMouseListener(adapter);
Then when you want to remove it, you use that reference.
GlobalScreen.removeNativeMouseListener(adapter);
NativeMouseAdapter adapter = new NativeMouseAdapter(){
public void nativeMouseClicked(NativeMouseEvent e){
pixelChoose(pix[tmp]);
setState(Frame.NORMAL);
GlobalScreen.removeNativeMouseListener(this);
}
};
MouseListener listener = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
setState(Frame.ICONIFIED);
GlobalScreen.addNativeMouseListener(adapter);
}
};
pix[i].addMouseListener(listener);
I am trying to make a chess program where I have an 8x8 array of JPanels which all require an addMouseListener but in this addMouseListener I need to make use of the index of that array for it to work, like this:
panels[0][0].addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
panels[0][0].setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
}
public void mouseReleased(MouseEvent e) {
}
});
Since I have 64 JPanels that means I need to copy this 63 times and possible changes need to be copied as well. Is there any better, more efficient way to achieve this?
Since I have 64 JPanels that means I need to copy this 63 times
You can write a generic listener
MouseListener ml = new MouseAdapter()
{
#Override
public void mousePressed(MouseEvent e)
{
JPanel panel = (JPanel)e.getSource();
panel.setBorder(...);
}
};
Then in your looping code you just do:
panels[?][?].addMouseListener( ml );
You should always attempt to write generic listeners so the code can be reused.
You should use a loop for this:
for (int r = 0; r < panels.length; ++r) {
for (int c = 0; c < panels[r].length; ++c) {
// Do this to fix the "must be final" error:
final int row = r;
final int col = c;
panels[row][col].addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
panels[row][col].setBorder(.....);
}
// ..... more
});
}
}
However, there are a few other additional ways to go about this. One is that you could write a class which saves the location of the panel:
class MyMouseListener extends MouseAdapter {
int panelRow;
int panelCol;
MyMouseListener(int panelRow, int panelCol) {
this.panelRow = panelRow;
this.panelCol = panelCol;
}
//.....
}
That is basically what the example using an anonymous class does behind the scenes. You could also save a reference to the panel itself.
Or you can use the getSource() method on the MouseEvent:
#Override
public void mousePressed(MouseEvent e) {
JPanel panelWhichWasClicked = (JPanel) e.getSource();
// .....
}
In that case, you only need 1 mouse listener which you can add to every panel.
When you have an array you must take different approach.
First your class should implement MouseListener which has 5 abstract methods but you are probably interested in mouseClicked:
public class Example implements MouseListener{
#Override
public void mouseClicked(MouseEvent e) {
JPanel panel = (JPanel) e.getSource(); // finding which panel is clicked on
}
#Override
public void mousePressed(MouseEvent e) {}
#Override
public void mouseReleased(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
}
Then somewhere inside your class you will do:
for(int i = 0; i < panels.length; i++){
for(int j = 0; j < panels[0].length; j++){
panels[0][0].addMouseListener(this);
}
}
for(int k=0;k< dtm.getRowCount();k++) //dtm is object of default table model
{
if(String.valueOf(dtm.getValueAt(k,1)).equalsIgnoreCase("Today") && check==0 )
{
cnt++;
JLabel jp=new JLabel();
panel.add(jp);
panel.setLayout(null);
if(cnt<=12)
{
jp.setBounds(j,500,100,100);
j=j+115;
jp.addMouseListener(this);
}
else
{
j=j-115;
jp.setBounds(j,400,100,100);
}
String b="<html><body text=#FDFA0B>"+String.valueOf(dtm.getValueAt(k,0))+"'s Birthday";
jp.setText(b);
jp.setFont(new java.awt.Font("comicbd",Font.ITALIC+Font.BOLD, 14));
}
}
It will not work mouselister only apply for last placed Label...
I want to apply mouse listener for all label how can I do that ..
please help me ....
Without SSCCE I can tell you that you're adding listener on 3 conditions:
String.valueOf(dtm.getValueAt(k,1)).equalsIgnoreCase("Today")
check == 0
and if(cnt<=12)
Other JLabels (that don't pass these conditions) haven't assigned your listener.
Make sure that you're clicking correct labels.
Or move jp.addMouseListener(this); just after JLabel creation (if you want to add listener to all your JLabels).
You certainly can add the same MouseListener to multiple components - here's an example in it's simplest form:
MouseListener ml = new MouseListener() {
#Override
public void mouseReleased(MouseEvent e) {System.out.println("Released!");}
#Override
public void mousePressed(MouseEvent e) {System.out.println("Pressed!");}
#Override
public void mouseExited(MouseEvent e) {System.out.println("Exited!");}
#Override
public void mouseEntered(MouseEvent e) {System.out.println("Entered!");}
#Override
public void mouseClicked(MouseEvent e) {System.out.println("Clicked!");}
};
JLabel j1 = new JLabel("Label1");
j1.addMouseListener(ml);
JLabel j2 = new JLabel("Label2");
j2.addMouseListener(ml);
BUT according to your code, you're messing with a JTable - and JTable's act differently than you're thinking. The labels you're trying to edit are actually part of a TableCellEditor. The JTable uses the single TableCellEditor (read: single JLabel) to display every cell in the JTable. This is why you're only seeing the Listener applied to the last cell (because that's the only the last cell has a full component any more - the rest are just ghosts of where the component was applied before).
The good news is you can add a MouseListener to the JTable, and obtain information from there:
final JTable table = new JTable();
MouseListener ml = new MouseListener() {
#Override
public void mouseReleased(MouseEvent e) {
System.out.println(table.getModel().getValueAt(table.getSelectedRow(), table.getSelectedColumn()));
}
#Override
public void mousePressed(MouseEvent e) {
System.out.println(table.getModel().getValueAt(table.getSelectedRow(), table.getSelectedColumn()));
}
#Override
public void mouseExited(MouseEvent e) {
System.out.println(table.getModel().getValueAt(table.getSelectedRow(), table.getSelectedColumn()));
}
#Override
public void mouseEntered(MouseEvent e) {
System.out.println(table.getModel().getValueAt(table.getSelectedRow(), table.getSelectedColumn()));
}
#Override
public void mouseClicked(MouseEvent e) {
System.out.println(table.getModel().getValueAt(table.getSelectedRow(), table.getSelectedColumn()));
}
};
table.addMouseListener(ml);
One Option is to add another inner class:
class MListener extends MouseAdapter{
public void mouseReleased(MouseEvent e) {}
//other mouse evetns
}
then rather then:
jp.addmousListener(this);
do:
jp.addMouseListener(new MListener());
I have been trying with no luck to get a JFormattedTextField to highlight on mouse click. I have been able to get it to work fine while tabbing through fields, however I would like to highlight everything on clicking.
I am only able to highlight on mouse click if I click and hold for about 1.5-2 seconds on the text field; I have no idea why.
I've searched and tried a few fixes including extending the class;
class HFTextField extends JFormattedTextField
{
HFTextField(MaskFormatter formatter)
{
super(formatter);
}
#Override
protected void processFocusEvent(FocusEvent e)
{
super.processFocusEvent(e);
if (e.getID() == FocusEvent.FOCUS_GAINED)
{
this.selectAll();
}
}
}
I am also defining a (rather verbose!) FocusListener which uses SwingUtilities.invokelater;
public static FocusListener CreateHighlightTextFieldFocusListener(final JTextField text_field)
{
FocusListener fl =
new FocusAdapter()
{
public void focusGained(FocusEvent evt)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
text_field.selectAll();
}
});
}
};
return fl;
}
and this is the function that creates formatted text fields;
public static JTextField CreateFormattedTextField(int x, int y, int width, int height,
Method action_method, Method changed_method, Method remove_method,
Method update_method, String mask_formatter, String banned_chars)
{
MaskFormatter formatter = null;
try {
formatter = new MaskFormatter(mask_formatter);
} catch (ParseException e) {
assert(false);
}
if(banned_chars != null)
formatter.setInvalidCharacters(banned_chars);
JTextField text_field = new HFTextField(formatter);
text_field.setBounds(x, y, width, height);
if(action_method != null)
{
text_field.addActionListener(CreateTextFieldActionListener(action_method, text_field));
}
text_field.getDocument().addDocumentListener(
CreateTextFieldDocumentListener(changed_method, remove_method,
update_method, text_field));
text_field.addFocusListener(CreateHighlightTextFieldFocusListener(text_field));
return text_field;
Any help would be greatly appreciated!
maybe you have got problems with EDT,
how method you use for/how you added value to JTextField
works with JTextField, JFormateddTextField, with JComboBox too, and with AutoCompleted funcionalies http://www.java2s.com/Code/Java/Swing-JFC/AutocompleteTextField.htm
private FocusListener focsListener = new FocusListener() {
#Override
public void focusGained(FocusEvent e) {
dumpInfo(e);
}
#Override
public void focusLost(FocusEvent e) {
//dumpInfo(e);
}
private void dumpInfo(FocusEvent e) {
//System.out.println("Source : " + name(e.getComponent()));
//System.out.println("Opposite : " + name(e.getOppositeComponent()));
//System.out.println("Temporary: " + e.isTemporary());
Component c = e.getComponent();
if (c instanceof JFormattedTextField) {
((JFormattedTextField) c).requestFocus();
((JFormattedTextField) c).setText(((JFormattedTextField) c).getText());
((JFormattedTextField) c).selectAll();
} else if (c instanceof JTextField) {
((JTextField) c).requestFocus();
((JTextField) c).setText(((JTextField) c).getText());
((JTextField) c).selectAll();
}
}
private String name(Component c) {
return (c == null) ? null : c.getName();
}
};
Try the following code
yourTextField.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusGained(java.awt.event.FocusEvent evt) {
SwingUtilities.invokeLater( new Runnable() {
#Override
public void run() {
yourTextField.selectAll();
}
});
}
});
I hate to give a simple answer, but have you tried using the MouseListener interface (or MouseAdapter class)?
Have you tried something like this:
fieldName.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
JTextComponent text = (JTextComponent) e.getSource();
text.selectAll();
}
});
Also, I would not recommend doing this asynchronously.
If you want specialized behavior for a mouse click, then add a MouseAdapter to your JTextFiled, and in the mouseClicked event handler, explicitly alter the background.
basically you can use this code (not sure that for each formatter and input masks), but for Number, Date and String you can use following, with ensure that this JFormattedTextField doesn't implements AutoCompleted
myTextField.addFocusListener(new FocusListener() {
#Override
public void focusGained(FocusEvent e) {
myTextField.requestFocus();
myTextField.setText(myTextField.getText());
myTextField.selectAll();
}
#Override
public void focusLost(FocusEvent e) {
}
});
sure you can pack that into InvokeLate...
How should I detect that column moved action is finished in JTable? I've added columnModeListener to my column model but the problem is columnMoved method is called every time a column moves (by certain pixels). I don't want this behavior. I just want to detect when the column dragging is finished.
columnModel.addColumnModelListener(new TableColumnModelListener() {
public void columnAdded(TableColumnModelEvent e) {
}
public void columnRemoved(TableColumnModelEvent e) {
}
public void columnMoved(TableColumnModelEvent e) {
//this is called so many times
//I don't want this, but something like column moved finished event
System.out.println("Moved "+e.getFromIndex()+", "+e.getToIndex());
}
public void columnMarginChanged(ChangeEvent e) {
}
public void columnSelectionChanged(ListSelectionEvent e) {
}
});
I hope it is clear what I'm looking for. Thanks.
This is what I ended up doing. I know it is dirty, but it fits for what I'm looking:
boolean dragComplete = false;
apTable.getTableHeader().addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
if (dragComplete) {
System.out.println("Drag completed");
}
dragComplete = false;
}
});
columnModel.addColumnModelListener(new TableColumnModelListener() {
public void columnAdded(TableColumnModelEvent e) {
}
public void columnRemoved(TableColumnModelEvent e) {
}
public void columnMoved(TableColumnModelEvent e) {
dragComplete = true;
}
public void columnMarginChanged(ChangeEvent e) {
}
public void columnSelectionChanged(ListSelectionEvent e) {
}
});
Here's an inner class I use to determine when the column ordering has changed. Note that the user may not have let go of the mouse at this point, so the dragging may continue further.
private class ColumnUpdateListener implements TableColumnModelListener {
int lastFrom = 0;
int lastTo = 0;
private void verifyChange(int from, int to) {
if (from != lastFrom || to != lastTo) {
lastFrom = from;
lastTo = to;
///////////////////////////////////////
// Column order has changed! Do something here
///////////////////////////////////////
}
}
public void columnMoved(TableColumnModelEvent e) {
verifyChange(e.getFromIndex(), e.getToIndex());
}
public void columnAdded(TableColumnModelEvent e) {
verifyChange(e.getFromIndex(), e.getToIndex());
}
public void columnRemoved(TableColumnModelEvent e) {
verifyChange(e.getFromIndex(), e.getToIndex());
}
public void columnMarginChanged(ChangeEvent e) {}
public void columnSelectionChanged(ListSelectionEvent e) {}
}
It's worked well for me.
This might be a better approach:
table.setTableHeader(new JTableHeader(table.getColumnModel()) {
#Override
public void setDraggedColumn(TableColumn column) {
boolean finished = draggedColumn != null && column == null;
super.setDraggedColumn(column);
if (finished) {
onColumnChange(table); // Handle the event here...
}
}
});
This is what works for me (both column movements and margin resizes):
I extend the table and override the columnMoved and columnMarginChanged
methods in the following way:
... first add some variables for state keeping
private int lastMovementDistance = 0;
private boolean bigMove = false;
private boolean resizeBegan = false;
...
#Override
public void columnMarginChanged(ChangeEvent e) {
super.columnMarginChanged(e);
if (isShowing()){
resizeBegan = true;
}
}
#Override
public void columnMoved(TableColumnModelEvent e) {
super.columnMoved(e);
//this will be set to 0 when the column is dragged
//to where it should begin if released
lastMovementDistance = Math.abs(getTableHeader().getDraggedDistance());
if (e.getFromIndex() != e.getToIndex()){
//note, this does not guarantee that the columns will be eventually
//swapped - the user may move the column back.
//but it prevents us from reacting to movements where
//the user hasn't even moved the column further then its nearby region.
//Works for me, because I don't care if the columns stay the same
//I only need the updates to be infrequent and don't want to miss
//changes to the column order
bigMove = true;
}
}
... then in the constructor of my table i do this:
public MyTable(){
...
getTableHeader().addMouseListener(new MouseAdapter(){
public void mouseReleased(MouseEvent evt) {
if (bigMove && lastMovementDistance == 0 ){
//react! the tables have possibly switched!
}
else if (resizeBegan){
//react! columns resized
}
resizeBegan = false;
bigMove = false;
}
});
...
}
It is kinda like a hack, but it works for me.
Nice answer on your own question ashokgelal. Just a little improvement I think. Your code also trigger on single click on the header. Using one more flag you can prevent the 'dragComplete' trigger when the column haven't really changed.
Modified code:
boolean mDraggingColumn = false;
boolean mColumnCHangedIndex = false;
tblObjects.getTableHeader().addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
if (mDraggingColumn && mColumnCHangedIndex) {
System.out.println("Column changed");
}
mDraggingColumn = false;
mColumnCHangedIndex = false;
}
});
tblObjects.getColumnModel().addColumnModelListener(new TableColumnModelListener() {
#Override
public void columnAdded(TableColumnModelEvent e) {}
#Override
public void columnRemoved(TableColumnModelEvent e) {}
#Override
public void columnMoved(TableColumnModelEvent e) {
mDraggingColumn = true;
if (e.getFromIndex() != e.getToIndex()) {
mColumnCHangedIndex = true;
}
}
#Override
public void columnMarginChanged(ChangeEvent e) {}
#Override
public void columnSelectionChanged(ListSelectionEvent e) {}
});
If I understand you correctly, maybe you want to look at the mouse listeners. Maybe the MOUSE_RELEASED event?
All answers fail at one use-case: if the table is in a layout filling up the entire window, then resizing the window will resize the table and thus its columns. By watching for mouse events on the column headers, we fail to receive the event when the user resize the window.
I looked at the JTable&friends source-code, and the columnMarginChanged() method is always called in a sub-sub-sub...-function called by JTable.doLayout().
Then, my solution is to watch for doLayout() calls that trigger at least one columnMarginChanged().
In fact, columnMarginChanged() is called for every columns.
Here is my solution:
private class ResizableJTable extends JTable {
private TableColumnModelListener columnModelListener;
private boolean columnsWereResized;
#Override
public void setColumnModel(TableColumnModel columnModel) {
if (getColumnModel() != null) {
getColumnModel().removeColumnModelListener(columnModelListener);
columnModelListener = null;
}
if (columnModel != null) {
columnModelListener = new TableColumnModelListener() {
public void columnSelectionChanged(ListSelectionEvent e) {
// Nothing to do
}
public void columnRemoved(TableColumnModelEvent e) {
// Nothing to do
}
public void columnMoved(TableColumnModelEvent e) {
// Nothing to do
}
public void columnMarginChanged(ChangeEvent e) {
columnsWereResized = true;
}
public void columnAdded(TableColumnModelEvent e) {
// Nothing to do
}
};
columnModel.addColumnModelListener(columnModelListener);
}
super.setColumnModel(columnModel);
}
#Override
public void doLayout() {
columnsWereResized = false;
super.doLayout();
if (columnsWereResized) {
onColumnsResized();
}
}
/**
* Sub-classes can override this method to
* get the columns-were-resized event.
* By default this method must be empty,
* but here we added debug code.
*/
protected void onColumnsResized() {
int[] columnSizes = getColumnSizes();
String sizes = "";
for (int i : columnSizes) {
sizes += i + " ";
}
System.out.println("COLUMNS RESIZED: [ " + sizes + "]");
}
protected int[] getColumnSizes() {
TableColumnModel columnModel = getTableHeader().getColumnModel();
int columnCount = columnModel.getColumnCount();
int[] columnSizes = new int[columnCount];
for(int i = 0; i < columnCount; i++) {
TableColumn column = columnModel.getColumn(i);
columnSizes[i] = column.getWidth();
}
return columnSizes;
}
}