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