001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.core;
043:
044: import java.awt.Dialog;
045: import java.awt.Toolkit;
046: import java.awt.Window;
047: import java.io.IOException;
048: import java.lang.reflect.Method;
049: import java.net.URL;
050: import java.text.MessageFormat;
051: import java.util.ArrayList;
052: import java.util.Iterator;
053: import java.util.List;
054: import java.util.logging.Level;
055: import java.util.logging.Logger;
056: import java.util.prefs.PreferenceChangeEvent;
057: import java.util.prefs.PreferenceChangeListener;
058: import javax.swing.JDialog;
059: import javax.swing.SwingUtilities;
060: import javax.swing.event.ChangeListener;
061: import org.netbeans.ModuleManager;
062: import org.netbeans.TopSecurityManager;
063: import org.netbeans.core.startup.MainLookup;
064: import org.netbeans.core.startup.ModuleSystem;
065: import org.netbeans.core.startup.layers.SessionManager;
066: import org.netbeans.core.ui.SwingBrowser;
067: import org.openide.LifecycleManager;
068: import org.openide.NotifyDescriptor;
069: import org.openide.awt.HtmlBrowser;
070: import org.openide.cookies.SaveCookie;
071: import org.openide.loaders.DataObject;
072: import org.openide.util.ChangeSupport;
073: import org.openide.util.Exceptions;
074: import org.openide.util.HelpCtx;
075: import org.openide.util.Lookup;
076: import org.openide.util.LookupEvent;
077: import org.openide.util.LookupListener;
078: import org.openide.util.Mutex;
079: import org.openide.util.NbBundle;
080: import org.openide.util.RequestProcessor;
081: import org.openide.util.Task;
082: import org.openide.util.lookup.InstanceContent;
083: import org.openide.windows.Mode;
084: import org.openide.windows.TopComponent;
085: import org.openide.windows.WindowManager;
086:
087: /**
088: * Main switchboard for the NetBeans core.
089: * Manages startup sequence, holds references to important objects, etc.
090: */
091: public abstract class NbTopManager {
092: /* masks to define the interactivity level */
093:
094: /** initialize the main window?
095: * if not set the main window is not create nor shown.
096: */
097: public static final int IL_MAIN_WINDOW = 0x0001;
098: /** initialize window system?
099: * if not set the selected node is taken from the top manager.
100: */
101: public static final int IL_WINDOWS = 0x0002;
102: /** initialize workspaces when not created?
103: */
104: public static final int IL_WORKSPACES = 0x0004;
105:
106: /** Initialize everything.
107: */
108: public static final int IL_ALL = 0xffff;
109:
110: /** Constructs a new manager.
111: */
112: public NbTopManager() {
113: assert defaultTopManager == null : "Only one instance allowed"; // NOI18N
114: defaultTopManager = this ;
115:
116: Lookup lookup = Lookup.getDefault();
117: if (!(lookup instanceof MainLookup)) {
118: throw new ClassCastException("Wrong Lookup impl found: "
119: + lookup);
120: }
121: MainLookup.startedNbTopManager();
122: }
123:
124: /** Getter for instance of this manager.
125: */
126: public static NbTopManager get() {
127: assert defaultTopManager != null : "Must be initialized already"; // NOI18N
128: return defaultTopManager;
129: }
130:
131: /** Danger method for clients who think they want an NbTM but don't actually
132: * care whether it is ready or not. Should be removed eventually by getting
133: * rid of useless protected methods in this class, and using Lookup to find
134: * each configurable piece of impl.
135: * @return a maybe half-constructed NbTM
136: */
137: public static NbTopManager getUninitialized() {
138: return get();
139: }
140:
141: private static NbTopManager defaultTopManager;
142:
143: /**
144: * Checks whether the top manager has been loaded already.
145: * Used during early startup sequence.
146: */
147: public static synchronized boolean isInitialized() {
148: return defaultTopManager != null;
149: }
150:
151: /** Test method to check whether some level of interactivity is enabled.
152: * XXX this method is unused; can it be deleted?
153: * @param il mask composed of the constants of IL_XXXX
154: * @return true if such level is enabled
155: */
156: public abstract boolean isInteractive(int il);
157:
158: //
159: // The main method allows access to registration service
160: //
161:
162: /** Register new instance.
163: */
164: public final void register(Object obj) {
165: MainLookup.register(obj);
166: }
167:
168: /** Register new instance.
169: * @param obj source
170: * @param conv convertor which postponing an instantiation
171: */
172: public final <T, R> void register(T obj,
173: InstanceContent.Convertor<T, R> conv) {
174: MainLookup.register(obj, conv);
175: }
176:
177: /** Unregisters the service.
178: */
179: public final void unregister(Object obj) {
180: MainLookup.unregister(obj);
181: }
182:
183: /** Unregisters the service registered with a convertor.
184: */
185: public final <T, R> void unregister(T obj,
186: InstanceContent.Convertor<T, R> conv) {
187: MainLookup.unregister(obj, conv);
188: }
189:
190: //
191: // Implementation of methods from TopManager
192: //
193:
194: /** Shows a specified HelpCtx in IDE's help window.
195: * @param helpCtx thehelp to be shown
196: * @deprecated Better to use org.netbeans.api.javahelp.Help
197: */
198: public void showHelp(HelpCtx helpCtx) {
199: // Awkward but should work.
200: try {
201: Class<?> c = (Lookup.getDefault().lookup(ClassLoader.class))
202: .loadClass("org.netbeans.api.javahelp.Help"); // NOI18N
203: Object o = Lookup.getDefault().lookup(c);
204: if (o != null) {
205: Method m = c.getMethod("showHelp",
206: new Class[] { HelpCtx.class }); // NOI18N
207: m.invoke(o, new Object[] { helpCtx });
208: return;
209: }
210: } catch (ClassNotFoundException cnfe) {
211: // ignore - maybe javahelp module is not installed, not so strange
212: } catch (Exception e) {
213: // potentially more serious
214: Logger.getLogger(NbTopManager.class.getName()).log(
215: Level.WARNING, null, e);
216: }
217: // Did not work.
218: Toolkit.getDefaultToolkit().beep();
219: }
220:
221: /**
222: * Implementation of URL displayer, which shows documents in the configured web browser.
223: */
224: public static final class NbURLDisplayer extends
225: HtmlBrowser.URLDisplayer {
226: /** Default constructor for lookup. */
227: public NbURLDisplayer() {
228: }
229:
230: /** WWW browser window. */
231: private NbBrowser htmlViewer;
232:
233: public void showURL(final URL u) {
234: Mutex.EVENT.readAccess(new Runnable() {
235: public void run() {
236: if (htmlViewer == null) {
237: htmlViewer = new NbBrowser();
238: }
239: htmlViewer.showUrl(u);
240: }
241: });
242: }
243: }
244:
245: /**
246: * Default status displayer implementation; GUI is in StatusLine.
247: */
248: public static final class NbStatusDisplayer extends
249: org.openide.awt.StatusDisplayer {
250: /** Default constructor for lookup. */
251: public NbStatusDisplayer() {
252: }
253:
254: private final ChangeSupport cs = new ChangeSupport(this );
255: private String text = ""; // NOI18N
256:
257: public void setStatusText(String text) {
258: synchronized (this ) {
259: this .text = text;
260: }
261: cs.fireChange();
262: Logger.getLogger(NbStatusDisplayer.class.getName()).log(
263: Level.FINE, "Status text updated: {0}", text);
264: }
265:
266: public synchronized String getStatusText() {
267: return text;
268: }
269:
270: public void addChangeListener(ChangeListener l) {
271: cs.addChangeListener(l);
272: }
273:
274: public void removeChangeListener(ChangeListener l) {
275: cs.removeChangeListener(l);
276: }
277: }
278:
279: /** saves all opened objects */
280: private static void saveAll() {
281: DataObject dobj = null;
282: ArrayList<DataObject> bad = new ArrayList<DataObject>();
283: DataObject[] modifs = DataObject.getRegistry().getModified();
284: if (modifs.length == 0) {
285: // Do not show MSG_AllSaved
286: return;
287: }
288: for (int i = 0; i < modifs.length; i++) {
289: try {
290: dobj = modifs[i];
291: SaveCookie sc = dobj.getCookie(SaveCookie.class);
292:
293: if (sc != null) {
294: org.openide.awt.StatusDisplayer
295: .getDefault()
296: .setStatusText(
297: MessageFormat
298: .format(
299: NbBundle
300: .getBundle(
301: NbTopManager.class)
302: .getString(
303: "CTL_FMT_SavingMessage"),
304: new Object[] { dobj
305: .getName() }));
306: sc.save();
307: }
308: } catch (IOException ex) {
309: Logger.getLogger(NbTopManager.class.getName()).log(
310: Level.WARNING, null, ex);
311: bad.add(dobj);
312: }
313: }
314: NotifyDescriptor descriptor;
315: //recode this part to show only one dialog?
316: Iterator<DataObject> ee = bad.iterator();
317: while (ee.hasNext()) {
318: descriptor = new NotifyDescriptor.Message(MessageFormat
319: .format(NbBundle.getBundle(NbTopManager.class)
320: .getString("CTL_Cannot_save"),
321: new Object[] { ee.next().getPrimaryFile()
322: .getName() }));
323: org.openide.DialogDisplayer.getDefault().notify(descriptor);
324: }
325: // notify user that everything is done
326: org.openide.awt.StatusDisplayer.getDefault().setStatusText(
327: NbBundle.getBundle(NbTopManager.class).getString(
328: "MSG_AllSaved"));
329: }
330:
331: /** Interface describing basic control over window system.
332: * @since 1.15 */
333: public interface WindowSystem {
334: void show();
335:
336: void hide();
337:
338: void load();
339:
340: void save();
341:
342: void clear();
343: } // End of WindowSystem interface.
344:
345: public static boolean isModalDialogPresent() {
346: return hasModalDialog(WindowManager.getDefault()
347: .getMainWindow())
348: // XXX Trick to get the shared frame instance.
349: || hasModalDialog(new JDialog().getOwner());
350: }
351:
352: private static boolean hasModalDialog(Window w) {
353: Window[] ws = w.getOwnedWindows();
354: for (int i = 0; i < ws.length; i++) {
355: if (ws[i] instanceof Dialog && ((Dialog) ws[i]).isModal()) {
356: return true;
357: } else if (hasModalDialog(ws[i])) {
358: return true;
359: }
360: }
361:
362: return false;
363: }
364:
365: private static boolean doingExit = false;
366:
367: public static void exit() {
368: // #37160 So there is avoided potential clash between hiding GUI in AWT
369: // and accessing AWTTreeLock from saving routines (winsys).
370: if (SwingUtilities.isEventDispatchThread()) {
371: doExit();
372: } else {
373: SwingUtilities.invokeLater(new Runnable() {
374: public void run() {
375: doExit();
376: }
377: });
378: }
379: }
380:
381: /**
382: * @return True if the IDE is shutting down.
383: */
384: public static boolean isExiting() {
385: return doingExit;
386: }
387:
388: private static void doExit() {
389: if (doingExit) {
390: return;
391: }
392: doingExit = true;
393: // save all open files
394: try {
395: if (System.getProperty("netbeans.close") != null
396: || ExitDialog.showDialog()) {
397:
398: final WindowSystem windowSystem = Lookup.getDefault()
399: .lookup(WindowSystem.class);
400:
401: // #29831: hide frames between closing() and close()
402: Runnable hideFrames = new Runnable() {
403: public void run() {
404: org.netbeans.CLIHandler.stopServer();
405:
406: if (windowSystem != null) {
407: windowSystem.hide();
408: windowSystem.save();
409: }
410: if (Boolean
411: .getBoolean("netbeans.close.when.invisible")) {
412: // hook to permit perf testing of time to *apparently* shut down
413: TopSecurityManager.exit(0);
414: }
415: }
416: };
417:
418: if (org.netbeans.core.startup.Main.getModuleSystem()
419: .shutDown(hideFrames)) {
420: try {
421: try {
422: LoaderPoolNode.store();
423: } catch (IOException ioe) {
424: Logger.getLogger(
425: NbTopManager.class.getName()).log(
426: Level.WARNING, null, ioe);
427: }
428: //#46940 -saving just once..
429: // // save window system, [PENDING] remove this after the winsys will
430: // // persist its state automaticaly
431: // if (windowSystem != null) {
432: // windowSystem.save();
433: // }
434: SessionManager.getDefault().close();
435: } catch (ThreadDeath td) {
436: throw td;
437: } catch (Throwable t) {
438: // Do not let problems here prevent system shutdown. The module
439: // system is down; the IDE cannot be used further.
440: Exceptions.printStackTrace(t);
441: }
442: // #37231 Someone (e.g. Jemmy) can install its own EventQueue and then
443: // exit is dispatched through that proprietary queue and it
444: // can be refused by security check. So, we need to replan
445: // to RequestProcessor to avoid security problems.
446: Task exitTask = new Task(new Runnable() {
447: public void run() {
448: TopSecurityManager.exit(0);
449: }
450: });
451: RequestProcessor.getDefault().post(exitTask);
452: exitTask.waitFinished();
453: }
454: }
455: } finally {
456: doingExit = false;
457: }
458: }
459:
460: /**
461: * Default implementation of the lifecycle manager interface that knows
462: * how to save all modified DataObject's, and to exit the IDE safely.
463: */
464: public static final class NbLifecycleManager extends
465: LifecycleManager {
466: /** Default constructor for lookup. */
467: public NbLifecycleManager() {
468: }
469:
470: public void saveAll() {
471: NbTopManager.saveAll();
472: }
473:
474: public void exit() {
475: NbTopManager.exit();
476: }
477: }
478:
479: /** Get the module subsystem. */
480: public abstract ModuleSystem getModuleSystem();
481:
482: public static Lookup getModuleLookup() {
483: return org.netbeans.core.startup.Main.getModuleSystem()
484: .getManager().getModuleLookup();
485: }
486:
487: public static List getModuleJars() {
488: return org.netbeans.core.startup.Main.getModuleSystem()
489: .getModuleJars();
490: }
491:
492: /**
493: * Able to reuse HtmlBrowserComponent.
494: */
495: public static class NbBrowser {
496:
497: private HtmlBrowserComponent brComp;
498: private PreferenceChangeListener idePCL;
499: private static Lookup.Result factoryResult;
500:
501: static {
502: factoryResult = Lookup.getDefault().lookupResult(
503: HtmlBrowser.Factory.class);
504: factoryResult.allItems();
505: factoryResult.addLookupListener(new LookupListener() {
506: public void resultChanged(LookupEvent ev) {
507: ((NbURLDisplayer) org.openide.awt.HtmlBrowser.URLDisplayer
508: .getDefault()).htmlViewer = null;
509: }
510: });
511: }
512:
513: public NbBrowser() {
514: HtmlBrowser.Factory browser = IDESettings.getWWWBrowser();
515: if (browser == null) {
516: // Fallback.
517: browser = new SwingBrowser();
518: }
519: // try if an internal browser is set and possibly try to reuse an
520: // existing component
521: if (browser.createHtmlBrowserImpl().getComponent() != null) {
522: brComp = findOpenedBrowserComponent();
523: }
524: if (brComp == null) {
525: brComp = new HtmlBrowserComponent(browser, true, true);
526: brComp.putClientProperty("TabPolicy", "HideWhenAlone"); // NOI18N
527: }
528: setListener();
529: }
530:
531: /**
532: * Tries to find already opened <code>HtmlBrowserComponent</code>. In
533: * the case of success returns the instance, null otherwise.
534: */
535: private HtmlBrowserComponent findOpenedBrowserComponent() {
536: for (Iterator it = WindowManager.getDefault().getModes()
537: .iterator(); it.hasNext();) {
538: Mode m = (Mode) it.next();
539: if ("editor".equals(m.getName())) { // NOI18N
540: TopComponent[] tcs = m.getTopComponents();
541: for (int i = 0; i < tcs.length; i++) {
542: if (tcs[i] instanceof HtmlBrowserComponent) {
543: return (HtmlBrowserComponent) tcs[i];
544: }
545: }
546: break;
547: }
548: }
549: return null;
550: }
551:
552: /** Show URL in browser
553: * @param url URL to be shown
554: */
555: private void showUrl(URL url) {
556: brComp.open();
557: brComp.requestActive();
558: brComp.setURL(url);
559: }
560:
561: /**
562: * Sets listener that invalidates this as main IDE's browser if user changes the settings
563: */
564: private void setListener() {
565: if (idePCL != null) {
566: return;
567: }
568: try {
569: // listen on preffered browser change
570: idePCL = new PreferenceChangeListener() {
571: public void preferenceChange(
572: PreferenceChangeEvent evt) {
573: if (IDESettings.PROP_WWWBROWSER.equals(evt
574: .getKey())) {
575: ((NbURLDisplayer) HtmlBrowser.URLDisplayer
576: .getDefault()).htmlViewer = null;
577: if (idePCL != null) {
578: IDESettings
579: .getPreferences()
580: .removePreferenceChangeListener(
581: idePCL);
582: idePCL = null;
583: brComp = null;
584: }
585: }
586: }
587: };
588: IDESettings.getPreferences()
589: .addPreferenceChangeListener(idePCL);
590: } catch (Exception ex) {
591: Exceptions.printStackTrace(ex);
592: }
593: }
594: }
595: }
|