001    /*
002      Copyright (C) 2002-2003 Renaud Pawlak, Laurent Martelli.
003    
004      This program is free software; you can redistribute it and/or modify
005      it under the terms of the GNU Lesser General Public License as
006      published by the Free Software Foundation; either version 2 of the
007      License, or (at your option) any later version.
008    
009      This program is distributed in the hope that it will be useful,
010      but WITHOUT ANY WARRANTY; without even the implied warranty of
011      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012      GNU Lesser General Public License for more details.
013    
014      You should have received a copy of the GNU Lesser General Public
015      License along with this program; if not, write to the Free Software
016      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
017      USA */
018    
019    package org.objectweb.jac.aspects.gui;
020    
021    import java.util.Collection;
022    import java.util.HashMap;
023    import java.util.Hashtable;
024    import java.util.List;
025    import java.util.Map;
026    import java.util.Vector;
027    import org.apache.log4j.Logger;
028    import org.objectweb.jac.core.rtti.AbstractMethodItem;
029    import org.objectweb.jac.core.rtti.ClassItem;
030    import org.objectweb.jac.core.rtti.MemberItem;
031    import org.objectweb.jac.core.rtti.MethodItem;
032    
033    /**
034     * This interface allows the programmer to create simple customized GUIs.
035     *
036     * <p>The idea is to define sub-panels that can contain views on the
037     * objects of the application. The geometric placement of the
038     * sub-panel is defined by the <code>setSubPanesGeometry</code> method
039     * that has to be called at contruction-time.
040     *
041     * <p>Once the geometry is chosen, you can tell a pane to contain a
042     * view on a given object by using the <code>addReferenceToPane</code>
043     * method.
044     *
045     * <p>This class must be subclassed by the porgrammer of a JAC
046     * application to provide a customized GUI.
047     *
048     * @see GuiAC
049     * @see #setSubPanesGeometry(int,int,boolean[])
050     * @see #addReferenceToPane(MemberItem,String,String[],String) */
051    
052    public class CustomizedGUI { 
053        static Logger logger = Logger.getLogger("gui");
054    
055        //GuiAC guiAC = null;
056    
057        // paneID -> {viewType,args[]}
058        Hashtable paneContents = new Hashtable();
059        // paneID -> viewType
060        Hashtable paneContainers = new Hashtable();
061        // (Integer)splitterID -> (Integer)location
062        Hashtable splitters = new Hashtable();
063        // changed paneID -> invalid_paneID
064        Hashtable invalidPanes = new Hashtable(); 
065    
066        // WARNING: the following order is very important
067        /** The subpanes are separated by an horizontal splitter */
068        public static final int HORIZONTAL = 0;
069        /** The upper subpanes are separated by a vertical splitter */
070        public static final int HORIZONTAL_UP = 1;
071        /** The down subpanes are separated by a vertical splitter */
072        public static final int HORIZONTAL_DOWN = 2;
073        /** The subpanes are separated by a vertical splitter */
074        public static final int VERTICAL = 3;
075        /** The left subpanes are separated by an horizontal splitter */
076        public static final int VERTICAL_LEFT = 4;
077        /** The right subpanes are separated by an horizontal splitter */
078        public static final int VERTICAL_RIGHT = 5;
079    
080        public static final String BOTTOM = "BOTTOM";
081        public static final String TOP = "TOP";
082    
083        public void setInvalidPane(String changedPane, String invalidPane) {
084            invalidPanes.put(changedPane,invalidPane);
085        }
086    
087        public String getInvalidPane(String changedPane) {
088            return (String)invalidPanes.get(changedPane);
089        }
090    
091        /**
092         * Gets the number of sub-panes in the main window of the GUI.
093         *
094         * @return the number of panels 
095         */
096        int subPanesCount= 1;
097        public int getSubPanesCount() {
098            return subPanesCount;
099        }
100    
101        int geometry = 0;
102        public int getGeometry() {
103            return geometry;
104        }
105    
106        boolean[] scrollings = new boolean[] {false};
107        public boolean[] getScrollings() {
108            return scrollings;
109        }
110       
111        String application;
112       
113        /**
114         * Get the value of application.
115         * @return value of application.
116         */
117        public String getApplication() {
118            return application;
119        }
120       
121        /**
122         * Set the value of application.
123         * @param v  Value to assign to application.
124         */
125        public void setApplication(String  v) {
126            this.application = v;
127        }
128       
129    
130        /**
131         * Sets the geometric arrangement of the panes.
132         *
133         * <p><ul><li><code>subPanesCount == 4 && geometry == VERTICAL</code>:
134         * <pre>
135         *    +-------+
136         *    | 0 | 2 |
137         *    +---|---+
138         *    | 1 | 3 |
139         *    +-------+
140         * </pre>
141         * </li>
142         * <li><code>subPanesCount == 4 && geometry == HORIZONTAL</code>:
143         * <pre>
144         *    +-------+
145         *    | 0 | 1 |
146         *    +-------+
147         *    | 2 | 3 |
148         *    +-------+
149         * </pre>
150         * </li>
151         * <li><code>subPanesCount == 3 && geometry == HORIZONTAL_UP</code>:
152         * <pre>
153         *    +-------+
154         *    | 0 | 1 |
155         *    +-------+
156         *    |   2   |
157         *    +-------+
158         * </pre>
159         * </li>
160         * </li>
161         * <li><code>subPanesCount == 3 && geometry == HORIZONTAL_DOWN</code>:
162         * <pre>
163         *    +-------+
164         *    |   2   |
165         *    +-------+
166         *    | 0 | 1 |
167         *    +-------+
168         * </pre>
169         * </li>
170         * <li><code>subPanesCount == 3 && geometry == VERTICAL_LEFT</code>:
171         * <pre>
172         *    +---+---+
173         *    | 0 |   |
174         *    +---| 2 |
175         *    | 1 |   |
176         *    +---+---+
177         * </pre>
178         * </li>
179         * <li><code>subPanesCount == 3 && geometry == VERTICAL_RIGHT</code>:
180         * <pre>
181         *    +---+---+
182         *    |   | 0 |
183         *    | 2 |---+
184         *    |   | 1 |
185         *    +---+---+
186         * </pre>
187         * </li>
188         * <li><code>subPanesCount == 2 && geometry == VERTICAL</code>:
189         * <pre>
190         *    +---+---+
191         *    |   |   |
192         *    | 0 | 1 |
193         *    |   |   |
194         *    +---+---+
195         * </pre>
196         * </li>
197         * <li><code>subPanesCount == 2 && geometry == HORIZONTAL</code>:
198         * <pre>
199         *    +-------+
200         *    |   0   |
201         *    +-------+
202         *    |   1   |
203         *    +-------+
204         * </pre>
205         * </li>
206         *
207         * @param subPanesCount the number of subpanes in the window
208         * @param geometry the geometry = <code>VERTICAL || HORIZONTAL ||
209         * VERTICAL_LEFT || VERTICAL_RIGHT || HORIZONTAL_UP ||
210         * HORIZONTAL_DOWN</code> (see above)
211         * @param scrollings a set of string that tells if the sub-panes
212         * must be srollable or not 
213         */
214        public void setSubPanesGeometry(int subPanesCount, int geometry, 
215                                        boolean[] scrollings) {
216            this.subPanesCount = subPanesCount;
217            this.geometry = geometry;
218            this.scrollings = scrollings;
219        }
220    
221        /**
222         * Sets the object that should be opened in the given panel id.
223         *
224         * @param paneId the panel id (see the geometry to know its
225         * placement)
226         * @param viewType of the type of the view
227         * @param args parameters to pass to the view type constructor
228         *
229         * @see #setSubPanesGeometry(int,int,boolean[]) 
230         */
231        public void setPaneContent(String paneId, 
232                                   String viewType, String[] args) {      
233            paneContents.put(paneId,new PanelContent(viewType,args));
234        }
235    
236        /**
237         * Returns the type of the object to be displayed in a pane
238         */
239        public String getPaneContentType(String paneID) {
240            return (String)((Object[])paneContents.get(paneID))[0];
241        }
242    
243        public String[] getPaneContentArgs(String paneID) {
244            return (String[])((Object[])paneContents.get(paneID))[1];
245        }
246    
247        public Map getPaneContents() {
248            return paneContents;
249        }
250    
251        public void setPaneContainer(String paneId, String containerType) {
252            paneContainers.put(paneId,containerType);
253        }
254    
255        public String getPaneContainer(String paneId) {
256            return (String)paneContainers.get(paneId);
257        }
258    
259        public Map getPaneContainers() {
260            return paneContainers;
261        }
262    
263        Hashtable subPaneContents = new Hashtable();
264       
265        public Map getSubPaneContents() {
266            return subPaneContents;
267        }
268    
269        // PaneID -> Vector of FieldItem
270        HashMap targetContainers = new HashMap();
271    
272        /**
273         * Tells a referenced object to open in a given panel when a view
274         * is asked by the user (instead of opening in a popup).
275         *
276         * @param reference the reference (can be a method's result)
277         * @param viewType the type of the view that must be opened
278         * @param paneId the panel id where the view must be opened */
279    
280        public void addReferenceToPane(MemberItem reference, 
281                                       String viewType, String[] viewParams,
282                                       String paneId) {
283            Vector containers = (Vector)targetContainers.get(reference);
284            if (containers==null) {
285                containers = new Vector();
286                targetContainers.put(reference,containers);
287            }
288            containers.add(new Target(paneId,viewType,viewParams));
289        }
290    
291        public Map getTargetContainers() {
292            return targetContainers;
293        }
294    
295        public List getFieldTargets(MemberItem reference) {
296            List result = null;
297            while (result==null && reference!=null) {
298                result = (List)targetContainers.get(reference);
299                ClassItem superClass = reference.getClassItem().getSuperclass();
300                if (superClass!=null)
301                    reference = superClass.getFieldNoError(reference.getName());
302                else 
303                    reference = null;
304            }
305            return result;
306        }
307    
308        int left;
309        int up;
310        int width;
311        int height;
312    
313        public int getLeft() {
314            return left;
315        }
316    
317        public int getUp() {
318            return up;
319        }
320    
321        public int getWidth() {
322            return width;
323        }
324    
325        public int getHeight() {
326            return height;
327        }
328    
329        boolean geometrySet = false;
330    
331        /**
332         * Returns true if the geometry of this GUI was set with
333         * <code>setPosition</code>.
334         *
335         * @see #setPosition(int,int,int,int) 
336         */
337        public boolean isGeometrySet() {
338            return geometrySet;
339        }
340    
341        /**
342         * Sets the dimensions and position of the window regarding to the
343         * main screen.
344         *
345         * @param left left-border pixel
346         * @param up upper-border pixel
347         * @param width in percentage regarding the screen
348         * @param height in percentage regarding the screen 
349         * @see #isGeometrySet() 
350         */
351    
352        public void setPosition(int left, int up, int width, int height) {
353            this.up = up;
354            this.left = left;
355            this.width = width;
356            this.height = height;
357            geometrySet = true;
358        }
359    
360        /**
361         * Sets a splitter location.
362         *
363         * <p>The splitter is referenced by its index going from the
364         * front-end splitter to the back-end splitters. For instance, in
365         * the case of a 3 sub-panel window, the 0 index references the
366         * splitter that splits the main window in two, the 1 index, the
367         * one that splits the half-window in two other smaller parts.
368         *
369         * @param splitterId the splitter's index
370         * @param location the position in pixel, regarding to the top/left
371         * component, a negative value means that the splitter should be
372         * set at the preferred sized of the inner components */
373    
374        public void setSplitterLocation(int splitterId, float location) {
375            splitters.put(new Integer(splitterId), new Float(location));
376        }
377    
378        public Map getSplitters() {
379            return splitters;
380        }
381    
382        boolean hasStatusBar=false;
383        MethodItem statusBarMethod=null;
384        String statusPosition = BOTTOM;
385    
386        public boolean hasStatusBar() {
387            return hasStatusBar;
388        }
389    
390        public String getStatusPosition() {
391            return statusPosition;
392        }
393    
394        public MethodItem getStatusBarMethod() {
395            return statusBarMethod;
396        }
397    
398        /**
399         * Adds a status bar to the GUI.
400         */
401    
402        public void addStatusBar(MethodItem method,String position) {
403            hasStatusBar=true;
404            statusBarMethod = method;
405            statusPosition=position;
406        }
407        
408        /*
409          public void showStatus(String message) {
410          if( statusBar == null ) {
411          System.out.println(message);
412          } else {
413          ((JLabel)statusBar.getComponent(0)).setText(message);
414          }
415          }
416        */
417    
418        /**
419         * Creates a new customized GUI.
420         *
421         * <p>When subclassing, a typical implementation of the constructor is:
422         *
423         * <pre class=code>
424         * super()
425         * // initialization calls such as setSubPanesGeometry
426         * // addReferenceToPane, setPosition
427         * ...
428         * show();
429         * </pre>
430         *
431         * @see #setSubPanesGeometry(int,int,boolean[])
432         * @see #addReferenceToPane(MemberItem,String,String[],String)
433         * @see #setPosition(int,int,int,int)
434         * @see #setSplitterLocation(int,float) 
435         */   
436        public CustomizedGUI(String id) {
437            this.id = id;
438        }
439    
440        String id;
441        public String getId() {
442            return id;
443        }
444    
445        Hashtable menus = new Hashtable();
446    
447        public Hashtable getMenus() {
448            return menus;
449        }
450    
451        public Menu getMenus(String name) {
452            Menu menu=(Menu)menus.get(name);
453            if(menu==null) {
454                menu=new Menu();
455                menus.put(name,menu);
456            }
457            return menu;
458        }
459    
460        public boolean hasMenuBar() {
461            return menus.size()>0;
462        }
463    
464        /**
465         * Add an item in a menu
466         *
467         * @param menuPath the path of the menu where to add an item
468         * @param callback the method to call when this item is selected
469         *
470         * @see #addMenuSeparator(String,String[])
471         */
472        public void addMenuItem(String menuName,
473                                String[] menuPath, 
474                                Callback callback) {
475            String[] path = new String[menuPath.length-1];
476            System.arraycopy(menuPath,0,path,0,menuPath.length-1);
477            getMenu(menuName,path).put(
478                menuPath[menuPath.length-1],callback);
479        }
480    
481        /**
482         * Add a separator in a menu
483         *
484         * @param menuName the name of the menu
485         * @param menuPath the path of the menu where to add a separator
486         *
487         * @see #addMenuItem(String,String[],Callback)
488         */
489        public void addMenuSeparator(String menuName,String[] menuPath) {
490            getMenu(menuName,menuPath).addSeparator();
491        }
492    
493        Vector toolbar = new Vector();
494    
495        /**
496         * Add a button in the toolbar
497         *
498         * @param method the static method to call when the button is pressed
499         *
500         * @see #addToolbarSeparator()
501         * @see #addToolbarAction(String,AbstractMethodItem)
502         */
503        public void addToolbarAction(AbstractMethodItem method) {
504            toolbar.add(new Callback(null,method, new Object [0]));
505        }
506    
507        /**
508         * Add a button in the toolbar
509         *
510         * @param objectName name of the object on which to invoke the method
511         * @param method the method to call when the button is pressed
512         *
513         * @see #addToolbarSeparator()
514         * @see #addToolbarAction(AbstractMethodItem)
515         */
516        public void addToolbarAction(String objectName, AbstractMethodItem method) {
517            toolbar.add(new Callback(objectName,method,new Object [0]));
518        }
519    
520        /**
521         * Add a separator in the toolbar
522         *
523         * @see #addToolbarAction(AbstractMethodItem)
524         */
525        public void addToolbarSeparator() {
526            toolbar.add(null);
527        }
528    
529        /**
530         * Returns the toolbar of the customized GUI.
531         *
532         * @return a collection representing the tool. It contains
533         * AbstractMethodItem objects for buttons, and null for a
534         * separator.
535         */
536        public Collection getToolbar() {
537            return toolbar;
538        }
539    
540        public boolean hasToolBar() {
541            return toolbar.size()>0;
542        }
543    
544        /**
545         * Find a menu defined by its path
546         */
547        protected Menu getMenu(String menuName,String[] menuPath) {
548            Menu menu = getMenus(menuName);
549            for (int i=0; i<menuPath.length; i++) {
550                if (menu.containsKey(menuPath[i])) {
551                    Object current = menu.get(menuPath[i]);
552                    if (current instanceof Menu) {
553                        menu = (Menu)current;
554                    } else {
555                        logger.warn("overriding menu item "+current);
556                        Menu old = menu;
557                        menu = new Menu();
558                        old.put(menuPath[i],menu);
559                    }
560                } else {
561                    Menu old = menu;
562                    menu = new Menu();
563                    old.put(menuPath[i],menu);
564                }
565            }
566            return menu;
567        }
568    
569        public void setMenuIcon(String menuName,
570                                String[] menuPath, String icon) {
571            getMenu(menuName,menuPath).setIcon(icon);
572        }
573       
574        public void setMenuPosition(String menuName,String position) {
575            getMenus(menuName).setPosition(position);
576        }
577       
578        String welcomeTitle = "Welcome";
579        String welcomeMessage = null;
580        String welcomeIcon = null;
581    
582        public void setWelcomeMessage(String title,String message,String icon) {
583            this.welcomeTitle = title;
584            this.welcomeMessage = message;
585            this.welcomeIcon = icon;
586        }
587    
588        String title;
589        public void setTitle(String title) {
590            this.title=title;
591        }
592        public String getTitle() {
593            return title;
594        }
595    
596        Vector cssURLs=new Vector();
597        public void addStyleSheetURL(String url) {
598            cssURLs.add(url);
599        }
600    
601        public Vector getStyleSheetURLs() {
602            return cssURLs; 
603        }
604    
605        AbstractMethodItem onCloseHandler;
606        public void setOnCloseHandler(AbstractMethodItem handler) {
607            onCloseHandler = handler;
608        }
609        public AbstractMethodItem getOnCloseHandler() {
610            return onCloseHandler;
611        }
612    
613        String icon;
614        public String getIcon() {
615            return icon;
616        }
617        public void setIcon(String icon) {
618            this.icon = icon;
619        }
620    }