OK so before my INV program was to REMOVE menu items but then I was all like.. that's too much. So what if I were to ADD menu items for specific Right-clicked items INSTEAD of removing every time?
So, if you right-clicked on Item 1, you'd get "Use" and "Drop" added to the menu. Then ONCE you choose your option, the JMenu would delete everything so it would be right where we started. Then if you right-clicked on ITem 2, it would add "Use" and "Cancel". See where I'm going?
I tried doing it myself, but I just can't figure out how to do it -- for example, to add a new JMenuItem, you need to do this:
popup.add(item = new JMenuItem("Cancel"));
item.addActionListener(menuListener);
and, as you can see, add an actionlistener. I can't do that under if (actItemx == "Item 1") { so... what do I do?
Anyways, here's what I have so far:
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseAdapter;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
public class inv extends JApplet implements MouseListener
{
public JList listbox;
public JPopupMenu popup;
public JMenuItem item;
public void init()
{
ActionListener menuListener = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
String invAction = event.getActionCommand();
int itemSelect = listbox.getSelectedIndex();
Object actItem = listbox.getModel().getElementAt(itemSelect);
System.out.println("Popup menu item [" + invAction + "] [ " + actItem + " ] was pressed.");
}
};
popup = new JPopupMenu();
popup.add(item = new JMenuItem("Use"));
item.addActionListener(menuListener);
popup.add(item = new JMenuItem("Drop"));
item.addActionListener(menuListener);
popup.add(item = new JMenuItem("Cancel"));
item.addActionListener(menuListener);
String listData[] =
{
"Item 1","Item 2","Item 3","Item 4"
};
listbox = new JList( listData );
listbox.addMouseListener( new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
if ( SwingUtilities.isRightMouseButton(e) )
{
System.out.println("Row: " + getRow(e.getPoint()));
listbox.setSelectedIndex(getRow(e.getPoint()));
}
}
}
);
listbox.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
add(listbox);
listbox.setVisible(true);
listbox.setFocusable(false);
listbox.addMouseListener(new MousePopupListener());
}
class MousePopupListener extends MouseAdapter
{
public void mousePressed(MouseEvent e)
{
checkPopup(e);
}
public void mouseClicked(MouseEvent e)
{
checkPopup(e);
}
public void mouseReleased(MouseEvent e)
{
checkPopup(e);
}
private void checkPopup(MouseEvent e)
{
if (e.isPopupTrigger())
{
int itemSelectx = listbox.getSelectedIndex();
Object actItemx = listbox.getModel().getElementAt(itemSelectx);
System.out.println("You pressed on " + actItemx);
if (actItemx == "Item 1") {
System.out.println("Removed cancel for " + actItemx);
popup.remove(itemSelectx); // So upon right-click on Item 1, you won't see "Cancel" menu.
}
popup.show(inv.this, e.getX(), e.getY());
popup.revalidate();
}
}
}
private int getRow(Point point)
{
return listbox.locationToIndex(point);
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
}
public void mousePressed(MouseEvent e)
{
}
public void mouseClicked(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
}
What about setting up different JPopup menu's for each type of item. What I mean by that is you have something that looks like this:
public JPopupMenu useDropPopup;
public JPopupMenu useCancelPopup;
public void init() {
ActionListener menuListener = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
String invAction = event.getActionCommand();
int itemSelect = listbox.getSelectedIndex();
Object actItem = listbox.getModel().getElementAt(itemSelect);
System.out.println("Popup menu item [" + invAction + "] [ " + actItem + " ] was pressed.");
}
};
useDropPopup = new JPopupMenu();
useCancelPopup = new JPopupMenu();
JMenuItem useMenuItem = new JMenuItem("Use");
useMenuItem.addActionListener(menuListener);
JMenuItem dropMenuItem = new JMenuItem("Drop");
dropMenuItem.addActionListener(menuListener);
JMenuItem cancelMenuItem = new JMenuItem("Cancel");
cancelMenuItem.addActionListener(menuListener);
useDropPopup.add(useMenuItem);
useDropPopup.add(dropMenuItem);
useCancelPopup.add(useMenuItem);
useCancelPopup.add(cancelMenuItem);
// ... etc bring up the appropriate popup depending on the item.
}
Also, you should not assign a JMenuItem to item inside of a method call. That's bad practice. Also consider using different actionlisteners for each menu item so you can separate the functional code of each menu item, ex:
useMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
useMenuAction(event);
}
};
// ... after the init method
public void useMenuAction(ActionEvent evt) {
// Add specific use menu code here.
}
This is mostly an addition to the answer from jluzwick:
Instead of creating a JMenuItem and adding an ActionListener to it, you can use a Action - this is basically a combination of a ActionListener with a name, optionally Icon and some other properties. (Most simply extend from AbstractAction, overriding the actionPerformed-Method.)
Then add the Action to your JMenu, and it will construct the JMenuItem itself. (You can also use the same Action object on other places, like Buttons, the "normal" menu bar, etc.)
Related
I have this java swing program, and im trying to figure out how can i create a button that upon clicking it will clear the text areas & change the icon of the person to put their hand down.
The buttons are dynamically generated using a for loop
And this
// To create buttons
for(int i=0 ; i < list.length; i++){
Participant pa = list[i];
JButton b = new JButton(pa.getNameButton(),participant);
b.addActionListener(e ->
{
String s = pa.toString() + questionPane.getText();
final ImageIcon raise = resizeIcon(new ImageIcon("src/raise.png"),30,30);
b.setIcon(raise);
JOptionPane.showMessageDialog(null,s,"Welcome to Chat Room",JOptionPane.INFORMATION_MESSAGE,pa.getImage());
});
p.add(b);
}
// Clear button logic
clearButton.addActionListener(e ->{
questionPane.setText("");
hostPane.setText("");
});
Okay, this is going to be a bit of fun.
The following example decouples much of the concept and makes use of a basic "observer pattern" to notify interested parties that the state has changed (ie, the chat's been cleared).
This is a basic concept where by you decouple the "what" from the "how", ie, "what" it is you want done (update the model) from the "how" it gets done (ie, button push). This makes it easier to adapt to more complex systems.
The example contains a ChatService, which has a single listener, which, for this example, simple tells interested parties that the chat has been cleared.
A more complex solution might have the ChatService generating events for when a user "raises" their hand, which allows the interested parties to deal with it in what ever way is relevant to them.
The example makes use of the Action API to decouple the work performed by each action from the UI itself. This helps create a single unit of work which is easier to deal with when you have a dynamic data set.
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
ChatService chatService = new ChatService();
JPanel panel = new JPanel();
String[] names = new String[] {"Bryan", "Alan", "George", "Henry"};
List<PeopleAction> actions = new ArrayList<>(names.length);
for (String name : names) {
PeopleAction action = new PeopleAction(chatService, name, false);
actions.add(action);
}
Random rnd = new Random();
actions.get(rnd.nextInt(names.length)).setRaised(true);
for (Action action : actions) {
JButton btn = new JButton(action);
panel.add(btn);
}
setLayout(new GridLayout(2, 1));
add(panel);
JPanel hostPane = new JPanel();
JButton clearButton = new JButton(new ClearAction(chatService));
hostPane.add(clearButton);
add(hostPane);
}
}
public class ChatService {
private List<ChatListener> listeners = new ArrayList<>(25);
public void addChatListeners(ChatListener listener) {
listeners.add(listener);
}
public void removeChatListener(ChatListener listener) {
listeners.remove(listener);
}
protected void fireChatCleared() {
if (listeners.isEmpty()) {
return;
}
for (ChatListener listener : listeners) {
listener.chatCleared();
}
}
public void clear() {
// Do what's required
fireChatCleared();
}
}
public interface ChatListener {
public void chatCleared();
}
public class PeopleAction extends AbstractAction implements ChatListener {
private String name;
private boolean raised;
public PeopleAction(ChatService chatService, String name, boolean raised) {
// You can use either LARGE_ICON_KEY or SMALL_ICON to set the icon
this.name = name;
if (raised) {
putValue(NAME, "* " + name);
} else {
putValue(NAME, name);
}
chatService.addChatListeners(this);
}
public void setRaised(boolean raised) {
if (raised) {
putValue(NAME, "* " + name);
} else {
putValue(NAME, name);
}
}
public boolean isRaised() {
return raised;
}
#Override
public void actionPerformed(ActionEvent evt) {
// Do what ever needs to be done
setRaised(!isRaised());
}
#Override
public void chatCleared() {
setRaised(false);
}
}
public class ClearAction extends AbstractAction {
private ChatService chatService;
public ClearAction(ChatService chatService) {
this.chatService = chatService;
putValue(NAME, "Clear");
}
#Override
public void actionPerformed(ActionEvent evt) {
chatService.clear();
}
}
}
I am trying to write a class that both listens for actions from buttons and notifies another class when one of the buttons is pressed. I have an ArrayList<ActionListener> and methods addActionListener(ActionListener al), removeActionListener(ActionListener al), and notifyActionListeners(ActionEvent ae). I print to a separate window whenever I add a listener, and print the size of actionListeners as well. It works great and prints that I have 1 actionListener, but then when I try to notify the listeners it says that there are 0 objects in actionListeners. I added a println() to the removeActionListener(al) method to see if it is called, and it never is.
Here's the class:
package state;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import driver.GameDriver;
import ui.Button;
public class MainMenu extends Menu {
private static final long serialVersionUID = -7130241947836998525L;
private ArrayList<ActionListener> actionListeners;
private Button play;
private Button scores;
private Button settings;
private Button help;
private Button exit;
public MainMenu() {
super("Main Menu");
actionListeners = new ArrayList<ActionListener>();
}
#Override
protected void addComponents() {
//Irrelevant to Stackexchange
}
#Override
public void actionPerformed(ActionEvent arg0) {
Object src = arg0.getSource();
if (src == play) {
} else if (src == scores) {
} else if (src == settings) {
} else if (src == help) {
} else if (src == exit) {
ActionEvent ae = new ActionEvent(this, ActionEvent.ACTION_FIRST, "exit");
notifyActionListeners(ae);
}
}
public void addActionListener(ActionListener al) {
GameDriver.println("Added Listener:");
actionListeners.add(al);
GameDriver.println(actionListeners.size());
}
public void removeActionListener(ActionListener al) {
GameDriver.println("Removed al for some reason");
actionListeners.remove(al);
}
private void notifyActionListeners(ActionEvent ae) {
GameDriver.println("Sending exit to " + actionListeners.size() + " listeners.");
for(int i = 0; i < actionListeners.size(); i++) {
GameDriver.println("Exit sent");
actionListeners.get(i).actionPerformed(ae);
}
}
}
Here are the methods that actually reference the instance of MainMenu:
1. Initialization
protected GameDriver() {
mainMenu = new MainMenu();
mainMenu.addActionListener(this);
debugger = new Debugger();
println("Size Loader Test...");
SizeLoader.loadSizes();
println(SizeLoader.getCurrentSize());
println("Complete.");
println("Window Test...");
window = new Window("Asteroids");
windowManager = new WindowManager();
// window.addWindowFocusListener(windowManager);
// window.addWindowListener(windowManager);
// window.addWindowStateListener(windowManager);
window.buildWindow(SizeLoader.getCurrentWidth(), SizeLoader.getCurrentHeight());
window.add(new MainMenu());
println("Complete");
println("Menu Test...");
}
And here's the actionPerformed(ae):
#Override
public void actionPerformed(ActionEvent e) {
println("Event happened");
if (e.getSource() == mainMenu) {
if (e.getActionCommand() == "exit") {
println("Exiting FR this time...");
}
}
}
As per my comment: 3) You're creating more than one MainMenu object, one you add a listener to and the other you add to window. This looks to be a serious bug.
Instead create only one instance, set its state as needed (add listeners) and add it to the gui.
So change
window.add(new MainMenu());
To
window.add(mainMenu);
And again as per my prior comment, don't use == for String equality check but rather the .equals method.
So I have a JFrame set up with a menu with the current structure that looks something along the lines of this:
File
Exit
Pages
Reviews
A
B
C
Help
About
I want to create a Action Listener that only listens to menu items under Reviews. Is this a possibility (and if so, how) or do I have to create a generic listener and check if it's one of those items?
Yes, it is possible:
Store your menu items as fields
Add the same ActionListener to each menu item.
In the listener check for the source to know which item was clicked.
Should look like:
public class YourFrame extends JFrame implements ActionListener {
private final JMenuItem menuA, menuB;
public YourFrame(){
super("Your app");
JMenuBar menuBar = new JMenuBar();
JMenu menuReviews = new JMenu("Reviews");
menuA = new JMenuItem("A");
menuB = new JMenuItem("B");
...
menuReviews.add(menuA);
menuReviews.add(menuB);
menuBar.add(menuReviews);
setJMenuBar(menuBar);
...
menuA.addActionListener(this);
menuB.addActionListener(this);
...
}
public void actionPerformed(ActionEvent event){
if(event.getSource()==menuA){
System.out.println("Menu A clicked");
...
}else if(event.getSource()==menuB){
System.out.println("Menu B clicked");
...
}
}
}
Note that here I let the JFrame implement ActionListener, but this is just for convenience. You could use a dedicated class, or an anonymous class created in the constructor:
ActionListener reviewsListener = new ActionListener(){
public void actionPerformed(ActionEvent event){
if(event.getSource()==menuA){
System.out.println("Menu A clicked");
...
}else if(event.getSource()==menuB){
System.out.println("Menu B clicked");
...
}
}
};
menuA.addActionListener(reviewsListener);
menuB.addActionListener(reviewsListener);
If you want to integrate this process a little more, I could also suggest to extend JMenu, so that you can pass it your action listener and add it systematically to new menu items.
public class YourJMenu extends JMenu {
private ActionListener listener;
public YourJMenu(String name, ActionListener listener){
super(name);
this.listener = listener;
}
#Override
public JMenuItem add(JMenuItem item){
item.addActionListener(listener);
return super.add(item);
}
}
With this, you just need to write:
JMenu menuReviews = new YourJMenu("Reviews", this);
and drop the:
menuA.addActionListener(this);
menuB.addActionListener(this);
Using a common method we can add the action listener to all the menu items under a menu. Below is a example code.
public class MenuItemEvent {
JFrame objFrm = new JFrame("Menu event demo");
JMenuBar mBar;
JMenu mnu;
JMenuItem mnuItem1, mnuItem2, mnuItem3;
public void show() {
objFrm.setSize(300, 300);
mBar = new JMenuBar();
mnu = new JMenu("Reviews");
mBar.add(mnu);
mnuItem1 = new JMenuItem("A");
mnu.add(mnuItem1);
mnuItem2 = new JMenuItem("B");
mnu.add(mnuItem2);
mnuItem3 = new JMenuItem("C");
mnu.add(mnuItem3);
//method call
fnAddActionListener(mnu);
objFrm.setJMenuBar(mBar);
objFrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
objFrm.setVisible(true);
}
//method to add action listener to all menu items under a menu
public void fnAddActionListener(JMenu mnu) {
if (mnu.getItemCount() != 0) {
for (int iCount = 0; iCount < mnu.getItemCount(); iCount++) {
(mnu.getItem(iCount)).addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
fnMenuItemAction(e);
}
});
}
}
}
//menu item action event
public void fnMenuItemAction(ActionEvent e) {
if (e.getSource().equals(mnuItem1)) {
System.out.println("Menu Item 1");
} else if (e.getSource().equals(mnuItem2)) {
System.out.println("Menu Item 2");
} else if (e.getSource().equals(mnuItem3)) {
System.out.println("Menu Item 3");
}
}
public static void main(String[] args) {
new MenuItemEvent().show();
}
}
or with the below function
//fnMenuItemAdd(mnu,mnuItem1)
//etc.
public void fnMenuItemAdd(JMenu mnu, JMenuItem mni) {
mnu.add(mni);
mni.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
fnMenuItemAction(e);
}
});
}
My menuitem are being added thorugh database .
i have to perform action such as opening the new jframe , if a user select a particular menuitem.
Here the menu dimension is add to the Menubar , and under which various menuitem are being added such as Period , Entity, which are being fetch from database.
Now i want to open a new jframe on the click of Period menuitem .
public void MenuExp(){
JMenu DimensionMenu = new JMenu("Dimension");
JMenu editMenu = new JMenu("Help");
jMenuBar1.add(DimensionMenu);
jMenuBar1.add(editMenu);
//JMenuItem newAction = new JMenuItem("Account");
//fileMenu.add(newAction);
//JMenuItem newPeriod = new JMenuItem("Period");
//fileMenu.add(newPeriod);
try{
Class.forName("oracle.jdbc.OracleDriver");
Connection comm = (Connection)DriverManager.getConnection("jdbc:oracle:thin:#192.168.100.25:1521:orcl","SYSTEM","Admin123");
Statement st = comm.createStatement();
String Query = "select OBJECT_NAME from RAHUL_APP1.HSP_OBJECT where OBJECT_TYPE = 2 AND OBJECT_ID <> 30" ;
//and User_Name ='" + jTextField1.getText()+"'";
ResultSet rs = st.executeQuery(Query);
while(rs.next()){
JMenuItem newAction = new JMenuItem(rs.getString(1));
DimensionMenu.add(newAction);
rs.close();
st.close();
comm.close();
newAction.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0){
System.out.println("You have clicked on the Account");
}
});
}
} catch(Exception e){
JOptionPane.showMessageDialog(this,e);
}
}
You need to do some parametrization of the frame or have for example frame class stored also in DB and initialize it using reflexion...
Update:
Implementation can be like this:
package betlista.so.swing.menuitemdialogsfromdb;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
public class MainFrame extends JFrame {
public MainFrame() {
super("Frame");
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
add(createMenu());
pack();
}
private JMenuBar createMenu() {
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("Open");
menu.add(new DialogCreatingMenuItem("Dialog 1", "betlista.so.swing.menuitemdialogsfromdb.MainFrame$MyDialog1"));
menu.add(new DialogCreatingMenuItem("Dialog 2", "betlista.so.swing.menuitemdialogsfromdb.MainFrame$MyDialog2"));
menuBar.add(menu);
return menuBar;
}
class DialogCreatingMenuItem extends JMenuItem implements ActionListener {
String className;
public DialogCreatingMenuItem(String text, String className) throws HeadlessException {
super(text);
this.className = className;
addActionListener(this);
}
#Override
public void actionPerformed(ActionEvent ae) {
try {
Class<JDialog> clazz = (Class<JDialog>)Class.forName(this.className);
JDialog dialog = clazz.newInstance();
dialog.setVisible(true);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
public static class MyDialog1 extends JDialog {
public MyDialog1() {
setTitle("Dialog 1");
add(new JLabel("Dialog 1"));
pack();
}
}
public static class MyDialog2 extends JDialog {
public MyDialog2() {
setTitle("Dialog 2");
add(new JLabel("Dialog 2"));
pack();
}
}
public static void main(String[] args) {
new MainFrame().setVisible(true);
}
}
where Strings in
menu.add(new DialogCreatingMenuItem("Dialog 1", "betlista.so.swing.menuitemdialogsfromdb.MainFrame$MyDialog1"));
menu.add(new DialogCreatingMenuItem("Dialog 2", "betlista.so.swing.menuitemdialogsfromdb.MainFrame$MyDialog2"));
are retrieved from database...
Here is a sample code:
menuItem1.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
...
}
});
Remember the steps to creating a menu:
1. Create a MenuBar and add to the panel
2. Create a Menu and add to MenuBar
3. Create a MenuItem and add to Menu
Then add the listener to the MenuItem
Edit: if you use it outside the try statement it should work
Now i want to open a new jframe on the click of Period menuitem
Of course you have to add an ActionListener to your menu to do that, but the real question is How do you determine the right listener to each menu item? Since you fetch your menu items from database then it's not that easy as it looks like.
Note: see The Use of Multiple JFrames, Good/Bad Practice?
Option 1 (kind of dirty)
As I've said, you could store an action command and set it back to the menu item just like you set the menu's name:
while(rs.next()) {
String menuName = rs.getString("menuname");
String actionCommand = rs.getString("actioncommand");
JMenuItem newAction = new JMenuItem(menuName);
newAction.setActionCommand(actionCommand);
DimensionMenu.add(newAction);
...
}
Then you can have a listener that make use of ActionEvent#getActionCommand() to decide the right action to perform and attach this listener to all your menu items:
class MenuActionListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent evt) {
String actionCommand = evt.getActionCommand();
switch (actionCommand) {
case "OpenNewDialog": openNewDialog(); break;
...
}
}
private void openNewDialog() {
// implementation here
}
}
Then:
ActionListener listener = new MenuActionListener();
while(rs.next()) {
String menuName = rs.getString("menuname");
String actionCommand = rs.getString("actioncommand");
JMenuItem newAction = new JMenuItem(menuName);
newAction.setActionCommand(actionCommand);
newAction.addActionListener(listener);
DimensionMenu.add(newAction);
...
}
Option 2
Implement Factory method pattern to create a specific ActionListener or Action based on menu's action command:
class ActionListenerFactory {
public static Action createAction(final String actionCommand) {
switch (actionCommand) {
case "OpenNewDialog": return openNewDialogAction(); break;
...
}
}
private Action openNewDialogAction() {
Action action = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent evt) {
// open new dialog here
}
};
return action;
}
}
Then:
while(rs.next()) {
String menuName = rs.getString("menuname");
String actionCommand = rs.getString("actioncommand");
JMenuItem newAction = new JMenuItem(menuName);
newAction.setActionCommand(actionCommand);
newAction.addActionListener(ActionListenerFactory.createAction(actionCommand));
DimensionMenu.add(newAction);
...
}
See also:
How to Use Actions tutorial.
Concurrency in Swing lesson.
Worker threads and SwingWorker to do database calls in a background thread and create/update Swing component in the Event Dispatch Thread
I'm blocked with a probelm about Java and the use of JTree:
I want to create a JTree with, node by node, some JButtons components (or Images, I don't mind), like in the following picture. It will be 3 or 4 buttons in the same row. I succeed to do that.
But where I'm blocked is when I want to add a mouselistener on each of this button to manage their tooltip or an action on them.
In fact the JTree component is most of the time used to manage the action on the full node, but not on its inside components.
I did a short code, in comparaison at the real big code I have to work in, to quickly test what I say:
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.tree.*;
import java.awt.event.*;
import java.awt.*;
import java.io.IOException;
import java.net.URL;
public class TreeWithPopup extends JPanel {
DefaultMutableTreeNode root, node1, node2, node3;
public TreeWithPopup() {
MyJTree tree;
root = new DefaultMutableTreeNode("root", true);
node1 = new DefaultMutableTreeNode("node 1", true);
node2 = new DefaultMutableTreeNode("node 2", true);
node3 = new DefaultMutableTreeNode("node 3", true);
root.add(node1);
node1.add(node2);
root.add(node3);
setLayout(new BorderLayout());
tree = new MyJTree(root);
tree.setCellRenderer(new PCellRenderer());
add(new JScrollPane((JTree) tree), "Center");
}
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
public static void main(String s[]) {
JFrame frame = new JFrame("Tree with button");
TreeWithPopup panel = new TreeWithPopup();
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.setForeground(Color.black);
frame.setBackground(Color.lightGray);
frame.getContentPane().add(panel, "Center");
frame.setSize(panel.getPreferredSize());
frame.setVisible(true);
frame.addWindowListener(new WindowCloser());
}
}
class WindowCloser extends WindowAdapter {
public void windowClosing(WindowEvent e) {
Window win = e.getWindow();
win.setVisible(false);
System.exit(0);
}
}
class MyJTree extends JTree implements ActionListener {
MyJTree(DefaultMutableTreeNode dmtn) {
super(dmtn);
addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent arg0) {
System.out.println("JTree.MouseListener");
}
public void mouseEntered(MouseEvent arg0) {
System.out.println("JTree.MouseListener");
}
public void mouseExited(MouseEvent arg0) {
System.out.println("JTree.MouseListener");
}
public void mousePressed(MouseEvent arg0) {
System.out.println("JTree.MouseListener");
}
public void mouseReleased(MouseEvent arg0) {
System.out.println("JTree.MouseListener");
}
});
}
public void actionPerformed(ActionEvent ae) {
System.out.println("MyJTree.ActionPerformed");
}
}
class PCellRenderer extends DefaultTreeCellRenderer {
public Component getTreeCellRendererComponent(JTree ptree, Object pvalue, boolean psel, boolean pexpanded, boolean pleaf, int prow,
boolean phasFocus) {
Box myPanel = new Box(BoxLayout.X_AXIS);
JButton myButton = new JButton("test");
Image imgToUse = null;
Image imgRollOver = null;
try {
URL urlIcon = new URL("file:///C:/1.jpg"); // <===== change their the path to icons
imgToUse = ImageIO.read(urlIcon);
urlIcon = new URL("file:///C:/2.jpg"); // <===== change their the path to icons
imgRollOver = ImageIO.read(urlIcon);
} catch (IOException e) {
e.printStackTrace();
}
myButton.setRolloverIcon(new ImageIcon(imgRollOver));
myButton.setIcon(new ImageIcon(imgToUse));
myButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
System.out.println(" detected on ");
}
});
myButton.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent arg0) {
System.out.println("myButton.MouseListener");
}
public void mouseEntered(MouseEvent arg0) {
System.out.println("myButton.MouseListener");
}
public void mouseExited(MouseEvent arg0) {
System.out.println("myButton.MouseListener");
}
public void mousePressed(MouseEvent arg0) {
System.out.println("myButton.MouseListener");
}
public void mouseReleased(MouseEvent arg0) {
System.out.println("myButton.MouseListener");
}
});
myPanel.add(myButton);
return myPanel;
}
}
You just have to change the icon path or put the two following icons at "c:/"
I also searched to use the x/y position of the row event but I was unable to find the position of my button after rendering.
If anyone can have an idea how to do that, he could be very helpful.
Thanks, at least for read this question ;-)
Tooltip support is actually provided by the TableCellRenderer directly. Your TableCellRenderer is a little muddled. It extends from DefaultTreeCellRenderer but makes no use of any of it's features, instead creating a new Box, JButton and loading icons each time a cell is rendered...
This is going to increase your memory usage a slow you application down...
Instead, try something like...
class PCellRenderer extends Box implements TreeCellRenderer {
private Image imgToUse = null;
private Image imgRollOver = null;
public PCellRenderer() {
super(BoxLayout.X_AXIS);
JButton myButton = new JButton("test");
try {
URL urlIcon = new URL("file:///C:/1.jpg"); // <===== change their the path to icons
imgToUse = ImageIO.read(urlIcon);
urlIcon = new URL("file:///C:/2.jpg"); // <===== change their the path to icons
imgRollOver = ImageIO.read(urlIcon);
} catch (IOException e) {
e.printStackTrace();
}
myButton.setRolloverIcon(new ImageIcon(imgRollOver));
myButton.setIcon(new ImageIcon(imgToUse));
add(myButton);
}
public Component getTreeCellRendererComponent(JTree ptree, Object pvalue, boolean psel, boolean pexpanded, boolean pleaf, int prow,
boolean phasFocus) {
// set the tooltip text here...
// Maybe change the icon...
return this;
}
}
Now, actually doing something...
Renderers are rubber stamps. That are not actually life components. Think of them like photos. You can take a snap shot of your friends, but you can't interact with them...same thing here...
Your idea of a MouseListener on the JTree is a correct one, in fact the JavaDocs actually have a demonstration of this...
public void mousePressed(MouseEvent e) {
int selRow = tree.getRowForLocation(e.getX(), e.getY());
TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
if(selRow != -1) {
if(e.getClickCount() == 1) {
mySingleClick(selRow, selPath);
}
else if(e.getClickCount() == 2) {
myDoubleClick(selRow, selPath);
}
}
}