blob: fc9cfed97fd9990d9ac20eb56246d571b9505948 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2007 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.forms.editor;
import java.util.Vector;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.dialogs.IPageChangeProvider;
import org.eclipse.jface.dialogs.IPageChangedListener;
import org.eclipse.jface.dialogs.PageChangedEvent;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorActionBarContributor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.part.MultiPageEditorActionBarContributor;
import org.eclipse.ui.part.MultiPageEditorPart;
import org.eclipse.ui.part.MultiPageSelectionProvider;
/**
* This class forms a base of multi-page form editors that typically use one or
* more pages with forms and one page for raw source of the editor input.
* <p>
* Pages are added 'lazily' i.e. adding a page reserves a tab for it but does
* not cause the page control to be created. Page control is created when an
* attempt is made to select the page in question. This allows editors with
* several tabs and complex pages to open quickly.
* <p>
* Subclasses should extend this class and implement <code>addPages</code>
* method. One of the two <code>addPage</code> methods should be called to
* contribute pages to the editor. One adds complete (standalone) editors as
* nested tabs. These editors will be created right away and will be hooked so
* that key bindings, selection service etc. is compatible with the one for the
* standalone case. The other method adds classes that implement
* <code>IFormPage</code> interface. These pages will be created lazily and
* they will share the common key binding and selection service. Since 3.1,
* FormEditor is a page change provider. It allows listeners to attach to it and
* get notified when pages are changed. This new API in JFace allows dynamic
* help to update on page changes.
*
* @since 3.0
*/
public abstract class FormEditor extends MultiPageEditorPart implements
IPageChangeProvider {
/**
* An array of pages currently in the editor. Page objects are not limited
* to those that implement <code>IFormPage</code>, hence the size of this
* array matches the number of pages as viewed by the user.
* <p>
* Subclasses can access this field but should not modify it.
*/
protected Vector pages = new Vector();
private FormToolkit toolkit;
private int currentPage = -1;
private ListenerList pageListeners = new ListenerList();
private static class FormEditorSelectionProvider extends
MultiPageSelectionProvider {
private ISelection globalSelection;
/**
* @param formEditor the editor
*/
public FormEditorSelectionProvider(FormEditor formEditor) {
super(formEditor);
}
public ISelection getSelection() {
IEditorPart activeEditor = ((FormEditor) getMultiPageEditor())
.getActiveEditor();
if (activeEditor != null) {
ISelectionProvider selectionProvider = activeEditor.getSite()
.getSelectionProvider();
if (selectionProvider != null)
return selectionProvider.getSelection();
}
if (globalSelection != null) {
return globalSelection;
}
return StructuredSelection.EMPTY;
}
/*
* (non-Javadoc) Method declared on <code> ISelectionProvider </code> .
*/
public void setSelection(ISelection selection) {
IEditorPart activeEditor = ((FormEditor) getMultiPageEditor())
.getActiveEditor();
if (activeEditor != null) {
ISelectionProvider selectionProvider = activeEditor.getSite()
.getSelectionProvider();
if (selectionProvider != null)
selectionProvider.setSelection(selection);
} else {
this.globalSelection = selection;
fireSelectionChanged(new SelectionChangedEvent(this,
globalSelection));
}
}
}
/**
* The constructor.
*/
public FormEditor() {
}
/**
* Overrides super to plug in a different selection provider.
*/
public void init(IEditorSite site, IEditorInput input)
throws PartInitException {
setSite(site);
setInput(input);
site.setSelectionProvider(new FormEditorSelectionProvider(this));
}
/**
* Creates the common toolkit for this editor and adds pages to the editor.
*
* @see #addPages
*/
protected void createPages() {
addPages();
}
/*
* @see org.eclipse.ui.part.MultiPageEditorPart#createPageContainer(org.eclipse.swt.widgets.Composite)
*/
protected Composite createPageContainer(Composite parent) {
parent = super.createPageContainer(parent);
toolkit = createToolkit(parent.getDisplay());
return parent;
}
/**
* Creates the form toolkit. The method can be implemented to substitute a
* subclass of the toolkit that should be used for this editor. A typical
* use of this method would be to create the form toolkit using one shared
* <code>FormColors</code> object to share resources across the multiple
* editor instances.
*
* @param display
* the display to use when creating the toolkit
* @return the newly created toolkit instance
*/
protected FormToolkit createToolkit(Display display) {
return new FormToolkit(display);
}
/**
* Subclass should implement this method to add pages to the editor using
* 'addPage(IFormPage)' method.
*/
protected abstract void addPages();
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.IPageChangeProvider#addPageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener)
*/
public void addPageChangedListener(IPageChangedListener listener) {
pageListeners.add(listener);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.IPageChangeProvider#removePageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener)
*/
public void removePageChangedListener(IPageChangedListener listener) {
pageListeners.remove(listener);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.IPageChangeProvider#getSelectedPage()
*/
public Object getSelectedPage() {
return getActivePageInstance();
}
/**
* Adds the form page to this editor. Form page will be loaded lazily. Its
* part control will not be created until it is activated for the first
* time.
*
* @param page
* the form page to add
*/
public int addPage(IFormPage page) throws PartInitException {
int i = super.addPage(page.getPartControl());
configurePage(i, page);
return i;
}
/**
* Adds the form page to this editor at the specified index (0-based). Form
* page will be loaded lazily. Its part control will not be created until it
* is activated for the first time.
*
* @param index
* the position to add the page at (0-based)
* @param page
* the form page to add
* @since 3.1
*/
public void addPage(int index, IFormPage page) throws PartInitException {
super.addPage(index, page.getPartControl());
configurePage(index, page);
}
/**
* Adds a simple SWT control as a page. Overrides superclass implementation
* to keep track of pages.
*
* @param control
* the page control to add
* @return the 0-based index of the newly added page
*/
public int addPage(Control control) {
int i = super.addPage(control);
try {
registerPage(-1, control);
} catch (PartInitException e) {
// cannot happen for controls
}
return i;
}
/**
* Adds a simple SWT control as a page. Overrides superclass implementation
* to keep track of pages.
*
* @param control
* the page control to add
* @param index
* the index at which to add the page (0-based)
* @since 3.1
*/
public void addPage(int index, Control control) {
super.addPage(index, control);
try {
registerPage(index, control);
} catch (PartInitException e) {
// cannot happen for controls
}
}
/**
* Tests whether the editor is dirty by checking all the pages that
* implement <code>IFormPage</code>. If none of them is dirty, the method
* delegates further processing to <code>super.isDirty()</code>.
*
* @return <code>true</code> if any of the pages in the editor are dirty,
* <code>false</code> otherwise.
* @since 3.1
*/
public boolean isDirty() {
if (pages != null) {
for (int i = 0; i < pages.size(); i++) {
Object page = pages.get(i);
if (page instanceof IFormPage) {
IFormPage fpage = (IFormPage) page;
if (fpage.isDirty())
return true;
}
}
}
return super.isDirty();
}
/**
* Commits all dirty pages in the editor. This method should
* be called as a first step of a 'save' operation.
* @param onSave <code>true</code> if commit is performed as part
* of the 'save' operation, <code>false</code> otherwise.
* @since 3.3
*/
protected void commitPages(boolean onSave) {
if (pages != null) {
for (int i = 0; i < pages.size(); i++) {
Object page = pages.get(i);
if (page instanceof IFormPage) {
IFormPage fpage = (IFormPage)page;
IManagedForm mform = fpage.getManagedForm();
if (mform != null && mform.isDirty())
mform.commit(onSave);
}
}
}
}
/**
* Adds a complete editor part to the multi-page editor.
*
* @see MultiPageEditorPart#addPage(IEditorPart, IEditorInput)
*/
public int addPage(IEditorPart editor, IEditorInput input)
throws PartInitException {
int index = super.addPage(editor, input);
if (editor instanceof IFormPage)
configurePage(index, (IFormPage) editor);
else
registerPage(-1, editor);
return index;
}
/**
* Adds a complete editor part to the multi-page editor at the specified
* position.
*
* @see MultiPageEditorPart#addPage(int, IEditorPart, IEditorInput)
* @since 3.1
*/
public void addPage(int index, IEditorPart editor, IEditorInput input)
throws PartInitException {
super.addPage(index, editor, input);
if (editor instanceof IFormPage)
configurePage(index, (IFormPage) editor);
else
registerPage(index, editor);
}
/**
* Configures the form page.
*
* @param index
* the page index
* @param page
* the page to configure
* @throws PartInitException
* if there are problems in configuring the page
*/
protected void configurePage(int index, IFormPage page)
throws PartInitException {
setPageText(index, page.getTitle());
// setPageImage(index, page.getTitleImage());
page.setIndex(index);
registerPage(index, page);
}
/**
* Overrides the superclass to remove the page from the page table.
*
* @param pageIndex
* the 0-based index of the page in the editor
*/
public void removePage(int pageIndex) {
if (pageIndex >= 0 && pageIndex < pages.size()) {
Object page = pages.get(pageIndex);
pages.remove(page);
if (page instanceof IFormPage) {
IFormPage fpage = (IFormPage) page;
if (!fpage.isEditor())
fpage.dispose();
updatePageIndices();
}
}
super.removePage(pageIndex);
}
// fix the page indices after the removal
private void updatePageIndices() {
for (int i = 0; i < pages.size(); i++) {
Object page = pages.get(i);
if (page instanceof IFormPage) {
IFormPage fpage = (IFormPage) page;
fpage.setIndex(i);
}
}
}
/**
* Called to indicate that the editor has been made dirty or the changes
* have been saved.
*/
public void editorDirtyStateChanged() {
firePropertyChange(PROP_DIRTY);
}
/**
* Disposes the pages and the toolkit after disposing the editor itself.
* Subclasses must call 'super' when reimplementing the method.
*/
public void dispose() {
super.dispose();
for (int i = 0; i < pages.size(); i++) {
Object page = pages.get(i);
if (page instanceof IFormPage) {
IFormPage fpage = (IFormPage) page;
// don't dispose source pages because they will
// be disposed as nested editors by the superclass
if (!fpage.isEditor())
fpage.dispose();
}
}
pages = null;
// toolkit may be null if editor has been instantiated
// but never created - see defect #62190
if (toolkit != null) {
toolkit.dispose();
toolkit = null;
}
}
/**
* Returns the toolkit owned by this editor.
*
* @return the toolkit object
*/
public FormToolkit getToolkit() {
return toolkit;
}
/**
* Widens the visibility of the method in the superclass.
*
* @return the active nested editor
*/
public IEditorPart getActiveEditor() {
return super.getActiveEditor();
}
/**
* Returns the current page index. The value is identical to the value of
* 'getActivePage()' except during the page switch, when this method still
* has the old active page index.
* <p>
* Another important difference is during the editor closing. When the tab
* folder is disposed, 'getActivePage()' will return -1, while this method
* will still return the last active page.
*
* @see #getActivePage
* @return the currently selected page or -1 if no page is currently
* selected
*/
protected int getCurrentPage() {
return currentPage;
}
/**
* @see MultiPageEditorPart#pageChange(int)
*/
protected void pageChange(int newPageIndex) {
// fix for windows handles
int oldPageIndex = getCurrentPage();
if (oldPageIndex != -1 && pages.size() > oldPageIndex
&& pages.get(oldPageIndex) instanceof IFormPage
&& oldPageIndex != newPageIndex) {
// Check the old page
IFormPage oldFormPage = (IFormPage) pages.get(oldPageIndex);
if (oldFormPage.canLeaveThePage() == false) {
setActivePage(oldPageIndex);
return;
}
}
// Now is the absolute last moment to create the page control.
Object page = pages.get(newPageIndex);
if (page instanceof IFormPage) {
IFormPage fpage = (IFormPage) page;
if (fpage.getPartControl() == null) {
fpage.createPartControl(getContainer());
setControl(newPageIndex, fpage.getPartControl());
fpage.getPartControl().setMenu(getContainer().getMenu());
}
}
if (oldPageIndex != -1 && pages.size() > oldPageIndex
&& pages.get(oldPageIndex) instanceof IFormPage) {
// Commit old page before activating the new one
IFormPage oldFormPage = (IFormPage) pages.get(oldPageIndex);
IManagedForm mform = oldFormPage.getManagedForm();
if (mform != null)
mform.commit(false);
}
if (pages.size() > newPageIndex
&& pages.get(newPageIndex) instanceof IFormPage)
((IFormPage) pages.get(newPageIndex)).setActive(true);
if (oldPageIndex != -1 && pages.size() > oldPageIndex
&& newPageIndex != oldPageIndex &&
pages.get(oldPageIndex) instanceof IFormPage)
((IFormPage) pages.get(oldPageIndex)).setActive(false);
// Call super - this will cause pages to switch
super.pageChange(newPageIndex);
this.currentPage = newPageIndex;
IFormPage newPage = getActivePageInstance();
if (newPage != null)
firePageChanged(new PageChangedEvent(this, newPage));
}
/**
* Sets the active page using the unique page identifier.
*
* @param pageId
* the id of the page to switch to
* @return page that was set active or <samp>null </samp> if not found.
*/
public IFormPage setActivePage(String pageId) {
for (int i = 0; i < pages.size(); i++) {
Object page = pages.get(i);
if (page instanceof IFormPage) {
IFormPage fpage = (IFormPage) page;
if (fpage.getId().equals(pageId)) {
setActivePage(i);
return fpage;
}
}
}
return null;
}
/**
* Finds the page instance that has the provided id.
*
* @param pageId
* the id of the page to find
* @return page with the matching id or <code>null</code> if not found.
*/
public IFormPage findPage(String pageId) {
for (int i = 0; i < pages.size(); i++) {
Object page = pages.get(i);
if (page instanceof IFormPage) {
IFormPage fpage = (IFormPage) pages.get(i);
if (fpage.getId().equals(pageId))
return fpage;
}
}
return null;
}
/**
* Sets the active page using the unique page identifier and sets its input
* to the provided object.
*
* @param pageId
* the id of the page to switch to
* @param pageInput
* the page input
* @return page that was set active or <samp>null </samp> if not found.
*/
public IFormPage setActivePage(String pageId, Object pageInput) {
IFormPage page = setActivePage(pageId);
if (page != null) {
IManagedForm mform = page.getManagedForm();
if (mform != null)
mform.setInput(pageInput);
}
return page;
}
/**
* Iterates through the pages calling similar method until a page is found
* that contains the desired page input.
*
* @param pageInput
* the object to select and reveal
* @return the page that accepted the request or <code>null</code> if no
* page has the desired object.
* @see #setActivePage(String, Object)
*/
public IFormPage selectReveal(Object pageInput) {
for (int i = 0; i < pages.size(); i++) {
Object page = pages.get(i);
if (page instanceof IFormPage) {
IFormPage fpage = (IFormPage) page;
if (fpage.selectReveal(pageInput))
return fpage;
}
}
return null;
}
/**
* Returns active page instance if the currently selected page index is not
* -1, or <code>null</code> if it is.
*
* @return active page instance if selected, or <code>null</code> if no
* page is currently active.
*/
public IFormPage getActivePageInstance() {
int index = getActivePage();
if (index != -1) {
Object page = pages.get(index);
if (page instanceof IFormPage)
return (IFormPage) page;
}
return null;
}
/**
* @see MultiPageEditorPart#setActivePage(int)
*/
protected void setActivePage(int pageIndex) {
// fix for window handles problem
// this should be called only when the editor is first opened
if (pages.size() > pageIndex
&& pages.get(pageIndex) instanceof IFormPage) {
super.setActivePage(pageIndex);
IFormPage activePage = (IFormPage) pages.get(pageIndex);
activePage.setActive(true);
} else
super.setActivePage(pageIndex);
updateActionBarContributor(pageIndex);
}
/**
* Notifies action bar contributor about page change.
*
* @param pageIndex
* the index of the new page
*/
protected void updateActionBarContributor(int pageIndex) {
// this is to enable the undo/redo actions before a page change has
// occurred
IEditorActionBarContributor contributor = getEditorSite()
.getActionBarContributor();
if (contributor != null
&& contributor instanceof MultiPageEditorActionBarContributor) {
((MultiPageEditorActionBarContributor) contributor)
.setActivePage(getEditor(pageIndex));
}
}
/**
* Closes the editor programmatically.
*
* @param save
* if <code>true</code>, the content should be saved before
* closing.
*/
public void close(final boolean save) {
Display display = getSite().getShell().getDisplay();
display.asyncExec(new Runnable() {
public void run() {
if (toolkit != null) {
getSite().getPage().closeEditor(FormEditor.this, save);
}
}
});
}
private void registerPage(int index, Object page) throws PartInitException {
if (!pages.contains(page)) {
if (index == -1)
pages.add(page);
else
pages.add(index, page);
}
if (page instanceof IFormPage) {
IFormPage fpage = (IFormPage) page;
if (fpage.isEditor() == false)
fpage.init(getEditorSite(), getEditorInput());
}
}
private void firePageChanged(final PageChangedEvent event) {
Object[] listeners = pageListeners.getListeners();
for (int i = 0; i < listeners.length; ++i) {
final IPageChangedListener l = (IPageChangedListener) listeners[i];
SafeRunnable.run(new SafeRunnable() {
public void run() {
l.pageChanged(event);
}
});
}
}
}