Using a JFileChooser with Swing GUI classes and listeners
Asked Answered
C

4

4

This is my current menu :

public class DrawPolygons
{
    public static void main (String[] args) throws FileNotFoundException
    {

        /**
         *   Menu - file reader option
         */

        JMenuBar menuBar;
        JMenu menu;
        JMenuItem menuItem;

        //  Create the menu bar.
        menuBar = new JMenuBar();

        //  Build the first menu.
        menu = new JMenu("File");
        menu.setMnemonic(KeyEvent.VK_F);
        menu.getAccessibleContext().setAccessibleDescription("I have items");
        menuBar.add(menu);

        //  a group of JMenuItems
        menuItem = new JMenuItem("Load",KeyEvent.VK_T);
        menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, ActionEvent.ALT_MASK));
        menuItem.getAccessibleContext().setAccessibleDescription("Load your old polygons");
        menu.add(menuItem);

        menuItem = new JMenuItem("Save",KeyEvent.VK_U);
        menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_2, ActionEvent.ALT_MASK));
        menuItem.getAccessibleContext().setAccessibleDescription("Save the contents of your polygons");
        menu.add(menuItem);

        // attaching the menu to the frame
        JFrame frame = new JFrame("Draw polygons");
        frame.setJMenuBar(menuBar);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(new DrawingPanel());
        frame.pack();
        frame.setVisible(true);

    }

}

It has two options for Load and Save. enter image description here

Now , how can I attach the JFileChooser to the actionPerformed method , here :

/**
 * Main class
 * @author X2
 *
 */
class DrawingPanel extends JPanel implements MouseListener, MouseMotionListener ,KeyListener
{



    // code
    // code
    // and more code


    static DrawingPanel app ;  
    private static final Dimension MIN_DIM = new Dimension(300, 300);
    private static final Dimension PREF_DIM = new Dimension(500, 500);

    public Dimension getMinimumSize() { return MIN_DIM; }
    public Dimension getPreferredSize() { return PREF_DIM; }

    JMenuItem open, save;  
    JTextArea textArea ;  
    JFileChooser chooser ;  
    FileInputStream fis ;  
    BufferedReader br ;  
    FileOutputStream fos ;  
    BufferedWriter bwriter ;  

    public void actionPerformed( ActionEvent event )    
    {  
        Object obj = event.getSource() ;  
        chooser = new JFileChooser() ;  
        if ( chooser.showOpenDialog( app ) ==  JFileChooser.APPROVE_OPTION )  
        if ( obj == open )   
        {  
            try  
            {  
                fis = new FileInputStream(   
                      chooser.getSelectedFile() ) ;  
                br  = new BufferedReader(   
                      new InputStreamReader( fis ) ) ;  
                String read ;  
                StringBuffer text = new StringBuffer() ;  
                while( ( read = br.readLine() ) != null )   
                {  
                   text.append( read ).append( "\n" ) ;  
                }  
                textArea.setText( text.toString() ) ;  
            }  
            catch( IOException e )   
            {  
                JOptionPane.showMessageDialog( this , "Error in File Operation" 
                        ,"Error in File Operation" ,JOptionPane.INFORMATION_MESSAGE) ;  
            }  
        }  
    }  



    /**
     *  The constructor
     */
    DrawingPanel()
    {
        super();
        addMouseListener(this);
        addMouseMotionListener(this);
        addKeyListener(this);
        setFocusable(true);
        requestFocusInWindow();
    }


    // a lot of code more
    // and more 
    // and more

}

With the initial code of the menu and the Jpanel , that I created in main ?

Regards

------------------------

EDIT:

The "new" code :

public class DrawPolygons
{
    public static void main (String[] args) throws FileNotFoundException
    {
        // attaching the menu to the frame
        JFrame frame = new JFrame("Draw polygons");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


        // JMenuBar

        //  Create the menu and JmenuBar
        JMenuBar menuBar = new JMenuBar();

        //  Build the first menu.
        JMenu menu = new JMenu("File");
        menu.setMnemonic(KeyEvent.VK_F);
        menu.getAccessibleContext().setAccessibleDescription("I have items");
        menuBar.add(menu);

        // menu option - load 

        // create the load option
        final JMenuItem loadItem = new JMenuItem("Load",KeyEvent.VK_T);
        // add the shortcut 
        loadItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, ActionEvent.ALT_MASK));
        // short description 
        loadItem.getAccessibleContext().setAccessibleDescription("Load your old polygons");

        // JFileChooser with filter
        JFileChooser fileChooser = new JFileChooser(".");
        // apply the filter to file chooser
        FileNameExtensionFilter filter = new FileNameExtensionFilter("scn files (*.scn)", "scn");
        fileChooser.setFileFilter(filter);

        fileChooser.setControlButtonsAreShown(false);
        frame.add(fileChooser, BorderLayout.CENTER);

        final JLabel directoryLabel = new JLabel(" ");
        directoryLabel.setFont(new Font("Serif", Font.BOLD | Font.ITALIC, 36));

        final JLabel filenameLabel = new JLabel(" ");
        filenameLabel.setFont(new Font("Serif", Font.BOLD | Font.ITALIC, 36));


        // add listener to LOAD
        loadItem.addActionListener(
                new ActionListener() 
                {
                    public void actionPerformed(ActionEvent actionEvent) 
                    {
                          JFileChooser theFileChooser = new JFileChooser(); 
                          String command = actionEvent.getActionCommand();
                          if (command.equals(JFileChooser.APPROVE_SELECTION)) {
                            File selectedFile = theFileChooser.getSelectedFile();
                            directoryLabel.setText(selectedFile.getParent());
                            filenameLabel.setText(selectedFile.getName());
                          } else if (command.equals(JFileChooser.CANCEL_SELECTION)) {
                            directoryLabel.setText(" ");
                            filenameLabel.setText(" ");
                          }
                    }} // end listener
        ); // end listener to loadItem              

        menu.add(loadItem);

        // now SAVE 

        // create the option for save
        JMenuItem saveItem = new JMenuItem("Save",KeyEvent.VK_U);
        // key shortcut for save
        saveItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_2, ActionEvent.ALT_MASK));
        saveItem.getAccessibleContext().setAccessibleDescription("Save the contents of your polygons");
        // add the save to the menu 
        menu.add(saveItem);


        frame.setJMenuBar(menuBar);
        frame.setContentPane(new DrawingPanel());

        frame.pack();
        frame.setVisible(true);
    }

}

The problem is , now , when I hit Load Under File , nothing is happening . Why ?

I added the listener , but nothing .

Cyrilla answered 31/3, 2013 at 10:47 Comment(1)
Why are you adding ActionListener to JPanel instead of menuItem(load)?Adamek
C
18

As a general rule, you should not have your GUI classes, such as the class extending JPanel, implement any listener interfaces, and in fact you should strive for just the opposite -- to separate the control functions of the program (the listeners and the like) from the view functions of the program (the GUI). So my answer to your question of "how can I attach the JFileChooser to the actionPerformed method... [to my DrawingPanel class which extends JPanel], is to strive to not do this.

Instead have your view classes implement interfaces that would allow the control classes to more easily interact with them.

Edit 1: your new code you never display the JFileChooser dialog. You need to display the open dialog:

// first make sure that you've declared the JFrame frame as final
int result = theFileChooser.showOpenDialog(frame); 
if (result == JFileChooser.APPROVE_OPTION) {
  // ... code goes here           
}

Edit 2

Swing Model-View-Control Example:

For example, here's an MVC or Model-View-Control implementation that shows the view, the model and the control. This is all very simplistic, and all it does at present is to open a text file and display it in a JTextField, that's it, and it tries to seperate out the control functions from the view.

MvcMain class

import javax.swing.SwingUtilities;

public class MvcMain {

   private static void createAndShowGui() {
      MvcView view = new ShowTextView("Show Text");
      MvcModel model = new ShowTextModel();
      ShowTextControl control = new ShowTextControl(view, model);

      control.showView(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

MvcModel interface

import java.beans.PropertyChangeListener;

public interface MvcModel {
   static final String TEXT = "text";
   static final String STATUS = "STATUS";

   String getText();

   String getStatus();

   void setText(String text);

   void setStatus(String text);

   void addPropertyChangeListener(PropertyChangeListener listener);

   void removePropertyChangeListener(PropertyChangeListener listener);
}

MvcView interface

import java.awt.Window;
import javax.swing.Action;

public interface MvcView {

   void setVisible(boolean visible);

   void setFileAction(Action fileAction);

   void setOpenFileAction(Action openFileAction);

   void setSaveToFileAction(Action saveToFileAction);

   void setExitAction(Action exitAction);

   void setStatusText(String string);

   String getTextAreaText();

   void setTextAreaText(String text);

   Window getTopWindow();

}

ShowTextView class

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.Window;
import javax.swing.*;

public class ShowTextView implements MvcView {
   private JFrame frame = new JFrame();
   private JMenuBar menuBar = new JMenuBar();
   private JMenu fileMenu = new JMenu();
   private StatusBar statusBar = new StatusBar();
   private ViewDisplayText displayText = new ViewDisplayText();

   public ShowTextView(String title) {
      menuBar.add(fileMenu);

      frame.setTitle(title);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(displayText.getMainComponent(), BorderLayout.CENTER);
      frame.getContentPane().add(statusBar.getComponent(), BorderLayout.PAGE_END);
      frame.setJMenuBar(menuBar);
   }

   @Override
   public void setVisible(boolean visible) {
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   @Override
   public void setOpenFileAction(Action action) {
      displayText.setOpenFileButtonAction(action);
      fileMenu.add(new JMenuItem(action));
   }

   @Override
   public void setSaveToFileAction(Action action) {
      displayText.setSaveToFileAction(action);
      fileMenu.add(new JMenuItem(action));
   }

   @Override
   public void setExitAction(Action exitAction) {
      displayText.setExitAction(exitAction);
      fileMenu.add(new JMenuItem(exitAction));
   } 

   @Override
   public void setFileAction(Action fileAction) {
      fileMenu.setAction(fileAction);
   }


   @Override
   public String getTextAreaText() {
      return displayText.getTextAreaText();
   }

   @Override
   public void setTextAreaText(String text) {
      displayText.setTextAreaText(text);
   }

   @Override
   public Window getTopWindow() {
      return frame;
   }

   @Override
   public void setStatusText(String text) {
      statusBar.setText(text);
   }

}

class ViewDisplayText {
   private static final int TA_ROWS = 30;
   private static final int TA_COLS = 50;
   private static final int GAP = 2;
   private JPanel mainPanel = new JPanel();
   private JButton openFileButton = new JButton();
   private JButton saveToFileButton = new JButton();
   private JButton exitButton = new JButton();
   private JTextArea textArea = new JTextArea(TA_ROWS, TA_COLS);

   public ViewDisplayText() {
      JPanel buttonPanel = new JPanel(new GridLayout(1, 0, GAP, 0));
      buttonPanel.add(openFileButton);
      buttonPanel.add(saveToFileButton);
      buttonPanel.add(exitButton);

      mainPanel.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
      mainPanel.setLayout(new BorderLayout());
      mainPanel.add(new JScrollPane(textArea), BorderLayout.CENTER);
      mainPanel.add(buttonPanel, BorderLayout.PAGE_END);
   }

   public void setExitAction(Action exitAction) {
      exitButton.setAction(exitAction);
   }

   public JComponent getMainComponent() {
      return mainPanel;
   }

   public void setOpenFileButtonAction(Action action) {
      openFileButton.setAction(action);
   }

   public void setSaveToFileAction(Action action) {
      saveToFileButton.setAction(action);
   }

   public String getTextAreaText() {
      return textArea.getText();
   }

   public void setTextAreaText(String text) {
      textArea.setText(text);
   }
}

class StatusBar {
   private static final String STATUS = "Status: ";
   private JLabel label = new JLabel(STATUS);

   public StatusBar() {
      label.setBorder(BorderFactory.createLineBorder(Color.black));
   }

   public JComponent getComponent() {
      return label;
   }

   public void setText(String text) {
      label.setText(STATUS + text);
   }
}

ShowTextModel class

import java.beans.PropertyChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;

public class ShowTextModel implements MvcModel {
   private String text;
   private String status;
   private SwingPropertyChangeSupport propChangeSupport = 
         new SwingPropertyChangeSupport(this);

   @Override
   public String getText() {
      return text;
   }

   @Override
   public void setText(String text) {
      String newValue = text;
      String oldValue = this.text;
      this.text = newValue;
      propChangeSupport.firePropertyChange(TEXT, oldValue, newValue);
   }

   @Override
   public void setStatus(String status) {
      String newValue = status;
      String oldValue = this.status;
      this.status = newValue;
      propChangeSupport.firePropertyChange(STATUS, oldValue, newValue);
   }

   @Override
   public void addPropertyChangeListener(PropertyChangeListener listener) {
      propChangeSupport.addPropertyChangeListener(listener);
   }

   @Override
   public void removePropertyChangeListener(PropertyChangeListener listener) {
      propChangeSupport.removePropertyChangeListener(listener);
   }

   @Override
   public String getStatus() {
      return status;
   }

}

ShowTextControl class

import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.util.concurrent.ExecutionException;
import javax.swing.*;

public class ShowTextControl {

   private MvcView view;
   private MvcModel model;

   public ShowTextControl(MvcView view, MvcModel model) {
      this.view = view;
      this.model = model;

      view.setFileAction(new FileAction("File", KeyEvent.VK_F));
      view.setOpenFileAction(new OpenFileAction(view, model, "Open File",
            KeyEvent.VK_O));
      view.setSaveToFileAction(new SaveToFileAction(view, model,
            "Save to File", KeyEvent.VK_S));
      view.setExitAction(new ExitAction(view, model, "Exit", KeyEvent.VK_X));

      model.addPropertyChangeListener(new ModelListener(view, model));
   }

   public void showView(boolean visible) {
      view.setVisible(visible);
   }
}

@SuppressWarnings("serial")
class OpenFileAction extends AbstractAction {
   private MvcView view;
   private MvcModel model;

   public OpenFileAction(MvcView view, MvcModel model, String name, int keyCode) {
      super(name);
      putValue(MNEMONIC_KEY, keyCode);
      this.view = view;
      this.model = model;
   }

   @Override
   public void actionPerformed(ActionEvent evt) {
      JFileChooser fileChooser = new JFileChooser();
      fileChooser.setMultiSelectionEnabled(false);
      int result = fileChooser.showOpenDialog(view.getTopWindow());
      if (result == JFileChooser.APPROVE_OPTION) {
         File file = fileChooser.getSelectedFile();
         if (file.exists()) {
            if (file.getName().endsWith(".txt")) {
               model.setStatus("Opening file \"" + file.getName() + "\"");

               OpenFileWorker openFileWorker = new OpenFileWorker(file);
               openFileWorker.addPropertyChangeListener(
                     new OpenFileWorkerListener(model));
               openFileWorker.execute();
            } else {
               model.setStatus("File \"" + file.getName()
                     + "\" is not a text file");
            }
         } else {
            model.setStatus("File \"" + file.getName() + "\" does not exist");
         }
      }
   }

}

class OpenFileWorker extends SwingWorker<String, Void> {
   private File file;

   public OpenFileWorker(File file) {
      this.file = file;
   }

   public File getFile() {
      return file;
   }

   @Override
   protected String doInBackground() throws Exception {
      StringBuilder stringBuilder = new StringBuilder();
      Scanner scan = null;
      try {
         scan = new Scanner(file);
         while (scan.hasNextLine()) {
            stringBuilder.append(scan.nextLine() + "\n");
         }

      } catch (FileNotFoundException e) {
         e.printStackTrace();
      } finally {
         if (scan != null) {
            scan.close();
         }
      }
      return stringBuilder.toString();
   }
}

class OpenFileWorkerListener implements PropertyChangeListener {
   private MvcModel model;

   public OpenFileWorkerListener(MvcModel model) {
      this.model = model;
   }

   @Override
   public void propertyChange(PropertyChangeEvent evt) {
      if (SwingWorker.StateValue.DONE == evt.getNewValue()) {
         OpenFileWorker openFileWorker = (OpenFileWorker) evt.getSource();
         try {
            String text = openFileWorker.get();
            model.setText(text);
            model.setStatus("File \"" + openFileWorker.getFile().getName() + "\" opened");
         } catch (InterruptedException e) {
            e.printStackTrace();
         } catch (ExecutionException e) {
            e.printStackTrace();
         }
      }
   }
}

@SuppressWarnings("serial")
class FileAction extends AbstractAction {
   public FileAction(String name, int keyCode) {
      super(name);
      putValue(MNEMONIC_KEY, keyCode);
   }

   @Override
   public void actionPerformed(ActionEvent arg0) {
      // pretty much empty
   }
}

@SuppressWarnings("serial")
class SaveToFileAction extends AbstractAction {
   private MvcView view;
   private MvcModel model;

   public SaveToFileAction(MvcView view, MvcModel model, String name,
         int keyCode) {
      super(name);
      putValue(MNEMONIC_KEY, keyCode);
      this.view = view;
      this.model = model;
   }

   @Override
   public void actionPerformed(ActionEvent e) {
      // TODO finish!
   }
}

@SuppressWarnings("serial")
class ExitAction extends AbstractAction {
   private MvcView view;
   // private MvcModel model; // TODO: may use this later

   public ExitAction(MvcView view, MvcModel model, String name, int keyCode) {
      super(name);
      putValue(MNEMONIC_KEY, keyCode);
      this.view = view;
      // this.model = model; // TODO: may use this later
   }

   @Override
   public void actionPerformed(ActionEvent e) {
      view.getTopWindow().dispose();
   }
}

class ModelListener implements PropertyChangeListener {
   private MvcView view;
   private MvcModel model;

   public ModelListener(MvcView view, MvcModel model) {
      this.view = view;
      this.model = model;
   }

   @Override
   public void propertyChange(PropertyChangeEvent pcEvt) {
      if (MvcModel.TEXT.equals(pcEvt.getPropertyName())) {
         view.setTextAreaText(model.getText());
      }

      else if (MvcModel.STATUS.equals(pcEvt.getPropertyName())) {
         view.setStatusText(model.getStatus());
      }
   }

}

In this example, I've combined java classes in one file for brevity, but in the application, they'd be in their own files but would all share the same package. Note that while this may be "over-kill" for this simple application, I've used this sort of structure with several very large Swing applications with good success. The main benefit for me comes when I need to debug or enhance a program months after creation, since this separation of concerns and information and behavior hading makes it much easier for me make changes in one part of the program without offending or upsetting another part of the program.

Also, the reason for the interfaces is so that you can create new or different GUI's that look differently, but that can respond in the same way. I've also used them frequently to help create mock classes that allow me to better test my modules in isolation.

Crandell answered 31/3, 2013 at 12:15 Comment(0)
I
1

I would recommend using Action(s) to use there. On each action you have some specific actionlistener, set your panel as an listener if you like to do so:

Action Explained

Integral answered 31/3, 2013 at 11:7 Comment(1)
I added the action , like you suggested , but now nothing listens to the Load , even though I added the listener . Can you please see the edited post ?Cyrilla
V
1

The quick-fix for your problem is to provide a handler so that you can attach the ActionListener. You make the JMenus in main(), but you don't have visibility to them in your DrawingPanel. A cheap way to get this done would be to pass the JMenuItems into your DrawingPanel constructor. Modify the constructor to something like:

DrawingPanel(JMenuItem save, JMenuItem open) {
    // the stuff you already got
    this.save = save;
    this.open = open;
    save.addActionListener(this);
    open.addActionListener(this);
}

Then you'd pass them into your DrawingPanel in main:

JMenuItem loadMenuItem = new JMenuItem("Load");
JMenuItem saveMenuItem = new JMenuItem("Save");
...
frame.getContentPane(new DrawingPanel(saveMenuItem, loadMenuItem));

From a Java-style perspective, it's not clear whether DrawingPanel is the best place to handle save and load actions. As others have mentioned, creating (separate) Actions for each JMenuItem is often a better pattern. In your case, you might be able to provide other accessor or helper methods inside DrawingPanel that would give the saver/loader workers the info they'd need to do their Action.


Edit: (to address the OP's "new code" Edit)

I think the problem with the "new code" is that you're making a new JFileChooser in the actionPerformed method, and not using the existing one that you made earlier and added to the frame. If you make the first one final then you can use the same JFileChooser in the actionPerformed method.

Verrocchio answered 31/3, 2013 at 11:41 Comment(0)
T
1

To elaborate on what Kitesurfer said I would use an Action because most often I have toolbar buttons or other components performing the same actions as menu items. To avoid duplicate or unnecessary code somewhere in the class (or any where I'm able to access it from the current class) I create an Action field that I can reuse if need be or move if I decide to refactor my code. Here's an example.

JMenuItem miLoad = new JMenuItem(actionLoad);

Action actionLoad = new AbstractAction("Load") {
    public void actionPerformed(ActionEvent e) {
        //your code to load the file goes here like a normal ActionListener
    }
};

Definitely check out the API to see what parameters can be passed into the AbstractAction class, I used a String so the JMenuItem would display the string, you can also set the Icon, I don't remember all of the constructors so it would be worth taking a look. Oh and passing the JMenuItems into the constructor of the DrawingPanel class isn't necessarily a bad idea, but if it inherits from JPanel I don't believe you can add a menu bar to a JPanel so make sure it gets added to your JFrame too. Hope that helps.

Tavares answered 31/3, 2013 at 12:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.