Source code: org/eclipse/ui/application/WorkbenchAdvisor.java
1 /*******************************************************************************
2 * Copyright (c) 2003, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11
12 package org.eclipse.ui.application;
13
14 import org.eclipse.core.runtime.IAdaptable;
15 import org.eclipse.core.runtime.IStatus;
16 import org.eclipse.core.runtime.Status;
17 import org.eclipse.swt.SWTError;
18 import org.eclipse.swt.SWTException;
19 import org.eclipse.swt.widgets.Display;
20 import org.eclipse.swt.widgets.Shell;
21 import org.eclipse.ui.IWorkbenchPreferenceConstants;
22 import org.eclipse.ui.IWorkbenchWindow;
23 import org.eclipse.ui.PlatformUI;
24 import org.eclipse.ui.WorkbenchException;
25 import org.eclipse.ui.internal.WorkbenchPlugin;
26 import org.eclipse.ui.internal.WorkbenchWindowConfigurer;
27 import org.eclipse.ui.internal.util.PrefUtil;
28
29 /**
30 * Public base class for configuring the workbench.
31 * <p>
32 * Note that the workbench advisor object is created in advance of creating the
33 * workbench. However, by the time the workbench starts calling methods on this
34 * class, <code>PlatformUI.getWorkbench</code> is guaranteed to have been
35 * properly initialized.
36 * </p>
37 * <p>
38 * Example of creating and running a workbench (in an
39 * <code>IPlatformRunnable</code>):
40 * <pre>
41 * <code>
42 * public class MyApplication implements IPlatformRunnable {
43 * public Object run(Object args) {
44 * WorkbenchAdvisor workbenchAdvisor = new MyWorkbenchAdvisor();
45 * Display display = PlatformUI.createDisplay();
46 * int returnCode = PlatformUI.createAndRunWorkbench(display, workbenchAdvisor);
47 * if (returnCode == PlatformUI.RETURN_RESTART) {
48 * return IPlatformRunnable.EXIT_RESTART;
49 * } else {
50 * return IPlatformRunnable.EXIT_OK;
51 * }
52 * }
53 * </code>
54 * </pre>
55 * </p>
56 * <p>
57 * An application should declare a subclass of <code>WorkbenchAdvisor</code>
58 * and override methods to configure the workbench to suit the needs of the
59 * particular application.
60 * </p>
61 * <p>
62 * The following advisor methods are called at strategic points in the
63 * workbench's lifecycle (all occur within the dynamic scope of the call
64 * to {@link PlatformUI#createAndRunWorkbench PlatformUI.createAndRunWorkbench}):
65 * <ul>
66 * <li><code>initialize</code> - called first; before any windows; use to
67 * register things</li>
68 * <li><code>preStartup</code> - called second; after initialize but
69 * before first window is opened; use to temporarily disable things during
70 * startup or restore</li>
71 * <li><code>postStartup</code> - called third; after first window is
72 * opened; use to reenable things temporarily disabled in previous step</li>
73 * <li><code>postRestore</code> - called after the workbench and its windows
74 * has been recreated from a previously saved state; use to adjust the
75 * restored workbench</li>
76 * <li><code>preWindowOpen</code> - called as each window is being opened;
77 * use to configure aspects of the window other than actions bars </li>
78 * <li><code>fillActionBars</code> - called after <code>preWindowOpen</code> to
79 * configure a window's action bars</li>
80 * <li><code>postWindowRestore</code> - called after a window has been
81 * recreated from a previously saved state; use to adjust the restored
82 * window</li>
83 * <li><code>postWindowCreate</code> - called after a window has been created,
84 * either from an initial state or from a restored state; used to adjust the
85 * window</li>
86 * <li><code>openIntro</code> - called immediately before a window is opened in
87 * order to create the introduction component, if any.
88 * <li><code>postWindowOpen</code> - called after a window has been
89 * opened; use to hook window listeners, etc.</li>
90 * <li><code>preWindowShellClose</code> - called when a window's shell
91 * is closed by the user; use to pre-screen window closings</li>
92 * <li><code>eventLoopException</code> - called to handle the case where the
93 * event loop has crashed; use to inform the user that things are not well</li>
94 * <li><code>eventLoopIdle</code> - called when there are currently no more
95 * events to be processed; use to perform other work or to yield until new
96 * events enter the queue</li>
97 * <li><code>preShutdown</code> - called just after event loop has terminated
98 * but before any windows have been closed; use to deregister things registered
99 * during initialize</li>
100 * <li><code>postShutdown</code> - called last; after event loop has terminated
101 * and all windows have been closed; use to deregister things registered during
102 * initialize</li>
103 * </ul>
104 * </p>
105 *
106 * @since 3.0
107 */
108 public abstract class WorkbenchAdvisor {
109
110 /**
111 * Bit flag for {@link #fillActionBars fillActionBars} indicating that the
112 * operation is not filling the action bars of an actual workbench window,
113 * but rather a proxy (used for perspective customization).
114 */
115 public static final int FILL_PROXY = 0x01;
116
117 /**
118 * Bit flag for {@link #fillActionBars fillActionBars} indicating that the
119 * operation is supposed to fill (or describe) the workbench window's menu
120 * bar.
121 */
122 public static final int FILL_MENU_BAR = 0x02;
123
124 /**
125 * Bit flag for {@link #fillActionBars fillActionBars} indicating that the
126 * operation is supposed to fill (or describe) the workbench window's cool
127 * bar.
128 */
129 public static final int FILL_COOL_BAR = 0x04;
130
131 /**
132 * Bit flag for {@link #fillActionBars fillActionBars} indicating that the
133 * operation is supposed to fill (or describe) the workbench window's status
134 * line.
135 */
136 public static final int FILL_STATUS_LINE = 0x08;
137
138 /**
139 * The workbench configurer.
140 */
141 private IWorkbenchConfigurer workbenchConfigurer;
142
143 private boolean introOpened;
144
145 /**
146 * Creates and initializes a new workbench advisor instance.
147 */
148 protected WorkbenchAdvisor() {
149 // do nothing
150 }
151
152 /**
153 * Remembers the configurer and calls <code>initialize</code>.
154 * <p>
155 * For internal use by the workbench only.
156 * </p>
157 *
158 * @param configurer an object for configuring the workbench
159 */
160 public final void internalBasicInitialize(IWorkbenchConfigurer configurer) {
161 if (workbenchConfigurer != null) {
162 throw new IllegalStateException();
163 }
164 this.workbenchConfigurer = configurer;
165 initialize(configurer);
166 }
167
168 /**
169 * Performs arbitrary initialization before the workbench starts running.
170 * <p>
171 * This method is called during workbench initialization prior to any
172 * windows being opened.
173 * Clients must not call this method directly (although super calls are okay).
174 * The default implementation does nothing. Subclasses may override.
175 * Typical clients will use the configurer passed in to tweak the
176 * workbench. If further tweaking is required in the future,
177 * the configurer may be obtained using <code>getWorkbenchConfigurer</code>.
178 * </p>
179 *
180 * @param configurer an object for configuring the workbench
181 */
182 public void initialize(IWorkbenchConfigurer configurer) {
183 // do nothing
184 }
185
186 /**
187 * Returns the workbench configurer for the advisor. Can
188 * be <code>null</code> if the advisor is not initialized yet.
189 *
190 * @return the workbench configurer, or <code>null</code>
191 * if the advisor is not initialized yet
192 */
193 protected IWorkbenchConfigurer getWorkbenchConfigurer() {
194 return workbenchConfigurer;
195 }
196
197 /**
198 * Performs arbitrary actions just before the first workbench window is
199 * opened (or restored).
200 * <p>
201 * This method is called after the workbench has been initialized and
202 * just before the first window is about to be opened.
203 * Clients must not call this method directly (although super calls are okay).
204 * The default implementation does nothing. Subclasses may override.
205 * </p>
206 */
207 public void preStartup() {
208 // do nothing
209 }
210
211 /**
212 * Performs arbitrary actions after the workbench windows have been
213 * opened (or restored), but before the main event loop is run.
214 * <p>
215 * This method is called just after the windows have been opened.
216 * Clients must not call this method directly (although super calls are okay).
217 * The default implementation does nothing. Subclasses may override.
218 * It is okay to call <code>IWorkbench.close()</code> from this method.
219 * </p>
220 */
221 public void postStartup() {
222 // do nothing
223 }
224
225 /**
226 * Performs arbitrary finalization before the workbench is about to
227 * shut down.
228 * <p>
229 * This method is called immediately prior to workbench shutdown before any
230 * windows have been closed.
231 * Clients must not call this method directly (although super calls are okay).
232 * The default implementation returns <code>true</code>. Subclasses may override.
233 * </p>
234 * <p>
235 * The advisor may veto a regular shutdown by returning <code>false</code>,
236 * although this will be ignored if the workbench is being forced to shut down.
237 * </p>
238 * @return <code>true</code> to allow the workbench to proceed with shutdown,
239 * <code>false</code> to veto a non-forced shutdown
240 */
241 public boolean preShutdown() {
242 return true;
243 }
244
245 /**
246 * Performs arbitrary finalization after the workbench stops running.
247 * <p>
248 * This method is called during workbench shutdown after all windows
249 * have been closed.
250 * Clients must not call this method directly (although super calls are okay).
251 * The default implementation does nothing. Subclasses may override.
252 * </p>
253 */
254 public void postShutdown() {
255 // do nothing
256 }
257
258 /**
259 * Performs arbitrary actions when the event loop crashes (the code that
260 * handles a UI event throws an exception that is not caught).
261 * <p>
262 * This method is called when the code handling a UI event throws an
263 * exception. In a perfectly functioning application, this method would
264 * never be called. In practice, it comes into play when there are bugs
265 * in the code that trigger unchecked runtime exceptions. It is also
266 * activated when the system runs short of memory, etc.
267 * Fatal errors (ThreadDeath) are not passed on to this method, as there
268 * is nothing that could be done.
269 * </p>
270 * <p>
271 * Clients must not call this method directly (although super calls are okay).
272 * The default implementation logs the problem so that it does not go
273 * unnoticed. Subclasses may override or extend this method. It is generally
274 * a bad idea to override with an empty method, and you should be
275 * especially careful when handling Errors.
276 * </p>
277 *
278 * @param exception the uncaught exception that was thrown inside the UI
279 * event loop
280 */
281 public void eventLoopException(Throwable exception) {
282 // Protection from client doing super(null) call
283 if (exception == null) {
284 return;
285 }
286
287 try {
288 // Log the exception
289 String msg = exception.getMessage();
290 if (msg == null) {
291 msg = exception.toString();
292 }
293 WorkbenchPlugin.log(
294 "Unhandled event loop exception", //$NON-NLS-1$
295 new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, 0, msg, exception));
296
297 // Handle nested exception from SWT (see bug 6312)
298 Throwable nested = null;
299 if (exception instanceof SWTException) {
300 nested = ((SWTException)exception).throwable;
301 } else if (exception instanceof SWTError) {
302 nested = ((SWTError)exception).throwable;
303 }
304 if (nested != null) {
305 msg = nested.getMessage();
306 if (msg == null) {
307 msg = nested.toString();
308 }
309 WorkbenchPlugin.log(
310 "*** SWT nested exception", //$NON-NLS-1$
311 new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, 0, msg, nested));
312 }
313
314 // Print it onto the console if debugging
315 if (WorkbenchPlugin.DEBUG) {
316 exception.printStackTrace();
317 }
318 } catch (Throwable e) {
319 // One of the log listeners probably failed. Core should have logged the
320 // exception since its the first listener.
321 System.err.println("Error while logging event loop exception:"); //$NON-NLS-1$
322 exception.printStackTrace();
323 System.err.println("Logging exception:"); //$NON-NLS-1$
324 e.printStackTrace();
325 }
326 }
327
328 /**
329 * Performs arbitrary work or yields when there are no events to be processed.
330 * <p>
331 * This method is called when there are currently no more events on the queue
332 * to be processed at the moment.
333 * </p><p>
334 * Clients must not call this method directly (although super calls are okay).
335 * The default implementation yields until new events enter the queue.
336 * Subclasses may override or extend this method. It is generally
337 * a bad idea to override with an empty method.
338 * It is okay to call <code>IWorkbench.close()</code> from this method.
339 * </p>
340 * @param display the main display of the workbench UI
341 */
342 public void eventLoopIdle(Display display) {
343 // default: yield cpu until new events enter the queue
344 display.sleep();
345 }
346
347 /**
348 * Performs arbitrary actions before the given workbench window is
349 * opened.
350 * <p>
351 * This method is called before the window's controls have been created.
352 * Clients must not call this method directly (although super calls are okay).
353 * The default implementation does nothing. Subclasses may override.
354 * Typical clients will use the configurer passed in to tweak the
355 * workbench window in an application-specific way; however, filling the
356 * window's menu bar, tool bar, and status line must be done in
357 * {@link #fillActionBars fillActionBars}, which is called immediately
358 * after this method is called.
359 * </p>
360 *
361 * @param configurer an object for configuring the particular workbench
362 * window being opened
363 */
364 public void preWindowOpen(IWorkbenchWindowConfigurer configurer) {
365 // do nothing
366 }
367
368 /**
369 * Configures the action bars using the given action bar configurer.
370 * Under normal circumstances, <code>flags</code> does not include
371 * <code>FILL_PROXY</code>, meaning this is a request to fill the actions\
372 * bars of the given workbench window; the
373 * remaining flags indicate which combination of
374 * the menu bar (<code>FILL_MENU_BAR</code>),
375 * the tool bar (<code>FILL_COOL_BAR</code>),
376 * and the status line (<code>FILL_STATUS_LINE</code>) are to be filled.
377 * <p>
378 * If <code>flags</code> does include <code>FILL_PROXY</code>, then this
379 * is a request to describe the actions bars of the given workbench window
380 * (which will already have been filled);
381 * again, the remaining flags indicate which combination of the menu bar,
382 * the tool bar, and the status line are to be described.
383 * The actions included in the proxy action bars can be the same instances
384 * as in the actual window's action bars. Calling <code>ActionFactory</code>
385 * to create new action instances is not recommended, because these
386 * actions internally register listeners with the window and there is no
387 * opportunity to dispose of these actions.
388 * </p>
389 * <p>
390 * This method is called just after {@link #preWindowOpen preWindowOpen}.
391 * Clients must not call this method directly (although super calls are okay).
392 * The default implementation does nothing. Subclasses may override.
393 * </p>
394 *
395 * @param window the workbench window
396 * @param configurer the action bar configurer object
397 * @param flags bit mask composed from the constants
398 * {@link #FILL_MENU_BAR FILL_MENU_BAR},
399 * {@link #FILL_COOL_BAR FILL_COOL_BAR},
400 * {@link #FILL_STATUS_LINE FILL_STATUS_LINE},
401 * and {@link #FILL_PROXY FILL_PROXY}
402 * Note: should 1st param be IWorkbenchWindowConfigurer to be more consistent with other methods?
403 * Note: suggest adding ActionBuilder as API, to encapsulate the action building outside
404 * of the advisor, and to handle the common pattern of hanging onto the action builder
405 * in order to properly handle FILL_PROXY
406 */
407 public void fillActionBars(IWorkbenchWindow window, IActionBarConfigurer configurer, int flags) {
408 // do nothing by default
409 }
410
411 /**
412 * Performs arbitrary actions after the given workbench window has been
413 * restored, but before it is opened.
414 * <p>
415 * This method is called after a previously-saved window have been
416 * recreated. This method is not called when a new window is created from
417 * scratch. This method is never called when a workbench is started for the
418 * very first time, or when workbench state is not saved or restored.
419 * Clients must not call this method directly (although super calls are okay).
420 * The default implementation does nothing. Subclasses may override.
421 * It is okay to call <code>IWorkbench.close()</code> from this method.
422 * </p>
423 *
424 * @param configurer an object for configuring the particular workbench
425 * window just restored
426 * Note: document checked exception
427 */
428 public void postWindowRestore(IWorkbenchWindowConfigurer configurer) throws WorkbenchException {
429 // do nothing
430 }
431
432 /**
433 * Opens the introduction componenet.
434 * <p>
435 * Clients must not call this method directly (although super calls are okay).
436 * The default implementation opens the intro in the first window provided
437 * the preference IWorkbenchPreferences.SHOW_INTRO is <code>true</code>. If
438 * an intro is shown then this preference will be set to <code>false</code>.
439 * Subsequently, and intro will be shown only if
440 * <code>WorkbenchConfigurer.getSaveAndRestore()</code> returns
441 * <code>true</code> and the introduction was visible on last shutdown.
442 * Subclasses may override.
443 * </p>
444 *
445 * @param configurer configurer an object for configuring the particular workbench
446 * window just created
447 */
448 public void openIntro(IWorkbenchWindowConfigurer configurer) {
449 if (introOpened)
450 return;
451
452 introOpened = true;
453
454 boolean showIntro = PrefUtil.getAPIPreferenceStore().getBoolean(
455 IWorkbenchPreferenceConstants.SHOW_INTRO);
456
457 if (!showIntro)
458 return;
459
460 if (getWorkbenchConfigurer().getWorkbench().getIntroManager().hasIntro()) {
461 getWorkbenchConfigurer()
462 .getWorkbench()
463 .getIntroManager().showIntro(
464 configurer.getWindow(),
465 false);
466
467 PrefUtil.getAPIPreferenceStore().setValue(IWorkbenchPreferenceConstants.SHOW_INTRO, false);
468 PrefUtil.saveAPIPrefs();
469 }
470 }
471
472 /**
473 * Performs arbitrary actions after the given workbench window has been
474 * created (possibly after being restored), but has not yet been opened.
475 * <p>
476 * This method is called after a new window has been created from scratch,
477 * or when a previously-saved window has been restored. In the latter case,
478 * this method is called after <code>postWindowRestore</code>.
479 * Clients must not call this method directly (although super calls are okay).
480 * The default implementation does nothing. Subclasses may override.
481 * </p>
482 *
483 * @param configurer an object for configuring the particular workbench
484 * window just created
485 */
486 public void postWindowCreate(IWorkbenchWindowConfigurer configurer) {
487 // do nothing
488 }
489
490 /**
491 * Performs arbitrary actions after the given workbench window has been
492 * opened (possibly after being restored).
493 * <p>
494 * This method is called after a window has been opened. This method is
495 * called after a new window has been created from scratch, or when
496 * a previously-saved window has been restored.
497 * Clients must not call this method directly (although super calls are okay).
498 * The default implementation does nothing. Subclasses may override.
499 * </p>
500 *
501 * @param configurer an object for configuring the particular workbench
502 * window just opened
503 */
504 public void postWindowOpen(IWorkbenchWindowConfigurer configurer) {
505 // do nothing
506 }
507
508 /**
509 * Performs arbitrary actions as the given workbench window's shell is being
510 * closed directly, and possibly veto the close.
511 * <p>
512 * This method is called from a ShellListener associated with the workbench
513 * window. It is not called when the window is being closed for other reasons.
514 * Clients must not call this method directly (although super calls are okay).
515 * The default implementation does nothing. Subclasses may override.
516 * Typical clients may use the configurer passed in to access the
517 * workbench window being closed. If this method
518 * returns <code>false</code>, then the user's request to close the shell is
519 * ignored. This gives the workbench advisor an opportunity to query the user
520 * and/or veto the closing of a window under some circumstances.
521 * </p>
522 *
523 * @param configurer an object for configuring the particular workbench
524 * window whose shell is being closed
525 * @return <code>true</code> to allow the window to close,
526 * and <code>false</code> to prevent the window from closing
527 * @see org.eclipse.ui.IWorkbenchWindow#close
528 */
529 public boolean preWindowShellClose(IWorkbenchWindowConfigurer configurer) {
530 // do nothing, but allow the close() to proceed
531 return true;
532 }
533
534 /**
535 * Performs arbitrary actions after the given workbench window is
536 * closed.
537 * <p>
538 * This method is called after the window's controls have been disposed.
539 * Clients must not call this method directly (although super calls are okay).
540 * The default implementation does nothing. Subclasses may override.
541 * Typical clients will use the configurer passed in to tweak the
542 * workbench window in an application-specific way.
543 * </p>
544 *
545 * @param configurer an object for configuring the particular workbench
546 * window being closed
547 */
548 public void postWindowClose(IWorkbenchWindowConfigurer configurer) {
549 // do nothing
550 }
551
552 /**
553 * Returns whether the menu with the given id is an application menu of the
554 * given window. This is used during OLE "in place" editing. Application
555 * menus should be preserved during menu merging. All other menus may be
556 * removed from the window.
557 * <p>
558 * The default implementation returns false. Subclasses may override.
559 * </p>
560 *
561 * @param configurer an object for configuring the workbench window
562 * @param menuId the menu id
563 * @return <code>true</code> for application menus, and <code>false</code>
564 * for part-specific menus
565 */
566 public boolean isApplicationMenu(IWorkbenchWindowConfigurer configurer, String menuId) {
567 // default: not an application menu
568 return false;
569 }
570
571 /**
572 * Returns the default input for newly created workbench pages.
573 * <p>
574 * The default implementation returns <code>null</code>.
575 * Subclasses may override.
576 * </p>
577 *
578 * @return the default input for a new workbench window page, or
579 * <code>null</code> if none
580 */
581 public IAdaptable getDefaultPageInput() {
582 // default: no input
583 return null;
584 }
585
586 /**
587 * Returns the id of the perspective to use for the initial workbench window.
588 * <p>
589 * This method is called during startup when the workbench is creating
590 * the first new window. Subclasses must implement.
591 * </p>
592 * <p>
593 * If the {@link IWorkbenchPreferenceConstants#DEFAULT_PERSPECTIVE_ID} preference
594 * is specified, it supercedes the perspective specified here.
595 * </p>
596 *
597 * @return the id of the perspective for the initial window
598 */
599 public abstract String getInitialWindowPerspectiveId();
600
601 /**
602 * Returns the id of the preference page that should be presented most
603 * prominently.
604 * <p>
605 * The default implementation returns <code>null</code>.
606 * Subclasses may override.
607 * </p>
608 *
609 * @return the id of the preference page, or <code>null</code> if none
610 */
611 public String getMainPreferencePageId() {
612 // default: no opinion
613 return null;
614 }
615
616 /**
617 * Creates the contents of the window.
618 * <p>
619 * The default implementation adds a menu bar, a cool bar, a status line,
620 * a perspective bar, and a fast view bar. The visibility of these controls
621 * can be configured using the <code>setShow*</code> methods on
622 * <code>IWorkbenchWindowConfigurer</code>.
623 * </p>
624 * <p>
625 * Subclasses may override to define custom window contents and layout,
626 * but must call <code>IWorkbenchWindowConfigurer.createPageComposite</code>.
627 * </p>
628 *
629 * @param configurer the window configurer
630 * @param shell the window's shell
631 * @see IWorkbenchWindowConfigurer#createMenuBar
632 * @see IWorkbenchWindowConfigurer#createCoolBarControl
633 * @see IWorkbenchWindowConfigurer#createStatusLineControl
634 * @see IWorkbenchWindowConfigurer#createPageComposite
635 */
636 public void createWindowContents(IWorkbenchWindowConfigurer configurer, Shell shell) {
637 ((WorkbenchWindowConfigurer) configurer).createDefaultContents(shell);
638 }
639
640 /**
641 * Opens the workbench windows on startup.
642 * The default implementation tries to restore the previously saved
643 * workbench state using <code>IWorkbenchConfigurer.restoreWorkbenchState()</code>.
644 * If there was no previously saved state, or if the restore failed,
645 * then a first-time window is opened using
646 * <code>IWorkbenchConfigurer.openFirstTimeWindow</code>.
647 *
648 * @return <code>true</code> to proceed with workbench startup,
649 * or <code>false</code> to exit
650 */
651 public boolean openWindows() {
652 IStatus status = getWorkbenchConfigurer().restoreState();
653 if (!status.isOK()) {
654 if (status.getCode() == IWorkbenchConfigurer.RESTORE_CODE_EXIT) {
655 return false;
656 }
657 if (status.getCode() == IWorkbenchConfigurer.RESTORE_CODE_RESET) {
658 getWorkbenchConfigurer().openFirstTimeWindow();
659 }
660 }
661 return true;
662 }
663 }
664