Is there some way to copy the currently displayed tooltip to the clipboard as a string without complex XY-coord calculation that maps to the tooltip text area? This is especially challenging on a chart with tooltip displayed at an angle, also to only capture if being displayed. For example to get ctl-c to copy the displaying tooltip to clipboard:
PlotThisDaysData extends JFrame implements ... KeyListener{
#Override
public void keyTyped( KeyEvent e ) {
char typed = e.getKeyChar();
if ( typed == KeyEvent.VK_C ) /*VK_C?*/ {
String tooltipStr = myChart.???(); // <<<<<<<<<<<<< get displaying tooltip <<<<
StringSelection selection = new StringSelection( tooltipStr );
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents( selection, selection );
}
}
Perhaps there is some event when a tooltip gets displayed so I can store a String pointer and use when ctl-c is entered?
Tooltips are displayed in response to mouse events received by the chart's enclosing ChartPanel. To copy the currently displayed tooltip to the clipboard as the mouse moves,
Add a ChartMouseListener to the chart panel, as shown here.
When your listener sees a desired ChartEntity, ask the ChartPanel for the relevant text and copy it to the clipboard.
Toolkit toolkit = Toolkit.getDefaultToolkit();
Clipboard clipboard = toolkit.getSystemClipboard();
…
#Override
public void chartMouseMoved(ChartMouseEvent cme) {
…
String t = chartPanel.getToolTipText(cme.getTrigger());
clipboard.setContents(new StringSelection(t), null);
}
A similar approach can be used in a key binding, as shown here. Use the chart panel's getMousePosition() to construct the required MouseEvent trigger.
Get the chart panel's InputMap, ActionMap, and the platform's shortcut mask.
InputMap im = chartPanel.getInputMap();
ActionMap am = chartPanel.getActionMap();
int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
Put the desired KeyStroke in the chart panel's InputMap
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, mask), "copytip");
Put the corresponding Action in the chart panel's ActionMap
am.put("copytip", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
Point p = chartPanel.getMousePosition();
String t = chartPanel.getToolTipText(new MouseEvent(chartPanel,
0, System.currentTimeMillis(), 0, p.x, p.y, 0, false));
clipboard.setContents(new StringSelection(t), null);
}
});
Avoid KeyListener, as it requires keyboard focus.
Related
How would I programmatically click a Swing JButton in a way that would register all the relevant action/mouse events and be visible to the user (i.e. they'd see the button being pressed as if they actually clicked it)?
The button is in the same application I'm running; I'm not trying to control a button in another application. I suppose I could directly inject events into the queue, but I'd prefer to avoid that approach if possible, and doing it that way wouldn't show a visible click.
I see the java.awt.Robot class offers methods to move the mouse and click the mouse, but not to make it click a particular button.
Have you tried using doClick()?
If doClick() is not what you want, you can move the mouse really to the button and press it:
public void click(AbstractButton button, int millis) throws AWTException
{
Point p = button.getLocationOnScreen();
Robot r = new Robot();
r.mouseMove(p.x + button.getWidth() / 2, p.y + button.getHeight() / 2);
r.mousePress(InputEvent.BUTTON1_MASK);
try { Thread.sleep(millis); } catch (Exception e) {}
r.mouseRelease(InputEvent.BUTTON1_MASK);
}
Even though the asker was satisfied with button.doClick(), I was looking for something like what happens after setting a mnemonic, i.e. with button.setMnemonic(KeyEvent.VK_A). You can actually hold down ALT + A without anything happening (except the visual change). And upon release of the key A (with or without ALT), the button fires an ActionEvent.
I found that I can get the ButtonModel (see Java 8 API) with button.getModel(), then visually press the button with model.setPressed(true); model.setArmed(true); (both are changed by mnemonics), and visually release the button by setting both to false. And when model.setPressed(false) is called while the button is both pressed and armed, the button fires an ActionEvent automatically (calling model.setArmed(false) only changes the button visually).
[Quote from ButtonModel Java API documentation]
A button is triggered, and an ActionEvent is fired, when the mouse is released while the model is armed [...]
To make the application react to key presses when the button is visible (without the containing window or the button needing to be the focus owner, i.e. when another component in the window is focussed) I used key bindings (see the Official Java Tutorial).
Working code: Press SHIFT + A to visually press the button (in contrast to pressing ALT with the key after the mnemonic is set with button.setMnemonic()). And release the key to print the action command ("button") on the console.
// MnemonicCode.java
import javax.swing.*;
import java.awt.event.*;
public class MnemonicCode extends JFrame
{
public MnemonicCode(int keyCode)
{
JButton button = new JButton("button");
getContentPane().add(button);
addMnemonicToButton(button,keyCode);
button.addActionListener(new ActionListener () {
public void actionPerformed(ActionEvent e)
{
System.out.println(e.getActionCommand());
}
});
pack();
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) throws Exception
{
MnemonicCode bp = new MnemonicCode(KeyEvent.VK_A);
}
void addMnemonicToButton(JButton button,int keyCode)
{
int shiftMask = InputEvent.SHIFT_DOWN_MASK;
// signature: getKeyStroke(int keyCode, int modifiers, boolean onKeyRelease)
KeyStroke keyPress = KeyStroke.getKeyStroke(keyCode,shiftMask,false);
KeyStroke keyReleaseWithShift = KeyStroke.getKeyStroke(keyCode,shiftMask,true);
// get maps for key bindings
InputMap inputMap = button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = button.getActionMap();
// add key bindings for pressing and releasing the button
inputMap.put(keyPress,"press"+keyCode);
actionMap.put("press"+keyCode, new ButtonPress(button));
inputMap.put(keyReleaseWithShift,"releaseWithShift"+keyCode);
actionMap.put("releaseWithShift"+keyCode, new ButtonRelease(button));
///*
// add key binding for releasing SHIFT before A
// if you use more than one modifier it gets really messy
KeyStroke keyReleaseAfterShift = KeyStroke.getKeyStroke(keyCode,0,true);
inputMap.put(keyReleaseAfterShift,"releaseAfterShift"+keyCode);
actionMap.put("releaseAfterShift"+keyCode, new ButtonRelease(button));
//*/
}
class ButtonPress extends AbstractAction
{
private JButton button;
private ButtonModel model;
ButtonPress(JButton button)
{
this.button = button;
this.model = button.getModel();
}
public void actionPerformed(ActionEvent e)
{
// visually press the button
model.setPressed(true);
model.setArmed(true);
button.requestFocusInWindow();
}
}
class ButtonRelease extends AbstractAction
{
private ButtonModel model;
ButtonRelease(JButton button)
{
this.model = button.getModel();
}
public void actionPerformed(ActionEvent e)
{
if (model.isPressed()) {
// visually release the button
// setPressed(false) also makes the button fire an ActionEvent
model.setPressed(false);
model.setArmed(false);
}
}
}
}
You could always simulate it by firing an action event with it as the source.
http://download.oracle.com/javase/6/docs/api/java/awt/event/ActionEvent.html
To fire it, create the action event above, and whatever listener you want just call
ActionEvent e = new ActionEvent(myButton,1234,"CommandToPeform");
myListener.actionPerformed(e);
From: http://download.oracle.com/javase/6/docs/api/javax/swing/JButton.html
/**
* Click a button on screen
*
* #param button Button to click
* #param millis Time that button will remain "clicked" in milliseconds
*/
public void click(AbstractButton button, int millis) {
b.doClick(millis);
}
Based on #Courteaux's answer, this method clicks the first cell in a JTable:
private void clickFirstCell() {
try {
jTable1.changeSelection(0, 0, false, false);
Point p = jTable1.getLocationOnScreen();
Rectangle cellRect = jTable1.getCellRect(0, 0, true);
Robot r = new Robot();
Point mouse = MouseInfo.getPointerInfo().getLocation();
r.mouseMove(p.x + cellRect.x + cellRect.width / 2,
p.y + cellRect.y + cellRect.height / 2);
r.mousePress(InputEvent.BUTTON1_MASK);
try {
Thread.sleep(50);
} catch (Exception e) {
}
r.mouseRelease(InputEvent.BUTTON1_MASK);
r.mouseMove(mouse.x, mouse.y);
} catch (AWTException ex) {
}
}
I want to drag a text box from vertical layout container and drop it in a flow layout container. On drop, it should appear as a Rich Text Area. Please find the code below. I tried to debug the code and it is getting entered only inside dragging. Debugging is not coming inside droptarget. Can you please help me?
final TextButton textButton = new TextButton();
textButton.setText("Text Box");
DragSource source = new DragSource(textButton) {
#Override
protected void onDragStart(DndDragStartEvent event) {
super.onDragStart(event);
}
};
DropTarget dropTarget = new DropTarget(flowLayoutContainer);
dropTarget.setOperation(Operation.COPY);
dropTarget.addDropHandler(new DndDropHandler() {
#Override
public void onDrop(DndDropEvent event) {
final RichTextArea textBox1 = new RichTextArea();
flowLayoutContainer.add(textBox1);
}
});
I have a JTable with editable cells. When I click in a cell, it enters edit mode; the same happens when I'm moving through cell using the directional arrows.
Now I want to select the cell instead of start editing, and edit the cell only when the Enter key is pressed.
If any other information is needed, please just ask for it.
Edit: Action for Enter key
class EnterAction extends AbstractAction {
#Override
public void actionPerformed(ActionEvent e) {
JTable tbl = (JTable) e.getSource();
tbl.editCellAt(tbl.getSelectedRow(), tbl.getSelectedColumn());
if (tbl.getEditorComponent() != null) {
tbl.getEditorComponent().requestFocus();
}
}
}
Now this is for left arrow action the rest of 3 are not hard to deduce from this one:
class LeftAction extends AbstractAction {
#Override
public void actionPerformed(ActionEvent e) {
JTable tbl = (JTable)e.getSource();
tbl.requestFocus();
tbl.changeSelection(tbl.getSelectedRow(), tbl.getSelectedColumn() > 0 ? tbl.getSelectedColumn()-1:tbl.getSelectedColumn(), false, false);
if(tbl.getCellEditor()!=null)
tbl.getCellEditor().stopCellEditing();
}
}
And this is how you bind this actions:
final String solve = "Solve";
KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
table.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(enter, solve);
table.getActionMap().put(solve, new EnterAction());
final String sel = "Sel";
KeyStroke arrow = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0);
table.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(arrow, sel);
table.getActionMap().put(sel, new LeftAction());
Oh,i almost forgot,to select the cell instead of edit on Mouse Click:
public static MouseListener mAdapterTable = new MouseListener()
{
#Override
public void mousePressed(MouseEvent e)
{
JTable tbl=((JTable)e.getComponent());
if(tbl.isEditing())
{
tbl.getCellEditor().stopCellEditing();
}
}
#Override
public void mouseClicked(MouseEvent e) {
JTable tbl=((JTable)e.getComponent());
if(tbl.isEditing() )
tbl.getCellEditor().stopCellEditing();
}
#Override
public void mouseReleased(MouseEvent e) {
JTable tbl=((JTable)e.getComponent());
if(tbl.isEditing() )
tbl.getCellEditor().stopCellEditing();
}
};
The EventListner must be added to table like so:
table.addMouseListener(mAdapterTable);
Use Key Bindings for this. Most Look & Feel implementations already bind F2 to the table's startEditing action, but you add a different binding:
tree.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "startEditing");
This will effectively replace the previous binding of Enter to the table's selectNextRowCell action.
Here is what i would do:
First enable the single cell selection for the JTable
Create a KeyAdapter or KeyListener for the JTable or for the JPanel,
what contains your table.
In the KeyAdapter's keyPressed() method enter the edit mode of the
selected cell, something like this:
http://www.exampledepot.com/egs/javax.swing.table/StopEdit.html
You can check in the keyPressed() method, if the user pressed the right button for editing. I'm not sure, if the normal (double click) editing is disabled in your table, then what happens, if you try to edit it programmatically, but if it doesn't work, then you can enable the editing on the selected cell, when the user presses the edit button, then when he/she finished, disable it again.
How would I programmatically click a Swing JButton in a way that would register all the relevant action/mouse events and be visible to the user (i.e. they'd see the button being pressed as if they actually clicked it)?
The button is in the same application I'm running; I'm not trying to control a button in another application. I suppose I could directly inject events into the queue, but I'd prefer to avoid that approach if possible, and doing it that way wouldn't show a visible click.
I see the java.awt.Robot class offers methods to move the mouse and click the mouse, but not to make it click a particular button.
Have you tried using doClick()?
If doClick() is not what you want, you can move the mouse really to the button and press it:
public void click(AbstractButton button, int millis) throws AWTException
{
Point p = button.getLocationOnScreen();
Robot r = new Robot();
r.mouseMove(p.x + button.getWidth() / 2, p.y + button.getHeight() / 2);
r.mousePress(InputEvent.BUTTON1_MASK);
try { Thread.sleep(millis); } catch (Exception e) {}
r.mouseRelease(InputEvent.BUTTON1_MASK);
}
Even though the asker was satisfied with button.doClick(), I was looking for something like what happens after setting a mnemonic, i.e. with button.setMnemonic(KeyEvent.VK_A). You can actually hold down ALT + A without anything happening (except the visual change). And upon release of the key A (with or without ALT), the button fires an ActionEvent.
I found that I can get the ButtonModel (see Java 8 API) with button.getModel(), then visually press the button with model.setPressed(true); model.setArmed(true); (both are changed by mnemonics), and visually release the button by setting both to false. And when model.setPressed(false) is called while the button is both pressed and armed, the button fires an ActionEvent automatically (calling model.setArmed(false) only changes the button visually).
[Quote from ButtonModel Java API documentation]
A button is triggered, and an ActionEvent is fired, when the mouse is released while the model is armed [...]
To make the application react to key presses when the button is visible (without the containing window or the button needing to be the focus owner, i.e. when another component in the window is focussed) I used key bindings (see the Official Java Tutorial).
Working code: Press SHIFT + A to visually press the button (in contrast to pressing ALT with the key after the mnemonic is set with button.setMnemonic()). And release the key to print the action command ("button") on the console.
// MnemonicCode.java
import javax.swing.*;
import java.awt.event.*;
public class MnemonicCode extends JFrame
{
public MnemonicCode(int keyCode)
{
JButton button = new JButton("button");
getContentPane().add(button);
addMnemonicToButton(button,keyCode);
button.addActionListener(new ActionListener () {
public void actionPerformed(ActionEvent e)
{
System.out.println(e.getActionCommand());
}
});
pack();
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) throws Exception
{
MnemonicCode bp = new MnemonicCode(KeyEvent.VK_A);
}
void addMnemonicToButton(JButton button,int keyCode)
{
int shiftMask = InputEvent.SHIFT_DOWN_MASK;
// signature: getKeyStroke(int keyCode, int modifiers, boolean onKeyRelease)
KeyStroke keyPress = KeyStroke.getKeyStroke(keyCode,shiftMask,false);
KeyStroke keyReleaseWithShift = KeyStroke.getKeyStroke(keyCode,shiftMask,true);
// get maps for key bindings
InputMap inputMap = button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = button.getActionMap();
// add key bindings for pressing and releasing the button
inputMap.put(keyPress,"press"+keyCode);
actionMap.put("press"+keyCode, new ButtonPress(button));
inputMap.put(keyReleaseWithShift,"releaseWithShift"+keyCode);
actionMap.put("releaseWithShift"+keyCode, new ButtonRelease(button));
///*
// add key binding for releasing SHIFT before A
// if you use more than one modifier it gets really messy
KeyStroke keyReleaseAfterShift = KeyStroke.getKeyStroke(keyCode,0,true);
inputMap.put(keyReleaseAfterShift,"releaseAfterShift"+keyCode);
actionMap.put("releaseAfterShift"+keyCode, new ButtonRelease(button));
//*/
}
class ButtonPress extends AbstractAction
{
private JButton button;
private ButtonModel model;
ButtonPress(JButton button)
{
this.button = button;
this.model = button.getModel();
}
public void actionPerformed(ActionEvent e)
{
// visually press the button
model.setPressed(true);
model.setArmed(true);
button.requestFocusInWindow();
}
}
class ButtonRelease extends AbstractAction
{
private ButtonModel model;
ButtonRelease(JButton button)
{
this.model = button.getModel();
}
public void actionPerformed(ActionEvent e)
{
if (model.isPressed()) {
// visually release the button
// setPressed(false) also makes the button fire an ActionEvent
model.setPressed(false);
model.setArmed(false);
}
}
}
}
You could always simulate it by firing an action event with it as the source.
http://download.oracle.com/javase/6/docs/api/java/awt/event/ActionEvent.html
To fire it, create the action event above, and whatever listener you want just call
ActionEvent e = new ActionEvent(myButton,1234,"CommandToPeform");
myListener.actionPerformed(e);
From: http://download.oracle.com/javase/6/docs/api/javax/swing/JButton.html
/**
* Click a button on screen
*
* #param button Button to click
* #param millis Time that button will remain "clicked" in milliseconds
*/
public void click(AbstractButton button, int millis) {
b.doClick(millis);
}
Based on #Courteaux's answer, this method clicks the first cell in a JTable:
private void clickFirstCell() {
try {
jTable1.changeSelection(0, 0, false, false);
Point p = jTable1.getLocationOnScreen();
Rectangle cellRect = jTable1.getCellRect(0, 0, true);
Robot r = new Robot();
Point mouse = MouseInfo.getPointerInfo().getLocation();
r.mouseMove(p.x + cellRect.x + cellRect.width / 2,
p.y + cellRect.y + cellRect.height / 2);
r.mousePress(InputEvent.BUTTON1_MASK);
try {
Thread.sleep(50);
} catch (Exception e) {
}
r.mouseRelease(InputEvent.BUTTON1_MASK);
r.mouseMove(mouse.x, mouse.y);
} catch (AWTException ex) {
}
}
I have a component where i want to display a custom jtooltip. That is easy, just change the getTooltip method. Similar for location and text.
However i also want to change the timers. The tooltip should always be displayed if the mouse is over a cellrenderer of the component. If it leaves all of those it should be turned invisible.
I know that i can use TooltipManager to control the times globally. But the best solution is probably to just shortcircut that and display the tooltip myself with a mouselistener. However when i tried to do that (unregister the component in TooltipManager and setting the tooltip visible, with text and in the correct position, in a mouse listener) the tooltip never showed at all. What am i doing wrong?
Edit:
Now the question has changed! Into 2 questions.
My solution is for now this, however it losses the shadow that the jtooltip always displays sometimes frustratingly, and it is hidden if the mouse exits into the popup itself. How to filter the mouseexit events over the popup if the popup is not even a component? I could do some hacking based on the lastPosition, but that seems stupid, since i don't really know its width.
private Popup lastPopup;
private final JToolTip tooltip = ...;
private Point lastPoint;
#Override public void mouseMoved(MouseEvent e) {
Point p = privateToolTipLocation(e);
if (p == null || p.equals(lastPoint)) {
return;
}
lastPoint = p;
tooltip.setTipText(privateToolTipText(e));
//copy
p = new Point(p);
SwingUtilities.convertPointToScreen(p, this);
Popup newPopup = PopupFactory.getSharedInstance().getPopup(this, tooltip, p.x, p.y);
if (lastPopup != null) {
lastPopup.hide();
}
lastPopup = newPopup;
newPopup.show();
}
#Override public void mouseExited(MouseEvent e) {
if (lastPopup != null && someUnknownCondiction) {
lastPopup.hide();
lastPopup = null;
}
}
Rather than trying to reimplement the display of tooltips, you could add a mouse listener to your component that changes the global tooltip timer when the mouse enters and leaves the region above the component.
Here is some example code:
instantTooltipComponent.addMouseListener(new MouseAdapter()
{
final int defaultTimeout = ToolTipManager.sharedInstance().getInitialDelay();
#Override
public void mouseEntered(MouseEvent e) {
ToolTipManager.sharedInstance().setInitialDelay(0);
}
#Override
public void mouseExited(MouseEvent e) {
ToolTipManager.sharedInstance().setInitialDelay(defaultTimeout);
}
});
This should change the tooltip delay to zero whenever the mouse moves over your component and change it back to the default delay whenever the mouse moves off of your component.
But the best solution is probably to
just shortcircut that and display the
tooltip myself with a mouselistener
Invoke the default Action for the component to display the tooltip:
Action toolTipAction = component.getActionMap().get("postTip");
if (toolTipAction != null)
{
ActionEvent postTip = new ActionEvent(component, ActionEvent.ACTION_PERFORMED, "");
toolTipAction.actionPerformed( postTip );
}
Edit:
Above code doesn't appear to work anymore. Ctrl+F1 is the default KeyStroke used to display the tool tip of a component. So an alternative approach is to dispatch the Ctrl+F1 KeyStroke to the component. For example:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class PostTipSSCCE2 extends JPanel
{
public PostTipSSCCE2()
{
FocusAdapter fa = new FocusAdapter()
{
public void focusGained(FocusEvent e)
{
JComponent component = (JComponent)e.getSource();
KeyEvent ke = new KeyEvent(
component,
KeyEvent.KEY_PRESSED,
System.currentTimeMillis(),
KeyEvent.CTRL_MASK,
KeyEvent.VK_F1,
KeyEvent.CHAR_UNDEFINED);
component.dispatchEvent( ke );
}
};
MouseAdapter ma = new MouseAdapter()
{
#Override
public void mouseEntered(MouseEvent e)
{
JComponent component = (JComponent)e.getSource();
KeyEvent ke = new KeyEvent(
component,
KeyEvent.KEY_PRESSED,
System.currentTimeMillis(),
KeyEvent.CTRL_MASK,
KeyEvent.VK_F1,
KeyEvent.CHAR_UNDEFINED);
component.dispatchEvent( ke );
}
};
JButton button = new JButton("Button");
button.setToolTipText("button tool tip");
button.addFocusListener( fa );
button.addMouseListener( ma );
add( button );
JTextField textField = new JTextField(10);
textField.setToolTipText("text field tool tip");
textField.addFocusListener( fa );
textField.addMouseListener( ma );
add( textField );
JCheckBox checkBox = new JCheckBox("CheckBox");
checkBox.setToolTipText("checkbox tool tip");
checkBox.addFocusListener( fa );
checkBox.addMouseListener( ma );
add( checkBox );
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("PostTipSSCCE2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new JScrollPane(new PostTipSSCCE2()) );
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Apparently what controls the display of the Tooltip is if the getTooltipText returns null or not. Having that to null, eliminated a npe and allowed things to display. However there are some artifacts still..