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