/*******************************************************************************
* Copyright (c) 2011, 2014 Wind River Systems, 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:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.te.ui.views.editor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.tcf.te.ui.views.editor.pages.AbstractCustomFormToolkitEditorPage;
import org.eclipse.tcf.te.ui.views.editor.pages.AbstractEditorPage;
import org.eclipse.tcf.te.ui.views.extensions.EditorPageBinding;
import org.eclipse.tcf.te.ui.views.extensions.EditorPageBindingExtensionPointManager;
import org.eclipse.tcf.te.ui.views.extensions.EditorPageExtensionPointManager;
import org.eclipse.tcf.te.ui.views.interfaces.IEditorPage;
import org.eclipse.tcf.te.ui.views.interfaces.IEditorSaveAsAdapter;
import org.eclipse.tcf.te.ui.views.interfaces.IUIConstants;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IPersistable;
import org.eclipse.ui.IPersistableEditor;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.XMLMemento;
import org.eclipse.ui.forms.IFormPart;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.editor.FormEditor;
import org.eclipse.ui.forms.editor.IFormPage;
import org.eclipse.ui.internal.part.NullEditorInput;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
/**
* Editor implementation.
*/
@SuppressWarnings("restriction")
public final class Editor extends FormEditor implements IPersistableEditor, ITabbedPropertySheetPageContributor {
// The reference to an memento to restore once the editor got activated
private IMemento mementoToRestore;
// The editor event listener instance
private EditorEventListener listener;
/* (non-Javadoc)
* @see org.eclipse.ui.forms.editor.FormEditor#addPages()
*/
@Override
protected void addPages() {
// Read extension point and add the contributed pages.
IEditorInput input = getEditorInput();
// Get all applicable editor page bindings
EditorPageBinding[] bindings = EditorPageBindingExtensionPointManager.getInstance().getApplicableEditorPageBindings(input);
for (EditorPageBinding binding : bindings) {
processPageBinding(binding);
}
if (mementoToRestore != null) {
// Loop over all registered pages and pass on the editor specific memento
// to the pages which implements IPersistableEditor as well
for (Object page : pages) {
if (page instanceof IPersistableEditor) {
((IPersistableEditor)page).restoreState(mementoToRestore);
}
}
mementoToRestore = null;
}
}
/**
* Override this method to delegate the setFocus to
* the active form page.
*/
@Override
public void setFocus() {
IFormPage fpage = getActivePageInstance();
if(fpage != null) {
fpage.setFocus();
}
else {
super.setFocus();
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.WorkbenchPart#setTitleImage(org.eclipse.swt.graphics.Image)
*
* Re-export the method as public!
*/
@Override
public void setTitleImage(Image titleImage) {
super.setTitleImage(titleImage);
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.EditorPart#getTitleToolTip()
*/
@Override
public String getTitleToolTip() {
String fullToolTip = super.getTitleToolTip();
IFormPage page = getActivePageInstance();
if (page instanceof AbstractCustomFormToolkitEditorPage) {
String titleStateDecoration = ((AbstractCustomFormToolkitEditorPage)page).getFormTitleStateDecoration();
if (titleStateDecoration != null) fullToolTip += " " + titleStateDecoration; //$NON-NLS-1$
}
return fullToolTip;
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.forms.editor.FormEditor#getActivePageInstance()
*/
@Override
public IFormPage getActivePageInstance() {
int index = getActivePage();
if (index != -1) {
return getPage(index);
}
return super.getActivePageInstance();
}
/**
* Returns the page which has the specified index.
*
* @param index The page's index.
* @return The page object or null if it does not exists.
*/
private IFormPage getPage(int index) {
for (int i = 0; i < pages.size(); i++) {
Object page = pages.get(i);
if (page instanceof IFormPage) {
IFormPage fpage = (IFormPage) page;
if (fpage.getIndex() == index) {
return fpage;
}
}
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.ui.forms.editor.FormEditor#setActivePage(int)
*/
@Override
public void setActivePage(int pageIndex) {
if (getPage(pageIndex) != null) {
super.setActivePage(pageIndex);
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.MultiPageEditorPart#getEditor(int)
*/
@Override
protected IEditorPart getEditor(int pageIndex) {
return getPage(pageIndex) != null ? super.getEditor(pageIndex) : null;
}
/* (non-Javadoc)
* @see org.eclipse.ui.forms.editor.FormEditor#createPageContainer(org.eclipse.swt.widgets.Composite)
*/
@Override
protected Composite createPageContainer(Composite parent) {
// Create page container is the first method called by the final
// MultiPageEditorPart#createPartControl. Use it as hook to close
// editors with a NullEditorInput.
if (getEditorInput() instanceof NullEditorInput) {
// DisplayUtil.safeAsyncExec(new Runnable() {
// @Override
// public void run() {
// close(false);
// }
// });
close(false);
}
return super.createPageContainer(parent);
}
/**
* Update the editor page list. Pages which are not longer valid
* will be removed and pages now being valid gets added.
*/
public void updatePageList() {
// Remember the currently active page
String activePageId = getActivePageInstance() != null ? getActivePageInstance().getId() : null;
String activePageTitle = getActivePageInstance() != null ? getActivePageInstance().getTitle() : null;
// Get the editor input object
IEditorInput input = getEditorInput();
// Get all applicable editor page bindings
List<EditorPageBinding> bindings = new ArrayList<EditorPageBinding>(Arrays.asList(EditorPageBindingExtensionPointManager.getInstance().getApplicableEditorPageBindings(input)));
// Get a copy of the currently added pages
List<Object> oldPages = pages != null ? new ArrayList<Object>(Arrays.asList(pages.toArray())) : new ArrayList<Object>();
// Loop through the old pages and determine if the page is still applicable
Iterator<Object> iterator = oldPages.iterator();
while (iterator.hasNext()) {
Object element = iterator.next();
// Skip over pages not being a form page.
if (!(element instanceof IFormPage)) {
continue;
}
IFormPage page = (IFormPage)element;
// Find the corresponding page binding
EditorPageBinding binding = null;
for (EditorPageBinding candidate : bindings) {
if (candidate.getPageId().equals(page.getId())) {
binding = candidate;
break;
}
}
if (binding != null) {
// Found binding -> page is still applicable
bindings.remove(binding);
} else {
// No binding found -> page is not longer applicable
removePage(pages.indexOf(page));
}
}
// If the are remaining bindings left, this are new pages.
// --> Process them now
for (EditorPageBinding binding : bindings) {
processPageBinding(binding);
}
// If possible, restore the active page
if (activePageId != null) {
int index = getIndexOf(activePageId);
if (index == -1 && activePageTitle != null) {
index = getIndexOfByTitle(activePageTitle);
}
if (index != -1) setActivePage(index);
}
}
/**
* Process the given editor page binding.
*
* @param binding The editor page binding. Must not be <code>null</code>.
*/
protected void processPageBinding(EditorPageBinding binding) {
Assert.isNotNull(binding);
String pageId = binding.getPageId();
if (pageId != null) {
// Get the corresponding editor page instance
IEditorPage page = EditorPageExtensionPointManager.getInstance().getEditorPage(pageId, true);
if (page != null) {
try {
// Associate this editor with the page instance.
// This is typically done in the constructor, but we are
// utilizing a default constructor to instantiate the page.
page.initialize(this);
// Read in the "insertBefore" and "insertAfter" properties of the binding
String insertBefore = binding.getInsertBefore().trim();
String insertAfter = binding.getInsertAfter().trim();
boolean pageAdded = false;
// insertBefore will be processed before insertAfter.
if (!"".equals(insertBefore)) { //$NON-NLS-1$
String[] pageIds = insertBefore.split(","); //$NON-NLS-1$
for (String insertBeforePageId : pageIds) {
// If it is "first", we insert the page at index 0
if ("first".equalsIgnoreCase(insertBeforePageId)) { //$NON-NLS-1$
if (getIndexOf(page.getId()) == -1) {
addPage(0, page);
}
pageAdded = true;
break;
}
// Find the index of the page we shall insert this page before
int index = getIndexOf(insertBeforePageId);
if (index != -1) {
if (getIndexOf(page.getId()) == -1) {
addPage(index, page);
}
pageAdded = true;
break;
}
}
}
// If the page hasn't been added till now, process insertAfter
if (!pageAdded && !"".equals(insertAfter)) { //$NON-NLS-1$
String[] pageIds = insertAfter.split(","); //$NON-NLS-1$
for (String insertAfterPageId : pageIds) {
// If it is "last", we insert the page at the end
if ("last".equalsIgnoreCase(insertAfterPageId)) { //$NON-NLS-1$
if (getIndexOf(page.getId()) == -1) {
addPage(page);
}
pageAdded = true;
break;
}
// Find the index of the page we shall insert this page after
int index = getIndexOf(insertAfterPageId);
if (index != -1 && index + 1 < pages.size()) {
if (getIndexOf(page.getId()) == -1) {
addPage(index + 1, page);
}
pageAdded = true;
break;
}
}
}
// Add the page to the end if not added otherwise
if (!pageAdded && getIndexOf(page.getId()) == -1) {
addPage(page);
}
} catch (PartInitException e) { /* ignored on purpose */ }
}
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.forms.editor.FormEditor#configurePage(int, org.eclipse.ui.forms.editor.IFormPage)
*/
@Override
protected void configurePage(int index, IFormPage page) throws PartInitException {
super.configurePage(index, page);
// Update the page text as it may have changed after initializing
// the editor input
setPageText(index, page.getTitle());
}
/**
* Returns the index of the page with the given id.
*
* @param pageId The page id. Must not be <code>null</code>.
* @return The page index or <code>-1</code> if not found.
*/
private int getIndexOf(String pageId) {
Assert.isNotNull(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)) {
return i;
}
}
}
return -1;
}
/**
* Returns the index of the page with the given title.
*
* @param pageTitle The page title. Must not be <code>null</code>.
* @return The page index or <code>-1</code> if not found.
*/
private int getIndexOfByTitle(String pageTitle) {
Assert.isNotNull(pageTitle);
for (int i = 0; i < pages.size(); i++) {
Object page = pages.get(i);
if (page instanceof IFormPage) {
IFormPage fpage = (IFormPage)page;
if (fpage.getTitle().equals(pageTitle)) {
return i;
}
}
}
return -1;
}
/* (non-Javadoc)
* @see org.eclipse.ui.forms.editor.FormPage#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
*/
@Override
public void init(IEditorSite site, IEditorInput input) throws PartInitException {
super.init(site, input);
// Update the part name
if (!"".equals(input.getName())) { //$NON-NLS-1$
setPartName(input.getName());
}
// Dispose an existing event listener instance
if (listener != null) { listener.dispose(); listener = null; }
// Create the event listener. The event listener does register itself.
listener = new EditorEventListener(this);
}
/**
* Update the editor part name based on the current editor input.
*/
public void updatePartName() {
IEditorInput input = getEditorInput();
String oldPartName = getPartName();
if (input instanceof EditorInput) {
// Reset the editor input name to trigger recalculation
((EditorInput)input).name = null;
// If the name changed, apply the new name
if (!oldPartName.equals(input.getName())) {
setPartName(input.getName());
}
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.forms.editor.FormEditor#dispose()
*/
@Override
public void dispose() {
// Dispose an existing event listener instance
if (listener != null) { listener.dispose(); listener = null; }
super.dispose();
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
public void doSave(IProgressMonitor monitor) {
// The pages may require some save pre processing
for (Object page : pages) {
if (page instanceof AbstractEditorPage) {
((AbstractEditorPage)page).preDoSave(monitor);
}
}
// Commit the page changes
commitPages(true);
// The pages may require some save post processing
for (Object page : pages) {
if (page instanceof AbstractEditorPage) {
((AbstractEditorPage)page).postDoSave(monitor);
}
}
editorDirtyStateChanged();
}
public void revert() {
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()) {
for (int j = 0; j < mform.getParts().length; j++) {
IFormPart part = mform.getParts()[j];
part.refresh();
}
}
}
}
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.EditorPart#doSaveAs()
*/
@Override
public void doSaveAs() {
IEditorSaveAsAdapter adapter = getEditorInput() != null ? (IEditorSaveAsAdapter)Platform.getAdapterManager().getAdapter(getEditorInput(), IEditorSaveAsAdapter.class) : null;
if (adapter != null) {
Object newNode = adapter.doSaveAs(getEditorInput());
if (newNode != null) {
setInput(new EditorInput(newNode));
updatePartName();
updatePageList();
for (Object page : pages) {
if (page instanceof AbstractEditorPage) {
((AbstractEditorPage) page).init(getEditorSite(), getEditorInput());
}
}
}
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
*/
@Override
public boolean isSaveAsAllowed() {
IEditorSaveAsAdapter adapter = getEditorInput() != null ? (IEditorSaveAsAdapter)Platform.getAdapterManager().getAdapter(getEditorInput(), IEditorSaveAsAdapter.class) : null;
if (adapter != null) {
return adapter.isSaveAsAllowed(getEditorInput());
}
return false;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPersistableEditor#restoreState(org.eclipse.ui.IMemento)
*/
@Override
public void restoreState(IMemento memento) {
// Get the editor specific memento
mementoToRestore = internalGetMemento(memento);
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPersistable#saveState(org.eclipse.ui.IMemento)
*/
@Override
public void saveState(IMemento memento) {
// Get the editor specific memento
memento = internalGetMemento(memento);
// Loop over all registered pages and pass on the editor specific memento
// to the pages which implements IPersistable as well
for (Object page : pages) {
if (page instanceof IPersistable) {
((IPersistable)page).saveState(memento);
}
}
}
/**
* Internal helper method accessing our editor local child memento
* from the given parent memento.
*/
private IMemento internalGetMemento(IMemento memento) {
// Assume the editor memento to be the same as the parent memento
IMemento editorMemento = memento;
// If the parent memento is not null, create a child within the parent
if (memento != null) {
editorMemento = memento.getChild(Editor.class.getName());
if (editorMemento == null) {
editorMemento = memento.createChild(Editor.class.getName());
}
} else {
// The parent memento is null. Create a new internal instance
// of a XMLMemento. This case is happening if the user switches
// to another perspective an the view becomes visible by this switch.
editorMemento = XMLMemento.createWriteRoot(Editor.class.getName());
}
return editorMemento;
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.MultiPageEditorPart#getAdapter(java.lang.Class)
*/
@Override
public Object getAdapter(Class adapter) {
if (adapter == IPropertySheetPage.class) {
return new TabbedPropertySheetPage(this);
}
// We pass on the adapt request to the currently active page
Object adapterInstance = getActivePageInstance() != null ? getActivePageInstance().getAdapter(adapter) : null;
if (adapterInstance == null) {
// If failed to adapt via the currently active page, pass on to the super implementation
adapterInstance = super.getAdapter(adapter);
}
return adapterInstance;
}
/* (non-Javadoc)
* @see org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor#getContributorId()
*/
@Override
public String getContributorId() {
return IUIConstants.TABBED_PROPERTIES_CONTRIBUTOR_ID;
}
/**
* Fires a property changed event.
*
* @param propertyId the id of the property that changed
*/
@Override
public final void firePropertyChange(final int propertyId) {
super.firePropertyChange(propertyId);
}
}