001    /*
002      Copyright (C) 2001-2003 Renaud Pawlak <renaud@aopsys.com>, 
003                              Laurent Martelli <laurent@aopsys.com>
004      
005      This program is free software; you can redistribute it and/or modify
006      it under the terms of the GNU Lesser General Public License as
007      published by the Free Software Foundation; either version 2 of the
008      License, or (at your option) any later version.
009    
010      This program is distributed in the hope that it will be useful,
011      but WITHOUT ANY WARRANTY; without even the implied warranty of
012      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013      GNU Lesser General Public License for more details.
014    
015      You should have received a copy of the GNU Lesser General Public License
016      along with this program; if not, write to the Free Software
017      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
018    
019    package org.objectweb.jac.aspects.gui.swing;
020    
021    import java.awt.Component;
022    import java.awt.Dialog;
023    import java.awt.Font;
024    import java.awt.Frame;
025    import java.awt.Window;
026    import java.awt.font.TextAttribute;
027    import java.io.File;
028    import java.io.FileOutputStream;
029    import java.io.IOException;
030    import java.io.OutputStreamWriter;
031    import java.io.Reader;
032    import java.util.Arrays;
033    import java.util.Collection;
034    import java.util.HashSet;
035    import java.util.Hashtable;
036    import java.util.Iterator;
037    import java.util.Map;
038    import javax.swing.JFileChooser;
039    import javax.swing.UIManager;
040    import org.apache.log4j.Logger;
041    import org.objectweb.jac.aspects.gui.CommitException;
042    import org.objectweb.jac.aspects.gui.CustomizedDisplay;
043    import org.objectweb.jac.aspects.gui.CustomizedGUI;
044    import org.objectweb.jac.aspects.gui.CustomizedView;
045    import org.objectweb.jac.aspects.gui.DialogView;
046    import org.objectweb.jac.aspects.gui.DisplayContext;
047    import org.objectweb.jac.aspects.gui.EditorContainer;
048    import org.objectweb.jac.aspects.gui.FieldEditor;
049    import org.objectweb.jac.aspects.gui.GenericFactory;
050    import org.objectweb.jac.aspects.gui.GuiAC;
051    import org.objectweb.jac.aspects.gui.View;
052    import org.objectweb.jac.aspects.gui.ViewFactory;
053    import org.objectweb.jac.core.Collaboration;
054    import org.objectweb.jac.core.NameRepository;
055    import org.objectweb.jac.core.rtti.AbstractMethodItem;
056    import org.objectweb.jac.util.ExtArrays;
057    import org.objectweb.jac.util.Strings;
058    
059    public class SwingDisplay implements CustomizedDisplay {
060        static Logger logger = Logger.getLogger("display");
061    
062        /* Generic family -> Swing family */
063        static Map fontFamilies = new Hashtable();
064        static {
065            fontFamilies.put("serif","Serif");
066            fontFamilies.put("sans-serif","SansSerif");
067            fontFamilies.put("monospace","Monospaced");
068        }
069    
070        ViewFactory factory;
071        // customizedID -> customized
072        Hashtable frames = new Hashtable();
073    
074        public SwingDisplay(ViewFactory factory) {
075            this.factory = factory;
076            initFonts();
077        }
078    
079        protected void initFonts() {
080            // Fonts settings
081            Map fontAttributes = GuiAC.getFontAttributes();
082            if (fontAttributes.size()>0) {
083                int type = 0;
084    
085                String weight = (String)fontAttributes.get("weight"); 
086                if (weight!=null) {
087                    if (weight.compareToIgnoreCase("bold")==0) {
088                        type |= Font.BOLD;
089                    } else if (weight.compareToIgnoreCase("normal")==0) {
090                        // nothing
091                    } else {
092                        logger.warn("Unknown font weight "+weight);
093                    }
094                }
095    
096                String style = (String)fontAttributes.get("style"); 
097                if (style!=null) {
098                    if (style.compareToIgnoreCase("italic")==0) {
099                        type |= Font.ITALIC;
100                    } else if (style.compareToIgnoreCase("normal")==0) {
101                        // nothing
102                    } else {
103                        logger.warn("Unknown font style "+style);
104                    }
105                }
106    
107                String size = (String)fontAttributes.get("size");
108                String family = (String)fontAttributes.get("family");
109    
110                Font font = new Font( 
111                    family!=null ? family : "SansSerif", 
112                    type, 
113                    size!=null ? Integer.parseInt(size) : 12 );
114    
115                UIManager.put("Label.font", font);
116                UIManager.put("ComboBox.font", font);
117                UIManager.put("Menu.font", font);
118                UIManager.put("MenuBar.font", font);
119                UIManager.put("Panel.font", font);
120                UIManager.put("Border.font", font);
121                UIManager.put("MenuItem.font", font);
122                UIManager.put("Button.font", font);
123                UIManager.put("RadioButton.font", font);
124                UIManager.put("RadioButtonMenuItem.font", font);
125                UIManager.put("CheckBox.font", font);
126                UIManager.put("CheckBoxMenuItem.font", font);
127                UIManager.put("TabbedPane.font", font);
128            }
129            boldifyFont("TableHeader.font");
130        }
131    
132        public static void boldifyFont(String resourceName) {
133            UIManager.put(resourceName,boldifyFont(UIManager.getFont(resourceName)));
134        }
135    
136        public static Font boldifyFont(Font font) {
137            Map attributes = font.getAttributes();
138            attributes.put(TextAttribute.WEIGHT,TextAttribute.WEIGHT_BOLD);
139            return new Font(attributes);
140        }
141    
142        // Strings
143        HashSet timedoutDialogs = new HashSet();
144        public void addTimedoutDialog(DialogView dialog) {
145            timedoutDialogs.add(dialog);
146        }
147    
148        public void closeWindow(View window, boolean validate) {
149            window.close(validate);
150            if (window instanceof Window)
151                ((Window)window).dispose();
152        }
153    
154        public void fullRefresh() {
155            Iterator it = frames.entrySet().iterator();
156            while(it.hasNext()) {
157                Map.Entry entry = (Map.Entry)it.next();
158                View frame = (View)entry.getValue();
159                CustomizedGUI customized = ((CustomizedView)frame).getCustomizedGUI();
160                frame.close(true);
161                ((Window)frame).dispose();
162                View newframe = factory.createView(
163                    customized.getTitle(),"Customized",
164                    new Object[] {customized},
165                    new DisplayContext(this,null));
166                logger.debug("frame created "+newframe);
167                frames.put(entry.getKey(),newframe);
168                ((Component)newframe).setVisible(true);
169            }
170        }
171    
172        public void showCustomized(String id, Object object, Map panels) {
173        }
174    
175        public void showCustomized(String id, Object object) {
176            logger.debug("showCustomized("+id+","+object+")");
177            CustomizedGUI customized = (CustomizedGUI)object;
178            Component frame = (Component)frames.get(id);
179            if (frame!=null) {
180                if (frame instanceof Window) {
181                    logger.debug("showing window "+id);
182                    ((Window)frame).show();
183                    ((Window)frame).toFront();
184                }
185            } else {
186                try {
187                    frame = (Component)factory.createView(
188                        customized.getTitle(),"Customized",
189                        new Object[] {customized},
190                        new DisplayContext(this,null));
191                    frames.put(id,frame);
192                    frame.setVisible(true);
193                    // do this after show because otherwise, percentage
194                    // positions goes wrong.
195                    ((SwingCustomized)frame).setSplitters();
196                } catch (Exception e) {
197                    e.printStackTrace();
198                }
199            }
200        }
201    
202        public CustomizedView getCustomizedView(String customizedID) {
203            return (CustomizedView)frames.get(customizedID);
204        }
205    
206        public Collection getCustomizedViews() {
207            return frames.values();
208        }
209    
210        public ViewFactory getFactory() {
211            return factory;
212        }
213    
214        // implements the display interface
215    
216        public void show(Object object) {
217            show(object,
218                 "Object",new String[] {GuiAC.DEFAULT_VIEW});
219        }
220    
221        public void show(Object object,
222                         String viewType, Object[] viewParams) {
223            logger.debug("show("+Strings.hex(object)+")");
224            if (object==null) {
225                return;
226                /*
227                  } else if (object instanceof Throwable) {
228                  Log.trace("exception",(Throwable)object);
229                  String message = ((Throwable)object).getMessage();
230                  if ("".equals(message) || message==null)
231                  showMessage(object.getClass().getName(),"Error",false,false,true);
232                  else
233                  showMessage(message,"Error",false,false,true);
234                */
235            } else if (object instanceof Reader) {
236                saveStreamToFile((Reader)object);
237            } else if (object.getClass().isArray()) {
238                // create a customized gui to show the array
239                /*
240                  SwingCustomizedGUI gui = new SwingCustomizedGUI(true);
241                  gui.setSubPanesGeometry(2, Constants.HORIZONTAL, new boolean[] {false,true});
242                  CollectionWrapper c = new CollectionWrapper(Arrays.asList((Object[])object));
243                  gui.setObjectForPane(NameRepository.get().getName(c),0);
244                  gui.addReferenceToPane("org.objectweb.jac.aspects.gui.CollectionWrapper","collection",1);
245                  gui.applicationStarted();
246                  gui.setPosition(0,0,60,60);
247                  gui.show();
248                */
249            } else {
250                String name = NameRepository.get().getName(object);
251                logger.debug("name of "+object.getClass().getName()+" is "+name);
252                addViewFor(object,viewType,viewParams);
253            }
254        }
255    
256        public boolean showModal(Object object, 
257                                 String viewType, Object[] viewParams,
258                                 String title, String header, 
259                                 Object parent,
260                                 boolean okButton, 
261                                 boolean cancelButton, 
262                                 boolean closeButton) 
263        {
264            logger.debug("showModal("+
265                      (object!=null?object.getClass().getName():"null")+
266                      viewType+Arrays.asList(viewParams)+
267                      ","+title+",parent="+parent+")");
268            if (object==null) {
269                return addViewFor( 
270                    null,viewType,viewParams,
271                    title, header, parent,
272                    okButton, cancelButton, closeButton);
273            } else if (object instanceof CommitException) {
274                CommitException e = (CommitException)object;
275                showError(
276                    "Commit error",
277                    "Failed to set value of "+e.getField()+
278                    " on "+GuiAC.toString(e.getObject())+
279                    ": "+e.getNested().getMessage());
280                return true;
281            } else if (object.getClass().isArray()) {
282                return addViewFor(
283                    Arrays.asList((Object[])object),viewType,viewParams,
284                    title, header, parent,
285                    okButton, cancelButton, closeButton);
286            } else {
287                return addViewFor(
288                    object,viewType,viewParams,
289                    title, header, parent,
290                    okButton, cancelButton, closeButton);
291            }
292        }
293    
294        public boolean showModal(Object object, String title, String header, 
295                                 Object parent,
296                                 boolean okButton, 
297                                 boolean cancelButton, 
298                                 boolean closeButton) 
299        {
300            return showModal(object,
301                             "Object",new String[] {GuiAC.DEFAULT_VIEW},
302                             title,header,
303                             parent,
304                             okButton,cancelButton,closeButton);
305        }
306    
307        public void openView(Object object) {
308            logger.debug("openView("+object.getClass().getName()+")");
309            show(object);
310        }
311    
312        public boolean showInput(Object substance, AbstractMethodItem method, 
313                                 Object[] parameters) 
314        {
315            logger.debug("showInput("+method.getName()+
316                      ","+Arrays.asList(parameters)+")");
317            DisplayContext dc = (DisplayContext)Collaboration.get()
318                .getAttribute(GuiAC.DISPLAY_CONTEXT);
319            if (dc==null) {
320                dc = new DisplayContext(this,null);
321            }
322    
323            DialogView page = GenericFactory.createInputDialog(substance,
324                                                               method,parameters,dc);
325            if (page.waitForClose()) {
326                EditorContainer inputView = (EditorContainer)page.getContentView();
327                Iterator it = inputView.getEditors().iterator();
328                int i=0;
329                while (it.hasNext()) {
330                    if (method.getParameterTypes()[i] != DisplayContext.class) {
331                        FieldEditor editor = (FieldEditor)it.next();
332                        method.setParameter(parameters,i,editor.getValue());
333                    }
334                    i++;
335                }
336                return true;
337            } else {
338                return false;
339            }
340        }
341    
342        String displayID;
343    
344        public String getDisplayID() {
345            return displayID;
346        }
347    
348        public void setDisplayID(String displayID) {
349            this.displayID = displayID;
350        }
351    
352        public boolean showMessage(String message, String title,
353                                   boolean okButton, 
354                                   boolean cancelButton, 
355                                   boolean closeButton ) 
356        {
357            DisplayContext context = 
358                (DisplayContext)Collaboration.get().getAttribute(
359                    GuiAC.DISPLAY_CONTEXT);
360            return showModal(null,title,message,context.getWindow(),
361                             okButton,cancelButton,closeButton);
362        }
363    
364        public void showMessage(String message, String title) {
365            showMessage(message,title,false,false,true);
366        }
367    
368        public Object showRefreshMessage(String message, String title) {
369            View page;
370            try {
371                logger.debug("showMessage("+title+","+message+")");
372                DisplayContext context = new DisplayContext(this,null);
373                View label = factory.createView(message,"Label",
374                                                new Object[] {},context);
375                page = factory.createView("Object view","Window",
376                                          new Object[] {label},context);
377            } finally {
378                refresh();
379            }
380            return page;
381        }
382    
383        public void showError(String message, String title) {
384            showMessage(title,message);      
385        }
386    
387        public void refresh() {}
388    
389        public void applicationStarted() {
390        }
391       
392        public void close() {
393            // close all customized guis
394            Iterator i = frames.values().iterator();
395            while(i.hasNext()) {
396                View view = (View)i.next();
397                view.close(true);
398            }
399        }   
400    
401        /** show a save dialog and save the stream into the selected file 
402        * @param reader the stream to save
403        */
404        public void saveStreamToFile(Reader reader) 
405        {
406            JFileChooser chooser = new JFileChooser();
407            if (JFileChooser.APPROVE_OPTION == chooser.showSaveDialog(null)) {
408                try {
409                    File file = chooser.getSelectedFile();
410                    logger.debug("saving stream to "+file.getAbsolutePath());
411                    OutputStreamWriter writer = new OutputStreamWriter(
412                        new FileOutputStream(file),"UTF-8");
413                    int b = reader.read();
414                    while (b != -1) {
415                        writer.write(b);
416                        b = reader.read();
417                    }
418                    writer.close();
419                } catch (IOException e) {
420                    logger.error("saveStreamToFile() failed",e);
421                }
422            }
423        }
424    
425        public boolean addViewFor(final Object substance) {
426            return addViewFor(substance, null, null, null,
427                              false, false, true);
428        }
429    
430        public boolean addViewFor(final Object substance,
431                                  String viewType, Object[] viewParams) {
432            return addViewFor(substance,
433                              viewType,viewParams,
434                              null,null,null,
435                              false,false,true);
436        }
437    
438        public boolean addViewFor(Object substance,
439                                  String title, String header, 
440                                  Object parent,
441                                  boolean okButton, boolean cancelButton,
442                                  boolean closeButton) 
443        {
444            return addViewFor(substance,
445                              "Object",new String[] {GuiAC.DEFAULT_VIEW},
446                              title,header,parent,
447                              okButton,cancelButton,closeButton);
448        }
449        /**
450         * Adds a view for a given Jac object.<p>
451         *
452         * @param substance the object to add a view for 
453         * @return true if the OK button or the close button were clicked.
454         */
455        public boolean addViewFor(Object substance,
456                                  String viewType, Object[] viewParams,
457                                  String title, String header, 
458                                  Object parent,
459                                  boolean okButton, boolean cancelButton,
460                                  boolean closeButton) 
461        {
462            logger.debug("addViewFor: parent="+parent);
463            if (title == null) {
464                if (substance!=null) {
465                    Class substance_type = substance.getClass();
466                    if(substance_type==String.class) {
467                        title = "Message" + " -" +
468                            org.objectweb.jac.core.dist.Distd.getLocalContainerName() + "-";
469                    } else {
470                        String tn = substance_type.getName();            
471                        title = tn.substring( tn.lastIndexOf('.') + 1) + " " +
472                            GuiAC.toString( substance ) + " -" +
473                            org.objectweb.jac.core.dist.Distd.getLocalContainerName() + "-";
474                    }
475                } else {
476                    if (title == null) {
477                        title= "<null> -" +
478                            org.objectweb.jac.core.dist.Distd.getLocalContainerName() + "-";
479                    }
480                }
481            }
482    
483            ObjectViewDialog view = null;
484            try {
485                DisplayContext context = new DisplayContext(this,null);
486                View objectView = 
487                    substance==null ? null : factory.createView(
488                        "object",viewType,ExtArrays.add(substance,viewParams),context);
489    
490                if (parent==null)
491                    view = new ObjectViewDialog(
492                        objectView,title,header,okButton,cancelButton,closeButton,context);
493                else if (parent instanceof Dialog)
494                    view = new ObjectViewDialog(
495                        objectView,title,header,(Dialog)parent,
496                        okButton,cancelButton,closeButton,context);
497                else if (parent instanceof Frame)
498                    view = new ObjectViewDialog(
499                        objectView,title,header,(Frame)parent,
500                        okButton,cancelButton,closeButton,context);
501    
502                context.setWindow(view);
503                return view.ok;
504    
505            } catch (Exception e) {
506                e.printStackTrace();
507                return false;
508            }
509    
510        }
511    
512        public boolean fillParameters(AbstractMethodItem method, Object[] parameters) {
513            return false;
514        }
515    
516        public void onInvocationReturn(Object substance, AbstractMethodItem method) {
517        }
518    }