Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: jpicedt/ui/internal/InternalFrameMDIManager.java


1   /*
2    MDIManager.java - January 1, 2002 - jPicEdt 1.3.2, a picture editor for LaTeX.
3    Copyright (C) 1999-2002 Sylvain Reynal
4   
5    Département de Physique
6    Ecole Nationale Supérieure de l'Electronique et de ses Applications (ENSEA)
7    6, avenue du Ponceau
8    F-95014 CERGY CEDEX
9   
10   Tel : +33 130 736 245
11   Fax : +33 130 736 667
12   e-mail : reynal@ensea.fr
13   jPicEdt web page : http://www.jpicedt.org
14    
15   This program is free software; you can redistribute it and/or
16   modify it under the terms of the GNU General Public License
17   as published by the Free Software Foundation; either version 2
18   of the License, or any later version.
19    
20   This program is distributed in the hope that it will be useful,
21   but WITHOUT ANY WARRANTY; without even the implied warranty of
22   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23   GNU General Public License for more details.
24    
25   You should have received a copy of the GNU General Public License
26   along with this program; if not, write to the Free Software
27   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
28   */
29  
30  package jpicedt.ui.internal;
31  
32  import jpicedt.JPicEdt;
33  import jpicedt.Version;
34  import jpicedt.MiscUtilities;
35  
36  import jpicedt.graphic.PECanvas;
37  import jpicedt.graphic.event.SelectionListener;
38  import jpicedt.graphic.event.SelectionEvent;
39  
40  import jpicedt.ui.MDIManager;
41  import jpicedt.ui.PEDrawingBoard;
42  import jpicedt.ui.util.SystemOutUtilities;
43  import jpicedt.ui.util.PEProgressBar;
44  import jpicedt.ui.action.ActionRegistry;
45  
46  import jpicedt.widgets.ModalInternalFrame;
47  
48  import javax.swing.*;
49  import java.awt.*;
50  import java.awt.event.*;
51  import java.beans.*;
52  import java.util.*;
53  import javax.swing.undo.*;
54  import javax.swing.event.*;
55  
56  /**
57   * An MDI Manager implementation for "childframe" mode (i.e. using JInternalFrame's)<p>
58   * <b>Layer management : </b>
59   * <ul>
60   * <li> PEDrawingBoard's are added to JDesktopPane.DEFAULT_LAYER (bottom most layer)
61   * <li> Unfortunately, with the current scheme used for action dispatching, only PEDrawingBoard's can
62   * be added to the JDesktopPane. Other components 
63   * (e.g. toolbars) CAN'T be used as a click on a toolbar belonging to, say,
64   * the PALETTE_LAYER, would immediately make it the active internal frame, and since action dispatching
65   * relies on the fact that the active internal frame is supposed to host a PEDrawingBoard, 
66   * this wouldn't work. Hence we now use floatable (dockable) toolbars instead, which belong to the
67   * glass-pane.
68   * </ul>
69   * </p>
70   * @since jPicEdt 1.3.2
71   * @author Sylvain Reynal
72   */
73  public class InternalFrameMDIManager extends MDIManager {
74  
75    private JFrame mainFrame;
76    private PEMenuBar menubar;
77    private PEToolBar mainToolbar; // shared actions, e.g. "Save", "Open", ...
78    private JDesktopPane desktopPane;
79    private PEDesktopManager desktopManager;
80    private SelectionHandler selectionHandler = new SelectionHandler(); // notifies dockable panels when a selection event is triggered by a drawing board
81    private UndoableEditHandler undoableEditHandler = new UndoableEditHandler();
82    private HashMap dockablePanelsMap = new HashMap(); // a hash table used to hold dockable panels titles
83    private PEPopupMenuFactory popupMenuFactory;
84    
85    public static final String KEY_DESKTOP_COLOR = "ui.desktop-color";
86    public static final Color desktopColorDEFAULT = new Color(106,105,207); // KDE2 !!!
87    public static final String KEY_GEOMETRY_X = "ui.geometry.x";
88    public static final String KEY_GEOMETRY_Y = "ui.geometry.y";
89    public static final String KEY_GEOMETRY_WIDTH = "ui.geometry.width";
90    public static final String KEY_GEOMETRY_HEIGHT = "ui.geometry.height";
91  
92    /**
93     * @param actionRegistry a registry of Action's to be used by menu-bars or tool-bars.
94     * @param progressBar The progress bar to inform of progress in the initialization ; can be null.
95     */
96    public InternalFrameMDIManager(PEProgressBar progressBar){
97  
98      // init LAF
99      jpicedt.ui.LAFManager.updateLaf();
100 
101     // add a PropertyChangeListener to the UIManager, so that
102     // every change in LAF leads to an immediate repainting of
103     // the whole component tree
104     UIManager.addPropertyChangeListener(new PropertyChangeListener(){
105                                           public void propertyChange(PropertyChangeEvent e) {
106                                             if (e.getPropertyName().equals("lookAndFeel")) {
107                                               JComponent c = (JComponent)mainFrame.getRootPane();
108                                               SwingUtilities.updateComponentTreeUI(c);
109                                               c.invalidate();  c.validate(); c.repaint();
110                                             }}});
111 
112     // init the application main frame
113     mainFrame = new JFrame("jPicEdt "+Version.getVersion());
114     mainFrame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
115     mainFrame.getContentPane().setLayout(new BorderLayout());
116     // allows JOptionPane to be instanciated with an implicit root frame
117     JOptionPane.setRootFrame(mainFrame);
118 
119     try {
120       // menubar
121       menubar=new PEMenuBar();
122       mainFrame.setJMenuBar(menubar);
123       menubar.updateRecentFilesSubmenu(); 
124       menubar.updateScriptsMenu();
125       menubar.updateFragmentsMenu();
126 
127       // main toolbar
128       mainToolbar=new PEToolBar();
129       mainFrame.getContentPane().add(mainToolbar, BorderLayout.NORTH);
130 
131       // desktop
132       desktopPane = new JDesktopPane();
133       desktopPane.setBackground(desktopColorDEFAULT);
134       desktopPane.setBorder(BorderFactory.createLineBorder(Color.black));
135       desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE); // JDK1.2 -> desktopPane.putClientProperty("JDesktopPane.dragMode","outline");
136       mainFrame.getContentPane().add(desktopPane, BorderLayout.CENTER);
137       int desktopPanePreferredWidth = (int)(Toolkit.getDefaultToolkit().getScreenSize().width * 0.9);
138       int desktopPanePreferredHeight = (int)(Toolkit.getDefaultToolkit().getScreenSize().height * 0.9);
139       desktopPane.setPreferredSize(new Dimension(desktopPanePreferredWidth,desktopPanePreferredHeight));
140       desktopManager=new PEDesktopManager();
141       desktopPane.setDesktopManager(desktopManager);
142     }
143     catch(MissingResourceException e){
144       System.err.println("During GUI initialization, I was unable to find internationalization resource: " + e.getKey());
145       System.err.println("JVM stack trace dump :");
146       e.printStackTrace();
147       System.err.println("In any case, you may need to check the integrity of the jpicedt.jar file you downloaded.");
148       System.err.println("If it still doesn't work, please send a bug report to : reynal@ensea.fr, with this console dump included.");
149       System.err.println(":-((((((((((  Exiting... sorry !");
150       SystemOutUtilities.instance().redirect(SystemOutUtilities.STANDARD); // close any open File Stream
151       if (progressBar != null) 
152         progressBar.fatalError(e.getMessage() + "\nSee "+ SystemOutUtilities.getErrorLogFile() + " for details."); //[todo] to localize
153       System.exit(0);
154     }
155     catch (Exception e) {
156       System.err.println("OK, there SURE is a bug somewhere... Thank you for sending a bug report including the following stack trace to syd@jpicedt.org");
157       System.err.println("Stack trace dump starts here :");
158       e.printStackTrace();
159       System.err.println(":-((((((((((  Exiting...sorry !");
160       SystemOutUtilities.instance().redirect(SystemOutUtilities.STANDARD);// close any open File Stream
161       if (progressBar != null) 
162         progressBar.fatalError(e.getMessage() + "\nSee "+ SystemOutUtilities.getErrorLogFile() + " for details."); //[todo] to localize
163       System.exit(0);
164     }
165     mainFrame.addWindowListener(new WindowAdapter(){
166                                   public void windowClosing(WindowEvent e){
167                                     new jpicedt.JPicEdt.ExitAction().actionPerformed(null);
168                                   }
169                                 });
170     mainFrame.validate();
171     // size and location on the screen :
172     Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
173     int x = JPicEdt.getProperty(KEY_GEOMETRY_X,(int)(0.05*screenSize.width));
174     int y = JPicEdt.getProperty(KEY_GEOMETRY_Y,(int)(0.05*screenSize.height));
175     int w = JPicEdt.getProperty(KEY_GEOMETRY_WIDTH,(int)(0.9*screenSize.width));
176     int h = JPicEdt.getProperty(KEY_GEOMETRY_HEIGHT,(int)(0.9*screenSize.height));
177     mainFrame.setSize(w,h);
178     mainFrame.setLocation(x,y);
179 
180     // init colours from pref.
181     update();
182     // init popup menu :
183     popupMenuFactory = new PEPopupMenuFactory();
184     if (progressBar != null) {
185       progressBar.increment("GUI created successfully !"); 
186       progressBar.destroy(); // close progress bar
187     }
188     
189     // setVisible "realizes" the main JFrame (i.e. create new Swing thread)
190     mainFrame.setVisible(true);
191   }
192 
193 
194   /**
195    * Update local properties from the JPicEdt's preferences (e.g. GUI colours, rendering-hints,...)
196    */
197   public void update(){
198     //desktopPane = null; // test error.log
199     // colours :
200     Color desktopColor = JPicEdt.getProperty(KEY_DESKTOP_COLOR,desktopColorDEFAULT);
201     desktopPane.setBackground(desktopColor);
202     if (mainFrame.isVisible()){
203       desktopPane.repaint();
204     }
205     // rendering hints :
206     RenderingHints rh = MiscUtilities.parseRenderingHints(JPicEdt.getPreferences());
207     PEDrawingBoard[] boards = getAllDrawingBoards();
208     for (int i=0; i<boards.length; i++){
209       boards[i].getCanvas().getRenderingHints().add(rh);
210       boards[i].getCanvas().repaint();
211     }
212     // [underway]
213     // updates colors (they might've been modified from inside Preference tabpane) :
214     //PEInternalFrame activeFrame = mdiManager.getSelectedDrawingBoard();
215     //if (activeFrame != null) activeFrame.getCanvas().repaint(); // Grid, text and bg colours may have changed
216   }
217 
218   /**
219    * Save UI geometry to JPicEdt's preferences.
220    */
221   public void saveGeometry(){
222     // save desktop geometry :
223     Properties preferences = JPicEdt.getPreferences();
224     preferences.setProperty(KEY_GEOMETRY_X,Integer.toString(mainFrame.getX()));
225     preferences.setProperty(KEY_GEOMETRY_Y,Integer.toString(mainFrame.getY()));
226     preferences.setProperty(KEY_GEOMETRY_WIDTH,Integer.toString(mainFrame.getWidth()));
227     preferences.setProperty(KEY_GEOMETRY_HEIGHT,Integer.toString(mainFrame.getHeight()));
228     // save dockable toolbars geometry :
229     Set keys = dockablePanelsMap.keySet();
230     for (Iterator it = keys.iterator(); it.hasNext();){
231       String key = (String)it.next();
232       DockablePanel tif = (DockablePanel)dockablePanelsMap.get(key);
233       if (tif == null) continue;
234       preferences.setProperty("ui."+key+".x",Integer.toString(tif.getX()));
235       preferences.setProperty("ui."+key+".y",Integer.toString(tif.getY()));
236       preferences.setProperty("ui."+key+".width",Integer.toString(tif.getWidth()));
237       preferences.setProperty("ui."+key+".height",Integer.toString(tif.getHeight()));
238       preferences.setProperty("ui."+key+".visible",tif.isVisible()?"true":"false");
239     }
240     
241   }
242 
243 
244   //////////////////////////////////////////////////////////////////////
245   //// GUI COMPONENTS
246   //////////////////////////////////////////////////////////////////////
247 
248 
249   /**
250    * Requests that the argument string  be displayed in the status bar
251    */
252   public void showStatus(String str){
253     PEDrawingBoard b = getSelectedDrawingBoard();
254     if (b!=null) b.getStatusBar().showMessage(str);
255   }
256 
257   /**
258    * update the menu (or menu-items) containing the list of currently open boards, so that
259    * a click on one of these items activate (i.e. brings to front) the corresponding board.
260    */
261   public void updateWindowMenu(){
262     menubar.updateWindowMenu();
263   }
264 
265   /**
266    * update the menu (or menu-items) containing the list of the most recently saved boards,
267    * so that a click on one of these items re-opens the corresponding board.
268    */
269   public void updateRecentFilesSubmenu(){
270     menubar.updateRecentFilesSubmenu();
271   }
272   
273   /**
274    * Update the menus (or menu-items, buttons,...) related to undo/redo action according to the given
275    * undoName and redoName labels.
276    */
277   public void updateUndoRedoMenus(String undoName,String redoName){
278     menubar.updateUndoRedoMenus(undoName,redoName);
279   }
280 
281   /**
282    * update the menu containing the list of installed BSH scripts
283    */
284   public void updateScriptsMenu(){
285     menubar.updateScriptsMenu();
286   }
287   
288 
289 
290   //////////////////////////////////////////////////////////////////////
291   //// BOARDS MANAGEMENT
292   //////////////////////////////////////////////////////////////////////
293 
294   /**
295    * Add the given PEDrawingBoard component to the JDesktopPane associated with this MDIManager.<br>
296    * PEDrawingBoard's are added to DRAWING_BOARD_LAYER. 
297    * @since jPicEdt 1.3.2
298    * @author Sylvain Reynal
299    */
300   public void addDrawingBoard(PEDrawingBoard board){
301 
302     BoardInternalFrame f = new BoardInternalFrame(board);
303     int nbFrame = getDrawingBoardCount();
304     //int dist = f.getHeight() - f.getContentPane().getHeight(); // title bar height DOESN'T WORK !
305     int dist=30;
306     f.setLocation(nbFrame * dist, nbFrame * dist); 
307     // !!! registering the selection-listener BEFORE adding the board to the DesktopPane allows dockable
308     // toolbars which are instances of SelectionListener to be notified that a new board has been activated,
309     // thus giving'em a chance to synchronize their state with that of the new active board.
310     board.getCanvas().addSelectionListener(selectionHandler);
311     desktopPane.add(f); // DEFAULT_LAYER (side effect : calls "activateFrame()")
312     f.pack();
313     f.setVisible(true);
314     
315     //  [pending] either move to JPicEdt, or to super-class
316     board.getCanvas().addUndoableEditListener(undoableEditHandler);
317     board.setPopupMenuFactory(popupMenuFactory);
318           
319   }
320     
321 
322   /**
323    * Remove the given drawing board from the list of open drawing boards, if it exists.<p>
324    * @since jPicEdt 1.3.2
325    * @author Sylvain Reynal
326    */
327   public void removeDrawingBoard(PEDrawingBoard board){
328     //  [pending] move to superclass
329     board.getCanvas().removeSelectionListener(selectionHandler);
330     board.getCanvas().removeUndoableEditListener(undoableEditHandler);
331     BoardInternalFrame f = getHostingFrame(board);
332     if (f == null) return; // this board isn't hosted by any internal frame in this MDIManager
333     //jpicedt.Log.debug(this,"removeDrawingBoard","iframe="+f.getTitle());
334     //f.setVisible(false);
335     //f.setClosed(true);
336     f.dispose();
337     desktopPane.remove(f);
338   }
339 
340   /**
341    * @return the currently selected drawing board ;  
342    * null if there's no internal frame in the desktopPane.<br>
343    * If there's at least one internal frame in the desktop, but it's deactivated 
344    * (this should never happen however), then the policy is to arbitrarly activate one frame 
345    * (namely the topmost frame on the desktop which is also the last one in the framearray)
346    * @since jPicEdt 1.3.2
347    * @author Sylvain Reynal
348    */
349   public PEDrawingBoard getSelectedDrawingBoard(){
350 
351     JInternalFrame jifr = desktopPane.getSelectedFrame(); // returns null if there's no frame or if none is activated
352     if (jifr != null){ // there's an active frame
353       BoardInternalFrame ifr = (BoardInternalFrame)jifr;
354       return ifr.getBoard();
355     }
356     // else either there's no frame, or none is active
357     JInternalFrame[] allFrames = desktopPane.getAllFrames();
358     if (allFrames.length==0) return null;
359     // we have at least one frame in the desktop,
360     // but none is activated, we activate (arbitrarly) one of them :
361     //jpicedt.Log.debug(this,"getSelectedDrawingBoard","NO ACTIVE FRAME ! Activating top-most..");
362     BoardInternalFrame ifr = (BoardInternalFrame)allFrames[allFrames.length-1]; // top-most frame
363     if (ifr.isIcon()) desktopManager.deiconifyFrame(ifr);
364     desktopManager.activateFrame(ifr); // same as if user clicked on the title bar
365     //ifr.show();
366     return ifr.getBoard();
367 
368   }
369 
370   /**
371    * select the given drawing board by bringing it to front.
372    * @since jPicEdt 1.3.2
373    * @author Sylvain Reynal
374    */
375   public void selectDrawingBoard(PEDrawingBoard board){
376 
377     JInternalFrame iFrame = getHostingFrame(board);
378     if (iFrame == null) return;
379     if (iFrame.isIcon()) desktopManager.deiconifyFrame(iFrame);
380     //iFrame.show(); // [pending] use JDesktopPane.setSelectedFrame(iFrame) JDK 1.3
381     //desktopPane.setSelectedFrame(iFrame);
382     desktopManager.activateFrame(iFrame); // simulate a user click on the title bar
383 
384   }
385 
386   /**
387    * @return all opened drawing boards
388    * @since jPicEdt 1.3.2
389    * @author Sylvain Reynal
390    */
391   public PEDrawingBoard[] getAllDrawingBoards(){
392 
393     JInternalFrame[] frameArray = desktopPane.getAllFrames();
394     PEDrawingBoard[] boardArray = new PEDrawingBoard[frameArray.length];
395     for (int frameIndex = 0; frameIndex < frameArray.length; frameIndex++){
396       BoardInternalFrame ifr = (BoardInternalFrame)frameArray[frameIndex];
397       boardArray[frameIndex] = ifr.getBoard();
398     }
399     return boardArray;
400   }
401 
402   /**
403    * @return the BoardInternalFrame that contains the given board
404    * @since jPicEdt 1.3.2
405    * @author Sylvain Reynal
406    */
407   private BoardInternalFrame getHostingFrame(PEDrawingBoard board){
408 
409     JInternalFrame[] allFrames = desktopPane.getAllFrames();
410     for (int i = 0; i < allFrames.length; i++){
411       BoardInternalFrame ifr = (BoardInternalFrame)allFrames[i]; // only BoardInternalFrame's in DRAWING_BOARD_LAYER
412       if (ifr.getBoard()==board) return ifr;
413     }
414     return null;
415   }
416 
417   /**
418    * @return the number of open boards
419    */
420   public int getDrawingBoardCount(){
421     return desktopPane.getComponentCountInLayer(JDesktopPane.DEFAULT_LAYER.intValue());
422   }
423 
424 
425 
426 
427 
428 
429   //////////////////////////////////////////////////////////////////////
430   //// DOCKABLE PANES
431   //////////////////////////////////////////////////////////////////////
432   /**
433    * Add the given pane to the hashtable of dockable panels, with the given key.
434    * If pane is an instance of SelectionListener, it will be notified selection events from any
435    * currently opened drawing board.
436    * @param key used to retrieve geometrical properties from the given Properties object, e.g.
437    *        "ui." + key + ".visible" for the visible state, etc...
438    */
439   public void addDockablePanel(String key, JPanel pane){
440     //jpicedt.Log.debug(this,"addDockablePanel","key="+key+",pane="+pane+",map="+dockablePanelsMap);
441     if (dockablePanelsMap.containsKey(key)) return;
442     dockablePanelsMap.put(key,new DockablePanel(pane,key));
443     // retrieve things saved last time by saveGeometry() :
444     boolean vis = JPicEdt.getProperty("ui."+key+".visible",true);
445     if (vis) showDockablePanel(key,true);
446   }
447   
448   /**
449    * Toggle the visible state of the dockable pane with the given key.
450    */
451   public void toggleDockablePanel(String key){
452     //jpicedt.Log.debug(this,"showDockablePanel","key="+key+",vis="+visible+",map="+dockablePanelsMap);
453     if (!dockablePanelsMap.containsKey(key)) return;
454     DockablePanel dp = (DockablePanel)dockablePanelsMap.get(key);
455     dp.setVisible(!dp.isVisible());
456   }
457 
458   /**
459    * Set the visible state of the dockable pane with the given key.
460    */
461   public void showDockablePanel(String key,boolean visible){
462     //jpicedt.Log.debug(this,"showDockablePanel","key="+key+",vis="+visible+",map="+dockablePanelsMap);
463     if (!dockablePanelsMap.containsKey(key)) return;
464     DockablePanel dp = (DockablePanel)dockablePanelsMap.get(key);
465     dp.setVisible(visible);
466   }
467 
468   /**
469    * Return a set containing all keys (of type "String") that map to a DockablePanel
470    */
471   public Set getDockablePanelKeys(){
472     return dockablePanelsMap.keySet();
473   }
474   
475   /**
476    * Return the inner pane contained in the dockable panel 
477    * associated with the given key, or null if none match.
478    */
479   public JPanel getDockablePanelFromKey(String key){
480     return ((DockablePanel)dockablePanelsMap.get(key)).getInnerPane();
481   }
482 
483   /**
484    * A wrapper for dockable panels [pending] these are added to the main frame's layered pane,
485    * hence there's no way to set aka OUTLINE drag-mode. Any clue ?
486    */
487   private class DockablePanel extends JInternalFrame {
488     
489     private JPanel content;
490     private boolean packed=false;
491     private String key;
492 
493     /**
494      * @param key used to set the frame title (after proper i18n'ing), and for building keys used
495      *         to retrieve the geometry from the given Properties object
496      * @param pane the content of this JInternalFrame
497      * @param preferences the Properties object used to init the geometry of the frame
498      */
499     public DockablePanel(JPanel pane,String key){
500       super(jpicedt.Localizer.currentLocalizer().get(key),true); // resizable
501       this.content = pane;
502       this.key = key;
503       getContentPane().add(pane);
504       setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); // the only way to close it is through a call to JPicEdt.ViewAttributeEditorAction
505       mainFrame.getLayeredPane().add(this,JLayeredPane.PALETTE_LAYER);
506     }
507     /** return the content pane of this internal frame */
508     public JPanel getInnerPane(){
509       return content;
510     }
511     /** 
512      * set the visible state of this internal frame, first trying to fetch the geometrical aspect from
513      * the Properties object given in the constructor. This is done on first call only (see "packed" flag).
514      */
515     public void setVisible(boolean v){
516       if (!packed && InternalFrameMDIManager.this!=null) {
517         pack();
518         packed=true;
519         // set default location :
520         Dimension mainFrameSize = mainFrame.getSize();
521         Dimension iframeSize = getSize();
522         // crop panel size to main frame size if necessary :
523         if (iframeSize.height > mainFrameSize.height) iframeSize.height = mainFrameSize.height;
524         if (iframeSize.width > mainFrameSize.width) iframeSize.width = mainFrameSize.width;
525         int x = mainFrameSize.width - iframeSize.width;
526         int y = (mainFrameSize.height - iframeSize.height) / 2;
527         //jpicedt.Log.debug(this,"DockablePanel : x="+x+",y="+y);
528         setLocation(x,y);
529         // let's try to override it with preferences :
530         x = JPicEdt.getProperty("ui."+key+".x",x);
531         y = JPicEdt.getProperty("ui."+key+".y",y);
532         int w = JPicEdt.getProperty("ui."+key+".width",getSize().width);
533         int h = JPicEdt.getProperty("ui."+key+".height",getSize().height);
534         // work-around for null dimension inadvertently saved in the preference file :
535         if (w==0) w = getSize().width; 
536         if (h==0) h = getSize().height;
537         // avoid panel sticking out of the main frame area :
538         if (x+w > mainFrameSize.width) x = mainFrameSize.width - w;
539         if (y+h > mainFrameSize.height) y = mainFrameSize.height - h;
540         reshape(x,y,w,h);
541       }
542       super.setVisible(v);
543     }
544     
545   }
546   
547   
548   ///////////////////////////////////////////////////////////////////////
549   /////// MODAL DIALOG BOXES
550   ///////////////////////////////////////////////////////////////////////
551   /**
552    * [pending] experimental ! 1.3.2-beta9 ; not used yet.
553    */
554   public void showModalDialogBox(String title, JComponent innerPane){
555     ModalInternalFrame mif = new ModalInternalFrame(title, mainFrame.getRootPane(), desktopPane, innerPane);
556     mif.setVisible(true); // block I/O
557   }
558   
559   //////////////////////////////////////////////////////////////////////
560   //// GEOMETRY
561   //////////////////////////////////////////////////////////////////////
562 
563   /**
564    * Cascade all open internal frames
565    * @since jPicEdt 1.3.2
566    * @author Sylvain Reynal
567    */
568   public void cascadeDrawingBoards(){
569 
570     JInternalFrame[] allFrames = desktopPane.getAllFrames();
571     int counter=0;
572     for (int frameIndex = 0; frameIndex < allFrames.length; frameIndex++){
573 
574       JInternalFrame ifr = (JInternalFrame)allFrames[frameIndex];
575       //if (ifr.isIcon()) desktopManager.deiconifyFrame(ifr);
576       if (ifr.isIcon()) continue;
577       try {
578         ifr.setMaximum(false); // in case this one was Maximized (may fire a PropertyVetoException)
579         ifr.pack();
580         //ifr.show(); // already visible !
581         //int dist = ifr.getHeight() - ifr.getContentPane().getHeight(); // title bar height
582         int dist=30;
583         int x = counter * dist;
584         if (x + ifr.getWidth() > desktopPane.getWidth()) x = 0; // wrap around at desktop edge
585         int y = counter * dist;
586         if (y + ifr.getHeight() > desktopPane.getHeight()) y = 0; // wrap around at desktop edge
587         ifr.setLocation(x,y);
588         counter++;
589       }
590     catch (PropertyVetoException ex){}
591     }
592   }
593 
594   /**
595    * Tiles all open internal frames horizontally
596    * @since jPicEdt 1.3.2
597    * @author Sylvain Reynal
598    */
599   public void tileDrawingBoardsHorizontally(){
600 
601     JInternalFrame[] allFrames = desktopPane.getAllFrames();
602     ArrayList nonIconFrames = new ArrayList();
603     for(int i = 0; i < allFrames.length; i++){
604       if (!allFrames[i].isIcon()) {
605         try {
606           allFrames[i].setMaximum(false); // (may fire a PropertyVetoException)
607           nonIconFrames.add(allFrames[i]);
608         }
609         catch (PropertyVetoException ex){}
610       }
611     }
612     int nonIconNb = nonIconFrames.size();
613     if (nonIconNb == 0) return;
614 
615     // number of columns = int(sqrt(N))
616     int colNb = (int)Math.floor(Math.sqrt(nonIconNb));
617     int colWidth = desktopPane.getSize().width / colNb;
618 
619     int remaining = nonIconNb;
620 
621     for(int col = 0; col < colNb; col++){
622 
623       int numberOfFrameInThisColumn = (int)Math.floor(remaining / (colNb - col));
624       int rowHeight = desktopPane.getSize().height / numberOfFrameInThisColumn;
625 
626       for (int i = 0; i < numberOfFrameInThisColumn; i++){
627         JInternalFrame ifr = (JInternalFrame)nonIconFrames.get(nonIconNb-remaining);
628         ifr.reshape(col*colWidth, i * rowHeight , colWidth, rowHeight);
629         remaining--;
630       }
631     }
632   }
633 
634   /**
635    * Tiles all open internal frames vertically.
636    * @since jPicEdt 1.3.2
637    * @author Sylvain Reynal
638    */
639   public void tileDrawingBoardsVertically(){
640 
641     JInternalFrame[] allFrames = desktopPane.getAllFrames();
642     ArrayList nonIconFrames = new ArrayList();
643     for(int i = 0; i < allFrames.length; i++){
644       if (!allFrames[i].isIcon()) {
645         try {
646           allFrames[i].setMaximum(false); // (may fire a PropertyVetoException)
647           nonIconFrames.add(allFrames[i]);
648         }
649         catch (PropertyVetoException ex){}
650       }
651     }
652     int nonIconNb = nonIconFrames.size();
653     if (nonIconNb == 0) return;
654 
655     // number of row = int(sqrt(N)) where N = number of non-iconified frame
656     int rowNb = (int)Math.floor(Math.sqrt(nonIconNb));
657     int rowHeight = desktopPane.getSize().height / rowNb;
658     int remaining = nonIconNb;
659 
660     for(int rowIndex = 0; rowIndex < rowNb; rowIndex++){
661 
662       int numberOfFrameInThisRow = (int)Math.floor(remaining / (rowNb - rowIndex));
663       int colWidth = desktopPane.getSize().width / numberOfFrameInThisRow;
664 
665       for (int i = 0; i < numberOfFrameInThisRow; i++){
666         JInternalFrame ifr = (JInternalFrame)nonIconFrames.get(nonIconNb-remaining);
667         ifr.reshape(i*colWidth, rowIndex * rowHeight , colWidth, rowHeight);
668         remaining--;
669       }
670     }
671   }
672 
673 
674 
675 
676 
677 
678 
679   //////////////////////////////////////////////////////////////////////
680   //// MISC
681   //////////////////////////////////////////////////////////////////////
682 
683   /**
684    * set the title of the internal frame hosting the given board to the given string.
685    * @author Sylvain Reynal
686    * @since PicEdt 1.2.1
687    */
688   public void setHostingFrameTitle(String title, PEDrawingBoard board){
689 
690     JInternalFrame f = getHostingFrame(board);
691     if (f==null) return; // no frame hosting this board
692     f.setTitle(title);
693     f.revalidate();
694     f.repaint();
695   }
696 
697   ///////////////////////////////////////////////////
698   //// EVENT HANDLING
699   ///////////////////////////////////////////////////
700   
701   /**
702    * Handler for "Selection" events triggered e.g. by a click on a graphical object (from a given PEDrawingBoard)
703    * This marks the active drawing board as dirty, updates widgets states (i.e. menu items + toolbar buttons),
704    * possibly add a trailing "*" to the frame's title, and eventually notifies interested dockable toolbars
705    * that the drawing state changed (e.g. there's a selected graphical object filled in red -> change 
706    * the current active fill colour to red in the attributes editor palette).
707    */
708   private class SelectionHandler  implements SelectionListener{
709     public void selectionUpdate(SelectionEvent e){
710       //jpicedt.Log.debug(this,"selectionUpdate","evt="+e);
711       getSelectedDrawingBoard().setDirty(true); // moved from PEDrawingBoard ; see note in "PEDrawingBoard.init()"
712       JPicEdt.getActionRegistry().updateActionState();
713       // update hosting frame title
714       String title  = getSelectedDrawingBoard().getTitle() + "* [" + getSelectedDrawingBoard().getCanvas().getContentType().getPresentationName() + "]";
715       setHostingFrameTitle(title,getSelectedDrawingBoard());
716       // notifies dockable panels (that are instances of SelectionListener) 
717       // of possible changes in the active drawing :
718       Collection c = dockablePanelsMap.values();
719       for (Iterator it = c.iterator(); it.hasNext();){
720         JPanel p = ((DockablePanel)it.next()).getInnerPane();
721         if (p instanceof SelectionListener){
722           ((SelectionListener)p).selectionUpdate(e);
723         }
724       }
725     }
726   }
727   
728   /**
729    * Handler for "UndoableEdit" events sourced by PECanvas. Updates undo/redo menu items and
730    * corresponding toolbar buttons.
731    */
732   private class UndoableEditHandler implements UndoableEditListener {
733     public void undoableEditHappened(UndoableEditEvent e){
734       PEDrawingBoard board = getSelectedDrawingBoard();
735       if (board==null) return;
736       String redoName = board.getCanvas().getRedoPresentationName();
737       String undoName = board.getCanvas().getUndoPresentationName();
738       menubar.updateUndoRedoMenus(undoName,redoName);
739       JPicEdt.getActionRegistry().updateActionState();
740     }
741   }
742   
743   ////////////////////////////////////////////////////
744   /// INTERNAL FRAME
745   /////////////////////////////////////////////////////
746   /**
747    * InternalFrame wrapper for PEDrawingBoard's which allows to easily retrieve the internal frame content. 
748    */
749   private class BoardInternalFrame extends JInternalFrame {
750     private PEDrawingBoard board;
751 
752     BoardInternalFrame(PEDrawingBoard board){
753       super(board.getTitle(), true, true, true, true);
754       this.board = board;
755       this.getContentPane().add(board);
756     }
757 
758     PEDrawingBoard getBoard(){
759       return board;
760     }
761   }
762 
763   ////////////////////////////////////////////////////
764   /// DESKTOP MANAGER
765   /////////////////////////////////////////////////////
766   /**
767    * Handler for internal frame events
768    * @author Sylvain Reynal
769    * @since PicEdt 1.1.1
770    */
771   private class PEDesktopManager extends DefaultDesktopManager {
772 
773     /**
774      * Called when a frame gets activated
775      */
776     public void activateFrame(JInternalFrame f) {
777       //jpicedt.Log.debug(this,"activateFrame","iframe="+f.getTitle());
778       super.activateFrame(f);
779       BoardInternalFrame frame = (BoardInternalFrame)f;
780       PEDrawingBoard board = frame.getBoard();
781       //actionRegistry.updateActionState();
782       // terrible hack so that dockable toolbars which are instances of SelectionListener get notified
783       // when a new frame gets activated (note that selectAll(false) wouldn't work 100%, since 
784       // it has no effect if nothing is selected) :
785       board.getCanvas().selectAll(true);
786       board.getCanvas().selectAll(false);
787       // bug work-around ? 16/04/2002 but doesn't seem to work either. We still get two
788       // frames active at the same time, now and often...
789       //if (!f.isSelected()) try {f.setSelected(true);} catch (PropertyVetoException ex){}
790       //System.out.println("DesktopPane currently active frame="+((BoardInternalFrame)desktopPane.getSelectedFrame()));
791     }
792 
793     /**
794      * called when a frame gets deactivated
795      */
796     public void deactivateFrame(JInternalFrame f){
797       //jpicedt.Log.debug(this,"deactivateFrame","iframe="+f.getTitle());
798       super.deactivateFrame(f);
799       ((BoardInternalFrame)f).getBoard().getCanvas().selectAll(false); // we CAN'T have more than one active selection at a time !
800       //System.out.println("DesktopPane currently active frame="+((BoardInternalFrame)desktopPane.getSelectedFrame()));
801     }
802 
803     /**
804      * called when a frame gets closed
805      * if it has been modified since its last save, we ask if we need to save it.
806      * @param f frame getting closed
807      */
808     public void closeFrame(JInternalFrame f){
809       //jpicedt.Log.debug(this,"closeFrame","iframe="+f.getTitle());
810       BoardInternalFrame ifr = (BoardInternalFrame)f;
811       jpicedt.JPicEdt.closeBoard(ifr.getBoard()); // give a chance to save the board before closing
812       super.closeFrame(f);
813       //System.out.println("DesktopPane currently active frame="+((BoardInternalFrame)desktopPane.getSelectedFrame()));
814     }
815 
816 
817     /**
818      * used for debugging purpose
819      */
820     private String printAllFrames(){
821 
822       JInternalFrame[] jif = desktopPane.getAllFrames();
823       String s = "          Desktop contains : ";
824       for (int i = 0; i < jif.length; i++){
825         s += jif[i].getTitle() + "; ";
826       }
827       return s;
828     }
829 
830   } // PEDesktopManager
831 
832 } // class
833