2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2011 - Calixte DENIZET
5 * This file must be used under the terms of the CeCILL.
6 * This source file is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at
9 * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
13 package org.scilab.modules.gui.utils;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.ListIterator;
21 import java.util.UUID;
23 import javax.swing.Action;
25 import org.flexdock.docking.DockingConstants;
26 import org.scilab.modules.gui.bridge.tab.SwingScilabTab;
27 import org.scilab.modules.gui.bridge.window.SwingScilabWindow;
28 import org.scilab.modules.gui.messagebox.ScilabModalDialog;
29 import org.scilab.modules.gui.messagebox.ScilabModalDialog.AnswerOption;
30 import org.scilab.modules.gui.messagebox.ScilabModalDialog.ButtonType;
31 import org.scilab.modules.gui.messagebox.ScilabModalDialog.IconType;
32 import org.scilab.modules.gui.tab.Tab;
33 import org.scilab.modules.gui.tabfactory.ScilabTabFactory;
34 import org.scilab.modules.gui.window.Window;
35 import org.scilab.modules.localization.Messages;
38 * Class to handle the different closing operations.
40 * @author Calixte DENIZET
42 public class ClosingOperationsManager {
44 private static final String EXIT_CONFIRM = Messages.gettext("Are you sure you want to close %s ?");
45 private static final String EXIT_CONFIRM_AND = Messages.gettext("Are you sure you want to close %s and %s ?");
46 private static final String EXIT = Messages.gettext("Exit");
47 private static final String NULLUUID = new UUID(0L, 0L).toString();
48 private static final Map<SwingScilabTab, ClosingOperation> closingOps = new HashMap<SwingScilabTab, ClosingOperation>();
49 private static final Map<SwingScilabTab, List<SwingScilabTab>> deps = new HashMap<SwingScilabTab, List<SwingScilabTab>>();
51 private static SwingScilabTab root;
54 deps.put(null, new ArrayList<SwingScilabTab>());
58 * Register a closing operation for a tab
63 * the closing operation
65 public static void registerClosingOperation(SwingScilabTab tab,
66 ClosingOperation op) {
68 closingOps.put(tab, op);
73 * Register a closing operation for a tab
78 * the closing operation
80 public static void registerClosingOperation(Tab tab, ClosingOperation op) {
82 registerClosingOperation((SwingScilabTab) tab.getAsSimpleTab(), op);
87 * Start a closing operation on root
89 * @return true if the closing operation succeeded
91 public static boolean startClosingOperationOnRoot() {
94 SwingScilabWindow win = getWindow(root);
98 return startClosingOperation(win, true, true);
99 } else if (deps.get(null).size() != 0) {
101 List<SwingScilabTab> list = new ArrayList<SwingScilabTab>();
102 for (SwingScilabTab tab : deps.get(null)) {
103 collectTabsToClose(tab, list);
105 return close(list, null, true, true);
112 * Force a closing operation on root to dispose resources
114 public static void forceClosingOperationOnRoot() {
117 SwingScilabWindow win = getWindow(root);
121 startClosingOperation(win, false, false);
122 } else if (deps.get(null).size() != 0) {
124 List<SwingScilabTab> list = new ArrayList<SwingScilabTab>();
125 for (SwingScilabTab tab : deps.get(null)) {
126 collectTabsToClose(tab, list);
128 close(list, null, false, false);
133 * Start a closing operation on a tab
137 * @return true if the closing operation succeeded
139 public static boolean startClosingOperation(SwingScilabTab tab) {
140 return close(collectTabsToClose(tab), getWindow(tab), true, true);
144 * Start a closing operation on a tab
148 * @return true if the closing operation succeeded
150 public static boolean startClosingOperation(Tab tab) {
151 return startClosingOperation((SwingScilabTab) tab.getAsSimpleTab());
155 * Start a closing operation on a tab
159 * @return true if the closing operation succeeded
161 public static boolean startClosingOperation(SwingScilabTab tab, boolean askToExit, boolean mustSave) {
162 return close(collectTabsToClose(tab), getWindow(tab), askToExit, mustSave);
166 * Start a closing operation on multiple tabs
170 * @return true if the closing operation succeeded
172 public static boolean startClosingOperation(List<SwingScilabTab> tabs, boolean askToExit, boolean mustSave) {
173 final SwingScilabWindow win;
174 if (tabs.isEmpty()) {
175 // use the null window to select the console tab.
178 win = getWindow(tabs.get(0));
180 return close(collectTabsToClose(tabs), win, askToExit, mustSave);
184 * Start a closing operation on a tab
188 * @return true if the closing operation succeeded
190 public static boolean startClosingOperation(Tab tab, boolean askToExit, boolean mustSave) {
191 return startClosingOperation((SwingScilabTab) tab.getAsSimpleTab(), askToExit, mustSave);
195 * Start a closing operation on a tab
199 * @return true if the closing operation succeeded
201 public static boolean startClosingOperationWithoutSave(SwingScilabTab tab) {
202 return close(collectTabsToClose(tab), getWindow(tab), true, false);
206 * Start a closing operation on a tab
210 * @return true if the closing operation succeeded
212 public static boolean startClosingOperationWithoutSave(Tab tab) {
213 return startClosingOperationWithoutSave((SwingScilabTab) tab.getAsSimpleTab());
217 * Start a closing operation on a window
219 * Configured to ask for close and store configuration.
221 * @return true if the closing operation succeeded
223 * the window to close
225 public static boolean startClosingOperation(SwingScilabWindow window) {
226 return startClosingOperation(window, true, true);
230 * Start a closing operation on a window
232 * @return true if the closing operation succeeded
234 * the window to close
238 * store the configuration ?
240 public static boolean startClosingOperation(SwingScilabWindow window, boolean askToExit, boolean mustSave) {
241 // Put the closing operation in a try/catch to avoid that an exception
242 // blocks the shutting down. If it is not done, the Scilab process could stay alive.
244 if (window != null) {
245 List<SwingScilabTab> list = new ArrayList<SwingScilabTab>();
246 if (window.getDockingPort() != null) {
247 Object[] dockArray = window.getDockingPort().getDockables().toArray();
248 for (int i = 0; i < dockArray.length; i++) {
249 collectTabsToClose((SwingScilabTab) dockArray[i], list);
251 return close(list, window, askToExit, mustSave);
256 } catch (Exception e) {
264 * Start a closing operation on a window
267 * the window to close
271 * store the configuration ?
272 * @return true if the closing operation succeeded
274 public static boolean startClosingOperation(Window window, boolean askToExit, boolean mustSave) {
275 return startClosingOperation((SwingScilabWindow) window.getAsSimpleWindow(), askToExit, mustSave);
279 * Add a dependency between two tabs
286 public static void addDependency(SwingScilabTab parent, SwingScilabTab child) {
287 if (parent != null && child != null) {
288 List<SwingScilabTab> children = deps.get(parent);
289 if (children == null) {
290 children = new ArrayList<SwingScilabTab>();
291 deps.put(parent, children);
298 * Add a dependency between two tabs
305 public static void addDependency(Tab parent, Tab child) {
306 if (parent != null && child != null) {
307 addDependency((SwingScilabTab) parent.getAsSimpleTab(), (SwingScilabTab) child.getAsSimpleTab());
312 * Add a dependency between two tabs
319 public static void addDependency(SwingScilabTab parent, Tab child) {
320 if (parent != null && child != null) {
321 addDependency(parent, (SwingScilabTab) child.getAsSimpleTab());
326 * Add a dependency between two tabs
333 public static void addDependency(Tab parent, SwingScilabTab child) {
334 if (parent != null && child != null) {
335 addDependency((SwingScilabTab) parent.getAsSimpleTab(), child);
340 * Add a dependency with the root tab
345 public static void addDependencyWithRoot(SwingScilabTab child) {
347 addDependency(root, child);
352 * Add a dependency with the root tab
357 public static void addDependencyWithRoot(Tab child) {
359 addDependency(root, (SwingScilabTab) child.getAsSimpleTab());
364 * Set the root element (normally the console)
369 public static void setRoot(SwingScilabTab tab) {
370 List<SwingScilabTab> list = deps.get(root);
377 * Set the root element (normally the console)
382 public static void setRoot(Tab tab) {
383 setRoot((SwingScilabTab) tab.getAsSimpleTab());
387 * Return the parent tab
391 * @return the parent tab
393 private static SwingScilabTab getParent(SwingScilabTab tab) {
394 for (SwingScilabTab key : deps.keySet()) {
395 List<SwingScilabTab> list = deps.get(key);
396 if (list != null && list.contains(tab)) {
404 * Return the parent tab
408 * @return the parent tab
410 public static SwingScilabTab getElderTab(List<SwingScilabTab> tabs) {
411 if (tabs == null || tabs.size() == 0) {
415 int min = Integer.MAX_VALUE;
416 SwingScilabTab elder = null;
417 for (SwingScilabTab tab : tabs) {
419 SwingScilabTab t = getParent(tab);
434 * Close a list of tabs
439 * the window to use to center the modal dialog
441 * if true, then ask to exit
443 * if true, the configuration is saved
444 * @return true if the closing operation succeeded
446 private static final boolean close(List<SwingScilabTab> list, SwingScilabWindow window, boolean askToExit, boolean mustSave) {
448 if (!askToExit || canClose(list, window)) {
450 SwingScilabTab console = null;
452 // First thing we get the console (if it is here) to be sure to
454 for (SwingScilabTab tab : list) {
455 if (tab.getPersistentId().equals(NULLUUID)) {
461 // We remove the tabs which have a callback and no
463 // To avoid annoying situations the tab will be undocked and
465 List<SwingScilabTab> tabsToRemove = new ArrayList<SwingScilabTab>();
466 for (SwingScilabTab tab : list) {
467 if (closingOps.get(tab) == null) {
468 tab.setVisible(false);
469 tab.getActionButton("undock").getAction().actionPerformed(null);
470 Action action = ((SciClosingAction) tab.getActionButton(DockingConstants.CLOSE_ACTION).getAction()).getAction();
471 if (action == null) {
472 SwingScilabWindow win = getWindow(tab);
474 win.removeTabs(new SwingScilabTab[] { tab });
477 action.actionPerformed(null);
479 tabsToRemove.add(tab);
482 list.removeAll(tabsToRemove);
484 // we group the tabs by win
485 Map<SwingScilabWindow, List<SwingScilabTab>> map = new HashMap<SwingScilabWindow, List<SwingScilabTab>>();
486 for (SwingScilabTab tab : list) {
487 SwingScilabWindow win = getWindow(tab);
489 if (!map.containsKey(win)) {
490 map.put(win, new ArrayList<SwingScilabTab>());
492 map.get(win).add(tab);
496 List<SwingScilabWindow> winsWithOneTab = new ArrayList<SwingScilabWindow>();
497 List<SwingScilabWindow> windowsToClose = new ArrayList<SwingScilabWindow>();
498 for (SwingScilabWindow win : map.keySet()) {
499 List<SwingScilabTab> listTabs = map.get(win);
500 int nbDockedTabs = win.getNbDockedObjects();
501 if (nbDockedTabs == listTabs.size()) {
502 // all the tabs in the window are removed so we save the
505 WindowsConfigurationManager.saveWindowProperties(win);
507 windowsToClose.add(win);
509 if (nbDockedTabs - listTabs.size() == 1) {
510 winsWithOneTab.add(win);
512 // the window will stay opened
514 for (SwingScilabTab tab : listTabs) {
515 WindowsConfigurationManager.saveTabProperties(tab, true);
521 // If a parent and a child are removed, we make a dependency
523 // The parent restoration will imply the child one
524 for (SwingScilabTab tab : list) {
525 SwingScilabTab parent = getParent(tab);
526 if (list.contains(parent) || parent == null) {
527 if (parent != null) {
528 WindowsConfigurationManager.makeDependency(parent.getPersistentId(), tab.getPersistentId());
529 } else if (!tab.getPersistentId().equals(NULLUUID)) {
530 // if the parent is null, we make a dependency with
531 // the console which is the default root
532 WindowsConfigurationManager.makeDependency(NULLUUID, tab.getPersistentId());
535 WindowsConfigurationManager.removeDependency(tab.getPersistentId());
539 WindowsConfigurationManager.clean();
540 // We destroy all the tabs: children before parents.
541 for (SwingScilabTab tab : list) {
542 tab.setVisible(false);
543 if (!tab.getPersistentId().equals(NULLUUID)) {
545 closingOps.get(tab).destroy();
546 } catch (Exception e) {
547 // An error can occured during the destroy operation
548 // We show it but it mustn't avoid the window
555 // We remove the tabs in each window
556 // The tabs are removed in one time to avoid that the
557 // ActiveDockableTracker tryes to give the activation to a
559 for (SwingScilabWindow win : map.keySet()) {
560 win.removeTabs(map.get(win).toArray(new SwingScilabTab[0]));
563 // It stays one docked tab so we remove close and undock action
564 for (SwingScilabWindow win : winsWithOneTab) {
565 Object[] dockArray = win.getDockingPort().getDockables().toArray();
566 SwingScilabTab.removeActions((SwingScilabTab) dockArray[0]);
569 // We wait until all the windows are definitly closed
570 while (windowsToClose.size() != 0) {
571 List<SwingScilabWindow> toRemove = new ArrayList<SwingScilabWindow>();
572 for (SwingScilabWindow win : windowsToClose) {
573 WindowsConfigurationManager.removeWin(win.getUUID());
574 if (win.isDisplayable()) {
580 windowsToClose.removeAll(toRemove);
583 // We remove the tabs from the cache
584 for (SwingScilabTab tab : list) {
585 ScilabTabFactory.getInstance().removeFromCache(tab.getPersistentId());
586 SwingScilabTab parent = getParent(tab);
587 List<SwingScilabTab> l = deps.get(parent);
593 } catch (Exception e) {
596 if (console != null) {
598 closingOps.get(console).destroy();
599 } catch (Exception e) {
610 * Check if the tabs of the liste are closable or not
615 * the window to use to center the modal dialog
616 * @return true if all the tabs can be closed
618 private static final boolean canClose(List<SwingScilabTab> list,
619 SwingScilabWindow window) {
620 String question = makeQuestion(list);
621 if (question != null) {
622 if (ScilabModalDialog.show(window, new String[] { question }, EXIT, IconType.WARNING_ICON, ButtonType.YES_NO) == AnswerOption.NO_OPTION) {
627 for (SwingScilabTab t : list) {
628 ClosingOperation op = closingOps.get(t);
629 if (op != null && !op.canClose()) {
638 * Make the question to ask to exit
641 * the list of the tabs to close
642 * @return the question
644 private static final String makeQuestion(List<SwingScilabTab> list) {
645 List<String> apps = new ArrayList<String>();
646 List<SwingScilabTab> toBeRemoved = Collections.unmodifiableList(list);
647 for (SwingScilabTab t : list) {
648 ClosingOperation op = closingOps.get(t);
650 String name = op.askForClosing(toBeRemoved);
651 if (name != null && !apps.contains(name)) {
656 switch (apps.size()) {
660 return String.format(EXIT_CONFIRM, apps.get(0));
663 String str = apps.remove(0);
664 String last = apps.remove(apps.size() - 1);
665 for (String s : apps) {
669 return String.format(EXIT_CONFIRM_AND, str, last);
673 * Collect the tabs and their children to close (recursive function)
680 private static final void collectTabsToClose(SwingScilabTab tab,
681 List<SwingScilabTab> list) {
682 List<SwingScilabTab> children = deps.get(tab);
683 if (children != null) {
684 for (SwingScilabTab t : children) {
685 collectTabsToClose(t, list);
688 if (!list.contains(tab)) {
693 * Update the tab list in case of hidden (eg. dynamic) dependencies
695 final List<SwingScilabTab> ro = Collections.unmodifiableList(list);
696 for (ListIterator<SwingScilabTab> it = list.listIterator(); it.hasNext();) {
697 final SwingScilabTab t = it.next();
699 final ClosingOperation op = closingOps.get(t);
701 op.updateDependencies(ro, it);
707 * Collect the tabs and their children to close (recursive function)
711 * @return the list of the tabs to close
713 private static final List<SwingScilabTab> collectTabsToClose(
714 SwingScilabTab tab) {
715 final List<SwingScilabTab> list = new ArrayList<SwingScilabTab>();
716 collectTabsToClose(tab, list);
721 * Collect the tabs and their children to close (recursive function)
725 * @return the list of the tabs to close
727 private static final List<SwingScilabTab> collectTabsToClose(List<SwingScilabTab> tabs) {
728 final List<SwingScilabTab> list = new ArrayList<SwingScilabTab>();
729 for (final SwingScilabTab tab : tabs) {
730 collectTabsToClose(tab, list);
736 * Get the window containing the given tab
740 * @return the corresponding window
742 private static final SwingScilabWindow getWindow(SwingScilabTab tab) {
743 SwingScilabWindow win = SwingScilabWindow.allScilabWindows.get(tab.getParentWindowId());
752 * Inner interface to handle a closing operation Must be registered with the
753 * static method ClosingOperationsManager.registerClosingOperation
755 public interface ClosingOperation {
758 * @return true if the associated tab can be closed or not
760 public boolean canClose();
763 * Destroy the resources associated to the tab
765 public void destroy();
768 * @return non null String if the tab requires a
769 * "Are you sure you want to close FOO ?..."
771 public String askForClosing(final List<SwingScilabTab> list);
774 * Update the dependency list to handle specific dependency
777 * the tab list to update
779 * the iterator to update
781 public void updateDependencies(final List<SwingScilabTab> list, final ListIterator<SwingScilabTab> it);