blob: ddaf59217e62a662455523d66ba19ca942c54d8f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006 Sybase, Inc. and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Sybase, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.jst.pagedesigner.ui.common.sash;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.IPostSelectionProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jst.jsf.common.ui.internal.guiutils.SWTUtils;
import org.eclipse.jst.pagedesigner.PDPlugin;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.IEditorActionBarContributor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IKeyBindingService;
import org.eclipse.ui.INestableKeyBindingService;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.part.MultiPageEditorActionBarContributor;
import org.eclipse.ui.part.MultiPageEditorSite;
/**
* This class emulates the MultiPageEditorPart. But instead of using multipage,
* it use SashForm to separate the editors.
*
* @author mengbo
*/
public abstract class SashEditorPart extends EditorPart {
private int _orientation = SWT.VERTICAL;
private SashForm _sashForm;
/**
* List of nested editors. Element type: IEditorPart. Need to hang onto them
* here, in addition to using get/setData on the items, because dispose()
* needs to access them, but widgetry has already been disposed at that
* point.
*/
private ArrayList _nestedEditors = new ArrayList(3);
private Map _editorToComposite = new HashMap();
private IEditorPart _activeEditor = null;
/**
* Creates and adds a new page containing the given editor to this
* multi-page editor. The page is added at the given index. This also hooks
* a property change listener on the nested editor.
*
* @param editor
* the nested editor
* @param input
* the input for the nested editor
* @exception PartInitException
* if a new page could not be created
* @see org.eclipse.ui.part.MultiPageEditorPart#handlePropertyChange(int) the handler for
* property change events from the nested editor
*/
public void addPage(final IEditorPart editor, IEditorInput input)
throws PartInitException {
IEditorSite site = createSite(editor);
// call init first so that if an exception is thrown, we have created no
// new widgets
editor.init(site, input);
final Composite parent1 = new Composite(getContainer(), SWT.NONE);
FillLayout fillLayout = new FillLayout();
fillLayout.marginWidth = fillLayout.marginHeight = 1;
parent1.setLayout(fillLayout);
parent1.addListener(SWT.Activate, new Listener() {
public void handleEvent(Event event) {
if (event.type == SWT.Activate) {
activeEditorChanged(editor);
parent1.setBackground(ColorConstants.green);
}
}
});
parent1.addListener(SWT.Deactivate, new Listener() {
public void handleEvent(Event event) {
parent1.setBackground(ColorConstants.titleInactiveBackground);
}
});
SWTUtils.workaroundResize(parent1);
editor.createPartControl(parent1);
editor.addPropertyListener(new IPropertyListener() {
public void propertyChanged(Object source, int propertyId) {
SashEditorPart.this.handlePropertyChange(propertyId);
}
});
_nestedEditors.add(editor);
_editorToComposite.put(editor, parent1);
connectPage(editor);
}
/**
* @param editor
*/
protected void connectPage(IEditorPart editor) {
ISelectionProvider editSelectionProvider = editor.getSite()
.getSelectionProvider();
if (editSelectionProvider instanceof IPostSelectionProvider) {
((IPostSelectionProvider) editSelectionProvider)
.addPostSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
((SashEditorSelectionProvider) getSite()
.getSelectionProvider())
.firePostSelectionChanged(event);
}
});
} else {
editSelectionProvider
.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
((SashEditorSelectionProvider) getSite()
.getSelectionProvider())
.fireSelectionChanged(event);
}
});
}
}
/**
* Creates an empty container. Creates a CTabFolder with no style bits set,
* and hooks a selection listener which calls <code>pageChange()</code>
* whenever the selected tab changes.
*
* @param parent
* The composite in which the container tab folder should be
* created; must not be <code>null</code>.
* @return a new container
*/
private SashForm createContainer(Composite parent) {
// use SWT.FLAT style so that an extra 1 pixel border is not reserved
// inside the folder
SashForm newContainer = new SashForm(parent, SWT.NONE);
SWTUtils.workaroundResize(newContainer);
newContainer.setOrientation(_orientation);
return newContainer;
}
/**
* @throws PartInitException
*/
abstract protected void createPages() throws PartInitException;
/**
* The <code>MultiPageEditor</code> implementation of this
* <code>IWorkbenchPart</code> method creates the control for the
* multi-page editor by calling <code>createContainer</code>, then
* <code>createPages</code>. Subclasses should implement
* <code>createPages</code> rather than overriding this method.
*
* @param parent
* The parent in which the editor should be created; must not be
* <code>null</code>.
*/
public final void createPartControl(Composite parent) {
this._sashForm = createContainer(parent);
try {
createPages();
} catch (PartInitException ex) {
ex.printStackTrace();
}
// set the active page (page 0 by default), unless it has already been
// done
if (getActiveEditor() == null) {
if (!_nestedEditors.isEmpty()) {
setActiveEditor((IEditorPart) _nestedEditors.get(0));
}
}
}
/**
* Creates the site for the given nested editor. The
* <code>MultiPageEditorPart</code> implementation of this method creates
* an instance of <code>MultiPageEditorSite</code>. Subclasses may
* reimplement to create more specialized sites.
*
* @param editor
* the nested editor
* @return the editor site
*/
protected IEditorSite createSite(IEditorPart editor) {
return new SashEditorSite(this, editor);
}
/**
* The <code>MultiPageEditorPart</code> implementation of this
* <code>IWorkbenchPart</code> method disposes all nested editors.
* Subclasses may extend.
*/
public void dispose() {
_activeEditor = null;
for (int i = 0; i < _nestedEditors.size(); ++i) {
IEditorPart editor = (IEditorPart) _nestedEditors.get(i);
disposePart(editor);
}
_nestedEditors.clear();
_editorToComposite.clear();
}
/**
* Returns the active nested editor if there is one.
* <p>
* Subclasses should not override this method
* </p>
*
* @return the active nested editor, or <code>null</code> if none
*/
public IEditorPart getActiveEditor() {
return _activeEditor;
}
/**
* Returns the composite control containing this multi-page editor's pages.
* This should be used as the parent when creating controls for the
* individual pages. That is, when calling <code>addPage(Control)</code>,
* the passed control should be a child of this container.
* <p>
* Warning: Clients should not assume that the container is any particular
* subclass of Composite. The actual class used may change in order to
* improve the look and feel of multi-page editors. Any code making
* assumptions on the particular subclass would thus be broken.
* </p>
* <p>
* Subclasses should not override this method
* </p>
*
* @return the composite, or <code>null</code> if
* <code>createPartControl</code> has not been called yet
*/
protected Composite getContainer() {
return _sashForm;
}
/**
* Returns the editor for the given page index. The page index must be
* valid.
*
* @param pageIndex
* the index of the page
* @return the editor for the specified page, or <code>null</code> if the
* specified page was not created with
* <code>addPage(IEditorPart,IEditorInput)</code>
*/
protected IEditorPart getEditor(int pageIndex) {
return (IEditorPart) _nestedEditors.get(pageIndex);
}
/**
* Handles a property change notification from a nested editor. The default
* implementation simply forwards the change to listeners on this multi-page
* editor by calling <code>firePropertyChange</code> with the same
* property id. For example, if the dirty state of a nested editor changes
* (property id <code>IEditorPart.PROP_DIRTY</code>), this method handles
* it by firing a property change event for
* <code>IEditorPart.PROP_DIRTY</code> to property listeners on this
* multi-page editor.
* <p>
* Subclasses may extend or reimplement this method.
* </p>
*
* @param propertyId
* the id of the property that changed
*/
protected void handlePropertyChange(int propertyId) {
firePropertyChange(propertyId);
}
/**
* The <code>MultiPageEditorPart</code> implementation of this
* <code>IEditorPart</code> method sets its site to the given site, its
* input to the given input, and the site's selection provider to a
* <code>MultiPageSelectionProvider</code>. Subclasses may extend this
* method.
*
* @param site
* The site for which this part is being created; must not be
* <code>null</code>.
* @param input
* The input on which this editor should be created; must not be
* <code>null</code>.
* @throws PartInitException
* If the initialization of the part fails -- currently never.
*/
public void init(IEditorSite site, IEditorInput input)
throws PartInitException {
setSite(site);
setInput(input);
site.setSelectionProvider(new SashEditorSelectionProvider(this));
}
/**
* The <code>MultiPageEditorPart</code> implementation of this
* <code>IEditorPart</code> method returns whether the contents of any of
* this multi-page editor's nested editors have changed since the last save.
* Pages created with <code>addPage(Control)</code> are ignored.
* <p>
* Subclasses may extend or reimplement this method.
* </p>
*
* @return <code>true</code> if any of the nested editors are dirty;
* <code>false</code> otherwise.
*/
public boolean isDirty() {
// use nestedEditors to avoid SWT requests; see bug 12996
for (Iterator i = _nestedEditors.iterator(); i.hasNext();) {
IEditorPart editor = (IEditorPart) i.next();
if (editor.isDirty()) {
return true;
}
}
return false;
}
/**
* Notifies this multi-page editor that the page with the given id has been
* activated. This method is called when the user selects a different tab.
* <p>
* The <code>MultiPageEditorPart</code> implementation of this method sets
* focus to the new page, and notifies the action bar contributor (if there
* is one). This checks whether the action bar contributor is an instance of
* <code>MultiPageEditorActionBarContributor</code>, and, if so, calls
* <code>setActivePage</code> with the active nested editor. This also
* fires a selection change event if required.
* </p>
* <p>
* Subclasses may extend this method.
* </p>
* @param activeEditor
*
*/
protected void activeEditorChanged(IEditorPart activeEditor) {
setActiveEditor(activeEditor);
setFocus();
IEditorSite site = getEditorSite();
while (site != null) {
IEditorActionBarContributor contributor = site
.getActionBarContributor();
if (contributor instanceof MultiPageEditorActionBarContributor) {
((MultiPageEditorActionBarContributor) contributor)
.setActivePage(activeEditor);
}
if (site instanceof MultiPageEditorSite) {
site = (IEditorSite) ((MultiPageEditorSite) site)
.getMultiPageEditor().getSite();
} else if (site instanceof SashEditorSite) {
site = (IEditorSite) ((SashEditorSite) site).getSashEditor()
.getSite();
} else {
site = null;
}
}
if (activeEditor != null) {
// Workaround for 1GAUS7C: ITPUI:ALL - Editor not activated when
// restored from previous session
// do not need second if once fixed
ISelectionProvider selectionProvider = activeEditor.getSite()
.getSelectionProvider();
if (selectionProvider != null) {
SelectionChangedEvent event = new SelectionChangedEvent(
selectionProvider, selectionProvider.getSelection());
((SashEditorSelectionProvider) getSite().getSelectionProvider())
.fireSelectionChanged(event);
}
}
}
/**
* Disposes the given part and its site.
*
* @param part
* The part to dispose; must not be <code>null</code>.
*/
private void disposePart(final IWorkbenchPart part) {
SafeRunner.run(new SafeRunnable() {
public void run() {
if (part.getSite() instanceof SashEditorSite) {
SashEditorSite partSite = (SashEditorSite) part.getSite();
partSite.dispose();
}
part.dispose();
}
public void handleException(Throwable e) {
// Exception has already being logged by Core. Do nothing.
}
});
}
/**
* Sets the currently active page.
* @param part
*
*/
protected void setActiveEditor(IEditorPart part) {
_activeEditor = part;
}
/**
* The <code>MultiPageEditor</code> implementation of this
* <code>IWorkbenchPart</code> method sets focus on the active nested
* editor, if there is one.
* <p>
* Subclasses may extend or reimplement.
* </p>
*/
public void setFocus() {
setFocus(getActiveEditor());
}
/**
* Sets focus to the control for the given page. If the page has an editor,
* this calls its <code>setFocus()</code> method. Otherwise, this calls
* <code>setFocus</code> on the control for the page.
*
* @param pageIndex
* the index of the page
*/
private void setFocus(IEditorPart editor) {
final IKeyBindingService service = getSite().getKeyBindingService();
if (editor == null) {
// There is no selected page, so deactivate the active service.
if (service instanceof INestableKeyBindingService) {
final INestableKeyBindingService nestableService = (INestableKeyBindingService) service;
nestableService.activateKeyBindingService(null);
} else {
//WorkbenchPlugin
PDPlugin
.getLogger(getClass()).error("MultiPageEditorPart.setFocus() Parent key binding service was not an instance of INestableKeyBindingService. It was an instance of " + service.getClass().getName() + " instead."); //$NON-NLS-1$ //$NON-NLS-2$
}
return;
}
editor.setFocus();
// There is no selected page, so deactivate the active service.
if (service instanceof INestableKeyBindingService) {
final INestableKeyBindingService nestableService = (INestableKeyBindingService) service;
if (editor != null) {
nestableService.activateKeyBindingService(editor
.getEditorSite());
} else {
nestableService.activateKeyBindingService(null);
}
} else {
PDPlugin
.getLogger(getClass()).error("MultiPageEditorPart.setFocus() Parent key binding service was not an instance of INestableKeyBindingService. It was an instance of " + service.getClass().getName() + " instead."); //$NON-NLS-1$ //$NON-NLS-2$
}
}
public void doSave(IProgressMonitor monitor) {
if (_activeEditor != null) {
_activeEditor.doSave(monitor);
}
}
public void doSaveAs() {
if (_activeEditor != null) {
_activeEditor.doSaveAs();
}
}
public boolean isSaveAsAllowed() {
if (_activeEditor != null)
{
return _activeEditor.isSaveAsAllowed();
}
return false;
}
/**
* @param orientation
*/
public void setOrientation(int orientation) {
this._orientation = orientation;
if (_sashForm != null && !_sashForm.isDisposed()) {
_sashForm.setMaximizedControl(null);
_sashForm.setOrientation(_orientation);
}
}
/**
* @param part
*/
public void setMaximizedEditor(IEditorPart part) {
if (part != null) {
Composite c = (Composite) _editorToComposite.get(part);
if (c != null && _sashForm != null && !_sashForm.isDisposed()) {
_sashForm.setMaximizedControl(c);
part.setFocus();
}
} else {
if (_sashForm != null && !_sashForm.isDisposed()) {
_sashForm.setMaximizedControl(null);
}
}
}
}