| /******************************************************************************* |
| * Copyright (c) 2000, 2018 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.pde.internal.ui.editor; |
| |
| import java.io.*; |
| import java.util.ArrayList; |
| import org.eclipse.core.filesystem.EFS; |
| import org.eclipse.core.filesystem.IFileStore; |
| import org.eclipse.core.resources.*; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.jface.action.*; |
| import org.eclipse.jface.dialogs.IDialogSettings; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.ITextSelection; |
| import org.eclipse.jface.text.source.IAnnotationModel; |
| import org.eclipse.jface.viewers.*; |
| import org.eclipse.pde.core.IBaseModel; |
| import org.eclipse.pde.core.IWritable; |
| import org.eclipse.pde.internal.core.IWorkspaceModel; |
| import org.eclipse.pde.internal.core.plugin.IWritableDelimiter; |
| import org.eclipse.pde.internal.core.util.XMLComponentRegistry; |
| import org.eclipse.pde.internal.ui.*; |
| import org.eclipse.pde.internal.ui.editor.context.*; |
| import org.eclipse.pde.internal.ui.editor.plugin.MissingResourcePage; |
| import org.eclipse.pde.internal.ui.editor.plugin.OverviewPage; |
| import org.eclipse.pde.internal.ui.util.PDEModelUtility; |
| import org.eclipse.search.ui.text.ISearchEditorAccess; |
| import org.eclipse.search.ui.text.Match; |
| import org.eclipse.swt.custom.CTabFolder; |
| import org.eclipse.swt.dnd.*; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.ui.*; |
| import org.eclipse.ui.actions.ActionFactory; |
| import org.eclipse.ui.forms.IManagedForm; |
| import org.eclipse.ui.forms.editor.FormEditor; |
| import org.eclipse.ui.forms.editor.IFormPage; |
| import org.eclipse.ui.forms.widgets.FormToolkit; |
| import org.eclipse.ui.ide.*; |
| import org.eclipse.ui.part.MultiPageEditorPart; |
| import org.eclipse.ui.part.MultiPageEditorSite; |
| import org.eclipse.ui.views.contentoutline.IContentOutlinePage; |
| |
| /** |
| * A simple multi-page form editor that uses Eclipse Forms support. Example |
| * plug-in is configured to create one instance of form colors that is shared |
| * between multiple editor instances. |
| */ |
| public abstract class PDEFormEditor extends FormEditor implements IInputContextListener, IGotoMarker, ISearchEditorAccess { |
| /** |
| * Updates the OutlinePage selection. |
| * |
| * @since 3.0 |
| */ |
| public class PDEFormEditorChangeListener implements ISelectionChangedListener { |
| |
| /** |
| * Installs this selection changed listener with the given selection |
| * provider. If the selection provider is a post selection provider, |
| * post selection changed events are the preferred choice, otherwise |
| * normal selection changed events are requested. |
| * |
| * @param selectionProvider |
| */ |
| public void install(ISelectionProvider selectionProvider) { |
| if (selectionProvider == null) { |
| return; |
| } |
| |
| if (selectionProvider instanceof IPostSelectionProvider) { |
| IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider; |
| provider.addPostSelectionChangedListener(this); |
| } else { |
| selectionProvider.addSelectionChangedListener(this); |
| } |
| } |
| |
| @Override |
| public void selectionChanged(SelectionChangedEvent event) { |
| if (PDEPlugin.getDefault().getPreferenceStore().getBoolean("ToggleLinkWithEditorAction.isChecked")) //$NON-NLS-1$ |
| if (getFormOutline() != null) { |
| getFormOutline().setSelection(event.getSelection()); |
| } |
| } |
| |
| /** |
| * Removes this selection changed listener from the given selection |
| * provider. |
| */ |
| public void uninstall(ISelectionProvider selectionProvider) { |
| if (selectionProvider == null) { |
| return; |
| } |
| |
| if (selectionProvider instanceof IPostSelectionProvider) { |
| IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider; |
| provider.removePostSelectionChangedListener(this); |
| } else { |
| selectionProvider.removeSelectionChangedListener(this); |
| } |
| } |
| |
| } |
| |
| private static final String F_DIALOG_EDITOR_SECTION_KEY = "pde-form-editor"; //$NON-NLS-1$ |
| |
| /** |
| * The editor selection changed listener. |
| * |
| * @since 3.0 |
| */ |
| private PDEFormEditorChangeListener fEditorSelectionChangedListener; |
| private Clipboard clipboard; |
| private Menu fContextMenu; |
| protected InputContextManager fInputContextManager; |
| private ISortableContentOutlinePage fFormOutline; |
| private PDEMultiPageContentOutline fContentOutline; |
| private String fLastActivePageId; |
| private boolean fLastDirtyState; |
| private boolean fError; |
| |
| private static class PDEMultiPageEditorSite extends MultiPageEditorSite { |
| public PDEMultiPageEditorSite(MultiPageEditorPart multiPageEditor, IEditorPart editor) { |
| super(multiPageEditor, editor); |
| } |
| |
| @Override |
| public IEditorActionBarContributor getActionBarContributor() { |
| PDEFormEditor editor = (PDEFormEditor) getMultiPageEditor(); |
| PDEFormEditorContributor contributor = editor.getContributor(); |
| return contributor.getSourceContributor(); |
| } |
| |
| @Override |
| public IWorkbenchPart getPart() { |
| return getMultiPageEditor(); |
| } |
| } |
| |
| public PDEFormEditor() { |
| PDEPlugin.getDefault().getLabelProvider().connect(this); |
| XMLComponentRegistry.Instance().connect(this); |
| fInputContextManager = createInputContextManager(); |
| } |
| |
| /** |
| * We must override nested site creation so that we properly pass the source |
| * editor contributor when asked. |
| */ |
| @Override |
| protected IEditorSite createSite(IEditorPart editor) { |
| return new PDEMultiPageEditorSite(this, editor); |
| } |
| |
| public IProject getCommonProject() { |
| return fInputContextManager.getCommonProject(); |
| } |
| |
| public IBaseModel getAggregateModel() { |
| if (fInputContextManager != null) |
| return fInputContextManager.getAggregateModel(); |
| return null; |
| } |
| |
| protected abstract InputContextManager createInputContextManager(); |
| |
| /** |
| * Tests whether this editor has a context with a provided id. The test can |
| * be used to check whether to add certain pages. |
| * |
| * @param contextId |
| * @return <code>true</code> if provided context is present, |
| * <code>false</code> otherwise. |
| */ |
| public boolean hasInputContext(String contextId) { |
| return fInputContextManager.hasContext(contextId); |
| } |
| |
| public InputContextManager getContextManager() { |
| return fInputContextManager; |
| } |
| |
| protected void createInputContexts(InputContextManager contextManager) { |
| IEditorInput input = getEditorInput(); |
| if (input instanceof IFileEditorInput) { |
| // resource - find the project |
| createResourceContexts(contextManager, (IFileEditorInput) input); |
| } else if (input instanceof IStorageEditorInput) { |
| createStorageContexts(contextManager, (IStorageEditorInput) input); |
| } else if (input instanceof IURIEditorInput) { |
| IURIEditorInput uriEditorInput = (IURIEditorInput) input; |
| try { |
| IFileStore store = EFS.getStore(uriEditorInput.getURI()); |
| if (!EFS.SCHEME_FILE.equals(store.getFileSystem().getScheme())) |
| return; |
| FileStoreEditorInput sinput = new FileStoreEditorInput(store); |
| createSystemFileContexts(contextManager, sinput); |
| } catch (CoreException e) { |
| return; |
| } |
| } |
| } |
| |
| protected abstract void createResourceContexts(InputContextManager contexts, IFileEditorInput input); |
| |
| protected abstract void createSystemFileContexts(InputContextManager contexts, FileStoreEditorInput input); |
| |
| protected abstract void createStorageContexts(InputContextManager contexts, IStorageEditorInput input); |
| |
| @Override |
| protected FormToolkit createToolkit(Display display) { |
| // Create a toolkit that shares colors between editors. |
| return new FormToolkit(PDEPlugin.getDefault().getFormColors(display)); |
| } |
| |
| /* |
| * When subclassed, don't forget to call 'super' |
| */ |
| @Override |
| protected void createPages() { |
| clipboard = new Clipboard(getContainer().getDisplay()); |
| MenuManager manager = new MenuManager(); |
| IMenuListener listener = this::contextMenuAboutToShow; |
| manager.setRemoveAllWhenShown(true); |
| manager.addMenuListener(listener); |
| fContextMenu = manager.createContextMenu(getContainer()); |
| getContainer().setMenu(fContextMenu); |
| createInputContexts(fInputContextManager); |
| super.createPages(); |
| fInputContextManager.addInputContextListener(this); |
| String pageToShow = computeInitialPageId(); |
| if (pageToShow != null) |
| setActivePage(pageToShow); |
| updateTitle(); |
| if (getPageCount() == 1 && getContainer() instanceof CTabFolder) { |
| ((CTabFolder) getContainer()).setTabHeight(0); |
| } |
| |
| PDEModelUtility.connect(this); |
| } |
| |
| @Override |
| protected void pageChange(int newPageIndex) { |
| super.pageChange(newPageIndex); |
| IFormPage page = getActivePageInstance(); |
| updateContentOutline(page); |
| if (page != null) { |
| fLastActivePageId = page.getId(); |
| } |
| } |
| |
| @Override |
| public void setFocus() { |
| super.setFocus(); |
| IFormPage page = getActivePageInstance(); |
| // Could be done on setActive in PDEFormPage; |
| // but setActive only handles page switches and not focus events |
| if ((page != null) && (page instanceof PDEFormPage)) { |
| ((PDEFormPage) page).updateFormSelection(); |
| } |
| } |
| |
| public Clipboard getClipboard() { |
| return clipboard; |
| } |
| |
| protected void contextMenuAboutToShow(IMenuManager manager) { |
| PDEFormEditorContributor contributor = getContributor(); |
| IFormPage page = getActivePageInstance(); |
| if (page instanceof PDEFormPage) |
| ((PDEFormPage) page).contextMenuAboutToShow(manager); |
| if (contributor != null) |
| contributor.contextMenuAboutToShow(manager); |
| } |
| |
| public PDEFormEditorContributor getContributor() { |
| return (PDEFormEditorContributor) getEditorSite().getActionBarContributor(); |
| } |
| |
| protected String computeInitialPageId() { |
| String firstPageId = null; |
| String storedFirstPageId = loadDefaultPage(); |
| if (storedFirstPageId != null) |
| firstPageId = storedFirstPageId; |
| // Regardless what is the stored value, |
| // use source page if model is not valid |
| String invalidContextId = getFirstInvalidContextId(); |
| if (invalidContextId != null) |
| return invalidContextId; |
| return firstPageId; |
| } |
| |
| private String getFirstInvalidContextId() { |
| InputContext[] invalidContexts = fInputContextManager.getInvalidContexts(); |
| if (invalidContexts.length == 0) |
| return null; |
| // If primary context is among the invalid ones, return that. |
| for (InputContext context : invalidContexts) { |
| if (context.isPrimary()) |
| return context.getId(); |
| } |
| // Return the first one |
| return invalidContexts[0].getId(); |
| } |
| |
| @Override |
| public String getTitle() { |
| if (fInputContextManager == null) |
| return super.getTitle(); |
| InputContext context = fInputContextManager.getPrimaryContext(); |
| if (context == null) |
| return super.getTitle(); |
| return context.getInput().getName(); |
| } |
| |
| public void updateTitle() { |
| firePropertyChange(IWorkbenchPart.PROP_TITLE); |
| } |
| |
| public String getTitleProperty() { |
| return ""; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public void doSave(IProgressMonitor monitor) { |
| commitPages(true); |
| fInputContextManager.save(monitor); |
| editorDirtyStateChanged(); |
| } |
| |
| /** |
| * |
| */ |
| public void doRevert() { |
| IFormPage formPage = getActivePageInstance(); |
| // If the active page is a form page, commit all of its dirty field |
| // values to the model |
| if ((formPage != null) && (formPage instanceof PDEFormPage)) { |
| formPage.getManagedForm().commit(true); |
| } |
| // If the editor has source pages, revert them |
| // Reverting the source page fires events to the associated form pages |
| // which will cause all their values to be updated |
| boolean reverted = doRevertSourcePages(); |
| // If the editor does not have any source pages, revert the form pages |
| // by directly reloading the underlying model. |
| // Reloading the model fires a world changed event to all form pages |
| // causing them to update their values |
| if (reverted == false) { |
| reverted = doRevertFormPage(); |
| } |
| // If the revert operation was performed disable the revert action and |
| // fire the dirty event |
| if (reverted) { |
| editorDirtyStateChanged(); |
| } |
| } |
| |
| private boolean doRevertFormPage() { |
| boolean reverted = false; |
| IBaseModel model = getAggregateModel(); |
| if (model instanceof IWorkspaceModel) { |
| IWorkspaceModel workspaceModel = (IWorkspaceModel) model; |
| workspaceModel.reload(); |
| reverted = true; |
| } |
| return reverted; |
| } |
| |
| private boolean doRevertSourcePages() { |
| boolean reverted = false; |
| IFormPage[] pages = getPages(); |
| for (IFormPage page : pages) { |
| if (page instanceof PDESourcePage) { |
| PDESourcePage sourcePage = (PDESourcePage) page; |
| // Flush any pending editor operations into the document |
| // so that the revert operation executes (revert operation is |
| // aborted if the current document has not changed) |
| // This happens when a form page field is modified. |
| // The source page is not updated until a source operation is |
| // performed or the source page is made active and possibly |
| // in some other cases. |
| sourcePage.getInputContext().flushEditorInput(); |
| // Revert the source page to the contents of the last save to file |
| sourcePage.doRevertToSaved(); |
| reverted = true; |
| } |
| } |
| return reverted; |
| } |
| |
| public void doRevert(IEditorInput input) { |
| IFormPage currentPage = getActivePageInstance(); |
| if (currentPage != null && currentPage instanceof PDEFormPage) |
| ((PDEFormPage) currentPage).cancelEdit(); |
| InputContext context = fInputContextManager.getContext(input); |
| IFormPage page = findPage(context.getId()); |
| if (page != null && page instanceof PDESourcePage) { |
| PDESourcePage spage = (PDESourcePage) page; |
| spage.doRevertToSaved(); |
| } |
| editorDirtyStateChanged(); |
| } |
| |
| public void flushEdits() { |
| IFormPage[] pages = getPages(); |
| IManagedForm mForm = pages[getActivePage()].getManagedForm(); |
| if (mForm != null) |
| mForm.commit(false); |
| for (IFormPage page : pages) { |
| if (page instanceof PDESourcePage) { |
| PDESourcePage sourcePage = (PDESourcePage) page; |
| sourcePage.getInputContext().flushEditorInput(); |
| } |
| } |
| } |
| |
| public String getContextIDForSaveAs() { |
| // Sub-classes must override this method and the isSaveAsAllowed |
| // method to perform save as operations |
| return null; |
| } |
| |
| @Override |
| public void doSaveAs() { |
| try { |
| // Get the context for which the save as operation should be |
| // performed |
| String contextID = getContextIDForSaveAs(); |
| // Perform the same as operation |
| getContextManager().saveAs(getProgressMonitor(), contextID); |
| // Get the new editor input |
| IEditorInput input = getContextManager().findContext(contextID).getInput(); |
| // Store the new editor input |
| setInputWithNotify(input); |
| // Update the title of the editor using the name of the new editor |
| // input |
| setPartName(input.getName()); |
| // Fire a property change accordingly |
| firePropertyChange(PROP_DIRTY); |
| } catch (InterruptedException e) { |
| // Ignore |
| } catch (Exception e) { |
| String title = PDEUIMessages.PDEFormEditor_errorTitleProblemSaveAs; |
| String message = PDEUIMessages.PDEFormEditor_errorMessageSaveNotCompleted; |
| if (e.getMessage() != null) { |
| message = message + ' ' + e.getMessage(); |
| } |
| PDEPlugin.logException(e, title, message); |
| } |
| } |
| |
| @Override |
| public boolean isSaveAsAllowed() { |
| return false; |
| } |
| |
| /** |
| * |
| */ |
| private void storeDefaultPage() { |
| IEditorInput input = getEditorInput(); |
| String pageId = fLastActivePageId; |
| if (pageId == null) |
| return; |
| if (input instanceof IFileEditorInput) { |
| // Triggered by opening a file in the workspace |
| // e.g. From the Package Explorer View |
| setPropertyEditorPageKey((IFileEditorInput) input, pageId); |
| } else if (input instanceof IStorageEditorInput) { |
| // Triggered by opening a file NOT in the workspace |
| // e.g. From the Plug-in View |
| setDialogEditorPageKey(pageId); |
| } |
| } |
| |
| /** |
| * @param pageID |
| */ |
| protected void setDialogEditorPageKey(String pageID) { |
| // Use one global setting for all files belonging to a given editor |
| // type. Use the editor ID as the key. |
| // Could use the storage editor input to get the underlying file |
| // and use it as a unique key; but, the dialog settings file will |
| // grow out of control and we do not need that level of granularity |
| IDialogSettings section = getSettingsSection(); |
| section.put(getEditorID(), pageID); |
| } |
| |
| /** |
| * |
| */ |
| protected String getDialogEditorPageKey() { |
| // Use one global setting for all files belonging to a given editor |
| // type. Use the editor ID as the key. |
| // Could use the storage editor input to get the underlying file |
| // and use it as a unique key; but, the dialog settings file will |
| // grow out of control and we do not need that level of granularity |
| IDialogSettings section = getSettingsSection(); |
| return section.get(getEditorID()); |
| } |
| |
| protected abstract String getEditorID(); |
| |
| protected void setPropertyEditorPageKey(IFileEditorInput input, String pageId) { |
| // We are using the file itself to persist the editor page key property |
| // The value persists even after the editor is closed |
| IFile file = input.getFile(); |
| try { |
| // Set the editor page ID as a persistent property on the file |
| file.setPersistentProperty(IPDEUIConstants.PROPERTY_EDITOR_PAGE_KEY, pageId); |
| } catch (CoreException e) { |
| // Ignore |
| } |
| } |
| |
| private String loadDefaultPage() { |
| IEditorInput input = getEditorInput(); |
| if (input instanceof IFileEditorInput) { |
| // Triggered by opening a file in the workspace |
| // e.g. From the Package Explorer View |
| return getPropertyEditorPageKey((IFileEditorInput) input); |
| } else if (input instanceof IStorageEditorInput) { |
| // Triggered by opening a file NOT in the workspace |
| // e.g. From the Plug-in View |
| return getDialogEditorPageKey(); |
| } |
| return null; |
| } |
| |
| protected String getPropertyEditorPageKey(IFileEditorInput input) { |
| // We are using the file itself to persist the editor page key property |
| // The value persists even after the editor is closed |
| IFile file = input.getFile(); |
| // Get the persistent editor page key from the file |
| try { |
| return file.getPersistentProperty(IPDEUIConstants.PROPERTY_EDITOR_PAGE_KEY); |
| } catch (CoreException e) { |
| return null; |
| } |
| } |
| |
| @Override |
| public void dispose() { |
| storeDefaultPage(); |
| if (fEditorSelectionChangedListener != null) { |
| fEditorSelectionChangedListener.uninstall(getSite().getSelectionProvider()); |
| fEditorSelectionChangedListener = null; |
| } |
| //setSelection(new StructuredSelection()); |
| PDEPlugin.getDefault().getLabelProvider().disconnect(this); |
| XMLComponentRegistry.Instance().disconnect(this); |
| PDEModelUtility.disconnect(this); |
| if (clipboard != null) { |
| clipboard.dispose(); |
| clipboard = null; |
| } |
| super.dispose(); |
| fInputContextManager.dispose(); |
| fInputContextManager = null; |
| } |
| |
| @Override |
| public boolean isDirty() { |
| fLastDirtyState = computeDirtyState(); |
| return fLastDirtyState; |
| } |
| |
| private boolean computeDirtyState() { |
| IFormPage page = getActivePageInstance(); |
| if ((page != null && page.isDirty()) || (fInputContextManager != null && fInputContextManager.isDirty())) |
| return true; |
| return super.isDirty(); |
| } |
| |
| public boolean getLastDirtyState() { |
| return fLastDirtyState; |
| } |
| |
| public void fireSaveNeeded(String contextId, boolean notify) { |
| if (contextId == null) |
| return; |
| InputContext context = fInputContextManager.findContext(contextId); |
| if (context != null) |
| fireSaveNeeded(context.getInput(), notify); |
| } |
| |
| public void fireSaveNeeded(IEditorInput input, boolean notify) { |
| if (notify) |
| editorDirtyStateChanged(); |
| if (isDirty()) |
| validateEdit(input); |
| } |
| |
| @Override |
| public void editorDirtyStateChanged() { |
| super.editorDirtyStateChanged(); |
| PDEFormEditorContributor contributor = getContributor(); |
| if (contributor != null) |
| contributor.updateActions(); |
| } |
| |
| private void validateEdit(IEditorInput input) { |
| final InputContext context = fInputContextManager.getContext(input); |
| if (!context.validateEdit()) { |
| getSite().getShell().getDisplay().asyncExec(() -> { |
| doRevert(context.getInput()); |
| context.setValidated(false); |
| }); |
| } |
| } |
| |
| private IDialogSettings getSettingsSection() { |
| // Store global settings that will persist when the editor is closed |
| // in the dialog settings (This is cheating) |
| // Get the dialog settings |
| IDialogSettings root = PDEPlugin.getDefault().getDialogSettings(); |
| // Get the dialog section reserved for PDE form editors |
| IDialogSettings section = root.getSection(F_DIALOG_EDITOR_SECTION_KEY); |
| // If the section is not defined, define it |
| if (section == null) { |
| section = root.addNewSection(F_DIALOG_EDITOR_SECTION_KEY); |
| } |
| return section; |
| } |
| |
| @Override |
| public void gotoMarker(IMarker marker) { |
| IResource resource = marker.getResource(); |
| InputContext context = fInputContextManager.findContext(resource); |
| if (context == null) |
| return; |
| IFormPage page = getActivePageInstance(); |
| if (!context.getId().equals(page.getId())) |
| page = setActivePage(context.getId()); |
| IDE.gotoMarker(page, marker); |
| } |
| |
| public void openToSourcePage(Object object, int offset, int length) { |
| InputContext context = null; |
| if (object instanceof InputContext) { |
| context = (InputContext) object; |
| } else { |
| context = getInputContext(object); |
| } |
| if (context != null) { |
| PDESourcePage page = (PDESourcePage) setActivePage(context.getId()); |
| if (page != null) |
| page.selectAndReveal(offset, length); |
| } |
| } |
| |
| public void setSelection(ISelection selection) { |
| getSite().getSelectionProvider().setSelection(selection); |
| getContributor().updateSelectableActions(selection); |
| } |
| |
| public ISelection getSelection() { |
| return getSite().getSelectionProvider().getSelection(); |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public <T> T getAdapter(Class<T> key) { |
| if (key.equals(IContentOutlinePage.class)) { |
| return (T) getContentOutline(); |
| } |
| if (key.equals(IGotoMarker.class)) { |
| return (T) this; |
| } |
| if (key.equals(ISearchEditorAccess.class)) { |
| return (T) this; |
| } |
| return super.getAdapter(key); |
| } |
| |
| public Menu getContextMenu() { |
| return fContextMenu; |
| } |
| |
| public PDEMultiPageContentOutline getContentOutline() { |
| if (fContentOutline == null || fContentOutline.isDisposed()) { |
| fContentOutline = new PDEMultiPageContentOutline(this); |
| updateContentOutline(getActivePageInstance()); |
| } |
| return fContentOutline; |
| } |
| |
| /** |
| * |
| * @return outline page or null |
| */ |
| protected ISortableContentOutlinePage getFormOutline() { |
| if (fFormOutline == null) { |
| fFormOutline = createContentOutline(); |
| if (fFormOutline != null) { |
| fEditorSelectionChangedListener = new PDEFormEditorChangeListener(); |
| fEditorSelectionChangedListener.install(getSite().getSelectionProvider()); |
| } |
| } |
| return fFormOutline; |
| } |
| |
| abstract protected ISortableContentOutlinePage createContentOutline(); |
| |
| private void updateContentOutline(IFormPage page) { |
| if (fContentOutline == null) |
| return; |
| ISortableContentOutlinePage outline = null; |
| if (page instanceof PDESourcePage) { |
| outline = ((PDESourcePage) page).getContentOutline(); |
| } else { |
| outline = getFormOutline(); |
| if (outline != null && outline instanceof FormOutlinePage) |
| ((FormOutlinePage) outline).refresh(); |
| } |
| fContentOutline.setPageActive(outline); |
| } |
| |
| @Override |
| public IFormPage setActivePage(String pageId) { |
| IFormPage page = super.setActivePage(pageId); |
| if (page != null) |
| updateContentOutline(page); |
| return page; |
| } |
| |
| /* package */IFormPage[] getPages() { |
| ArrayList<Object> formPages = new ArrayList<>(); |
| for (int i = 0; i < pages.size(); i++) { |
| Object page = pages.get(i); |
| if (page instanceof IFormPage) |
| formPages.add(page); |
| } |
| return formPages.toArray(new IFormPage[formPages.size()]); |
| } |
| |
| protected void performGlobalAction(String id) { |
| // preserve selection |
| ISelection selection = getSelection(); |
| boolean handled = ((PDEFormPage) getActivePageInstance()).performGlobalAction(id); |
| if (!handled) { |
| IFormPage page = getActivePageInstance(); |
| if (page instanceof PDEFormPage) { |
| if (id.equals(ActionFactory.UNDO.getId())) { |
| fInputContextManager.undo(); |
| return; |
| } |
| if (id.equals(ActionFactory.REDO.getId())) { |
| fInputContextManager.redo(); |
| return; |
| } |
| if (id.equals(ActionFactory.CUT.getId()) || id.equals(ActionFactory.COPY.getId())) { |
| copyToClipboard(selection); |
| return; |
| } |
| } |
| } |
| } |
| |
| private void copyToClipboard(ISelection selection) { |
| Object[] objects = null; |
| String textVersion = null; |
| if (selection instanceof IStructuredSelection) { |
| IStructuredSelection ssel = (IStructuredSelection) selection; |
| if (ssel.isEmpty()) |
| return; |
| objects = ssel.toArray(); |
| try (StringWriter writer = new StringWriter(); PrintWriter pwriter = new PrintWriter(writer)) { |
| Class<? extends Object> objClass = null; |
| for (int i = 0; i < objects.length; i++) { |
| Object obj = objects[i]; |
| if (objClass == null) |
| objClass = obj.getClass(); |
| else if (objClass.equals(obj.getClass()) == false) |
| return; |
| if (obj instanceof IWritable) { |
| // Add a customized delimiter in between all serialized |
| // objects to format the text representation |
| if ((i != 0) && (obj instanceof IWritableDelimiter)) { |
| ((IWritableDelimiter) obj).writeDelimeter(pwriter); |
| } |
| ((IWritable) obj).write("", pwriter); //$NON-NLS-1$ |
| } else if (obj instanceof String) { |
| // Delimiter is always a newline |
| pwriter.println((String) obj); |
| } |
| } |
| pwriter.flush(); |
| textVersion = writer.toString(); |
| } catch (IOException e) { |
| } |
| } else if (selection instanceof ITextSelection) { |
| textVersion = ((ITextSelection) selection).getText(); |
| } |
| if ((textVersion == null || textVersion.length() == 0) && objects == null) |
| return; |
| // set the clipboard contents |
| Object[] o = null; |
| Transfer[] t = null; |
| if (objects == null) { |
| o = new Object[] {textVersion}; |
| t = new Transfer[] {TextTransfer.getInstance()}; |
| } else if (textVersion == null || textVersion.length() == 0) { |
| o = new Object[] {objects}; |
| t = new Transfer[] {ModelDataTransfer.getInstance()}; |
| } else { |
| o = new Object[] {objects, textVersion}; |
| t = new Transfer[] {ModelDataTransfer.getInstance(), TextTransfer.getInstance()}; |
| } |
| clipboard.setContents(o, t); |
| } |
| |
| public boolean canPasteFromClipboard() { |
| IFormPage page = getActivePageInstance(); |
| if (page instanceof PDEFormPage) { |
| return ((PDEFormPage) page).canPaste(getClipboard()); |
| } |
| return false; |
| } |
| |
| public boolean canCopy(ISelection selection) { |
| if (selection == null) |
| return false; |
| if (selection instanceof IStructuredSelection) |
| return !selection.isEmpty(); |
| if (selection instanceof ITextSelection) { |
| ITextSelection textSelection = (ITextSelection) selection; |
| return textSelection.getLength() > 0; |
| } |
| return false; |
| } |
| |
| public boolean canCut(ISelection selection) { |
| return canCopy(selection); |
| } |
| |
| void updateUndo(IAction undoAction, IAction redoAction) { |
| IModelUndoManager undoManager = fInputContextManager.getUndoManager(); |
| if (undoManager != null) |
| undoManager.setActions(undoAction, redoAction); |
| } |
| |
| /** |
| * Triggered by toggling the 'Link with Editor' button in the outline view |
| */ |
| public void synchronizeOutlinePage() { |
| // Get current page |
| IFormPage page = getActivePageInstance(); |
| |
| if (page instanceof PDESourcePage) { |
| // Synchronize with current source page |
| ((PDESourcePage) page).synchronizeOutlinePage(); |
| } else { |
| // Synchronize with current form page |
| // This currently does not work |
| // TODO: Fix 'Link with Editor' functionality for form pages |
| if (getFormOutline() != null) { |
| getFormOutline().setSelection(getSelection()); |
| } |
| } |
| } |
| |
| @Override |
| public IDocument getDocument(Match match) { |
| InputContext context = getInputContext(match.getElement()); |
| return context == null ? null : context.getDocumentProvider().getDocument(context.getInput()); |
| } |
| |
| @Override |
| public IAnnotationModel getAnnotationModel(Match match) { |
| InputContext context = getInputContext(match.getElement()); |
| return context == null ? null : context.getDocumentProvider().getAnnotationModel(context.getInput()); |
| } |
| |
| protected abstract InputContext getInputContext(Object object); |
| |
| @Override |
| protected final void addPages() { |
| fError = getAggregateModel() == null; |
| if (fError) { |
| try { |
| addPage(new MissingResourcePage(this)); |
| } catch (PartInitException e) { |
| PDEPlugin.logException(e); |
| } |
| } else |
| addEditorPages(); |
| } |
| |
| protected abstract void addEditorPages(); |
| |
| @Override |
| public final void contextAdded(InputContext context) { |
| if (fError) { |
| removePage(0); |
| addPages(); |
| if (!fError) |
| setActivePage(OverviewPage.PAGE_ID); |
| } else |
| editorContextAdded(context); |
| } |
| |
| public abstract void editorContextAdded(InputContext context); |
| |
| protected IProgressMonitor getProgressMonitor() { |
| IProgressMonitor monitor = null; |
| IStatusLineManager manager = getStatusLineManager(); |
| if (manager != null) { |
| monitor = manager.getProgressMonitor(); |
| } |
| if (monitor == null) { |
| monitor = new NullProgressMonitor(); |
| } |
| return monitor; |
| } |
| |
| protected IStatusLineManager getStatusLineManager() { |
| return getEditorSite().getActionBars().getStatusLineManager(); |
| } |
| |
| public void contributeToToolbar(IToolBarManager manager) { |
| } |
| |
| } |