can't edit text area in JPanel Java - java

I'm working on a gui program the allows you to open a JFrame with a txt file and allows you to copy and paste from a JMenu. But for some reason I cannot modify the text area after the txt file is loaded
heres my class with the methods, I have one that generates a JFrame I designed in my MyJFrame class and outputs a string in a scroll pane
the other method where I'm just trying to modify the that text area where the file is. I know this would be a lot easier if it was all in one class but my professor wants me to make this DisplayText method with the copy and paste methods.
public class DisplayText {
public JTextArea text = new JTextArea();
public void displayText(String title, String info)
{
MyJFrame f = new MyJFrame(title);
Container c = f.getContentPane();
c.add(text);
JScrollPane pane = new JScrollPane(text);
c.add(pane);
text.append(info);
f.setVisible(true);
}
public void selectText()
{
text.append("THIS IS A TEST");
}
public void insertText()
{
text.paste();
}
}
heres the code where I put the action listener for the copy menu item, its in the MyJFrame class
item8.addActionListener(new ActionListener() //copy menu item
{
public void actionPerformed(ActionEvent e)
{
dt.selectText();
}
});
the copy button in the menu does not work at all and I'm not sure why, could you guys help me out? Thanks!!

Related

Is there a better way to have JPanels communicate?

I'm working on a game which consists entirely of menus/screens which you interact with using buttons, essentially a text-based game but without the typing. To do this I have been working with Swing to create a basic UI, following this video series.
In order to have actions performed in one panel affect the state of another, I have followed the tutorial and created a UIListener interface, which extends EventListener. When a button is pressed in one JPanel, the fireUIEvent method of the panel is called, which in turn calls the sole method of the interface, uiEventOccurred, which is implemented in an anonymous class inside the main JFrame class. Then the JFrame can do whatever it wants with the event, and modify other JPanels it contains accordingly.
For example, I have a panel that represents the town. It has a button you click to travel to the dungeon. When this button is clicked, the town panel is replaced by the dungeon panel, which has a button you can click to return to the town.
I'll post the relevant classes:
the main frame class
public class GameMainFrame extends JFrame{
public JPanel rightPanel;
public GameMainFrame(String title) {
super(title);
/* Setting The Overall Layout */
setLayout(new BorderLayout());
/* Creating Individual UI Panels */
UIPanel uip_town = new Town_UI();
UIPanel uip_dung = new Dungeon_UI();
/* Creating A Nested Panel */
rightPanel = new JPanel();
rightPanel.setLayout(new BorderLayout());
rightPanel.add(uip_text, BorderLayout.WEST);
rightPanel.add(uip_town, BorderLayout.NORTH);
/* Creating A Listener To React To Events From The UI Panels */
uip_town.addUIListener(new UIListener() {
public void uiEventOccurred(UIEvent event) {
System.out.println("event text: " + event.getText());
if (event.getText().equals("goto_dungeon")) {
rightPanel.remove(uip_town);
rightPanel.add(uip_dung, BorderLayout.NORTH);
validate();
} else if (event.getText().equals("goto_town")) {
rightPanel.remove(uip_dung);
rightPanel.add(uip_town, BorderLayout.NORTH);
validate();
}
}
});
/* Adding Panels To The Content Pane Of The Frame */
Container c = getContentPane();
c.add(uip_info, BorderLayout.WEST);
c.add(rightPanel, BorderLayout.CENTER);
}
}
UIPanel class
public class UIPanel extends JPanel {
private EventListenerList listenerList;
public UIPanel() {
listenerList = new EventListenerList();
}
public void fireUIEvent(UIEvent event) {
Object[] listeners = listenerList.getListenerList();
for (int i = 0; i < listeners.length; i += 2) {
if (listeners[i] == UIListener.class) {
((UIListener) listeners[i + 1]).uiEventOccurred(event);
}
}
}
public void addUIListener(UIListener listener) {
listenerList.add(UIListener.class, listener);
}
public void removeUIListener(UIListener listener) {
listenerList.remove(UIListener.class, listener);
}
}
Town_UI class
public class Town_UI extends UIPanel {
public Town_UI() {
Dimension size = getPreferredSize();
size.height = 466;
setPreferredSize(size);
setBorder(BorderFactory.createTitledBorder("Town"));
JButton btn_dungeon_enter = new JButton("Enter the dungeon");
add(btn_dungeon_enter);
btn_dungeon_enter.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
fireUIEvent(new UIEvent(this, "goto_dungeon"));
}
});
}
}
Dungeon_UI class
public class Dungeon_UI extends UIPanel {
public Dungeon_UI() {
Dimension size = getPreferredSize();
size.height = 466;
setPreferredSize(size);
setBorder(BorderFactory.createTitledBorder("Dungeon"));
JButton btn_town_return = new JButton("Return to town");
add(btn_town_return);
btn_town_return.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
fireUIEvent(new UIEvent(this, "goto_town"));
}
});
}
}
UIEvent object
public class UIEvent extends EventObject {
private String text;
public UIEvent(Object source, String text) {
super(source);
this.text = text;
}
public String getText() {
return text;
}
}
UIListener interface
public interface UIListener extends EventListener {
public void uiEventOccurred(UIEvent e);
}
I have two problems with this. Number one, it is currently bugged. When I run the application, the Town_UI panel appears with its button "Enter the dungeon", and upon clicking on it the frame correctly updates, replacing the Town_UI panel with the Dungeon_UI panel. However, clicking on the "Return to town" button on the dungeon panel doesn't do anything. When trying to debug this I found that the cause was in the UIPanel method fireUIEvent. When the "Return to town" button is pressed, this method is correctly called, but when it creates the Object array using getListenerList, the array is empty, so it skips the for-loop and no action is performed. Because I am unfamiliar with ActionEvents and EventListeners in Java, though, I am not sure how to fix this.
My other problem is that all of the behaviors that occur when a button is pressed are handled in the anonymous class in the GameMainFrame class. This means that if I want to have a bunch of different buttons all do different things (which I do plan on implementing), I'll need a whole bunch of conditionals in the anonymous class to determine what button is pressed and perform the corresponding behavior. I feel like it would be a lot cleaner if the button behavior could be implemented in the anonymous ActionListener class of each button, but I'm not sure how I should restructure my code so that I can do this but also allow any action on any panel able modify any other panel.
In the tutorial I watched he claimed that this implementation of EventListeners was what you should do in order to avoid making your code a rat's nest, but it seems like that is where my code is heading if I use this implementation for what I am trying to do.

Running a void method from a different class using actionlistener for my menu combobox.

I am creating a little combobox menu for my ap comp sci project. This is my first time working with GUI so I am still familiarizing myself with it. I have a menu class and a few other classes that run mostly void methods to do simple things like calculate GPA, etc. My menu is working fine (at least I think so). However, when I select my first item in the menu (I only have one actionlistener at the moment but I will add more after) the actionlistener runs the void method (run()) and it opens the window. But after that nothing happens. I have user input in that method to type in grade percentages, etc, and it works when i run the method directly from the class. But I can't type or seem to have any input when the actionlistener runs it. Thank you for your time and help!
package First_Semester_Project;
import java.awt.Color;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Menu extends GradeCalculator
{
String [] info = {"Grade Calculator", "Spanish Conjugator", "Period Table of Elements", "Sentence Compiler"};
JLabel l= new JLabel("Welcome to the Resource Library!");
JComboBox c = new JComboBox(info);
JButton b = new JButton ("Select Resource");
public Menu ()
{
frame();
}
public void frame ()
{
JFrame f = new JFrame ("Resource Library");
l.setForeground(Color.white);
l.setFont(new Font("Arial", Font.BOLD, 16));
f.setSize(600,90);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel p = new JPanel();
p.setBackground(Color.gray);
p.add(c);
p.add(b);
p.add(l);
f.add(p);
b.addActionListener(new ActionListener()
{
public void actionPerformed (ActionEvent e ){
String s = c.getSelectedItem().toString();
//l.setText(s);
if (s.equals("Grade Calculator"))
{
run();
}
else if (s.equals("travel time"))
{
l.setText(s);
}
}
});
}
Im going to assume that the run method can be called from the class as its superclass GradeCalculator has the run() method.
A common thing that i like to do is create my own actionlistener class, so that i can easily follow my code and see who has access to what, this would be my solution.
b.addActionListener(new myCustomActionListener(this)); //replaces b.actionlistener
now i would add myCustomActionListener either to the end of the same file, or in a different class file alltogether - depends on your abstraction level really.
here is the class for reference:
public class myCustomActionListener implements ActionListener
{
Menu menu = null;
public myCustomActionListener(Menu menu)
{
this.menu = menu;
}
#Override
public void actionPerformed(ActionEvent arg0) {
String s = menu.c.getSelectedItem().toString();
//l.setText(s);
if (s.equals("Grade Calculator"))
{
menu.run();
}
else if (s.equals("travel time"))
{
menu.l.setText(s);
}
}
}

getContentPane and linking GUIs together

I'm having trouble with getContentPane() in my GUI.
public class CryptoMainMenu extends JPanel implements ActionListener {
public CryptoMainMenu()
{
Templates template = new Templates();
//setting up the primary panel
primaryPanel = new JPanel();
primaryPanel.setLayout(new BorderLayout());
//setting up algorithm button
algorithm = new JButton("Algorithm");
algorithm.addActionListener(this);
add(primaryPanel);
setSize(730, 400);
}
}
public class CryptoCategoriesMenu extends JFrame implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
if (event.getSource() == back)
{
CryptoMainMenu main = new CryptoMainMenu();
main.setVisible(true);
this.setVisible(false);
}
}
}
In CryptoMainMenu if I'm extending JPanel I can't use getContentPane().add(primaryPanel), but if I just have add(primaryPanel), then my program isn't working because I linked all of my GUI classes together, so that when it gets to CryptoCategoriesMenu, and if I try pressing the JButton back, CryptoMainMenu shows as blank window. Is there something similar to getContentPane() that I can use with JPanel?
Edit:
This is suppose to a menu type GUI. In CryptoMainMenu, it displays a GUI where the user can press a button and it leads to another GUI which is CryptoCategoriesMenu. In CryptoCategoriesMenu, it shows another set of buttons and one of them is back. When I just have add() and I press back, CryptoMainMenu doesn't show up and that's the problem I'm having.

Passing values between JFrames

I have two Jframes where frame1 has some text fields and when a button on frame1 is clicked, I open another JFrame which contains a search box and a JTable containing search results.
When I click on a result row on JTable, I want that particular values to be reflected in the frame1 text fields.
I tried passing the JFrame1's object as a parameter but I have no clear idea on how to achieve this.
Any help would be highly appreciated.
Thanks
First of all, your program design seems a bit off, as if you are using a JFrame for one of your windows where you should in fact be using a JDialog since it sounds as if one window should be dependent upon the other.
But regardless, you pass references of GUI objects the same as you would standard non-GUI Java code. If one window opens the other (the second often being the dialog), then the first window usually already holds a reference to the second window and can call methods off of it. The key often is when to have the first window call the second's methods to get its state. If the second is a modal dialog, then the when is easy -- immediately after the dialog returns which will be in the code immediately after you set the second dialog visible. If it is not a modal dialog, then you probably want to use a listener of some sort to know when to extract the information.
Having said this, the details will all depend on your program structure, and you'll need to tell us more about this if you want more specific help.
For a simple example that has one window open another, allows the user to enter text into the dialog windows JTextField, and then places the text in the first window's JTextField, please have a look at this:
import java.awt.Window;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class WindowCommunication {
private static void createAndShowUI() {
JFrame frame = new JFrame("WindowCommunication");
frame.getContentPane().add(new MyFramePanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
// let's be sure to start Swing on the Swing event thread
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
class MyFramePanel extends JPanel {
private JTextField field = new JTextField(10);
private JButton openDialogeBtn = new JButton("Open Dialog");
// here my main gui has a reference to the JDialog and to the
// MyDialogPanel which is displayed in the JDialog
private MyDialogPanel dialogPanel = new MyDialogPanel();
private JDialog dialog;
public MyFramePanel() {
openDialogeBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
openTableAction();
}
});
field.setEditable(false);
field.setFocusable(false);
add(field);
add(openDialogeBtn);
}
private void openTableAction() {
// lazy creation of the JDialog
if (dialog == null) {
Window win = SwingUtilities.getWindowAncestor(this);
if (win != null) {
dialog = new JDialog(win, "My Dialog",
ModalityType.APPLICATION_MODAL);
dialog.getContentPane().add(dialogPanel);
dialog.pack();
dialog.setLocationRelativeTo(null);
}
}
dialog.setVisible(true); // here the modal dialog takes over
// this line starts *after* the modal dialog has been disposed
// **** here's the key where I get the String from JTextField in the GUI held
// by the JDialog and put it into this GUI's JTextField.
field.setText(dialogPanel.getFieldText());
}
}
class MyDialogPanel extends JPanel {
private JTextField field = new JTextField(10);
private JButton okButton = new JButton("OK");
public MyDialogPanel() {
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
okButtonAction();
}
});
add(field);
add(okButton);
}
// to allow outside classes to get the text held by the JTextField
public String getFieldText() {
return field.getText();
}
// This button's action is simply to dispose of the JDialog.
private void okButtonAction() {
// win is here the JDialog that holds this JPanel, but it could be a JFrame or
// any other top-level container that is holding this JPanel
Window win = SwingUtilities.getWindowAncestor(this);
if (win != null) {
win.dispose();
}
}
}
You'd do a very similar technique to get information out of a JTable.
And again, if this information doesn't help you, then please tell us more about your program including showing us some of your code. The best code to show is a small compilable example, an SSCCE similar to what I've posted above.

Using JTextField with Model (on focusLost) and run Actions with model data

I have a Java Swing application, that has many JTextFields and a datamodel.
When leaving the textfields (focus lost), the text is written to the Model. So far, so good.
There are JMenu-Actions that read data from the model and send it to the server.
The Problem is, that the "focus lost" is not fired when running the Menu Action by it's accelerator. So the Actions reads and transmit the old value from the datamodel. .
There may be many ways to fix this...? Do you have suggestions how to solve this?
What doesn't work for me:
Write to model on every key press (via Document Listener): not usable, should only write on leaving textfield (focus lost). The text is (expensively) evaluated after writing it to the model - can not run after every key press.
Handling the writing to model in every action. There ca. 500 Textfields and ca. 100 actions. Diffucult to match without forgetting anything.
Runnable Demo Code:
package swingmodel;
import java.awt.FlowLayout;
import java.awt.event.*;
import javax.swing.*;
/**
* Simple Demo Problem. Enter a Text in the first Textfield and press ALT-T. The
* focus listener did no run, therefore the old value from model is displayed.
*/
public class TextDemoOnMenu extends JPanel {
private Model model;
public TextDemoOnMenu() {
super(new FlowLayout());
model = new Model();
MyTextField textField = new MyTextField(20, model);
add(textField);
add(new JTextField(5));
}
class MyTextField extends JTextField {
private Model model;
public MyTextField(int arg, Model model) {
super(arg);
this.model = model;
addFocusListener(new FocusAdapter() {
#Override
public void focusLost(FocusEvent e) {
System.out.println("focus lost");
writeToModel();
}
});
}
public void writeToModel() {
this.model.setText(getText());
}
}
class ShowModelTextAction extends AbstractAction {
public ShowModelTextAction(String string) {
super(string);
}
#Override
public void actionPerformed(ActionEvent e) {
String message = "text is: " + model.getText();
JOptionPane.showMessageDialog(TextDemoOnMenu.this, message);
}
}
class Model {
private String text;
void setText(String t) {
text = t;
}
public String getText() {
return text;
}
}
private void createAndShowGUI() {
// Create and set up the window.
JFrame frame = new JFrame("TextDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Add contents to the window.
frame.add(this);
Action action = new ShowModelTextAction("show text");
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("My Menu");
menuBar.add(menu);
JMenuItem menuItem = new JMenuItem("show text");
menuItem.setAction(action);
menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, ActionEvent.ALT_MASK));
menu.add(menuItem);
frame.setJMenuBar(menuBar);
// Display the window.
frame.setSize(400, 200);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TextDemoOnMenu().createAndShowGUI();
}
});
}
}
You could modify all your Actions to use the KeyboardFocusManager. You would get the current component that has focus. If it is one of your custom text fields, then you can force an update to the model before processing the Action.
The text is (expensively) evaluated after writing it to the model
Also, it sounds like you should be handling focusGained as well. Then can save the original text and do a comparison on the text on focus lost before automatically updating the model. (ie. what if somebody just tabs through all the text fields?).
Create a dirty flag for each textField. If the save event occurs you can update the model with any textField that is dirty.
If you are worried that you will 'forget' to do this for a particular textField it is your management of the textfields that is the problem. You should have a factory method for them that sets them up correctly and creates dirty flags for them.
My other suggestion is to when that save action occurs you request focus to a hidden component to trigger the focusLost on the textField. When the save is completed you give the focus back to the previous owner.

Categories

Resources