Javafx 2.0 - Get ContextMenu Parent Node in EventHandler - tabs

I am writing a javafx UI and would like to get the owner Node of a contextMenu from a eventHandler for the MenuItem that was clicked on.
My Code:
TabPane tabPane = new TabPane();
Tab tab1 = new Tab();
Tab tab2 = new Tab();
tabPane.getTabs().addAll(tab1,tab2);
ContextMenu contextMenu = new ContextMenu();
MenuItem menuItem = new MenuItem("Do Some Action");
menuItem.setOnAction(new EventHandler<ActionEvent>(){
#override
public void handle(ActionEvent e){
// Get the tab which was clicked on and do stuffs with it
}
});
contextMenu.getItems().add(menuItem);
for(Tab tab: tabPane.getTabs()){
tab.setContextMenu(contextMenu);
}
What I would like to do is get a reference to the tab that had it's contextMenu selected.
I was able to get a reference to what appears to be the ContextMenu of the MenuItem with the following code inside of the handle(ActionEvent e) method for the menuItem eventHandler:
ContextMenu menu = ((ContextMenu)((MenuItem)e.getSource()).getParentPopup());
My idea from there was to use ContextMenu's .getOwnerNode() method on menu and then have a reference to the tab, but when running that I get a reference to an item that I can't make sense of.
The toString() method for the object returned by .getOwnerNode() returns "TabPaneSkin$TabHeaderSkin$3#14f59cef" which I can't figure out the meaning of.
Is my approach of trying to work my way up the chain until I get to the node correct or is there an entirely different approach that will work better?
All I need to have is the functionality of a ContextMenu, and when the MenuItem is clicked on, I need to have a reference to the tab for which the ContextMenu was selected so I can do cool stuffs with it.

Create a ContextMenu for each tab.
Make each tab final.
Directly reference the final tab in the menu item event handler for the context menu.
Here is a code snippet:
final Tab tab = new Tab("xyzzy");
ContextMenu contextMenu = new ContextMenu();
MenuItem menuItem = new MenuItem("Do Some Action");
menuItem.setOnAction(new EventHandler<ActionEvent>(){
#Override public void handle(ActionEvent e){
tab.setText("Activated by User");
}
});
Every time the user right clicks on a tab header and selects the "Count Click" menu, the content of the related tab is updated with a count of the number of licks counted so far for that tab.
Here is an executable sample:
import javafx.application.*;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;
public class TabContext extends Application {
#Override public void start(Stage stage) {
TabPane tabPane = new TabPane();
tabPane.getTabs().addAll(
createTab("xyzzy", "aliceblue"),
createTab("frobozz", "blanchedalmond")
);
stage.setScene(new Scene(tabPane));
stage.show();
}
private Tab createTab(String tabName, String webColor) {
final Label content = new Label("0");
content.setAlignment(Pos.CENTER);
content.setPrefSize(200, 100);
content.setStyle("-fx-font-size: 30px; -fx-background-color: " + webColor + ";");
final Tab tab = new Tab(tabName);
tab.setContent(content);
ContextMenu contextMenu = new ContextMenu();
MenuItem menuItem = new MenuItem("Count Click");
menuItem.setOnAction(new EventHandler<ActionEvent>(){
#Override public void handle(ActionEvent e){
content.setText(
"" + (Integer.parseInt(content.getText()) + 1)
);
}
});
contextMenu.getItems().add(menuItem);
tab.setContextMenu(contextMenu);
return tab;
}
public static void main(String[] args) { Application.launch(args); }
}
Alternately to using an anonymous inner class this way, you could create an EventHandler subclass with a constructor that includes the Tab for which the EventHandler is attached.
class TabContextMenuHandler implements EventHandler<ActionEvent> {
private final Tab tab;
TabContextMenuHandler(Tab tab) {
this.tab = tab;
}
#Override public void handle(ActionEvent event) {
tab.setText("Activated by User");
}
}

Related

JavaFX ChoiceBox ContextMenu shown again when it is displayed and clicked again

I have JavaFX panel with ChoiceBox in Swing application. Standard behaviour of ChoiceBox is that when you click it for the first time the popup menu with items is shown and when you click ChoiceBox for the second time, the popup menu is hidden. But when you put it to Swing application the second click causes popup to hide and to be shown immediately again. How can I prevent this behaviour?
public class ComboTest {
private static void initAndShowGUI() {
JFrame frame = new JFrame("FX");
final JFXPanel fxPanel = new JFXPanel();
fxPanel.setPreferredSize(new Dimension(100, 100));
frame.add(fxPanel);
frame.pack();
frame.setVisible(true);
Platform.runLater(new Runnable() {
#Override
public void run() {
initFX(fxPanel);
}
});
}
private static void initFX(JFXPanel fxPanel) {
// This method is invoked on JavaFX thread
Scene scene = createScene();
fxPanel.setScene(scene);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
initAndShowGUI();
}
});
}
private static Scene createScene() {
ChoiceBox choiceBox = new ChoiceBox(FXCollections.observableArrayList("item 1", "item 2"));
VBox vbox = new VBox(choiceBox);
return new Scene(vbox);
}
}
My suspicion is that when I click the choicebox for the second time the popup loses focus which causes it to hide and the choicebox then handles mouse click and shows the popup again.
I believe that this problem caused by the existing ChoiceBox bug in javafx.
The simplest fix is just to use ComboBox instead:
ComboBox<String> choiceBox = new ComboBox<>(FXCollections.observableArrayList("item 1", "item 2"));

Resetting JTextfield Within JButton Actionlistener

I'm using CardLayout to develop a budget application. My code below creates a JPanel main_panel that houses a homepage JPanel card called "homePageCard" and another JPanel card called "addItemCard". When I click on the JButton "addItemButton" entitled "Add Item" it successfully takes me to the "addItemCard".
I also inserted a "previousButton" entitled "previous" to my "addItemCard" to take me back to the "homePageCard", added a "saveBudgetItem" button for the user to save his or her budget item data, and added 2 JTextFields: "budget_Item_Title_Field" and "budget_Amount_Field" to the "addItemCard". If you user tries to save the budget item by clicking on the JButton "saveBudgetItemButton" without filling out either of these two text fields (aka if either of the 2 fields contain empty Strings), the user should receive a JOptionPane error message dialog box. If the save is successful, another JOptionPane dialog box should tell the user the budget item was saved successfully.
Here's the crux of my problem: when I click on "previous" to get to the homepageCard and clear the text fields (within the action listener of the previous button), and then return to the addItemCard by pressing the "Add Item" button, it successfully clears both textfields by using budget_Item_Title_Field.setText("") and budget_Amount_Field.setText("").
However, when I type in new text for both text fields and click on the saveBudgetItem JButton, I incorrectly get the error message detailed below as a JOptionPane.showmessagedialog message, complaining that I still have empty Strings in either textfield when both fields are NOT empty.
How do I make it so that when I return to the addItemCard after clicking the "previous" button and type in a new budget item's title and amount in their respective textfields, the program recognizes that the updated textfields no longer contain empty Strings?
Here is my main class code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Main
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Create main GUI to display and to use software, and add contents to it
HomePage h = new HomePage();
}});
}
}
Here is my code for the HomePage class:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
public class HomePage extends JFrame {
// Panel to contain contents of JFrame
private static JPanel contentPanel = new JPanel(new BorderLayout());
private static Dimension screenDimensions = Toolkit.getDefaultToolkit().getScreenSize();
private static int screenWidth = screenDimensions.width;
private static int screenHeight = screenDimensions.height;
private static int frameWidth = 800;
private static int frameHeight = 510;
// the following are components for card layout and widgets for entering budget data
private static JPanel main_panel = new JPanel(new CardLayout());
private static JPanel homeCard = new JPanel(new BorderLayout());
private static JPanel addItemCard = new JPanel(new BorderLayout());
private static JPanel textfieldPanel = new JPanel(new FlowLayout());
private static JTextField budget_Title_Field;
private static JTextField amount_Field;
private static JButton previous = new JButton("<< previous");
private static JButton addItemButton = new JButton("Add Revenue or Expense");
private static JButton saveBudgetItemButton = new JButton("Save Budget Item");
public HomePage() {
// format frame
setSize(frameWidth, frameHeight);
setResizable(false);
setLocation((screenWidth - frameWidth) / 2, (screenHeight - frameHeight) / 2);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
// format home page and add all cards to main_panel to manipulate them in card layout
formatHomeCard(homeCard);
// add all cards to main_panel
main_panel.add(homeCard, "Home");
main_panel.add(addItemCard, "Add Item");
// add main_panel to home JFrame
this.add(main_panel);
}
public static void formatHomeCard(JPanel homeCard)
{
// Add functionality to addItemButton so it switches the card to the Add Item page
addItemButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
// Adds all of the contents of the add revenue or expense page to the
// addItemCard
formatAddItemCard(addItemCard);
// If the add item button is pressed, switch the JPanel so that the add item card
// shows
CardLayout cardLayout = (CardLayout) main_panel.getLayout();
cardLayout.show(main_panel, "Add Item");
}
});
// Add the addItemButton to the home page
homeCard.add(addItemButton, BorderLayout.CENTER);
}
// the purpose of this method is to add all contents to addItemCard
public static void formatAddItemCard(JPanel addItemCard)
{
// add 2 jtextfields, one for the budget title and the other for the budget amount
// I excluded irrelevant JLabels, etc. for the sake of avoiding unnecessary details
textfieldPanel.add(budget_Title_Field);
textfieldPanel.add(amount_Field);
addItemCard.add(textfieldPanel, BorderLayout.NORTH);
// add functionality to save budget item button and add it to addItemCard
saveBudgetItemButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
// If the data fields are complete, create and serialize the budget item. Else,
// prompt the user for more information.
if (!(budget_Title_Field.getText().equals("")) && !(amount_Field.getText().equals("")))
{
// will save the budget data here. confirm the user saved the budget item.
JOptionPane.showMessageDialog(null, "Success! Your budget item was saved and has been integrated into your budget.");
}
// if either budget item title or amount is missing, complain and don't save budget item
if (budget_Title_Field.getText().equals("") || amount_Field.getText().equals(""))
{
// PROBLEMATIC CODE HERE: INCORRECTLY ASSUMES TEXTFIELDS ARE EMPTY WHEN THEY'RE
// NOT AFTER CLICKING ON "PREVIOUS" BUTTON, THEN RETURNING TO THIS PAGE BY
// CLICKING "ADD REVENUE OR EXPENSE" BUTTON
// print out error message that budget item title and amount is incomplete
JOptionPane.showMessageDialog(null, "Please enter in both the budget item title and dollar amount to continue.");
}
}
});
addItemCard.add(saveBudgetItemButton, BorderLayout.CENTER);
// Add functionality to previous button and add it to the addItemCard
previous.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
// If the previous button is pressed, switch the JPanel so that the home page card shows
CardLayout cardLayout = (CardLayout) main_panel.getLayout();
cardLayout.show(main_panel, "Home");
budget_Title_Field.setText("");
amount_Field.setText("");
isExpenseButton.setSelected(true);
}
});
addItemCard.add(previous, BorderLayout.SOUTH);
}
}

Vaadin Drag Drop Component

We are creating a web application using Vaadin. Our application contains alot of drag and drop features.
We have an object which is drag-able.
We can click on it to open its menu as well.
Sometimes that when we click that item it behaves as if it is dragged.
When this happens we are unable to open its menu because the component is in dragmode.
All components with the same functionality behave the same however in development environment, when we restart the tomcat the problem disappeared?
I noticed that when the components start showing me this behavior the webpage in FireFox the behavior is fine there?
A simple solution to this could be to introduce a drag mode/edit button which would allow the user to switch the drag mode on and off.
This would mean the user could interact with the components and then enter this "drag mode" when they wished to drag them. Hence reducing the frustration of trying to interact with the component and it starting to "drag" instead.
I've create a simple example program to try out below.
public class DemoUI extends UI {
HorizontalSplitPanel splitPanel;
DragAndDropWrapper wrapperA;
DragAndDropWrapper wrapperB;
DragAndDropWrapper splitPaneWrapper;
Button buttonA;
Button buttonB;
private boolean isDragMode = false;
#WebServlet(value = "/*", asyncSupported = true)
#VaadinServletConfiguration(productionMode = false, ui = DemoUI.class)
public static class Servlet extends VaadinServlet {
}
#Override
protected void init(VaadinRequest request) {
final HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
Button buttonA = new Button("Button A");
Button buttonB = new Button("Button B");
final DragAndDropWrapper wrapperA = new DragAndDropWrapper(buttonA);
final DragAndDropWrapper wrapperB = new DragAndDropWrapper(buttonB);
final VerticalLayout leftPanel = new VerticalLayout();
final VerticalLayout rightPanel = new VerticalLayout();
DragAndDropWrapper leftPanelWrapper = new DragAndDropWrapper(leftPanel);
DragAndDropWrapper rightPanelWrapper = new DragAndDropWrapper(rightPanel);
buttonA.addClickListener(new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
Notification.show("Button A was clicked");
}
});
buttonB.addClickListener(new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
Notification.show("Button B was clicked");
}
});
leftPanelWrapper.setDropHandler(new DropHandler() {
#Override
public void drop(DragAndDropEvent event) {
leftPanel.addComponent(event.getTransferable().getSourceComponent());
}
#Override
public AcceptCriterion getAcceptCriterion() {
return AcceptAll.get();
}
});
rightPanelWrapper.setDropHandler(new DropHandler() {
#Override
public void drop(DragAndDropEvent event) {
rightPanel.addComponent(event.getTransferable().getSourceComponent());
}
#Override
public AcceptCriterion getAcceptCriterion() {
return AcceptAll.get();
}
});
final Button dragMode = new Button("Drag Mode On");
dragMode.addClickListener(new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
isDragMode = !isDragMode;
if (isDragMode) {
dragMode.setCaption("Drag Mode Off");
wrapperA.setDragStartMode(DragStartMode.WRAPPER);
wrapperB.setDragStartMode(DragStartMode.WRAPPER);
} else {
dragMode.setCaption("Drag Mode On");
wrapperA.setDragStartMode(DragStartMode.NONE);
wrapperB.setDragStartMode(DragStartMode.NONE);
}
}
});
leftPanel.setSizeFull();
rightPanel.setSizeFull();
leftPanelWrapper.setSizeFull();
rightPanelWrapper.setSizeFull();
leftPanel.addComponent(wrapperA);
rightPanel.addComponent(wrapperB);
splitPanel.setFirstComponent(leftPanelWrapper);
splitPanel.setSecondComponent(rightPanelWrapper);
splitPanel.setSizeFull();
VerticalLayout layout = new VerticalLayout();
layout.addComponent(dragMode);
layout.addComponent(splitPanel);
layout.setSizeFull();
this.setContent(layout);
this.setSizeFull();
}
.
All the best.

How can i execute a method from tabpane when i click it in javafx

I'm trying execute a method when i change to a specific tab.
If is not selected, when i choose it i need to lauch it.
this is what i am actually doing:
class Graph{
#FXML private TabPane Graphics;
public void llenarResponsables() throws SQLException{
IDProceso=cbProceso.getSelectionModel().getSelectedItem().getIdProceso();
IDIndicador = cbIndicador.getSelectionModel().getSelectedItem().getIdIndicador();
List<Responsables> responsables = new ArrayList<Responsables>();
cdFormatoAnalisis oFormatoAnalisis = new cdFormatoAnalisis();
responsables = oFormatoAnalisis.listarResponsables();
ObservableList<Responsables> tvLlenar = FXCollections.observableArrayList(responsables);
tcNombre.setCellValueFactory(new PropertyValueFac`tory<Responsables,String>("Usuario"));
tcNombre.setResizable(false);
tcPuesto.setCellValueFactory(new PropertyValueFactory<Responsables,String>("Categoria"));
tcPuesto.setResizable(false);
tvResponsables.setItems(tvLlenar);
Graphics.setOnMouseClicked();
}
Well I am not sure what you are trying to do in the method mentioned about, but if you want to fire events on change of tabs, you can use the ChangeListener on your TabPane
A working example
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.stage.Stage;
public class FireEventsOnTabChange extends Application {
#Override
public void start(Stage stage) throws Exception {
TabPane tabPane = new TabPane();
Tab tab1 = new Tab("Tab1");
Tab tab2 = new Tab("Tab2");
Tab tab3 = new Tab("Tab3");
tabPane.getTabs().addAll(tab1, tab2, tab3);
Scene scene = new Scene(tabPane, 200, 200);
stage.setScene(scene);
stage.show();
tabPane.getSelectionModel().selectedItemProperty()
.addListener(new ChangeListener<Tab>() {
#Override
public void changed(ObservableValue<? extends Tab> old,
Tab oldTab, Tab newTab) {
//Check for Tab and call you method here
System.out.println("You have selected "
+ newTab.getText());
}
});
}
public static void main(String[] args) {
launch(args);
}
}

Command Pattern Usefulness when using JComponents

So, I'm developing a program using the Swing library and I obviously have buttons and menu items. Some of these are supposed to do the same stuff, and I thought using the Command Pattern should be the way to do it, e.g. I have a "save" button and a "save" menu item and they have to implement the same saving algorithm.
Command Pattern seems to be ok but I can't get who's the receiver in all that. Isn't a comand supposed to work on an object which implements some sort of "receiver interface", so that you can use different commands on different receivers coupling them aribtrarily? It looks like there's no "receiver" in my implementation of the pattern.
Another doubt i have is should a command be implemented as a singleton, since you could potentially call its functions from differents parts of the same project, and it would be handly to instantiate it only once and make it statically invokable?
Thank you.
" I obviously have buttons and menu items. Some of these are supposed to do the same stuff,"
As #nIcEcOw noted, that's what Actions are for. This Answer Shows exactly this.
As stated in the How to use Actions :
An Action can be used to separate functionality and state from a component. For example, if you have two or more components that perform the same function, consider using an Action object to implement the function. An Action object is an action listener that provides not only action-event handling, but also centralized handling of the state of action-event-firing components such as tool bar buttons, menu items, common buttons, and text fields. The state that an action can handle includes text, icon, mnemonic, enabled, and selected status.
An There only three Actions. Ont to open, save, and new. Each Action has an ActionCommand, and icon, and and action to perform. Both the JMenuItem and JToolBar button share the same Action and do the same thing. Here is the code you can run.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
public class ActionTest {
public ActionTest() {
ImageIcon openIcon = new ImageIcon(
ActionTest.class.getResource("/resources/image/open.gif"));
ImageIcon saveIcon = new ImageIcon(
ActionTest.class.getResource("/resources/image/save.gif"));
ImageIcon newIcon = new ImageIcon(
ActionTest.class.getResource("/resources/image/new.gif"));
Action openAction = new AbstractAction("Open", openIcon) {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Open File");
}
};
Action saveAction = new AbstractAction("Save", saveIcon) {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Save File");
}
};
Action newAction = new AbstractAction("New", newIcon) {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("New File");
}
};
JMenuItem openMenuItem = new JMenuItem(openAction);
JMenuItem saveMenuItem = new JMenuItem(saveAction);
JMenuItem newMenuItem = new JMenuItem(newAction);
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
fileMenu.add(openMenuItem);
fileMenu.add(saveMenuItem);
fileMenu.add(newMenuItem);
menuBar.add(fileMenu);
JToolBar toolBar = new JToolBar();
toolBar.add(Box.createHorizontalGlue());
toolBar.setBorder(new LineBorder(Color.LIGHT_GRAY, 1));
toolBar.add(newAction);
toolBar.add(openAction);
toolBar.add(saveAction);
JFrame frame = new JFrame("Toolbar and Menu Test");
frame.setJMenuBar(menuBar);
frame.add(toolBar, BorderLayout.PAGE_START);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ActionTest();
}
});
}
}
As stated in the quote from the above mentioned tutorial, you can do more than just add an image and an action command to the Action. You can use it to set mnemonics and accelorators. Here is a custom Action class that takes
An action command String
an icon
a description for tooltips
a mnemonic
and a key accelorator.
private class MyAction extends AbstractAction {
String name;
public MyAction(String name, Icon icon) {
super(name, icon);
this.name = name;
}
public MyAction(String name, Icon icon, String desc,
Integer mnemonic, KeyStroke accelorator) {
super(name, icon);
putValue(Action.SHORT_DESCRIPTION, desc);
putValue(Action.MNEMONIC_KEY, mnemonic);
putValue(Action.ACCELERATOR_KEY, accelorator);
this.name = name;
}
#Override
public void actionPerformed(ActionEvent e) {
switch (name) {
case "Open":
System.out.println("Open");
break;
case "New":
System.out.println("New");
break;
case "Save":
System.out.println("Save");
break;
}
}
}
Here's an instantiation of this Action
Action newAction = new MyAction("New", newIcon,
"Creates a new file",
new Integer(KeyEvent.VK_N),
KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
And here's the new result. You will see the actionCommand in the menu, along with the key mnemonics and accelerators, tooltips, and you will see the jtoolbar buttons share the same traits. You will also see in the final code, that never once once a component created. All you do is add the Action to the JToolBar and the JMenu and let them work their magic.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ActionInterfaceDemo extends JFrame {
public ActionInterfaceDemo() {
ImageIcon openIcon = new ImageIcon(ActionInterfaceDemo.class.getResource("/resources/image/open.gif"));
ImageIcon saveIcon = new ImageIcon(ActionInterfaceDemo.class.getResource("/resources/image/save.gif"));
ImageIcon newIcon = new ImageIcon(ActionInterfaceDemo.class.getResource("/resources/image/new.gif"));
Action openAction = new MyAction("Open", openIcon,
"Opens a file",
new Integer(KeyEvent.VK_O),
KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK));
Action saveAction = new MyAction("Save", saveIcon,
"Saves a file",
new Integer(KeyEvent.VK_S),
KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK));
Action newAction = new MyAction("New", newIcon,
"Creates a new file",
new Integer(KeyEvent.VK_N),
KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
setJMenuBar(menuBar);
menuBar.add(fileMenu);
fileMenu.add(newAction);
fileMenu.add(openAction);
fileMenu.add(saveAction);
JToolBar toolBar = new JToolBar("Alignment");
toolBar.setBorder(BorderFactory.createLineBorder(Color.BLUE));
toolBar.add(Box.createHorizontalGlue());
toolBar.add(newAction);
toolBar.add(openAction);
toolBar.add(saveAction);
add(toolBar, BorderLayout.PAGE_START);
add(new JScrollPane(new TextArea(10, 40)), BorderLayout.CENTER);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setTitle("Action Interface Demo");
pack();
setLocationByPlatform(true);
setVisible(true);
}
private class MyAction extends AbstractAction {
String name;
public MyAction(String name, Icon icon) {
super(name, icon);
this.name = name;
}
public MyAction(String name, Icon icon, String desc,
Integer mnemonic, KeyStroke accelorator) {
super(name, icon);
putValue(Action.SHORT_DESCRIPTION, desc);
putValue(Action.MNEMONIC_KEY, mnemonic);
putValue(Action.ACCELERATOR_KEY, accelorator);
this.name = name;
}
#Override
public void actionPerformed(ActionEvent e) {
switch (name) {
case "Open":
System.out.println("Open");
break;
case "New":
System.out.println("New");
break;
case "Save":
System.out.println("Save");
break;
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
new ActionInterfaceDemo();
}
});
}
}
UPDATE
The better explain the relationship of Action and Command Patterns
As noted in Command Pattern
The command pattern is a commonly used pattern which encapsulates a method call or action-like code into a single class. The advantages of being able to package a method (or methods) into a class become evident when you have multiple invokers for a single action (for example a button and a menu item may perform the same action).
In Swing and Borland Delphi programming, an Action is a command object. In addition to the ability to perform the desired command, an Action may have an associated icon, keyboard shortcut, tooltip text, and so on. A toolbar button or menu item component may be completely initialized using only the Action object.
So basically Swing uses the concept of the command pattern through the use of Actions
As for OP's question
"Command Pattern seems to be ok but I can't get who's the receiver in all that."
As for the receiver, the wiki uses a text editor as an example and defines the receiver as such
Receiver, Target Object: the object that is about to be copied, pasted, moved, etc. The receiver object owns the method that is called by the command's execute method. The receiver is typically also the target object. For example, if the receiver object is a cursor and the method is called moveUp, then one would expect that the cursor is the target of the moveUp action. On the other hand, if the code is defined by the command object itself, the target object will be a different object entirely.
The main more components of a Command Pattern are stated as follows
Four terms always associated with the command pattern are command, receiver, invoker and client.
Client, Source, Invoker: the button, toolbar button, or menu item clicked, the shortcut key pressed by the user.
So to put it all together:
The MenuItem (client) invokes it
Action (command object) which calls it actionPerformed which in turn
Invokes an method on the receiver.
The wiki article is good read with a Java example
When two or more components are mean to do exactly the same thingy, one should look at Action, which reduces the duplicate code.
Small example for further help :
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ActionExample {
private JFrame frame;
private JButton button;
private JMenuItem exitItem;
private Action commonActions;
private class CommonActions extends AbstractAction {
public CommonActions(String title, String desc) {
super(title);
putValue(SHORT_DESCRIPTION, desc);
}
#Override
public void actionPerformed(ActionEvent ae) {
JOptionPane.showMessageDialog(frame,
"Closing Frame", "Information", JOptionPane.INFORMATION_MESSAGE);
frame.dispose();
}
};
private void displayGUI() {
frame = new JFrame("Action Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
commonActions = new CommonActions("Exit", "To Exit the Application");
JPanel contentPane = new JPanel();
button = new JButton();
button.setAction(commonActions);
contentPane.add(button);
frame.setJMenuBar(getMenuBar());
frame.setContentPane(contentPane);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JMenuBar getMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
exitItem = new JMenuItem(commonActions);
fileMenu.add(exitItem);
menuBar.add(fileMenu);
return menuBar;
}
public static void main(String[] args) {
Runnable runnable = new Runnable() {
#Override
public void run() {
new ActionExample().displayGUI();
}
};
EventQueue.invokeLater(runnable);
}
}
ADDED an example with SINGLETON PATTERN (though I am not sure of this approach(about how good this approach is))
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ActionExample {
private JFrame frame;
private JButton button;
private JMenuItem exitItem;
private void displayGUI() {
frame = new JFrame("Action Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
CommonActions.setValues(frame);
JPanel contentPane = new JPanel();
button = new JButton();
button.setAction(CommonActions.getInstance());
contentPane.add(button);
frame.setJMenuBar(getMenuBar());
frame.setContentPane(contentPane);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JMenuBar getMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
exitItem = new JMenuItem(CommonActions.getInstance());
fileMenu.add(exitItem);
menuBar.add(fileMenu);
return menuBar;
}
public static void main(String[] args) {
Runnable runnable = new Runnable() {
#Override
public void run() {
new ActionExample().displayGUI();
}
};
EventQueue.invokeLater(runnable);
}
}
class CommonActions extends AbstractAction {
private static CommonActions commonActions = null;
private static JFrame frame = null;
static {
try {
commonActions = new CommonActions("Exit", "To Exit the Application");
} catch (Exception e) {
throw new RuntimeException("BINGO, an error");
}
}
private CommonActions(String title, String desc) {
super(title);
putValue(SHORT_DESCRIPTION, desc);
}
public static CommonActions getInstance() {
return commonActions;
}
public static void setValues(JFrame f) {
frame = f;
}
#Override
public void actionPerformed(ActionEvent ae) {
JOptionPane.showMessageDialog(frame,
"Closing Frame", "Information", JOptionPane.INFORMATION_MESSAGE);
frame.dispose();
}
}