Tooltip visibility in SWT - java

I have a Class which displays a label. When I hover the mouse over that label there is a empty tooltip being displayed. I would want to remove that tooltip(I mean I dont want it to be shown). I can easily say tooltip.setVisibility(false) but I should not change the code inside the MouseTrackListener Anonymous class. I need to use a toltip property pr anythin else so that when I extend this class I need to have an option to easily set the visibility of this tooltip if needed or disable it if not needed.
Here is my Code snippet (JAVA SWT)
tooltip = new ToolTip(parent.getShell(), SWT.NONE);
MouseTrackListener mouseTrackListener = new MouseTrackListener() {
#Override
public void mouseEnter(MouseEvent e) {
if (text != null && !text.isEmpty()) {
tooltip.setLocation(Display.getCurrent().getCursorLocation().x,
Display.getCurrent().getCursorLocation().y + TOOLTIP_OFFSET_Y);
tooltip.setVisible(true);
}
}
#Override
public void mouseExit(MouseEvent e) {
if (text != null && !text.isEmpty()) {
tooltip.setVisible(false);
}
}
#Override
public void mouseHover(MouseEvent e) {
}};
label.addMouseTrackListener(mouseTrackListener);
iconLabel.addMouseTrackListener(mouseTrackListener);

Not quite sure of what you want to do. Just hide empty tooltips or get control over when a tip is shown?
Maybe this fits your needs:
private boolean showToolTip = true;
tooltip = new ToolTip(parent.getShell(), SWT.NONE);
public void setShowToolTip (boolean show){
showToolTip = show;
}
MouseTrackListener mouseTrackListener = new MouseTrackListener() {
#Override
public void mouseEnter(MouseEvent e) {
if (text != null && !text.isEmpty()) {
tooltip.setLocation(Display.getCurrent().getCursorLocation().x,
Display.getCurrent().getCursorLocation().y + TOOLTIP_OFFSET_Y);
if (showToolTip){
tooltip.setVisible(true);
} else {
tooltip.setVisible(false);
}
}
Or do you want a solution in which the code in your instance of MouseTrackListerner is not changed?

You could try ToolTip's setAutoHide(boolean autoHide) property. (not used it, so not sure)
if(text == null || text.isEmpty()) {
tooltip.setAutoHide(true);
}
Does this help?

Related

apply shift_right or shift_left on multiple lines

i have a source viewer. In this, i need to shift multiple lines to right, when tab is pressed and remove tab, on these lines, when Shift+Tab is pressed. Below is my code. I m not sure how to proceed further. Can you please suggest how this can be done.
public class MyViewer extends SourceViewer {
public MyViewer(final Composite parent, final int styles) {
super(parent, null, styles);
final Font font = JFaceResources.getFont(MyConstants.EDITOR_TEXT_FONT);
getTextWidget().setFont(font);
getTextWidget().addTraverseListener(new TraverseListener() {
public void keyTraversed(final TraverseEvent e) {
if (e.detail == SWT.TRAVERSE_TAB_NEXT || e.detail == SWT.TRAVERSE_TAB_PREVIOUS) {
if (areMultipleLinesSelected()) {
e.doit = true;
final ITextSelection textSelection = (ITextSelection) FastCodeTemplateViewer.this.getSelection();
//what to do after this, to apply, SHIFT_LEFT or SHIFT_RIGHT, on the selected content
}
});
#Override
public void setDocument(final IDocument document) {
...
}
#Override
public void setHyperlinkPresenter(final IHyperlinkPresenter hyperlinkPresenter) throws IllegalStateException {
...
}
}
The SourceViewer already defines an operation to shift text left and right. There is no need to extend the SourceViewer. The operations can be invoked from the traverse listener just like this:
sourceViewer.getTextWidget().addTraverseListener( new TraverseListener() {
#Override
public void keyTraversed( TraverseEvent event ) {
if( event.detail == SWT.TRAVERSE_TAB_NEXT ) {
if( sourceViewer.canDoOperation( ITextOperationTarget.SHIFT_RIGHT ) ) {
sourceViewer.doOperation( ITextOperationTarget.SHIFT_RIGHT );
}
event.doit = false;
} else if( event.detail == SWT.TRAVERSE_TAB_PREVIOUS ) {
if( sourceViewer.canDoOperation( ITextOperationTarget.SHIFT_LEFT ) ) {
sourceViewer.doOperation( ITextOperationTarget.SHIFT_LEFT );
}
event.doit = false;
}
}
} );
Setting the doit flag to false prevents the traveral from taking place, i.e. transferring the focus to the previous/next control.
This has the side effect that the selection is replaced by a tab character after it was shifted. To prevent this, a VerifyKeyListener can help if it suppresses the tab key if anything is selected.
textViewer.prependVerifyKeyListener( new VerifyKeyListener() {
#Override
public void verifyKey( VerifyEvent verifyEvent ) {
if( verifyEvent.keyCode == SWT.TAB && sourceViewer.getSelectedRange().y > 0 ) {
verifyEvent.doit = false;
}
}
} );
You may figure out a less 'hacky' solution if you study the code of the Eclipse Text Editor which also offers this feature. In particualr the interaction between ShiftAction and TextEditor.

GWT TextArea - Resize Handler

Some browsers show for HTML Textareas a handle to resize the textbox. Is there any way to react to this resize events in GWT?
I tried it with this code, but the event is not triggered:
TextArea textArea = new TextArea();
textArea.addHandler(new ResizeHandler() {
#Override
public void onResize(ResizeEvent event) {
System.out.println("resize");
}
}, ResizeEvent.getType());
"It doesn't look like you can specifically attach events to resizing a textarea. The resize event fires when the window is resized."
https://stackoverflow.com/a/2096352/1467482
The question already is two years old, but for them who are brought by Google to this question I have a solution.
You can use the mouse-down, mouse-move and mouse-up events to react on resizing of the text area. Here is a skeleton code:
TextArea textArea = new TextArea();
...
textArea.addMouseDownHandler(new MouseDownHandler() {
private HandlerRegistration mouseMoveUpRegistration;
private int lastWidth;
private int lastHeight;
#Override
public void onMouseDown(MouseDownEvent event) {
lastWidth = getOffsetWidth();
lastHeight = getOffsetHeight();
if (mouseMoveUpRegistration == null) {
mouseMoveUpRegistration = Event.addNativePreviewHandler(new NativePreviewHandler() {
#Override
public void onPreviewNativeEvent(NativePreviewEvent event) {
if (event.getTypeInt() == Event.ONMOUSEMOVE || event.getTypeInt() == Event.ONMOUSEUP) {
int width = getOffsetWidth();
int height = getOffsetHeight();
if (width != lastWidth || height != lastHeight) {
// do whatever you want to to while still resizing
lastWidth = width;
lastHeight = height;
}
if (event.getTypeInt() == Event.ONMOUSEUP) {
// do whatever you want to do when resizing finished
// perhaps check actual and initial size to see if the size really changed
/* mouse up => additionally remove the handler */
if (mouseMoveUpRegistration != null) {
mouseMoveUpRegistration.removeHandler();
mouseMoveUpRegistration = null;
}
}
}
});
}
}
});

BasicScrollBarUI: Label instead of Button - Can't scroll

I have made a custom BasicScrollBarUI.
I have replaced the incrButton / decrButton by JLabels.
Therefore i had to override the InstallListeners(), to add the Listener to the incr/decrLabel instead of the JButtons.
//TODO
protected void installListeners() {
trackListener = createTrackListener();
buttonListenerCustom = createArrowButtonListenerCustom();
modelListener = createModelListener();
propertyChangeListener = createPropertyChangeListener();
scrollbar.addMouseListener(trackListener);
scrollbar.addMouseMotionListener(trackListener);
scrollbar.getModel().addChangeListener(modelListener);
scrollbar.addPropertyChangeListener(propertyChangeListener);
// scrollbar.addFocusListener(getHandler());
if (incrLabel != null) {
incrLabel.addMouseListener(buttonListenerCustom);
System.out.println("OK gemacht");
}
if (decrLabel != null) {
decrLabel.addMouseListener(buttonListenerCustom);
}
scrollListener = createScrollListener();
scrollTimer = new Timer(scrollSpeedThrottle, scrollListener);
scrollTimer.setInitialDelay(300); // default InitialDelay?
}
Therefore i had to override the ArrowButtonListener, to react on the Labels.
protected class ArrowButtonListenerCustom extends MouseAdapter {
boolean handledEvent;
public void mousePressed(MouseEvent e) {
if (!scrollbar.isEnabled()) {
return;
}
// not an unmodified left mouse button
// if(e.getModifiers() != InputEvent.BUTTON1_MASK) {return; }
if (!SwingUtilities.isLeftMouseButton(e)) {
return;
}
int direction;
if (e.getSource() == incrLabel) {
direction = 1;
incrLabel.setIcon(new ImageIcon(increaseButtonPressedImage));
} else {
direction = -1;
decrLabel.setIcon(new ImageIcon(decreaseButtonPressedImage));
}
scrollByUnit(direction);
scrollTimer.stop();
scrollListener.setDirection(direction);
scrollListener.setScrollByBlock(false);
scrollTimer.start();
handledEvent = true;
if (!scrollbar.hasFocus() && scrollbar.isRequestFocusEnabled()) {
scrollbar.requestFocus();
}
}
public void mouseReleased(MouseEvent e) {
scrollTimer.stop();
handledEvent = false;
scrollbar.setValueIsAdjusting(false);
incrLabel.setIcon(new ImageIcon(increaseButtonImage));
decrLabel.setIcon(new ImageIcon(decreaseButtonImage));
}
}
Of course createArrowButtonListenerCustom() returns a new instance of ArrowButtonListenerCustom.
Now my Problem:
When I click on the incr/decrLabel, the List scrolls correctly, but the thumb of the ScrollBar doesn't move (or better: the Thumb isn't repainted. If I move the Mouse over the thumb, it gets repainted on the right place). I have the same problem, when I scroll with the MouseWheel.
I don't understand, why this doesn't work.
Thanks for your help!

JavaFX 2 TreeView - How to change default behavior of entering edit mode?

Inspired by the JavaFX tutorial on http://docs.oracle.com/javafx/2/ui_controls/tree-view.htm I am wondering how could I change the behaviour to enter a cell in edit mode. The behaviour I would like to get is
on one left mouse-click: just select the cell
on two left mouse-clicks: select cell and invoke some action
on right-mouse-click: enter cell in edit mode
I tried to install a mouse event handler on the TreeView/TreeCell but it seems that the event is already consumed by TreeCellBehavior.
In class TreeCellBehvior there is the following method:
private void simpleSelect(MouseEvent e) {
TreeView tv = getControl().getTreeView();
TreeItem treeItem = getControl().getTreeItem();
int index = getControl().getIndex();
MultipleSelectionModel sm = tv.getSelectionModel();
boolean isAlreadySelected = sm.isSelected(index);
tv.getSelectionModel().clearAndSelect(index);
// handle editing, which only occurs with the primary mouse button
if (e.getButton() == MouseButton.PRIMARY) {
if (e.getClickCount() == 1 && isAlreadySelected) {
tv.edit(treeItem);
} else if (e.getClickCount() == 1) {
// cancel editing
tv.edit(null);
} else if (e.getClickCount() == 2/* && ! getControl().isEditable()*/) {
if (treeItem.isLeaf()) {
// attempt to edit
tv.edit(treeItem);
} else {
// try to expand/collapse branch tree item
treeItem.setExpanded(! treeItem.isExpanded());
}
}
}
}
I am not sure if can replace the TreeCellBehavior with my own implementation. Though this method is private I am not sure if this would be the right way to go. Any idea?
I worked it out by myself. I disable the editable of TreeView by default. For each TreeItem there is a context menu allowing to change the items name. If context menu action is invoked the TreeView is set to editable and TreeView.edit() with the current TreeItem is invoked. Now startEdit() is called behind the scenes and edit mode is active.
However I have got some strange behavior after enter is pressed and commitEdit() is called. This method checks if the cell is still in edit mode (which it is and therefore returns true) causing an internal invocation of cancelEdit()?!?! As a workaround I introduced a commitModeProperty and check in cancelEdit() if it is set.. otherwise the new text value would never be set.
Here is my code:
public class FolderTreeCell extends TreeCell<FolderCellType> {
// workaround for a strange behaviour in commitEdit.. see initTextFieldListener()
private BooleanProperty commitModeProperty = new SimpleBooleanProperty(false);
public FolderTreeCell() {
assert Platform.isFxApplicationThread();
}
private ContextMenu createContextMenu() {
MenuItem menuItem = new MenuItem("Change folder name");
menuItem.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent evt) {
getTreeView().setEditable(true);
getTreeView().edit(getTreeItem());
}
});
return new ContextMenu(menuItem);
}
private void initTextFieldListener() {
getItem().textFieldProperty().get().setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent evt) {
if (evt.getCode() == KeyCode.ENTER) {
commitEdit(getItem()); // TODO calls updateItem() when isEditing() is true causing invocation of cancelEdit() ?!?!
}
}
});
}
#Override
public void commitEdit(FolderCellType newFolderCellType) {
commitModeProperty.set(true);
super.commitEdit(newFolderCellType);
commitModeProperty.set(false);
}
#Override
public void startEdit() {
super.startEdit();
setGraphic(getItem().getEditBox());
if (getItem().textFieldProperty().get().getOnKeyReleased() == null) {
initTextFieldListener();
}
getItem().textFieldProperty().get().selectAll();
getItem().textFieldProperty().get().requestFocus();
}
#Override
public void cancelEdit() {
super.cancelEdit();
getTreeView().setEditable(false);
if (!commitModeProperty.getValue()) {
getItem().resetCurrentEntry();
}
setGraphic(getItem().getViewBox());
}
#Override
public void updateItem(FolderCellType item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
setGraphic(item.getEditBox());
} else {
setGraphic(item.getViewBox());
if (getContextMenu() == null) {
setContextMenu(createContextMenu());
}
}
}
getTreeView().setEditable(false);
}
}

Prevent first column from reordering in JTable [duplicate]

I have a JTable and I need to be able to reorder the columns. However I want the first column to not be able to be re-ordered. I used the following to enable reordering:
table.getTableHeader().setReorderingAllowed(true);
The columns can now be reordered including the first column which I don't want. Is there any way to lock the first column?
I have seen some solutions that use two tables with the first column being in a separate table, but maybe there's a better/simpler way.
This is the solution that I used to prevent the 1st column from being re-ordered
private int columnValue = -1;
private int columnNewValue = -1;
tblResults.getColumnModel().addColumnModelListener(new TableColumnModelListener()
{
public void columnAdded(TableColumnModelEvent e) {}
public void columnMarginChanged(ChangeEvent e) {}
public void columnMoved(TableColumnModelEvent e)
{
if (columnValue == -1)
columnValue = e.getFromIndex();
columnNewValue = e.getToIndex();
}
public void columnRemoved(TableColumnModelEvent e) {}
public void columnSelectionChanged(ListSelectionEvent e) {}
});
tblResults.getTableHeader().addMouseListener(new MouseAdapter()
{
#Override
public void mouseReleased(MouseEvent e)
{
if (columnValue != -1 && (columnValue == 0 || columnNewValue == 0))
tblResults.moveColumn(columnNewValue, columnValue);
columnValue = -1;
columnNewValue = -1;
}
});
Cheers,
Nearly 4 years later, there's still no optimal solution in sight anywhere.
Yet another suboptimal approach to prevent dragging of the first column (and other columns over the first) is to intercept the mouseEvents before the mouseInputListener installed by the uidelegate can handle them (similar to a recent QA).
The collaborators
a custom MouseMotionListener which delegates all events to the originally installed, except the dragged if it would lead to another column above the first
replace the original with the custom
update the replacement whenever the LAF is changed (because the original is controlled by the ui). This requires subclassing of JTableHeader and do the wiring in updateUI
The custom MouseInputListener:
/**
* A delegating MouseInputListener to be installed instead of
* the one registered by the ui-delegate.
*
* It's implemented to prevent dragging the first column or any other
* column over the first.
*/
public static class DragHook implements MouseInputListener {
private JTableHeader header;
private MouseListener mouseDelegate;
private MouseMotionListener mouseMotionDelegate;
private int maxX;
public DragHook(JTableHeader header) {
this.header = header;
installHook();
}
/**
* Implemented to do some tweaks/bookkeeping before/after
* passing the event to the original
*
* - temporarily disallow reordering if hit on first column
* - calculate the max mouseX that's allowable in dragging to the left
*
*/
#Override
public void mousePressed(MouseEvent e) {
int index = header.columnAtPoint(e.getPoint());
boolean reorderingAllowed = header.getReorderingAllowed();
if (index == 0) {
// temporarily disable re-ordering
header.setReorderingAllowed(false);
}
mouseDelegate.mousePressed(e);
header.setReorderingAllowed(reorderingAllowed);
if (header.getDraggedColumn() != null) {
Rectangle r = header.getHeaderRect(index);
maxX = header.getColumnModel().getColumn(0).getWidth()
+ e.getX() - r.x -1;
}
}
/**
* Implemented to pass the event to the original only if the
* mouseX doesn't lead to dragging the column over the first.
*/
#Override
public void mouseDragged(MouseEvent e) {
TableColumn dragged = header.getDraggedColumn();
int index = getViewIndexForColumn(header.getColumnModel(), dragged);
// dragged column is at second position, allow only drags to the right
if (index == 1) {
if (e.getX() < maxX) return;
}
mouseMotionDelegate.mouseDragged(e);
}
//-------- delegating-only methods
#Override
public void mouseReleased(MouseEvent e) {
mouseDelegate.mouseReleased(e);
}
#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) {
mouseMotionDelegate.mouseMoved(e);
}
//------------ un-/install listeners
protected void installHook() {
installMouseHook();
installMouseMotionHook();
}
protected void installMouseMotionHook() {
MouseMotionListener[] listeners = header.getMouseMotionListeners();
for (int i = 0; i < listeners.length; i++) {
MouseMotionListener l = listeners[i];
if (l.getClass().getName().contains("TableHeaderUI")) {
this.mouseMotionDelegate = l;
listeners[i] = this;
}
header.removeMouseMotionListener(l);
}
for (MouseMotionListener l : listeners) {
header.addMouseMotionListener(l);
}
}
protected void installMouseHook() {
MouseListener[] listeners = header.getMouseListeners();
for (int i = 0; i < listeners.length; i++) {
MouseListener l = listeners[i];
if (l.getClass().getName().contains("TableHeaderUI")) {
this.mouseDelegate = l;
listeners[i] = this;
}
header.removeMouseListener(l);
}
for (MouseListener l : listeners) {
header.addMouseListener(l);
}
}
public void uninstallHook() {
uninstallMouseHook();
uninstallMouseMotionHook();
}
protected void uninstallMouseMotionHook() {
MouseMotionListener[] listeners = header.getMouseMotionListeners();
for (int i = 0; i < listeners.length; i++) {
MouseMotionListener l = listeners[i];
if (l == this) {
listeners[i] = mouseMotionDelegate;
}
header.removeMouseMotionListener(l);
}
for (MouseMotionListener l : listeners) {
header.addMouseMotionListener(l);
}
}
protected void uninstallMouseHook() {
MouseListener[] listeners = header.getMouseListeners();
for (int i = 0; i < listeners.length; i++) {
MouseListener l = listeners[i];
if (l == this) {
listeners[i] = mouseDelegate;
}
header.removeMouseListener(l);
}
for (MouseListener l : listeners) {
header.addMouseListener(l);
}
}
}
Usage which survives switching of LAF, f.i.:
JTable table = new JTable(new AncientSwingTeam()) {
#Override
protected JTableHeader createDefaultTableHeader() {
JTableHeader header = new JTableHeader(getColumnModel()) {
DragHook hook;
#Override
public void updateUI() {
if (hook != null) {
hook.uninstallHook();
hook = null;
}
super.updateUI();
hook = new DragHook(this);
}
};
return header;
}
};
I think that you need to override the columnMoved() method in TableColumnModelListener. the TableColumnModelEvent class has a getFromIndex() method that you should be able to look at to determine if it's your fixed column, and then you should be able to cancel the event.
Hope that helps. A
First you need to define a better and simpler way. What don't you like about the 2 table approach?
You can't use a TableColumnModelListener, because the event is fired "after" the column has already been moved.
The code for dragging the column is found in the BasicTableHeaderUI. So you could try overriding the code there, but then you would need to do it for all LAFs.
The above code invokes JTableHeader.getReorderingAllowed() on a mousePressed event to determine if column reordering is allowed. I guess you could override that method in the JTableHeader and perhaps use the MouseInfo class to get the current mouse location to determine if it was over the first column and then return false. But then now you would also need to create a custom JTable that uses the custom table header.
Of course with either of the above suggestions you might be able to prevent the first column from being moved. But don't forget you also need to prevent the 2nd column from being inserted before the first column. I don't believe there is a short simple answer to the question.
Fixed Column Table is my version of how this would be imlemented with two tables. Is it better? I don't know, but it is simple since its only a single line of code to use it.
I had the same issue, and I was searching about it. So far I found two ways of doing that.
The "if I was rewriting it myself" method : Modifying the base classes from Java.
TableColumn would need a new property, like the "resizingAllowed", it would need the "reorderingAllowed".
From this, the modifications take place in BasicTableHeaderUI :
There is already :
private static boolean canResize(TableColumn column,
JTableHeader header) {
return (column != null) && header.getResizingAllowed()
&& column.getResizable();
}
It would need too :
private static boolean canMove(TableColumn column,
JTableHeader header) {
return (column != null) && header.getReorderingAllowed()
&& column.getReorderable();
}
(Note that if you don't want the first column only to not move, you can do without changing the TableColumns :
private static boolean canMove(TableColumn column,
JTableHeader header) {
return (column != null) && header.getReorderingAllowed()
&& header.getColumnModel().getColumnIndex(column.getIdentifier()) != 0;
}
)
After, two places to modify in the MouseInputListener :
in the mousePressed, calling the canMove() instead of the header.getReorderingAllowed(). This ensures that a column which shouldn't be moved, won't be.
But this is not enough, we need to prevent the immobile columns from being moved during dragging another one. You need to change the mouseDragged, too, when it is getting the "newColumnIndex" :
if (0 < newColumnIndex && newColumnIndex < cm.getColumnCount())
You need to add the condition if this new index can be moved, for example using the "canMove()" method. This way, when you will drag a column to this immobile one, you will still drag it, but it won't swap them.
Note that this method would require you to explicitly set the UI for the JTableHeader used for your JTable, which is not really ideal. But this is the most adapted though, as it deals with the problem on the place it is supposed to.
The "Let's try to block the normal behavior with what we actually have" method : Not modifying the UI, this method focus on the JTableHeader to block the commands made by the UI.
First, to block dragging the first column, we need a subclass from JTableHeader, with this overridden method :
#Override
public void setDraggedColumn(TableColumn pAColumn)
{
int lIndex = -1;
if (pAColumn != null)
lIndex = getColumnModel().getColumnIndex(pAColumn.getIdentifier());
if (lIndex != 0)
super.setDraggedColumn(pAColumn);
}
This will prevent a user from dragging the first column. But like described earlier, this is only one part of the problem, we need to prevent another dragged column from swapping with this first one.
So far, I don't have a correct method for this. I tried by subclassing the TableColumnModel, and overriding the moveColumn() method :
#Override
public void moveColumn(int pColumnIndex, int pNewIndex)
{
//Move only if the first column is not concerned
if (pColumnIndex =! 0 && pNewIndex != 0)
super.moveColumn(pColumnIndex, pNewIndex);
}
But this won't work, as the UI will update anyway the mouse position in the mouseDragged method, you will have a jump from your dragged column to another place.
So I'm still searching, and wonder if someone has propositions concerning this part.
At first, I used the very last Gnoupi's suggestion consisting in subclassing the TableColumnModel and overriding moveColumn but there were still some annoying jumps.
This is "my" fully working and tested solution with no nasty jump, it mainly relies on StanislavKo and kleopatra's suggestions. I added a more complicated mechanism to revert the unwanted move when releasing the mouse button :
table.getTableHeader().setUI(new WindowsTableHeaderUI() {
#Override
protected MouseInputListener createMouseInputListener() {
return new BasicTableHeaderUI.MouseInputHandler() {
#Override
public void mouseDragged(MouseEvent e) {
if (header.isEnabled() && header.getReorderingAllowed() && header.getDraggedColumn() != null && header.getDraggedColumn().getModelIndex() == frozenColumnModelIndex) {
header.setDraggedDistance(0);
header.setDraggedColumn(null);
return;
}
super.mouseDragged(e);
}
#Override
public void mouseReleased(MouseEvent e) {
if (header.isEnabled() && header.getReorderingAllowed() && header.getDraggedColumn() != null &&
0 <= illegalTableColumnMoveFromIndex && illegalTableColumnMoveFromIndex < header.getTable().getColumnModel().getColumnCount()) {
header.setDraggedDistance(0);
header.setDraggedColumn(null);
header.getTable().getColumnModel().moveColumn(illegalTableColumnMoveToIndex, illegalTableColumnMoveFromIndex);
illegalTableColumnMoveFromIndex = -1;
illegalTableColumnMoveToIndex = -1;
return;
}
super.mouseReleased(e);
}
};
}
});
table.getColumnModel().addColumnModelListener(new TableColumnModelListener() {
#Override
public void columnAdded(TableColumnModelEvent e) {
}
#Override
public void columnRemoved(TableColumnModelEvent e) {
}
#Override
public void columnMoved(TableColumnModelEvent e) {
if (e.getFromIndex() != e.getToIndex() && table.getColumnModel().getColumn(e.getFromIndex()).getModelIndex() == frozenColumnModelIndex) {
illegalTableColumnMoveFromIndex = e.getFromIndex();
illegalTableColumnMoveToIndex = e.getToIndex();
} else {
illegalTableColumnMoveFromIndex = -1;
illegalTableColumnMoveToIndex = -1;
}
}
#Override
public void columnMarginChanged(ChangeEvent e) {
}
#Override
public void columnSelectionChanged(ListSelectionEvent e) {
}
});
Note that the latest valid move is accepted instead of completely reverting the column drag.
frozenColumnModelIndex is the index of the "frozen" column in the table model.
illegalTableColumnMoveFromIndex is the index of the column from where it was moved when the latest illegal move was detected.
illegalTableColumnMoveToIndex is the index of the column to where it was moved when the latest illegal move was detected.
The code inside mouseDragged is enough to prevent the frozen column from being dragged, the rest allows to prevent another column from being dragged to the frozen column.
It works as is under Microsoft Windows as I extend WindowsTableHeaderUI but rather use the reflection API to set the mouse input listener of the table header UI, call uninstallerListeners() and finally call header.addMouseListener(mouseInputListener) and header.addMouseMotionListener(mouseInputListener) in order to drive my solution cross-platform without making any assumption on the name of the class for each table header UI.
I admit it might be a bit less robust than kleopatra's solution. I thank you all for your help, I'm really grateful and I'm really happy to see that collaborative work just works :)
I have used the "The 'Let's try to block the normal behavior with what we actually have' method" approach. Gnoupi said that he did not solve the second part of the problem. Here is the solution for just Windows XP L&F:
copy XPStyle class to yourself.
extend WindowsTableHeaderUI. Take a look at the source code.
use it: getTableHeader().setUI(new TreeTableWindowsTableHeaderUI());
Thanks to Gnoupi for the efforts.
I will just put the column back after the move is complete. So something like.
#Override
public void moveColumn(int from, int to) {
super.moveColumn(from, to);
if (from == 0 || to == 0) {
super.moveColumn(to, from);
}
}

Categories