Skip to main content
This revision made June 13, 2011 10:18, by magnum
« earlier revision revert to this « later revision

Overview

For this second example, we will reuse what we have done for the first simple example : The Application will be able to view :

  • Any image files that can be handled by the JRE, and get some properties about these files, and also save the opened files
  • Any text files that can be handled by the JRE, and save the opened files as plain text. We will also get some properties about the text files.

We will use two different Plugins : one to open the image files and another for the text files.

We will create only one File menu, with an Open sub-menu (we can imagine that there can be other Plugins that will be able to open other sorts of images), an Analyse sub-menu for images and text analysis, and an Exit item. We also want a menu to be able to close the opened images. This time, we will also create two tool bars : one managed by the core application, and another by one of the plugins.

Other ways to register Actions

As we said in the first tutorial, there are a lot of ways to resister Actions. In the first tutorial, we created a List and put the AbstractMDIActions in it :

    public void register(Application app) {
        super.register(app);        
        importMenuActions = new Vector(1);
        importMenuActions.add(new ImportImageAction("Image"));                
    }
    
    public Object getStaticMenuElements(String menu) {
        if (menu.equals(PluginElementTypes.OPEN_ACTIONS)) return importMenuActions;
        else return null;
    }    

Now we will return directly the AbstractMDIActions :

    public void register(MDIApplication app) {
        super.register(app);        
        importImageAction = new ImportImageAction("Image");                        
    }
    
    public Object getStaticMenuElements(String menu) {
        if (menu.equals(PluginElementTypes.OPEN_ACTIONS)) return importImageAction
        else return null;
    }        

More control on the Actions process

In the first tutorial, the time of the Action was counted before the effective opening of the File, and the time was calculated even if the FileChooser was closed without selecting a File. We can change this by using the three following methods :

  • MDIApplication.stopTime(MDIAction) : Get the time since the last start, and Stop showing the progress bar
  • MDIApplication.startTime(MDIAction) : Start the time counting, and Show the progress bar
  • MDIApplication.noWriteMessages() : Used if the FileChooser was closed without selecting a File, don't print a message in the message Area for the current MDIAction

        public void run() throws Exception {
            app.stopTime(this); // don't show the progress bar
            JFileChooser chooser = new JFileChooser("Open Image");
            chooser.setDialogTitle("Open Image");        
            chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            if (chooser.showOpenDialog(app.getApplicationWindow()) == JFileChooser.APPROVE_OPTION) {
                app.startTime(this); // show the progress bar and start to count
                File file = chooser.getSelectedFile();
                BufferedImage image = ImageIO.read(file);
                if (image == null) throw new Exception("Bad File type");
                JScrollPane pane = new JScrollPane(new ImagePanel(image));
                app.addTab(pane, image, file.getName());                
            } else app.noWriteMessages();
        }

Creating a new Plugin

Before creating new MetaDatas, we will create our second Plugin. It will be able to open text files. We will then create two types of tabs :

  • Those that will correspond to images (opened by our first Plugin)
  • Those that will correspond to texts (opened by new Plugin)

public class OpenDocumentPlugin extends AbstractPlugin {
    //Plugin registered items
    public static final String OPEN_DOCUMENTS = "OpenDocuments"; 
    public static final String OPEN_DOCUMENTS_DESC = "Open Documents Plugin";
    protected AbstractAction importDocAction; // Plugin importMenuItem  
        
    public OpenDocumentPlugin() {
    }
    
    public String getPluginName() {
        return OPEN_DOCUMENTS;
    }    
    
    public Object getPluginProperty(String prop) {
        if (prop.equals(PluginElementTypes.PROPERTY_DATE)) return "undef";
        else if (prop.equals(PluginElementTypes.PROPERTY_DESC)) return OPEN_DOCUMENTS_DESC;
        else if (prop.equals(PluginElementTypes.PROPERTY_VERSION)) return "0.1";
        else return null;
    }    
        
    /** Register this plugin for the Applictation.
     */
    public void register(MDIApplication appli) {
        super.register(appli);   
        
        importDocAction = new ImportDocumentAction("Document");                                
        
        JToolBar tbar = new JToolBar("OpenDocument");
        tbar.add(analyseDocAction);
        appli.getToolBarPanel().add(tbar);
    }

    public Object getStaticMenuElements(String menu) {
        if (menu.equals(PluginElementTypes.OPEN_ACTIONS)) return importDocAction;
        else return null;
    }

    public Object getDynamicMenuElements(String menuKey, FileProperties tab) {
        return null;
    }      
 
    protected class ImportDocumentAction extends AbstractMDIAction {
        public ImportDocumentAction(String name) {
            super(appli, name);
            this.setDescription("Open Document", "Open Document");
        }

        public void run() throws Exception {
        }
    }    
}

We can now properly implement the run() method for our Action. There is nothing new here :

        app.stopTime(this);
        JFileChooser chooser = new JFileChooser("Open Document");
        chooser.setDialogTitle("Save Document");        
        chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        if (chooser.showOpenDialog(app.getApplicationWindow()) == JFileChooser.APPROVE_OPTION) {
            app.startTime(this);
            URL url = chooser.getSelectedFile().toURL();
            File file = chooser.getSelectedFile();
            JEditorPane pane = new JEditorPane();
            JScrollPane scroll = new JScrollPane(pane);
            pane.setEditable(true);
            pane.setPage(url);                    
            app.addTab(pane, pane.getDocument(), file.getName()); 
        } else app.noWriteMessages();
    }

Now we can add the plugin in the plugins declaration xml file :

  <?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE Plugins PUBLIC "Plugins DTD 1.0" "Plugins.dtd">
  <Plugins>
     <plugin mainclass="org.mdi.examples.second.plugins.OpenImagePlugin" desc="OpenImagePlugin" />
     <plugin mainclass="org.mdi.examples.second.plugins.OpenDocumentPlugin" desc="OpenDocumentPlugin" />     
  </Plugins>

Text files can now be opened and we have the following result :

But if we try to Analyse the corresponding tab, of course we have the following Exception :

We must Analyse only image files : For that, we need to use MetaDatas.

Creating MetaDatas

MetaDatas allow to add (or modify) Meta-informations about an existing tab. In our case, we will use them to give a type to the tab (TEXT or IMAGE). We will only allow to Analyse IMAGE tabs.

For the moment, we don't have any documents types. Now lets create a new interface, just to store our two different String types.

public interface ElementTypes {
    public String IMAGE = "image";
    
    public String TEXT = "text";  
}

Now we will add a MetaData, with the TEXT type, to our opened text files :

    protected class ImportDocumentAction extends AbstractMDIAction {

        public void run() throws Exception {    
                ...
                pane.setEditable(true);
                pane.setPage(url);    
                MetaData meta = new MetaData(2);
                meta.put(ElementTypes.TEXT, Boolean.TRUE);
                FileProperties prop = 
                        new FileProperties(file.getName(), scroll, pane.getDocument(), meta);
                app.addTab(pane, prop);      
            } else app.noWriteMessages();

The FileProperties contains all the properties of the tab. Before now, it was created "under the hood" by the Application when we called the addTab(JComponent, Object, String) method. We will now handle this ourselves. The FileProperties is initialized with :

  • The name of the File, which will be the name of the tab
  • The JComponent to add to the tab (in our case the scroll pane)
  • The associated Object (in our case the Document)
  • and The MetaData Map for the tab, which in our case only contains one entry :
    • The key is the TEXT String
    • The value is the Boolean TRUE

With this MetaData, we are saying that the tab is of the type "TEXT".

We better do the same thing for our first Plugin :

    protected class ImportImageAction extends AbstractMDIAction {
        public void run() throws Exception {
                ...
                JScrollPane pane = new JScrollPane(new ImagePanel(image));
                MetaData meta = new MetaData(2);
                meta.put(ElementTypes.IMAGE, Boolean.TRUE);
                
                FileProperties prop = new FileProperties(file.getName(), pane, image, meta);
                app.addTab(pane, prop);                
            } else app.noWriteMessages();
        }
    }

We will now need to use this MetaDatas for our AnalyseAction, else we will still have the same Exception.

Using MetaDatas

By learning to use MetaDatas, we will also learn to use the concept of dynamic menus. Dynamic menus are menus that will be enabled (for menu items or buttons) or present only for certain types of tabs. In our case, we want the "Analyse" action to be enabled only if the selected tab is of the type "IMAGE".

To do this, we will frist create a "MetaData template" that we will compare with the MetaData of the selected tab :

public class SecondMenuFactory extends AbstractMDIMenuFactory {   
    private static MetaData acceptAnalyseMeta = new MetaData();     
    static {
        acceptAnalyseMeta.put(ElementTypes.IMAGE, Boolean.TRUE);
    }            
    ...
}

Now the Analyse action will not be a static element any more. Instead, it will be added to the dynamic menus, such that we will be able to compare our static Metadata template to the selected MetaData dynamically :

public class SecondMenuFactory extends AbstractMDIMenuFactory {  
    protected void initMenus() {
        ...
        analyseImageAction = new AnalyseImageAction(appli, "Analyse");  
        this.addToDynamicMenuMap("AnalyseKey", analyseImageAction);
        ...
    }
    ...
    protected Object getDynamicMenuItems(FileProperties prop, String menuKey) {
        if (menuKey.equals(ElementTypes.ANALYSE_IMAGE_KEY)) {
            MetaData properties = prop.getMetaData();
            if (properties.isCompatibleWith(acceptAnalyseMeta, false)) return analyseImageAction;         
            else return null;
        } else return null;
    }        
}

We now test the compatibility of the MetaData with our static "template" before trying to analyse it.

If we select a tab which contains some text, the Analyse item is disabled :

If we select a tab which contains an image, the Analyse item is enabled :

More with MetaDatas

For the moment, we have only used MetaDatas for the main Application, and only to enable or disable menu items. Of course, it is also possible to use them in Plugins, and for whole menus. Lets create a SaveAs Action for the two Plugins. First we create the SaveAs menu and bind it to the PluginElementTypes.SAVEAS_ACTIONS key :

public class SecondMenuFactory extends AbstractMDIMenuFactory {        
    ...
    private JMenu savemenu = new JMenu("SaveAs");
 
    ...
    
    protected void initMenus() {
        ...
        dynamicMenuKeyMap.put(PluginElementTypes.SAVEAS_ACTIONS, savemenu);  
        ...
        filemenu.add(savemenu);             
    }

We added the savemenu to the dynamicMenuKeyMap, mapped to the PluginElementTypes.SAVEAS_ACTIONS key. Now we will look for dynamic content under this menu, for, plugins and the main application.

For the OpenImagePlugin :

 public class OpenImagePlugin extends AbstractPlugin {
    public static final String OPEN_IMAGES = "OpenImages"; 
    public static final String OPEN_IMAGES_DESC = "Second Open Images Plugin";
    protected ImportImageAction importImageAction; 
    protected SaveImageAction saveImageAction = new SaveImageAction("Image");  ;     
    
    private static MetaData acceptSaveAsMeta = new MetaData();     
    static {
        acceptSaveAsMeta.put(ElementTypes.IMAGE, Boolean.TRUE);
    }        
    
    public OpenImagePlugin() {
    } 
    
    ...
    
    public Object getDynamicMenuElements(String menuKey, FileProperties prop) {
        if (menuKey.equals(PluginElementTypes.SAVEAS_ACTIONS)) {
            MetaData properties = prop.getMetaData();
            if (properties.isCompatibleWith(acceptSaveAsMeta, false)) return saveImageAction;         
            else return null;
        } else return null;
    }         
  
    protected class SaveImageAction extends AbstractMDIAction {
        public SaveImageAction(String name) {
            super(appli, name);
            this.setDescription("Save Image", "Save Image");
        }

        public void run() throws Exception {
            if (app.getSelectedProperties() != null) {
                app.stopTime(this);
                JFileChooser chooser = new JFileChooser("Save Image");
                chooser.setDialogTitle("Save Image");        
                chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
                if (chooser.showSaveDialog(app.getApplicationWindow()) == JFileChooser.APPROVE_OPTION) {
                    app.startTime(this);
                    BufferedImage image = (BufferedImage)app.getSelectedProperties().getObject();
                    File file = chooser.getSelectedFile();
                    ImageIO.write(image, "jpg", file);
                } else app.noWriteMessages();
            } else app.noWriteMessages();
        }
    }    
    ...

We return null if the properties are not compatible, in other words if the selected tab is not of the IMAGE type. But if the selected tab is of the IMAGE type, we return the saveImageAction Action. The SaveAs menu will be constructed dynamically, depending on the type of the selected tab.

We do the same for the OpenDocumentPlugin :

 public class OpenDocumentPlugin extends AbstractPlugin {
    public static final String OPEN_DOCUMENTS = "OpenDocuments"; 
    public static final String OPEN_DOCUMENTS_DESC = "Open Documents Plugin";
    protected AbstractAction importDocAction; 
    protected AbstractAction saveDocAction = new SaveDocumentAction("Document");
          
    private static MetaData acceptSaveAsMeta = new MetaData();     
    static {
        acceptSaveAsMeta.put(ElementTypes.TEXT, Boolean.TRUE);
    }        
    
    public OpenDocumentPlugin() {
    }    
    
    ...
    
    public Object getDynamicMenuElements(String menuKey, FileProperties tab) {
        if (menuKey.equals(PluginElementTypes.SAVEAS_ACTIONS)) {
            MetaData properties = tab.getMetaData();
            if (properties.isCompatibleWith(acceptSaveAsMeta, false)) return saveDocAction;         
            else return null;
        } else return null;
    }       
    
    protected class SaveDocumentAction extends AbstractMDIAction {
        public SaveDocumentAction(String name) {
            super(appli, name);
            this.setDescription("Save Document", "Save Document As Plain text File");
        }

        public void run() throws Exception {
            if (app.getSelectedProperties() != null) {
                app.stopTime(this);
                JFileChooser chooser = new JFileChooser("Save Document");
                chooser.setDialogTitle("Save Document");        
                chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
                if (chooser.showSaveDialog(app.getApplicationWindow()) == JFileChooser.APPROVE_OPTION) {
                    app.startTime(this);
                    File file = chooser.getSelectedFile();
                    Document doc = (Document)app.getSelectedProperties().getObject();
                    String text = doc.getText(0, doc.getLength());
                    BufferedWriter writer = new BufferedWriter(new FileWriter(file));
                    writer.write(text);
                    writer.flush();
                    writer.close();
                }
            } else app.noWriteMessages();
        }
    }  
    ...

Now the SaveAs menu will depend on the selected tab :

Creating Tool bars

Adding Toolbars is easy. For example, lets add a Toolbar for the main Application, containing only the button Analyse :

 public class SecondMenuFactory extends AbstractMDIMenuFactory { 
    protected void initMenus() {
    ...
        analyseImageAction = new AnalyseImageAction(appli, "Analyse");      
        
        // create toolbar
        JToolBar tbar = new JToolBar("Standard");
        tbar.add(analyseImageAction);
        this.getToolBarPanel().add(tbar);    
        ...
    }
 }

This is done ! Now we have a Toolbar, and it will react as the Analyse item :

We can do exactly the same thing for other actions. For example lets create an Analyse Action for TEXT tabs only in the OpenDocumentPlugin class :

public class OpenDocumentPlugin extends AbstractPlugin {
    ...
    protected AbstractAction analyseDocAction;    

    public void register(MDIApplication appli) {
 
        super.register(appli);   
        
        importDocAction = new ImportDocumentAction("Document");                                
        analyseDocAction = new AnalyseDocumentAction("Document");    
        appli.getMenuFactory().addToDynamicMenuMap("Analyse", analyseDocAction);
        
        JToolBar tbar = new JToolBar("OpenDocument");
        tbar.add(analyseDocAction);
        appli.getToolBarPanel().add(tbar);
    } 

    public Object getDynamicMenuElements(String menuKey, FileProperties tab) {
        if (menuKey.equals("Analyse")) {
            MetaData properties = tab.getMetaData();
            if (properties.isCompatibleWith(acceptSaveAsMeta, false)) return analyseDocAction;         
            else return null;            
        } else if (menuKey.equals(PluginElementTypes.SAVEAS_ACTIONS)) {
            MetaData properties = tab.getMetaData();
            if (properties.isCompatibleWith(acceptSaveAsMeta, false)) return saveDocAction;         
            else return null;
        } else return null;
    }           
} 

As for other dynamic menus (and even if there it is only a button on a Toolbar), we have to register the associated Action under a specific key, and return it when asked by the Application in the getDynamicMenuElements method.

Getting it all Together

In this tutorial, we learned to :

  • Use MetaDatas and filter menus according to them
  • Use Other ways to register Actions
  • Associate toolbars with actions, both in the main Application and in Plugins
 
 
Close
loading
Please Confirm
Close