/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.ui.editors;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.ViewForm;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Item;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IPageListener;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.ide.IGotoMarker;
import org.eclipse.ui.part.EditorPart;
import org.teiid.designer.core.loading.ComponentLoadingManager;
import org.teiid.designer.core.loading.IManagedLoading;
import org.teiid.designer.ui.UiConstants;
import org.teiid.designer.ui.common.util.UiUtil;
import org.teiid.designer.ui.common.viewsupport.UiBusyIndicator;
import org.teiid.designer.ui.undo.IUndoManager;
/**
* MultiPageModelEditor is a specialization of EditorPart that contains a TabFolder for multiple editor pages, plus a splitter for
* showing and hiding panels beneath the editor pages. This class encapsulates the creation of these components from the
* ModelEditor, which manages the content of the tabs.
*
* @since 8.0
*/
public abstract class MultiPageModelEditor extends EditorPart implements IGotoMarker, IManagedLoading {
/** The panel for ModelObjectEditors. */
protected ModelObjectEditorPanel editorContainer;
/** The tab folder containing ModelEditorPages. */
CTabFolder tabFolder;
/** A splitter for the ModelObjectEditorPanel to reside beneath this editor's ViewForm */
private SashForm splitter;
/** The container widget for this multi-page editor's tabFolder */
private ViewForm viewForm;
/**
* 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 List nestedEditors = new ArrayList(3);
private List allEditors = new ArrayList(3);
private List objectEditors;
private HashMap objectEditorMap = new HashMap();
// ----------------------------
// Defect 22844 - requires a state variable to allow overriding/ignoring the internalSetFocus() method
// otherwise, the OperationObjectEditorPage gets focus when it shouldn't
// ----------------------------
private boolean ignoreInternalFocus = false;
private boolean tabFolderSelectionInProgress = false;
private Composite primaryParent;
private boolean managingLoading;
/**
* Creates an empty multi-page editor with no pages.
*/
protected MultiPageModelEditor() {
super();
}
/**
* Creates and adds a new page containing the given control to this multi-page editor. The control may be <code>null</code>,
* allowing it to be created and set later using <code>setControl</code>.
*
* @param control the control, or <code>null</code>
* @return the index of the new page
* @see #setControl
*/
public int addPage( Control control ) {
CTabItem item = createItem();
item.setControl(control);
return getPageCount() - 1;
}
/**
* Creates the pages of this multi-page editor. Subclasses must implement this method.
*/
abstract protected void createPages();
/**
* Determines if this Editor's resource is dirty. Subclasses must implement this method.
*/
abstract protected boolean isResourceDirty();
/**
* 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 must implement this method.
* </p>
*
* @param newPageIndex the index of the activated page
*/
abstract protected void pageChange( int newPageIndex );
/**
* Callback to notify a subclass that the specified IEditorPart has been lazily loaded into the tab pane for the first time.
* Allows the subclass to wire up selection and any other necessary initialization features.
*
* @param editor
*/
abstract protected void initializeEditor( IEditorPart editor );
/**
* Creates and adds a new tab page to this multi-page editor for the specified IEditorPart. This method does not call
* IEditorPart.createPartControl, which allows the lazy creation of components only when the tab is selected.
*
* @param editor the nested editor
* @param input the input for the nested editor
* @return the index of the new page
* @exception PartInitException if a new page could not be created
* @see #handlePropertyChange the handler for property change events from the nested editor
*/
public int addPage( final IEditorPart editor,
final IEditorInput input ) throws PartInitException {
final IEditorSite site = createSite(editor);
// call init first so that if an exception is thrown, we have created no new widgets
editor.init(site, input);
// create item for page
final CTabItem item = createItem();
Composite parent = new Composite(getTabFolder(), SWT.NONE);
parent.setLayout(new FillLayout());
item.setControl(parent);
// set the editor as data on the item so it can be found by the tab's SelectionListener
item.setData(editor);
allEditors.add(editor);
return getPageCount() - 1;
}
/**
* Initialize the specified IEditorPart and create it's control inside the specified CTabItem. This method allows the
* ModelEditor to completely initialize a page without knowing it's tab item.
*
* @param editor
*/
protected void initializePage( IEditorPart editor ) {
// Find the item and call the other method....
CTabItem[] items = getTabFolder().getItems();
for (int i = 0; i < items.length; i++) {
if (items[i].getData() == editor) {
initializePage(editor, items[i]);
break;
}
}
}
/**
* Initialize the specified IEditorPart and create it's control inside the specified CTabItem. This method allows the
* MultiPageModelEditor to lazily load individual IEditorParts as they are selected in the Modeler. This also hooks a property
* change listener on the nested editor.
*
* @param editor
* @param item
*/
protected void initializePage( IEditorPart editor,
CTabItem item ) {
try {
Composite parent = (Composite)item.getControl();
// call init first so that if an exception is thrown, we have created no new widgets
editor.createPartControl(parent);
editor.addPropertyListener(new IPropertyListener() {
@Override
public void propertyChanged( Object source,
int propertyId ) {
MultiPageModelEditor.this.handlePropertyChange(propertyId);
}
});
// remember the editor, as both data on the item, and in the list of editors (see field comment)
item.setData(editor);
nestedEditors.add(editor);
initializeEditor(editor);
parent.layout();
} catch (Exception e) {
// catch any Exception that occurred initializing a ModelEditorPage so that
// it can be removed and other pages function normally
String message = UiConstants.Util.getString("MultiPageModelEditor.pageInitializationErrorMessage", //$NON-NLS-1$
editor.getClass().getName());
UiConstants.Util.log(IStatus.ERROR, e, message);
String title = UiConstants.Util.getString("MultiPageModelEditor.pageInitializationErrorTitle"); //$NON-NLS-1$
MessageDialog.openError(getSite().getShell(), title, message);
}
}
protected boolean hasInitialized( IEditorPart editor ) {
return nestedEditors.contains(editor);
}
/**
* Find and load all ModelObjectEditor extensions.
*/
protected void initializeObjectEditors() {
// System.out.println("MultiPageModelEditor.initializeObjectEditors()");
// get the ModelEditorPage extension point from the plugin class
IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(UiConstants.PLUGIN_ID, UiConstants.ExtensionPoints.ModelObjectEditorPage.ID);
// get the all extensions to the ModelEditorPage extension point
IExtension[] extensions = extensionPoint.getExtensions();
objectEditors = new ArrayList(extensions.length);
// make executable extensions for every CLASSNAME
for (int i = extensions.length - 1; i >= 0; --i) {
IConfigurationElement[] elements = extensions[i].getConfigurationElements();
Object extension = null;
for (int j = 0; j < elements.length; ++j) {
try {
extension = elements[j].createExecutableExtension(UiConstants.ExtensionPoints.ModelObjectEditorPage.CLASSNAME);
if (extension instanceof ModelObjectEditorPage) {
objectEditors.add(extension);
String id = elements[j].getAttribute(UiConstants.ExtensionPoints.ModelObjectEditorPage.CLASSNAME);
objectEditorMap.put(id, extension);
// fire property changes in the ModelObjectEditorPage as if they were coming from
// this editor. this will cause the editor framework to ask this editor if it is
// dirty.
((ModelObjectEditorPage)extension).addPropertyListener(new IPropertyListener() {
@Override
public void propertyChanged( Object theSource,
final int thePropId ) {
UiUtil.runInSwtThread(new Runnable() {
@Override
public void run() {
changeProperty(thePropId);
}
}, true);
}
});
}
} catch (Exception e) {
// catch any Exception that occurred initializing a ModelEditorPage so that
// it can be removed and other pages function normally
String message = UiConstants.Util.getString("MultiPageModelEditor.pageInitializationErrorMessage", //$NON-NLS-1$
elements[j].getAttribute(UiConstants.ExtensionPoints.ModelObjectEditorPage.CLASSNAME));
UiConstants.Util.log(IStatus.ERROR, e, message);
String title = UiConstants.Util.getString("MultiPageModelEditor.pageInitializationErrorTitle"); //$NON-NLS-1$
MessageDialog.openError(getSite().getShell(), title, message);
}
}
}
// Initialize object editors
for (Iterator iter = this.objectEditors.iterator(); iter.hasNext();) {
((ModelObjectEditorPage)iter.next()).initialize(this);
} // for
}
void changeProperty( int thePropId ) {
firePropertyChange(thePropId);
}
/**
* Determine if any of the ModelObjectEditorPage extensions registered with this class want to edit the specified object
*
* @param object
* @return true if any extension can edit this object.
*/
public boolean canEditModelObject( Object object ) {
if (objectEditors != null) {
for (Iterator iter = objectEditors.iterator(); iter.hasNext();) {
ModelObjectEditorPage objectEditor = (ModelObjectEditorPage)iter.next();
if (objectEditor.canEdit(object, getActiveEditor())) {
return true;
}
}
}
return false;
}
/**
* Determine if any of the ModelObjectEditorPage extensions registered with this class can find an editable object for the
* input object.
*
* @param object
* @return true if any extension can edit this object.
*/
public Object getEditableObject( Object object ) {
if (objectEditors != null) {
Object editableObject = null;
for (Iterator iter = objectEditors.iterator(); iter.hasNext();) {
ModelObjectEditorPage objectEditor = (ModelObjectEditorPage)iter.next();
editableObject = objectEditor.getEditableObject(object);
if (editableObject != null) {
return editableObject;
}
}
}
return null;
}
/**
* Edit the specified object using the ModelObjectEditorPage extensions registered with this class for the specified object
*
* @param object the object to be displayed in the edit panel
* @param the id of a particular object editor to display; may be null.
*/
public void editModelObject( Object object,
String editorId ) {
if (editorId != null) {
ModelObjectEditorPage objectEditor = (ModelObjectEditorPage)objectEditorMap.get(editorId);
if (objectEditor != null && objectEditor.canEdit(object, getActiveEditor())) {
this.editorContainer.activateModelObjectEditor(objectEditor, object);
return;
}
}
if (objectEditors != null) {
ModelObjectEditorPage editorToOpen = null;
ArrayList canOpenList = new ArrayList(objectEditors.size());
for (Iterator iter = objectEditors.iterator(); iter.hasNext();) {
ModelObjectEditorPage objectEditor = (ModelObjectEditorPage)iter.next();
if (objectEditor.canEdit(object, getActiveEditor())) {
editorToOpen = objectEditor;
canOpenList.add(objectEditor);
}
}
if (canOpenList.size() > 1) {
// swjTODO: show a list and let the user pick. For now, log an exception and continue.
RuntimeException ex = new RuntimeException("More than one ModelObjectEditorPage found for " + object.toString()); //$NON-NLS-1$
UiConstants.Util.log(ex);
if (editorToOpen != null) {
this.editorContainer.activateModelObjectEditor(editorToOpen, object);
}
} else {
if (editorToOpen != null) {
this.editorContainer.activateModelObjectEditor(editorToOpen, object);
}
}
}
}
/**
* Obtain the current ModelObjectEditorPage displayed in this editor, or null if no such page is currently open.
*
* @return
*/
public ModelObjectEditorPage getActiveObjectEditor() {
return this.editorContainer.getActiveEditor();
}
/**
* Request that this Editor's ModelObjectEditorPanel be closed.
*
* @return true if there is no editor open or if the editor closed successfully. Returns false when the editor has vetoed the
* close, perhaps because it is holding state.
*/
public boolean closeObjectEditor() {
return this.editorContainer.close();
}
/**
* 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.
*
* @return a new container
*/
private void createContainer( Composite parent ) {
splitter = new SashForm(parent, SWT.VERTICAL);
GridData gid = new GridData();
gid.grabExcessHorizontalSpace = gid.grabExcessVerticalSpace = true;
gid.horizontalAlignment = gid.verticalAlignment = GridData.FILL;
splitter.setLayoutData(gid);
viewForm = new ViewForm(splitter, SWT.BORDER);
viewForm.setLayoutData(new GridData(GridData.FILL_BOTH));
tabFolder = new CTabFolder(viewForm, SWT.BOTTOM);
tabFolder.setLayoutData(new GridData(GridData.FILL_BOTH));
// add a SelectionListener to the tab folder that can lazily initialize editors
tabFolder.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected( final SelectionEvent e ) {
tabFolderSelectionInProgress = true;
selectTab((CTabItem)e.item);
}
});
viewForm.setContent(tabFolder);
editorContainer = new ModelObjectEditorPanel(this, splitter, SWT.NULL);
splitter.setWeights(new int[] {3, 2});
}
/**
* Creates a new CTabItem with no style bits set.
*
* @return the new item
*/
private CTabItem createItem() {
CTabItem item = new CTabItem(getTabFolder(), SWT.NONE);
return item;
}
/**
* 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.
*/
@Override
public final void createPartControl( Composite parent ) {
managingLoading = true;
this.primaryParent = parent;
ComponentLoadingManager manager = ComponentLoadingManager.getInstance();
manager.manageLoading(this);
}
private void internalCreatePartControl() {
createContainer(primaryParent);
createPages();
// set the active page (page 0 by default), unless it has already been done
if (getActivePage() == -1) setActivePage(0);
initializeObjectEditors();
new DropTarget(primaryParent, DND.DROP_NONE);
}
@Override
public void manageLoad(Properties args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
internalCreatePartControl();
managingLoading = false;
tabFolder.setSelection(0);
selectTab(tabFolder.getItem(0));
}
};
UiUtil.runInSwtThread(runnable, true);
}
private void selectTab(final CTabItem item ) {
tabFolderSelectionInProgress = true;
// System.out.println("MultiPageModelEditor.createContainer()$SelectionAdapter.widgetSelected"); //$NON-NLS-1$
UiBusyIndicator.showWhile(Display.getCurrent(), new Runnable() {
@Override
public void run() {
if( !item.isDisposed() ) {
IEditorPart editor = (IEditorPart)item.getData();
// see if this tab's IEditorPart has been initialized
if (!hasInitialized(editor)) {
initializePage(editor, item);
}
int newPageIndex = tabFolder.indexOf(item);
pageChange(newPageIndex);
}
tabFolderSelectionInProgress = false;
}
});
}
/**
* 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 ModelEditorSite(this, editor);
}
/**
* The <code>MultiPageEditorPart</code> implementation of this <code>IWorkbenchPart</code> method disposes all nested editors.
* Subclasses may extend.
*/
@Override
public void dispose() {
preDisposeEditors();
if (editorContainer != null) {
editorContainer.dispose();
}
for (int i = 0; i < nestedEditors.size(); ++i) {
IEditorPart editor = (IEditorPart)nestedEditors.get(i);
disposePart(editor);
}
// Memory leak Defect 22290 - Need to tell the ModelEditorSite to dispose also so we can clean up the PopupMenuExtender
// menu listeners that are holding on to our IEditorPart objects.
for (Iterator iter = allEditors.iterator(); iter.hasNext();) {
IEditorPart editorPart = (IEditorPart)iter.next();
if (editorPart.getSite() instanceof ModelEditorSite) {
((ModelEditorSite)editorPart.getSite()).dispose();
}
}
nestedEditors.clear();
allEditors.clear();
}
/*
* Private method used to tell any tab'd editors that haven't been initialized to clean up caches, threads etc...
*/
private void preDisposeEditors() {
Iterator iter = getAllEditors().iterator();
IEditorPart editor = null;
while (iter.hasNext()) {
editor = (IEditorPart)iter.next();
// see if this tab's IEditorPart has been initialized
if (editor instanceof ModelEditorPage) {
((ModelEditorPage)editor).preDispose();
}
}
}
/**
* 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() {
int index = getActivePage();
if (index != -1) return getEditor(index);
return null;
}
/**
* Returns the index of the currently active page, or -1 if there is no active page.
* <p>
* Subclasses should not override this method
* </p>
*
* @return the index of the active page, or -1 if there is no active page
*/
protected int getActivePage() {
CTabFolder tabFolder = getTabFolder();
if (tabFolder != null && !tabFolder.isDisposed()) return tabFolder.getSelectionIndex();
return -1;
}
/**
* @see org.eclipse.ui.part.WorkbenchPart#getAdapter(java.lang.Class)
* @since 5.5
*/
@Override
public Object getAdapter( Class adapter ) {
if (adapter.equals(IUndoManager.class)) {
return getUndoManager();
}
return super.getAdapter(adapter);
}
/**
* Obtains the current <code>IUndoManager</code> for this editor.
*
* @return the undo manager or <code>null</code>
* @since 5.5.3
*/
protected IUndoManager getUndoManager() {
return null;
}
/**
* 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 splitter;
}
/**
* Returns the control for the given page index, or <code>null</code> if no control has been set for the page. The page index
* must be valid.
* <p>
* Subclasses should not override this method
* </p>
*
* @param pageIndex the index of the page
* @return the control for the specified page, or <code>null</code> if none has been set
*/
protected Control getControl( int pageIndex ) {
return getItem(pageIndex).getControl();
}
/**
* 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 ) {
Item item = getItem(pageIndex);
if (item != null) {
Object data = item.getData();
if (data instanceof IEditorPart) {
return (IEditorPart)data;
}
}
return null;
}
/**
* Returns the tab item for the given page index (page index is 0-based). The page index must be valid.
*
* @param pageIndex the index of the page
* @return the tab item for the given page index
*/
protected CTabItem getItem( int pageIndex ) {
return getTabFolder().getItem(pageIndex);
}
/**
* Returns the number of pages in this multi-page editor.
*
* @return the number of pages
*/
protected int getPageCount() {
CTabFolder folder = getTabFolder();
// May not have been created yet, or may have been disposed.
if (folder != null && !folder.isDisposed()) return folder.getItemCount();
return 0;
}
/**
* Returns the image for the page with the given index, or <code>null</code> if no image has been set for the page. The page
* index must be valid.
*
* @param pageIndex the index of the page
* @return the image, or <code>null</code> if none
*/
protected Image getPageImage( int pageIndex ) {
return getItem(pageIndex).getImage();
}
/**
* Returns the text label for the page with the given index. Returns the empty string if no text label has been set for the
* page. The page index must be valid.
*
* @param pageIndex the index of the page
* @return the text label for the page
*/
protected String getPageText( int pageIndex ) {
return getItem(pageIndex).getText();
}
/**
* Returns the tab folder containing this multi-page editor's pages.
*
* @return the tab folder, or <code>null</code> if <code>createPartControl</code> has not been called yet
*/
public CTabFolder getTabFolder() {
return tabFolder;
}
/**
* 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.
*/
@Override
@SuppressWarnings( "unused" )
public void init( IEditorSite site,
IEditorInput input ) throws PartInitException {
setSite(site);
setInput(input);
}
/**
* 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>
*/
@Override
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;
}
}
// if no page editors are dirty see if the model object editor is dirty
if ((editorContainer != null) && (editorContainer.getActiveEditor() != null)
&& editorContainer.getActiveEditor().isDirty()) {
return true;
}
return isResourceDirty();
}
private void disposePart( final IWorkbenchPart part ) {
SafeRunner.run(new SafeRunnable() {
@Override
public void run() {
part.dispose();
}
@Override
public void handleException( Throwable e ) {
// Exception has already being logged by Core. Do nothing.
}
});
}
/**
* Removes the page with the given index from this multi-page editor. The controls for the page are disposed of; if the page
* has an editor, it is disposed of too. The page index must be valid.
*
* @param pageIndex the index of the page
* @see #addPage
*/
public void removePage( int pageIndex ) {
Assert.isTrue(pageIndex >= 0 && pageIndex < getPageCount());
// get editor (if any) before disposing item
IEditorPart editor = getEditor(pageIndex);
// dispose item before disposing editor, in case there's an exception in editor's dispose
getItem(pageIndex).dispose();
// dispose editor (if any)
if (editor != null) {
nestedEditors.remove(editor);
disposePart(editor);
}
}
/**
* Sets the currently active page.
*
* @param pageIndex the index of the page to be activated; the index must be valid
*/
protected void setActivePage( int pageIndex ) {
Assert.isTrue(pageIndex >= 0 && pageIndex < getPageCount());
getTabFolder().setSelection(pageIndex);
}
/**
* Sets the control for the given page index. The page index must be valid.
*
* @param pageIndex the index of the page
* @param control the control for the specified page, or <code>null</code> to clear the control
*/
protected void setControl( int pageIndex,
Control control ) {
getItem(pageIndex).setControl(control);
}
/**
* 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>
*/
@Override
public void setFocus() {
if( managingLoading ) {
return;
}
if (getSite().getWorkbenchWindow().getActivePage() == null) {
getSite().getWorkbenchWindow().addPageListener(new IPageListener() {
@Override
public void pageOpened( IWorkbenchPage page ) {
}
@Override
public void pageClosed( IWorkbenchPage page ) {
}
@Override
public void pageActivated( IWorkbenchPage page ) {
// System.out.println("MultiPageModelEditor.setFocus()$IPageListener.pageActivated"); //$NON-NLS-1$
page.getWorkbenchWindow().removePageListener(this);
// ----------------------------
// Defect 22844 - isIgnoreInternalFocus() then don't call internalSetFocus() method
// This cleans up simple selection causing focus to OperationEditorPage way too often
// ----------------------------
if (!isIgnoreInternalFocus()) {
internalSetFocus();
} else {
setIgnoreInternalFocus(true);
}
}
});
} else {
// ----------------------------
// Defect 22844 - isIgnoreInternalFocus() then don't call internalSetFocus() method
// This cleans up simple selection causing focus to OperationEditorPage way too often
// ----------------------------
if (!isIgnoreInternalFocus()) {
internalSetFocus();
} else {
setIgnoreInternalFocus(true);
}
}
}
public void selectPage( IEditorPart editor ) {
// Find the item and call the other method....
CTabItem[] items = getTabFolder().getItems();
for (int i = 0; i < items.length; i++) {
if (items[i].getData() == editor) {
CTabItem item = items[i];
getTabFolder().setSelection(item);
Event event = new Event();
event.widget = getTabFolder();
event.item = item;
event.type = SWT.Selection;
getTabFolder().notifyListeners(SWT.Selection, event);
break;
}
}
}
public boolean isPageSelected( ModelEditorPage page ) {
// Find the item and call the other method....
CTabItem currentSelectedTab = getTabFolder().getSelection();
if (currentSelectedTab != null && currentSelectedTab.getData() instanceof ModelEditorPage) {
return currentSelectedTab.getData() == page;
}
return false;
}
void internalSetFocus() {
// Select appropriate editor
CTabItem[] items = getTabFolder().getItems();
for (int ndx = items.length; --ndx >= 0;) {
CTabItem item = items[ndx];
ModelEditorPage editor = (ModelEditorPage)item.getData();
if (ndx == 0 || editor.isSelectedFirst(getEditorInput())) {
getTabFolder().setSelection(item);
Event event = new Event();
event.widget = getTabFolder();
event.item = item;
event.type = SWT.Selection;
if( !tabFolderSelectionInProgress ) {
getTabFolder().notifyListeners(SWT.Selection, event);
}
break;
}
}
int index = getActivePage();
if (index != -1) {
setFocus(index);
}
}
/**
* 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.
*
* @pageIndex the index of the page
*/
private void setFocus( int pageIndex ) {
if (pageIndex < 0 || pageIndex >= getPageCount()) return;
IEditorPart editor = getEditor(pageIndex);
if (editor != null) {
// if( editor instanceof ModelEditorPage ) {
// ((ModelEditorPage)editor).setInitialFocus();
// } else {
// editor.setFocus();
// }
} else {
Control control = getControl(pageIndex);
if (control != null) {
control.setFocus();
}
}
}
/**
* Sets the image for the page with the given index, or <code>null</code> to clear the image for the page. The page index must
* be valid.
*
* @param pageIndex the index of the page
* @param image the image, or <code>null</code>
*/
protected void setPageImage( int pageIndex,
Image image ) {
getItem(pageIndex).setImage(image);
}
/**
* Sets the text label for the page with the given index. The page index must be valid. The text label must not be null.
*
* @param pageIndex the index of the page
* @param text the text label
*/
protected void setPageText( int pageIndex,
String text ) {
getItem(pageIndex).setText(text);
}
/**
* Sets the text label for the page with the given index. The page index must be valid. The text label must not be null.
*
* @param pageIndex the index of the page
* @param text the text for the tooltip
*/
protected void setPageToolTipText( int index,
String text ) {
getTabFolder().getItem(index).setToolTipText(text);
}
/**
* Expands or restores the ObjectEditor panel from the bottom of the splitter to the entire width & height of the editor site.
*
* @param hideTabs
*/
void zoomObjectEditor( boolean hideTabs ) {
if (tabFolder != null && !tabFolder.isDisposed()) {
if (hideTabs) {
tabFolder.getParent().setVisible(false);
} else {
tabFolder.getParent().setVisible(true);
}
splitter.layout();
}
}
/**
* Called by ModelEditor to allow this object to notify any active ModelObjectEditor that it needs to save it's state.
*/
final protected void preSave( boolean isClosing ) {
this.editorContainer.saveEditorState(isClosing);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.IEditorPart#gotoMarker(org.eclipse.core.resources.IMarker)
*/
@Override
public void gotoMarker( IMarker marker ) {
// until there's a better way, just send the marker to all open ModelEditorPages
for (Iterator iter = this.nestedEditors.iterator(); iter.hasNext();) {
ModelEditorPage editor = (ModelEditorPage)iter.next();
IDE.gotoMarker(editor, marker);
}
}
/**
* @return Returns the allEditors.
* @since 4.2
*/
public List getAllEditors() {
return this.allEditors;
}
/**
* @return Returns the nestedEditors.
* @since 4.2
*/
public List getNestedEditors() {
return new ArrayList(this.nestedEditors);
}
public List getObjectEditors() {
return this.objectEditors;
}
/**
* Method provides individual editors and ModelEditor a way to override the internalSetFocus() call inside this class. See
* Defect 22844
*
* @return ignoreInternalFocus
* @since 5.0.2
*/
public boolean isIgnoreInternalFocus() {
return this.ignoreInternalFocus;
}
/**
* Method provides individual editors and ModelEditor a way to override the internalSetFocus() call inside this class. See
* Defect 22844
*
* @param theIgnoreInternalFocus
* @since 5.0.2
*/
public void setIgnoreInternalFocus( boolean theIgnoreInternalFocus ) {
this.ignoreInternalFocus = theIgnoreInternalFocus;
}
public void updateReadOnlyState() {
if (this.objectEditors != null) {
for (Iterator iter = this.objectEditors.iterator(); iter.hasNext();) {
((ModelObjectEditorPage)iter.next()).updateReadOnlyState();
}
}
}
}