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