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.
Related
We are developing Eclipse plugin project.
When you select an object in the Editor Area
the properties display in the viewpart below the editor.
The Viewpart contains a viewer with XML content.
I'm using StructuredTextViewer from WPT for this purpose. The syntax highlighting work properly, but the content assistant doesn't.
public class AxelotXmlViewer extends StructuredTextViewer
{
public AxelotXmlViewer(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, boolean showAnnotationsOverview, int styles)
{
super(parent, verticalRuler, overviewRuler, showAnnotationsOverview, styles);
appendVerifyKeyListener(new VerifyKeyListener()
{
#Override
public void verifyKey(VerifyEvent event)
{
// Check for Ctrl+Spacebar call Content Assistant
if (event.stateMask == SWT.CTRL && event.character == ' ')
{
if (AxelotXmlViewer.this.canDoOperation(StructuredTextViewer.CONTENTASSIST_PROPOSALS))
AxelotXmlViewer.this.doOperation(StructuredTextViewer.CONTENTASSIST_PROPOSALS);
// Veto this key press to avoid further processing
event.doit = false;
}
}
});
}
#Override
public void setDocument(IDocument document)
{
if (document instanceof IStructuredDocument)
{
super.setDocument(document);
}
else if (document != null)
{
...
}
else
{
super.setDocument(null);
}
}
}
I know how to deal with left or right click separately, dragging, double-clicking, but I can't figure out how to do something if the user clicks left and right mouse buttons at the same time without interfering/causing other events to fire.
#Override
public void handle(Event event) {
if (event.getSource() instanceof Tile) {
Tile tile = (Tile) event.getSource();
if (event.getEventType().equals(MouseEvent.MOUSE_CLICKED)) {
if (((MouseEvent) event).getButton().equals(MouseButton.SECONDARY))
tile.toggleFlag();
else if (((MouseEvent) event).getClickCount() == 2)
mineField.massClick(tile);
}
if (event.getEventType().equals(MouseEvent.DRAG_DETECTED))
if (!((MouseEvent) event).getButton().equals(MouseButton.SECONDARY))
tile.startFullDrag();
if (event.getEventType().equals(MouseDragEvent.MOUSE_DRAG_ENTERED))
tile.arm();
if (event.getEventType().equals(MouseDragEvent.MOUSE_DRAG_EXITED))
tile.disarm();
if (event.getEventType().equals(MouseDragEvent.MOUSE_DRAG_RELEASED))
mineField.clickedTile(tile);
if (event.getEventType().equals(ActionEvent.ANY))
mineField.clickedTile(tile);
}
}
Also, if you see a problem with my code feel free to point it out, always looking to improve.
The simple version is this:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
StackPane root = new StackPane();
root.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> {
if( e.isPrimaryButtonDown() && e.isSecondaryButtonDown()) {
System.out.println( "Both down");
} else if( e.isPrimaryButtonDown()) {
System.out.println( "Primary down");
} else if( e.isSecondaryButtonDown()) {
System.out.println( "Secondary down");
}
});
Scene scene = new Scene(root,400,400);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
If you prefer your own event happening when both buttons are pressed, you could try it this way:
public class Main extends Application {
BooleanProperty primaryMouseButtonDown = new SimpleBooleanProperty();
BooleanProperty secondaryMouseButtonDown = new SimpleBooleanProperty();
#Override
public void start(Stage primaryStage) {
try {
StackPane root = new StackPane();
root.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> {
primaryMouseButtonDown.setValue( e.isPrimaryButtonDown());
secondaryMouseButtonDown.setValue( e.isSecondaryButtonDown());
});
root.addEventFilter(MouseEvent.MOUSE_RELEASED, e -> {
primaryMouseButtonDown.setValue( e.isPrimaryButtonDown());
secondaryMouseButtonDown.setValue( e.isSecondaryButtonDown());
});
BooleanBinding binding = Bindings.and(primaryMouseButtonDown, secondaryMouseButtonDown);
binding.addListener( new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
System.out.println( "Mouse Button Event: " + oldValue + " -> " + newValue);
}
});
Scene scene = new Scene(root,400,400);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
There are 2 boolean properties, one for the primary button down and one for the secondary button down. Both properties are connected via a BooleanBinding. Whenever one of the properties change via the mouse event, an event is fired. So what's left to do is for you to check if newValue is true and fire your handling code.
Do something more along the lines of, watch for mouse presses, and set a boolean to true when a mousePressed event is called for left/right mouse button. Then later in the event look to see if both booleans for left and right are true. If they are, act on it as if both were pressed at the same time.
boolean mouse_1, mouse_2 = false;
public void mousePressed(MouseEvent e){
//The numbers are just made up I don't remember the actual codes for the buttons but it's simple enough to figure out.
if(e.getButton()==1){
mouse_1 = true;
}
if(e.getButton()==2){
mouse_2 = true;
}
if(mouse_1&&mouse_2){
//Your code here
}
}
public void mouseReleased(MouseEvent e){
if(e.getButton() == 1){
mouse_1 = false;
}
if(e.getButton() == 2){
mouse_2 = false;
}
}
Assume this is some sort of handler class... But this is the short for how to implement it.
I'm probably late to answer this question, but I'm going to post my solution in order to demonstrate how to handle single-button clicks separately from both buttons being clicked at the same time
Existing answers already explained how to detect both mouse buttons being clicked at the same time. But mouse events (click, press, and release) are still triggered by individual buttons and previous posters didn't address how to avoid these events from interfering with each other.
My solution is to track both buttons being pressed on mouse press and detect mouse clicks of any kind on mouse release:
//flag to track both buttons being pressed
private boolean wereBothButtonsPressed = false;
private void onMousePressed(MouseEvent e) {
//single button press sets flag to false
wereBothButtonsPressed = e.isPrimaryButtonDown() && e.isSecondaryButtonDown();
}
private void onMouseReleased(MouseEvent e) {
if (e.isPrimaryButtonDown() || e.isSecondaryButtonDown()) {
//do nothing if user is still holding the button
return;
}
if (wereBothButtonsPressed) {
System.out.prinln("Both buttons");
} else if (e.getButton() == MouseButton.PRIMARY) {
System.out.prinln("Only primary");
} else if (e.getButton() == MouseButton.SECONDARY) {
System.out.prinln("Only secondary");
}
}
You can set these handlers for specific events on specific controls or fit them into your method:
private boolean wereBothButtonsPressed = false;
#Override
public void handle(Event event) {
...
if (event.getEventType().equals(MouseEvent.MOUSE_PRESSED)) {
MouseEvent me = (MouseEvent) event;
wereBothButtonsPressed = me.isPrimaryButtonDown() && me.isSecondaryButtonDown();
} else if (event.getEventType().equals(MouseEvent.MOUSE_RELEASED)) {
MouseEvent me = (MouseEvent) event;
if (!me.isPrimaryButtonDown() && !me.isSecondaryButtonDown()) {
if(wereBothButtonsPressed) {
mineField.massClick(tile);
} else if(me.getButton() == MouseButton.PRIMARY) {
mineField.clickedTile(tile);
} else if(me.getButton() == MouseButton.SECONDARY) {
tile.toggleFlag();
}
}
...
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?
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);
}
}
If you have JTable set with table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) and then you click drag on a row that is not already selected, it starts selecting multiple rows. We don't want that behavior. We want it so if you click on a node, even if it's not already selected, it will start dragging it.
We do need the multi select mode on, so setting it to single select (which does result in the behavior we want) is not an option.
Update: At this point, it appears it will require some type of ugly hack since the logic is in a private method BasicTableUI$Handler.canStartDrag
I found one possible solution. You bracket the mouse listeners so you can lie to the call to isCellSelected during the canStartDrag call.
Subclass JTable (or in my case, JXTreeTable). In the constructor call this:
private void setupSelectionDragHack()
{
// Bracket the other mouse listeners so we may inject our lie
final MouseListener[] ls = getMouseListeners();
for (final MouseListener l : ls)
{
removeMouseListener(l);
}
addMouseListener(new MouseAdapter()
{
#Override
public void mousePressed(final MouseEvent e)
{
// NOTE: it might not be necessary to check the row, but... I figure it's safer maybe?
mousingRow = rowAtPoint(e.getPoint());
mousingInProgress = true;
}
});
for (final MouseListener l : ls)
{
addMouseListener(l);
}
addMouseListener(new MouseAdapter()
{
#Override
public void mousePressed(final MouseEvent e)
{
mousingInProgress = false;
}
});
}
And then you'll need this:
#Override
public boolean isCellSelected(final int row, final int column)
{
if (mousingInProgress && row == mousingRow)
{
// Only lie to the canStartDrag caller. We tell the truth to everyone else.
final StackTraceElement[] elms = Thread.currentThread().getStackTrace();
for (int i = 0; i < 3; i++)
{
if (elms[i].getMethodName().equals("canStartDrag"))
{
return mousingInProgress;
}
}
}
return super.isCellSelected(row, column);
}
It's an ugly hack in many ways, but... for now it seems to work.
Unfortunately none of the other answers worked for me.
So I made my own hack/fix for the problem (I'm posting it here for others with the same problem):
public class SFixTable extends JTable {
private static final long serialVersionUID = 1082882838948078289L;
boolean pressed = false;
int currSRow = -100;
public SFixTable(TableModel dm) {
super(dm);
}
public SFixTable() {
super();
}
public SFixTable(Vector<?> rowData, Vector<?> columnNames) {
super(rowData, columnNames);
}
#Override
protected void processMouseEvent(MouseEvent e) {
int row = rowAtPoint(e.getPoint());
int col = columnAtPoint(e.getPoint());
if (SwingUtilities.isLeftMouseButton(e) && !e.isShiftDown() && !e.isControlDown()) {
boolean isDragRelease = (e.getID() == MouseEvent.MOUSE_RELEASED) && row != currSRow;
boolean isStartClick = (e.getID() == MouseEvent.MOUSE_PRESSED);
if (row >= 0 && col >= 0) {
if (isStartClick) {
super.changeSelection(row, col, false, false);
} else if (isDragRelease) {
super.changeSelection(currSRow, col, false, false);
}
}
pressed = (e.getID() == MouseEvent.MOUSE_PRESSED);
if (pressed) {
currSRow = row;
} else {
currSRow = -100;
}
}
super.processMouseEvent(e);
}
#Override
public boolean isCellSelected(int row, int col) {
return (pressed)? (row == currSRow) : super.isCellSelected(row, col);
}
}
It's a bug:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6349223
and as you already assumed, it requires some ugly hack. Here's one (not from me, but from a user Aephyr on old sun forums which didn't survive the migration to OTN)
table = new JTable() {
// fix for http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6349223
// requirement is the option to turn off drag-selection if dragEnabled
// fix posted in sun dev forum by Aephyr
// http://forums.sun.com/thread.jspa?threadID=5436355&tstart=0
private boolean pressed;
#Override
protected void processMouseEvent(MouseEvent e) {
pressed = e.getID() == MouseEvent.MOUSE_PRESSED;
if (pressed && !e.isShiftDown() && !e.isControlDown())
clearSelection();
try {
super.processMouseEvent(e);
} finally {
pressed = false;
}
}
#Override
public boolean isCellSelected(int row, int col) {
return pressed ? true : super.isCellSelected(row, col);
}
};
Similar to kleopatra's answer, but this seems to handle a few issues with the previous one -- you can control-click to both add and remove items from a multiple selection, and you can successfully drag a multi-select group. I've tested this only with an ETable/Outline from NetBeans, but should work with a regular JTable.
table = new JTable() {
private boolean inPress = false;
#Override protected void processMouseEvent(MouseEvent e) {
inPress = e.getID() == MouseEvent.MOUSE_PRESSED && e.getButton() == MouseEvent.BUTTON1 && !e.isShiftDown() && !e.isControlDown();
try {
super.processMouseEvent(e);
} finally {
inPress = false;
}
}
#Override public boolean isCellSelected(int row, int col) {
boolean selected = super.isCellSelected(row, col);
if (inPress) {
if (!selected)
clearSelection();
return true;
}
return selected;
}
};
If what you are looking for is to drag an unselected row in a single selection JTable, setting the table's selection model to SINGLE_SELECTION mode is not enough, you also have to set the column model's selection mode.
JTable table = new JTable();
table.getSelectionModel()
.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.getColumnModel().getSelectionModel()
.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);