* Bug #8157 fixed - Under Mac OS X, item "Scilab/Quit Scilab" and "File/Quit" were
[scilab.git] / scilab / modules / gui / src / java / org / scilab / modules / gui / bridge / window / SwingScilabWindow.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2007 - INRIA - Vincent Couvert
4  * Copyright (C) 2007 - INRIA - Bruno JOFRET
5  * Copyright (C) 2007 - INRIA - Marouane BEN JELLOUL
6  * Copyright (C) 2009 - DIGITEO - Sylvestre LEDRU (Mac OS X port)
7  * Copyright (C) 2011 - DIGITEO - Vincent Couvert
8  *
9  * This file must be used under the terms of the CeCILL.
10  * This source file is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution.  The terms
12  * are also available at
13  * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
14  *
15  */
16
17 package org.scilab.modules.gui.bridge.window;
18
19 import org.flexdock.docking.Dockable;
20 import org.flexdock.docking.DockingManager;
21 import org.flexdock.docking.DockingPort;
22 import org.flexdock.docking.activation.ActiveDockableTracker;
23 import org.flexdock.docking.defaults.DefaultDockingPort;
24 import org.flexdock.docking.defaults.DefaultDockingStrategy;
25 import org.flexdock.docking.drag.effects.EffectsManager;
26 import org.flexdock.docking.drag.preview.GhostPreview;
27 import org.scilab.modules.action_binding.InterpreterManagement;
28 import org.scilab.modules.commons.gui.ScilabKeyStroke;
29 import org.scilab.modules.gui.bridge.menubar.SwingScilabMenuBar;
30 import org.scilab.modules.gui.bridge.tab.SwingScilabTab;
31 import org.scilab.modules.gui.bridge.textbox.SwingScilabTextBox;
32 import org.scilab.modules.gui.bridge.toolbar.SwingScilabToolBar;
33 import org.scilab.modules.gui.menubar.MenuBar;
34 import org.scilab.modules.gui.menubar.SimpleMenuBar;
35 import org.scilab.modules.gui.tab.SimpleTab;
36 import org.scilab.modules.gui.textbox.SimpleTextBox;
37 import org.scilab.modules.gui.textbox.TextBox;
38 import org.scilab.modules.gui.toolbar.SimpleToolBar;
39 import org.scilab.modules.gui.toolbar.ToolBar;
40 import org.scilab.modules.gui.utils.ClosingOperationsManager;
41 import org.scilab.modules.gui.utils.Position;
42 import org.scilab.modules.gui.utils.SciDockingListener;
43 import org.scilab.modules.gui.utils.ScilabSwingUtilities;
44 import org.scilab.modules.gui.utils.Size;
45 import org.scilab.modules.gui.window.SimpleWindow;
46
47 import javax.swing.ImageIcon;
48 import javax.swing.JComponent;
49 import javax.swing.JFrame;
50 import javax.swing.SwingUtilities;
51 import javax.swing.WindowConstants;
52 import java.awt.Dimension;
53 import java.awt.Frame;
54 import java.awt.event.ActionEvent;
55 import java.awt.event.ActionListener;
56 import java.awt.event.WindowAdapter;
57 import java.awt.event.WindowEvent;
58 import java.lang.reflect.InvocationTargetException;
59 import java.rmi.server.UID;
60 import java.util.Collections;
61 import java.util.HashMap;
62 import java.util.Iterator;
63 import java.util.Map;
64 import java.util.Set;
65 import java.util.UUID;
66
67 /**
68  * Swing implementation for Scilab windows in GUIs
69  * This implementation uses FlexDock package
70  * @author Vincent COUVERT
71  * @author Bruno JOFRET
72  * @author Marouane BEN JELLOUL
73  * @author Sylvestre LEDRU (Mac OS X port)
74
75  */
76 public class SwingScilabWindow extends JFrame implements SimpleWindow {
77
78     private static final long serialVersionUID = -5661926417765805660L;
79
80     private static final int DEFAULTWIDTH = 500;
81     private static final int DEFAULTHEIGHT = 500;
82
83     public static Map<String, SwingScilabWindow> allScilabWindows = Collections.synchronizedMap(new HashMap<String, SwingScilabWindow>());
84
85     static {
86         DefaultDockingStrategy.setDefaultResizeWeight(0.5);
87         DefaultDockingStrategy.keepConstantPercentage(true);
88     }
89
90     private DefaultDockingPort sciDockingPort;
91     private SciDockingListener sciDockingListener;
92     private SimpleMenuBar menuBar;
93     private SimpleToolBar toolBar;
94     private SimpleTextBox infoBar;
95     private String uuid;
96     private int elementId; // the id of the Window which contains this SimpleWindow
97     private String windowUID;
98     private final boolean MAC_OS_X = (System.getProperty("os.name").toLowerCase().startsWith("mac os x"));
99
100     /**
101      * Constructor
102      */
103     public SwingScilabWindow() {
104         super();
105         this.uuid = UUID.randomUUID().toString();
106
107         setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
108
109         // By default ctrl+w close the window
110         ActionListener listener = new ActionListener() {
111             @Override
112             public void actionPerformed(ActionEvent e) {
113                 processWindowEvent(new WindowEvent(SwingScilabWindow.this, WindowEvent.WINDOW_CLOSING));
114             }
115         };
116         getRootPane().registerKeyboardAction(listener, ScilabKeyStroke.getKeyStroke("OSSCKEY W"), JComponent.WHEN_IN_FOCUSED_WINDOW);
117
118         // TODO : Only for testing : Must be removed
119         this.setDims(new Size(DEFAULTWIDTH, DEFAULTHEIGHT));
120         this.setTitle("Scilab");
121         setIconImage(new ImageIcon(ScilabSwingUtilities.findIcon("scilab", "256x256")).getImage());
122
123         /* defining the Layout */
124         super.setLayout(new java.awt.BorderLayout());
125
126         /* Create automatically a docking port associated to the window */
127         sciDockingPort = new DefaultDockingPort();
128
129         EffectsManager.setPreview(new GhostPreview());
130
131         /* The docking port is the center of the Layout of the Window */
132         super.add(sciDockingPort, java.awt.BorderLayout.CENTER);
133
134         /* there is no menuBar, no toolBar and no infoBar at creation */
135         this.menuBar = null;
136         this.toolBar = null;
137         this.infoBar = null;
138
139         sciDockingListener = new SciDockingListener();
140         sciDockingPort.addDockingListener(sciDockingListener);
141
142         /*
143          * Prevent the background RootPane to catch Focus.
144          * Causes trouble with Scicos use xclick & co.
145          */
146         //this.setFocusable(false);
147
148         // let the OS choose the window position if not specified by user.
149         setLocationByPlatform(true);
150
151         addWindowListener(new WindowAdapter() {
152             @Override
153             public void windowClosing(WindowEvent e) {
154                 ClosingOperationsManager.startClosingOperation(SwingScilabWindow.this);
155             }
156         });
157
158         if (MAC_OS_X) {
159             registerForMacOSXEvents();
160         }
161
162         windowUID = new UID().toString();
163
164         sciDockingListener.setAssociatedWindowId(windowUID);
165
166         allScilabWindows.put(windowUID, this);
167     }
168
169     /**
170      * This method registers some methods against the specific Mac OS X API
171      * (in order to set the "special" mac os x menus)
172      */
173     private void registerForMacOSXEvents() {
174         try {
175             // Generate and register the OSXAdapter, passing it a hash of all the methods we wish to
176             // use as delegates for various com.apple.eawt.ApplicationListener methods
177             OSXAdapter.setAboutHandler(this, getClass().getDeclaredMethod("macosxAbout", (Class[]) null));
178             OSXAdapter.setQuitHandler(this, getClass().getDeclaredMethod("macosxQuit", (Class[]) null));
179             OSXAdapter.setPreferencesHandler(this, getClass().getDeclaredMethod("macosxPreferences", (Class[]) null));
180             OSXAdapter.setDockIcon(new ImageIcon(ScilabSwingUtilities.findIcon("puffin", "256x256")));
181         } catch (java.lang.NoSuchMethodException e) {
182             System.err.println("OSXAdapter could not find the method: " + e.getLocalizedMessage());
183         }
184     }
185
186     /**
187      * This method is called by the OSXAdapter class when the specific Mac
188      * OS X "About" menu is called. It is the only case where this method
189      * should be used
190      */
191     public void macosxAbout() {
192         InterpreterManagement.requestScilabExec("about();");
193     }
194
195     /**
196      * This method is called by the OSXAdapter class when the specific Mac
197      * OS X "Quit Scilab" menu is called. It is the only case where this method
198      * should be used
199      */
200     public void macosxQuit() {
201         InterpreterManagement.requestScilabExec("exit();");
202     }
203
204     /**
205      * This method is called by the OSXAdapter class when the specific Mac
206      * OS X "Preferences" menu is called. It is the only case where this method
207      * should be used
208      */
209     public void macosxPreferences() {
210         InterpreterManagement.requestScilabExec("preferences();");
211     }
212
213     /**
214      * @return the UUID associated with this window
215      */
216     public String getUUID() {
217         return uuid;
218     }
219
220     /**
221      * @param uuid the UUID associated with this window
222      */
223     public void setUUID(String uuid) {
224         this.uuid = uuid;
225     }
226
227     /**
228      * Creates a swing Scilab window
229      * @return the created window
230      */
231     public static SimpleWindow createWindow() {
232         return new SwingScilabWindow();
233     }
234
235     /**
236      * Draws a swing Scilab window
237      * @see org.scilab.modules.gui.uielement.UIElement#draw()
238      */
239     @Override
240     public void draw() {
241         this.setVisible(true);
242         this.doLayout();
243     }
244
245     /**
246      * Private method to raise to the front the window
247      */
248     private void raiseToFront() {
249         // force visibility
250         setVisible(true);
251
252         // deiconify the window if needed
253         setState(NORMAL);
254
255         // put it in front of others
256         toFront();
257     }
258
259     /**
260      * Deiconify the window and put it in front of other window
261      */
262     @Override
263     public void raise() {
264         // blocking call. So graphic synchronization must be desactivated here.
265         if (!SwingUtilities.isEventDispatchThread()) {
266             /* javasci bug: See bug 9544 why we are doing this check */
267             try {
268                 SwingUtilities.invokeAndWait(new Runnable() {
269                     @Override
270                     public void run() {
271                         raiseToFront();
272                     }
273                 });
274             } catch (InterruptedException e) {
275                 e.printStackTrace();
276             } catch (InvocationTargetException e) {
277                 e.printStackTrace();
278             }
279         } else {
280             raiseToFront();
281         }
282     }
283
284     /**
285      * Gets the dimensions (width and height) of a swing Scilab window
286      * @return the dimensions of the window
287      * @see org.scilab.modules.gui.uielement.UIElement#getDims()
288      */
289     @Override
290     public Size getDims() {
291         return new Size(getSize().width, getSize().height);
292     }
293
294     /**
295      * Sets the dimensions (width and height) of a swing Scilab window
296      * @param newWindowSize the dimensions to set to the window
297      * @see org.scilab.modules.gui.uielement.UIElement#setDims(org.scilab.modules.gui.utils.Size)
298      */
299     @Override
300     public void setDims(Size newWindowSize) {
301         //if (!SwingUtilities.isEventDispatchThread()) {
302         if (getDims().getWidth() != newWindowSize.getWidth() || getDims().getHeight() != newWindowSize.getHeight()) {
303             Dimension finalDim = new Dimension(newWindowSize.getWidth(), newWindowSize.getHeight());
304
305             setSize(finalDim);
306             // validate so the new values are taken into account immediately
307             validate();
308         }
309         //}
310     }
311
312     /**
313      * Gets the position (X-coordinate and Y-coordinate) of a swing Scilab window
314      * @return the position of the window
315      * @see org.scilab.modules.gui.uielement.UIElement#getPosition()
316      */
317     @Override
318     public Position getPosition() {
319         return new Position(this.getX(), this.getY());
320     }
321
322     /**
323      * Sets the position (X-coordinate and Y-coordinate) of a swing Scilab window
324      * @param newWindowPosition the position to set to the window
325      * @see org.scilab.modules.gui.uielement.UIElement#setPosition(org.scilab.modules.gui.utils.Position)
326      */
327     @Override
328     public void setPosition(Position newWindowPosition) {
329         //if (!SwingUtilities.isEventDispatchThread()) {
330         if (getPosition().getX() != newWindowPosition.getX() || getPosition().getY() != newWindowPosition.getY()) {
331             this.setLocation(newWindowPosition.getX(), newWindowPosition.getY());
332         }
333         //}
334     }
335
336     /**
337      * Gets the title of a swing Scilab window
338      * @return the title of the window
339      * @see javax.swing.JFrame#getTitle()
340      */
341     @Override
342     public String getTitle() {
343         return super.getTitle();
344     }
345
346     /**
347      * Sets the title of a swing Scilab window
348      * @param newWindowTitle the title to set to the window
349      * @see java.awt.Frame#setTitle(java.lang.String)
350      */
351     @Override
352     public void setTitle(String newWindowTitle) {
353         // set only if required
354         if (newWindowTitle != null && !newWindowTitle.equals(getTitle())) {
355             super.setTitle(newWindowTitle);
356         }
357     }
358
359     /**
360      * {@inheritedDoc}
361      */
362     @Override
363     public void setName(String name) {
364         super.setName(name);
365         setTitle(name);
366     }
367
368     /**
369      * Gets the docking port associated to the window (created by default at window creation)
370      * @return the docking port associated to the window
371      */
372     public DockingPort getDockingPort() {
373         //return (DockingPort) centerFrame.getContentPane();
374         return sciDockingPort;
375     }
376
377     /**
378      * Add a Scilab tab to a Scilab window
379      * @param newTab the Scilab tab to add to the Scilab window
380      * @see org.scilab.modules.gui.window.Window#addTab(org.scilab.modules.gui.tab.Tab)
381      */
382     @Override
383     public void addTab(SimpleTab newTab) {
384         SwingScilabTab tab = (SwingScilabTab) newTab;
385         tab.setParentWindowId(this.windowUID);
386         DockingManager.dock(tab, this.getDockingPort());
387         ActiveDockableTracker.requestDockableActivation(tab);
388     }
389
390     /**
391      * Remove a Scilab tab from a Scilab window
392      * @param tabs the Scilab tabs to remove from the Scilab window
393      * @see org.scilab.modules.gui.window.Window#removeTab(org.scilab.modules.gui.tab.Tab)
394      */
395     public void removeTabs(SwingScilabTab[] tabs) {
396         for (SwingScilabTab tab : tabs) {
397             DockingManager.unregisterDockable((Dockable) tab);
398             tab.close();
399             DockingManager.close(tab);
400         }
401         if (getDockingPort().getDockables().isEmpty()) {
402             // remove xxxBars
403             if (toolBar != null) {
404                 ((SwingScilabToolBar) toolBar).close();
405             }
406             if (menuBar != null) {
407                 ((SwingScilabMenuBar) menuBar).close();
408             }
409
410             // clean all
411             this.removeAll();
412             close();
413
414             // disable docking port
415             ActiveDockableTracker.getTracker(this).setActive(null);
416             sciDockingPort.removeDockingListener(sciDockingListener);
417             sciDockingPort = null;
418             sciDockingListener = null;
419         } else {
420             /* Make sur a Tab is active */
421             Set<SwingScilabTab> docks = sciDockingPort.getDockables();
422             Iterator<SwingScilabTab> it = docks.iterator();
423             ActiveDockableTracker.requestDockableActivation(it.next());
424         }
425     }
426
427     /**
428      * Remove a Scilab tab from a Scilab window
429      * @param tab the Scilab tab to remove from the Scilab window
430      * @see org.scilab.modules.gui.window.Window#removeTab(org.scilab.modules.gui.tab.Tab)
431      */
432     @Override
433     public void removeTab(SimpleTab tab) {
434         removeTabs(new SwingScilabTab[] {(SwingScilabTab) tab});
435     }
436
437     /**
438      * Sets a Scilab MenuBar to a Scilab window
439      * @param newMenuBar the Scilab MenuBar to add to the Scilab window
440      * @see org.scilab.modules.gui.window.Window#addMenuBar(org.scilab.modules.gui.menubar.MenuBar)
441      */
442     @Override
443     public void addMenuBar(MenuBar newMenuBar) {
444         if (newMenuBar == null) {
445             if (this.menuBar != null) {
446                 this.menuBar = null;
447                 super.setJMenuBar(null);
448             }
449             // else nothing to do both are null
450         } else {
451             if (this.menuBar != newMenuBar.getAsSimpleMenuBar()) {
452                 this.menuBar = newMenuBar.getAsSimpleMenuBar();
453                 super.setJMenuBar((SwingScilabMenuBar) newMenuBar.getAsSimpleMenuBar());
454             }
455             //  else nothing to do element alredy set
456         }
457     }
458
459     /**
460      * Sets a Scilab ToolBar to a Scilab window
461      * @param newToolBar the Scilab ToolBar to set to the Scilab window
462      * @see org.scilab.modules.gui.window.Window#addToolBar(org.scilab.modules.gui.toolbar.ToolBar)
463      */
464     @Override
465     public void addToolBar(ToolBar newToolBar) {
466         if (newToolBar == null) {
467             if (this.toolBar != null) {
468                 // Remove old InfoBar if already set
469                 super.remove((SwingScilabToolBar) this.toolBar);
470                 this.toolBar = null;
471             }
472             // else nothing to do both are null
473         } else {
474             if (this.toolBar != newToolBar.getAsSimpleToolBar()) {
475                 if (this.toolBar != null) {
476                     // Remove old InfoBar if already set
477                     super.remove((SwingScilabToolBar) this.toolBar);
478                 }
479                 this.toolBar = newToolBar.getAsSimpleToolBar();
480                 super.add((SwingScilabToolBar) this.toolBar, java.awt.BorderLayout.PAGE_START);
481             }
482             //  else nothing to do element alredy set
483         }
484     }
485
486     /**
487      * Sets a Scilab InfoBar to a Scilab window
488      * @param newInfoBar the Scilab InfoBar to set to the Scilab window
489      * @see org.scilab.modules.gui.window.Window#addInfoBar(org.scilab.modules.gui.textbox.TextBox)
490      */
491     @Override
492     public void addInfoBar(TextBox newInfoBar) {
493         if (newInfoBar == null) {
494             if (this.infoBar != null) {
495                 // Remove old InfoBar if already set
496                 super.remove((SwingScilabTextBox) this.infoBar);
497                 this.infoBar = null;
498             }
499             // else nothing to do both are null
500         } else {
501             if (this.infoBar != newInfoBar.getAsSimpleTextBox()) {
502                 if (this.infoBar != null) {
503                     // Remove old InfoBar if already set
504                     super.remove((SwingScilabTextBox) this.infoBar);
505                 }
506                 this.infoBar = newInfoBar.getAsSimpleTextBox();
507                 super.add((SwingScilabTextBox) this.infoBar, java.awt.BorderLayout.PAGE_END);
508             }
509             //  else nothing to do element alredy set
510         }
511     }
512
513     /**
514      * Get the element id for this window
515      * @return id the id of the corresponding window object
516      */
517     @Override
518     public int getElementId() {
519         return elementId;
520     }
521
522     /**
523      * Set the element id for this window
524      * @param id the id of the corresponding window object
525      */
526     @Override
527     public void setElementId(int id) {
528         this.elementId = id;
529         //sciDockingListener.setAssociatedWindowId(id);
530     }
531
532     /**
533      * Close the window
534      * @see org.scilab.modules.gui.window.SimpleWindow#close()
535      */
536     @Override
537     public void close() {
538         try {
539             dispose();
540         } catch (IllegalStateException e) {
541             enableInputMethods(false);
542         }
543         allScilabWindows.remove(windowUID);
544     }
545
546     /**
547      * @return number of objects (tabs) docked in this window
548      */
549     @Override
550     public int getNbDockedObjects() {
551         return sciDockingPort.getDockables().size();
552     }
553
554     /**
555      * Update the dimension of the window and its component.
556      * Only useful when the window is not yet visible
557      */
558     @Override
559     public void updateDimensions() {
560         pack();
561     }
562
563     /**
564      * DeIconify Window
565      */
566     @Override
567     public void windowDeiconified() {
568         super.setState(Frame.NORMAL);
569     }
570
571     /**
572      * Iconify Window
573      */
574     @Override
575     public void windowIconified() {
576         super.setState(Frame.ICONIFIED);
577     }
578
579     /**
580      * Maximized Window
581      */
582     @Override
583     public void windowMaximized() {
584         super.setExtendedState(Frame.MAXIMIZED_BOTH);
585     }
586
587     /**
588      * Window is in the "normal" state.
589      */
590     @Override
591     public void windowNormal() {
592         super.setState(Frame.NORMAL);
593     }
594
595     /**
596      * Get the window UID
597      * @return the UID
598      */
599     public String getId() {
600         return windowUID;
601     }
602 }