Source code: org/eclipse/ui/internal/PartPane.java
1 /*******************************************************************************
2 * Copyright (c) 2000, 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 package org.eclipse.ui.internal;
12
13
14 import org.eclipse.core.runtime.Platform;
15 import org.eclipse.jface.action.MenuManager;
16 import org.eclipse.jface.util.IPropertyChangeListener;
17 import org.eclipse.jface.util.PropertyChangeEvent;
18 import org.eclipse.jface.util.SafeRunnable;
19 import org.eclipse.swt.SWT;
20 import org.eclipse.swt.events.FocusAdapter;
21 import org.eclipse.swt.events.FocusEvent;
22 import org.eclipse.swt.events.KeyAdapter;
23 import org.eclipse.swt.events.KeyEvent;
24 import org.eclipse.swt.events.KeyListener;
25 import org.eclipse.swt.events.SelectionAdapter;
26 import org.eclipse.swt.events.SelectionEvent;
27 import org.eclipse.swt.events.TraverseEvent;
28 import org.eclipse.swt.events.TraverseListener;
29 import org.eclipse.swt.graphics.Point;
30 import org.eclipse.swt.graphics.Rectangle;
31 import org.eclipse.swt.layout.FillLayout;
32 import org.eclipse.swt.widgets.Composite;
33 import org.eclipse.swt.widgets.Control;
34 import org.eclipse.swt.widgets.Event;
35 import org.eclipse.swt.widgets.Listener;
36 import org.eclipse.swt.widgets.Menu;
37 import org.eclipse.swt.widgets.MenuItem;
38 import org.eclipse.swt.widgets.Sash;
39 import org.eclipse.ui.IWorkbenchPart;
40 import org.eclipse.ui.IWorkbenchPartReference;
41 import org.eclipse.ui.IWorkbenchWindow;
42 import org.eclipse.ui.PlatformUI;
43 import org.eclipse.ui.internal.misc.UIStats;
44 import org.eclipse.ui.internal.presentations.PresentablePart;
45 import org.eclipse.ui.presentations.IPresentablePart;
46
47 /**
48 * Provides the common behavior for both views
49 * and editor panes.
50 *
51 * TODO: Delete ViewPane and EditorPane, and make this class non-abstract.
52 *
53 * TODO: Stop subclassing LayoutPart. This class cannot be interchanged with other LayoutParts.
54 * Pointers that refer to PartPane instances should do so directly rather than referring to
55 * LayoutPart and downcasting. The getPresentablePart() method only applies to PartPanes, and
56 * should be removed from LayoutPart.
57 */
58 public abstract class PartPane extends LayoutPart
59 implements Listener
60 {
61
62 protected PresentablePart presentableAdapter = new PresentablePart(this);
63 public static final String PROP_ZOOMED = "zoomed"; //$NON-NLS-1$
64 private boolean isZoomed = false;
65 private MenuManager paneMenuManager;
66 protected IWorkbenchPartReference partReference;
67 protected WorkbenchPage page;
68 protected Composite control;
69
70 private TraverseListener traverseListener = new TraverseListener() {
71 /* (non-Javadoc)
72 * @see org.eclipse.swt.events.TraverseListener#keyTraversed(org.eclipse.swt.events.TraverseEvent)
73 */
74 public void keyTraversed(TraverseEvent e) {
75 // Hack: Currently, SWT sets focus whenever we call Control.traverse. This doesn't
76 // cause too much of a problem for ctrl-pgup and ctrl-pgdn, but it is seriously unexpected
77 // for other traversal events. When (and if) it becomes possible to call traverse() without
78 // forcing a focus change, this if statement should be removed and ALL events should be
79 // forwarded to the container.
80 if (e.detail == SWT.TRAVERSE_PAGE_NEXT || e.detail == SWT.TRAVERSE_PAGE_PREVIOUS) {
81 ILayoutContainer container = getContainer();
82 if (container != null && container instanceof LayoutPart) {
83 LayoutPart parent = (LayoutPart)container;
84 Control parentControl = parent.getControl();
85 if (parentControl != null && !parentControl.isDisposed()) {
86 parentControl.traverse(e.detail);
87 e.doit = false;
88 }
89 }
90 }
91 }
92
93 };
94
95
96 public static class Sashes {
97 public Sash left;
98 public Sash right;
99 public Sash top;
100 public Sash bottom;
101 }
102
103 /**
104 * Construct a pane for a part.
105 */
106 public PartPane(IWorkbenchPartReference partReference, WorkbenchPage workbenchPage) {
107 super(partReference.getId());
108 this.partReference = partReference;
109 this.page = workbenchPage;
110 ((WorkbenchPartReference)partReference).setPane(this);
111 }
112 /**
113 * Factory method for creating the SWT Control hierarchy for this Pane's child.
114 */
115 protected void createChildControl() {
116 final IWorkbenchPart part[] = new IWorkbenchPart[]{partReference.getPart(false)};
117 if(part[0] == null)
118 return;
119
120 // must call createControl(..) first.
121 // TODO: should only really call this method from createControl()
122 if(control == null)
123 return;
124
125 // Make sure the child control has not been created yet
126 if(control.getChildren().length != 0)
127 return;
128
129 final Composite content = new Composite(control, SWT.NONE);
130 content.setLayout(new FillLayout());
131
132 String error = WorkbenchMessages.format("PartPane.unableToCreate", new Object[] {partReference.getTitle()}); //$NON-NLS-1$
133 Platform.run(new SafeRunnable(error) {
134 public void run() {
135 try {
136 UIStats.start(UIStats.CREATE_PART_CONTROL,id);
137 part[0].createPartControl(content);
138
139 Rectangle oldBounds = control.getBounds();
140
141 ((WorkbenchPartReference)getPartReference()).refreshFromPart();
142
143 // Unless refreshing the part has somehow triggered a layout,
144 // we need to force a layout now. (SWT only triggers a layout if the
145 // bounds change, so check that case here).
146 if (oldBounds.equals(control.getBounds())) {
147 control.layout(true);
148 }
149 } finally {
150 UIStats.end(UIStats.CREATE_PART_CONTROL,id);
151 }
152 }
153 public void handleException(Throwable e) {
154 // Log error.
155 Workbench wb = (Workbench)PlatformUI.getWorkbench();
156 if (!wb.isStarting())
157 super.handleException(e);
158
159 // Dispose old part.
160 Control children[] = content.getChildren();
161 for (int i = 0; i < children.length; i++){
162 children[i].dispose();
163 }
164
165 // Create new part.
166 IWorkbenchPart newPart = createErrorPart(part[0]);
167 part[0].getSite().setSelectionProvider(null);
168 newPart.createPartControl(content);
169 ((WorkbenchPartReference)partReference).setPart(newPart);
170 part[0] = newPart;
171 }
172 });
173 page.addPart(partReference);
174 page.firePartOpened(part[0]);
175 }
176
177 public void addSizeMenuItem (Menu menu, int index) {
178 //Add size menu
179 MenuItem item = new MenuItem(menu, SWT.CASCADE, index);
180 item.setText(WorkbenchMessages.getString("PartPane.size")); //$NON-NLS-1$
181 Menu sizeMenu = new Menu(menu);
182 item.setMenu(sizeMenu);
183 addSizeItems(sizeMenu);
184 }
185
186 /**
187 *
188 */
189 public void createControl(Composite parent) {
190 if (getControl() != null)
191 return;
192
193 // Create view form.
194 control = new Composite(parent, SWT.NONE);
195 control.setLayout(new FillLayout());
196 // the part should never be visible by default. It will be made visible
197 // by activation. This allows us to have views appear in tabs without
198 // becoming active by default.
199 control.setVisible(false);
200
201 // Create a title bar.
202 createTitleBar();
203
204 // Create content.
205 createChildControl();
206
207 // When the pane or any child gains focus, notify the workbench.
208 control.addListener(SWT.Activate, this);
209
210 control.addTraverseListener(traverseListener);
211 }
212
213 protected abstract IWorkbenchPart createErrorPart(IWorkbenchPart oldPart);
214 /**
215 * Create a title bar for the pane if required.
216 */
217 protected abstract void createTitleBar();
218 /**
219 * @private
220 */
221 public void dispose() {
222 super.dispose();
223
224 if ((control != null) && (!control.isDisposed())) {
225 control.removeListener(SWT.Activate, this);
226 control.removeTraverseListener(traverseListener);
227 control.dispose();
228 control = null;
229 }
230 if ((paneMenuManager != null)) {
231 paneMenuManager.dispose();
232 paneMenuManager = null;
233 }
234 }
235 /**
236 * User has requested to close the pane.
237 * Take appropriate action depending on type.
238 */
239 abstract public void doHide();
240
241 /**
242 * Zooms in on the part contained in this pane.
243 */
244 protected void doZoom() {
245 if (getWindow() instanceof IWorkbenchWindow)
246 page.toggleZoom(partReference);
247 }
248 /**
249 * Gets the presentation bounds.
250 */
251 public Rectangle getBounds() {
252 return getControl().getBounds();
253 }
254 /**
255 * Get the control.
256 */
257 public Control getControl() {
258 return control;
259 }
260
261 /*
262 * @see LayoutPart#getMinimumHeight()
263 */
264 public int getMinimumHeight() {
265 if (control == null || control.isDisposed())
266 return super.getMinimumHeight();
267
268 // account for the borders
269 return control.computeTrim(0, 0, 0, 0).height;
270 }
271
272 /**
273 * Answer the part child.
274 */
275 public IWorkbenchPartReference getPartReference() {
276 return partReference;
277 }
278
279 /**
280 * @see Listener
281 */
282 public void handleEvent(Event event) {
283 if (event.type == SWT.Activate)
284 requestActivation();
285 }
286 /**
287 * Return whether the pane is zoomed or not
288 */
289 public boolean isZoomed() {
290 return isZoomed;
291 }
292 /**
293 * Move the control over another one.
294 */
295 public void moveAbove(Control refControl) {
296 if (getControl() != null)
297 getControl().moveAbove(refControl);
298 }
299 /**
300 * Notify the workbook page that the part pane has
301 * been activated by the user.
302 */
303 protected void requestActivation() {
304 this.page.requestActivation(partReference.getPart(true));
305 }
306 /**
307 * Sets the parent for this part.
308 */
309 public void setContainer(ILayoutContainer container) {
310 super.setContainer(container);
311 }
312
313 /**
314 * Shows the receiver if <code>visible</code> is true otherwise hide it.
315 */
316 public void setVisible(boolean makeVisible) {
317 super.setVisible(makeVisible);
318 if(makeVisible) //Make sure that the part is restored.
319 partReference.getPart(true);
320 }
321 /**
322 * Sets focus to this part.
323 */
324 public void setFocus() {
325 requestActivation();
326 IWorkbenchPart part = partReference.getPart(true);
327 if (part != null) {
328 part.setFocus();
329 }
330 }
331 /**
332 * Sets the workbench page of the view.
333 */
334 public void setWorkbenchPage(WorkbenchPage workbenchPage) {
335 this.page = workbenchPage;
336 }
337 /**
338 * Set whether the pane is zoomed or not
339 */
340 public void setZoomed(boolean isZoomed) {
341 if (this.isZoomed == isZoomed)
342 return; // do nothing if we're already in the right state.
343
344 super.setZoomed(isZoomed);
345
346 this.isZoomed = isZoomed;
347
348 final Object[] listeners = getPropertyListeners().getListeners();
349 if (listeners.length > 0) {
350 Boolean oldValue = isZoomed ? Boolean.FALSE : Boolean.TRUE;
351 Boolean zoomed = isZoomed ? Boolean.TRUE : Boolean.FALSE;
352 PropertyChangeEvent event = new PropertyChangeEvent(this, PROP_ZOOMED, oldValue, zoomed);
353 for (int i = 0; i < listeners.length; ++i)
354 ((IPropertyChangeListener)listeners[i]).propertyChange(event);
355 }
356 }
357 /**
358 * Informs the pane that it's window shell has
359 * been activated.
360 */
361 /* package */ abstract void shellActivated();
362 /**
363 * Informs the pane that it's window shell has
364 * been deactivated.
365 */
366 /* package */ abstract void shellDeactivated();
367 /**
368 * Indicate focus in part.
369 */
370 public abstract void showFocus(boolean inFocus);
371 /**
372 * @see IPartDropTarget::targetPartFor
373 */
374 public LayoutPart targetPartFor(LayoutPart dragSource) {
375 return this;
376 }
377
378 /**
379 * Returns the PartStack that contains this PartPane, or null if none.
380 *
381 * @return
382 */
383 public PartStack getStack() {
384 ILayoutContainer container = getContainer();
385 if (container instanceof PartStack) {
386 return (PartStack) container;
387 }
388
389 return null;
390 }
391
392 /**
393 * Show a title label menu for this pane.
394 */
395 public void showPaneMenu() {
396 PartStack folder = getStack();
397
398 if (folder != null) {
399 folder.showSystemMenu();
400 }
401 }
402 /**
403 * Show the context menu for this part.
404 */
405 public void showViewMenu() {
406 PartStack folder = getStack();
407
408 if (folder != null) {
409 folder.showPaneMenu();
410 }
411 }
412
413 /**
414 * Finds and return the sashes around this part.
415 */
416 protected Sashes findSashes() {
417 Sashes result = new Sashes();
418
419 ILayoutContainer container = getContainer();
420
421 if (container == null) {
422 return result;
423 }
424
425 container.findSashes(this, result);
426 return result;
427 }
428
429 /**
430 * Enable the user to resize this part using
431 * the keyboard to move the specified sash
432 */
433 protected void moveSash(final Sash sash) {
434 moveSash(sash, this);
435 }
436
437 public static void moveSash(final Sash sash, final LayoutPart toGetFocusWhenDone) {
438 final KeyListener listener = new KeyAdapter() {
439 public void keyPressed(KeyEvent e) {
440 if (e.character == SWT.ESC || e.character == '\r') {
441 if(toGetFocusWhenDone != null)
442 toGetFocusWhenDone.setFocus();
443 }
444 }
445 };
446 sash.addFocusListener(new FocusAdapter() {
447 public void focusGained(FocusEvent e) {
448 sash.setBackground(sash.getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION));
449 sash.addKeyListener(listener);
450 }
451 public void focusLost(FocusEvent e) {
452 sash.setBackground(null);
453 sash.removeKeyListener(listener);
454 }
455 });
456 sash.setFocus();
457
458 }
459
460 /**
461 * Add a menu item to the Size Menu
462 */
463 protected void addSizeItem(Menu sizeMenu, String labelMessage, final Sash sash) {
464 MenuItem item = new MenuItem(sizeMenu, SWT.NONE);
465 item.setText(labelMessage); //$NON-NLS-1$
466 item.addSelectionListener(new SelectionAdapter() {
467 public void widgetSelected(SelectionEvent e) {
468 moveSash(sash);
469 }
470 });
471 item.setEnabled(!isZoomed() && sash != null);
472 }
473 /**
474 * Returns the workbench page of this pane.
475 */
476 public WorkbenchPage getPage() {
477 return page;
478 }
479 /**
480 * Add the Left,Right,Up,Botton menu items to the Size menu.
481 */
482 protected void addSizeItems(Menu sizeMenu) {
483 Sashes sashes = findSashes();
484 addSizeItem(sizeMenu,
485 WorkbenchMessages.getString("PartPane.sizeLeft"), sashes.left); //$NON-NLS-1$
486 addSizeItem(sizeMenu,
487 WorkbenchMessages.getString("PartPane.sizeRight"), sashes.right); //$NON-NLS-1$
488 addSizeItem(sizeMenu,
489 WorkbenchMessages.getString("PartPane.sizeTop"), sashes.top); //$NON-NLS-1$
490 addSizeItem(sizeMenu,
491 WorkbenchMessages.getString("PartPane.sizeBottom"), sashes.bottom); //$NON-NLS-1$
492 }
493
494
495 /**
496 * Pin this part.
497 */
498 protected void doDock() {
499 // do nothing
500 }
501
502 /**
503 * Set the busy state of the pane.
504 */
505 public void setBusy(boolean isBusy) {
506 //Do nothing by default
507 }
508
509 /**
510 * Show a highlight for the receiver if it is
511 * not currently the part in the front of its
512 * presentation.
513 *
514 */
515 public void showHighlight(){
516 //No nothing by default
517 }
518
519 /**
520 * Ensure that we are not in the zoomed before reparenting.
521 * TODO: I am certain this isn't correct but I'll be damned if I know what is.
522 */
523 public void reparent(Composite newParent) {
524 if (isZoomed())
525 setZoomed(false);
526 super.reparent(newParent);
527 }
528
529 /**
530 * @return
531 */
532 public abstract Control getToolBar();
533
534 /**
535 * @return
536 */
537 public boolean hasViewMenu() {
538 return false;
539 }
540
541 /**
542 * @param location
543 */
544 public void showViewMenu(Point location) {
545
546 }
547
548 public boolean isBusy() {
549 return false;
550 }
551
552 /* (non-Javadoc)
553 * @see org.eclipse.ui.internal.LayoutPart#getPresentablePart()
554 */
555 public IPresentablePart getPresentablePart() {
556 return presentableAdapter;
557 }
558
559 }