I have a JTreeTable and have successfully implemented a MouseMotionListener to show a tooltip whenever the mouse is over one of the cells. However when clicking on the cell the tooltip does not show up. I've tried several things like setting the text on the mouseClicked and mouseReleased events but that doesn't work. I found this code -
Action toolTipAction = treeTable.getActionMap().get("postTip");
if(toolTipAction != null){
ActionEvent postTip = new ActionEvent(treeTable,ActionEvent.ACTION_PERFORMED, "");
toolTipAction.actionPerformed(postTip);
}
to use in the mouseReleased method, which does make the tooltip popup, but it's then in the wrong position. So next i tried overriding the getTooltipLocation method on the JTreeTable, and this works fine for mouseMoved events but doesn't get called with the above method. Can anyone shed some light on how to do this?
Thanks
Andy
You can use the following approach to show the tooltip (there will be a slight delay). Then you can override the getToolTipLocation() method since a MouseEvent will now be generated:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ToolTipOnRelease extends JPanel
{
public ToolTipOnRelease()
{
JLabel label = new JLabel( "First Name:" );
add( label );
JTextField textField = new JTextField(15);
add( textField );
MouseListener ml = new MouseAdapter()
{
public void mouseReleased(MouseEvent e)
{
JComponent component = (JComponent)e.getSource();
component.setToolTipText("Mouse released on: " + component.getClass().toString());
MouseEvent phantom = new MouseEvent(
component,
MouseEvent.MOUSE_MOVED,
System.currentTimeMillis(),
0,
0,
0,
0,
false);
ToolTipManager.sharedInstance().mouseMoved(phantom);
}
};
label.addMouseListener( ml );
textField.addMouseListener( ml );
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("ToolTipOnRelease");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new ToolTipOnRelease() );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
org.apache.jorphan.gui.JTreeTable extends javax.swing.JComponent
javax.swing.JComponent#setToopTipText() doesn't work?
I do realize that you want to use Action but for tooltips? I would use Action when multiple UI actions would need to share it.
Related
My question is on how do I go about know what the text of the drag and drop location is. This is current working code.
gameCell.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e){
JButton button = (JButton)e.getSource();
int currentNumber = Integer.parseInt(button.getText());
TransferHandler handle = button.getTransferHandler();
handle.exportAsDrag(button, e, TransferHandler.COPY);
So the idea is there is a gameboard which is just a bunch of cells (all JButtons), one large table. When I drag one cell to another then the dragged cell's value will become the clicked cell's value, so therefore how do I tell the original value of the JButton cell before it is copied over by the dragged cell.
If you are just trying to "copy" the text from one button to another then you can use code like the following:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DragIcon extends JPanel
{
public DragIcon()
{
TransferHandler iconHandler = new TransferHandler( "icon" );
MouseListener dragListener = new DragMouseAdapter();
JLabel label1 = new JLabel("Label1");
label1.setTransferHandler( iconHandler );
label1.addMouseListener(dragListener);
label1.setIcon( new ImageIcon("copy16.gif") );
JLabel label2 = new JLabel("Label2");
label2.setTransferHandler( iconHandler );
label2.addMouseListener(dragListener);
add( label1 );
add( label2 );
}
private class DragMouseAdapter extends MouseAdapter
{
public void mousePressed(MouseEvent e)
{
JComponent c = (JComponent)e.getSource();
TransferHandler handler = c.getTransferHandler();
handler.exportAsDrag(c, e, TransferHandler.COPY);
// handler.exportAsDrag(c, e, TransferHandler.MOVE);
}
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Drag Icon");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DragIcon());
frame.setLocationByPlatform( true );
frame.setSize(200, 100);
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
The default TransferHandler allows you to specify a property that you want to copy. In my example I'm copying the Icon. In your case you would use:
TransferHandler iconHandler = new TransferHandler( "text" );
to copy the text.
Note in my example I also tried to "move" the Icon from one label to another but it doesn't work. I'm not sure what needs to be changed to move a property.
I want to make a kind of digital clock which you can activate by using enter to kind of refresh the clock display, for that I use this method:
private static void GUI(String time, int action){
JLabel textLabel = new JLabel(time);
JPanel panel = new JPanel();
JFrame enterMessage = new JFrame("Tester");
if (action == 1){
enterMessage.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
enterMessage.setSize(190, 80);
enterMessage.setVisible(true);
panel.setBackground(Color.WHITE);
panel.add(textLabel);
enterMessage.add(panel);
}else {
System.out.printf("Refresh");
panel.revalidate();
enterMessage.revalidate();
panel.repaint();
enterMessage.repaint();
}
}
}
This method gets called twice in the program code: one time to make the GUI upon opening the program and everytime an enterpress is detected to refresh it. I searched on internet how to refresh a JPanel and I found that you needed to use revalidate(); and then repaint(); but it does not refresh the time displayed by the panel. How would I refresh it?
ps:the time is passed from the main as a string and everytime an enterpress is detected gets overwritten and passed
Follow Java naming conventions. Variable names should NOT start with an upper case character.
Don't keep adding the label to the panel. Just use the setText(...) method of JLabel to change the text being displayed.
Edit:
An example of a SSCCE that shows you how to use the setText(...) method:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class TimerTime extends JPanel implements ActionListener
{
private JLabel timeLabel;
public TimerTime()
{
timeLabel = new JLabel( new Date().toString() );
add( timeLabel );
Timer timer = new Timer(1000, this);
timer.setInitialDelay(1);
timer.start();
}
#Override
public void actionPerformed(ActionEvent e)
{
//System.out.println(e.getSource());
timeLabel.setText( new Date().toString() );
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("TimerTime");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new TimerTime() );
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
I have JSplitPane that has oneTouchExpandable set to true.
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
splitPane.setDividerSize(10);
splitPane.setOneTouchExpandable(true);
The problem is that I do not know how to attach key bindings to up and down arrows on JSplitPane's divider. For up arrow I want Ctrl+U and for down - Ctrl + D.
Thanks!
Implementation of the arrow button shown by OneTouchExpandable is UI label and will take extra work unnecessarily to bind them. You can easily use Key Binding on JSplitPane itself to control the JSplitPane divider location using setDividerLocation(int). Increase on Ctrl + U and Decrease on Ctrl + D. For example:
Action incrDividerLoc = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JSplitPane srcSplitPan = (JSplitPane) e.getSource();
(srcSplitPan).setDividerLocation(srcSplitPan.getDividerLocation()+10);
}
};
Action decrDividerLoc = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JSplitPane srcSplitPan = (JSplitPane) e.getSource();
(srcSplitPan).setDividerLocation(srcSplitPan.getDividerLocation()-10);
}
};
jSplitPane1.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_U, KeyEvent.CTRL_DOWN_MASK),
"increaseDivider");
jSplitPane1.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_D, KeyEvent.CTRL_DOWN_MASK),
"decreaseDivider");
jSplitPane1.getActionMap().put("increaseDivider", incrDividerLoc);
jSplitPane1.getActionMap().put("decreaseDivider", decrDividerLoc);
Note: method A value less than 0 passed to setDividerLocation(int) implies the divider should be reset to a value that attempts to honor the preferred size of the left/top component. After notifying the listeners, the last divider location is updated, via setLastDividerLocation.
The problem is that I do not know how to attach key bindings to up and down arrows on JSplitPane's divider.
Normally you would try to access the Action of the button. In many cases the component will already define an Action that you can use. See Key Bindings for a list of the default bindings for a JSplitPane. Unfortunately there is no Action to support the one touch clicking options.
So we need to access the buttons directly from the UI:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.basic.*;
public class SplitPaneDividerAction extends AbstractAction
{
private boolean leading;
public SplitPaneDividerAction(boolean leading)
{
this.leading = leading;
}
#Override
public void actionPerformed(ActionEvent e)
{
JSplitPane splitPane = (JSplitPane)e.getSource();
BasicSplitPaneUI ui = (BasicSplitPaneUI)splitPane.getUI();
BasicSplitPaneDivider divider = ui.getDivider();
if (leading)
((JButton)divider.getComponent(0)).doClick();
else
((JButton)divider.getComponent(1)).doClick();
}
private static void createAndShowUI()
{
JPanel leading = new JPanel();
leading.setPreferredSize( new Dimension(200, 100) );
leading.setBackground( Color.BLUE );
leading.setFocusable(true);
JPanel trailing = new JPanel();
trailing.setPreferredSize( new Dimension(200, 100) );
trailing.setBackground( Color.RED );
trailing.setFocusable(true);
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leading, trailing);
splitPane.setOneTouchExpandable(true);
splitPane.setDividerLocation(100);
InputMap im = splitPane.getInputMap(JSplitPane.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
ActionMap am = splitPane.getActionMap();
im.put(KeyStroke.getKeyStroke("control U"), "leading");
im.put(KeyStroke.getKeyStroke("control D"), "trailing");
am.put("leading", new SplitPaneDividerAction(true));
am.put("trailing", new SplitPaneDividerAction(false));
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( splitPane );
frame.setSize(200, 200);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Of course this approach will only work if your LAF extends from the BasicSplitPaneUI.
I have a simple JSlider with an attached ChangeListerner. Here's the code:
JSlider slider = new JSlider();
slider.setMinorTickSpacing(2);
slider.setMajorTickSpacing(20);
slider.setPaintLabels(true);
slider.setPaintTicks(true);
slider.setSnapToTicks(true);
slider.setOrientation(SwingConstants.VERTICAL);
contentPane.add(slider, BorderLayout.CENTER);
slider.addChangeListener(new SliderListener());
class SliderListener implements ChangeListener {
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider)e.getSource();
if (!source.getValueIsAdjusting()) {
System.out.println("boo");
}
}
}
As you can see, the code isn't doing much, all I want to do for now is make sure the event is only firing once, and hence my event is simply to print something to the Console within Eclipse.
But the above code is printing "boo" twice each time I change the Slider. I'm guessing this has got something to do with Mouse Release on Slider, but whatever it is, I want it to only fire the event once, and hence only print the word once.
How can I achieve that?
Thanks
Are you certain the listener is not added twice ? The following SSCCE works as expected on my machine (OS X, JDK7)
import javax.swing.JFrame;
import javax.swing.JSlider;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.EventQueue;
public class SliderTest {
public static void main( String[] args ) {
EventQueue.invokeLater( new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame( );
final JSlider slider = new JSlider( 0, 100 );
frame.add( slider );
slider.addChangeListener( new ChangeListener() {
#Override
public void stateChanged( ChangeEvent e ) {
if ( !( slider.getValueIsAdjusting() ) ){
System.out.println( "SliderTest.stateChanged" );
}
}
} );
frame.pack();
frame.setVisible( true );
frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE );
}
} );
}
}
I know the issue. My workaround is to set global eventOnwer to all other listeners,
That solves other event fires to occure while one is the event owner.
And then, to solve the slider, I set the getValueIsAdjusting() to true in a if. In case of another fire, return.
Note: You may have to compile and run my example to fully understand my question. If this is not kosher, I apologize in advance.
I am trying to create a Swing control that is based on a JToggleButton and a JPopupMenu.
The toggle button is selected iff the popup menu is visible, and the toggle button is deselected iff the popup menu is not visible. Thus, the behavior is similar to a JComboBox, except that the popup can contain arbitrary components.
The code that follows is an example of how I would create the control (except that it would be in its own class... something like a JPopupToggleButton). Unfortunately, it exhibits different behavior under different look and feels (I have tested it with Metal and Nimbus).
The code as posted here behaves as expected in Metal, but not in Nimbus. When using Nimbus, just show and hide the popup by repeatedly clicking the toggle button and you will see what I mean.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
public class PopupButtonExample extends JFrame
{
public static void main( String[] args )
{
java.awt.EventQueue.invokeLater( new Runnable()
{
#Override
public void run()
{
PopupButtonExample example = new PopupButtonExample();
example.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
example.setVisible( true );
}
});
}
public PopupButtonExample()
{
super( "Components in Popup" );
JPanel popupPanel = new JPanel();
popupPanel.setLayout( new BorderLayout() );
popupPanel.add( new JLabel( "This popup has components" ),
BorderLayout.NORTH );
popupPanel.add( new JTextArea( "Some text", 15, 20 ),
BorderLayout.CENTER );
popupPanel.add( new JSlider(), BorderLayout.SOUTH );
final JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add( popupPanel );
final JToggleButton popupButton = new JToggleButton( "Show Popup" );
popupButton.addActionListener( new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
if( popupButton.isSelected() )
popupMenu.show( popupButton, 0, popupButton.getHeight() );
}
});
popupMenu.addPopupMenuListener( new PopupMenuListener()
{
#Override
public void popupMenuWillBecomeVisible(PopupMenuEvent pme) {}
#Override
public void popupMenuCanceled(PopupMenuEvent pme) {}
#Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent pme) {
Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
Point componentLoc = popupButton.getLocationOnScreen();
mouseLoc.x -= componentLoc.x;
mouseLoc.y -= componentLoc.y;
if( !popupButton.contains( mouseLoc ) )
popupButton.setSelected( false );
}
});
JPanel toolBarPanel = new JPanel();
toolBarPanel.add( popupButton );
JToolBar toolBar = new JToolBar();
toolBar.add( toolBarPanel );
setLayout( new BorderLayout() );
add( toolBar, BorderLayout.PAGE_START );
setPreferredSize( new Dimension( 640, 480 ) );
pack();
}
}
Commeting out the following lines makes the code behave as expected in Nimbus, but not in Metal. Again, just keep clicking the toggle button to see what I mean.
// Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
// Point componentLoc = popupButton.getLocationOnScreen();
// mouseLoc.x -= componentLoc.x;
// mouseLoc.y -= componentLoc.y;
// if( !popupButton.contains( mouseLoc ) )
So here are my two questions:
(1) In Nimbus, why does the click that hides the popup panel not get passed to the toggle button, as it does with Metal?
(2) How can I solve this problem so that it works with all look and feels?
Nimbus is too buggy (and development ended somewhere in the middle) I see that you need three mouse click to the JToggleButton in compare with Metal
every standard L&F have got own specific issues, especially SystemLookAndFeel
use JWindow rather that JPopup, because with JPopup there are another Bugs too e.g. JPopup with JCombobox
After some investigation, I found the cause for the difference between Nimbus and Metal. The following flag is used (at least by BasicPopupMenuUI) to control the consumption of events when a popup is closed:
UIManager.getBoolean( "PopupMenu.consumeEventOnClose" );
When using Nimbus, this returns true. When using Metal, this returns false. Thus, the method popupMenuWillBecomeInvisible should be defined as follows:
if( UIManager.getBoolean( "PopupMenu.consumeEventOnClose" ) )
{
popupButton.setSelected( false );
}
else
{
Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
Point componentLoc = popupButton.getLocationOnScreen();
mouseLoc.x -= componentLoc.x;
mouseLoc.y -= componentLoc.y;
if( !popupButton.contains( mouseLoc ) )
{
popupButton.setSelected( false );
}
}