001    // GraphLab Project: http://graphlab.sharif.edu
002    // Copyright (C) 2008 Mathematical Science Department of Sharif University of Technology
003    // Distributed under the terms of the GNU General Public License (GPL): http://www.gnu.org/licenses/
004    
005    package graphlab.ui;
006    
007    import graphlab.platform.core.AbstractAction;
008    import graphlab.platform.core.BlackBoard;
009    import graphlab.platform.extension.Extension;
010    import graphlab.platform.lang.CommandAttitude;
011    import graphlab.platform.parameter.Parameter;
012    import graphlab.platform.parameter.Parametrizable;
013    import graphlab.ui.components.ExtensionConfigFrame;
014    import graphlab.ui.components.GFrame;
015    import graphlab.ui.components.gmenu.GMenuBar;
016    import graphlab.ui.components.gmenu.GMenuItem;
017    import graphlab.ui.xml.UIHandlerImpl;
018    
019    import javax.swing.*;
020    import javax.swing.border.EmptyBorder;
021    import java.awt.*;
022    import java.awt.event.ActionEvent;
023    import java.awt.event.ActionListener;
024    import java.awt.event.MouseAdapter;
025    import java.awt.event.MouseEvent;
026    import java.lang.reflect.Field;
027    
028    /**
029     * the base class for creating extension handlers
030     * the implementing class will have a menu assigned to it automatically the name of the menu will be
031     * from the constructors parametr(sp) and the will also listen to UI.getUIEvent(sp.getName())
032     *
033     * @author azin azadi
034    
035     */
036    public abstract class AbstractExtensionAction<t extends Extension> extends AbstractAction implements ActionListener {
037        //todo: Write how to create new extension types document.
038        protected JMenu parentMenu;
039        public GMenuItem menuItem;
040        ExtensionConfigFrame ecf;
041        /**
042         * a button which will be added to the right of menu item
043         */
044        protected JButton extraButton;
045    
046        public t getTarget() {
047            return target;
048        }
049    
050        public t target;
051    
052        public AbstractExtensionAction(BlackBoard bb, t sp) {
053            super(bb);
054            target = sp;
055            String name = getMenuNamePrefix() + sp.getName();
056            String actionId = name + sp.getDescription();
057            listen4Event(UIUtils.getUIEventKey(actionId));
058            if (!name.equals("")) {
059                menuItem = createMenuItem(name, actionId, bb);
060                parentMenu = getParentMenu();
061                if (parentMenu != null)
062                    GMenuBar.insert(parentMenu, menuItem, getMenuPlace());
063            }
064            createExtensionCommandsForCommandLine();
065        }
066    
067        /**
068         * It is a XML-Based UI concept
069         *
070         * @return the place which the extension menu will be inserted in its parents menu, the place is
071         *         only a comparative value, it means that Menu Items with bigger place will be inserted after smaller place Menue Items
072         */
073        protected int getMenuPlace() {
074            return 40;
075        }
076    
077        /**
078         * if you want to create custom menues for your extension (i.e. want to add some extra components to it) you should
079         * override this method.
080         *
081         * @param name
082         * @param actionId
083         * @param bb
084         * @return
085         * @see graphlab.plugins.algorithmanimator.extension.AlgorithmExtensionAction
086         */
087        protected final GMenuItem createMenuItem(String name, String actionId, BlackBoard bb) {
088            int index = Math.max(name.indexOf(UIHandlerImpl.menueIndexChar), 0);
089            String labelString = name.replace(UIHandlerImpl.menueIndexChar + "", "");
090            if (isInsertExtraButtonToMenuItem()) {
091                GMenuItem menuItem = new GMenuItem(labelString, actionId, bb, null, index) {
092                    public Dimension getPreferredSize() {
093                        Dimension preferredSize = super.getPreferredSize();
094                        Dimension bps = extraButton.getPreferredSize();
095                        preferredSize.width += bps.getWidth() + 4;
096                        preferredSize.height = (int) Math.max(preferredSize.height, bps.getHeight()) + 1;
097                        return preferredSize;
098                    }
099    
100                };
101                String imageURL = "/graphlab/plugins/main/resources/edit16_.gif";
102                extraButton = new JButton(new ImageIcon(getClass().getResource(imageURL)));
103                extraButton.setBorder(new EmptyBorder(0, 0, 0, 0));
104                extraButton.addActionListener(this);
105                extraButton.addMouseListener(new MouseAdapter() {
106                    public void mouseClicked(MouseEvent e) {
107                        actionPerformed(null);
108                    }
109                });
110                menuItem.setLayout(new BorderLayout());
111                menuItem.add(extraButton, BorderLayout.EAST);
112                return menuItem;
113            } else
114                return new GMenuItem(name, actionId, bb);
115        }
116    
117        /**
118         * determines wheter to insert an extra button at the right side of mene item,
119         * normally this is true whenever the target extension implements Parametrizable (have some parameters)
120         * on this mode on pressing the button a dialog for setting the parameters will be shown, and then
121         * it will be executed, if the user clicks the menu item directly the extension will be
122         * performed without asking of parameters.
123         * <p/>
124         * if you want to override this method for your own ExtensionAction
125         * NOTE that on pressing the button the actionPerformed will be called, so you can
126         * do what you want to do by overriding that method.
127         *
128         * @return true to insert the extra button, false to no insert it.
129         * @see Parametrizable
130         * @see @Parameter
131         * @see graphlab.plugins.algorithmanimator.extension.AlgorithmExtensionAction
132         */
133        protected boolean isInsertExtraButtonToMenuItem() {
134            return target instanceof Parametrizable;
135        }
136    
137        /**
138         * inorder if you created a new type of extension and for that extension you want to do some thing different
139         * when it is called on commandline (i.e. in normal state it uses some GUI functionalities and you want to avoid them)
140         * you can override this method and do what you want, which will
141         * be called whenever your extension called from commandline
142         * for an example see GraphGeneratorExtensionAction
143         *
144         * @see graphlab.plugins.graphgenerator.core.extension.GraphGeneratorExtensionAction
145         */
146        public Object performExtensionInCommandLine() {
147            performExtension();
148            return null;
149        }
150    
151        protected void createExtensionCommandsForCommandLine() {
152            final AbstractExtensionAction<t> ths = this;
153            final t trgClass = target;
154            final CommandAttitude comati = trgClass.getClass().getAnnotation(CommandAttitude.class);
155    
156            String command = "";
157            CommandAttitude c = comati;
158            String cname;
159            String abrv;
160            String desc;
161            if (c != null) {
162    //            Shell.set_variable("_" + target.getClass().getSimpleName(), target);
163                cname = c.name();
164                abrv = c.abbreviation();
165                desc = c.description();
166                if (desc == null || desc.equals(""))
167                    desc = target.getDescription();
168            } else {
169                cname = target.getClass().getSimpleName();
170                abrv = "";
171                desc = target.getDescription();
172            }
173            command += cname + "(";
174            String help = "";
175    
176            for (Field f : target.getClass().getFields()) {
177                if (f.getAnnotation(Parameter.class) != null) {
178                    command += f.getType().getName()
179                            + " "
180                            + f.getName() + ",";
181                    help += "_" + target.getClass().getSimpleName()
182                            + "."
183                            + f.getName()
184                            + " = "
185                            + f.getName()
186                            + ";\n";
187                }
188            }
189            if (command.endsWith(","))
190                command = command.substring(0, command.length() - 1);
191            command += ")\n{";
192    //        System.out.println(command);
193            ExtensionShellCommandProvider.addCommand(ths, trgClass, cname, abrv, command, desc, help);
194    
195        }
196    
197        /**
198         * to put a prefix before the name of your extension menu override this method
199         */
200        protected String getMenuNamePrefix() {
201            return "";
202        }
203    
204        /**
205         * gets a menu for adding the sub menu, if returns null, no submenu will added!
206         */
207        protected JMenu getParentMenu() {
208            GFrame f = UIUtils.getGFrame(blackboard);
209            String mname = getParentMenuName();
210            return f.getMenu().getUniqueMenu(mname, -1);
211        }
212    
213        /**
214         * first checks if o instanceof Parametrizable if so, shows an
215         * editor for it's Parameters.
216         *
217         * @param o
218         * @return true if o isn't an instance of Parametrizable or the user cancells the editing
219         */
220        public boolean testAndSetParameters(Object o) {
221            if (o instanceof Parametrizable) {
222                ParameterShower ps = new ParameterShower();
223    
224                return ps.show((Parametrizable) o);
225            } else
226                return true;
227        }
228    
229        /**
230         * removes all UI Components that are created for the extension (menues, ...)
231         */
232        public void removeCreatedUIComponents() {
233            if (parentMenu != null && menuItem != null) {
234                parentMenu.remove(menuItem);
235                //if parent menu is empty remove it and so on.
236                if (parentMenu.getMenuComponentCount() == 0) {
237                    parentMenu.getParent().remove(parentMenu);
238                }
239            }
240    
241        }
242    
243        /**
244         * returns the menu name that the menuitem of this action is its child
245         */
246        public abstract String getParentMenuName();
247    
248        public final void performAction(String eventKey, Object value) {
249            performExtension();
250        }
251    
252        /**
253         * occurs whenever extraButton pressed
254         * <p/>
255         * =
256         */
257        public void actionPerformed(ActionEvent e) {
258            parentMenu.setSelected(false);
259    //        final ExtensionExternalData tt = target.getClass().getAnnotation(ExtensionExternalData.class);
260    //        ecf = new ExtensionConfigFrame(this.target, tt);
261    //        ecf.setAlwaysOnTop(true);
262    //        if (tt != null) {
263    //            if (tt.helpURL() != null) {
264    //                JButton helpBtn = new JButton(new ImageIcon(getClass().getResource("/graphlab/plugins/main/resources/help16.gif")));
265    //                helpBtn.setBorder(new EmptyBorder(0, 0, 0, 0));
266    //                helpBtn.addMouseListener(new MouseAdapter() {
267    //                    public void mouseClicked(MouseEvent e) {
268    //                        try {
269    //                            Browser.browse(new URL(tt.helpURL()));
270    //                        } catch (MalformedURLException e1) {
271    //                            e1.printStackTrace();
272    //                        }
273    //                    }
274    //                });
275    //                menuItem.add(helpBtn, BorderLayout.EAST);
276    //            }
277    //            if (tt.sourceCodeURL() != null) {
278    //                JButton srcBtn = new JButton(new ImageIcon(getClass().getResource("/graphlab/plugins/main/resources/open16.gif")));
279    //                srcBtn.setBorder(new EmptyBorder(0, 0, 0, 0));
280    //                srcBtn.addMouseListener(new MouseAdapter() {
281    //                    public void mouseClicked(MouseEvent e) {
282    //                        try {
283    //                            Browser.browse(new URL(tt.sourceCodeURL()));
284    //                        } catch (MalformedURLException e1) {
285    //                            e1.printStackTrace();
286    //                        }
287    //                    }
288    //                });
289    //                menuItem.add(srcBtn, BorderLayout.EAST);
290    //            }
291    //        }
292    //
293    //
294    //        ecf.setVisible(true);
295            if (testAndSetParameters(target))
296                performExtension();
297        }
298    
299        public abstract void performExtension();
300    
301    }