diff options
author | Kai Maetzel | 2002-09-24 16:56:43 +0000 |
---|---|---|
committer | Kai Maetzel | 2002-09-24 16:56:43 +0000 |
commit | 14d37ec991e842df973b8c9e1196ba0a255df21e (patch) | |
tree | 3cc2a87ba91aaa43ea7919f6d8cc9f114ed96542 /org.eclipse.ui.workbench.texteditor | |
parent | db4ac57b04d3091f1e46fa274fe527444c03a1a0 (diff) | |
download | eclipse.platform.text-14d37ec991e842df973b8c9e1196ba0a255df21e.tar.gz eclipse.platform.text-14d37ec991e842df973b8c9e1196ba0a255df21e.tar.xz eclipse.platform.text-14d37ec991e842df973b8c9e1196ba0a255df21e.zip |
First cut of org.eclipse.ui splitFirst_cut_splitting_org_eclipse_ui_200209241530
Diffstat (limited to 'org.eclipse.ui.workbench.texteditor')
68 files changed, 15099 insertions, 0 deletions
diff --git a/org.eclipse.ui.workbench.texteditor/.classpath b/org.eclipse.ui.workbench.texteditor/.classpath new file mode 100644 index 00000000000..4673a9a260d --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/.classpath @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src/"/> + <classpathentry kind="src" path="/org.eclipse.core.resources"/> + <classpathentry kind="src" path="/org.eclipse.jface"/> + <classpathentry kind="src" path="/org.eclipse.core.runtime"/> + <classpathentry kind="src" path="/org.eclipse.jface.text"/> + <classpathentry kind="src" path="/org.eclipse.text"/> + <classpathentry kind="var" path="JRE_LIB" rootpath="JRE_SRCROOT" sourcepath="JRE_SRC"/> + <classpathentry kind="src" path="/org.eclipse.ui.workbench"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/org.eclipse.ui.workbench.texteditor/.cvsignore b/org.eclipse.ui.workbench.texteditor/.cvsignore new file mode 100644 index 00000000000..c5e82d74585 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/.cvsignore @@ -0,0 +1 @@ +bin
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/.project b/org.eclipse.ui.workbench.texteditor/.project new file mode 100644 index 00000000000..a0fcf5e5955 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/.project @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.ui.workbench.texteditor</name> + <comment></comment> + <projects> + <project>org.eclipse.core.resources</project> + <project>org.eclipse.core.runtime</project> + <project>org.eclipse.jface</project> + <project>org.eclipse.jface.text</project> + <project>org.eclipse.text</project> + <project>org.eclipse.ui.workbench</project> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/org.eclipse.ui.workbench.texteditor/build.properties b/org.eclipse.ui.workbench.texteditor/build.properties new file mode 100644 index 00000000000..8c60a3d3d15 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/build.properties @@ -0,0 +1,4 @@ +source.texteditor.jar = src/ +bin.includes = *.jar,\ + fragment.xml,\ + fragment.properties diff --git a/org.eclipse.ui.workbench.texteditor/fragment.properties b/org.eclipse.ui.workbench.texteditor/fragment.properties new file mode 100644 index 00000000000..d08c2e1b8c9 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/fragment.properties @@ -0,0 +1,2 @@ +fragmentName= Text Editor Framework Fragment +providerName= Eclipse.org diff --git a/org.eclipse.ui.workbench.texteditor/fragment.xml b/org.eclipse.ui.workbench.texteditor/fragment.xml new file mode 100644 index 00000000000..69ab2c6f394 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/fragment.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<fragment + id="org.eclipse.ui.workbench.texteditor" + name="%fragmentName" + version="2.1.0" + provider-name="%providerName" + plugin-id="org.eclipse.ui.workbench" + plugin-version="2.1.0"> + + <runtime> + <library name="texteditor.jar"> + <export name="*"/> + </library> + </runtime> + + <requires> + <import plugin="org.eclipse.text"/> + </requires> + + +</fragment> diff --git a/org.eclipse.ui.workbench.texteditor/scripts/exportplugin.xml b/org.eclipse.ui.workbench.texteditor/scripts/exportplugin.xml new file mode 100644 index 00000000000..7e4684ff72f --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/scripts/exportplugin.xml @@ -0,0 +1,29 @@ +<project name="Export Workbench Text Editor Fragment" default="export" basedir=".."> + <target name="init"> + <tstamp/> + <property name="destdir" value="../../plugin-export" /> + <property name="plugin" value="org.eclipse.ui.workbench.texteditor" /> + <property name="version" value="_2.1.0" /> + <property name="dest" value="${destdir}/${plugin}${version}" /> + </target> + + <target name="build" depends="init"> + <eclipse.incrementalBuild project="${plugin}" kind="incr"/> + </target> + + <target name="export" depends="build"> + <mkdir dir="${destdir}" /> + <delete dir="${dest}" /> + <mkdir dir="${dest}" /> + <jar + jarfile="${dest}/texteditor.jar" + basedir="bin" + /> + <copy file="fragment.xml" todir="${dest}"/> + <copy file="fragment.properties" todir="${dest}"/> + <zip zipfile="${dest}/texteditorsrc.jar"> + <fileset dir="src" /> + </zip> + </target> + +</project>
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AbstractDocumentProvider.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AbstractDocumentProvider.java new file mode 100644 index 00000000000..a10ce6ef3f7 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AbstractDocumentProvider.java @@ -0,0 +1,791 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentListener; + +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.jface.util.Assert; + +import org.eclipse.ui.PlatformUI; + +import org.eclipse.core.resources.IResourceStatus; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + + + +/** + * An abstract implementation of a shareable document provider. + * <p> + * Subclasses must implement <code>createDocument</code>, + * <code>createAnnotationModel</code>, and <code>doSaveDocument</code>. + * </p> + */ +public abstract class AbstractDocumentProvider implements IDocumentProvider, IDocumentProviderExtension { + + + /** + * Collection of all information managed for a connected element. + */ + protected class ElementInfo implements IDocumentListener { + + /** The element for which the info is stored */ + public Object fElement; + /** How often the element has been connected */ + public int fCount; + /** Can the element be saved */ + public boolean fCanBeSaved; + /** The element's document */ + public IDocument fDocument; + /** The element's annotation model */ + public IAnnotationModel fModel; + /** + * Has element state been validated + * @since 2.0 + */ + public boolean fIsStateValidated; + /** + * The status of this element + * @since 2.0 + */ + public IStatus fStatus; + + + /** + * Creates a new element info, initialized with the given + * document and annotation model. + * + * @param document the document + * @param model the annotation model + */ + public ElementInfo(IDocument document, IAnnotationModel model) { + fDocument= document; + fModel= model; + fCount= 0; + fCanBeSaved= false; + fIsStateValidated= false; + } + + /** + * An element info equals another object if this object is an element info + * and if the documents of the two element infos are equal. + * @see Object#equals + */ + public boolean equals(Object o) { + if (o instanceof ElementInfo) { + ElementInfo e= (ElementInfo) o; + return fDocument.equals(e.fDocument); + } + return false; + } + + /* + * @see Object#hashCode + */ + public int hashCode() { + return fDocument.hashCode(); + } + + /* + * @see IDocumentListener#documentChanged(DocumentEvent) + */ + public void documentChanged(DocumentEvent event) { + fCanBeSaved= true; + removeUnchangedElementListeners(fElement, this); + fireElementDirtyStateChanged(fElement, fCanBeSaved); + } + + /* + * @see IDocumentListener#documentAboutToBeChanged(DocumentEvent) + */ + public void documentAboutToBeChanged(DocumentEvent event) { + } + }; + + + /** + * Enables a certain behavior. + * Indicates whether this provider should behave as described in + * use case 5 of http://bugs.eclipse.org/bugs/show_bug.cgi?id=10806. + * Current value: <code>false</code> + * @since 2.0 + */ + static final protected boolean PR10806_UC5_ENABLED= false; + + /** + * Enables a certain behavior. + * Indicates whether this provider should behave as described in + * http://bugs.eclipse.org/bugs/show_bug.cgi?id=14469 + * Notes: This contradicts <code>PR10806_UC5_ENABLED</code>. + * Current value: <code>true</code> + * @since 2.0 + */ + static final protected boolean PR14469_ENABLED= true; + + /** + * Constant for representing an ok status. This is considered a value object. + * @since 2.0 + */ + static final protected IStatus STATUS_OK= new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK, "OK", null); + + /** + * Constant for representing an error status. This is considered a value object. + * @since 2.0 + */ + static final protected IStatus STATUS_ERROR= new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, IStatus.INFO, "ERROR", null); + + + /** Information of all connected elements */ + private Map fElementInfoMap= new HashMap(); + /** The element state listeners */ + private List fElementStateListeners= new ArrayList(); + + + /** + * Creates a new document provider. + */ + protected AbstractDocumentProvider() { + } + + /** + * Creates the document for the given element.<p> + * Subclasses must implement this method. + * + * @param element the element + * @return the document + * @exception CoreException if the document could not be created + */ + protected abstract IDocument createDocument(Object element) throws CoreException; + + /** + * Creates an annotation model for the given element. <p> + * Subclasses must implement this method. + * + * @param element the element + * @return the annotation model + * @exception CoreException if the annotation model could not be created + */ + protected abstract IAnnotationModel createAnnotationModel(Object element) throws CoreException; + + /** + * Performs the actual work of saving the given document provided for the + * given element. <p> + * Subclasses must implement this method. + * + * @param monitor a progress monitor to report progress and request cancelation + * @param element the element + * @param document the document + * @param overwrite indicates whether an overwrite should happen if necessary + * @exception CoreException if document could not be stored to the given element + */ + protected abstract void doSaveDocument(IProgressMonitor monitor, Object element, IDocument document, boolean overwrite) throws CoreException; + + + /** + * Returns the element info object for the given element. + * + * @param element the element + * @return the element info object, or <code>null</code> if none + */ + protected ElementInfo getElementInfo(Object element) { + return (ElementInfo) fElementInfoMap.get(element); + } + + /** + * Creates a new element info object for the given element.<p> + * This method is called from <code>connect</code> when an element info needs + * to be created. The <code>AbstractDocumentProvider</code> implementation + * of this method returns a new element info object whose document and + * annotation model are the values of <code>createDocument(element)</code> + * and <code>createAnnotationModel(element)</code>, respectively. Subclasses + * may override. + * + * @param element the element + * @return a new element info object + * @exception CoreException if the document or annotation model could not be created + */ + protected ElementInfo createElementInfo(Object element) throws CoreException { + return new ElementInfo(createDocument(element), createAnnotationModel(element)); + } + + /** + * Disposes of the given element info object. <p> + * This method is called when an element info is disposed. The + * <code>AbstractDocumentProvider</code> implementation of this + * method does nothing. Subclasses may reimplement. + * + * @param element the element + * @param info the element info object + */ + protected void disposeElementInfo(Object element, ElementInfo info) { + } + + /** + * Called on initial creation and when the dirty state of the element + * changes to <code>false</code>. Adds all listeners which must be + * active as long as the element is not dirty. This method is called + * before <code>fireElementDirtyStateChanged</code> or <code> + * fireElementContentReplaced</code> is called. + * Subclasses may extend. + * + * @param element the element + * @param info the element info object + */ + protected void addUnchangedElementListeners(Object element, ElementInfo info) { + if (info.fDocument != null) + info.fDocument.addDocumentListener(info); + } + + /** + * Called when the given element gets dirty. Removes all listeners + * which must be active only when the element is not dirty. This + * method is called before <code>fireElementDirtyStateChanged</code> + * or <code>fireElementContentReplaced</code> is called. + * Subclasses may extend. + * + * @param element the element + * @param info the element info object + */ + protected void removeUnchangedElementListeners(Object element, ElementInfo info) { + if (info.fDocument != null) + info.fDocument.removeDocumentListener(info); + } + + /** + * Enumerates the elements connected via this document provider. + * + * @return the list of elements (element type: <code>Object</code>) + */ + protected Iterator getConnectedElements() { + Set s= new HashSet(); + Set keys= fElementInfoMap.keySet(); + if (keys != null) + s.addAll(keys); + return s.iterator(); + } + + /* + * @see IDocumentProvider#connect + */ + public final void connect(Object element) throws CoreException { + ElementInfo info= (ElementInfo) fElementInfoMap.get(element); + if (info == null) { + + info= createElementInfo(element); + if (info == null) + info= new ElementInfo(null, null); + + info.fElement= element; + + addUnchangedElementListeners(element, info); + + fElementInfoMap.put(element, info); + if (fElementInfoMap.size() == 1) + connected(); + } + ++ info.fCount; + } + + /** + * This hook method is called when this provider starts managing documents for + * elements. I.e. it is called when the first element gets connected to this provider. + * Subclasses may extend. + * @since 2.0 + */ + protected void connected() { + } + + /* + * @see IDocumentProvider#disconnect + */ + public final void disconnect(Object element) { + ElementInfo info= (ElementInfo) fElementInfoMap.get(element); + + if (info == null) + return; + + if (info.fCount == 1) { + + fElementInfoMap.remove(element); + removeUnchangedElementListeners(element, info); + disposeElementInfo(element, info); + + if (fElementInfoMap.size() == 0) + disconnected(); + + } else + -- info.fCount; + } + + /** + * This hook method is called when this provider stops managing documents for + * element. I.e. it is called when the last element gets disconnected from this provider. + * Subcalles may extend. + * @since 2.0 + */ + protected void disconnected() { + } + + /* + * @see IDocumentProvider#getDocument + */ + public IDocument getDocument(Object element) { + + if (element == null) + return null; + + ElementInfo info= (ElementInfo) fElementInfoMap.get(element); + return (info != null ? info.fDocument : null); + } + + /* + * @see IDocumentProvider#mustSaveDocument + */ + public boolean mustSaveDocument(Object element) { + + if (element == null) + return false; + + ElementInfo info= (ElementInfo) fElementInfoMap.get(element); + return (info != null ? info.fCount == 1 && info.fCanBeSaved : false); + } + + /* + * @see IDocumentProvider#getAnnotationModel + */ + public IAnnotationModel getAnnotationModel(Object element) { + + if (element == null) + return null; + + ElementInfo info= (ElementInfo) fElementInfoMap.get(element); + return (info != null ? info.fModel : null); + } + + /* + * @see IDocumentProvider#canSaveDocument(Object) + */ + public boolean canSaveDocument(Object element) { + + if (element == null) + return false; + + ElementInfo info= (ElementInfo) fElementInfoMap.get(element); + return (info != null ? info.fCanBeSaved : false); + } + + /* + * @see IDocumentProvider#resetDocument(Object) + */ + public void resetDocument(Object element) throws CoreException { + if (element == null) + return; + + ElementInfo info= (ElementInfo) fElementInfoMap.get(element); + if (info != null) { + + IDocument original= null; + IStatus status= null; + + try { + original= createDocument(element); + } catch (CoreException x) { + status= x.getStatus(); + } + + info.fStatus= status; + + if (original != null) { + fireElementContentAboutToBeReplaced(element); + info.fDocument.set(original.get()); + if (info.fCanBeSaved) { + info.fCanBeSaved= false; + addUnchangedElementListeners(element, info); + } + fireElementContentReplaced(element); + } + } + } + + /* + * @see IDocumentProvider#saveDocument(IProgressMonitor, Object, IDocument, boolean) + */ + public void saveDocument(IProgressMonitor monitor, Object element, IDocument document, boolean overwrite) throws CoreException { + + if (element == null) + return; + + ElementInfo info= (ElementInfo) fElementInfoMap.get(element); + if (info != null) { + + if (info.fDocument != document) { + Status status= new Status(IStatus.WARNING, PlatformUI.PLUGIN_ID, IResourceStatus.ERROR, EditorMessages.getString("AbstractDocumentProvider.error.save.inuse"), null); //$NON-NLS-1$ + throw new CoreException(status); + } + + doSaveDocument(monitor, element, document, overwrite); + info.fCanBeSaved= false; + addUnchangedElementListeners(element, info); + fireElementDirtyStateChanged(element, false); + + } else { + doSaveDocument(monitor, element, document, overwrite); + } + } + + /** + * The <code>AbstractDocumentProvider</code> implementation of this + * <code>IDocumentProvider</code> method does nothing. Subclasses may + * reimplement. + * + * @param element the element + */ + public void aboutToChange(Object element) { + } + + /** + * The <code>AbstractDocumentProvider</code> implementation of this + * <code>IDocumentProvider</code> method does nothing. Subclasses may + * reimplement. + * + * @param element the element + */ + public void changed(Object element) { + } + + /* + * @see IDocumentProvider#addElementStateListener(IElementStateListener) + */ + public void addElementStateListener(IElementStateListener listener) { + Assert.isNotNull(listener); + if (!fElementStateListeners.contains(listener)) + fElementStateListeners.add(listener); + } + + /* + * @see IDocumentProvider#removeElementStateListener(IElementStateListener) + */ + public void removeElementStateListener(IElementStateListener listener) { + Assert.isNotNull(listener); + fElementStateListeners.remove(listener); + } + + /** + * Informs all registered element state listeners about a change in the + * dirty state of the given element. + * + * @param element the element + * @param isDirty the new dirty state + * @see IElementStateListener#elementDirtyStateChanged + */ + protected void fireElementDirtyStateChanged(Object element, boolean isDirty) { + Iterator e= new ArrayList(fElementStateListeners).iterator(); + while (e.hasNext()) { + IElementStateListener l= (IElementStateListener) e.next(); + l.elementDirtyStateChanged(element, isDirty); + } + } + + /** + * Informs all registered element state listeners about an impending + * replace of the given element's content. + * + * @param element the element + * @see IElementStateListener#elementContentAboutToBeReplaced + */ + protected void fireElementContentAboutToBeReplaced(Object element) { + Iterator e= new ArrayList(fElementStateListeners).iterator(); + while (e.hasNext()) { + IElementStateListener l= (IElementStateListener) e.next(); + l.elementContentAboutToBeReplaced(element); + } + } + + /** + * Informs all registered element state listeners about the just-completed + * replace of the given element's content. + * + * @param element the element + * @see IElementStateListener#elementContentReplaced + */ + protected void fireElementContentReplaced(Object element) { + Iterator e= new ArrayList(fElementStateListeners).iterator(); + while (e.hasNext()) { + IElementStateListener l= (IElementStateListener) e.next(); + l.elementContentReplaced(element); + } + } + + /** + * Informs all registered element state listeners about the deletion + * of the given element. + * + * @param element the element + * @see IElementStateListener#elementDeleted + */ + protected void fireElementDeleted(Object element) { + Iterator e= new ArrayList(fElementStateListeners).iterator(); + while (e.hasNext()) { + IElementStateListener l= (IElementStateListener) e.next(); + l.elementDeleted(element); + } + } + + /** + * Informs all registered element state listeners about a move. + * + * @param originalElement the element before the move + * @param movedElement the element after the move + * @see IElementStateListener#elementMoved + */ + protected void fireElementMoved(Object originalElement, Object movedElement) { + Iterator e= new ArrayList(fElementStateListeners).iterator(); + while (e.hasNext()) { + IElementStateListener l= (IElementStateListener) e.next(); + l.elementMoved(originalElement, movedElement); + } + } + + /* + * @see IDocumentProvider#getModificationStamp(Object) + * @since 2.0 + */ + public long getModificationStamp(Object element) { + return 0; + } + + /* + * @see IDocumentProvider#getSynchronizationStamp(Object) + * @since 2.0 + */ + public long getSynchronizationStamp(Object element) { + return 0; + } + + /* + * @see IDocumentProvider#isDeleted(Object) + * @since 2.0 + */ + public boolean isDeleted(Object element) { + return false; + } + + /* + * @see IDocumentProviderExtension#isReadOnly(Object) + * @since 2.0 + */ + public boolean isReadOnly(Object element) { + return true; + } + + /* + * @see IDocumentProviderExtension#isModifiable(Object) + * @since 2.0 + */ + public boolean isModifiable(Object element) { + return false; + } + + /** + * Returns whether <code>validateState</code> has been called for the given element + * since the element's state has potentially been invalidated. + * + * @param element the element + * @return whether <code>validateState</code> has been called for the given element + * @since 2.0 + */ + public boolean isStateValidated(Object element) { + ElementInfo info= (ElementInfo) fElementInfoMap.get(element); + if (info != null) + return info.fIsStateValidated; + return false; + } + + /** + * Hook method for validating the state of the given element. Must not take care of cache updating etc. + * Default implementation is empty. + * + * @param element the element + * @param computationContext the context in which validation happens + * @exception CoreException in case validation fails + * @since 2.0 + */ + protected void doValidateState(Object element, Object computationContext) throws CoreException { + } + + /* + * @see IDocumentProviderExtension#validateState(Object, Object) + * @since 2.0 + */ + final public void validateState(Object element, Object computationContext) throws CoreException { + ElementInfo info= (ElementInfo) fElementInfoMap.get(element); + if (info != null) { + doValidateState(element, computationContext); + doUpdateStateCache(element); + info.fIsStateValidated= true; + fireElementStateValidationChanged(element, true); + } + } + + /** + * Hook method for updating the state of the given element. + * Default implementation is empty. + * + * @param element the element + * @exception CoreException in case state cache updating fails + * @since 2.0 + */ + protected void doUpdateStateCache(Object element) throws CoreException { + } + + /** + * Returns whether the state of the element must be invalidated given its + * previous read-only state. + * + * @param element the element + * @param wasReadOnly the previous read-only state + * @return <code>true</code> if the state of the given element must be invalidated + * @since 2.0 + */ + protected boolean invalidatesState(Object element, boolean wasReadOnly) { + Assert.isTrue(PR10806_UC5_ENABLED != PR14469_ENABLED); + boolean readOnlyChanged= (isReadOnly(element) != wasReadOnly); + if (PR14469_ENABLED) + return readOnlyChanged && !canSaveDocument(element); + return readOnlyChanged; + } + + /* + * @see IDocumentProviderExtension#updateStateCache(Object) + * @since 2.0 + */ + final public void updateStateCache(Object element) throws CoreException { + ElementInfo info= (ElementInfo) fElementInfoMap.get(element); + if (info != null) { + boolean wasReadOnly= isReadOnly(element); + doUpdateStateCache(element); + if (invalidatesState(element, wasReadOnly)) { + info.fIsStateValidated= false; + fireElementStateValidationChanged(element, false); + } + } + } + + /* + * @see IDocumentProviderExtension#setCanSaveDocument(Object) + * @since 2.0 + */ + public void setCanSaveDocument(Object element) { + if (element != null) { + ElementInfo info= (ElementInfo) fElementInfoMap.get(element); + if (info != null) { + info.fCanBeSaved= true; + removeUnchangedElementListeners(element, info); + fireElementDirtyStateChanged(element, info.fCanBeSaved); + } + } + } + + /** + * Informs all registered element state listeners about a change in the + * state validation of the given element. + * + * @param element the element + * @param isStateValidated + * @see IElementStateListenerExtension#elementStateValidationChanged(Object, boolean) + * @since 2.0 + */ + protected void fireElementStateValidationChanged(Object element, boolean isStateValidated) { + Iterator e= new ArrayList(fElementStateListeners).iterator(); + while (e.hasNext()) { + Object o= e.next(); + if (o instanceof IElementStateListenerExtension) { + IElementStateListenerExtension l= (IElementStateListenerExtension) o; + l.elementStateValidationChanged(element, isStateValidated); + } + } + } + + /** + * Informs all registered element state listeners about the current + * change of the element + * + * @param element the element + * @see IElementStateListenerExtension#elementStateChanging(Object) + * @since 2.0 + */ + protected void fireElementStateChanging(Object element) { + Iterator e= new ArrayList(fElementStateListeners).iterator(); + while (e.hasNext()) { + Object o= e.next(); + if (o instanceof IElementStateListenerExtension) { + IElementStateListenerExtension l= (IElementStateListenerExtension) o; + l.elementStateChanging(element); + } + } + } + + /** + * Informs all registered element state listeners about the failed + * change of the element + * + * @param element the element + * @see IElementStateListenerExtension#elementStateChangeFailed(Object) + * @since 2.0 + */ + protected void fireElementStateChangeFailed(Object element) { + Iterator e= new ArrayList(fElementStateListeners).iterator(); + while (e.hasNext()) { + Object o= e.next(); + if (o instanceof IElementStateListenerExtension) { + IElementStateListenerExtension l= (IElementStateListenerExtension) o; + l.elementStateChangeFailed(element); + } + } + } + + /* + * @see IDocumentProviderExtension#getStatus(Object) + * @since 2.0 + */ + public IStatus getStatus(Object element) { + ElementInfo info= (ElementInfo) fElementInfoMap.get(element); + if (info != null) { + if (info.fStatus != null) + return info.fStatus; + return (info.fDocument == null ? STATUS_ERROR : STATUS_OK); + } + + return STATUS_ERROR; + } + + /* + * @see org.eclipse.ui.texteditor.IDocumentProviderExtension#synchronize(Object) + * @since 2.0 + */ + public void synchronize(Object element) throws CoreException { + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AbstractMarkerAnnotationModel.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AbstractMarkerAnnotationModel.java new file mode 100644 index 00000000000..d84e2bf2167 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AbstractMarkerAnnotationModel.java @@ -0,0 +1,571 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.AnnotationModel; +import org.eclipse.jface.util.Assert; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; + +import org.eclipse.ui.PlatformUI; + + +/** + * Abstract implementation of a marker-based annotation model. + * <p> + * Markers are provided by an underlying source (a subclass responsibility). + * Markers whose textual range gets deleted during text editing are removed + * from the model on save. The <code>updateMarkers</code> method can be used + * to force the model to update the source's markers with any changes to their + * locations due to edits. Clients can register <code>IMarkerUpdater</code> + * objects in order to define the process of marker updating. Registration can be done + * using the <code>"org.eclipse.ui.markerUpdaters"</code> extension point. + * <p> + * Subclasses must implement the following methods: + * <ul> + * <li><code>retrieveMarkers</code></li> + * <li><code>isAcceptable</code></li> + * <li><code>deleteMarkers</code></li> + * <li><code>listenToMarkerChanges</code></li> + * </ul> + * </p> + */ +public abstract class AbstractMarkerAnnotationModel extends AnnotationModel { + + /** List of annotations whose text range became invalid because of document changes */ + private List fDeletedAnnotations= new ArrayList(2); + /** Reference counters to track how often an annotation model is connected to its document */ + private HashMap fRefcountTable= new HashMap(); + /** List of registered and instantiated marker updaters */ + private List fInstantiatedMarkerUpdaters= null; + /** List of registered but not yet instantiated marker updaters */ + private List fMarkerUpdaterSpecifications= null; + + + /** + * Retrieves all markers from this model.<p> + * Subclasses must implement this method. + * + * @return the list of markers + * @exception CoreException if there is a problem getting the markers + */ + protected abstract IMarker[] retrieveMarkers() throws CoreException; + + /** + * Deletes the given markers from this model.<p> + * Subclasses must implement this method. + * + * @param markers the list of markers + * @exception CoreException if there are problems deleting the markers + */ + protected abstract void deleteMarkers(IMarker[] markers) throws CoreException; + + /** + * Tells the model whether it should listen for marker changes. <p> + * Subclasses must implement this method. + * + * @param listen <code>true</code> if this model should listen, and + * <code>false</code> otherwise + */ + protected abstract void listenToMarkerChanges(boolean listen); + + /** + * Determines whether the marker is acceptable as an addition to this model. + * If the marker, say, represents an aspect or range of no interest to this + * model, the marker is rejected.<p> + * Subclasses must implement this method. + * + * @param marker the marker + * @return <code>true</code> if the marker is acceptable + */ + protected abstract boolean isAcceptable(IMarker marker); + + /** + * Creates a new annotation model. The annotation model does not manage any + * annotations and is not connected to any document. + */ + protected AbstractMarkerAnnotationModel() { + } + + /** + * Adds the given marker updater to this annotation model. + * It is client's responsibility to ensure the consitency of the + * set of registered marker updaters. + * + * @param markerUpdater the marker updater to be added + */ + protected void addMarkerUpdater(IMarkerUpdater markerUpdater) { + if (!fInstantiatedMarkerUpdaters.contains(markerUpdater)) + fInstantiatedMarkerUpdaters.add(markerUpdater); + } + + /** + * Removes the given marker updater from this annotation model. + * + * @param markerUpdater the marker updater to be removed + */ + protected void removeMarkerUpdater(IMarkerUpdater markerUpdater) { + fInstantiatedMarkerUpdaters.remove(markerUpdater); + } + + /** + * Creates a new annotation for the given marker.<p> + * Subclasses may override. + * + * @param marker the marker + * @return the new marker annotation + */ + protected MarkerAnnotation createMarkerAnnotation(IMarker marker) { + return new MarkerAnnotation(marker); + } + + /** + * Handles an unanticipated <code>CoreException</code> in + * a standard manner. + * + * @param exception the exception + * @param message a message to aid debugging + */ + protected void handleCoreException(CoreException exception, String message) { + + ILog log= Platform.getPlugin(PlatformUI.PLUGIN_ID).getLog(); + + if (message != null) + log.log(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, 0, message, null)); + + log.log(exception.getStatus()); + } + + /** + * Creates and returns the character position of the given marker based + * on its attributes. <p> + * Subclasses may override. + * + * @param marker the marker + * @return the new position or <code>null</code> if no valid position + */ + protected Position createPositionFromMarker(IMarker marker) { + + int start= MarkerUtilities.getCharStart(marker); + int end= MarkerUtilities.getCharEnd(marker); + + if (start > end) { + end= start + end; + start= end - start; + end= end - start; + } + + if (start == -1 && end == -1) { + // marker line number is 1-based + int line= MarkerUtilities.getLineNumber(marker); + if (line > 0 && fDocument != null) { + try { + start= fDocument.getLineOffset(line - 1); + end= start; + } catch (BadLocationException x) { + } + } + } + + if (start > -1 && end > -1) + return new Position(start, end - start); + + return null; + } + + /** + * Creates an annotation for the given marker and adds it to this model. + * Does nothing if the marker is not acceptable to this model. + * + * @param marker the marker + * @see #isAcceptable + */ + protected final void addMarkerAnnotation(IMarker marker) { + + if (isAcceptable(marker)) { + Position p= createPositionFromMarker(marker); + if (p != null) + addAnnotation(createMarkerAnnotation(marker), p, false); + } + } + + /** + * Connects to the source of markers as marker change listener. + * @see AnnotationModel#connected + */ + protected void connected() { + + listenToMarkerChanges(true); + + try { + catchupWithMarkers(); + } catch (CoreException x) { + handleCoreException(x, EditorMessages.getString("AbstractMarkerAnnotationModel.connected")); //$NON-NLS-1$ + } + + fireModelChanged(); + } + + /** + * Installs all marker updaters for this marker annotation model. + */ + private void installMarkerUpdaters() { + + // initialize lists - indicates that the initialization happened + fMarkerUpdaterSpecifications= new ArrayList(2); + fInstantiatedMarkerUpdaters= new ArrayList(2); + + // populate list + IExtensionPoint extensionPoint= Platform.getPluginRegistry().getExtensionPoint(PlatformUI.PLUGIN_ID, "markerUpdaters"); //$NON-NLS-1$ + if (extensionPoint != null) { + IConfigurationElement[] elements= extensionPoint.getConfigurationElements(); + for (int i= 0; i < elements.length; i++) + fMarkerUpdaterSpecifications.add(elements[i]); + } + } + + /** + * Uninstalls all marker updaters. + */ + private void uninstallMarkerUpdaters() { + if (fInstantiatedMarkerUpdaters != null) { + fInstantiatedMarkerUpdaters.clear(); + fInstantiatedMarkerUpdaters= null; + } + + if (fMarkerUpdaterSpecifications != null) { + fMarkerUpdaterSpecifications.clear(); + fMarkerUpdaterSpecifications= null; + } + } + + /** + * Removes the marker change listener. + * @see AnnotationModel#disconnected + */ + protected void disconnected() { + listenToMarkerChanges(false); + uninstallMarkerUpdaters(); + } + + /** + * Returns the position known to this annotation model for the given marker. + * + * @param marker the marker + * @return the position, or <code>null</code> if none + */ + public Position getMarkerPosition(IMarker marker) { + MarkerAnnotation a= getMarkerAnnotation(marker); + if (a != null) { + return (Position) fAnnotations.get(a); + } + return null; + } + + /** + * Updates the annotation corresponding to the given marker which has changed + * in some way. <p> + * Subclasses may override. + * + * @param marker the marker + */ + protected void modifyMarkerAnnotation(IMarker marker) { + MarkerAnnotation a= getMarkerAnnotation(marker); + if (a != null) { + + // update annotation presentation + a.update(); + + // update annotation position + Position p1= createPositionFromMarker(marker); + if (p1 != null) { + Position p0= (Position) fAnnotations.get(a); + p0.setOffset(p1.getOffset()); + p0.setLength(p1.getLength()); + } + } + } + + /* + * @see AnnotationModel#removeAnnotations + */ + protected void removeAnnotations(List annotations, boolean fireModelChanged, boolean modelInitiated) { + if (annotations != null && annotations.size() > 0) { + + List markerAnnotations= new ArrayList(); + for (Iterator e= annotations.iterator(); e.hasNext();) { + Annotation a= (Annotation) e.next(); + if (a instanceof MarkerAnnotation) + markerAnnotations.add(a); + + // remove annotations from annotation model + removeAnnotation(a, false); + } + + if (markerAnnotations.size() > 0) { + + if (modelInitiated) { + // if model initiated also remove it from the marker manager + + listenToMarkerChanges(false); + try { + + IMarker[] m= new IMarker[markerAnnotations.size()]; + for (int i= 0; i < m.length; i++) { + MarkerAnnotation ma = (MarkerAnnotation) markerAnnotations.get(i); + m[i]= ma.getMarker(); + } + deleteMarkers(m); + + } catch (CoreException x) { + handleCoreException(x, EditorMessages.getString("AbstractMarkerAnnotationModel.removeAnnotations")); //$NON-NLS-1$ + } + listenToMarkerChanges(true); + + } else { + // remember deleted annotations in order to remove their markers later on + fDeletedAnnotations.addAll(markerAnnotations); + } + } + + if (fireModelChanged) + fireModelChanged(); + } + } + + /** + * Removes the annotation corresponding to the given marker. Does nothing + * if there is no annotation for this marker. + * + * @param marker the marker + */ + protected final void removeMarkerAnnotation(IMarker marker) { + MarkerAnnotation a= getMarkerAnnotation(marker); + if (a != null) { + removeAnnotation(a, false); + } + } + + /** + * Re-populates this model with annotations for all markers retrieved + * from the maker source via <code>retrieveMarkers</code>. + * + * @exception CoreException if there is a problem getting the markers + */ + private void catchupWithMarkers() throws CoreException { + + for (Iterator e=getAnnotationIterator(false); e.hasNext();) { + Annotation a= (Annotation) e.next(); + if (a instanceof MarkerAnnotation) + removeAnnotation(a, false); + } + + IMarker[] markers= retrieveMarkers(); + if (markers != null) { + for (int i= 0; i < markers.length; i++) + addMarkerAnnotation(markers[i]); + } + } + + /** + * Returns this model's annotation for the given marker. + * + * @param marker the marker + * @return the annotation, or <code>null</code> if none + */ + public final MarkerAnnotation getMarkerAnnotation(IMarker marker) { + Iterator e= getAnnotationIterator(false); + while (e.hasNext()) { + Object o= e.next(); + if (o instanceof MarkerAnnotation) { + MarkerAnnotation a= (MarkerAnnotation) o; + if (marker.equals(a.getMarker())) { + return a; + } + } + } + return null; + } + + /** + * Creates a marker updater as specified in the given configuration element. + * + * @param element the configuration element + * @return the created marker updater or <code>null</code> if none could be created + */ + private IMarkerUpdater createMarkerUpdater(IConfigurationElement element) { + try { + return (IMarkerUpdater) element.createExecutableExtension("class"); //$NON-NLS-1$ + } catch (CoreException x) { + handleCoreException(x, EditorMessages.getString("AbstractMarkerAnnotationModel.createMarkerUpdater")); //$NON-NLS-1$ + } + + return null; + } + + /** + * Checks whether a marker updater is registered for the type of the + * given marker but not yet instantiated. If so, the method instantiates + * the marker updater and registers it with this model. + * + * @param marker the marker for which to look for an updater + * @since 2.0 + */ + private void checkMarkerUpdaters(IMarker marker) { + List toBeDeleted= new ArrayList(); + for (int i= 0; i < fMarkerUpdaterSpecifications.size(); i++) { + IConfigurationElement spec= (IConfigurationElement) fMarkerUpdaterSpecifications.get(i); + String markerType= spec.getAttribute("markerType"); //$NON-NLS-1$ + if (markerType == null || MarkerUtilities.isMarkerType(marker, markerType)) { + toBeDeleted.add(spec); + IMarkerUpdater updater= createMarkerUpdater(spec); + if (updater != null) + addMarkerUpdater(updater); + } + } + + for (int i= 0; i < toBeDeleted.size(); i++) + fMarkerUpdaterSpecifications.remove(toBeDeleted.get(i)); + } + + /** + * Updates the given marker according to the given position in the given + * document. If the given position is <code>null</code>, the marker is + * assumed to carry the correct positional information. If it is detected + * that the marker is invalid and should thus be deleted, this method + * returns <code>false</code>. + * + * @param marker the marker to be updated + * @param document the document into which the given position points + * @param position the current position of the marker inside the given document + * @exception CoreException if there is a problem updating the marker + * @since 2.0 + */ + public boolean updateMarker(IMarker marker, IDocument document, Position position) throws CoreException { + + if (fMarkerUpdaterSpecifications == null) + installMarkerUpdaters(); + + if (!fMarkerUpdaterSpecifications.isEmpty()) + checkMarkerUpdaters(marker); + + boolean isOK= true; + + for (int i= 0; i < fInstantiatedMarkerUpdaters.size(); i++) { + IMarkerUpdater updater= (IMarkerUpdater) fInstantiatedMarkerUpdaters.get(i); + String markerType= updater.getMarkerType(); + if (markerType == null || MarkerUtilities.isMarkerType(marker, markerType)) { + + if (position == null) { + /* compatibility code */ + position= createPositionFromMarker(marker); + } + + isOK= (isOK && updater.updateMarker(marker, document, position)); + } + } + + return isOK; + } + + /** + * Updates the markers managed by this annotation model by calling + * all registered marker updaters (<code>IMarkerUpdater</code>). + * + * @param document the document to which this model is currently connected + * @exception CoreException if there is a problem updating the markers + */ + public void updateMarkers(IDocument document) throws CoreException { + + Assert.isTrue(fDocument == document); + + if (fAnnotations.size() == 0 && fDeletedAnnotations.size() == 0) + return; + + if (fMarkerUpdaterSpecifications == null) + installMarkerUpdaters(); + + listenToMarkerChanges(false); + + // update all markers with the positions known by the annotation model + for (Iterator e= getAnnotationIterator(false); e.hasNext();) { + Object o= e.next(); + if (o instanceof MarkerAnnotation) { + MarkerAnnotation a= (MarkerAnnotation) o; + IMarker marker= a.getMarker(); + Position position= (Position) fAnnotations.get(a); + if ( !updateMarker(marker, document, position)) { + if ( !fDeletedAnnotations.contains(a)) + fDeletedAnnotations.add(a); + } + } + } + + if (!fDeletedAnnotations.isEmpty()) { + removeAnnotations(fDeletedAnnotations, true, true); + fDeletedAnnotations.clear(); + } + + listenToMarkerChanges(true); + } + + /** + * Resets all the markers to their original state. + */ + public void resetMarkers() { + + // reinitializes the positions from the markers + for (Iterator e= getAnnotationIterator(false); e.hasNext();) { + Object o= e.next(); + if (o instanceof MarkerAnnotation) { + MarkerAnnotation a= (MarkerAnnotation) o; + Position p= createPositionFromMarker(a.getMarker()); + if (p != null) { + removeAnnotation(a, false); + addAnnotation(a, p, false); + } + } + } + + // add the markers of deleted positions back to the annotation model + for (Iterator e= fDeletedAnnotations.iterator(); e.hasNext();) { + Object o= e.next(); + if (o instanceof MarkerAnnotation) { + MarkerAnnotation a= (MarkerAnnotation) o; + Position p= createPositionFromMarker(a.getMarker()); + if (p != null) + addAnnotation(a, p, false); + } + } + fDeletedAnnotations.clear(); + + // fire annotation model changed + fireModelChanged(); + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AbstractRulerActionDelegate.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AbstractRulerActionDelegate.java new file mode 100644 index 00000000000..5752dd163f1 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AbstractRulerActionDelegate.java @@ -0,0 +1,145 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + + +package org.eclipse.ui.texteditor; + + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.text.source.IVerticalRulerInfo; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.widgets.Control; + +import org.eclipse.ui.IEditorActionDelegate; +import org.eclipse.ui.IEditorPart; + +/** + * This class serves as an adapter for actions contributed to the vertical ruler's + * context menu. This adapter provides the contributed actions access to their editor + * and the editor's vertical ruler. These actions gain only limited access to the vertical + * ruler as defined by <code>IVerticalRulerInfo</code>. The adapter updates the + * adapter (inner) action on menu and mouse action on the vertical ruler.<p> + * Extending classes must implement the factory method + * <code>createAction(ITextEditor editor, IVerticalRulerInfo)</code>. + * @since 2.0 + */ +public abstract class AbstractRulerActionDelegate implements IEditorActionDelegate, MouseListener, IMenuListener { + + /** The editor. */ + private IEditorPart fEditor; + /** The action calling the action delegate. */ + private IAction fCallerAction; + /** The underlying action. */ + private IAction fAction; + + /** + * The factory method creating the underlying action. + * + * @param editor the editor the action to be created will work on + * @param rulerInfo the vertical ruler the action to be created will work on + * @return the created action + */ + protected abstract IAction createAction(ITextEditor editor, IVerticalRulerInfo rulerInfo); + + + /* + * @see IEditorActionDelegate#setActiveEditor(IAction, IEditorPart) + */ + public void setActiveEditor(IAction callerAction, IEditorPart targetEditor) { + if (fEditor != null) { + IVerticalRulerInfo rulerInfo= (IVerticalRulerInfo) fEditor.getAdapter(IVerticalRulerInfo.class); + if (rulerInfo != null) { + Control control= rulerInfo.getControl(); + if (control != null && !control.isDisposed()) + control.removeMouseListener(this); + } + + if (fEditor instanceof ITextEditorExtension) + ((ITextEditorExtension) fEditor).removeRulerContextMenuListener(this); + } + + fEditor= targetEditor; + fCallerAction= callerAction; + fAction= null; + + if (fEditor != null && fEditor instanceof ITextEditor) { + if (fEditor instanceof ITextEditorExtension) + ((ITextEditorExtension) fEditor).addRulerContextMenuListener(this); + + IVerticalRulerInfo rulerInfo= (IVerticalRulerInfo) fEditor.getAdapter(IVerticalRulerInfo.class); + if (rulerInfo != null) { + fAction= createAction((ITextEditor) fEditor, rulerInfo); + update(); + + Control control= rulerInfo.getControl(); + if (control != null && !control.isDisposed()) + control.addMouseListener(this); + } + } + } + + /* + * @see IActionDelegate#run(IAction) + */ + public void run(IAction callerAction) { + if (fAction != null) + fAction.run(); + } + + /* + * @see IActionDelegate#selectionChanged(IAction, ISelection) + */ + public void selectionChanged(IAction action, ISelection selection) { + } + + /** + * Requests the adaptee to update itself to the current state. + */ + private void update() { + if (fAction != null && fAction instanceof IUpdate) { + ((IUpdate) fAction).update(); + if (fCallerAction != null) { + fCallerAction.setText(fAction.getText()); + fCallerAction.setEnabled(fAction.isEnabled()); + } + } + } + + /* + * @see IMenuListener#menuAboutToShow(IMenuManager) + */ + public void menuAboutToShow(IMenuManager manager) { + update(); + } + + /* + * @see MouseListener#mouseDoubleClick(MouseEvent) + */ + public void mouseDoubleClick(MouseEvent e) { + } + + /* + * @see MouseListener#mouseDown(MouseEvent) + */ + public void mouseDown(MouseEvent e) { + update(); + } + + /* + * @see MouseListener#mouseUp(MouseEvent) + */ + public void mouseUp(MouseEvent e) { + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AbstractTextEditor.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AbstractTextEditor.java new file mode 100644 index 00000000000..8d2ca781fa8 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AbstractTextEditor.java @@ -0,0 +1,3550 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + + +package org.eclipse.ui.texteditor; + + +import java.lang.reflect.InvocationTargetException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ST; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.custom.VerifyKeyListener; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.events.VerifyEvent; +import org.eclipse.swt.events.VerifyListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IPluginDescriptor; +import org.eclipse.core.runtime.IPluginPrerequisite; +import org.eclipse.core.runtime.IPluginRegistry; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.IStatusLineManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferenceConverter; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IFindReplaceTarget; +import org.eclipse.jface.text.IFindReplaceTargetExtension; +import org.eclipse.jface.text.IMarkRegionTarget; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.IRewriteTarget; +import org.eclipse.jface.text.ITextInputListener; +import org.eclipse.jface.text.ITextListener; +import org.eclipse.jface.text.ITextOperationTarget; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.text.ITextViewerExtension; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.TextEvent; +import org.eclipse.jface.text.TextSelection; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.text.source.IVerticalRuler; +import org.eclipse.jface.text.source.IVerticalRulerExtension; +import org.eclipse.jface.text.source.IVerticalRulerInfo; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.jface.text.source.SourceViewerConfiguration; +import org.eclipse.jface.text.source.VerticalRuler; +import org.eclipse.jface.util.Assert; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; + +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IEditorActionBarContributor; +import org.eclipse.ui.IEditorDescriptor; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorRegistry; +import org.eclipse.ui.IEditorSite; +import org.eclipse.ui.IKeyBindingService; +import org.eclipse.ui.INavigationLocationProvider; +import org.eclipse.ui.IPartListener; +import org.eclipse.ui.IReusableEditor; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.NavigationLocation; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.actions.WorkspaceModifyOperation; +import org.eclipse.ui.help.WorkbenchHelp; +import org.eclipse.ui.internal.EditorPluginAction; +import org.eclipse.ui.part.EditorActionBarContributor; +import org.eclipse.ui.part.EditorPart; + + + + +/** + * Abstract base implementation of a text editor. + * <p> + * Subclasses are responsible for configuring the editor appropriately. + * The standard text editor, <code>TextEditor</code>, is one such example. + * </p> + * <p> + * If a subclass calls <code>setEditorContextMenuId</code> the arguments is + * used as the id under which the editor's context menu is registered for extensions. + * If no id is set, the context menu is registered under <b>[editor_id].EditorContext</b> + * whereby [editor_id] is replaced with the editor's part id. If the editor is instructed to + * run in version 1.0 context menu registration compatibility mode, the latter form of the + * registration even happens if a context menu id has been set via <code>setEditorContextMenuId</code>. + * If no id is set while in compatibility mode, the menu is registered under + * <code>DEFAULT_EDITOR_CONTEXT_MENU_ID</code>. + * </p> + * <p> + * If a subclass calls <code>setRulerContextMenuId</code> the arguments is + * used as the id under which the ruler's context menu is registered for extensions. + * If no id is set, the context menu is registered under <b>[editor_id].RulerContext</b> + * whereby [editor_id] is replaced with the editor's part id. If the editor is instructed to + * run in version 1.0 context menu registration compatibility mode, the latter form of the + * registration even happens if a context menu id has been set via <code>setRulerContextMenuId</code>. + * If no id is set while in compatibility mode, the menu is registered under + * <code>DEFAULT_RULER_CONTEXT_MENU_ID</code>. + * </p> + * + * @see org.eclipse.ui.editors.text.TextEditor + */ +public abstract class AbstractTextEditor extends EditorPart implements ITextEditor, IReusableEditor, ITextEditorExtension, INavigationLocationProvider { + + /** + * Tag used in xml configuration files to specify editor action contributions. + * Current value: <code>editorContribution</code> + * @since 2.0 + */ + private static final String TAG_CONTRIBUTION_TYPE= "editorContribution"; //$NON-NLS-1$ + + /** + * Internal element state listener. + */ + class ElementStateListener implements IElementStateListener, IElementStateListenerExtension { + + /** + * Internal <code>VerifyListener</code> for performing the state validation of the + * editor input in case of the first attempted manipulation via typing on the keyboard. + * @since 2.0 + */ + class Validator implements VerifyListener { + + /** Indicates whether the editor input changed during the process of state validation. */ + private boolean fInputChanged; + /** Detector for editor input changes during the process of state validation. */ + private ITextInputListener fInputListener= new ITextInputListener() { + public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {} + public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { fInputChanged= true; } + }; + + /* + * @see VerifyListener#verifyText(VerifyEvent) + */ + public void verifyText(VerifyEvent e) { + + ISourceViewer viewer= getSourceViewer(); + fInputChanged= false; + viewer.addTextInputListener(fInputListener); + try { + validateState(getEditorInput()); + sanityCheckState(getEditorInput()); + if (isEditorInputReadOnly() || fInputChanged) + e.doit= false; + } finally { + viewer.removeTextInputListener(fInputListener); + } + } + }; + + /** + * The listener's validator. + * @since 2.0 + */ + private Validator fValidator; + + /* + * @see IElementStateListenerExtension#elementStateValidationChanged(Object, boolean) + * @since 2.0 + */ + public void elementStateValidationChanged(Object element, boolean isStateValidated) { + + if (element != null && element.equals(getEditorInput())) { + + enableSanityChecking(true); + + if (isStateValidated && fValidator != null) { + ISourceViewer viewer= getSourceViewer(); + if (viewer != null) { + StyledText textWidget= viewer.getTextWidget(); + if (textWidget != null && !textWidget.isDisposed()) + textWidget.removeVerifyListener(fValidator); + fValidator= null; + } + } else if (!isStateValidated && fValidator == null) { + ISourceViewer viewer= getSourceViewer(); + if (viewer != null) { + StyledText textWidget= viewer.getTextWidget(); + if (textWidget != null && !textWidget.isDisposed()) { + fValidator= new Validator(); + textWidget.addVerifyListener(fValidator); + } + } + } + + } + } + + /* + * @see IElementStateListener#elementDirtyStateChanged + */ + public void elementDirtyStateChanged(Object element, boolean isDirty) { + if (element != null && element.equals(getEditorInput())) { + enableSanityChecking(true); + firePropertyChange(PROP_DIRTY); + } + } + + /* + * @see IElementStateListener#elementContentAboutToBeReplaced + */ + public void elementContentAboutToBeReplaced(Object element) { + if (element != null && element.equals(getEditorInput())) { + enableSanityChecking(true); + rememberSelection(); + resetHighlightRange(); + } + } + + /* + * @see IElementStateListener#elementContentReplaced + */ + public void elementContentReplaced(Object element) { + if (element != null && element.equals(getEditorInput())) { + enableSanityChecking(true); + firePropertyChange(PROP_DIRTY); + restoreSelection(); + } + } + + /* + * @see IElementStateListener#elementDeleted + */ + public void elementDeleted(Object deletedElement) { + if (deletedElement != null && deletedElement.equals(getEditorInput())) { + enableSanityChecking(true); + close(false); + } + } + + /* + * @see IElementStateListener#elementMoved + */ + public void elementMoved(Object originalElement, Object movedElement) { + + if (originalElement != null && originalElement.equals(getEditorInput())) { + + enableSanityChecking(true); + + if (!canHandleMove((IEditorInput) originalElement, (IEditorInput) movedElement)) { + close(true); + return; + } + + if (movedElement == null || movedElement instanceof IEditorInput) { + rememberSelection(); + + IDocumentProvider d= getDocumentProvider(); + IDocument changed= null; + if (isDirty()) + changed= d.getDocument(getEditorInput()); + + setInput((IEditorInput) movedElement); + + if (changed != null) { + d.getDocument(getEditorInput()).set(changed.get()); + validateState(getEditorInput()); + updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE); + } + + restoreSelection(); + } + } + } + + /* + * @see IElementStateListenerExtension#elementStateChanging(Object) + * @since 2.0 + */ + public void elementStateChanging(Object element) { + if (element != null && element.equals(getEditorInput())) + enableSanityChecking(false); + } + + /* + * @see IElementStateListenerExtension#elementStateChangeFailed(Object) + * @since 2.0 + */ + public void elementStateChangeFailed(Object element) { + if (element != null && element.equals(getEditorInput())) + enableSanityChecking(true); + } + }; + + /** + * Internal text listener for updating all content dependent + * actions. The updating is done asynchronously. + */ + class TextListener implements ITextListener { + + /** The posted updater code. */ + private Runnable fRunnable= new Runnable() { + public void run() { + if (fSourceViewer != null) { + // check whether editor has not been disposed yet + updateContentDependentActions(); + } + } + }; + + /** Display used for posting the updater code. */ + private Display fDisplay; + + /* + * @see ITextListener#textChanged(TextEvent) + */ + public void textChanged(TextEvent event) { + + /* + * Also works for text events which do not base on a DocumentEvent. + * This way, if the visible document of the viewer changes, all content + * dependent actions are updated as well. + */ + + if (fDisplay == null) + fDisplay= getSite().getShell().getDisplay(); + + fDisplay.asyncExec(fRunnable); + } + }; + + /** + * Compare configuration elements according to the prerequisite relation + * of their defining plug-ins. + * @since 2.0 + */ + static class ConfigurationElementComparator implements Comparator { + + /* + * @see Comparator#compare(Object, Object) + * @since 2.0 + */ + public int compare(Object object0, Object object1) { + + IConfigurationElement element0= (IConfigurationElement)object0; + IConfigurationElement element1= (IConfigurationElement)object1; + + if (dependsOn(element0, element1)) + return -1; + + if (dependsOn(element1, element0)) + return +1; + + return 0; + } + + /** + * Returns whether one configuration element depends on the other element. Does this by + * checking the dependency chain of the defining plugins. + * + * @param element0 the first element + * @param element1 the second element + * @return <code>true</code> if <code>element0</code> depends on <code>element1</code>. + * @since 2.0 + */ + private static boolean dependsOn(IConfigurationElement element0, IConfigurationElement element1) { + IPluginDescriptor descriptor0= element0.getDeclaringExtension().getDeclaringPluginDescriptor(); + IPluginDescriptor descriptor1= element1.getDeclaringExtension().getDeclaringPluginDescriptor(); + + return dependsOn(descriptor0, descriptor1); + } + + /** + * Returns whether one plugin depends on the other plugin. + * + * @param descriptor0 descriptor of the first plugin + * @param descriptor1 descriptor of the second plugin + * @return <code>true</code> if <code>descriptor0</code> depends on <code>descriptor1</code>. + * @since 2.0 + */ + private static boolean dependsOn(IPluginDescriptor descriptor0, IPluginDescriptor descriptor1) { + + IPluginRegistry registry= Platform.getPluginRegistry(); + IPluginPrerequisite[] prerequisites= descriptor0.getPluginPrerequisites(); + + for (int i= 0; i < prerequisites.length; i++) { + IPluginPrerequisite prerequisite= prerequisites[i]; + String id= prerequisite.getUniqueIdentifier(); + IPluginDescriptor descriptor= registry.getPluginDescriptor(id); + + if (descriptor != null && (descriptor.equals(descriptor1) || dependsOn(descriptor, descriptor1))) + return true; + } + + return false; + } + } + + /** + * Internal property change listener for handling changes in the editor's preferences. + */ + class PropertyChangeListener implements IPropertyChangeListener { + /* + * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent) + */ + public void propertyChange(PropertyChangeEvent event) { + handlePreferenceStoreChanged(event); + } + }; + + /** + * Internal key verify listener for triggering action activation codes. + */ + class ActivationCodeTrigger implements VerifyKeyListener { + + /** Indicates whether this trigger has been installed. */ + private boolean fIsInstalled= false; + /** + * The key binding service to use. + * @since 2.0 + */ + private IKeyBindingService fKeyBindingService; + + /* + * @see VerifyKeyListener#verifyKey(VerifyEvent) + */ + public void verifyKey(VerifyEvent event) { + + ActionActivationCode code= null; + int size= fActivationCodes.size(); + for (int i= 0; i < size; i++) { + code= (ActionActivationCode) fActivationCodes.get(i); + if (code.matches(event)) { + IAction action= getAction(code.fActionId); + if (action != null) { + + if (action instanceof IUpdate) + ((IUpdate) action).update(); + + if (!action.isEnabled() && action instanceof IReadOnlyDependent) { + IReadOnlyDependent dependent= (IReadOnlyDependent) action; + boolean writable= dependent.isEnabled(true); + if (writable) { + event.doit= false; + return; + } + } else if (action.isEnabled()) { + event.doit= false; + action.run(); + return; + } + } + } + } + if (fKeyBindingService.processKey(event)) + event.doit= false; + } + + /** + * Installs this trigger on the editor's text widget. + * @since 2.0 + */ + public void install() { + if (!fIsInstalled) { + + if (fSourceViewer instanceof ITextViewerExtension) { + ITextViewerExtension e= (ITextViewerExtension) fSourceViewer; + e.prependVerifyKeyListener(this); + } else { + StyledText text= fSourceViewer.getTextWidget(); + text.addVerifyKeyListener(this); + } + + fKeyBindingService= getEditorSite().getKeyBindingService(); + fKeyBindingService.enable(true); + fIsInstalled= true; + } + } + + /** + * Uninstalls this trigger from the editor's text widget. + * @since 2.0 + */ + public void uninstall() { + if (fIsInstalled) { + + if (fSourceViewer instanceof ITextViewerExtension) { + ITextViewerExtension e= (ITextViewerExtension) fSourceViewer; + e.removeVerifyKeyListener(this); + } else if (fSourceViewer != null) { + StyledText text= fSourceViewer.getTextWidget(); + if (text != null && !text.isDisposed()) + text.removeVerifyKeyListener(fActivationCodeTrigger); + } + + fIsInstalled= false; + fKeyBindingService= null; + } + } + + /** + * Registers the given action for key activation. + * @param action the action to be registered + * @since 2.0 + */ + public void registerActionForKeyActivation(IAction action) { + if (action.getActionDefinitionId() != null) + fKeyBindingService.registerAction(action); + } + + /** + * The given action is no longer available for key activation + * @param action the action to be unregistered + * @since 2.0 + */ + public void unregisterActionFromKeyActivation(IAction action) { + // No such action available on the service + } + }; + + /** + * Representation of action activation codes. + */ + static class ActionActivationCode { + + public String fActionId; + public char fCharacter; + public int fKeyCode= -1; + public int fStateMask= SWT.DEFAULT; + + /** + * Creates a new action activation code for the given action id. + * @param actionId the action id + */ + public ActionActivationCode(String actionId) { + fActionId= actionId; + } + + /** + * Returns <code>true</code> if this activation code matches the given verify event. + * @param event the event to test for matching + */ + public boolean matches(VerifyEvent event) { + return (event.character == fCharacter && + (fKeyCode == -1 || event.keyCode == fKeyCode) && + (fStateMask == SWT.DEFAULT || event.stateMask == fStateMask)); + } + }; + + /** + * Internal part and shell activation listener for triggering state validation. + * @since 2.0 + */ + class ActivationListener extends ShellAdapter implements IPartListener { + + /** Cache of the active workbench part. */ + private IWorkbenchPart fActivePart; + /** Indicates whether activation handling is currently be done. */ + private boolean fIsHandlingActivation= false; + + /* + * @see IPartListener#partActivated(IWorkbenchPart) + */ + public void partActivated(IWorkbenchPart part) { + fActivePart= part; + handleActivation(); + } + + /* + * @see IPartListener#partBroughtToTop(IWorkbenchPart) + */ + public void partBroughtToTop(IWorkbenchPart part) { + } + + /* + * @see IPartListener#partClosed(IWorkbenchPart) + */ + public void partClosed(IWorkbenchPart part) { + } + + /* + * @see IPartListener#partDeactivated(IWorkbenchPart) + */ + public void partDeactivated(IWorkbenchPart part) { + fActivePart= null; + } + + /* + * @see IPartListener#partOpened(IWorkbenchPart) + */ + public void partOpened(IWorkbenchPart part) { + } + + /* + * @see ShellListener#shellActivated(ShellEvent) + */ + public void shellActivated(ShellEvent e) { + /* + * Workaround for problem described in + * http://dev.eclipse.org/bugs/show_bug.cgi?id=11731 + * Will be removed when SWT has solved the problem. + */ + e.widget.getDisplay().asyncExec(new Runnable() { + public void run() { + handleActivation(); + } + }); + } + + /** + * Handles the activation triggering a element state check in the editor. + */ + private void handleActivation() { + if (fIsHandlingActivation) + return; + + if (fActivePart == AbstractTextEditor.this) { + fIsHandlingActivation= true; + try { + safelySanityCheckState(getEditorInput()); + } finally { + fIsHandlingActivation= false; + } + } + } + }; + + /** + * Internal interface for a cursor listener. I.e. aggregation + * of mouse and key listener. + * @since 2.0 + */ + interface ICursorListener extends MouseListener, KeyListener { + }; + + /** + * Maps an action definition id to an StyledText action. + * @since 2.0 + */ + static class IdMapEntry { + + /** The action id */ + private String fActionId; + /** The StyledText action */ + private int fAction; + + /** + * Creates a new mapping. + * @param actionId the action id + * @param action the StyledText action + */ + public IdMapEntry(String actionId, int action) { + fActionId= actionId; + fAction= action; + } + + /** + * Returns the action id. + * @return the action id + */ + public String getActionId() { + return fActionId; + } + + /** + * Returns the action + * @return the action + */ + public int getAction() { + return fAction; + } + }; + + /** + * Internal action to scroll the editor's viewer by a specified number of lines. + * @since 2.0 + */ + class ScrollLinesAction extends Action { + + /** Number of lines to scroll. */ + private int fScrollIncrement; + + /** + * Creates a new scroll action that scroll the given number of lines. If the + * increment is < 0, it's scrolling up, if > 0 it's scrolling down. + * @param scrollIncrement the number of lines to scroll + */ + public ScrollLinesAction(int scrollIncrement) { + fScrollIncrement= scrollIncrement; + } + + /* + * @see IAction#run() + */ + public void run() { + ISourceViewer viewer= getSourceViewer(); + int topIndex= viewer.getTopIndex(); + int newTopIndex= Math.max(0, topIndex + fScrollIncrement); + viewer.setTopIndex(newTopIndex); + } + }; + + /** + * @since 2.1 + */ + class ToggleInsertModeAction extends TextNavigationAction { + + public ToggleInsertModeAction(StyledText textWidget) { + super(textWidget, ST.TOGGLE_OVERWRITE); + } + + /* + * @see org.eclipse.jface.action.IAction#run() + */ + public void run() { + super.run(); + fOverwriting= !fOverwriting; + handleInsertModeChanged(); + } + + }; + + /** + * Internal action to show the editor's ruler context menu (accessibility). + * @since 2.0 + */ + class ShowRulerContextMenuAction extends Action { + + /* + * @see IAction#run() + */ + public void run() { + if (fSourceViewer == null) + return; + + StyledText text= fSourceViewer.getTextWidget(); + if (text == null || text.isDisposed()) + return; + + Point location= text.getLocationAtOffset(text.getCaretOffset()); + location.x= 0; + + if (fVerticalRuler instanceof IVerticalRulerExtension) + ((IVerticalRulerExtension) fVerticalRuler).setLocationOfLastMouseButtonActivity(location.x, location.y); + + location= text.toDisplay(location); + fRulerContextMenu.setLocation(location.x, location.y); + fRulerContextMenu.setVisible(true); + } + }; + + + /** + * Editor specific selection provider which wraps the source viewer's selection provider. + * + */ + class SelectionProvider implements ISelectionProvider { + + /* + * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(ISelectionChangedListener) + */ + public void addSelectionChangedListener(ISelectionChangedListener listener) { + if (fSourceViewer != null) + fSourceViewer.getSelectionProvider().addSelectionChangedListener(listener); + } + + /* + * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection() + */ + public ISelection getSelection() { + return doGetSelection(); + } + + /* + * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(ISelectionChangedListener) + */ + public void removeSelectionChangedListener(ISelectionChangedListener listener) { + if (fSourceViewer != null) + fSourceViewer.getSelectionProvider().removeSelectionChangedListener(listener); + } + + /* + * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(ISelection) + */ + public void setSelection(ISelection selection) { + doSetSelection(selection); + } + }; + + + + /** Key used to look up font preference */ + public final static String PREFERENCE_FONT= JFaceResources.TEXT_FONT; + /** + * Key used to look up foreground color preference + * Value: <code>AbstractTextEditor.Color.Foreground</code> + * @since 2.0 + */ + public final static String PREFERENCE_COLOR_FOREGROUND= "AbstractTextEditor.Color.Foreground"; //$NON-NLS-1$ + /** + * Key used to look up background color preference + * Value: <code>AbstractTextEditor.Color.Background</code> + * @since 2.0 + */ + public final static String PREFERENCE_COLOR_BACKGROUND= "AbstractTextEditor.Color.Background"; //$NON-NLS-1$ + /** + * Key used to look up foreground color system default preference + * Value: <code>AbstractTextEditor.Color.Foreground.SystemDefault</code> + * @since 2.0 + */ + public final static String PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT= "AbstractTextEditor.Color.Foreground.SystemDefault"; //$NON-NLS-1$ + /** + * Key used to look up background color system default preference + * Value: <code>AbstractTextEditor.Color.Background.SystemDefault</code> + * @since 2.0 + */ + public final static String PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT= "AbstractTextEditor.Color.Background.SystemDefault"; //$NON-NLS-1$ + /** + * Key used to look up find scope background color preference + * Value: <code>AbstractTextEditor.Color.FindScope</code> + * @since 2.0 + */ + public final static String PREFERENCE_COLOR_FIND_SCOPE= "AbstractTextEditor.Color.FindScope"; //$NON-NLS-1$ + + /** Menu id for the editor context menu. */ + public final static String DEFAULT_EDITOR_CONTEXT_MENU_ID= "#EditorContext"; //$NON-NLS-1$ + /** Menu id for the ruler context menu. */ + public final static String DEFAULT_RULER_CONTEXT_MENU_ID= "#RulerContext"; //$NON-NLS-1$ + + /** The width of the vertical ruler */ + protected final static int VERTICAL_RULER_WIDTH= 12; + + /** + * The complete mapping between action definition ids used by eclipse and StyledText actions. + * @since 2.0 + */ + protected final static IdMapEntry[] ACTION_MAP= new IdMapEntry[] { + // navigation + new IdMapEntry(ITextEditorActionDefinitionIds.LINE_UP, ST.LINE_UP), + new IdMapEntry(ITextEditorActionDefinitionIds.LINE_DOWN, ST.LINE_DOWN), + new IdMapEntry(ITextEditorActionDefinitionIds.LINE_START, ST.LINE_START), + new IdMapEntry(ITextEditorActionDefinitionIds.LINE_END, ST.LINE_END), + new IdMapEntry(ITextEditorActionDefinitionIds.COLUMN_PREVIOUS, ST.COLUMN_PREVIOUS), + new IdMapEntry(ITextEditorActionDefinitionIds.COLUMN_NEXT, ST.COLUMN_NEXT), + new IdMapEntry(ITextEditorActionDefinitionIds.PAGE_UP, ST.PAGE_UP), + new IdMapEntry(ITextEditorActionDefinitionIds.PAGE_DOWN, ST.PAGE_DOWN), + new IdMapEntry(ITextEditorActionDefinitionIds.WORD_PREVIOUS, ST.WORD_PREVIOUS), + new IdMapEntry(ITextEditorActionDefinitionIds.WORD_NEXT, ST.WORD_NEXT), + new IdMapEntry(ITextEditorActionDefinitionIds.TEXT_START, ST.TEXT_START), + new IdMapEntry(ITextEditorActionDefinitionIds.TEXT_END, ST.TEXT_END), + new IdMapEntry(ITextEditorActionDefinitionIds.WINDOW_START, ST.WINDOW_START), + new IdMapEntry(ITextEditorActionDefinitionIds.WINDOW_END, ST.WINDOW_END), + // selection + new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_UP, ST.SELECT_LINE_UP), + new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_DOWN, ST.SELECT_LINE_DOWN), + new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_START, ST.SELECT_LINE_START), + new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_END, ST.SELECT_LINE_END), + new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_COLUMN_PREVIOUS, ST.SELECT_COLUMN_PREVIOUS), + new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_COLUMN_NEXT, ST.SELECT_COLUMN_NEXT), + new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_PAGE_UP, ST.SELECT_PAGE_UP), + new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_PAGE_DOWN, ST.SELECT_PAGE_DOWN), + new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS, ST.SELECT_WORD_PREVIOUS), + new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT, ST.SELECT_WORD_NEXT), + new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_TEXT_START, ST.SELECT_TEXT_START), + new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_TEXT_END, ST.SELECT_TEXT_END), + new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WINDOW_START, ST.SELECT_WINDOW_START), + new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WINDOW_END, ST.SELECT_WINDOW_END), + // modification + new IdMapEntry(ITextEditorActionDefinitionIds.CUT, ST.CUT), + new IdMapEntry(ITextEditorActionDefinitionIds.COPY, ST.COPY), + new IdMapEntry(ITextEditorActionDefinitionIds.PASTE, ST.PASTE), + new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_PREVIOUS, ST.DELETE_PREVIOUS), + new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_NEXT, ST.DELETE_NEXT) + }; + + + private final String fReadOnlyLabel = EditorMessages.getString("Editor.statusline.state.readonly.label"); //$NON-NLS-1$ + private final String fWritableLabel = EditorMessages.getString("Editor.statusline.state.writable.label"); //$NON-NLS-1$ + private final String fInsertModeLabel = EditorMessages.getString("Editor.statusline.mode.insert.label"); //$NON-NLS-1$ + private final String fOverwriteModeLabel = EditorMessages.getString("Editor.statusline.mode.overwrite.label"); //$NON-NLS-1$ + + private static class PositionLabelValue { + + public int fValue; + + public String toString() { + return String.valueOf(fValue); + } + }; + + /** The error message shown in the status line in case of failed information look up. */ + protected final String fErrorLabel= EditorMessages.getString("Editor.statusline.error.label"); //$NON-NLS-1$ + private final String fPositionLabelPattern= EditorMessages.getString("Editor.statusline.position.pattern"); //$NON-NLS-1$ + private final PositionLabelValue fLineLabel= new PositionLabelValue(); + private final PositionLabelValue fColumnLabel= new PositionLabelValue(); + private final Object[] fPositionLabelPatternArguments= new Object[] { fLineLabel, fColumnLabel }; + + + + + /** The editor's internal document provider */ + private IDocumentProvider fInternalDocumentProvider; + /** The editor's external document provider */ + private IDocumentProvider fExternalDocumentProvider; + /** The editor's preference store */ + private IPreferenceStore fPreferenceStore; + /** The editor's range indicator */ + private Annotation fRangeIndicator; + /** The editor's source viewer configuration */ + private SourceViewerConfiguration fConfiguration; + /** The editor's source viewer */ + private ISourceViewer fSourceViewer; + /** The editor's selection provider */ + private SelectionProvider fSelectionProvider= new SelectionProvider(); + /** The editor's font */ + private Font fFont; + /** + * The editor's foreground color + * @since 2.0 + */ + private Color fForegroundColor; + /** + * The editor's background color + * @since 2.0 + */ + private Color fBackgroundColor; + /** + * The find scope's highlight color + * @since 2.0 + */ + private Color fFindScopeHighlightColor; + + /** The editor's status line */ + private IEditorStatusLine fEditorStatusLine; + /** The editor's vertical ruler */ + private IVerticalRuler fVerticalRuler; + /** The editor's context menu id */ + private String fEditorContextMenuId; + /** The ruler's context menu id */ + private String fRulerContextMenuId; + /** The editor's help context id */ + private String fHelpContextId; + /** The editor's presentation mode */ + private boolean fShowHighlightRangeOnly; + /** The actions registered with the editor */ + private Map fActions= new HashMap(10); + /** The actions marked as selection dependent */ + private List fSelectionActions= new ArrayList(5); + /** The actions marked as content dependent */ + private List fContentActions= new ArrayList(5); + /** + * The actions marked as property dependent + * @since 2.0 + */ + private List fPropertyActions= new ArrayList(5); + /** + * The actions marked as state dependent + * @since 2.0 + */ + private List fStateActions= new ArrayList(5); + /** The editor's action activation codes */ + private List fActivationCodes= new ArrayList(2); + /** The verify key listener for activation code triggering */ + private ActivationCodeTrigger fActivationCodeTrigger= new ActivationCodeTrigger(); + /** Context menu listener */ + private IMenuListener fMenuListener; + /** Vertical ruler mouse listener */ + private MouseListener fMouseListener; + /** Selection changed listener */ + private ISelectionChangedListener fSelectionChangedListener; + /** Title image to be disposed */ + private Image fTitleImage; + /** The text context menu to be disposed */ + private Menu fTextContextMenu; + /** The ruler context menu to be disposed */ + private Menu fRulerContextMenu; + /** The editor's element state listener */ + private IElementStateListener fElementStateListener= new ElementStateListener(); + /** The editor's text listener */ + private ITextListener fTextListener= new TextListener(); + /** The editor's property change listener */ + private IPropertyChangeListener fPropertyChangeListener= new PropertyChangeListener(); + /** + * The editor's activation listener + * @since 2.0 + */ + private ActivationListener fActivationListener= new ActivationListener(); + /** + * The map of the editor's status fields + * @since 2.0 + */ + private Map fStatusFields; + /** + * The editor's cursor listener + * @since 2.0 + */ + private ICursorListener fCursorListener; + /** + * The editor's insert mode + * @since 2.0 + */ + private boolean fOverwriting= false; + /** + * The editor's remembered text selection + * @since 2.0 + */ + private ISelection fRememberedSelection; + /** + * Indicates whether the editor runs in 1.0 context menu registration compatibility mode + * @since 2.0 + */ + private boolean fCompatibilityMode= true; + /** + * The number of reentrances into error correction code while saving + * @since 2.0 + */ + private int fErrorCorrectionOnSave; + /** + * The incremental find target + * @since 2.0 + */ + private IncrementalFindTarget fIncrementalFindTarget; + /** + * The mark region target + * @since 2.0 + */ + private IMarkRegionTarget fMarkRegionTarget; + /** + * Cached modification stamp of the editor's input + * @since 2.0 + */ + private long fModificationStamp= -1; + /** + * Ruler context menu listeners. + * @since 2.0 + */ + private List fRulerContextMenuListeners= new ArrayList(); + /** + * Indicates whether sanity checking in enabled. + * @since 2.0 + */ + private boolean fIsSanityCheckEnabled= true; + + + + /** + * Creates a new text editor. If not explicitly set, this editor uses + * a <code>SourceViewerConfiguration</code> to configure its + * source viewer. This viewer does not have a range indicator installed, + * nor any menu id set. By default, the created editor runs in 1.0 context + * menu registration compatibility mode. + */ + protected AbstractTextEditor() { + super(); + fEditorContextMenuId= null; + fRulerContextMenuId= null; + fHelpContextId= null; + } + + /* + * @see ITextEditor#getDocumentProvider + */ + public IDocumentProvider getDocumentProvider() { + if (fInternalDocumentProvider != null) + return fInternalDocumentProvider; + return fExternalDocumentProvider; + } + + /** + * Returns the editor's range indicator. + * + * @return the editor's range indicator + */ + protected final Annotation getRangeIndicator() { + return fRangeIndicator; + } + + /** + * Returns the editor's source viewer configuration. + * + * @return the editor's source viewer configuration + */ + protected final SourceViewerConfiguration getSourceViewerConfiguration() { + return fConfiguration; + } + + /** + * Returns the editor's source viewer. + * + * @return the editor's source viewer + */ + protected final ISourceViewer getSourceViewer() { + return fSourceViewer; + } + + /** + * Returns the editor's vertical ruler. + * + * @return the editor's vertical ruler + */ + protected final IVerticalRuler getVerticalRuler() { + return fVerticalRuler; + } + + /** + * Returns the editor's context menu id. + * + * @return the editor's context menu id + */ + protected final String getEditorContextMenuId() { + return fEditorContextMenuId; + } + + /** + * Returns the ruler's context menu id. + * + * @return the ruler's context menu id + */ + protected final String getRulerContextMenuId() { + return fRulerContextMenuId; + } + + /** + * Returns the editor's help context id. + * + * @return the editor's help context id + */ + protected final String getHelpContextId() { + return fHelpContextId; + } + + /** + * Returns this editor's preference store. + * + * @return this editor's preference store + */ + protected final IPreferenceStore getPreferenceStore() { + return fPreferenceStore; + } + + /** + * Sets this editor's document provider. This method must be + * called before the editor's control is created. + * + * @param provider the document provider + */ + protected void setDocumentProvider(IDocumentProvider provider) { + Assert.isNotNull(provider); + fInternalDocumentProvider= provider; + } + + /** + * Sets this editor's source viewer configuration used to configure its + * internal source viewer. This method must be called before the editor's + * control is created. If not, this editor uses a <code>SourceViewerConfiguration</code>. + * + * @param configuration the source viewer configuration object + */ + protected void setSourceViewerConfiguration(SourceViewerConfiguration configuration) { + Assert.isNotNull(configuration); + fConfiguration= configuration; + } + + /** + * Sets the annotation which this editor uses to represent the highlight + * range if the editor is configured to show the entire document. If the + * range indicator is not set, this editor uses a <code>DefaultRangeIndicator</code>. + * + * @param rangeIndicator the annotation + */ + protected void setRangeIndicator(Annotation rangeIndicator) { + Assert.isNotNull(rangeIndicator); + fRangeIndicator= rangeIndicator; + } + + /** + * Sets this editor's context menu id. + * + * @param contextMenuId the context menu id + */ + protected void setEditorContextMenuId(String contextMenuId) { + Assert.isNotNull(contextMenuId); + fEditorContextMenuId= contextMenuId; + } + + /** + * Sets the ruler's context menu id. + * + * @param contextMenuId the context menu id + */ + protected void setRulerContextMenuId(String contextMenuId) { + Assert.isNotNull(contextMenuId); + fRulerContextMenuId= contextMenuId; + } + + /** + * Sets the context menu registration 1.0 compatibility mode. (See class + * description for more details.) + * + * @param compatible <code>true</code> if compatibility mode is enabled + * @since 2.0 + */ + protected final void setCompatibilityMode(boolean compatible) { + fCompatibilityMode= compatible; + } + + /** + * Sets the editor's help context id. + * + * @param helpContextId the help context id + */ + protected void setHelpContextId(String helpContextId) { + Assert.isNotNull(helpContextId); + fHelpContextId= helpContextId; + } + + /** + * Sets this editor's preference store. This method must be + * called before the editor's control is created. + * + * @param store the new preference store + */ + protected void setPreferenceStore(IPreferenceStore store) { + if (fPreferenceStore != null) + fPreferenceStore.removePropertyChangeListener(fPropertyChangeListener); + + fPreferenceStore= store; + + if (fPreferenceStore != null) + fPreferenceStore.addPropertyChangeListener(fPropertyChangeListener); + } + + /* + * @see ITextEditor#isEditable + */ + public boolean isEditable() { + IDocumentProvider provider= getDocumentProvider(); + if (provider instanceof IDocumentProviderExtension) { + IDocumentProviderExtension extension= (IDocumentProviderExtension) provider; + return extension.isModifiable(getEditorInput()); + } + return false; + } + + /* + * @see ITextEditor#getSelectionProvider + */ + public ISelectionProvider getSelectionProvider() { + return fSelectionProvider; + } + + /** + * Remembers the current selection of this editor. This method is called when, e.g., + * the content of the editor is about to be reverted to the saved state. This method + * remembers the selection in a semantic format, i.e., in a format which allows to + * restore the selection even if the originally selected text is no longer part of the + * editor's content.<p> + * Subclasses should implement this method including all necessary state. This + * default implementation remembers the textual range only and is thus purely + * syntactic. + * + * @see #restoreSelection + * @since 2.0 + */ + protected void rememberSelection() { + fRememberedSelection= doGetSelection(); + } + + /** + * Returns the current selection. + * @return ISelection + */ + protected ISelection doGetSelection() { + ISelectionProvider sp= null; + if (fSourceViewer != null) + sp= fSourceViewer.getSelectionProvider(); + return (sp == null ? null : sp.getSelection()); + } + + /** + * Restores a selection previously remembered by <code>rememberSelection</code>. + * Subclasses may reimplement this method and thereby semantically adapt the + * remembered selection. This default implementation just selects the + * remembered textual range. + * + * @see #rememberSelection + * @since 2.0 + */ + protected void restoreSelection() { + doSetSelection(fRememberedSelection); + fRememberedSelection= null; + } + + /** + * Sets the given selection. + * @param selection + */ + protected void doSetSelection(ISelection selection) { + if (selection instanceof ITextSelection) { + ITextSelection textSelection= (ITextSelection) selection; + selectAndReveal(textSelection.getOffset(), textSelection.getLength()); + } + } + + /** + * Creates and returns the listener on this editor's context menus. + * + * @return the menu listener + */ + protected final IMenuListener getContextMenuListener() { + if (fMenuListener == null) { + fMenuListener= new IMenuListener() { + + public void menuAboutToShow(IMenuManager menu) { + String id= menu.getId(); + if (getRulerContextMenuId().equals(id)) { + setFocus(); + rulerContextMenuAboutToShow(menu); + } else if (getEditorContextMenuId().equals(id)) { + setFocus(); + editorContextMenuAboutToShow(menu); + } + } + }; + } + return fMenuListener; + } + + /** + * Creates and returns the listener on this editor's vertical ruler. + * + * @return the mouse listener + */ + protected final MouseListener getRulerMouseListener() { + if (fMouseListener == null) { + fMouseListener= new MouseListener() { + + private boolean fDoubleClicked= false; + + private void triggerAction(String actionID) { + IAction action= getAction(actionID); + if (action != null) { + if (action instanceof IUpdate) + ((IUpdate) action).update(); + if (action.isEnabled()) + action.run(); + } + } + + public void mouseUp(MouseEvent e) { + setFocus(); + if (1 == e.button && !fDoubleClicked) + triggerAction(ITextEditorActionConstants.RULER_CLICK); + fDoubleClicked= false; + } + + public void mouseDoubleClick(MouseEvent e) { + if (1 == e.button) { + fDoubleClicked= true; + triggerAction(ITextEditorActionConstants.RULER_DOUBLE_CLICK); + } + } + + public void mouseDown(MouseEvent e) { + StyledText text= fSourceViewer.getTextWidget(); + if (text != null && !text.isDisposed()) { + Display display= text.getDisplay(); + Point location= display.getCursorLocation(); + fRulerContextMenu.setLocation(location.x, location.y); + } + } + }; + } + return fMouseListener; + } + + /** + * Returns this editor's selection changed listener to be installed + * on the editor's source viewer. + * + * @return the listener + */ + protected final ISelectionChangedListener getSelectionChangedListener() { + if (fSelectionChangedListener == null) { + fSelectionChangedListener= new ISelectionChangedListener() { + + private Runnable fRunnable= new Runnable() { + public void run() { + // check whether editor has not been disposed yet + if (fSourceViewer != null) { + updateSelectionDependentActions(); + handleCursorPositionChanged(); + } + } + }; + + private Display fDisplay; + + public void selectionChanged(SelectionChangedEvent event) { + if (fDisplay == null) + fDisplay= getSite().getShell().getDisplay(); + fDisplay.asyncExec(fRunnable); + } + }; + } + + return fSelectionChangedListener; + } + + /** + * Returns this editor's "cursor" listener to be installed on the editor's + * source viewer. This listener is listening to key and mouse button events. + * It triggers the updating of the status line by calling + * <code>handleCursorPositionChanged()</code>. + * + * @return the listener + * @since 2.0 + */ + protected final ICursorListener getCursorListener() { + if (fCursorListener == null) { + fCursorListener= new ICursorListener() { + + public void keyPressed(KeyEvent e) { + } + + public void keyReleased(KeyEvent e) { + handleCursorPositionChanged(); + } + + public void mouseDoubleClick(MouseEvent e) { + } + + public void mouseDown(MouseEvent e) { + } + + public void mouseUp(MouseEvent e) { + handleCursorPositionChanged(); + } + }; + } + return fCursorListener; + } + + /* + * @see IEditorPart#init + */ + public void init(IEditorSite site, IEditorInput input) throws PartInitException { + + setSite(site); + + try { + doSetInput(input); + } catch (CoreException x) { + throw new PartInitException(x.getStatus()); + } + + IWorkbenchWindow window= getSite().getWorkbenchWindow(); + window.getPartService().addPartListener(fActivationListener); + window.getShell().addShellListener(fActivationListener); + } + + /** + * Creates the vertical ruler to be used by this editor. + * Subclasses may re-implement this method. + * + * @return the vertical ruler + */ + protected IVerticalRuler createVerticalRuler() { + return new VerticalRuler(VERTICAL_RULER_WIDTH); + } + + /** + * Creates the source viewer to be used by this editor. + * Subclasses may re-implement this method. + * + * @param parent the parent control + * @param ruler the vertical ruler + * @param styles style bits + * @return the source viewer + */ + protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) { + return new SourceViewer(parent, ruler, styles); + } + + /** + * The <code>AbstractTextEditor</code> implementation of this + * <code>IWorkbenchPart</code> method creates the vertical ruler and + * source viewer. Subclasses may extend. + */ + public void createPartControl(Composite parent) { + + fVerticalRuler= createVerticalRuler(); + + int styles= SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION; + fSourceViewer= createSourceViewer(parent, fVerticalRuler, styles); + + if (fConfiguration == null) + fConfiguration= new SourceViewerConfiguration(); + fSourceViewer.configure(fConfiguration); + + if (fRangeIndicator != null) + fSourceViewer.setRangeIndicator(fRangeIndicator); + + fSourceViewer.addTextListener(fTextListener); + getSelectionProvider().addSelectionChangedListener(getSelectionChangedListener()); + + initializeViewerFont(fSourceViewer); + initializeViewerColors(fSourceViewer); + initializeFindScopeColor(fSourceViewer); + + StyledText styledText= fSourceViewer.getTextWidget(); + styledText.addMouseListener(getCursorListener()); + styledText.addKeyListener(getCursorListener()); + + if (getHelpContextId() != null) + WorkbenchHelp.setHelp(styledText, getHelpContextId()); + + + String id= fEditorContextMenuId != null ? fEditorContextMenuId : DEFAULT_EDITOR_CONTEXT_MENU_ID; + + MenuManager manager= new MenuManager(id, id); + manager.setRemoveAllWhenShown(true); + manager.addMenuListener(getContextMenuListener()); + fTextContextMenu= manager.createContextMenu(styledText); + styledText.setMenu(fTextContextMenu); + + if (fEditorContextMenuId != null) + getSite().registerContextMenu(fEditorContextMenuId, manager, getSelectionProvider()); + else if (fCompatibilityMode) + getSite().registerContextMenu(DEFAULT_EDITOR_CONTEXT_MENU_ID, manager, getSelectionProvider()); + + if ((fEditorContextMenuId != null && fCompatibilityMode) || fEditorContextMenuId == null) { + String partId= getSite().getId(); + if (partId != null) + getSite().registerContextMenu(partId + ".EditorContext", manager, getSelectionProvider()); //$NON-NLS-1$ + } + + if (fEditorContextMenuId == null) + fEditorContextMenuId= DEFAULT_EDITOR_CONTEXT_MENU_ID; + + + id= fRulerContextMenuId != null ? fRulerContextMenuId : DEFAULT_RULER_CONTEXT_MENU_ID; + manager= new MenuManager(id, id); + manager.setRemoveAllWhenShown(true); + manager.addMenuListener(getContextMenuListener()); + + Control rulerControl= fVerticalRuler.getControl(); + fRulerContextMenu= manager.createContextMenu(rulerControl); + rulerControl.setMenu(fRulerContextMenu); + rulerControl.addMouseListener(getRulerMouseListener()); + + if (fRulerContextMenuId != null) + getSite().registerContextMenu(fRulerContextMenuId, manager, getSelectionProvider()); + else if (fCompatibilityMode) + getSite().registerContextMenu(DEFAULT_RULER_CONTEXT_MENU_ID, manager, getSelectionProvider()); + + if ((fRulerContextMenuId != null && fCompatibilityMode) || fRulerContextMenuId == null) { + String partId= getSite().getId(); + if (partId != null) + getSite().registerContextMenu(partId + ".RulerContext", manager, getSelectionProvider()); //$NON-NLS-1$ + } + + if (fRulerContextMenuId == null) + fRulerContextMenuId= DEFAULT_RULER_CONTEXT_MENU_ID; + + getSite().setSelectionProvider(getSelectionProvider()); + + fActivationCodeTrigger.install(); + createNavigationActions(); + createAccessibilityActions(); + createActions(); + + initializeSourceViewer(getEditorInput()); + } + + /** + * Initializes the given viewer's font. + * + * @param viewer the viewer + * @since 2.0 + */ + private void initializeViewerFont(ISourceViewer viewer) { + + IPreferenceStore store= getPreferenceStore(); + if (store != null) { + + FontData data= null; + + if (store.contains(PREFERENCE_FONT) && !store.isDefault(PREFERENCE_FONT)) + data= PreferenceConverter.getFontData(store, PREFERENCE_FONT); + else + data= PreferenceConverter.getDefaultFontData(store, PREFERENCE_FONT); + + if (data != null) { + + Font font= new Font(viewer.getTextWidget().getDisplay(), data); + setFont(viewer, font); + + if (fFont != null) + fFont.dispose(); + + fFont= font; + return; + } + } + + // if all the preferences failed + setFont(viewer, JFaceResources.getTextFont()); + } + + /** + * Sets the font for the given viewer sustaining selection and scroll position. + * + * @param sourceViewer the source viewer + * @param font the font + * @since 2.0 + */ + private void setFont(ISourceViewer sourceViewer, Font font) { + if (sourceViewer.getDocument() != null) { + + Point selection= sourceViewer.getSelectedRange(); + int topIndex= sourceViewer.getTopIndex(); + + StyledText styledText= sourceViewer.getTextWidget(); + Control parent= styledText; + if (sourceViewer instanceof ITextViewerExtension) { + ITextViewerExtension extension= (ITextViewerExtension) sourceViewer; + parent= extension.getControl(); + } + + parent.setRedraw(false); + + styledText.setFont(font); + + if (fVerticalRuler instanceof IVerticalRulerExtension) { + IVerticalRulerExtension e= (IVerticalRulerExtension) fVerticalRuler; + e.setFont(font); + } + + sourceViewer.setSelectedRange(selection.x , selection.y); + sourceViewer.setTopIndex(topIndex); + + if (parent instanceof Composite) { + Composite composite= (Composite) parent; + composite.layout(true); + } + + parent.setRedraw(true); + + + } else { + + StyledText styledText= sourceViewer.getTextWidget(); + styledText.setFont(font); + + if (fVerticalRuler instanceof IVerticalRulerExtension) { + IVerticalRulerExtension e= (IVerticalRulerExtension) fVerticalRuler; + e.setFont(font); + } + } + } + + /** + * Creates a color from the information stored in the given preference store. + * Returns <code>null</code> if there is no such information available. + * + * @param store the store to read from + * @param key the key used for the lookup in the preference store + * @param display the display used create the color + * @return the created color according to the specification in the preference store + * @since 2.0 + */ + private Color createColor(IPreferenceStore store, String key, Display display) { + + RGB rgb= null; + + if (store.contains(key)) { + + if (store.isDefault(key)) + rgb= PreferenceConverter.getDefaultColor(store, key); + else + rgb= PreferenceConverter.getColor(store, key); + + if (rgb != null) + return new Color(display, rgb); + } + + return null; + } + + /** + * Initializes the given viewer's colors. + * + * @param viewer the viewer to be initialized + * @since 2.0 + */ + private void initializeViewerColors(ISourceViewer viewer) { + + IPreferenceStore store= getPreferenceStore(); + if (store != null) { + + StyledText styledText= viewer.getTextWidget(); + + // ----------- foreground color -------------------- + Color color= store.getBoolean(PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT) + ? null + : createColor(store, PREFERENCE_COLOR_FOREGROUND, styledText.getDisplay()); + styledText.setForeground(color); + + if (fForegroundColor != null) + fForegroundColor.dispose(); + + fForegroundColor= color; + + // ---------- background color ---------------------- + color= store.getBoolean(PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT) + ? null + : createColor(store, PREFERENCE_COLOR_BACKGROUND, styledText.getDisplay()); + styledText.setBackground(color); + + if (fBackgroundColor != null) + fBackgroundColor.dispose(); + + fBackgroundColor= color; + } + } + + /** + * Initializes the background color used for highlighting the document ranges + * defining search scopes. + * @param viewer the viewer to initialize + * @since 2.0 + */ + private void initializeFindScopeColor(ISourceViewer viewer) { + + IPreferenceStore store= getPreferenceStore(); + if (store != null) { + + StyledText styledText= viewer.getTextWidget(); + + Color color= createColor(store, PREFERENCE_COLOR_FIND_SCOPE, styledText.getDisplay()); + + IFindReplaceTarget target= viewer.getFindReplaceTarget(); + if (target != null && target instanceof IFindReplaceTargetExtension) + ((IFindReplaceTargetExtension) target).setScopeHighlightColor(color); + + if (fFindScopeHighlightColor != null) + fFindScopeHighlightColor.dispose(); + + fFindScopeHighlightColor= color; + } + } + + + /** + * Initializes the editor's source viewer based on the given editor input. + * + * @param input the editor input to be used to initialize the source viewer + */ + private void initializeSourceViewer(IEditorInput input) { + + IAnnotationModel model= getDocumentProvider().getAnnotationModel(input); + IDocument document= getDocumentProvider().getDocument(input); + + if (document != null) { + fSourceViewer.setDocument(document, model); + fSourceViewer.setEditable(isEditable()); + fSourceViewer.showAnnotations(model != null); + } + + if (fElementStateListener instanceof IElementStateListenerExtension) { + IElementStateListenerExtension extension= (IElementStateListenerExtension) fElementStateListener; + extension.elementStateValidationChanged(input, false); + } + } + + /** + * Initializes the editor's title based on the given editor input. + * + * @param input the editor input to be used + */ + private void initializeTitle(IEditorInput input) { + + Image oldImage= fTitleImage; + fTitleImage= null; + String title= ""; //$NON-NLS-1$ + + if (input != null) { + IEditorRegistry editorRegistry = getEditorSite().getPage().getWorkbenchWindow().getWorkbench().getEditorRegistry(); + IEditorDescriptor editorDesc= editorRegistry.findEditor(getSite().getId()); + ImageDescriptor imageDesc= editorDesc != null ? editorDesc.getImageDescriptor() : null; + + fTitleImage= imageDesc != null ? imageDesc.createImage() : null; + title= input.getName(); + } + + setTitleImage(fTitleImage); + setTitle(title); + + firePropertyChange(PROP_DIRTY); + + if (oldImage != null && !oldImage.isDisposed()) + oldImage.dispose(); + } + + /** + * If there is no implicit document provider set, the external one is + * re-initialized based on the given editor input. + * + * @param input the editor input. + */ + private void updateDocumentProvider(IEditorInput input) { + if (getDocumentProvider() != null) + getDocumentProvider().removeElementStateListener(fElementStateListener); + + if (fInternalDocumentProvider == null) + fExternalDocumentProvider= DocumentProviderRegistry.getDefault().getDocumentProvider(input); + + if (getDocumentProvider() != null) + getDocumentProvider().addElementStateListener(fElementStateListener); + } + + /** + * Internal <code>setInput</code> method. + * + * @param input the input to be set + * @exception CoreException if input cannot be connected to the document provider + */ + protected void doSetInput(IEditorInput input) throws CoreException { + + if (input == null) + + close(isSaveOnCloseNeeded()); + + else { + + IEditorInput oldInput= getEditorInput(); + if (oldInput != null) + getDocumentProvider().disconnect(oldInput); + + super.setInput(input); + + updateDocumentProvider(input); + + IDocumentProvider provider= getDocumentProvider(); + if (provider == null) { + IStatus s= new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, IStatus.OK, EditorMessages.getString("Editor.error.no_provider"), null); //$NON-NLS-1$ + throw new CoreException(s); + } + + provider.connect(input); + + initializeTitle(input); + if (fSourceViewer != null) + initializeSourceViewer(input); + + updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE); + } + } + + /* + * @see EditorPart#setInput + */ + public final void setInput(IEditorInput input) { + + try { + + doSetInput(input); + + } catch (CoreException x) { + String title= EditorMessages.getString("Editor.error.setinput.title"); //$NON-NLS-1$ + String msg= EditorMessages.getString("Editor.error.setinput.message"); //$NON-NLS-1$ + Shell shell= getSite().getShell(); + ErrorDialog.openError(shell, title, msg, x.getStatus()); + } + } + + /* + * @see ITextEditor#close + */ + public void close(final boolean save) { + + enableSanityChecking(false); + + Display display= getSite().getShell().getDisplay(); + display.asyncExec(new Runnable() { + public void run() { + if (fSourceViewer != null) { + // check whether editor has not been disposed yet + getSite().getPage().closeEditor(AbstractTextEditor.this, save); + } + } + }); + } + + /** + * The <code>AbstractTextEditor</code> implementation of this + * <code>IWorkbenchPart</code> method may be extended by subclasses. + * Subclasses must call <code>super.dispose()</code>. + */ + public void dispose() { + + if (fActivationListener != null) { + IWorkbenchWindow window= getSite().getWorkbenchWindow(); + window.getPartService().removePartListener(fActivationListener); + Shell shell= window.getShell(); + if (shell != null && !shell.isDisposed()) + shell.removeShellListener(fActivationListener); + fActivationListener= null; + } + + if (fTitleImage != null) { + fTitleImage.dispose(); + fTitleImage= null; + } + + if (fFont != null) { + fFont.dispose(); + fFont= null; + } + + if (fPropertyChangeListener != null) { + if (fPreferenceStore != null) { + fPreferenceStore.removePropertyChangeListener(fPropertyChangeListener); + fPreferenceStore= null; + } + fPropertyChangeListener= null; + } + + if (fActivationCodeTrigger != null) { + fActivationCodeTrigger.uninstall(); + fActivationCodeTrigger= null; + } + + IDocumentProvider provider= getDocumentProvider(); + if (provider != null) { + + IEditorInput input= getEditorInput(); + if (input != null) + provider.disconnect(input); + + if (fElementStateListener != null) { + provider.removeElementStateListener(fElementStateListener); + fElementStateListener= null; + } + + fInternalDocumentProvider= null; + fExternalDocumentProvider= null; + } + + if (fSourceViewer != null) { + + if (fTextListener != null) { + fSourceViewer.removeTextListener(fTextListener); + fTextListener= null; + } + + fSelectionProvider= null; + fSourceViewer= null; + } + + if (fTextContextMenu != null) { + fTextContextMenu.dispose(); + fTextContextMenu= null; + } + + if (fRulerContextMenu != null) { + fRulerContextMenu.dispose(); + fRulerContextMenu= null; + } + + if (fActions != null) { + fActions.clear(); + fActions= null; + } + + if (fSelectionActions != null) { + fSelectionActions.clear(); + fSelectionActions= null; + } + + if (fContentActions != null) { + fContentActions.clear(); + fContentActions= null; + } + + if (fPropertyActions != null) { + fPropertyActions.clear(); + fPropertyActions= null; + } + + if (fStateActions != null) { + fStateActions.clear(); + fStateActions= null; + } + + if (fActivationCodes != null) { + fActivationCodes.clear(); + fActivationCodes= null; + } + + if (fEditorStatusLine != null) + fEditorStatusLine= null; + + super.setInput(null); + + super.dispose(); + } + + /** + * Determines whether the given preference change affects the editor's + * presentation. This implementation always returns <code>false</code>. + * May be reimplemented by subclasses. + * + * @param event the event which should be investigated + * @return <code>true</code> if the event describes a preference change affecting the editor's presentation + * @since 2.0 + */ + protected boolean affectsTextPresentation(PropertyChangeEvent event) { + return false; + } + + /** + * Handles a property change event describing a change + * of the editor's preference store and updates the preference + * related editor properties. + * + * @param event the property change event + */ + protected void handlePreferenceStoreChanged(PropertyChangeEvent event) { + + if (fSourceViewer == null) + return; + + String property= event.getProperty(); + + if (PREFERENCE_FONT.equals(property)) { + initializeViewerFont(fSourceViewer); + + } else if (PREFERENCE_COLOR_FOREGROUND.equals(property) || PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT.equals(property) || + PREFERENCE_COLOR_BACKGROUND.equals(property) || PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT.equals(property)) + { + initializeViewerColors(fSourceViewer); + } else if (PREFERENCE_COLOR_FIND_SCOPE.equals(property)) { + initializeFindScopeColor(fSourceViewer); + } + + if (affectsTextPresentation(event)) + fSourceViewer.invalidateTextPresentation(); + } + + /** + * Handles an external change of the editor's input element. + */ + protected void handleEditorInputChanged() { + + String title; + String msg; + Shell shell= getSite().getShell(); + + IDocumentProvider provider= getDocumentProvider(); + if (provider == null) { + // fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=15066 + close(false); + return; + } + + IEditorInput input= getEditorInput(); + if (provider.isDeleted(input)) { + + if (isSaveAsAllowed()) { + + title= EditorMessages.getString("Editor.error.activated.deleted.save.title"); //$NON-NLS-1$ + msg= EditorMessages.getString("Editor.error.activated.deleted.save.message"); //$NON-NLS-1$ + + String[] buttons= { + EditorMessages.getString("Editor.error.activated.deleted.save.button.save"), //$NON-NLS-1$ + EditorMessages.getString("Editor.error.activated.deleted.save.button.close"), //$NON-NLS-1$ + }; + + MessageDialog dialog= new MessageDialog(shell, title, null, msg, MessageDialog.QUESTION, buttons, 0); + + if (dialog.open() == 0) { + NullProgressMonitor pm= new NullProgressMonitor(); + performSaveAs(pm); + if (pm.isCanceled()) + handleEditorInputChanged(); + } else { + close(false); + } + + } else { + + title= EditorMessages.getString("Editor.error.activated.deleted.close.title"); //$NON-NLS-1$ + msg= EditorMessages.getString("Editor.error.activated.deleted.close.message"); //$NON-NLS-1$ + if (MessageDialog.openConfirm(shell, title, msg)) + close(false); + } + + } else { + + title= EditorMessages.getString("Editor.error.activated.outofsync.title"); //$NON-NLS-1$ + msg= EditorMessages.getString("Editor.error.activated.outofsync.message"); //$NON-NLS-1$ + + if (MessageDialog.openQuestion(shell, title, msg)) { + try { + + if (provider instanceof IDocumentProviderExtension) { + IDocumentProviderExtension extension= (IDocumentProviderExtension) provider; + extension.synchronize(input); + } else { + doSetInput(input); + } + + + } catch (CoreException x) { + title= EditorMessages.getString("Editor.error.refresh.outofsync.title"); //$NON-NLS-1$ + msg= EditorMessages.getString("Editor.error.refresh.outofsync.message"); //$NON-NLS-1$ + ErrorDialog.openError(shell, title, msg, x.getStatus()); + } + } + +// // disabled because of http://bugs.eclipse.org/bugs/show_bug.cgi?id=15166 +// else { +// markEditorAsDirty(); +// } + + } + } + + /** + * Marks this editor and its editor input as dirty. + * @since 2.0 + */ + private void markEditorAsDirty() { + + if (isDirty()) + return; + + IDocumentProvider provider= getDocumentProvider(); + if (provider instanceof IDocumentProviderExtension) { + + provider.removeElementStateListener(fElementStateListener); + try { + + IDocumentProviderExtension extension= (IDocumentProviderExtension) provider; + extension.setCanSaveDocument(getEditorInput()); + firePropertyChange(PROP_DIRTY); + + } finally { + provider.addElementStateListener(fElementStateListener); + } + + } + } + + /** + * The <code>AbstractTextEditor</code> implementation of this + * <code>IEditorPart</code> method calls <code>oerformSaveAs</code>. + * Subclasses may reimplement. + */ + public void doSaveAs() { + /* + * 1GEUSSR: ITPUI:ALL - User should never loose changes made in the editors. + * Changed Behavior to make sure that if called inside a regular save (because + * of deletion of input element) there is a way to report back to the caller. + */ + performSaveAs(new NullProgressMonitor()); + } + + /** + * Performs a save as and reports the result state back to the + * given progress monitor. This default implementation does nothing. + * Subclasses may reimplement. + * + * @param progressMonitor the progress monitor for communicating result state or <code>null</code> + */ + protected void performSaveAs(IProgressMonitor progressMonitor) { + } + + /** + * The <code>AbstractTextEditor</code> implementation of this + * <code>IEditorPart</code> method may be extended by subclasses. + */ + public void doSave(IProgressMonitor progressMonitor) { + + IDocumentProvider p= getDocumentProvider(); + if (p == null) + return; + + if (p.isDeleted(getEditorInput())) { + + if (isSaveAsAllowed()) { + + /* + * 1GEUSSR: ITPUI:ALL - User should never loose changes made in the editors. + * Changed Behavior to make sure that if called inside a regular save (because + * of deletion of input element) there is a way to report back to the caller. + */ + performSaveAs(progressMonitor); + + } else { + + Shell shell= getSite().getShell(); + String title= EditorMessages.getString("Editor.error.save.deleted.title"); //$NON-NLS-1$ + String msg= EditorMessages.getString("Editor.error.save.deleted.message"); //$NON-NLS-1$ + MessageDialog.openError(shell, title, msg); + } + + } else { + + performSaveOperation(createSaveOperation(false), progressMonitor); + } + } + + /** + * Enables/Disabled sanity checking. + * @param enable <code>true</code> if santity checking should be enabled, <code>false</code> otherwise + * @since 2.0 + */ + protected void enableSanityChecking(boolean enable) { + synchronized (this) { + fIsSanityCheckEnabled= enable; + } + } + + /** + * Checks the state of the given editor input if sanity checking is enabled. + * @param input the editor input whose state is to be checked + * @since 2.0 + */ + protected void safelySanityCheckState(IEditorInput input) { + boolean enabled= false; + + synchronized (this) { + enabled= fIsSanityCheckEnabled; + } + + if (enabled) + sanityCheckState(input); + } + + /** + * Checks the state of the given editor input. + * @param input the editor input whose state is to be checked + * @since 2.0 + */ + protected void sanityCheckState(IEditorInput input) { + + IDocumentProvider p= getDocumentProvider(); + if (p == null) + return; + + if (fModificationStamp == -1) + fModificationStamp= p.getSynchronizationStamp(input); + + long stamp= p.getModificationStamp(input); + if (stamp != fModificationStamp) { + fModificationStamp= stamp; + if (stamp != p.getSynchronizationStamp(input)) + handleEditorInputChanged(); + } + + updateState(getEditorInput()); + updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE); + } + + /** + * Validates the state of the given editor input. The predominate intent + * of this method is to take any action propably necessary to ensure that + * the input can persistently be changed. + * + * @param input the input to be validated + * @since 2.0 + */ + protected void validateState(IEditorInput input) { + IDocumentProvider provider= getDocumentProvider(); + if (provider instanceof IDocumentProviderExtension) { + IDocumentProviderExtension extension= (IDocumentProviderExtension) provider; + try { + + boolean wasReadOnly= isEditorInputReadOnly(); + + extension.validateState(input, getSite().getShell()); + + if (fSourceViewer != null) + fSourceViewer.setEditable(isEditable()); + + if (wasReadOnly != isEditorInputReadOnly()) + updateStateDependentActions(); + + } catch (CoreException x) { + ILog log= Platform.getPlugin(PlatformUI.PLUGIN_ID).getLog(); + log.log(x.getStatus()); + } + } + } + + /** + * Updates the state of the given editor input such as read-only flag etc. + * + * @param input the input to be validated + * @since 2.0 + */ + protected void updateState(IEditorInput input) { + IDocumentProvider provider= getDocumentProvider(); + if (provider instanceof IDocumentProviderExtension) { + IDocumentProviderExtension extension= (IDocumentProviderExtension) provider; + try { + + boolean wasReadOnly= isEditorInputReadOnly(); + extension.updateStateCache(input); + + if (fSourceViewer != null) + fSourceViewer.setEditable(isEditable()); + + if (wasReadOnly != isEditorInputReadOnly()) + updateStateDependentActions(); + + } catch (CoreException x) { + ILog log= Platform.getPlugin(PlatformUI.PLUGIN_ID).getLog(); + log.log(x.getStatus()); + } + } + } + + /** + * Creates a workspace modify operation which saves the content of the editor + * to the editor's input element. <code>overwrite</code> indicates whether + * the editor input element may be overwritten if necessary. Clients may + * reimplement this method. + * + * @param overwrite indicates whether or not overwrititng is allowed + * @return the save operation + */ + protected WorkspaceModifyOperation createSaveOperation(final boolean overwrite) { + return new WorkspaceModifyOperation() { + public void execute(final IProgressMonitor monitor) throws CoreException { + IEditorInput input= getEditorInput(); + getDocumentProvider().saveDocument(monitor, input, getDocumentProvider().getDocument(input), overwrite); + } + }; + } + + /** + * Performs the given save operation and handles errors appropriatly. + * + * @param operation the operation to be performed + * @param progressMonitor the monitor in which to run the operation + */ + protected void performSaveOperation(WorkspaceModifyOperation operation, IProgressMonitor progressMonitor) { + + IDocumentProvider provider= getDocumentProvider(); + + try { + + provider.aboutToChange(getEditorInput()); + operation.run(progressMonitor); + editorSaved(); + + } catch (InterruptedException x) { + } catch (InvocationTargetException x) { + + Throwable t= x.getTargetException(); + if (t instanceof CoreException) + handleExceptionOnSave((CoreException) t, progressMonitor); + else { + Shell shell= getSite().getShell(); + String title= EditorMessages.getString("Editor.error.save.title"); //$NON-NLS-1$ + String msg= EditorMessages.getString("Editor.error.save.message"); //$NON-NLS-1$ + MessageDialog.openError(shell, title, msg + t.getMessage()); + } + + } finally { + provider.changed(getEditorInput()); + } + } + + /** + * Handles the given exception. If the exception reports an out-of-sync + * situation, this is reported to the user. Otherwise, the exception + * is generically reported. + * + * @param exception the exception to handle + * @param progressMonitor the progress monitor + */ + protected void handleExceptionOnSave(CoreException exception, IProgressMonitor progressMonitor) { + + try { + ++ fErrorCorrectionOnSave; + + Shell shell= getSite().getShell(); + + IDocumentProvider p= getDocumentProvider(); + long modifiedStamp= p.getModificationStamp(getEditorInput()); + long synchStamp= p.getSynchronizationStamp(getEditorInput()); + + if (fErrorCorrectionOnSave == 1 && modifiedStamp != synchStamp) { + + String title= EditorMessages.getString("Editor.error.save.outofsync.title"); //$NON-NLS-1$ + String msg= EditorMessages.getString("Editor.error.save.outofsync.message"); //$NON-NLS-1$ + + if (MessageDialog.openQuestion(shell, title, msg)) + performSaveOperation(createSaveOperation(true), progressMonitor); + else { + /* + * 1GEUPKR: ITPJUI:ALL - Loosing work with simultaneous edits + * Set progress monitor to canceled in order to report back + * to enclosing operations. + */ + if (progressMonitor != null) + progressMonitor.setCanceled(true); + } + } else { + + String title= EditorMessages.getString("Editor.error.save.title"); //$NON-NLS-1$ + String msg= EditorMessages.getString("Editor.error.save.message"); //$NON-NLS-1$ + ErrorDialog.openError(shell, title, msg, exception.getStatus()); + + /* + * 1GEUPKR: ITPJUI:ALL - Loosing work with simultaneous edits + * Set progress monitor to canceled in order to report back + * to enclosing operations. + */ + if (progressMonitor != null) + progressMonitor.setCanceled(true); + } + + } finally { + -- fErrorCorrectionOnSave; + } + } + + /** + * The <code>AbstractTextEditor</code> implementation of this + * <code>IEditorPart</code> method returns <code>false</code>. Subclasses + * may override. + */ + public boolean isSaveAsAllowed() { + return false; + } + + /* + * @see IEditorPart#isSaveOnCloseNeeded() + */ + public boolean isSaveOnCloseNeeded() { + IDocumentProvider p= getDocumentProvider(); + return p == null ? false : p.mustSaveDocument(getEditorInput()); + } + + /* + * @see IEditorPart#isDirty + */ + public boolean isDirty() { + IDocumentProvider p= getDocumentProvider(); + return p == null ? false : p.canSaveDocument(getEditorInput()); + } + + /** + * The <code>AbstractTextEditor</code> implementation of this + * <code>ITextEditor</code> method may be extended by subclasses. + */ + public void doRevertToSaved() { + + IDocumentProvider p= getDocumentProvider(); + if (p == null) + return; + + try { + + p.resetDocument(getEditorInput()); + + IAnnotationModel model= p.getAnnotationModel(getEditorInput()); + if (model instanceof AbstractMarkerAnnotationModel) { + AbstractMarkerAnnotationModel markerModel= (AbstractMarkerAnnotationModel) model; + markerModel.resetMarkers(); + } + + firePropertyChange(PROP_DIRTY); + + } catch (CoreException x) { + String title= EditorMessages.getString("Editor.error.revert.title"); //$NON-NLS-1$ + String msg= EditorMessages.getString("Editor.error.revert.message"); //$NON-NLS-1$ + Shell shell= getSite().getShell(); + ErrorDialog.openError(shell, title, msg, x.getStatus()); + } + } + + /* + * @see ITextEditor#setAction + */ + public void setAction(String actionID, IAction action) { + Assert.isNotNull(actionID); + if (action == null) { + action= (IAction) fActions.remove(actionID); + if (action != null) + fActivationCodeTrigger.unregisterActionFromKeyActivation(action); + } else { + fActions.put(actionID, action); + fActivationCodeTrigger.registerActionForKeyActivation(action); + } + } + + /* + * @see ITextEditor#setActionActivationCode(String, char, int, int) + */ + public void setActionActivationCode(String actionID, char activationCharacter, int activationKeyCode, int activationStateMask) { + + Assert.isNotNull(actionID); + + ActionActivationCode found= findActionActivationCode(actionID); + if (found == null) { + found= new ActionActivationCode(actionID); + fActivationCodes.add(found); + } + + found.fCharacter= activationCharacter; + found.fKeyCode= activationKeyCode; + found.fStateMask= activationStateMask; + } + + /** + * Returns the activation code registered for the specified action. + * + * @param actionID the action id + * @return the registered activation code or <code>null</code> if no code has been installed + */ + private ActionActivationCode findActionActivationCode(String actionID) { + int size= fActivationCodes.size(); + for (int i= 0; i < size; i++) { + ActionActivationCode code= (ActionActivationCode) fActivationCodes.get(i); + if (actionID.equals(code.fActionId)) + return code; + } + return null; + } + + /* + * @see ITextEditor#removeActionActivationCode(String) + */ + public void removeActionActivationCode(String actionID) { + Assert.isNotNull(actionID); + ActionActivationCode code= findActionActivationCode(actionID); + if (code != null) + fActivationCodes.remove(code); + } + + /* + * @see ITextEditor#getAction + */ + public IAction getAction(String actionID) { + Assert.isNotNull(actionID); + IAction action= (IAction) fActions.get(actionID); + + if (action == null) { + action= findContributedAction(actionID); + if (action != null) + setAction(actionID, action); + } + + return action; + } + + /** + * Returns the action with the given action id that has been contributed via xml to this editor. + * The lookup honors the dependencies of plugins. + * + * @param actionID the action id to look up + * @return the action that has been contributed + * @since 2.0 + */ + private IAction findContributedAction(String actionID) { + IExtensionPoint extensionPoint= Platform.getPluginRegistry().getExtensionPoint(PlatformUI.PLUGIN_ID, "editorActions"); //$NON-NLS-1$ + if (extensionPoint != null) { + IConfigurationElement[] elements= extensionPoint.getConfigurationElements(); + + List actions= new ArrayList(); + for (int i= 0; i < elements.length; i++) { + IConfigurationElement element= elements[i]; + if (TAG_CONTRIBUTION_TYPE.equals(element.getName())) { + if (!getSite().getId().equals(element.getAttribute("targetID"))) //$NON-NLS-1$ + continue; + + IConfigurationElement[] children= element.getChildren("action"); //$NON-NLS-1$ + for (int j= 0; j < children.length; j++) { + IConfigurationElement child= children[j]; + if (actionID.equals(child.getAttribute("actionID"))) //$NON-NLS-1$ + actions.add(child); + } + } + } + Collections.sort(actions, new ConfigurationElementComparator()); + + if (actions.size() != 0) { + IConfigurationElement element= (IConfigurationElement) actions.get(0); + return new EditorPluginAction(element, "class", this); //$NON-NLS-1$ + } + } + + return null; + } + + /** + * Updates the specified action by calling <code>IUpdate.update</code> + * if applicable. + * + * @param actionId the action id + */ + private void updateAction(String actionId) { + Assert.isNotNull(actionId); + if (fActions != null) { + IAction action= (IAction) fActions.get(actionId); + if (action instanceof IUpdate) + ((IUpdate) action).update(); + } + } + + /** + * Marks or unmarks the given action to be updated on text selection changes. + * + * @param actionId the action id + * @param mark <code>true</code> if the action is selection dependent + */ + public void markAsSelectionDependentAction(String actionId, boolean mark) { + Assert.isNotNull(actionId); + if (mark) { + if (!fSelectionActions.contains(actionId)) + fSelectionActions.add(actionId); + } else + fSelectionActions.remove(actionId); + } + + /** + * Marks or unmarks the given action to be updated on content changes. + * + * @param actionId the action id + * @param mark <code>true</code> if the action is content dependent + */ + public void markAsContentDependentAction(String actionId, boolean mark) { + Assert.isNotNull(actionId); + if (mark) { + if (!fContentActions.contains(actionId)) + fContentActions.add(actionId); + } else + fContentActions.remove(actionId); + } + + /** + * Marks or unmarks the given action to be updated on property changes. + * + * @param actionId the action id + * @param mark <code>true</code> if the action is property dependent + * @since 2.0 + */ + public void markAsPropertyDependentAction(String actionId, boolean mark) { + Assert.isNotNull(actionId); + if (mark) { + if (!fPropertyActions.contains(actionId)) + fPropertyActions.add(actionId); + } else + fPropertyActions.remove(actionId); + } + + /** + * Marks or unmarks the given action to be updated on state changes. + * + * @param actionId the action id + * @param mark <code>true</code> if the action is state dependent + * @since 2.0 + */ + public void markAsStateDependentAction(String actionId, boolean mark) { + Assert.isNotNull(actionId); + if (mark) { + if (!fStateActions.contains(actionId)) + fStateActions.add(actionId); + } else + fStateActions.remove(actionId); + } + + /** + * Updates all selection dependent actions. + */ + protected void updateSelectionDependentActions() { + if (fSelectionActions != null) { + Iterator e= fSelectionActions.iterator(); + while (e.hasNext()) + updateAction((String) e.next()); + } + } + + /** + * Updates all content dependent actions. + */ + protected void updateContentDependentActions() { + if (fContentActions != null) { + Iterator e= fContentActions.iterator(); + while (e.hasNext()) + updateAction((String) e.next()); + } + } + + /** + * Updates all property dependent actions. + * @since 2.0 + */ + protected void updatePropertyDependentActions() { + if (fPropertyActions != null) { + Iterator e= fPropertyActions.iterator(); + while (e.hasNext()) + updateAction((String) e.next()); + } + } + + /** + * Updates all state dependent actions. + * @since 2.0 + */ + protected void updateStateDependentActions() { + if (fStateActions != null) { + Iterator e= fStateActions.iterator(); + while (e.hasNext()) + updateAction((String) e.next()); + } + } + + /** + * Creates this editor's standard navigation actions. + * <p> + * Subclasses may extend. + * </p> + * @since 2.0 + */ + protected void createNavigationActions() { + + IAction action; + + StyledText textWidget= getSourceViewer().getTextWidget(); + for (int i= 0; i < ACTION_MAP.length; i++) { + IdMapEntry entry= (IdMapEntry) ACTION_MAP[i]; + action= new TextNavigationAction(textWidget, entry.getAction()); + action.setActionDefinitionId(entry.getActionId()); + setAction(entry.getActionId(), action); + } + + action= new ToggleInsertModeAction(textWidget); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.TOGGLE_OVERWRITE); + setAction(ITextEditorActionDefinitionIds.TOGGLE_OVERWRITE, action); + + action= new ScrollLinesAction(-1); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.SCROLL_LINE_UP); + setAction(ITextEditorActionDefinitionIds.SCROLL_LINE_UP, action); + + action= new ScrollLinesAction(1); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.SCROLL_LINE_DOWN); + setAction(ITextEditorActionDefinitionIds.SCROLL_LINE_DOWN, action); + } + + /** + * Creates this editor's accessibility actions. + * @since 2.0 + */ + private void createAccessibilityActions() { + IAction action= new ShowRulerContextMenuAction(); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_RULER_CONTEXT_MENU); + setAction(ITextEditorActionDefinitionIds.SHOW_RULER_CONTEXT_MENU, action); + } + + /** + * Creates this editor's standard actions and connects them with the global + * workbench actions. + * <p> + * Subclasses may extend. + * </p> + */ + protected void createActions() { + + ResourceAction action; + + action= new TextOperationAction(EditorMessages.getResourceBundle(), "Editor.Undo.", this, ITextOperationTarget.UNDO); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.UNDO_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.UNDO); + setAction(ITextEditorActionConstants.UNDO, action); + + action= new TextOperationAction(EditorMessages.getResourceBundle(), "Editor.Redo.", this, ITextOperationTarget.REDO); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.REDO_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.REDO); + setAction(ITextEditorActionConstants.REDO, action); + + action= new TextOperationAction(EditorMessages.getResourceBundle(), "Editor.Cut.", this, ITextOperationTarget.CUT); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.CUT); + setAction(ITextEditorActionConstants.CUT, action); + + action= new TextOperationAction(EditorMessages.getResourceBundle(), "Editor.Copy.", this, ITextOperationTarget.COPY, true); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.COPY); + setAction(ITextEditorActionConstants.COPY, action); + + action= new TextOperationAction(EditorMessages.getResourceBundle(), "Editor.Paste.", this, ITextOperationTarget.PASTE); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.PASTE_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.PASTE); + setAction(ITextEditorActionConstants.PASTE, action); + + action= new TextOperationAction(EditorMessages.getResourceBundle(), "Editor.Delete.", this, ITextOperationTarget.DELETE); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE); + setAction(ITextEditorActionConstants.DELETE, action); + + action= new DeleteLineAction(EditorMessages.getResourceBundle(), "Editor.DeleteLine.", this, DeleteLineAction.WHOLE); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_LINE_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_LINE); + setAction(ITextEditorActionConstants.DELETE_LINE, action); + + action= new DeleteLineAction(EditorMessages.getResourceBundle(), "Editor.DeleteLineToBeginning.", this, DeleteLineAction.TO_BEGINNING); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_LINE_TO_BEGINNING_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_LINE_TO_BEGINNING); + setAction(ITextEditorActionConstants.DELETE_LINE_TO_BEGINNING, action); + + action= new DeleteLineAction(EditorMessages.getResourceBundle(), "Editor.DeleteLineToEnd.", this, DeleteLineAction.TO_END); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_LINE_TO_END_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_LINE_TO_END); + setAction(ITextEditorActionConstants.DELETE_LINE_TO_END, action); + + action= new MarkAction(EditorMessages.getResourceBundle(), "Editor.SetMark.", this, MarkAction.SET_MARK); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.SET_MARK_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.SET_MARK); + setAction(ITextEditorActionConstants.SET_MARK, action); + + action= new MarkAction(EditorMessages.getResourceBundle(), "Editor.ClearMark.", this, MarkAction.CLEAR_MARK); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.CLEAR_MARK_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.CLEAR_MARK); + setAction(ITextEditorActionConstants.CLEAR_MARK, action); + + action= new MarkAction(EditorMessages.getResourceBundle(), "Editor.SwapMark.", this, MarkAction.SWAP_MARK); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.SWAP_MARK_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.SWAP_MARK); + setAction(ITextEditorActionConstants.SWAP_MARK, action); + + action= new TextOperationAction(EditorMessages.getResourceBundle(), "Editor.SelectAll.", this, ITextOperationTarget.SELECT_ALL, true); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.SELECT_ALL_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_ALL); + setAction(ITextEditorActionConstants.SELECT_ALL, action); + + action= new ShiftAction(EditorMessages.getResourceBundle(), "Editor.ShiftRight.", this, ITextOperationTarget.SHIFT_RIGHT); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.SHIFT_RIGHT_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHIFT_RIGHT); + setAction(ITextEditorActionConstants.SHIFT_RIGHT, action); + + action= new ShiftAction(EditorMessages.getResourceBundle(), "Editor.ShiftLeft.", this, ITextOperationTarget.SHIFT_LEFT); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.SHIFT_LEFT_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHIFT_LEFT); + setAction(ITextEditorActionConstants.SHIFT_LEFT, action); + + action= new TextOperationAction(EditorMessages.getResourceBundle(), "Editor.Print.", this, ITextOperationTarget.PRINT, true); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.PRINT_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.PRINT); + setAction(ITextEditorActionConstants.PRINT, action); + + action= new FindReplaceAction(EditorMessages.getResourceBundle(), "Editor.FindReplace.", this); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.FIND_REPLACE); + setAction(ITextEditorActionConstants.FIND, action); + + action= new FindNextAction(EditorMessages.getResourceBundle(), "Editor.FindNext.", this, true); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_NEXT_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.FIND_NEXT); + setAction(ITextEditorActionConstants.FIND_NEXT, action); + + action= new FindNextAction(EditorMessages.getResourceBundle(), "Editor.FindPrevious.", this, false); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_PREVIOUS_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.FIND_PREVIOUS); + setAction(ITextEditorActionConstants.FIND_PREVIOUS, action); + + action= new IncrementalFindAction(EditorMessages.getResourceBundle(), "Editor.FindIncremental.", this); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_INCREMENTAL_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.FIND_INCREMENTAL); + setAction(ITextEditorActionConstants.FIND_INCREMENTAL, action); + + action= new AddMarkerAction(EditorMessages.getResourceBundle(), "Editor.AddBookmark.", this, IMarker.BOOKMARK, true); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.BOOKMARK_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.ADD_BOOKMARK); + setAction(ITextEditorActionConstants.BOOKMARK, action); + +// FIXME: need another way to contribute this action +// action= new AddTaskAction(EditorMessages.getResourceBundle(), "Editor.AddTask.", this); //$NON-NLS-1$ +// action.setHelpContextId(IAbstractTextEditorHelpContextIds.ADD_TASK_ACTION); +// action.setActionDefinitionId(ITextEditorActionDefinitionIds.ADD_TASK); +// setAction(ITextEditorActionConstants.ADD_TASK, action); + + action= new SaveAction(EditorMessages.getResourceBundle(), "Editor.Save.", this); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.SAVE_ACTION); + // action.setActionDefinitionId(ITextEditorActionDefinitionIds.SAVE); + setAction(ITextEditorActionConstants.SAVE, action); + + action= new RevertToSavedAction(EditorMessages.getResourceBundle(), "Editor.Revert.", this); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.REVERT_TO_SAVED_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.REVERT_TO_SAVED); + setAction(ITextEditorActionConstants.REVERT_TO_SAVED, action); + + action= new GotoLineAction(EditorMessages.getResourceBundle(), "Editor.GotoLine.", this); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.GOTO_LINE_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_GOTO); + setAction(ITextEditorActionConstants.GOTO_LINE, action); + + markAsContentDependentAction(ITextEditorActionConstants.UNDO, true); + markAsContentDependentAction(ITextEditorActionConstants.REDO, true); + markAsContentDependentAction(ITextEditorActionConstants.FIND, true); + markAsContentDependentAction(ITextEditorActionConstants.FIND_NEXT, true); + markAsContentDependentAction(ITextEditorActionConstants.FIND_PREVIOUS, true); + markAsContentDependentAction(ITextEditorActionConstants.FIND_INCREMENTAL, true); + + markAsSelectionDependentAction(ITextEditorActionConstants.CUT, true); + markAsSelectionDependentAction(ITextEditorActionConstants.COPY, true); + markAsSelectionDependentAction(ITextEditorActionConstants.PASTE, true); + markAsSelectionDependentAction(ITextEditorActionConstants.DELETE, true); + markAsSelectionDependentAction(ITextEditorActionConstants.SHIFT_RIGHT, true); + markAsSelectionDependentAction(ITextEditorActionConstants.SHIFT_LEFT, true); + + markAsPropertyDependentAction(ITextEditorActionConstants.REVERT_TO_SAVED, true); + + markAsStateDependentAction(ITextEditorActionConstants.UNDO, true); + markAsStateDependentAction(ITextEditorActionConstants.REDO, true); + markAsStateDependentAction(ITextEditorActionConstants.CUT, true); + markAsStateDependentAction(ITextEditorActionConstants.PASTE, true); + markAsStateDependentAction(ITextEditorActionConstants.DELETE, true); + markAsStateDependentAction(ITextEditorActionConstants.SHIFT_RIGHT, true); + markAsStateDependentAction(ITextEditorActionConstants.SHIFT_LEFT, true); + markAsStateDependentAction(ITextEditorActionConstants.FIND, true); + markAsStateDependentAction(ITextEditorActionConstants.DELETE_LINE, true); + markAsStateDependentAction(ITextEditorActionConstants.DELETE_LINE_TO_BEGINNING, true); + markAsStateDependentAction(ITextEditorActionConstants.DELETE_LINE_TO_END, true); + + setActionActivationCode(ITextEditorActionConstants.SHIFT_RIGHT,'\t', -1, SWT.NONE); + setActionActivationCode(ITextEditorActionConstants.SHIFT_LEFT, '\t', -1, SWT.SHIFT); + } + + /** + * Convenience method to add the action installed under the given action id to the given menu. + * @param menu the menu to add the action to + * @param actionId the id of the action to be added + */ + protected final void addAction(IMenuManager menu, String actionId) { + IAction action= getAction(actionId); + if (action != null) { + if (action instanceof IUpdate) + ((IUpdate) action).update(); + menu.add(action); + } + } + + /** + * Convenience method to add the action installed under the given action id to the specified group of the menu. + * @param menu the menu to add the action to + * @param group the group in the menu + * @param actionId the id of the action to add + */ + protected final void addAction(IMenuManager menu, String group, String actionId) { + IAction action= getAction(actionId); + if (action != null) { + if (action instanceof IUpdate) + ((IUpdate) action).update(); + + IMenuManager subMenu= menu.findMenuUsingPath(group); + if (subMenu != null) + subMenu.add(action); + else + menu.appendToGroup(group, action); + } + } + + /** + * Convenience method to add a new group after the specified group. + * @param menu the menu to add the new group to + * @param existingGroup the group after which to insert the new group + * @param newGroup the new group + */ + protected final void addGroup(IMenuManager menu, String existingGroup, String newGroup) { + IMenuManager subMenu= menu.findMenuUsingPath(existingGroup); + if (subMenu != null) + subMenu.add(new Separator(newGroup)); + else + menu.appendToGroup(existingGroup, new Separator(newGroup)); + } + + /** + * Sets up the ruler context menu before it is made visible. + * <p> + * Subclasses may extend to add other actions. + * </p> + * + * @param menu the menu + */ + protected void rulerContextMenuAboutToShow(IMenuManager menu) { + + for (Iterator i = fRulerContextMenuListeners.iterator(); i.hasNext();) + ((IMenuListener) i.next()).menuAboutToShow(menu); + + addAction(menu, ITextEditorActionConstants.RULER_MANAGE_BOOKMARKS); + addAction(menu, ITextEditorActionConstants.RULER_MANAGE_TASKS); + + menu.add(new Separator(ITextEditorActionConstants.GROUP_REST)); + menu.add(new Separator(ITextEditorActionConstants.MB_ADDITIONS)); + } + + /** + * Sets up this editor's context menu before it is made visible. + * <p> + * Subclasses may extend to add other actions. + * </p> + * + * @param menu the menu + */ + protected void editorContextMenuAboutToShow(IMenuManager menu) { + + menu.add(new Separator(ITextEditorActionConstants.GROUP_UNDO)); + menu.add(new Separator(ITextEditorActionConstants.GROUP_COPY)); + menu.add(new Separator(ITextEditorActionConstants.GROUP_PRINT)); + menu.add(new Separator(ITextEditorActionConstants.GROUP_EDIT)); + menu.add(new Separator(ITextEditorActionConstants.GROUP_FIND)); + menu.add(new Separator(ITextEditorActionConstants.GROUP_ADD)); + menu.add(new Separator(ITextEditorActionConstants.GROUP_REST)); + menu.add(new Separator(ITextEditorActionConstants.MB_ADDITIONS)); + menu.add(new Separator(ITextEditorActionConstants.GROUP_SAVE)); + + if (isEditable()) { + addAction(menu, ITextEditorActionConstants.GROUP_UNDO, ITextEditorActionConstants.UNDO); + addAction(menu, ITextEditorActionConstants.GROUP_UNDO, ITextEditorActionConstants.REVERT_TO_SAVED); + addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.CUT); + addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.COPY); + addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.PASTE); + addAction(menu, ITextEditorActionConstants.GROUP_SAVE, ITextEditorActionConstants.SAVE); + } else { + addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.COPY); + } + } + + /** + * Returns the status line manager of this editor. + * @return the status line manager of this editor + * @since 2.0 + */ + private IStatusLineManager getStatusLineManager() { + + IEditorActionBarContributor contributor= getEditorSite().getActionBarContributor(); + if (!(contributor instanceof EditorActionBarContributor)) + return null; + + IActionBars actionBars= ((EditorActionBarContributor) contributor).getActionBars(); + if (actionBars == null) + return null; + + return actionBars.getStatusLineManager(); + } + + /* + * @see IAdaptable#getAdapter(Class) + */ + public Object getAdapter(Class required) { + + if (IEditorStatusLine.class.equals(required)) { + if (fEditorStatusLine == null) { + IStatusLineManager statusLineManager= getStatusLineManager(); + ISelectionProvider selectionProvider= getSelectionProvider(); + if (statusLineManager != null && selectionProvider != null) + fEditorStatusLine= new EditorStatusLine(statusLineManager, selectionProvider); + } + return fEditorStatusLine; + } + + if (IVerticalRulerInfo.class.equals(required)) { + if (fVerticalRuler instanceof IVerticalRulerInfo) + return fVerticalRuler; + } + + if (IMarkRegionTarget.class.equals(required)) { + if (fMarkRegionTarget == null) { + IStatusLineManager manager= getStatusLineManager(); + if (manager != null) + fMarkRegionTarget= (fSourceViewer == null ? null : new MarkRegionTarget(fSourceViewer, manager)); + } + return fMarkRegionTarget; + } + + if (IncrementalFindTarget.class.equals(required)) { + if (fIncrementalFindTarget == null) { + IStatusLineManager manager= getStatusLineManager(); + if (manager != null) + fIncrementalFindTarget= (fSourceViewer == null ? null : new IncrementalFindTarget(fSourceViewer, manager)); + } + return fIncrementalFindTarget; + } + + if (IFindReplaceTarget.class.equals(required)) { + IFindReplaceTarget target= (fSourceViewer == null ? null : fSourceViewer.getFindReplaceTarget()); + if (target != null && target instanceof IFindReplaceTargetExtension) + ((IFindReplaceTargetExtension) target).setScopeHighlightColor(fFindScopeHighlightColor); + return target; + } + + if (ITextOperationTarget.class.equals(required)) + return (fSourceViewer == null ? null : fSourceViewer.getTextOperationTarget()); + + if (IRewriteTarget.class.equals(required)) { + if (fSourceViewer instanceof ITextViewerExtension) { + ITextViewerExtension extension= (ITextViewerExtension) fSourceViewer; + return extension.getRewriteTarget(); + } + return null; + } + + return super.getAdapter(required); + } + + /* + * @see IDesktopPart#setFocus() + */ + public void setFocus() { + if (fSourceViewer != null && fSourceViewer.getTextWidget() != null) + fSourceViewer.getTextWidget().setFocus(); + } + + /** + * If the editor can be saved all marker ranges have been changed according to + * the text manipulations. However, those changes are not yet propagated to the + * marker manager. Thus, when opening a marker, the marker's position in the editor + * must be determined as it might differ from the position stated in the marker. + * @param marker the marker to go to + * @see EditorPart#gotoMarker + */ + public void gotoMarker(IMarker marker) { + + if (fSourceViewer == null) + return; + + int start= MarkerUtilities.getCharStart(marker); + int end= MarkerUtilities.getCharEnd(marker); + + if (start < 0 || end < 0) { + + // there is only a line number + int line= MarkerUtilities.getLineNumber(marker); + if (line > -1) { + + // marker line numbers are 1-based + -- line; + + try { + + IDocument document= getDocumentProvider().getDocument(getEditorInput()); + selectAndReveal(document.getLineOffset(line), document.getLineLength(line)); + + } catch (BadLocationException x) { + // marker refers to invalid text position -> do nothing + } + } + + } else { + + // look up the current range of the marker when the document has been edited + IAnnotationModel model= getDocumentProvider().getAnnotationModel(getEditorInput()); + if (model instanceof AbstractMarkerAnnotationModel) { + + AbstractMarkerAnnotationModel markerModel= (AbstractMarkerAnnotationModel) model; + Position pos= markerModel.getMarkerPosition(marker); + if (pos == null || pos.isDeleted()) { + // do nothing if position has been deleted + return; + } + + start= pos.getOffset(); + end= pos.getOffset() + pos.getLength(); + } + + IDocument document= getDocumentProvider().getDocument(getEditorInput()); + int length= document.getLength(); + if (end - 1 < length && start < length) + selectAndReveal(start, end - start); + } + } + + /* + * @see ITextEditor#showsHighlightRangeOnly + */ + public boolean showsHighlightRangeOnly() { + return fShowHighlightRangeOnly; + } + + /* + * @see ITextEditor#showHighlightRangeOnly + */ + public void showHighlightRangeOnly(boolean showHighlightRangeOnly) { + fShowHighlightRangeOnly= showHighlightRangeOnly; + } + + /* + * @see ITextEditor#setHighlightRange + */ + public void setHighlightRange(int start, int length, boolean moveCursor) { + if (fSourceViewer == null) + return; + + if (fShowHighlightRangeOnly) { + if (moveCursor) { + IRegion visibleRegion= fSourceViewer.getVisibleRegion(); + if (start != visibleRegion.getOffset() || length != visibleRegion.getLength()) + fSourceViewer.setVisibleRegion(start, length); + } + } else { + IRegion rangeIndication= fSourceViewer.getRangeIndication(); + if (rangeIndication == null || start != rangeIndication.getOffset() || length != rangeIndication.getLength()) + fSourceViewer.setRangeIndication(start, length, moveCursor); + } + } + + /* + * @see ITextEditor#getHighlightRange + */ + public IRegion getHighlightRange() { + if (fSourceViewer == null) + return null; + + if (fShowHighlightRangeOnly) + return fSourceViewer.getVisibleRegion(); + + return fSourceViewer.getRangeIndication(); + } + + /* + * @see ITextEditor#resetHighlightRange + */ + public void resetHighlightRange() { + if (fSourceViewer == null) + return; + + if (fShowHighlightRangeOnly) + fSourceViewer.resetVisibleRegion(); + else + fSourceViewer.removeRangeIndication(); + } + + /** + * Adjusts the highlight range so that at least the specified range + * is highlighted. <p> + * Subclasses may re-implement this method. + * + * @param offset the offset of the range which at least should be highlighted + * @param length the length of the range which at least should be highlighted + */ + protected void adjustHighlightRange(int offset, int length) { + if (fSourceViewer == null) + return; + + if (!fSourceViewer.overlapsWithVisibleRegion(offset, length)) + fSourceViewer.resetVisibleRegion(); + } + + /* + * @see ITextEditor#selectAndReveal + */ + public void selectAndReveal(int start, int length) { + if (fSourceViewer == null) + return; + + ISelection selection= getSelectionProvider().getSelection(); + if (selection instanceof TextSelection) { + TextSelection textSelection= (TextSelection) selection; + if (textSelection.getOffset() != 0 || textSelection.getLength() != 0) + markInNavigationHistory(); + } + + StyledText widget= fSourceViewer.getTextWidget(); + widget.setRedraw(false); + { + adjustHighlightRange(start, length); + + fSourceViewer.revealRange(start, length); + fSourceViewer.setSelectedRange(start, length); + + markInNavigationHistory(); + } + widget.setRedraw(true); + } + + /* + * @see org.eclipse.ui.INavigationLocationProvider#createNavigationLocation() + * 2.1 - WORK_IN_PROGRESS do not use. + */ + public NavigationLocation createNavigationLocation() { + return new TextSelectionNavigationLocation(this); + } + + /** + * Writes a check mark of the given situation into the navigation history. + * 2.1 - WORK_IN_PROGRESS do not use. + */ + protected void markInNavigationHistory() { + IWorkbenchPage page= getEditorSite().getPage(); + page.addNavigationHistoryEntry(this, createNavigationLocation()); + } + + /** + * Subclasses may extend. + * 2.1 - WORK_IN_PROGRESS do not use. + */ + protected void editorSaved() { + IWorkbenchPage page= getEditorSite().getPage(); + NavigationLocation[] locations= page.getNavigationHistoryEntries(getEditorInput()); + for (int i= 0; i < locations.length; i++) { + if (locations[i] instanceof TextSelectionNavigationLocation) { + TextSelectionNavigationLocation location= (TextSelectionNavigationLocation) locations[i]; + location.partSaved(this); + } + } + } + + /* + * @see EditorPart#firePropertyChange + */ + protected void firePropertyChange(int property) { + super.firePropertyChange(property); + updatePropertyDependentActions(); + } + + /* + * @see ITextEditorExtension#setStatusField(IStatusField, String) + * @since 2.0 + */ + public void setStatusField(IStatusField field, String category) { + Assert.isNotNull(category); + if (field != null) { + + if (fStatusFields == null) + fStatusFields= new HashMap(3); + + fStatusFields.put(category, field); + updateStatusField(category); + + } else if (fStatusFields != null) + fStatusFields.remove(category); + } + + /** + * Returns the current status field for the given status category. + * + * @param category the status category + * @return the current status field for the given status category. + * @since 2.0 + */ + protected IStatusField getStatusField(String category) { + if (category != null && fStatusFields != null) + return (IStatusField) fStatusFields.get(category); + return null; + } + + /** + * Returns whether this editor is in overwrite or insert mode. + * + * @return <code>true</code> if in insert mode, <code>false</code> for overwrite mode + * @since 2.0 + */ + protected boolean isInInsertMode() { + return !fOverwriting; + } + + /** + * Handles a potential change of the cursor position. Subclasses may extend. + * @since 2.0 + */ + protected void handleCursorPositionChanged() { + updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION); + } + + /** + * Handles a change of the editor's insert mode. Subclasses may extend. + * @since 2.0 + */ + protected void handleInsertModeChanged() { + updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_MODE); + } + + /** + * Updates the status fields for the given category. + * @param category + * @since 2.0 + */ + protected void updateStatusField(String category) { + + if (category == null) + return; + + IStatusField field= getStatusField(category); + if (field != null) { + + String text= null; + + if (ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION.equals(category)) + text= getCursorPosition(); + else if (ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE.equals(category)) + text= isEditorInputReadOnly() ? fReadOnlyLabel : fWritableLabel; + else if (ITextEditorActionConstants.STATUS_CATEGORY_INPUT_MODE.equals(category)) + text= isInInsertMode() ? fInsertModeLabel : fOverwriteModeLabel; + + field.setText(text == null ? fErrorLabel : text); + } + } + + /** + * Updates all status fields. + * @since 2.0 + */ + protected void updateStatusFields() { + if (fStatusFields != null) { + Iterator e= fStatusFields.keySet().iterator(); + while (e.hasNext()) + updateStatusField((String) e.next()); + } + } + + /** + * Returns a description of the cursor position. + * @return a description of the cursor position + * @since 2.0 + */ + protected String getCursorPosition() { + + if (fSourceViewer == null) + return fErrorLabel; + + StyledText styledText= fSourceViewer.getTextWidget(); + + int offset= fSourceViewer.getVisibleRegion().getOffset(); + int caret= offset + styledText.getCaretOffset(); + IDocument document= fSourceViewer.getDocument(); + + if (document == null) + return fErrorLabel; + + try { + + int line= document.getLineOfOffset(caret); + + int lineOffset= document.getLineOffset(line); + int tabWidth= styledText.getTabs(); + int column= 0; + for (int i= lineOffset; i < caret; i++) + if ('\t' == document.getChar(i)) + column += tabWidth - (column % tabWidth); + else + column++; + + fLineLabel.fValue= line + 1; + fColumnLabel.fValue= column + 1; + return MessageFormat.format(fPositionLabelPattern, fPositionLabelPatternArguments); + + } catch (BadLocationException x) { + return fErrorLabel; + } + } + + /* + * @see ITextEditorExtension#isEditorInputReadOnly() + * @since 2.0 + */ + public boolean isEditorInputReadOnly() { + IDocumentProvider provider= getDocumentProvider(); + if (provider instanceof IDocumentProviderExtension) { + IDocumentProviderExtension extension= (IDocumentProviderExtension) provider; + return extension.isReadOnly(getEditorInput()); + } + return true; + } + + /* + * @see ITextEditorExtension#addRulerContextMenuListener(IMenuListener) + * @since 2.0 + */ + public void addRulerContextMenuListener(IMenuListener listener) { + fRulerContextMenuListeners.add(listener); + } + + /* + * @see ITextEditorExtension#removeRulerContextMenuListener(IMenuListener) + * @since 2.0 + */ + public void removeRulerContextMenuListener(IMenuListener listener) { + fRulerContextMenuListeners.remove(listener); + } + + /** + * Returns wether this editor can handle the move of the original element + * so that it ends up being the moved element. By default this method returns + * <code>true</code>. Subclasses may reimplement. + * @param originalElement the original element + * @param movedElement the moved element + * @since 2.0 + */ + protected boolean canHandleMove(IEditorInput originalElement, IEditorInput movedElement) { + return true; + } +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AddMarkerAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AddMarkerAction.java new file mode 100644 index 00000000000..b01473659df --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AddMarkerAction.java @@ -0,0 +1,279 @@ +package org.eclipse.ui.texteditor; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + + +import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.Platform;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IInputValidator; +import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.PlatformUI; + + + +/** + * Action for creating a marker of a specified type for the editor's + * input element based on the editor's selection. If required, the + * action asks the user to provide a marker label. The action is initially + * associated with a text editor via the constructor, but that can be + * subsequently changed using <code>setEditor</code>. + * <p> + * The following keys, prepended by the given option prefix, + * are used for retrieving resources from the given bundle: + * <ul> + * <li><code>"dialog.title"</code> - the input dialog's title</li> + * <li><code>"dialog.message"</code> - the input dialog's message</li> + * <li><code>"error.dialog.title"</code> - the error dialog's title</li> + * <li><code>"error.dialog.message"</code> - the error dialog's message</li> + * </ul> + * This class may be instantiated but is not intended for subclassing. + * </p> + */ +public class AddMarkerAction extends TextEditorAction { + + + /** The type for newly created markers */ + private String fMarkerType; + /** Should the user be asked for a label? */ + private boolean fAskForLabel; + /** The action's resource bundle */ + private ResourceBundle fBundle; + /** The prefix used for resource bundle lookup */ + private String fPrefix; + + + /** + * Creates a new action for the given text editor. The action configures its + * visual representation from the given resource bundle. + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys + * (described in <code>ResourceAction</code> constructor), or + * <code>null</code> if none + * @param editor the text editor + * @param markerType the type of marker to add + * @param askForLabel <code>true</code> if the user should be asked for + * a label for the new marker + * @see ResourceAction#ResourceAction + */ + public AddMarkerAction(ResourceBundle bundle, String prefix, ITextEditor textEditor, String markerType, boolean askForLabel) { + super(bundle, prefix, textEditor); + fBundle= bundle; + fPrefix= prefix; + fMarkerType= markerType; + fAskForLabel= askForLabel; + } + + /** + * Returns this action's resource bundle. + * + * @return this action's resource bundle + */ + protected ResourceBundle getResourceBundle() { + return fBundle; + } + + /** + * Returns this action's resource key prefix. + * + * @return this action's resource key prefix + */ + protected String getResourceKeyPrefix() { + return fPrefix; + } + + /* + * @see IAction#run() + */ + public void run() { + IResource resource= getResource(); + if (resource == null) + return; + Map attributes= getInitialAttributes(); + if (fAskForLabel) { + if (!askForLabel(attributes)) + return; + } + + try { + MarkerUtilities.createMarker(resource, attributes, fMarkerType); + } catch (CoreException x) { + + Platform.getPlugin(PlatformUI.PLUGIN_ID).getLog().log(x.getStatus()); + + Shell shell= getTextEditor().getSite().getShell(); + String title= getString(fBundle, fPrefix + "error.dialog.title", fPrefix + "error.dialog.title"); //$NON-NLS-2$ //$NON-NLS-1$ + String msg= getString(fBundle, fPrefix + "error.dialog.message", fPrefix + "error.dialog.message"); //$NON-NLS-2$ //$NON-NLS-1$ + + ErrorDialog.openError(shell, title, msg, x.getStatus()); + } + } + + /* + * @see TextEditorAction#update() + */ + public void update() { + setEnabled(getResource() != null); + } + + /** + * Asks the user for a marker label. Returns <code>true</code> if a label + * is entered, <code>false</code> if the user cancels the input dialog. + * The value for the attribute <code>message</code> is modified in the given + * attribute map. + * + * @param attributes the attributes map + * @return <code>true</code> if a label has been entered + */ + protected boolean askForLabel(Map attributes) { + + Object o= attributes.get("message"); //$NON-NLS-1$ + String proposal= (o instanceof String) ? (String) o : ""; //$NON-NLS-1$ + if (proposal == null) + proposal= ""; //$NON-NLS-1$ + + String title= getString(fBundle, fPrefix + "dialog.title", fPrefix + "dialog.title"); //$NON-NLS-2$ //$NON-NLS-1$ + String message= getString(fBundle, fPrefix + "dialog.message", fPrefix + "dialog.message"); //$NON-NLS-2$ //$NON-NLS-1$ + IInputValidator inputValidator = new IInputValidator() { + public String isValid(String newText) { + return (newText == null || newText.length() == 0) ? " " : null; //$NON-NLS-1$ + } + }; + InputDialog dialog= new InputDialog(getTextEditor().getSite().getShell(), title, message, proposal, inputValidator); + + String label= null; + if (dialog.open() != Window.CANCEL) + label= dialog.getValue(); + + if (label == null) + return false; + + label= label.trim(); + if (label.length() == 0) + return false; + + attributes.put("message", label); //$NON-NLS-1$ + return true; + } + + /** + * Returns the attributes the new marker will be initialized with. + * Subclasses may extend or replace this method. + * + * @return the attributes the new marker will be initialized with + */ + protected Map getInitialAttributes() { + + Map attributes= new HashMap(11); + + ITextSelection selection= (ITextSelection) getTextEditor().getSelectionProvider().getSelection(); + if (!selection.isEmpty()) { + + int start= selection.getOffset(); + int length= selection.getLength(); + + if (length < 0) { + length= -length; + start -= length; + } + + MarkerUtilities.setCharStart(attributes, start); + MarkerUtilities.setCharEnd(attributes, start + length); + + // marker line numbers are 1-based + int line= selection.getStartLine(); + MarkerUtilities.setLineNumber(attributes, line == -1 ? -1 : line + 1); + + IDocument document= getTextEditor().getDocumentProvider().getDocument(getTextEditor().getEditorInput()); + MarkerUtilities.setMessage(attributes, getLabelProposal(document, start, length)); + + } + + return attributes; + } + + /** + * Returns the initial label for the marker. + * + * @param document the document from which to extract a label proposal + * @param offset the document offset of the range from which to extract the label proposal + * @param length the length of the range from which to extract the label proposal + * @return the label proposal + */ + protected String getLabelProposal(IDocument document, int offset, int length) { + + + try { + + + if (length > 0) + return document.get(offset, length); + + + char ch; + + // Get the first white char before the selection. + int left= offset; + + int line= document.getLineOfOffset(offset); + int limit= document.getLineOffset(line); + + while (left > limit) { + ch= document.getChar(left); + if (Character.isWhitespace(ch)) + break; + --left; + } + + limit += document.getLineLength(line); + + // Now get the first letter. + while (left <= limit) { + ch= document.getChar(left); + if (!Character.isWhitespace(ch)) + break; + ++left; + } + + if (left > limit) + return null; + + // Get the next white char. + int right= (offset + length > limit ? limit : offset + length); + while (right < limit) { + ch= document.getChar(right); + if (Character.isWhitespace(ch)) + break; + ++right; + } + + // Trim the string and return it. + if (left != right) { + String label= document.get(left, right - left); + return label.trim(); + } + + } catch (BadLocationException x) { + // don't proposal label then + } + + return null; + } + + /** + * Returns the resource on which to create the marker, + * or <code>null</code> if there is no applicable resource. This + * queries the editor's input using <code>getAdapter(IResource.class)</code>. + * Subclasses may override this method. + * + * @return the resource to which to attach the newly created marker + */ + protected IResource getResource() { + ITextEditor editor= getTextEditor(); + if (editor != null) { + IEditorInput input= editor.getEditorInput(); + return (IResource) ((IAdaptable) input).getAdapter(IResource.class); + } + return null; + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/BasicMarkerUpdater.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/BasicMarkerUpdater.java new file mode 100644 index 00000000000..a21e707f4df --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/BasicMarkerUpdater.java @@ -0,0 +1,92 @@ +package org.eclipse.ui.texteditor; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.Position; + +import org.eclipse.core.resources.IMarker; + + +/** + * Updates a marker's positional attributes which are + * start position, end position, and line number. + */ +public final class BasicMarkerUpdater implements IMarkerUpdater { + + private final static String[] ATTRIBUTES= { + IMarker.CHAR_START, + IMarker.CHAR_END, + IMarker.LINE_NUMBER + }; + + /** + * Creates a new basic marker updater. + */ + public BasicMarkerUpdater() { + super(); + } + + /* + * @see IMarkerUpdater#getAttribute() + */ + public String[] getAttribute() { + return ATTRIBUTES; + } + + /* + * @see IMarkerUpdater#getMarkerType() + */ + public String getMarkerType() { + return null; + } + + /* + * @see IMarkerUpdater#updateMarker(IMarker, IDocument, Position) + */ + public boolean updateMarker(IMarker marker, IDocument document, Position position) { + + if (position == null) + return true; + + if (position.isDeleted()) + return false; + + boolean offsetsInitialized= false; + boolean offsetsChanged= false; + int markerStart= MarkerUtilities.getCharStart(marker); + int markerEnd= MarkerUtilities.getCharEnd(marker); + + if (markerStart != -1 && markerEnd != -1) { + + offsetsInitialized= true; + + int offset= position.getOffset(); + if (markerStart != offset) { + MarkerUtilities.setCharStart(marker, offset); + offsetsChanged= true; + } + + offset += position.getLength(); + if (markerEnd != offset) { + MarkerUtilities.setCharEnd(marker, offset); + offsetsChanged= true; + } + } + + if (!offsetsInitialized || (offsetsChanged && MarkerUtilities.getLineNumber(marker) != -1)) { + try { + // marker line numbers are 1-based + MarkerUtilities.setLineNumber(marker, document.getLineOfOffset(position.getOffset()) + 1); + } catch (BadLocationException x) { + } + } + + return true; + } +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/BasicTextEditorActionContributor.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/BasicTextEditorActionContributor.java new file mode 100644 index 00000000000..a544ae147b2 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/BasicTextEditorActionContributor.java @@ -0,0 +1,214 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionItem; +import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager; +
import org.eclipse.jface.action.IStatusLineManager; + +import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.part.EditorActionBarContributor; + + + +/** + * Manages the installation and deinstallation of global actions for + * the same type of editors. + * <p> + * If instantiated and used as-is, this contributor connects to all of the workbench defined + * global editor actions the corresponding actions of the current editor. It also adds addition + * actions for searching and navigation (go to line) as well as a set of status fields. + * <p> + * Subclasses may override the following methods: + * <ul> + * <li><code>contributeToMenu</code> - extend to contribute to menu</li> + * <li><code>contributeToToolBar</code> - reimplement to contribute to toolbar</li> + * <li><code>contributeToStatusLine</code> - reimplement to contribute to status line</li> + * <li><code>setActiveEditor</code> - extend to react to editor changes</li> + * </ul> + * </p> + * #see ITextEditorActionConstants + */ +public class BasicTextEditorActionContributor extends EditorActionBarContributor { + + /** The global actions to be connected with editor actions */ + private final static String[] ACTIONS= { + ITextEditorActionConstants.UNDO, + ITextEditorActionConstants.REDO, + ITextEditorActionConstants.CUT, + ITextEditorActionConstants.COPY, + ITextEditorActionConstants.PASTE, + ITextEditorActionConstants.DELETE, + ITextEditorActionConstants.SELECT_ALL, + ITextEditorActionConstants.FIND, + ITextEditorActionConstants.BOOKMARK, + ITextEditorActionConstants.ADD_TASK, + ITextEditorActionConstants.PRINT, + ITextEditorActionConstants.REVERT + }; + + /** + * The status fields to be set to the editor + * @since 2.0 + */ + private final static String[] STATUSFIELDS= { + ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE, + ITextEditorActionConstants.STATUS_CATEGORY_INPUT_MODE, + ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION + }; + + /** The active editor part */ + private IEditorPart fActiveEditorPart; + /** + * The find next action + * @since 2.0 + */ + private RetargetTextEditorAction fFindNext; + /** + * The find previous action + * @since 2.0 + */ + private RetargetTextEditorAction fFindPrevious; + /** + * The incremental find action + * @since 2.0 + */ + private RetargetTextEditorAction fIncrementalFind; + /** The go to line action */ + private RetargetTextEditorAction fGotoLine; + /** + * The map of status fields + * @since 2.0 + */ + private Map fStatusFields; + + + /** + * Creates an empty editor action bar contributor. The action bars are + * furnished later via the <code>init</code> method. + * + * @see org.eclipse.ui.IEditorActionBarContributor#init + */ + public BasicTextEditorActionContributor() { + + fFindNext= new RetargetTextEditorAction(EditorMessages.getResourceBundle(), "Editor.FindNext."); //$NON-NLS-1$ + fFindPrevious= new RetargetTextEditorAction(EditorMessages.getResourceBundle(), "Editor.FindPrevious."); //$NON-NLS-1$ + fIncrementalFind= new RetargetTextEditorAction(EditorMessages.getResourceBundle(), "Editor.FindIncremental."); //$NON-NLS-1$ + fGotoLine= new RetargetTextEditorAction(EditorMessages.getResourceBundle(), "Editor.GotoLine."); //$NON-NLS-1$ + + fStatusFields= new HashMap(3); + for (int i= 0; i < STATUSFIELDS.length; i++) + fStatusFields.put(STATUSFIELDS[i], new StatusLineContributionItem(STATUSFIELDS[i])); + } + + /** + * Returns the active editor part. + * + * @return the active editor part + */ + protected final IEditorPart getActiveEditorPart() { + return fActiveEditorPart; + } + + /** + * Returns the action registered with the given text editor. + * + * @param editor the editor, or <code>null</code> + * @param actionId the action id + * @return the action, or <code>null</code> if none + */ + protected final IAction getAction(ITextEditor editor, String actionId) { + return (editor == null ? null : editor.getAction(actionId)); + } + + /** + * The method installs the global action handlers for the given text editor. + * This method cannot be overridden by subclasses. + * @since 2.0 + */ + private void doSetActiveEditor(IEditorPart part) { + + if (fActiveEditorPart == part) + return; + + if (fActiveEditorPart instanceof ITextEditorExtension) { + ITextEditorExtension extension= (ITextEditorExtension) fActiveEditorPart; + for (int i= 0; i < STATUSFIELDS.length; i++) + extension.setStatusField(null, STATUSFIELDS[i]); + } + + fActiveEditorPart= part; + ITextEditor editor= (part instanceof ITextEditor) ? (ITextEditor) part : null; + + IActionBars actionBars= getActionBars(); + if (actionBars != null) { + for (int i= 0; i < ACTIONS.length; i++) + actionBars.setGlobalActionHandler(ACTIONS[i], getAction(editor, ACTIONS[i])); + } + + fFindNext.setAction(getAction(editor, ITextEditorActionConstants.FIND_NEXT)); + fFindPrevious.setAction(getAction(editor, ITextEditorActionConstants.FIND_PREVIOUS)); + fIncrementalFind.setAction(getAction(editor, ITextEditorActionConstants.FIND_INCREMENTAL)); + fGotoLine.setAction(getAction(editor, ITextEditorActionConstants.GOTO_LINE)); + + if (fActiveEditorPart instanceof ITextEditorExtension) { + ITextEditorExtension extension= (ITextEditorExtension) fActiveEditorPart; + for (int i= 0; i < STATUSFIELDS.length; i++) + extension.setStatusField((IStatusField) fStatusFields.get(STATUSFIELDS[i]), STATUSFIELDS[i]); + } + } + + /** + * The <code>BasicTextEditorActionContributor</code> implementation of this + * <code>IEditorActionBarContributor</code> method installs the global + * action handler for the given text editor by calling a private helper + * method. Subclasses may extend. + */ + public void setActiveEditor(IEditorPart part) { + doSetActiveEditor(part); + } + + /* + * @see EditorActionBarContributor#contributeToMenu(IMenuManager) + */ + public void contributeToMenu(IMenuManager menu) { + + IMenuManager editMenu= menu.findMenuUsingPath(IWorkbenchActionConstants.M_EDIT); + if (editMenu != null) { + editMenu.appendToGroup(IWorkbenchActionConstants.FIND_EXT, fFindNext); + editMenu.appendToGroup(IWorkbenchActionConstants.FIND_EXT,fFindPrevious); + editMenu.appendToGroup(IWorkbenchActionConstants.FIND_EXT,fIncrementalFind); + editMenu.appendToGroup(IWorkbenchActionConstants.FIND_EXT,fGotoLine); + } + } + + /* + * @see EditorActionBarContributor#contributeToStatusLine(IStatusLineManager) + * @since 2.0 + */ + public void contributeToStatusLine(IStatusLineManager statusLineManager) { + super.contributeToStatusLine(statusLineManager); + for (int i= 0; i < STATUSFIELDS.length; i++) + statusLineManager.add((IContributionItem) fStatusFields.get(STATUSFIELDS[i])); + } + + /* + * @see IEditorActionBarContributor#dispose() + * @since 2.0 + */ + public void dispose() { + doSetActiveEditor(null); + super.dispose(); + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/BookmarkRulerAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/BookmarkRulerAction.java new file mode 100644 index 00000000000..4444508b1b5 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/BookmarkRulerAction.java @@ -0,0 +1,31 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v0.5 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v05.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import org.eclipse.core.resources.IMarker; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.text.source.IVerticalRulerInfo; + +/** + * Adapter for the managing bookmark action. + * @since 2.0 + */ +public class BookmarkRulerAction extends AbstractRulerActionDelegate { + + /* + * @see AbstractRulerActionDelegate#createAction(ITextEditor, IVerticalRulerInfo) + */ + protected IAction createAction(ITextEditor editor, IVerticalRulerInfo rulerInfo) { + return new MarkerRulerAction(EditorMessages.getResourceBundle(), "Editor.ManageBookmarks.", editor, rulerInfo, IMarker.BOOKMARK, true); //$NON-NLS-1$ + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ContentAssistAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ContentAssistAction.java new file mode 100644 index 00000000000..b8d4e21017a --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ContentAssistAction.java @@ -0,0 +1,126 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import java.util.ResourceBundle; + +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.jface.text.ITextOperationTarget; +import org.eclipse.jface.text.ITextOperationTargetExtension; +import org.eclipse.jface.text.source.ISourceViewer; + +import org.eclipse.ui.IWorkbenchPartSite; + + +/** + * A content asisst action which gets its target from its text editor. + * <p> + * The action is initially associated with a text editor via the constructor, + * but can subsequently be changed using <code>setEditor</code>. + * </p> + * <p> + * If this class is used as is, it works by asking the text editor for its text operation target + * (using <code>getAdapter(ITextOperationTarget.class)</code> and runs the content assist + * operation on this target. + * </p> + * @since 2.0 + */ +public final class ContentAssistAction extends TextEditorAction { + + /** The text operation target */ + private ITextOperationTarget fOperationTarget; + + /** + * Creates and initializes the action for the given text editor. + * The action configures its visual representation from the given resource + * bundle. The action works by asking the text editor at the time for its + * text operation target adapter (using + * <code>getAdapter(ITextOperationTarget.class)</code>. The action runs the + * content assist operation on this target. + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys + * (described in <code>ResourceAction</code> constructor), or + * <code>null</code> if none + * @param editor the text editor + * @see ResourceAction#ResourceAction + */ + public ContentAssistAction(ResourceBundle bundle, String prefix, ITextEditor editor) { + super(bundle, prefix, editor); + update(); + } + + /** + * Runs the content assist operation on the editor's text operation target. + */ + public void run() { + if (fOperationTarget != null) { + + ITextEditor editor= getTextEditor(); + if (editor != null) { + + Display display= null; + + IWorkbenchPartSite site= editor.getSite(); + Shell shell= site.getShell(); + if (shell != null && !shell.isDisposed()) + display= shell.getDisplay(); + + BusyIndicator.showWhile(display, new Runnable() { + public void run() { + fOperationTarget.doOperation(ISourceViewer.CONTENTASSIST_PROPOSALS); + } + }); + } + } + } + + /** + * The <code>ContentAssistAction</code> implementation of this + * <code>IUpdate</code> method discovers the operation through the current + * editor's <code>ITextOperationTarget</code> adapter, and sets the + * enabled state accordingly. + */ + public void update() { + + ITextEditor editor= getTextEditor(); + + if (fOperationTarget == null && editor!= null) + fOperationTarget= (ITextOperationTarget) editor.getAdapter(ITextOperationTarget.class); + + if (fOperationTarget == null) { + setEnabled(false); + return; + } + + if (editor instanceof ITextEditorExtension && fOperationTarget instanceof ITextOperationTargetExtension) { + ITextEditorExtension extension= (ITextEditorExtension) editor; + ITextOperationTargetExtension targetExtension= (ITextOperationTargetExtension) fOperationTarget; + boolean isEnabled= !extension.isEditorInputReadOnly(); + targetExtension.enableOperation(ISourceViewer.CONTENTASSIST_PROPOSALS, isEnabled); + } + + setEnabled(fOperationTarget.canDoOperation(ISourceViewer.CONTENTASSIST_PROPOSALS)); + } + + /* + * @see TextEditorAction#setEditor(ITextEditor) + */ + public void setEditor(ITextEditor editor) { + super.setEditor(editor); + fOperationTarget= null; + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ConvertLineDelimitersAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ConvertLineDelimitersAction.java new file mode 100644 index 00000000000..7c41f48f66c --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ConvertLineDelimitersAction.java @@ -0,0 +1,286 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import java.lang.reflect.InvocationTargetException; +import java.util.ResourceBundle; + +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.core.runtime.IProgressMonitor; + +import org.eclipse.jface.dialogs.ProgressMonitorDialog; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentExtension; +import org.eclipse.jface.text.IDocumentPartitioner; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.IRewriteTarget; + + +/** + * An action to convert line delimiters of a text editor document to a particular line + * delimiter. + * @since 2.0 + */ +public class ConvertLineDelimitersAction extends TextEditorAction { + + /** The target line delimiter. */ + private final String fLineDelimiter; + + /** + * Creates a line delimiter conversion action. + * + * @param editor the editor + * @param lineDelimiter the target line delimiter to convert the editor's document to + */ + public ConvertLineDelimitersAction(ITextEditor editor, String lineDelimiter) { + this(EditorMessages.getResourceBundle(), "dummy", editor, lineDelimiter); //$NON-NLS-1$ + } + + /** + * Creates a line delimiter conversion action. + * + * @param bundle the resource bundle + * @param prefix the prefix for the resource bundle lookup + * @param editor the editor + * @param lineDelimiter the target line delimiter to convert the editor's document to + */ + public ConvertLineDelimitersAction(ResourceBundle bundle, String prefix, ITextEditor editor, String lineDelimiter) { + super(bundle, prefix, editor); + fLineDelimiter= lineDelimiter; + + String platformLineDelimiter= System.getProperty("line.separator"); //$NON-NLS-1$ + setText(EditorMessages.getString(getLabelKey(fLineDelimiter, platformLineDelimiter))); + + update(); + } + + /* + * @see Action#run() + */ + public void run() { + + try { + + ITextEditor editor= getTextEditor(); + if (editor == null) + return; + + Object adapter= editor.getAdapter(IRewriteTarget.class); + if (adapter instanceof IRewriteTarget) { + + IRewriteTarget target= (IRewriteTarget) adapter; + IDocument document= target.getDocument(); + if (document != null) { + Shell shell= getTextEditor().getSite().getShell(); + ConvertRunnable runnable= new ConvertRunnable(target, fLineDelimiter); + + if (document.getNumberOfLines() < 40) { + BusyIndicator.showWhile(shell.getDisplay(), runnable); + + } else { + ProgressMonitorDialog dialog= new ProgressMonitorDialog(shell); + dialog.run(false, true, runnable); + } + } + } + + } catch (InterruptedException e) { + // action cancelled + + } catch (InvocationTargetException e) { + // should not happen + } + } + + /** + * Converts all line delimiters of the document to <code>lineDelimiter</code>. + */ + private static class ConvertRunnable implements IRunnableWithProgress, Runnable { + + private final IRewriteTarget fRewriteTarget; + private final String fLineDelimiter; + + public ConvertRunnable(IRewriteTarget rewriteTarget, String lineDelimiter) { + fRewriteTarget= rewriteTarget; + fLineDelimiter= lineDelimiter; + } + + private static class DummyMonitor implements IProgressMonitor { + public void beginTask(String name, int totalWork) {} + public void done() {} + public void internalWorked(double work) {} + public boolean isCanceled() {return false;} + public void setCanceled(boolean value) {} + public void setTaskName(String name) {} + public void subTask(String name) {} + public void worked(int work) {} + } + + /* + * @see IRunnableWithProgress#run(IProgressMonitor) + */ + public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + + IDocument document= fRewriteTarget.getDocument(); + final int lineCount= document.getNumberOfLines(); + monitor.beginTask(EditorMessages.getString("Editor.ConvertLineDelimiter.title"), lineCount); //$NON-NLS-1$ + + fRewriteTarget.setRedraw(false); + fRewriteTarget.beginCompoundChange(); + + if (document instanceof IDocumentExtension) + ((IDocumentExtension) document).startSequentialRewrite(true); + + IDocumentPartitioner partitioner= document.getDocumentPartitioner(); + if (partitioner != null) { + partitioner.disconnect(); + document.setDocumentPartitioner(null); + } + + try { + for (int i= 0; i < lineCount; i++) { + if (monitor.isCanceled()) + throw new InterruptedException(); + + final String delimiter= document.getLineDelimiter(i); + if (delimiter != null && delimiter.length() > 0 && !delimiter.equals(fLineDelimiter)) { + IRegion region= document.getLineInformation(i); + document.replace(region.getOffset() + region.getLength(), delimiter.length(), fLineDelimiter); + } + + monitor.worked(1); + } + + } catch (BadLocationException e) { + throw new InvocationTargetException(e); + + } finally { + + if (partitioner != null) { + partitioner.connect(document); + document.setDocumentPartitioner(partitioner); + } + + if (document instanceof IDocumentExtension) + ((IDocumentExtension) document).stopSequentialRewrite(); + + fRewriteTarget.endCompoundChange(); + fRewriteTarget.setRedraw(true); + + monitor.done(); + } + } + + /* + * @see Runnable#run() + */ + public void run() { + try { + run(new DummyMonitor()); + + } catch (InterruptedException e) { + // cancelled, can't happen with dummy monitor + + } catch (InvocationTargetException e) { + // should not happen + } + } + } + + /** + * Returns whether the given document uses only the given line delimiter. + * @param document the document to check + * @param lineDelimiter the line delimiter to check for + */ + private static boolean usesLineDelimiterExclusively(IDocument document, String lineDelimiter) { + + try { + final int lineCount= document.getNumberOfLines(); + for (int i= 0; i < lineCount; i++) { + final String delimiter= document.getLineDelimiter(i); + if (delimiter != null && delimiter.length() > 0 && !delimiter.equals(lineDelimiter)) + return false; + } + + } catch (BadLocationException e) { + return false; + } + + return true; + } + + /** + * Computes and returns the key to be used to lookup the action's label in + * its resource bundle. + * + * @param lineDelimiter the line delimiter + * @param platformLineDelimiter the platform line delimiter + */ + private static String getLabelKey(String lineDelimiter, String platformLineDelimiter) { + if (lineDelimiter.equals(platformLineDelimiter)) { + + if (lineDelimiter.equals("\r\n")) //$NON-NLS-1$ + return "Editor.ConvertLineDelimiter.toWindows.default.label"; //$NON-NLS-1$ + + if (lineDelimiter.equals("\n")) //$NON-NLS-1$ + return "Editor.ConvertLineDelimiter.toUNIX.default.label"; //$NON-NLS-1$ + + if (lineDelimiter.equals("\r")) //$NON-NLS-1$ + return "Editor.ConvertLineDelimiter.toMac.default.label"; //$NON-NLS-1$ + + } else { + + if (lineDelimiter.equals("\r\n")) //$NON-NLS-1$ + return "Editor.ConvertLineDelimiter.toWindows.label"; //$NON-NLS-1$ + + if (lineDelimiter.equals("\n")) //$NON-NLS-1$ + return "Editor.ConvertLineDelimiter.toUNIX.label"; //$NON-NLS-1$ + + if (lineDelimiter.equals("\r")) //$NON-NLS-1$ + return "Editor.ConvertLineDelimiter.toMac.label"; //$NON-NLS-1$ + } + + return null; + } + + /** + * Internally sets the enable state of this action. + */ + private boolean doEnable() { + ITextEditor editor= getTextEditor(); + if (editor == null) + return false; + + if (editor instanceof ITextEditorExtension && ((ITextEditorExtension) editor).isEditorInputReadOnly()) + return false; + +// IDocument document= getDocument(); +// if (document == null || usesLineDelimiterExclusively(document, fLineDelimiter)) +// return false; + + return isEnabled(); + } + + /* + * @see IUpdate#update() + */ + public void update() { + super.update(); + setEnabled(doEnable()); + } + +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/DefaultRangeIndicator.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/DefaultRangeIndicator.java new file mode 100644 index 00000000000..34762756478 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/DefaultRangeIndicator.java @@ -0,0 +1,146 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.PaletteData; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; + + +/** + * Specialized annotation to indicate a particular range of text lines. + * <p> + * This class may be instantiated; it is not intended to be subclassed. + * This class is instantiated automatically by <code>AbstractTextEditor</code>. + * </p> + */ +public class DefaultRangeIndicator extends Annotation { + + private static PaletteData fgPaletteData; + private Image fImage; + + /** + * Creates a new range indicator. + */ + public DefaultRangeIndicator() { + super(); + setLayer(0); + } + + /* + * @see Annotation#paint + */ + public void paint(GC gc, Canvas canvas, Rectangle bounds) { + + Point canvasSize= canvas.getSize(); + + int x= 0; + int y= bounds.y; + int w= canvasSize.x; + int h= bounds.height; + int b= 1; + + if (y + h > canvasSize.y) + h= canvasSize.y - y; + + if (y < 0) { + h= h + y; + y= 0; + } + + if (h <= 0) + return; + + Image image = getImage(canvas); + gc.drawImage(image, 0, 0, w, h, x, y, w, h); + + gc.setBackground(canvas.getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION)); + gc.fillRectangle(x, bounds.y, w, b); + gc.fillRectangle(x, bounds.y + bounds.height - b, w, b); + } + + private Image getImage(Control control) { + if (fImage == null) { + fImage= createImage(control.getDisplay(), control.getSize()); + + control.addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + if (fImage != null && !fImage.isDisposed()) { + fImage.dispose(); + fImage= null; + } + } + }); + } else { + Rectangle imageRectangle= fImage.getBounds(); + Point controlSize= control.getSize(); + + if (imageRectangle.width < controlSize.x || imageRectangle.height < controlSize.y) { + fImage.dispose(); + fImage= createImage(control.getDisplay(), controlSize); + } + } + + return fImage; + } + + private static Image createImage(Display display, Point size) { + + int width= size.x; + int height= size.y; + + if (fgPaletteData == null) + fgPaletteData= createPalette(display); + + ImageData imageData= new ImageData(width, height, 1, fgPaletteData); + + for (int y= 0; y < height; y++) + for (int x= 0; x < width; x++) + imageData.setPixel(x, y, (x + y) % 2); + + return new Image(display, imageData); + } + + private static PaletteData createPalette(Display display) { + Color c1; + Color c2; + + if (false) { + // range lighter + c1= display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); + c2= display.getSystemColor(SWT.COLOR_LIST_BACKGROUND); + } else { + // range darker + c1= display.getSystemColor(SWT.COLOR_LIST_SELECTION); + c2= display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); + } + + RGB rgbs[]= new RGB[] { + new RGB(c1.getRed(), c1.getGreen(), c1.getBlue()), + new RGB(c2.getRed(), c2.getGreen(), c2.getBlue())}; + + return new PaletteData(rgbs); + } +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/DeleteLineAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/DeleteLineAction.java new file mode 100644 index 00000000000..880c485b3d7 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/DeleteLineAction.java @@ -0,0 +1,179 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v0.5 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v05.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import java.util.ResourceBundle; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextSelection; + + +/** + * An action to delete a whole line, the fraction of the line that is left from the cursor + * or the fraction that is right from the cursor. + * @since 2.0 + */ +public class DeleteLineAction extends TextEditorAction { + + /** Delete the whole line. */ + public static final int WHOLE= 0; + /** Delete to the beginning of line. */ + public static final int TO_BEGINNING= 1; + /** Delete to the end of line. */ + public static final int TO_END= 2; + + /** The type of deletion */ + private final int fType; + + /** + * Creates a line delimiter conversion action. + * + * @param editor the editor + * @param type the line deletion type, must be one of + * <code>WHOLE_LINE</code>, <code>TO_BEGINNING</code> or <code>TO_END</code> + */ + public DeleteLineAction(ResourceBundle bundle, String prefix, ITextEditor editor, int type) { + super(bundle, prefix, editor); + fType= type; + } + + + /** + * Returns the editor's document. + * @param editor the editor + * @return teh editor's document + */ + private static IDocument getDocument(ITextEditor editor) { + + IDocumentProvider documentProvider= editor.getDocumentProvider(); + if (documentProvider == null) + return null; + + IDocument document= documentProvider.getDocument(editor.getEditorInput()); + if (document == null) + return null; + + return document; + } + + + /** + * Returns the editor's selection. + * @param editor the editor + * @return the editor's selection + */ + private static ITextSelection getSelection(ITextEditor editor) { + + ISelectionProvider selectionProvider= editor.getSelectionProvider(); + if (selectionProvider == null) + return null; + + ISelection selection= selectionProvider.getSelection(); + if (!(selection instanceof ITextSelection)) + return null; + + return (ITextSelection) selection; + } + + /* + * @see IAction#run() + */ + public void run() { + + ITextEditor editor= getTextEditor(); + if (editor == null) + return; + + IDocument document= getDocument(editor); + if (document == null) + return; + + ITextSelection selection= getSelection(editor); + if (selection == null) + return; + + try { + deleteLine(document, selection.getOffset(), fType); + } catch (BadLocationException e) { + // should not happen + } + } + + /** + * Deletes the specified fraction of the line of the given offset. + * @param document the document + * @param position the offset + * @param type the specification of what to delete + * @throws BadLocationException if position is not valid in the given document + */ + private static void deleteLine(IDocument document, int position, int type) throws BadLocationException { + + int line= document.getLineOfOffset(position); + int offset= 0; + int length= 0; + + switch (type) { + case WHOLE: + offset= document.getLineOffset(line); + length= document.getLineLength(line); + break; + + case TO_BEGINNING: + offset= document.getLineOffset(line); + length= position - offset; + break; + + case TO_END: + offset= position; + + IRegion lineRegion= document.getLineInformation(line); + int end= lineRegion.getOffset() + lineRegion.getLength(); + + if (position == end) { + String lineDelimiter= document.getLineDelimiter(line); + length= lineDelimiter == null ? 0 : lineDelimiter.length(); + + } else { + length= end - offset; + } + break; + + default: + return; + } + + if (length != 0) + document.replace(offset, length, ""); //$NON-NLS-1$ + } + + /* + * @see IUpdate#update() + */ + public void update() { + + ITextEditor editor= getTextEditor(); + if (editor instanceof ITextEditorExtension) { + ITextEditorExtension extension= (ITextEditorExtension) editor; + if (extension.isEditorInputReadOnly()) { + setEnabled(false); + return; + } + } + + super.update(); + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/DocumentProviderRegistry.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/DocumentProviderRegistry.java new file mode 100644 index 00000000000..21cbe7418fb --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/DocumentProviderRegistry.java @@ -0,0 +1,287 @@ +package org.eclipse.ui.texteditor; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; + +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.PlatformUI; + + + + +/** + * This registry manages shareable document provider. Document + * providers are specified in <code>plugin.xml</code> either + * per name extension or per editor input type. A name extension + * rule always overrules an editor input type rule. Editor input + * type rules follow the same rules <code>IAdapterManager</code> + * used to find object adapters. + * + * @see org.eclipse.core.runtime.IAdapterManager + */ +public class DocumentProviderRegistry { + + /** The registry singleton. */ + private static DocumentProviderRegistry fgRegistry; + + /** + * Returns the standard document provider registry. + */ + public static DocumentProviderRegistry getDefault() { + if (fgRegistry == null) + fgRegistry= new DocumentProviderRegistry(); + return fgRegistry; + } + + + /** The mapping between name extensions and configuration elements. */ + private Map fExtensionMapping= new HashMap(); + /** The mapping between editor input type names and configuration elements. */ + private Map fInputTypeMapping= new HashMap(); + /** The mapping between configuration elements and instantiated document providers. */ + private Map fInstances= new HashMap(); + + + /** + * Creates a new document provider registry and intializes it with the information + * found in the plugin registry. + */ + private DocumentProviderRegistry() { + initialize(); + } + + /** + * Reads the comma-separated value of the given configuration element + * for the given attribute name and remembers the configuration element + * in the given map under the individual tokens of the attribute value. + */ + private void read(Map map, IConfigurationElement element, String attributeName) { + String value= element.getAttribute(attributeName); + if (value != null) { + StringTokenizer tokenizer= new StringTokenizer(value, ","); //$NON-NLS-1$ + while (tokenizer.hasMoreTokens()) { + String token= tokenizer.nextToken().trim(); + + Set s= (Set) map.get(token); + if (s == null) { + s= new HashSet(); + map.put(token, s); + } + s.add(element); + } + } + } + + /** + * Initializes the document provider registry. It retrieves all implementers of the <code>documentProviders</code> + * extension point and remembers those implementers based on the name extensions and the editor input + * types they are for. + */ + private void initialize() { + + IExtensionPoint extensionPoint; + extensionPoint= Platform.getPluginRegistry().getExtensionPoint(PlatformUI.PLUGIN_ID, "documentProviders"); //$NON-NLS-1$ + + if (extensionPoint == null) { + String msg= MessageFormat.format(EditorMessages.getString("DocumentProviderRegistry.error.extension_point_not_found"), new Object[] { PlatformUI.PLUGIN_ID }); //$NON-NLS-1$ + ILog log= Platform.getPlugin(PlatformUI.PLUGIN_ID).getLog(); + log.log(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, 0, msg, null)); + return; + } + + IConfigurationElement[] elements= extensionPoint.getConfigurationElements(); + for (int i= 0; i < elements.length; i++) { + read(fExtensionMapping, elements[i], "extensions"); //$NON-NLS-1$ + read(fInputTypeMapping, elements[i], "inputTypes"); //$NON-NLS-1$ + } + } + + /** + * Returns the document provider for the given configuration element. + * If there is no instantiated document provider remembered for this + * element, a new document provider is created and put into the cache. + */ + private IDocumentProvider getDocumentProvider(IConfigurationElement entry) { + IDocumentProvider provider= (IDocumentProvider) fInstances.get(entry); + if (provider == null) { + try { + provider= (IDocumentProvider) entry.createExecutableExtension("class"); //$NON-NLS-1$ + fInstances.put(entry, provider); + } catch (CoreException x) { + } + } + return provider; + } + + /** + * Returns the first enumerated element of the given set. + */ + private IConfigurationElement selectConfigurationElement(Set set) { + if (set != null && !set.isEmpty()) { + Iterator e= set.iterator(); + return (IConfigurationElement) e.next(); + } + return null; + } + + /** + * Returns a shareable document provider for the given name extension. + * + * @param extension the name extension to be used for lookup + * @return the shareable document provider + */ + public IDocumentProvider getDocumentProvider(String extension) { + + Set set= (Set) fExtensionMapping.get(extension); + if (set != null) { + IConfigurationElement entry= selectConfigurationElement(set); + return getDocumentProvider(entry); + } + return null; + } + + /** + * Computes the class hierarchy of the given type. The type is + * part of the computed hierarchy. + */ + private List computeClassList(Class type) { + + List result= new ArrayList(); + + Class c= type; + while (c != null) { + result.add(c); + c= c.getSuperclass(); + } + + return result; + } + + /** + * Computes the list of all interfaces for the given list of + * classes. The interface lists of the given classes are + * concatenated. + */ + private List computeInterfaceList(List classes) { + + List result= new ArrayList(4); + Hashtable visited= new Hashtable(4); + + Iterator e= classes.iterator(); + while (e.hasNext()) { + Class c= (Class) e.next(); + computeInterfaceList(c.getInterfaces(), result, visited); + } + + return result; + } + + /** + * Computes the list of all interfaces of the given list of interfaces, + * taking a depth-first approach. + */ + private void computeInterfaceList(Class[] interfaces, List result, Hashtable visited) { + + List toBeVisited= new ArrayList(interfaces.length); + + for (int i= 0; i < interfaces.length; i++) { + Class iface= interfaces[i]; + if (visited.get(iface) == null) { + visited.put(iface, iface); + result.add(iface); + toBeVisited.add(iface); + } + } + + Iterator e= toBeVisited.iterator(); + while(e.hasNext()) { + Class iface= (Class) e.next(); + computeInterfaceList(iface.getInterfaces(), result, visited); + } + } + + /** + * Returns the configuration elements for the first class in the list + * of given classes for which configuration elements have been remembered. + */ + private Object getFirstInputTypeMapping(List classes) { + Iterator e= classes.iterator(); + while (e.hasNext()) { + Class c= (Class) e.next(); + Object mapping= fInputTypeMapping.get(c.getName()); + if (mapping != null) + return mapping; + } + return null; + } + + /** + * Returns the appropriate configuration element for the given type. If + * there is no configuration element for the type's name, first the list of + * super classes is searched, and if not successful the list of all interfaces. + */ + private Object findInputTypeMapping(Class type) { + + if (type == null) + return null; + + Object mapping= fInputTypeMapping.get(type.getName()); + if (mapping != null) + return mapping; + + List classList= computeClassList(type); + mapping= getFirstInputTypeMapping(classList); + if (mapping != null) + return mapping; + + return getFirstInputTypeMapping(computeInterfaceList(classList)); + } + + /** + * Returns the shareable document for the type of the given editor input. + * + * @param editorInput the input for whose type the provider is looked up + * @return the shareable document provider + */ + public IDocumentProvider getDocumentProvider(IEditorInput editorInput) { + + IDocumentProvider provider= null; + + IFile file= (IFile) editorInput.getAdapter(IFile.class); + if (file != null) + provider= getDocumentProvider(file.getFileExtension()); + + if (provider == null) { + Set set= (Set) findInputTypeMapping(editorInput.getClass()); + if (set != null) { + IConfigurationElement entry= selectConfigurationElement(set); + provider= getDocumentProvider(entry); + } + } + + return provider; + } +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/EditorMessages.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/EditorMessages.java new file mode 100644 index 00000000000..6d672762a60 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/EditorMessages.java @@ -0,0 +1,30 @@ +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ +package org.eclipse.ui.texteditor; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +class EditorMessages { + + private static final String RESOURCE_BUNDLE= "org.eclipse.ui.texteditor.EditorMessages";//$NON-NLS-1$ + + private static ResourceBundle fgResourceBundle= ResourceBundle.getBundle(RESOURCE_BUNDLE); + + private EditorMessages() { + } + + public static String getString(String key) { + try { + return fgResourceBundle.getString(key); + } catch (MissingResourceException e) { + return "!" + key + "!";//$NON-NLS-2$ //$NON-NLS-1$ + } + } + + public static ResourceBundle getResourceBundle() { + return fgResourceBundle; + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/EditorMessages.properties b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/EditorMessages.properties new file mode 100644 index 00000000000..743474dfd58 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/EditorMessages.properties @@ -0,0 +1,286 @@ +############################################################# +# +# (c) Copyright IBM Corp. 2000, 2001. +# All Rights Reserved. +# +############################################################# + + +## Errors ## + +Editor.error.no_provider=Text editor does not have a document provider + +Editor.error.save.title=Save problems +Editor.error.save.message=Save could not be completed. + +Editor.error.save.deleted.title=Cannot Save +Editor.error.save.deleted.message=The file has been deleted. + + +Editor.error.save.outofsync.title=Update conflict +Editor.error.save.outofsync.message=The file has been changed on the file system. Do you want to overwrite the changes? + +Editor.error.activated.outofsync.title=File Changed +Editor.error.activated.outofsync.message=The file has been changed on the file system. Do you want to load the changes? + +Editor.error.activated.deleted.save.title=File Deleted +Editor.error.activated.deleted.save.message=The file has been deleted from the file system. Do you want to save your changes or close the editor without saving? +Editor.error.activated.deleted.save.button.save=Save +Editor.error.activated.deleted.save.button.close=Close +Editor.error.activated.deleted.close.title=File Deleted +Editor.error.activated.deleted.close.message=The file has been deleted from the file system. This editor will be closed. + +Editor.error.refresh.outofsync.title=Problems loading File +Editor.error.refresh.outofsync.message=The file could not be loaded from the file system. + +Editor.error.revert.title=Problems while reverting to saved state +Editor.error.revert.message=Could not revert to saved state. +Editor.error.setinput.title=Problem while opening +Editor.error.setinput.message=Cannot open input element: +Editor.error.no_input=Unable to read text editor input +Editor.error.invalid_input=Invalid text editor input + +AbstractDocumentProvider.error.save.inuse=File is either open in another editor or in use by an operation. + +DocumentProviderRegistry.error.extension_point_not_found=Extension point: {0}.documentProviders not found + + + +## Actions ## + +Editor.Undo.label=&Undo@Ctrl+Z +Editor.Undo.tooltip=Undo +Editor.Undo.image= +Editor.Undo.description=Undo + +Editor.Redo.label=&Redo@Ctrl+Y +Editor.Redo.tooltip=Redo +Editor.Redo.image= +Editor.Redo.description=Redo + +Editor.Cut.label=Cu&t@Ctrl+X +Editor.Cut.tooltip=Cut +Editor.Cut.image= +Editor.Cut.description=Cut + +Editor.Copy.label=&Copy@Ctrl+C +Editor.Copy.tooltip=Copy +Editor.Copy.image= +Editor.Copy.description=Copy + +Editor.Paste.label=&Paste@Ctrl+V +Editor.Paste.tooltip=Paste +Editor.Paste.image= +Editor.Paste.description=Paste + +Editor.Delete.label=&Delete@Delete +Editor.Delete.tooltip=Delete +Editor.Delete.image= +Editor.Delete.description=Delete + +Editor.Print.label=&Print@Ctrl+P +Editor.Print.tooltip=Print +Editor.Print.image= +Editor.Print.description=Print + +Editor.SelectAll.label=Select &All@Ctrl+A +Editor.SelectAll.tooltip=Select All +Editor.SelectAll.image= +Editor.SelectAll.description=Select All + +Editor.ShiftRight.label=Sh&ift Right +Editor.ShiftRight.tooltip=Shift Right +Editor.ShiftRight.image= +Editor.ShiftRight.description=Shift Right + +Editor.ShiftLeft.label=S&hift Left +Editor.ShiftLeft.tooltip=Shift Left +Editor.ShiftLeft.image= +Editor.ShiftLeft.description=Shift Left + +Editor.delete.line.submenu.label=D&elete Line + +Editor.DeleteLine.label=&Whole +Editor.DeleteLine.tooltip=Delete Line +Editor.DeleteLine= +Editor.DeleteLine.description=Delete Line + +Editor.DeleteLineToBeginning.label=To &Beginning +Editor.DeleteLineToBeginning.tooltip=Delete Line to Beginning +Editor.DeleteLineToBeginning= +Editor.DeleteLineToBeginning.description=Delete Line to Beginning + +Editor.DeleteLineToEnd.label=To &End +Editor.DeleteLineToEnd.tooltip=Delete Line to End +Editor.DeleteLineToEnd= +Editor.DeleteLineToEnd.description=Delete Line to End + +Editor.mark.status.message.mark.set=Mark set +Editor.mark.status.message.mark.cleared=Mark cleared +Editor.mark.status.message.mark.swapped=Mark swapped +Editor.mark.status.error.message.mark.not.set=Mark not set +Editor.mark.status.error.message.mark.not.visible=Mark not in visible region + +Editor.mark.submenu.label=&Mark + +Editor.SetMark.label=&Set +Editor.SetMark.tooltip=Set Mark +Editor.SetMark= +Editor.SetMark.description=Set Mark + +Editor.ClearMark.label=&Clear +Editor.ClearMark.tooltip=Clear Mark +Editor.ClearMark= +Editor.ClearMark.description=Clear Mark + +Editor.SwapMark.label=S&wap +Editor.SwapMark.tooltip=Swap Mark +Editor.SwapMark= +Editor.SwapMark.description=Swap Mark + +Editor.FindReplace.label=&Find/Replace...@Ctrl+F +Editor.FindReplace.tooltip=Find/Replace +Editor.FindReplace.image= +Editor.FindReplace.description=Find/Replace + +Editor.FindNext.label=Find &Next@Ctrl+K +Editor.FindNext.tooltip=Find Next +Editor.FindNext.image= +Editor.FindNext.description=Find Next + +Editor.FindPrevious.label=Find Pre&vious@Ctrl+Shift+K +Editor.FindPrevious.tooltip=Find Previous +Editor.FindPrevious.image= +Editor.FindPrevious.description=Find Previous + +Editor.FindIncremental.label=&Incremental Find@Ctrl+J +Editor.FindIncremental.tooltip=Incremental Find +Editor.FindIncremental.image= +Editor.FindIncremental.description=Incremental Find +Editor.FindIncremental.not_found.pattern={0} Incremental Find: {1} not found +Editor.FindIncremental.found.pattern={0} Incremental Find: {1} +Editor.FindIncremental.render.tab=<TAB> +Editor.FindIncremental.wrapped =Wrapped + +Editor.AddBookmark.label=Boo&kmark... +Editor.AddBookmark.tooltip=Add Bookmark +Editor.AddBookmark.image= +Editor.AddBookmark.description=Add Bookmark +Editor.AddBookmark.dialog.title=Add Bookmark +Editor.AddBookmark.dialog.message=Enter Bookmark name +Editor.AddBookmark.error.dialog.title=Add Bookmark +Editor.AddBookmark.error.dialog.message=Problems adding new bookmark + +Editor.AddTask.label=&Task... +Editor.AddTask.tooltip=Add Task +Editor.AddTask.image= +Editor.AddTask.description=Add Task +Editor.AddTask.dialog.title=Add Task +Editor.AddTask.dialog.message=Enter Task description +Editor.AddTask.error.dialog.title=Add Task +Editor.AddTask.error.dialog.message=Problems adding new task + +Editor.Save.label=&Save@Ctrl+S +Editor.Save.tooltip=Save +Editor.Save.image= +Editor.Save.description=Save + +Editor.Revert.label=Re&vert +Editor.Revert.tooltip=Revert +Editor.Revert.image= +Editor.Revert.description=Revert + +Editor.GotoLine.label=&Go to Line...@Ctrl+L +Editor.GotoLine.tooltip=Go to Line +Editor.GotoLine.image= +Editor.GotoLine.description=Go to Line +Editor.GotoLine.dialog.title=Go to Line +Editor.GotoLine.dialog.message=Enter line number (1..{0}): +Editor.GotoLine.dialog.invalid_input=Not a number +Editor.GotoLine.dialog.invalid_range=Line number out of range + +Editor.ManageBookmarks.tooltip=Adds and Removes Bookmarks +Editor.ManageBookmarks.image= +Editor.ManageBookmarks.description=Adds and removes Bookmarks +Editor.ManageBookmarks.add.label=Add Boo&kmark... +Editor.ManageBookmarks.remove.label=Remove Boo&kmark +Editor.ManageBookmarks.add.dialog.title=Add Bookmark +Editor.ManageBookmarks.add.dialog.message=Enter Bookmark name +Editor.ManageBookmarks.error.dialog.title=Managing Bookmarks +Editor.ManageBookmarks.error.dialog.message=Problems managing bookmarks + +Editor.ManageTasks.tooltip=Adds and Removes Tasks +Editor.ManageTasks.image= +Editor.ManageTasks.description=Adds and removes Tasks +Editor.ManageTasks.add.label=Add &Task... +Editor.ManageTasks.remove.label=Remove &Task +Editor.ManageTasks.add.dialog.title=Add Task +Editor.ManageTasks.add.dialog.message=Enter Task description +Editor.ManageTasks.error.dialog.title=Managing Tasks +Editor.ManageTasks.error.dialog.message=Problems managing tasks + +Editor.SelectMarker.tooltip=Selects the marker's range +Editor.SelectMarker.image= +Editor.SelectMarker.description=Selects the Marker's Range +Editor.SelectMarker.label=Select &Marker Range +Editor.SelectMarker.error.dialog.title=Selecting Marker Range +Editor.SelectMarker.error.dialog.message=Problems selecting marker range + +Editor.ConvertLineDelimiter.title=Converting line delimiters... +Editor.ConvertLineDelimiter.toWindows.label=to CRLF (&Windows) +Editor.ConvertLineDelimiter.toWindows.default.label=to CRLF (&Windows) [default] +Editor.ConvertLineDelimiter.toUNIX.label=to LF (&UNIX, MacOS X) +Editor.ConvertLineDelimiter.toUNIX.default.label=to LF (&UNIX, MacOS X) [default] +Editor.ConvertLineDelimiter.toMac.label=to CR (Classic &MacOS) +Editor.ConvertLineDelimiter.toMac.default.label=to CR (Classic &MacOS) [default] + +## Status line ## + +Editor.statusline.state.readonly.label=Read Only +Editor.statusline.state.writable.label=Writable +Editor.statusline.mode.insert.label=Insert +Editor.statusline.mode.overwrite.label=Overwrite +Editor.statusline.position.pattern={0} : {1} +Editor.statusline.error.label=? + +## Others ## + +AbstractMarkerAnnotationModel.connected=AbstractMarkerAnnotationModel.connected +AbstractMarkerAnnotationModel.createMarkerUpdater=AbstractMarkerAnnotationModel.createMarkerUpdater +AbstractMarkerAnnotationModel.removeAnnotations=AbstractMarkerAnnotationModel.removeAnnotations + +ResourceMarkerAnnotationModel.resourceChanged=ResourceMarkerAnnotationModel.resourceChanged + +WorkbenchChainedTextFontFieldEditor.defaultWorkbenchTextFont=<Using Workbench Text Font> + +FindReplace.title= Find/Replace +FindReplace.Find.label=&Find: +FindReplace.Replace.label=R&eplace With: +FindReplace.Direction=Direction +FindReplace.ForwardRadioButton.label=F&orward +FindReplace.BackwardRadioButton.label=&Backward +FindReplace.Scope=Scope +FindReplace.GlobalRadioButton.label=A&ll +FindReplace.SelectedRangeRadioButton.label=Selec&ted Lines +FindReplace.Options=Options +FindReplace.CaseCheckBox.label=&Case Sensitive +FindReplace.WrapCheckBox.label=Wra&p Search +FindReplace.WholeWordCheckBox.label=&Whole Word +FindReplace.IncrementalCheckBox.label=&Incremental +FindReplace.FindNextButton.label=Fi&nd +FindReplace.ReplaceFindButton.label=Replace/Fin&d +FindReplace.ReplaceSelectionButton.label=&Replace +FindReplace.ReplaceAllButton.label=Replace &All +FindReplace.CloseButton.label=Close +FindReplace.Status.noMatch.label=String Not Found +FindReplace.Status.replacement.label=1 Match replaced +FindReplace.Status.replacements.label={0} matches replaced + +FindNext.Status.noMatch.label=String Not Found + +MarkerRulerAction.addMarker=MarkerRulerAction.addMarker +MarkerRulerAction.getMarker=MarkerRulerAction.getMarker +MarkerRulerAction.removeMarkers=MarkerRulerAction.removeMarkers + +SelectMarkerRulerAction.getMarker=SelectMarkerRulerAction.getMarker +SelectMarkerRulerInfoAction.getMarker=SelectMarkerRulerInfoAction.getMarker diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/EditorStatusLine.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/EditorStatusLine.java new file mode 100644 index 00000000000..0a3197c3819 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/EditorStatusLine.java @@ -0,0 +1,99 @@ +package org.eclipse.ui.texteditor; + +import org.eclipse.swt.graphics.Image; + +import org.eclipse.jface.action.IStatusLineManager; +import org.eclipse.jface.util.Assert; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; + +/** + * An editor status line. + * The selection provider of the editor triggers the status line to be cleared. + * @since 2.1 + */ +class EditorStatusLine implements IEditorStatusLine { + + /** + * Clears the status line on selection changed. + */ + private class StatusLineClearer implements ISelectionChangedListener { + public void selectionChanged(SelectionChangedEvent event) { + fStatusLineManager.setErrorMessage(null); + + Assert.isTrue(this == fStatusLineClearer); + uninstallStatusLineClearer(); + } + }; + + /** The status line manager. */ + private final IStatusLineManager fStatusLineManager; + + /** The selection provider. */ + private final ISelectionProvider fSelectionProvider; + + /** The status line clearer, <code>null</code> if not installed. */ + private StatusLineClearer fStatusLineClearer; + + /** + * Constructor for EditorStatusLine. + */ + public EditorStatusLine(IStatusLineManager statusLineManager, ISelectionProvider selectionProvider) { + + Assert.isNotNull(statusLineManager); + Assert.isNotNull(selectionProvider); + + fStatusLineManager= statusLineManager; + fSelectionProvider= selectionProvider; + } + + /** + * Returns the status line manager. + */ + public IStatusLineManager getStatusLineManager() { + return fStatusLineManager; + } + + /** + * Returns the selection provider.
*/ + public ISelectionProvider getSelectionProvider() { + return fSelectionProvider; + } + + /* + * @see org.eclipse.ui.texteditor.IStatusLine#setMessage(boolean, String, Image)
*/ + public void setMessage(boolean error, String message, Image image) { + + if (error) + fStatusLineManager.setErrorMessage(image, message); + else + fStatusLineManager.setMessage(image, message); + + if (isMessageEmpty(message)) + uninstallStatusLineClearer(); + else + installStatusLineClearer(); + } + + private static boolean isMessageEmpty(String message) { + return message == null || message.trim().length() == 0; + } + + private void uninstallStatusLineClearer() { + if (fStatusLineClearer == null) + return; + + fSelectionProvider.removeSelectionChangedListener(fStatusLineClearer); + fStatusLineClearer= null; + } + + private void installStatusLineClearer() { + if (fStatusLineClearer != null) + return; + + StatusLineClearer statusLineClearer= new StatusLineClearer(); + fSelectionProvider.addSelectionChangedListener(statusLineClearer); + fStatusLineClearer= statusLineClearer; + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindNextAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindNextAction.java new file mode 100644 index 00000000000..e596d05aa8b --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindNextAction.java @@ -0,0 +1,332 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import java.util.ArrayList; +import java.util.List; +import java.util.ResourceBundle; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.action.IStatusLineManager; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.text.IFindReplaceTarget; +import org.eclipse.jface.text.IFindReplaceTargetExtension; +import org.eclipse.jface.text.IRegion; +import org.eclipse.swt.graphics.Point; +import org.eclipse.ui.IEditorActionBarContributor; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.part.EditorActionBarContributor; +import org.eclipse.ui.plugin.AbstractUIPlugin; + + +/** + * An action which finds the next/previous occurrence of the last search or + * the current selection if present. + * <p> + * This class may be instantiated; it is not intended to be subclassed. + * </p> + * @since 2.0 + */ +public class FindNextAction extends ResourceAction implements IUpdate { + + /** The action's target */ + private IFindReplaceTarget fTarget; + /** The part the action is bound to */ + private IWorkbenchPart fWorkbenchPart; + /** The workbench window */ + private IWorkbenchWindow fWorkbenchWindow; + /** The dialog settings to retrieve the last search */ + private IDialogSettings fDialogSettings; + /** The find history as initially given in the dialog settings. */ + private List fFindHistory= new ArrayList(); + /** The find string as initially given in the dialog settings. */ + private String fFindString; + /** The search direction as initially given in the dialog settings. */ + private boolean fForward; + /** The warpping flag as initially given in the dialog settings. */ + private boolean fWrapInit; + /** The case flag as initially given in the dialog settings. */ + private boolean fCaseInit; + /** The whole word flag as initially given in the dialog settings. */ + private boolean fWholeWordInit; + + /** + * Creates a new find/replace action for the given text editor. + * The action configures its visual representation from the given + * resource bundle. + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys + * (described in <code>ResourceAction</code> constructor), or + * <code>null</code> if none + * @param editor the text editor + * @param forward the search direction + * @see ResourceAction#ResourceAction + */ + public FindNextAction(ResourceBundle bundle, String prefix, IWorkbenchPart workbenchPart, boolean forward) { + super(bundle, prefix); + fWorkbenchPart= workbenchPart; + fForward= forward; + update(); + } + + /** + * Creates a new find/replace action for the given text editor. + * The action configures its visual representation from the given + * resource bundle. + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys + * (described in <code>ResourceAction</code> constructor), or + * <code>null</code> if none + * @param workbenchWindow the workbench window + * @param forward the search direction + * @see ResourceAction#ResourceAction + * + * @deprecated use FindReplaceAction(ResourceBundle, String, IWorkbenchPart) instead + */ + public FindNextAction(ResourceBundle bundle, String prefix, IWorkbenchWindow workbenchWindow, boolean forward) { + super(bundle, prefix); + fWorkbenchWindow= workbenchWindow; + fForward= forward; + update(); + } + + /** + * Returns the find string based on the selection or the find history. + * @return the find string + */ + private String getFindString() { + String string= getSelectionString(); + + if (string == null && !fFindHistory.isEmpty()) + string= (String) fFindHistory.get(0); + + return string; + } + + /** + * Returns the status line manager of the active editor. + * @return the status line manager of the active editor + */ + private IStatusLineManager getStatusLineManager() { + + IEditorPart editor= fWorkbenchPart.getSite().getPage().getActiveEditor(); + if (editor == null) + return null; + + IEditorActionBarContributor contributor= editor.getEditorSite().getActionBarContributor(); + if (contributor instanceof EditorActionBarContributor) { + return ((EditorActionBarContributor) contributor).getActionBars().getStatusLineManager(); + } + return null; + } + + /** + * Sets the "no matches found" error message to the status line. + */ + private void statusError() { + fWorkbenchPart.getSite().getShell().getDisplay().beep(); + + IStatusLineManager manager= getStatusLineManager(); + if (manager == null) + return; + + manager.setErrorMessage(EditorMessages.getString("FindNext.Status.noMatch.label")); //$NON-NLS-1$ + manager.setMessage(""); //$NON-NLS-1$ + } + + /** + * Clears the status line. + */ + private void statusClear() { + IStatusLineManager manager= getStatusLineManager(); + if (manager == null) + return; + + manager.setErrorMessage(""); //$NON-NLS-1$ + manager.setMessage(""); //$NON-NLS-1$ + } + + /* + * @see IAction#run + */ + public void run() { + if (fTarget != null) { + readConfiguration(); + + fFindString= getFindString(); + if (fFindString == null) { + statusError(); + return; + } + + if (!findNext(fFindString, fForward, fCaseInit, fWrapInit, fWholeWordInit)) { + statusError(); + } else { + statusClear(); + } + + writeConfiguration(); + } + } + + /* + * @see IUpdate#update() + */ + public void update() { + + if (fWorkbenchPart == null && fWorkbenchWindow != null) + fWorkbenchPart= fWorkbenchWindow.getPartService().getActivePart(); + + if (fWorkbenchPart != null) + fTarget= (IFindReplaceTarget) fWorkbenchPart.getAdapter(IFindReplaceTarget.class); + else + fTarget= null; + + setEnabled(fTarget != null && fTarget.canPerformFind()); + } + + /** + * @see FindReplaceDialog#findIndex(String, int, boolean, boolean, boolean, boolean) + */ + private int findIndex(String findString, int startPosition, boolean forwardSearch, boolean caseSensitive, boolean wrapSearch, boolean wholeWord) { + + if (forwardSearch) { + if (wrapSearch) { + int index= fTarget.findAndSelect(startPosition, findString, true, caseSensitive, wholeWord); + if (index == -1) + index= fTarget.findAndSelect(-1, findString, true, caseSensitive, wholeWord); + return index; + } + return fTarget.findAndSelect(startPosition, findString, true, caseSensitive, wholeWord); + } + + // backward + if (wrapSearch) { + int index= fTarget.findAndSelect(startPosition - 1, findString, false, caseSensitive, wholeWord); + if (index == -1) { + index= fTarget.findAndSelect(-1, findString, false, caseSensitive, wholeWord); + } + return index; + } + return fTarget.findAndSelect(startPosition - 1, findString, false, caseSensitive, wholeWord); + } + + /** + * Returns whether the specified search string can be found using the given options. + * @param findString the string to search for + * @param forwardSearch the search direction + * @param caseSensitive should the search honor cases + * @param wrapSearch should the search wrap to the start/end if end/start reached + * @param wholeWord does the find string represent a complete word + * @return <code>true</code> if the find string can be found using the given options + */ + private boolean findNext(String findString, boolean forwardSearch, boolean caseSensitive, boolean wrapSearch, boolean wholeWord) { + + Point r= fTarget.getSelection(); + int findReplacePosition= r.x; + if (forwardSearch) + findReplacePosition += r.y; + + int index= findIndex(findString, findReplacePosition, forwardSearch, caseSensitive, wrapSearch, wholeWord); + + if (index != -1) + return true; + + return false; + } + + //--------------- configuration handling -------------- + + /** + * Returns the dialog settings object used to share state + * between several find/replace dialogs. + * + * @return the dialog settings to be used + */ + private IDialogSettings getDialogSettings() { + AbstractUIPlugin plugin= (AbstractUIPlugin) Platform.getPlugin(PlatformUI.PLUGIN_ID); + IDialogSettings settings= plugin.getDialogSettings(); + fDialogSettings= settings.getSection(FindReplaceDialog.class.getName()); + if (fDialogSettings == null) + fDialogSettings= settings.addNewSection(FindReplaceDialog.class.getName()); + return fDialogSettings; + } + + /** + * Initializes itself from the dialog settings with the same state + * as at the previous invocation. + */ + private void readConfiguration() { + IDialogSettings s= getDialogSettings(); + + fWrapInit= s.getBoolean("wrap"); //$NON-NLS-1$ + fCaseInit= s.getBoolean("casesensitive"); //$NON-NLS-1$ + fWholeWordInit= s.getBoolean("wholeword"); //$NON-NLS-1$ + + String[] findHistory= s.getArray("findhistory"); //$NON-NLS-1$ + if (findHistory != null) { + fFindHistory.clear(); + for (int i= 0; i < findHistory.length; i++) + fFindHistory.add(findHistory[i]); + } + } + + /** + * Stores it current configuration in the dialog store. + */ + private void writeConfiguration() { + IDialogSettings s= getDialogSettings(); + + if (fFindString == null) + return; + + if (!fFindHistory.isEmpty() && fFindString.equals(fFindHistory.get(0))) + return; + + int index= fFindHistory.indexOf(fFindString); + if (index != -1) + fFindHistory.remove(index); + fFindHistory.add(0, fFindString); + + while (fFindHistory.size() > 8) + fFindHistory.remove(8); + String[] names= new String[fFindHistory.size()]; + fFindHistory.toArray(names); + s.put("findhistory", names); //$NON-NLS-1$ + } + + /** + * Returns the actual selection of the find replace target + */ + private String getSelectionString() { + + /* + * 1GF86V3: ITPUI:WINNT - Internal errors using Find/Replace Dialog + * Now uses TextUtilities rather than focussing on '\n' + */ + String selection= fTarget.getSelectionText(); + if (selection != null && selection.length() > 0) { + int[] info= TextUtilities.indexOf(TextUtilities.fgDelimiters, selection, 0); + if (info[0] > 0) + return selection.substring(0, info[0]); + else if (info[0] == -1) + return selection; + } + return null; + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceAction.java new file mode 100644 index 00000000000..8cb344485b7 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceAction.java @@ -0,0 +1,244 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import java.util.ResourceBundle;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.jface.text.IFindReplaceTarget;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IPartService;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.IWorkbenchWindow; + + +/** + * An action which opens a Find/Replace dialog. + * The dialog while open, tracks the active workbench part + * and retargets itself to the active find/replace target. + * <p> + * This class may be instantiated; it is not intended to be subclassed. + * </p> + * + * @see IFindReplaceTarget + */ +public class FindReplaceAction extends ResourceAction implements IUpdate { + + /** + * Represents the "global" find/replace dialog. It tracks the active + * part and retargets the find/replace dialog accordingly. The find/replace + * target is retrieved from the active part using + * <code>getAdapter(IFindReplaceTarget.class)</code>. + * <p> + * The stub has the same life cycle as the find/replace dialog. + */ + class FindReplaceDialogStub implements IPartListener, DisposeListener { + + private IWorkbenchPart fPart; + private IWorkbenchPart fPreviousPart; + private IFindReplaceTarget fPreviousTarget; + + private IWorkbenchWindow fWindow; + private FindReplaceDialog fDialog; + + /** + * Creates a new find/replace dialog accessor anchored at the given part site. + * @param site the part site + */ + public FindReplaceDialogStub(IWorkbenchPartSite site) { + + fWindow= site.getWorkbenchWindow(); + + fDialog= new FindReplaceDialog(site.getShell()); + fDialog.create(); + fDialog.getShell().addDisposeListener(this); + + IPartService service= fWindow.getPartService(); + service.addPartListener(this); + partActivated(service.getActivePart()); + } + + /** + * Returns the find/replace dialog. + * @return the find/replace dialog + */ + public FindReplaceDialog getDialog() { + return fDialog; + } + + /* + * @see IPartListener#partActivated(IWorkbenchPart) + */ + public void partActivated(IWorkbenchPart part) { + + IFindReplaceTarget target= part == null ? null : (IFindReplaceTarget) part.getAdapter(IFindReplaceTarget.class); + fPreviousPart= fPart; + fPart= target == null ? null : part; + + if (fPreviousTarget != target) { + fPreviousTarget= target; + if (fDialog != null) { + boolean isEditable= false; + if (fPart instanceof ITextEditorExtension) { + ITextEditorExtension extension= (ITextEditorExtension) fPart; + isEditable= !extension.isEditorInputReadOnly(); + } + fDialog.updateTarget(target, isEditable); + } + } + } + + /* + * @see IPartListener#partClosed(IWorkbenchPart) + */ + public void partClosed(IWorkbenchPart part) { + + if (part == fPreviousPart) { + fPreviousPart= null; + fPreviousTarget= null; + } + + if (part == fPart) + partActivated(null); + } + + /* + * @see DisposeListener#widgetDisposed(DisposeEvent) + */ + public void widgetDisposed(DisposeEvent event) { + + if (fgFindReplaceDialogStub == this) + fgFindReplaceDialogStub= null; + + if (fWindow != null) { + fWindow.getPartService().removePartListener(this); + fWindow= null; + } + fDialog= null; + fPart= null; + fPreviousPart= null; + fPreviousTarget= null; + } + + /* + * @see IPartListener#partOpened(IWorkbenchPart) + */ + public void partOpened(IWorkbenchPart part) {} + + /* + * @see IPartListener#partDeactivated(IWorkbenchPart) + */ + public void partDeactivated(IWorkbenchPart part) {} + + /* + * @see IPartListener#partBroughtToTop(IWorkbenchPart) + */ + public void partBroughtToTop(IWorkbenchPart part) {} + }; + + + /** Lister for disabling the dialog on editor close */ + private static FindReplaceDialogStub fgFindReplaceDialogStub; + /** The action's target */ + private IFindReplaceTarget fTarget; + /** The part the action is bound to */ + private IWorkbenchPart fWorkbenchPart; + /** The workbench window */ + private IWorkbenchWindow fWorkbenchWindow; + /** + * Indicates whether the find/replace target is editable + * @since 2.0 + */ + private boolean fIsTargetEditable= false; + + /** + * Creates a new find/replace action for the given text editor. + * The action configures its visual representation from the given + * resource bundle. + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys + * (described in <code>ResourceAction</code> constructor), or + * <code>null</code> if none + * @param editor the text editor + * @see ResourceAction#ResourceAction + */ + public FindReplaceAction(ResourceBundle bundle, String prefix, IWorkbenchPart workbenchPart) { + super(bundle, prefix); + fWorkbenchPart= workbenchPart; + update(); + } + + /** + * Creates a new find/replace action for the given text editor. + * The action configures its visual representation from the given + * resource bundle. + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys + * (described in <code>ResourceAction</code> constructor), or + * <code>null</code> if none + * @param workbenchWindow the workbench window + * @see ResourceAction#ResourceAction + * + * @deprecated use FindReplaceAction(ResourceBundle, String, IWorkbenchPart) instead + */ + public FindReplaceAction(ResourceBundle bundle, String prefix, IWorkbenchWindow workbenchWindow) { + super(bundle, prefix); + fWorkbenchWindow= workbenchWindow; + update(); + } + + /* + * @see IAction#run + */ + public void run() { + if (fTarget != null) { + + if (fgFindReplaceDialogStub != null) { + Shell shell= fWorkbenchPart.getSite().getShell(); + FindReplaceDialog dialog= fgFindReplaceDialogStub.getDialog(); + if (dialog != null && shell != dialog.getParentShell()) { + fgFindReplaceDialogStub= null; // here to avoid timing issues + dialog.close(); + } + } + + if (fgFindReplaceDialogStub == null) + fgFindReplaceDialogStub= new FindReplaceDialogStub(fWorkbenchPart.getSite()); + + FindReplaceDialog dialog= fgFindReplaceDialogStub.getDialog(); + dialog.updateTarget(fTarget, fIsTargetEditable); + dialog.open(); + } + } + + /* + * @see IUpdate#update() + */ + public void update() { + + if (fWorkbenchPart == null && fWorkbenchWindow != null) + fWorkbenchPart= fWorkbenchWindow.getPartService().getActivePart(); + + if (fWorkbenchPart instanceof ITextEditorExtension) { + ITextEditorExtension extension= (ITextEditorExtension) fWorkbenchPart; + fIsTargetEditable= !extension.isEditorInputReadOnly(); + } + + if (fWorkbenchPart != null) + fTarget= (IFindReplaceTarget) fWorkbenchPart.getAdapter(IFindReplaceTarget.class); + else + fTarget= null; + + setEnabled(fTarget != null && fTarget.canPerformFind()); + +// if (fgFindReplaceDialogStub != null) { +// FindReplaceDialog dialog= fgFindReplaceDialogStub.getDialog(); +// dialog.updateTarget(fTarget, fIsTargetEditable); +// } + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceDialog.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceDialog.java new file mode 100644 index 00000000000..8b02cc1ce52 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceDialog.java @@ -0,0 +1,1390 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.core.runtime.Platform; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.text.IFindReplaceTarget; +import org.eclipse.jface.text.IFindReplaceTargetExtension; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Region; + +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.help.WorkbenchHelp; +import org.eclipse.ui.plugin.AbstractUIPlugin; + + + +/** + * Find/Replace dialog. The dialog is opened on a particular + * target but can be re-targeted. Internally used by the <code>FindReplaceAction</code> + */ +class FindReplaceDialog extends Dialog { + + /** + * Updates the find replace dialog on activation changes. + */ + class ActivationListener extends ShellAdapter { + + /* + * @see ShellListener#shellActivated(ShellEvent) + */ + public void shellActivated(ShellEvent e) { + + String oldText= fFindField.getText(); // XXX workaround for 10766 + List oldList= new ArrayList(); + oldList.addAll(fFindHistory); + + readConfiguration(); + updateCombo(fFindField, fFindHistory); + + fFindField.removeModifyListener(fFindModifyListener); + if (!fFindHistory.equals(oldList) && !fFindHistory.isEmpty()) + fFindField.setText((String) fFindHistory.get(0)); + else + fFindField.setText(oldText); + fFindField.addModifyListener(fFindModifyListener); + + fActiveShell= (Shell) e.widget; + updateButtonState(); + if (getShell() == fActiveShell && !fFindField.isDisposed()) + fFindField.setFocus(); + } + + /* + * @see ShellListener#shellDeactivated(ShellEvent) + */ + public void shellDeactivated(ShellEvent e) { + storeSettings(); + + fGlobalRadioButton.setSelection(true); + fSelectedRangeRadioButton.setSelection(false); + + if (fTarget != null && (fTarget instanceof IFindReplaceTargetExtension)) + ((IFindReplaceTargetExtension) fTarget).setScope(null); + + fOldScope= null; + + fActiveShell= null; + updateButtonState(); + } + } + + /** + * Modify listener to update the search result in case of incremental search. + * @since 2.0 + */ + private class FindModifyListener implements ModifyListener { + + /* + * @see ModifyListener#modifyText(ModifyEvent) + */ + public void modifyText(ModifyEvent e) { + if (isIncrementalSearch()) { + if (fFindField.getText().equals("") && fTarget != null) { //$NON-NLS-1$ + // empty selection at base location + int offset= isForwardSearch() + ? fIncrementalBaseLocation.x + fIncrementalBaseLocation.y + : fIncrementalBaseLocation.x; + + fTarget.findAndSelect(offset, "", isForwardSearch(), isCaseSensitiveSearch(), isWholeWordSearch()); //$NON-NLS-1$ + } else { + performSearch(); + } + } + + updateButtonState(); + } + } + + /** The size of the dialogs search history. */ + private static final int HISTORY_SIZE= 5; + + private Point fLocation; + private Point fIncrementalBaseLocation; + private boolean fWrapInit, fCaseInit, fWholeWordInit, fForwardInit, fGlobalInit, fIncrementalInit; + private List fFindHistory; + private List fReplaceHistory; + private IRegion fOldScope; + + private boolean fIsTargetEditable; + private IFindReplaceTarget fTarget; + private Shell fParentShell; + private Shell fActiveShell; + + private ActivationListener fActivationListener= new ActivationListener(); + private ModifyListener fFindModifyListener= new FindModifyListener(); + + private Label fReplaceLabel, fStatusLabel; + private Button fForwardRadioButton, fGlobalRadioButton, fSelectedRangeRadioButton; + private Button fCaseCheckBox, fWrapCheckBox, fWholeWordCheckBox, fIncrementalCheckBox; + private Button fReplaceSelectionButton, fReplaceFindButton, fFindNextButton, fReplaceAllButton; + private Combo fFindField, fReplaceField; + private Rectangle fDialogPositionInit; + + private IDialogSettings fDialogSettings; + + /** + * Creates a new dialog with the given shell as parent. + * @param parentShell the parent shell + */ + public FindReplaceDialog(Shell parentShell) { + super(parentShell); + + fParentShell= null; + fTarget= null; + + fDialogPositionInit= null; + fFindHistory= new ArrayList(HISTORY_SIZE - 1); + fReplaceHistory= new ArrayList(HISTORY_SIZE - 1); + + fWrapInit= false; + fCaseInit= false; + fWholeWordInit= false; + fIncrementalInit= false; + fGlobalInit= true; + fForwardInit= true; + + readConfiguration(); + + setShellStyle(SWT.CLOSE | SWT.MODELESS | SWT.BORDER | SWT.TITLE); + setBlockOnOpen(false); + } + + /** + * Returns this dialog's parent shell. + * @return the dialog's parent shell + */ + public Shell getParentShell() { + return super.getParentShell(); + } + + + /** + * Returns <code>true</code> if control can be used + * + * @param control the control to be checked + * @return <code>true</code> if control can be used + */ + private boolean okToUse(Control control) { + return control != null && !control.isDisposed(); + } + + /* + * @see Window#create + */ + public void create() { + + super.create(); + + Shell shell= getShell(); + shell.addShellListener(fActivationListener); + if (fLocation != null) + shell.setLocation(fLocation); + + // set help context + WorkbenchHelp.setHelp(shell, IAbstractTextEditorHelpContextIds.FIND_REPLACE_DIALOG); + + // fill in combo contents + updateCombo(fFindField, fFindHistory); + updateCombo(fReplaceField, fReplaceHistory); + + // get find string + initFindStringFromSelection(); + + // set dialog position + if (fDialogPositionInit != null) + shell.setBounds(fDialogPositionInit); + + shell.setText(EditorMessages.getString("FindReplace.title")); //$NON-NLS-1$ + // shell.setImage(null); + } + + /** + * Create the button section of the find/replace dialog + * + * @param parent the parent composite + * @return the button section + */ + private Composite createButtonSection(Composite parent) { + + Composite panel= new Composite(parent, SWT.NULL); + GridLayout layout= new GridLayout(); + layout.numColumns= -2; + layout.makeColumnsEqualWidth= true; + panel.setLayout(layout); + + fFindNextButton= makeButton(panel, "FindReplace.FindNextButton.label", 102, true, new SelectionAdapter() { //$NON-NLS-1$ + public void widgetSelected(SelectionEvent e) { + if (isIncrementalSearch()) + initIncrementalBaseLocation(); + + performSearch(); + updateFindHistory(); + fFindNextButton.setFocus(); + } + }); + setGridData(fFindNextButton, GridData.FILL, true, GridData.FILL, false); + + fReplaceFindButton= makeButton(panel, "FindReplace.ReplaceFindButton.label", 103, false, new SelectionAdapter() { //$NON-NLS-1$ + public void widgetSelected(SelectionEvent e) { + performReplaceSelection(); + performSearch(); + updateFindAndReplaceHistory(); + fReplaceFindButton.setFocus(); + } + }); + setGridData(fReplaceFindButton, GridData.FILL, true, GridData.FILL, false); + + fReplaceSelectionButton= makeButton(panel, "FindReplace.ReplaceSelectionButton.label", 104, false, new SelectionAdapter() { //$NON-NLS-1$ + public void widgetSelected(SelectionEvent e) { + performReplaceSelection(); + updateFindAndReplaceHistory(); + fFindNextButton.setFocus(); + } + }); + setGridData(fReplaceSelectionButton, GridData.FILL, true, GridData.FILL, false); + + fReplaceAllButton= makeButton(panel, "FindReplace.ReplaceAllButton.label", 105, false, new SelectionAdapter() { //$NON-NLS-1$ + public void widgetSelected(SelectionEvent e) { + performReplaceAll(); + updateFindAndReplaceHistory(); + fFindNextButton.setFocus(); + } + }); + setGridData(fReplaceAllButton, GridData.FILL, true, GridData.FILL, false); + + // Make the all the buttons the same size as the Remove Selection button. + fReplaceAllButton.setEnabled(isEditable()); + + return panel; + } + + /** + * Creates the options configuration section of the find replace dialog. + * + * @param parent the parent composite + * @return the options configuration section + */ + private Composite createConfigPanel(Composite parent) { + + Composite panel= new Composite(parent, SWT.NULL); + GridLayout layout= new GridLayout(); + layout.numColumns= 2; + layout.makeColumnsEqualWidth= true; + panel.setLayout(layout); + + Composite directionGroup= createDirectionGroup(panel); + setGridData(directionGroup, GridData.FILL, true, GridData.FILL, false); + Composite scopeGroup= createScopeGroup(panel); + setGridData(scopeGroup, GridData.FILL, true, GridData.FILL, false); + + Composite optionsGroup= createOptionsGroup(panel); + setGridData(optionsGroup, GridData.FILL, true, GridData.FILL, false); + GridData data= (GridData) optionsGroup.getLayoutData(); + data.horizontalSpan= 2; + optionsGroup.setLayoutData(data); + + return panel; + } + + /* + * @see Window#createContents + */ + protected Control createContents(Composite parent) { + + Composite panel= new Composite(parent, SWT.NULL); + GridLayout layout= new GridLayout(); + layout.numColumns= 1; + layout.makeColumnsEqualWidth= true; + panel.setLayout(layout); + + Composite inputPanel= createInputPanel(panel); + setGridData(inputPanel, GridData.FILL, true, GridData.CENTER, false); + + Composite configPanel= createConfigPanel(panel); + setGridData(configPanel, GridData.FILL, true, GridData.CENTER, true); + + Composite buttonPanelB= createButtonSection(panel); + setGridData(buttonPanelB, GridData.FILL, true, GridData.CENTER, false); + + Composite statusBar= createStatusAndCloseButton(panel); + setGridData(statusBar, GridData.FILL, true, GridData.CENTER, false); + + updateButtonState(); + + return panel; + } + + /** + * Creates the direction defining part of the options defining section + * of the find replace dialog. + * + * @param parent the parent composite + * @return the direction defining part + */ + private Composite createDirectionGroup(Composite parent) { + + Composite panel= new Composite(parent, SWT.NONE); + GridLayout layout= new GridLayout(); + layout.marginWidth= 0; + layout.marginHeight= 0; + panel.setLayout(layout); + + Group group= new Group(panel, SWT.SHADOW_ETCHED_IN); + group.setText(EditorMessages.getString("FindReplace.Direction")); //$NON-NLS-1$ + GridLayout groupLayout= new GridLayout(); + group.setLayout(groupLayout); + group.setLayoutData(new GridData(GridData.FILL_BOTH)); + + SelectionListener selectionListener= new SelectionListener() { + public void widgetSelected(SelectionEvent e) { + if (isIncrementalSearch()) + initIncrementalBaseLocation(); + } + + public void widgetDefaultSelected(SelectionEvent e) { + } + }; + + fForwardRadioButton= new Button(group, SWT.RADIO | SWT.LEFT); + fForwardRadioButton.setText(EditorMessages.getString("FindReplace.ForwardRadioButton.label")); //$NON-NLS-1$ + setGridData(fForwardRadioButton, GridData.BEGINNING, false, GridData.CENTER, false); + fForwardRadioButton.addSelectionListener(selectionListener); + + Button backwardRadioButton= new Button(group, SWT.RADIO | SWT.LEFT); + backwardRadioButton.setText(EditorMessages.getString("FindReplace.BackwardRadioButton.label")); //$NON-NLS-1$ + setGridData(backwardRadioButton, GridData.BEGINNING, false, GridData.CENTER, false); + backwardRadioButton.addSelectionListener(selectionListener); + + backwardRadioButton.setSelection(!fForwardInit); + fForwardRadioButton.setSelection(fForwardInit); + + return panel; + } + + /** + * Creates the scope defining part of the find replace dialog. + * + * @param parent the parent composite + * @return the scope defining part + * @since 2.0 + */ + private Composite createScopeGroup(Composite parent) { + + Composite panel= new Composite(parent, SWT.NONE); + GridLayout layout= new GridLayout(); + layout.marginWidth= 0; + layout.marginHeight= 0; + panel.setLayout(layout); + + Group group= new Group(panel, SWT.SHADOW_ETCHED_IN); + group.setText(EditorMessages.getString("FindReplace.Scope")); //$NON-NLS-1$ + GridLayout groupLayout= new GridLayout(); + group.setLayout(groupLayout); + group.setLayoutData(new GridData(GridData.FILL_BOTH)); + + fGlobalRadioButton= new Button(group, SWT.RADIO | SWT.LEFT); + fGlobalRadioButton.setText(EditorMessages.getString("FindReplace.GlobalRadioButton.label")); //$NON-NLS-1$ + setGridData(fGlobalRadioButton, GridData.BEGINNING, false, GridData.CENTER, false); + fGlobalRadioButton.setSelection(fGlobalInit); + fGlobalRadioButton.addSelectionListener(new SelectionListener() { + public void widgetSelected(SelectionEvent e) { + if (!fGlobalRadioButton.getSelection()) + return; + + useSelectedLines(false); + } + + public void widgetDefaultSelected(SelectionEvent e) { + } + }); + + fSelectedRangeRadioButton= new Button(group, SWT.RADIO | SWT.LEFT); + fSelectedRangeRadioButton.setText(EditorMessages.getString("FindReplace.SelectedRangeRadioButton.label")); //$NON-NLS-1$ + setGridData(fSelectedRangeRadioButton, GridData.BEGINNING, false, GridData.CENTER, false); + fSelectedRangeRadioButton.setSelection(!fGlobalInit); + fSelectedRangeRadioButton.addSelectionListener(new SelectionListener() { + public void widgetSelected(SelectionEvent e) { + if (!fSelectedRangeRadioButton.getSelection()) + return; + + useSelectedLines(true); + } + + public void widgetDefaultSelected(SelectionEvent e) { + } + }); + + return panel; + } + + /** + * Tells the dialog to perform searches only in the scope given by the actually selected lines. + * @param selectedLines <code>true</code> if selected lines should be used + * @since 2.0 + */ + private void useSelectedLines(boolean selectedLines) { + if (isIncrementalSearch()) + initIncrementalBaseLocation(); + + if (fTarget == null || !(fTarget instanceof IFindReplaceTargetExtension)) + return; + + IFindReplaceTargetExtension extensionTarget= (IFindReplaceTargetExtension) fTarget; + + if (selectedLines) { + + IRegion scope; + if (fOldScope == null) { + Point lineSelection= extensionTarget.getLineSelection(); + scope= new Region(lineSelection.x, lineSelection.y); + } else { + scope= fOldScope; + fOldScope= null; + } + + int offset= isForwardSearch() + ? scope.getOffset() + : scope.getOffset() + scope.getLength(); + + extensionTarget.setSelection(offset, 0); + extensionTarget.setScope(scope); + } else { + fOldScope= extensionTarget.getScope(); + extensionTarget.setScope(null); + } + } + + /** + * Create the panel where the user specifies the text to search + * for and the optional replacement text + * + * @param parent the parent composite + * @return the input panel + */ + private Composite createInputPanel(Composite parent) { + + ModifyListener listener= new ModifyListener() { + public void modifyText(ModifyEvent e) { + updateButtonState(); + } + }; + + Composite panel= new Composite(parent, SWT.NULL); + GridLayout layout= new GridLayout(); + layout.numColumns= 2; + panel.setLayout(layout); + + Label findLabel= new Label(panel, SWT.LEFT); + findLabel.setText(EditorMessages.getString("FindReplace.Find.label")); //$NON-NLS-1$ + setGridData(findLabel, GridData.BEGINNING, false, GridData.CENTER, false); + + fFindField= new Combo(panel, SWT.DROP_DOWN | SWT.BORDER); + setGridData(fFindField, GridData.FILL, true, GridData.CENTER, false); + fFindField.addModifyListener(fFindModifyListener); + + fReplaceLabel= new Label(panel, SWT.LEFT); + fReplaceLabel.setText(EditorMessages.getString("FindReplace.Replace.label")); //$NON-NLS-1$ + setGridData(fReplaceLabel, GridData.BEGINNING, false, GridData.CENTER, false); + + fReplaceField= new Combo(panel, SWT.DROP_DOWN | SWT.BORDER); + setGridData(fReplaceField, GridData.FILL, true, GridData.CENTER, false); + fReplaceField.addModifyListener(listener); + + return panel; + } + + /** + * Creates the functional options part of the options defining + * section of the find replace dialog. + * + * @param the parent composite + * @return the options group + */ + private Composite createOptionsGroup(Composite parent) { + + Composite panel= new Composite(parent, SWT.NULL); + GridLayout layout= new GridLayout(); + layout.marginWidth= 0; + layout.marginHeight= 0; + panel.setLayout(layout); + + Group group= new Group(panel, SWT.SHADOW_NONE); + group.setText(EditorMessages.getString("FindReplace.Options")); //$NON-NLS-1$ + GridLayout groupLayout= new GridLayout(); + groupLayout.numColumns= 2; + groupLayout.makeColumnsEqualWidth= true; + group.setLayout(groupLayout); + group.setLayoutData(new GridData(GridData.FILL_BOTH)); + + SelectionListener selectionListener= new SelectionListener() { + public void widgetSelected(SelectionEvent e) { + storeSettings(); + } + + public void widgetDefaultSelected(SelectionEvent e) { + } + }; + + fCaseCheckBox= new Button(group, SWT.CHECK | SWT.LEFT); + fCaseCheckBox.setText(EditorMessages.getString("FindReplace.CaseCheckBox.label")); //$NON-NLS-1$ + setGridData(fCaseCheckBox, GridData.BEGINNING, false, GridData.CENTER, false); + fCaseCheckBox.setSelection(fCaseInit); + fCaseCheckBox.addSelectionListener(selectionListener); + + fWrapCheckBox= new Button(group, SWT.CHECK | SWT.LEFT); + fWrapCheckBox.setText(EditorMessages.getString("FindReplace.WrapCheckBox.label")); //$NON-NLS-1$ + setGridData(fWrapCheckBox, GridData.BEGINNING, false, GridData.CENTER, false); + fWrapCheckBox.setSelection(fWrapInit); + fWrapCheckBox.addSelectionListener(selectionListener); + + fWholeWordCheckBox= new Button(group, SWT.CHECK | SWT.LEFT); + fWholeWordCheckBox.setText(EditorMessages.getString("FindReplace.WholeWordCheckBox.label")); //$NON-NLS-1$ + setGridData(fWholeWordCheckBox, GridData.BEGINNING, false, GridData.CENTER, false); + fWholeWordCheckBox.setSelection(fWholeWordInit); + fWholeWordCheckBox.addSelectionListener(selectionListener); + + fIncrementalCheckBox= new Button(group, SWT.CHECK | SWT.LEFT); + fIncrementalCheckBox.setText(EditorMessages.getString("FindReplace.IncrementalCheckBox.label")); //$NON-NLS-1$ + setGridData(fIncrementalCheckBox, GridData.BEGINNING, false, GridData.CENTER, false); + fIncrementalCheckBox.setSelection(fIncrementalInit); + fIncrementalCheckBox.addSelectionListener(new SelectionListener() { + public void widgetSelected(SelectionEvent e) { + if (isIncrementalSearch()) + initIncrementalBaseLocation(); + + storeSettings(); + } + + public void widgetDefaultSelected(SelectionEvent e) { + } + }); + + return panel; + } + + /** + * Creates the status and close section of the dialog. + * + * @param parent the parent composite + * @return the status and close button + */ + private Composite createStatusAndCloseButton(Composite parent) { + + Composite panel= new Composite(parent, SWT.NULL); + GridLayout layout= new GridLayout(); + layout.numColumns= 2; + layout.marginWidth= 0; + layout.marginHeight= 0; + panel.setLayout(layout); + + fStatusLabel= new Label(panel, SWT.LEFT); + setGridData(fStatusLabel, GridData.FILL, true, GridData.CENTER, false); + + String label= EditorMessages.getString("FindReplace.CloseButton.label"); //$NON-NLS-1$ + Button closeButton= createButton(panel, 101, label, false); + setGridData(closeButton, GridData.END, false, GridData.END, false); + + return panel; + } + + /* + * @see Dialog#buttonPressed + */ + protected void buttonPressed(int buttonID) { + if (buttonID == 101) + close(); + } + + + + // ------- action invocation --------------------------------------- + + /** + * Returns the position of the specified search string, or <code>-1</code> if the string can + * not be found when searching using the given options. + * + * @param findString the string to search for + * @param startPosition the position at which to start the search + * @param forwardSearch the direction of the search + * @param caseSensitive should the search be case sensitive + * @param wrapSearch should the search wrap to the start/end if arrived at the end/start + * @param wholeWord does the search string represent a complete word + * @return the occurrence of the find string following the options or <code>-1</code> if nothing found + */ + private int findIndex(String findString, int startPosition, boolean forwardSearch, boolean caseSensitive, boolean wrapSearch, boolean wholeWord) { + + if (forwardSearch) { + if (wrapSearch) { + int index= fTarget.findAndSelect(startPosition, findString, true, caseSensitive, wholeWord); + if (index == -1) + index= fTarget.findAndSelect(-1, findString, true, caseSensitive, wholeWord); + return index; + } + return fTarget.findAndSelect(startPosition, findString, true, caseSensitive, wholeWord); + } + + // backward + if (wrapSearch) { + int index= fTarget.findAndSelect(startPosition - 1, findString, false, caseSensitive, wholeWord); + if (index == -1) { + index= fTarget.findAndSelect(-1, findString, false, caseSensitive, wholeWord); + } + return index; + } + return fTarget.findAndSelect(startPosition - 1, findString, false, caseSensitive, wholeWord); + } + + /** + * Returns whether the specified search string can be found using the given options. + * + * @param findString the string to search for + * @param forwardSearch the direction of the search + * @param caseSensitive should the search be case sensitive + * @param wrapSearch should the search wrap to the start/end if arrived at the end/start + * @param wholeWord does the search string represent a complete word + * @param incremental is this an incremental search + * @param global is the search scope the whoel document + * @return <code>true</code> if the search string can be found using the given options + * @since 2.0 + */ + private boolean findNext(String findString, boolean forwardSearch, boolean caseSensitive, boolean wrapSearch, boolean wholeWord, boolean incremental, boolean global) { + + if (fTarget == null) + return false; + + Point r= fTarget.getSelection(); + int findReplacePosition= r.x; + if (forwardSearch) + findReplacePosition += r.y; + + if (incremental) + findReplacePosition= forwardSearch + ? fIncrementalBaseLocation.x + fIncrementalBaseLocation.y + : fIncrementalBaseLocation.x; + + int index= findIndex(findString, findReplacePosition, forwardSearch, caseSensitive, wrapSearch, wholeWord); + + if (index != -1) + return true; + + return false; + } + + /** + * Returns the dialog's boundaries. + * @return the dialog's boundaries + */ + private Rectangle getDialogBoundaries() { + if (okToUse(getShell())) { + return getShell().getBounds(); + } else { + return fDialogPositionInit; + } + } + + /** + * Returns the dialog's history. + * @return the dialog's history + */ + private List getFindHistory() { + return fFindHistory; + } + + // ------- accessors --------------------------------------- + + /** + * Retrieves the string to search for from the appriopriate text input field and returns it. + * @return the search string + */ + private String getFindString() { + if (okToUse(fFindField)) { + return fFindField.getText(); + } + return ""; //$NON-NLS-1$ + } + + /** + * Returns the dialog's replace history. + * @return the dialog's replace history + */ + private List getReplaceHistory() { + return fReplaceHistory; + } + + /** + * Retrieves the replacement string from the appriopriate text input field and returns it. + * @return the replacement string + */ + private String getReplaceString() { + if (okToUse(fReplaceField)) { + return fReplaceField.getText(); + } + return ""; //$NON-NLS-1$ + } + + // ------- init / close --------------------------------------- + + /** + * Returns the actual selection of the find replace target + * @return the selection of the target + */ + private String getSelectionString() { + + /* + * 1GF86V3: ITPUI:WINNT - Internal errors using Find/Replace Dialog + * Now uses TextUtilities rather than focussing on '\n' + */ + String selection= fTarget.getSelectionText(); + if (selection != null && selection.length() > 0) { + int[] info= TextUtilities.indexOf(TextUtilities.fgDelimiters, selection, 0); + if (info[0] > 0) + return selection.substring(0, info[0]); + else if (info[0] == -1) + return selection; + } + return null; + } + + /* + * @see Window#close() + */ + public boolean close() { + handleDialogClose(); + return super.close(); + } + + /** + * Removes focus changed listener from browser and stores settings for re-open. + */ + private void handleDialogClose() { + + // remove listeners + if (fParentShell != null) { + fParentShell.removeShellListener(fActivationListener); + fParentShell= null; + } + + getShell().removeShellListener(fActivationListener); + + // store current settings in case of re-open + storeSettings(); + + if (fTarget != null && fTarget instanceof IFindReplaceTargetExtension) + ((IFindReplaceTargetExtension) fTarget).endSession(); + + // prevent leaks + fActiveShell= null; + fTarget= null; + } + + /** + * Stores the current state in the dialog settings. + * @since 2.0 + */ + private void storeSettings() { + fDialogPositionInit= getDialogBoundaries(); + fWrapInit= isWrapSearch(); + fWholeWordInit= isWholeWordSearch(); + fCaseInit= isCaseSensitiveSearch(); + fIncrementalInit= isIncrementalSearch(); + fForwardInit= isForwardSearch(); + + writeConfiguration(); + } + + /** + * Initializes the string to search for and the appropriate + * text inout field based on the selection found in the + * action's target. + */ + private void initFindStringFromSelection() { + if (fTarget != null && okToUse(fFindField)) { + String selection= getSelectionString(); + fFindField.removeModifyListener(fFindModifyListener); + if (selection != null) { + fFindField.setText(selection); + if (!selection.equals(fTarget.getSelectionText())) { + useSelectedLines(true); + fGlobalRadioButton.setSelection(false); + fSelectedRangeRadioButton.setSelection(true); + } + } else { + if ("".equals(fFindField.getText())) { //$NON-NLS-1$ + if (fFindHistory.size() > 0) + fFindField.setText((String) fFindHistory.get(0)); + else + fFindField.setText(""); //$NON-NLS-1$ + } + } + fFindField.addModifyListener(fFindModifyListener); + } + } + + /** + * Initializes the anchor used as starting point for incremental searching. + * @since 2.0 + */ + private void initIncrementalBaseLocation() { + if (fTarget != null && isIncrementalSearch()) { + fIncrementalBaseLocation= fTarget.getSelection(); + } else { + fIncrementalBaseLocation= new Point(0, 0); + } + } + + // ------- history --------------------------------------- + + /** + * Initialize the find history. + * @param history the history to be initialized + * @param init the initialization data + */ + private void initHistory(List history, List init) { + history.clear(); + for (int i= 0; i < init.size() && i < HISTORY_SIZE - 1; i++) { + history.add(init.get(i)); + } + } + + /** + * Retrieves and returns the option case sensitivity from the appropriate check box. + * @return <code>true</code> if case sensitive + */ + private boolean isCaseSensitiveSearch() { + if (okToUse(fCaseCheckBox)) { + return fCaseCheckBox.getSelection(); + } + return fCaseInit; + } + + /** + * Retrieves and returns the option search direction from the appropriate check box. + * @return <code>true</code> if searching forward + */ + private boolean isForwardSearch() { + if (okToUse(fForwardRadioButton)) { + return fForwardRadioButton.getSelection(); + } + return fForwardInit; + } + + /** + * Retrieves and returns the option global scope from the appropriate check box. + * @return <code>true</code> if searching globally + * @since 2.0 + */ + private boolean isGlobalSearch() { + if (okToUse(fGlobalRadioButton)) { + return fGlobalRadioButton.getSelection(); + } + return fGlobalInit; + } + + /** + * Retrieves and returns the option search whole words from the appropriate check box. + * @return <code>true</code> if searching for whole words + */ + private boolean isWholeWordSearch() { + if (okToUse(fWholeWordCheckBox)) { + return fWholeWordCheckBox.getSelection(); + } + return fWholeWordInit; + } + + /** + * Retrieves and returns the option wrap search from the appropriate check box. + * @return <code>true</code> if wrapping while searching + */ + private boolean isWrapSearch() { + if (okToUse(fWrapCheckBox)) { + return fWrapCheckBox.getSelection(); + } + return fWrapInit; + } + + /** + * Retrieves and returns the option incremental search from the appropriate check box. + * @return <code>true</code> if incremental search + * @since 2.0 + */ + private boolean isIncrementalSearch() { + if (okToUse(fIncrementalCheckBox)) { + return fIncrementalCheckBox.getSelection(); + } + return fIncrementalInit; + } + + /** + * Creates a button. + * @param parent the parent control + * @param key the key to lookup the button label + * @param id the button id + * @param dfltButton is this button the default button + * @param listener a button pressed listener + * @return teh new button + */ + private Button makeButton(Composite parent, String key, int id, boolean dfltButton, SelectionListener listener) { + String label= EditorMessages.getString(key); + Button b= createButton(parent, id, label, dfltButton); + b.addSelectionListener(listener); + return b; + } + + /** + * Returns the status line manager of the active editor or <code>null</code> if there is no such editor. + * @return the status line manager of the active editor + */ + private IEditorStatusLine getStatusLineManager() { + AbstractUIPlugin plugin= (AbstractUIPlugin) Platform.getPlugin(PlatformUI.PLUGIN_ID); + IWorkbenchWindow window= plugin.getWorkbench().getActiveWorkbenchWindow(); + if (window == null) + return null; + + IWorkbenchPage page= window.getActivePage(); + if (page == null) + return null; + + IEditorPart editor= page.getActiveEditor(); + if (editor == null) + return null; + + return (IEditorStatusLine) editor.getAdapter(IEditorStatusLine.class); + } + + /** + * Sets the given error message in the status line. + * @param message the error message + */ + private void statusMessage(boolean error, String message) { + fStatusLabel.setText(message); + + IEditorStatusLine statusLine= getStatusLineManager(); + if (statusLine != null) + statusLine.setMessage(error, message, null); + + if (error) + getShell().getDisplay().beep(); + } + + /** + * Sets the given error message in the status line. + * @param message the message + */ + private void statusError(String message) { + statusMessage(true, message); + } + + /** + * Sets the given message in the status line. + * @param message the message + */ + private void statusMessage(String message) { + statusMessage(false, message); + } + + /** + * Replaces all occurrences of the user's findString with + * the replace string. Indicate to the user the number of replacements + * that occur. + */ + private void performReplaceAll() { + + int replaceCount= 0; + String replaceString= getReplaceString(); + String findString= getFindString(); + + if (replaceString == null) + replaceString= ""; //$NON-NLS-1$ + + if (findString != null && findString.length() > 0) { + + replaceCount= replaceAll(findString, replaceString, isForwardSearch(), isCaseSensitiveSearch(), isWrapSearch(), isWholeWordSearch(), isGlobalSearch()); + + if (replaceCount != 0) { + if (replaceCount == 1) { // not plural + statusMessage(EditorMessages.getString("FindReplace.Status.replacement.label")); //$NON-NLS-1$ + } else { + String msg= EditorMessages.getString("FindReplace.Status.replacements.label"); //$NON-NLS-1$ + msg= MessageFormat.format(msg, new Object[] {String.valueOf(replaceCount)}); + statusMessage(msg); + } + } else { + statusError(EditorMessages.getString("FindReplace.Status.noMatch.label")); //$NON-NLS-1$ + } + } + + updateButtonState(); + } + + /** + * Replaces the current selection of the target with the user's + * replace string. + */ + private void performReplaceSelection() { + + String replaceString= getReplaceString(); + if (replaceString == null) + replaceString= ""; //$NON-NLS-1$ + + fTarget.replaceSelection(replaceString); + updateButtonState(); + } + + /** + * Locates the user's findString in the text of the target. + */ + private void performSearch() { + + String findString= getFindString(); + + if (findString != null && findString.length() > 0) { + + boolean somethingFound= findNext(findString, isForwardSearch(), isCaseSensitiveSearch(), isWrapSearch(), isWholeWordSearch(), isIncrementalSearch(), isGlobalSearch()); + + if (somethingFound) { + statusMessage(""); //$NON-NLS-1$ + } else { + statusError(EditorMessages.getString("FindReplace.Status.noMatch.label")); //$NON-NLS-1$ + } + } + + updateButtonState(); + } + + /** + * Replaces all occurrences of the user's findString with + * the replace string. Returns the number of replacements + * that occur. + * + * @param findString the string to search for + * @param replaceString the replacement string + * @param forwardSearch the search direction + * @param caseSensitive should the search be case sensitive + * @param wrapSearch should search wrap to start/end if end/start is reached + * @param wholeWord does the search string represent a complete word + * @param global is the search performed globally + * @return the number of occurrences + * @since 2.0 + */ + private int replaceAll(String findString, String replaceString, boolean forwardSearch, boolean caseSensitive, boolean wrapSearch, boolean wholeWord, boolean global) { + + int replaceCount= 0; + int findReplacePosition= 0; + + if (wrapSearch) { // search the whole text + findReplacePosition= 0; + forwardSearch= true; + } else if (fTarget.getSelectionText() != null) { + // the cursor is set to the end or beginning of the selected text + Point selection= fTarget.getSelection(); + findReplacePosition= selection.x; + } + + if (fTarget instanceof IFindReplaceTargetExtension) + ((IFindReplaceTargetExtension) fTarget).setReplaceAllMode(true); + + try { + int index= 0; + while (index != -1) { + index= fTarget.findAndSelect(findReplacePosition, findString, forwardSearch, caseSensitive, wholeWord); + if (index != -1) { // substring not contained from current position + if (forwardSearch) + findReplacePosition= index + replaceString.length(); + else + findReplacePosition= index - replaceString.length(); + fTarget.replaceSelection(replaceString); + replaceCount++; + } + } + } finally { + if (fTarget instanceof IFindReplaceTargetExtension) + ((IFindReplaceTargetExtension) fTarget).setReplaceAllMode(false); + } + + return replaceCount; + } + + // ------- ui creation --------------------------------------- + + /** + * Attaches the given layout specification to the <code>component</code> + * + * @param component the component + * @param horizontalAlignment horizontal alignment + * @param grabExcessHorizontalSpace grab excess horizontal space + * @param verticalAlignment vertical alignment + * @param grabExcessVerticalSpace grab excess vertical space + */ + private void setGridData(Control component, int horizontalAlignment, boolean grabExcessHorizontalSpace, int verticalAlignment, boolean grabExcessVerticalSpace) { + GridData gd= new GridData(); + gd.horizontalAlignment= horizontalAlignment; + gd.grabExcessHorizontalSpace= grabExcessHorizontalSpace; + gd.verticalAlignment= verticalAlignment; + gd.grabExcessVerticalSpace= grabExcessVerticalSpace; + component.setLayoutData(gd); + } + + /** + * Updates the enabled state of the buttons. + */ + private void updateButtonState() { + if (okToUse(getShell()) && okToUse(fFindNextButton)) { + String selectedText= null; + if (fTarget != null) { + selectedText= fTarget.getSelectionText(); + } + + boolean selection= (selectedText != null && selectedText.length() > 0); + + boolean enable= fTarget != null && (fActiveShell == fParentShell || fActiveShell == getShell()); + String str= getFindString(); + boolean findString= (str != null && str.length() > 0); + + fFindNextButton.setEnabled(enable && findString); + fReplaceSelectionButton.setEnabled(enable && isEditable() && selection); + fReplaceFindButton.setEnabled(enable && isEditable() && findString && selection); + fReplaceAllButton.setEnabled(enable && isEditable() && findString); + } + } + + /** + * Updates the given combo with the given content. + * @param combo combo to be updated + * @param content to be put into the combo + */ + private void updateCombo(Combo combo, List content) { + combo.removeAll(); + for (int i= 0; i < content.size(); i++) { + combo.add(content.get(i).toString()); + } + } + + // ------- open / reopen --------------------------------------- + + /** + * Called after executed find/replace action to update the history + */ + private void updateFindAndReplaceHistory() { + updateFindHistory(); + if (okToUse(fReplaceField)) { + updateHistory(fReplaceField, fReplaceHistory); + } + + } + + /** + * Called after executed find action to update the history + */ + private void updateFindHistory() { + if (okToUse(fFindField)) { + fFindField.removeModifyListener(fFindModifyListener); + updateHistory(fFindField, fFindHistory); + fFindField.addModifyListener(fFindModifyListener); + } + } + + /** + * Updates the combo with the history. + * @param combo to be updated + * @param history to be put into the combo + */ + private void updateHistory(Combo combo, List history) { + String findString= combo.getText(); + int index= history.indexOf(findString); + if (index != 0) { + if (index != -1) { + history.remove(index); + } + history.add(0, findString); + updateCombo(combo, history); + combo.setText(findString); + } + } + + /** + * Returns whether the target is editable + * @return <code>true</code> if target is editable + */ + private boolean isEditable() { + boolean isEditable= (fTarget == null ? false : fTarget.isEditable()); + return fIsTargetEditable && isEditable; + } + + /** + * Updates this dialog because of a different target. + * @param target the new target + * @param isTargetEditable <code>true</code> if the new target can be modifed + * @since 2.0 + */ + public void updateTarget(IFindReplaceTarget target, boolean isTargetEditable) { + + fIsTargetEditable= isTargetEditable; + + if (target != fTarget) { + if (fTarget != null && fTarget instanceof IFindReplaceTargetExtension) + ((IFindReplaceTargetExtension) fTarget).endSession(); + + fTarget= target; + + if (fTarget != null && fTarget instanceof IFindReplaceTargetExtension) { + ((IFindReplaceTargetExtension) fTarget).beginSession(); + + fGlobalInit= true; + fGlobalRadioButton.setSelection(fGlobalInit); + fSelectedRangeRadioButton.setSelection(!fGlobalInit); + } + } + + if (okToUse(fReplaceLabel)) { + fReplaceLabel.setEnabled(isEditable()); + fReplaceField.setEnabled(isEditable()); + initFindStringFromSelection(); + initIncrementalBaseLocation(); + updateButtonState(); + } + } + + /** + * Sets the parent shell of this dialog to be the given shell. + * + * @param shell the new parent shell + */ + public void setParentShell(Shell shell) { + if (shell != fParentShell) { + + if (fParentShell != null) + fParentShell.removeShellListener(fActivationListener); + + fParentShell= shell; + fParentShell.addShellListener(fActivationListener); + } + + fActiveShell= shell; + } + + + //--------------- configuration handling -------------- + + /** + * Returns the dialog settings object used to share state + * between several find/replace dialogs. + * + * @return the dialog settings to be used + */ + private IDialogSettings getDialogSettings() { + AbstractUIPlugin plugin= (AbstractUIPlugin) Platform.getPlugin(PlatformUI.PLUGIN_ID); + IDialogSettings settings= plugin.getDialogSettings(); + fDialogSettings= settings.getSection(getClass().getName()); + if (fDialogSettings == null) + fDialogSettings= settings.addNewSection(getClass().getName()); + return fDialogSettings; + } + + /** + * Initializes itself from the dialog settings with the same state + * as at the previous invocation. + */ + private void readConfiguration() { + IDialogSettings s= getDialogSettings(); + + try { + int x= s.getInt("x"); //$NON-NLS-1$ + int y= s.getInt("y"); //$NON-NLS-1$ + fLocation= new Point(x, y); + } catch (NumberFormatException e) { + fLocation= null; + } + + fWrapInit= s.getBoolean("wrap"); //$NON-NLS-1$ + fCaseInit= s.getBoolean("casesensitive"); //$NON-NLS-1$ + fWholeWordInit= s.getBoolean("wholeword"); //$NON-NLS-1$ + fIncrementalInit= s.getBoolean("incremental"); //$NON-NLS-1$ + + String[] findHistory= s.getArray("findhistory"); //$NON-NLS-1$ + if (findHistory != null) { + List history= getFindHistory(); + history.clear(); + for (int i= 0; i < findHistory.length; i++) + history.add(findHistory[i]); + } + + String[] replaceHistory= s.getArray("replacehistory"); //$NON-NLS-1$ + if (replaceHistory != null) { + List history= getReplaceHistory(); + history.clear(); + for (int i= 0; i < replaceHistory.length; i++) + history.add(replaceHistory[i]); + } + } + + /** + * Stores it current configuration in the dialog store. + */ + private void writeConfiguration() { + IDialogSettings s= getDialogSettings(); + + Point location= getShell().getLocation(); + s.put("x", location.x); //$NON-NLS-1$ + s.put("y", location.y); //$NON-NLS-1$ + + s.put("wrap", fWrapInit); //$NON-NLS-1$ + s.put("casesensitive", fCaseInit); //$NON-NLS-1$ + s.put("wholeword", fWholeWordInit); //$NON-NLS-1$ + s.put("incremental", fIncrementalInit); //$NON-NLS-1$ + + List history= getFindHistory(); + while (history.size() > 8) + history.remove(8); + String[] names= new String[history.size()]; + history.toArray(names); + s.put("findhistory", names); //$NON-NLS-1$ + + history= getReplaceHistory(); + while (history.size() > 8) + history.remove(8); + names= new String[history.size()]; + history.toArray(names); + s.put("replacehistory", names); //$NON-NLS-1$ + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/GotoLineAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/GotoLineAction.java new file mode 100644 index 00000000000..5250e6f1de7 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/GotoLineAction.java @@ -0,0 +1,188 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import java.text.MessageFormat; +import java.util.ResourceBundle; + +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.jface.dialogs.IInputValidator; +import org.eclipse.jface.dialogs.InputDialog; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.viewers.ISelectionProvider; + +import org.eclipse.ui.IWorkbenchPage; + + +/** + * Action for jumping to a particular line if the editor's text viewer. + * The user is requested to enter the line number into an input dialog. + * The action is initially associated with a text editor via the constructor, + * but that can be subsequently changed using <code>setEditor</code>. + * <p> + * The following keys, prepended by the given option prefix, + * are used for retrieving resources from the given bundle: + * <ul> + * <li><code>"dialog.invalid_range"</code> - to indicate an invalid line number</li> + * <li><code>"dialog.invalid_input"</code> - to indicate an invalid line number format</li> + * <li><code>"dialog.title"</code> - the input dialog's title</li> + * <li><code>"dialog.message"</code> - the input dialog's message</li> + * </ul> + * This class may be instantiated; it is not intended to be subclassed. + * </p> + */ +public class GotoLineAction extends TextEditorAction { + + /** + * Validates whether the text found in the input field of the + * dialog forms a valid line number. A number is valid if it is + * one to which can be jumped. + */ + class NumberValidator implements IInputValidator { + + /* + * @see IInputValidator#isValid(String) + */ + public String isValid(String input) { + + if (input == null || input.length() == 0) + return " "; //$NON-NLS-1$ + + try { + int i= Integer.parseInt(input); + if (i <= 0 || fLastLine < i) + return fBundle.getString(fPrefix + "dialog.invalid_range"); //$NON-NLS-1$ + + } catch (NumberFormatException x) { + return fBundle.getString(fPrefix + "dialog.invalid_input"); //$NON-NLS-1$ + } + + return null; + } + }; + + /** + * Standard input dialog which additionally sets the focus to the + * text input field. Workaround for <code>InputDialog</code> issue. + * 1GIJZOO: ITPSRCEDIT:ALL - Gotodialog's edit field has no initial focus + * @since 2.0 + */ + class GotoLineDialog extends InputDialog { + + /* + * @see InputDialog#InputDialog + */ + public GotoLineDialog(Shell parent, String title, String message, String initialValue, IInputValidator validator) { + super(parent, title, message, initialValue, validator); + } + + /* + * @see InputDialog#createDialogArea(Composite) + */ + protected Control createDialogArea(Composite parent) { + Control result= super.createDialogArea(parent); + getText().setFocus(); + return result; + } + }; + + /** The biggest valid line number of the presented document */ + private int fLastLine; + /** This action's resource bundle */ + private ResourceBundle fBundle; + /** This action's prefix used for accessing the resource bundle */ + private String fPrefix; + + /** + * Creates a new action for the given text editor. The action configures its + * visual representation from the given resource bundle. + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys + * (described in <code>ResourceAction</code> constructor), or + * <code>null</code> if none + * @param editor the text editor + * @see ResourceAction#ResourceAction + */ + public GotoLineAction(ResourceBundle bundle, String prefix, ITextEditor editor) { + super(bundle, prefix, editor); + fBundle= bundle; + fPrefix= prefix; + } + + /** + * Jumps to the given line. + * + * @param line the line to jump to + */ + private void gotoLine(int line) { + + ITextEditor editor= getTextEditor(); + + IDocumentProvider provider= editor.getDocumentProvider(); + IDocument document= provider.getDocument(editor.getEditorInput()); + try { + + int start= document.getLineOffset(line); + editor.selectAndReveal(start, 0); + + IWorkbenchPage page= editor.getSite().getPage(); + page.activate(editor); + + } catch (BadLocationException x) { + // ignore + } + } + + /* + * @see Action#run() + */ + public void run() { + try { + + ITextEditor editor= getTextEditor(); + + if (editor == null) + return; + + IDocumentProvider docProvider= editor.getDocumentProvider(); + if (docProvider == null) + return; + + IDocument document= docProvider.getDocument(editor.getEditorInput()); + if (document == null) + return; + + fLastLine= document.getLineOfOffset(document.getLength()) + 1; + + String title= fBundle.getString(fPrefix + "dialog.title"); //$NON-NLS-1$ + String message= MessageFormat.format(fBundle.getString(fPrefix + "dialog.message"), new Object[] {new Integer(fLastLine)}); //$NON-NLS-1$ + + GotoLineDialog d= new GotoLineDialog(editor.getSite().getShell(), title, message, "", new NumberValidator()); //$NON-NLS-1$ + if (d.open() == d.OK) { + try { + int line= Integer.parseInt(d.getValue()); + gotoLine(line - 1); + } catch (NumberFormatException x) { + } + } + + } catch (BadLocationException x) { + } + } +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IAbstractTextEditorHelpContextIds.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IAbstractTextEditorHelpContextIds.java new file mode 100644 index 00000000000..02cf4fbbe47 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IAbstractTextEditorHelpContextIds.java @@ -0,0 +1,227 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import org.eclipse.ui.PlatformUI; + + +/** + * Help context ids for the text editor. + * <p> + * This interface contains constants only; it is not intended to be implemented. + * </p> + */ +public interface IAbstractTextEditorHelpContextIds { + + /** + * The string with which all other defined ids are prefixed to construct help context ids. + * Value: <code>"org.eclipse.ui."</code> + */ + public static final String PREFIX= PlatformUI.PLUGIN_ID + "."; //$NON-NLS-1$ + + /** + * The string which is appended to action ids to construct help context ids. + * Value: <code>"_action_context"</code> + */ + public static final String ACTION_POSTFIX= "_action_context"; //$NON-NLS-1$ + + + + + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.undo_action_context"</code> + */ + public static final String UNDO_ACTION= PREFIX + ITextEditorActionConstants.UNDO + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.redo_action_context"</code> + */ + public static final String REDO_ACTION= PREFIX + ITextEditorActionConstants.REDO + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.cut_action_context"</code> + */ + public static final String CUT_ACTION= PREFIX + ITextEditorActionConstants.CUT + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.copy_action_context"</code> + */ + public static final String COPY_ACTION= PREFIX + ITextEditorActionConstants.COPY + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.paste_action_context"</code> + */ + public static final String PASTE_ACTION= PREFIX + ITextEditorActionConstants.PASTE + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.delete_action_context"</code> + */ + public static final String DELETE_ACTION= PREFIX + ITextEditorActionConstants.DELETE + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.DeleteLine_action_context"</code> + * @since 2.0 + */ + public static final String DELETE_LINE_ACTION= PREFIX + ITextEditorActionConstants.DELETE_LINE + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.DeleteLineToBeginning_action_context"</code> + * @since 2.0 + */ + public static final String DELETE_LINE_TO_BEGINNING_ACTION= PREFIX + ITextEditorActionConstants.DELETE_LINE_TO_BEGINNING + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.DeleteLineToEnd_action_context"</code> + * @since 2.0 + */ + public static final String DELETE_LINE_TO_END_ACTION= PREFIX + ITextEditorActionConstants.DELETE_LINE_TO_END + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.SetMark_action_context"</code> + * @since 2.0 + */ + public static final String SET_MARK_ACTION= PREFIX + ITextEditorActionConstants.SET_MARK + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.ClearMark_action_context"</code> + * @since 2.0 + */ + public static final String CLEAR_MARK_ACTION= PREFIX + ITextEditorActionConstants.CLEAR_MARK + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.SwapMark_action_context"</code> + * @since 2.0 + */ + public static final String SWAP_MARK_ACTION= PREFIX + ITextEditorActionConstants.SWAP_MARK + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.selectAll_action_context"</code> + */ + public static final String SELECT_ALL_ACTION= PREFIX + ITextEditorActionConstants.SELECT_ALL + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.ShiftRight_action_context"</code> + */ + public static final String SHIFT_RIGHT_ACTION= PREFIX + ITextEditorActionConstants.SHIFT_RIGHT + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.ShiftLeft_action_context"</code> + */ + public static final String SHIFT_LEFT_ACTION= PREFIX + ITextEditorActionConstants.SHIFT_LEFT + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.find_action_context"</code> + */ + public static final String FIND_ACTION= PREFIX + ITextEditorActionConstants.FIND + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.FindNext_action_context"</code> + * @since 2.0 + */ + public static final String FIND_NEXT_ACTION = PREFIX + ITextEditorActionConstants.FIND_NEXT + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.FindPrevious_action_context"</code> + * @since 2.0 + */ + public static final String FIND_PREVIOUS_ACTION = PREFIX + ITextEditorActionConstants.FIND_PREVIOUS + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.FindIncremental_action_context"</code> + * @since 2.0 + */ + public static final String FIND_INCREMENTAL_ACTION = PREFIX + ITextEditorActionConstants.FIND_INCREMENTAL + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.bookmark_action_context"</code> + */ + public static final String BOOKMARK_ACTION= PREFIX + ITextEditorActionConstants.BOOKMARK + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.addTask_action_context"</code> + */ + public static final String ADD_TASK_ACTION= PREFIX + ITextEditorActionConstants.ADD_TASK + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.save_action_context"</code> + */ + public static final String SAVE_ACTION= PREFIX + ITextEditorActionConstants.SAVE + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.revert_action_context"</code> + */ + public static final String REVERT_TO_SAVED_ACTION= PREFIX + ITextEditorActionConstants.REVERT_TO_SAVED + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.GotoLine_action_context"</code> + */ + public static final String GOTO_LINE_ACTION= PREFIX + ITextEditorActionConstants.GOTO_LINE + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.print_action_context"</code> + */ + public static final String PRINT_ACTION= PREFIX + ITextEditorActionConstants.PRINT + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.ConvertLineDelimitersToWindows_action_context"</code> + * @since 2.0 + */ + public static final String CONVERT_LINE_DELIMITERS_TO_WINDOWS= PREFIX + ITextEditorActionConstants.CONVERT_LINE_DELIMITERS_TO_WINDOWS + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.ConvertLineDelimitersToUNIX_action_context"</code> + * @since 2.0 + */ + public static final String CONVERT_LINE_DELIMITERS_TO_UNIX= PREFIX + ITextEditorActionConstants.CONVERT_LINE_DELIMITERS_TO_UNIX + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.ConvertLineDelimitersToMAC_action_context"</code> + * @since 2.0 + */ + public static final String CONVERT_LINE_DELIMITERS_TO_MAC= PREFIX + ITextEditorActionConstants.CONVERT_LINE_DELIMITERS_TO_MAC + ACTION_POSTFIX; + + /** + * Help context id for the action. + * Value: <code>"org.eclipse.ui.find_replace_dialog_context"</code> + */ + public static final String FIND_REPLACE_DIALOG= PREFIX + "find_replace_dialog_context"; //$NON-NLS-1$ +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IDocumentProvider.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IDocumentProvider.java new file mode 100644 index 00000000000..aac92d96a0c --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IDocumentProvider.java @@ -0,0 +1,196 @@ +package org.eclipse.ui.texteditor; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + + + +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.source.IAnnotationModel; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + + + +/** + * A document provider maps between domain elements and documents. + * A document provider has the following responsibilities: + * <ul> + * <li> create an annotation model of a domain model element + * <li> create and manage a textual representation, i.e., a document, of a domain model element + * <li> create and save the content of domain model elements based on given documents + * <li> update the documents this document provider manages for domain model elements + * to changes directly applied to those domain model elements + * <li> notify all element state listeners about changes directly applied to domain model + * elements this document provider manages a document for, i.e. the document + * provider must know which changes of a domain model element are to be interpreted + * as element moves, deletes, etc. + * </ul> + * Text editors use document providers to bridge the gap between their input elements and the + * documents they work on. A single document provider may be shared between multiple editors; + * the methods take the editors' input elements as a parameter.<p> + * This interface may be implemented by clients; or subclass the standard + * abstract base class <code>AbstractDocumentProvider</code>. + * + * @see IDocument + * @see AbstractDocumentProvider + */ +public interface IDocumentProvider { + + /** + * Connects the given element to this document provider. This tells the provider + * that caller of this method is interested to work with the document provided for + * the given domain model element. By counting the invokations of this method and + * <code>disconnect(Object)</code> this provider can assume to know the + * correct number of clients working with the document provided for that + * domain model element. <p> + * The given element must not be <code>null</code>. + * + * @param element the element + * @exception CoreException if the textual representation or the annotation model + * of the element could not be created + */ + void connect(Object element) throws CoreException; + + /** + * Disconnects the given element from this document provider. This tells the provider + * that the caller of this method is no longer interested in working with the document + * provided for the given domain model element. By counting the invokations of + * <code>connect(Object)</code> and of this method this provider can assume to + * know the correct number of clients working with the document provided for that + * domain model element. <p> + * The given element must not be <code>null</code>. + * + * @param element the element + */ + void disconnect(Object element); + + /** + * Returns the document for the given element. Usually the document contains + * a textual presentation of the content of the element, or is the element itself. + * + * @param element the element, or <code>null</code> + * @return the document, or <code>null</code> if none + */ + IDocument getDocument(Object element); + + /** + * Resets the given element's document to its last saved state. + * Element state listeners are notified both before (<code>elementContentAboutToBeReplaced</code>) + * and after (<code>elementContentReplaced</code>) the content is changed. + * + * @param element the element, or <code>null</code> + */ + void resetDocument(Object element) throws CoreException; + + /** + * Saves the given document provided for the given element. + * + * @param monitor a progress monitor to report progress and request cancelation + * @param element the element, or <code>null</code> + * @param document the document + * @param overwrite indicates whether overwrite should be performed + * while saving the given element if necessary + * @exception CoreException if document could not be stored to the given element + */ + void saveDocument(IProgressMonitor monitor, Object element, IDocument document, boolean overwrite) throws CoreException; + + /** + * Returns the modification stamp of the given element. + * + * @param element the element + * @return the modification stamp of the given element + */ + long getModificationStamp(Object element); + + /** + * Returns the time stamp of the last synchronization of + * the given element and it's provided document. + * + * @param element the element + * @return the sysnchronization stamp of the given element + */ + long getSynchronizationStamp(Object element); + + /** + * Returns whether the given element has been deleted. + * + * @param element the element + * @return <code>true</code> if the element has been deleted + */ + boolean isDeleted(Object element); + + /** + * Returns whether the document provided for the given element must be saved. + * + * @param element the element, or <code>null</code> + * @return <code>true</code> if the document must be saved, and + * <code>false</code> otherwise (including the element is <code>null</code>) + */ + boolean mustSaveDocument(Object element); + + /** + * Returns whether the document provided for the given element differs from + * its original state which would required that it be saved. + * + * @param element the element, or <code>null</code> + * @return <code>true</code> if the document can be saved, and + * <code>false</code> otherwise (including the element is <code>null</code>) + */ + boolean canSaveDocument(Object element); + + /** + * Returns the annotation model for the given element. + * + * @param element the element, or <code>null</code> + * @return the annotation model, or <code>null</code> if none + */ + IAnnotationModel getAnnotationModel(Object element); + + /** + * Informs this document provider about upcoming changes of the given element. + * The changes might cause change notifications specific for the type of the given element. + * If this provider manages a document for the given element, the document provider + * must not change the document because of the notifications received after <code> + * aboutToChange</code> has been and before <code>changed</code> is called. In this case, + * it is assumed that the document is already up to date, e.g., a save operation is a + * typical case. <p> + * The concrete nature of the change notification depends on the concrete type of the + * given element. If the element is, e.g., an <code>IResource</code> the notification + * is a resource delta. + * + * @param element the element, or <code>null</code> + */ + void aboutToChange(Object element); + + /** + * Informs this document provider that the given element has been changed. + * All notifications have been sent out. If this provider manages a document + * for the given element, the document provider must from now on change the + * document on the receipt of change notifications. The concrete nature of the change + * notification depends on the concrete type of the given element. If the element is, + * e.g., an <code>IResource</code> the notification is a resource delta. + * + * @param element the element, or <code>null</code> + */ + void changed(Object element); + + /** + * Adds the given element state listener to this document provider. + * Has no effect if an identical listener is already registered. + * + * @param listener the listener + */ + void addElementStateListener(IElementStateListener listener); + + /** + * Removes the given element state listener from this document provider. + * Has no affect if an identical listener is not registered. + * + * @param listener the listener + */ + void removeElementStateListener(IElementStateListener listener); +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IDocumentProviderExtension.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IDocumentProviderExtension.java new file mode 100644 index 00000000000..27d14c601a7 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IDocumentProviderExtension.java @@ -0,0 +1,114 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; + + +/** + * Extension interface for <code>IDocumentProvider</code>. It adds the following + * functions: + * <ul> + * <li> dealing with immutable domain elements + * <li> state validation + * <li> persistent status of domain element operations + * <li> extended synchronization support + * </ul> + * @since 2.0 + */ +public interface IDocumentProviderExtension { + + /** + * Returns whether the document provider thinks that the given element is read-only. + * If this method returns <code>true</code>, <code>saveDocument</code> could fail. + * This method does not say anything about the document constructed from the given + * element. If the given element is not connected to this document provider, the return + * value is undefined. Document providers are allowed to use a cache to answer this + * question, i.e. there can be a difference between the "real" state of the element and + * the return value. + * + * @param element the element + * @return <code>true</code> if the given element is read-only, <code>false</code> otherwise + */ + boolean isReadOnly(Object element); + + /** + * Returns whether the document provider thinks that the given element can persistently be modified. + * This is orthogonal to <code>isReadOnly</code> as read-only elements may be modifiable and + * writable elements may not be modifiable. If the given element is not connected to this document + * provider, the result is undefined. Document providers are allowed to use a cache to answer this + * question, i.e. there can be a difference between the "real" state of the element and the return + * value. + * + * @param element the element + * @return <code>true</code> if the given element is modifiable, <code>false</code> otherwise + */ + boolean isModifiable(Object element); + + /** + * Validates the state of the given element. This method may change the "real" state of the + * element. If using, it also updates the internal caches, so that this method may also change + * the results returned by <code>isReadOnly</code> and <code>isModifiable</code>. If the + * given element is not connected to this document provider, the effect is undefined. + * + * @param element the element + * @param computationContext the context in which the computation is performed, e.g., a SWT shell + * @exception CoreException if validating fails + */ + void validateState(Object element, Object computationContext) throws CoreException; + + /** + * Returns whether the state of the given element has been validated. + * + * @param element the element + */ + boolean isStateValidated(Object element); + + /** + * Updates the state cache for the given element. This method may change the result returned + * by <code>isReadOnly</code> and <code>isModifiable</code>. If the given element is not + * connected to this document provider, the effect is undefined. + * + * @param element the element + * @exception CoreException if validating fails + */ + void updateStateCache(Object element) throws CoreException; + + /** + * Marks the document managed for the given element as saveable. I.e. + * <code>canBeSaved(element)</code> will return <code>true</code> + * afterwards. + * + * @param element the element + */ + void setCanSaveDocument(Object element); + + /** + * Returns the status of the given element. + * + * @param element the element + * @return the status of the given element + */ + IStatus getStatus(Object element); + + /** + * Synchronizes the document provided for the given element with the + * given element. After that call <code>getSynchronizationTimeStamp</code> + * and <code>getModificationTimeStamp</code> return the same value. + * + * @param element the element + * @exception CoreException if the synchronization could not be performed + */ + void synchronize(Object element) throws CoreException; +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IEditorStatusLine.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IEditorStatusLine.java new file mode 100644 index 00000000000..0c908044b84 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IEditorStatusLine.java @@ -0,0 +1,25 @@ +package org.eclipse.ui.texteditor; + +import org.eclipse.swt.graphics.Image; + +/** + * An interface to use the status line of an editor. + * + * @since 2.1 + */ +public interface IEditorStatusLine { + /** + * Sets the image and message to be displayed on the status line. + * <p> + * The error flag indicates that the message is an error message. + * If the error flag is set, a potential non-error message is overridden. + * If the error message is <code>null</code>, the non-error message is displayed. + * </p> + * + * @param error indicates that the message is an error message + * @param message the message to set (may be <code>null</code> to clear the message) + * @param image the image to set (may be <code>null</code> to clear the image) + */ + void setMessage(boolean error, String message, Image image); + +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IElementStateListener.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IElementStateListener.java new file mode 100644 index 00000000000..cfae2352d92 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IElementStateListener.java @@ -0,0 +1,60 @@ +package org.eclipse.ui.texteditor; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + +/** + * Interface for parties interested in standardized element changes. These + * changes are: + * <ul> + * <li> dirty state changes + * <li> content replacements + * <li> moves + * <li> deletions + * </ul> + * The notifications sent to the element state listeners inform about those standardized, + * abstract changes. The concrete change applied might differ from the one the listeners + * are notified about, but should be interpreted as the one the listeners receive. + */ +public interface IElementStateListener { + + /** + * Notifies that the dirty state of the given element has changed. + * + * @param element the element + * @param isDirty the new dirty state + */ + void elementDirtyStateChanged(Object element, boolean isDirty); + + /** + * Notifies that the content of the given element is about to be replaced. + * + * @param element the element + */ + void elementContentAboutToBeReplaced(Object element); + + /** + * Notifies that the content of the given element has been replaced. + * + * @param element the element + */ + void elementContentReplaced(Object element); + + /** + * Notifies that the given element has been deleted. + * + * @param element the element + */ + void elementDeleted(Object element); + + /** + * Notifies that the element has moved. If <code>movedElement</code> + * is <code>null</code> it is similar to <code>elementDeleted(originalElement)</code>. + * + * @param originalElement the element before the move + * @param movedElement the element after the move + */ + void elementMoved(Object originalElement, Object movedElement); +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IElementStateListenerExtension.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IElementStateListenerExtension.java new file mode 100644 index 00000000000..fea0399429f --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IElementStateListenerExtension.java @@ -0,0 +1,47 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + +/** + * Extension interface for <code>IElementStateListener</code>. + * It adds + * <ul> + * <li> state validation notification + * <li>a notion of session, i.e. a notification about an upcoming element change and error handling. + * </ul> + * @since 2.0 + */ +public interface IElementStateListenerExtension { + + /** + * Notifies that the state validation of the given element has changed. + * + * @param element the element + * @param isStateValidated the flag indicating whether state validation is done + */ + void elementStateValidationChanged(Object element, boolean isStateValidated); + + /** + * Notifies that the given element is currently being changed. This method may + * be sent from a non-ui thread. + * + * @param element the element + */ + void elementStateChanging(Object element); + + /** + * Notifies that changing the given element has failed. + * + * @param element the element + */ + void elementStateChangeFailed(Object element); +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IMarkerUpdater.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IMarkerUpdater.java new file mode 100644 index 00000000000..55676c36ecf --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IMarkerUpdater.java @@ -0,0 +1,53 @@ +package org.eclipse.ui.texteditor; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + + +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.Position; + +import org.eclipse.core.resources.IMarker; + + +/** + * A marker updater is responsible for saving changes to markers. + * Marker updaters either update markers of a specific types or + * any type. Also they either assume update responsibility for a + * specific set of marker attributes or any marker attribute. + * Marker updater must be registered with an <code>AbstractMarkerAnnotationModel</code>. + */ +public interface IMarkerUpdater { + + /** + * Returns the marker type for which this updater is responsible. If + * the result is <code>null</code>, the updater assumes responsibility + * for any marker type. + * + * @return the marker type or <code>null</code> for any marker type + */ + String getMarkerType(); + + /** + * Returns the attributes for which this updater is responsible. If the + * result is <code>null</code>, the updater assumes responsibility for + * any attributes. + * + * @return the attributes or <code>null</code> for any attribute + */ + String[] getAttribute(); + + /** + * Updates the given marker according to the position of the given document. + * If the given position is <code>null</code>, the marker is assumed to + * carry the correct positional information. If the updater recognizes that + * the marker should be deleted, it returns <code>false</code>. + * + * @param marker the marker to be updated + * @param document the document into which the given position points + * @param position the current position of the marker inside the given document + */ + boolean updateMarker(IMarker marker, IDocument document, Position position); +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IReadOnlyDependent.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IReadOnlyDependent.java new file mode 100644 index 00000000000..3146d7a35d2 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IReadOnlyDependent.java @@ -0,0 +1,37 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v0.5 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v05.html + +Contributors: + IBM Corporation - Initial API and implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + + +/** + * Extension interface for actions. Actions implementing this interface not + * only manage an enable/disable state but also manage a "hypothetical" + * enable state, depending on whether the target they work on is writable + * or read-only. + * @since 2.0 + */ +public interface IReadOnlyDependent { + + /** + * Returns whether the actions would be enabled if its target + * would be enabled given the writable state described by <code>isWritable</code>. + * <code>isEnabled()</code> and <code>isEnabled(boolean)</code> holds the following + * invariants: + * isEnabled() == false, iff isEnabled(true) == false || isEnabled(false) == false + * isEnabled() == true, iff isEnabled(true) == true || isEnabled(false) == true + * + * @param isWritable + * @return the hypothetical enable state of the action + */ + boolean isEnabled(boolean isWritable); +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IStatusField.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IStatusField.java new file mode 100644 index 00000000000..712dc4b4d0b --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IStatusField.java @@ -0,0 +1,39 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import org.eclipse.swt.graphics.Image; + + +/** + * Interface of a status field of a text editor. The field that shows up in the + * workbench's status line if the contributing editor is active. + * @since 2.0 + */ +public interface IStatusField { + + /** + * Sets the text of this status field. + * + * @param text the text shown in the status field + */ + void setText(String text); + + /** + * Sets the image of this status field. + * + * @param image the image shown in the status field + */ + void setImage(Image image); +} + diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ITextEditor.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ITextEditor.java new file mode 100644 index 00000000000..e4ceb3c3c74 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ITextEditor.java @@ -0,0 +1,173 @@ +package org.eclipse.ui.texteditor; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.viewers.ISelectionProvider; + +import org.eclipse.ui.IEditorPart; + + +/** + * Interface to a text editor. This interface defines functional extensions to + * <code>IEditorPart</code> as well as the configuration capabilities of a text editor. + * <p> + * Text editors are configured with an <code>IDocumentProvider</code> which + * delivers a textual presentation (<code>IDocument</code>) of the editor's input. + * The editor works on the document and forwards all input element related calls, + * such as <code>save</code>, to the document provider. The provider also delivers + * the input's annotation model which is used to control the editor's vertical ruler. + * </p> + * <p> + * Clients may implement this interface from scratch, but the recommended way is to + * subclass the abstract base class <code>AbstractTextEditor</code>. + * </p> + * + * @see IDocumentProvider + * @see org.eclipse.jface.text.source.IAnnotationModel + */ +public interface ITextEditor extends IEditorPart { + + /** + * Returns this text editor's document provider. + * + * @return the document provider + */ + IDocumentProvider getDocumentProvider(); + + /** + * Closes this text editor after optionally saving changes. + * + * @param save <code>true</code> if unsaved changed should be saved, and + * <code>false</code> if unsaved changed should be discarded + */ + void close(boolean save); + + /** + * Returns whether the text in this text editor can be changed by the user. + * + * @return <code>true</code> if it can be edited, and <code>false</code> + * if it is read-only + */ + boolean isEditable(); + + /** + * Abandons all modifications applied to this text editor's input element's + * textual presentation since the last save operation. + */ + void doRevertToSaved(); + + /** + * Installs the given action under the given action id. + * + * @param actionId the action id + * @param action the action, or <code>null</code> to clear it + * @see #getAction + */ + void setAction(String actionID, IAction action); + + /** + * Returns the action installed under the given action id. + * + * @param actionId the action id + * @return the action, or <code>null</code> if none + * @see #setAction + */ + IAction getAction(String actionId); + + /** + * Sets the given activation code for the specified action. If + * there is an activation code already registered, it is replaced. + * The activation code consists of the same information as + * a <code>KeyEvent</code>. If the activation code is triggered + * and the associated action is enabled, the action is performed + * and the triggering <code>KeyEvent</code> is considered consumed. + * If the action is disabled, the <code>KeyEvent</code> is passed + * on unmodified. Thus, action activation codes and action accelerators + * differ in their model of event consumption. The key code parameter + * can be <code>-1</code> to indicate a wild card. The state mask + * parameter can be SWT.DEFAULT to indicate a wild card + * + * @param actionId the action id + * @param character the activation code character + * @param keyCode the activation code key code or <code>-1</code> for wild card + * @param stateMask the activation code state mask or <code>SWT.DEFAULT</code> for wild card + */ + void setActionActivationCode(String actionId, char activationCharacter, int activationKeyCode, int activationStateMask); + + /** + * Removes any installed activation code for the specified action. + * If no activation code is installed, this method does not have + * any effect. + * + * @param actionId the action id + */ + void removeActionActivationCode(String actionId); + + /** + * Returns whether this text editor is configured to show only the + * highlighted range of the text. + * + * @return <code>true</code> if only the highlighted range is shown, and + * <code>false</code> if this editor shows the entire text of the document + * @see #showHighlightRangeOnly + */ + boolean showsHighlightRangeOnly(); + + /** + * Configures this text editor to show only the highlighted range of the + * text. + * + * @param showHighlightRangeOnly <code>true</code> if only the highlighted + * range is shown, and <code>false</code> if this editor shows the entire + * text of the document + * @see #showsHighlightRangeOnly + */ + void showHighlightRangeOnly(boolean showHighlightRangeOnly); + + /** + * Sets the highlighted range of this text editor to the specified region. + * + * @param offset the offset of the highlighted range + * @param length the length of the highlighted range + * @param moveCursor <code>true</code> if the cursor should be moved to + * the start of the highlighted range, and <code>false</code> to leave + * the cursor unaffected + * @see #getHighlightRange + */ + void setHighlightRange(int offset, int length, boolean moveCursor); + + /** + * Returns the highlighted range of this text editor. + * + * @return the highlighted range + * @see #setHighlightRange + */ + IRegion getHighlightRange(); + + /** + * Resets the highlighted range of this text editor. + */ + void resetHighlightRange(); + + /** + * Returns this text editor's selection provider. Repeated calls to this + * method return the same selection provider. + * + * @return the selection provider + */ + ISelectionProvider getSelectionProvider(); + + /** + * Selects and reveals the specified range in this text editor. + * + * @param offset the offset of the selection + * @param length the length of the selection + */ + void selectAndReveal(int offset, int length); +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ITextEditorActionConstants.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ITextEditorActionConstants.java new file mode 100644 index 00000000000..9cd3eb2df80 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ITextEditorActionConstants.java @@ -0,0 +1,229 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import org.eclipse.ui.IWorkbenchActionConstants; + + +/** + * Defines the names of those actions which are preregistered with the + * <code>AbstractTextEditor</code>. <code>RULER_DOUBLE_CLICK</code> defines + * the action which is registered as being executed when the editor's + * ruler has been double clicked. This interface extends the set of names + * available from <code>IWorkbenchActionConstants</code>. It also defines the + * names of the menu groups in a text editor's context menu. + */ +public interface ITextEditorActionConstants extends IWorkbenchActionConstants { + + /** + * Context menu group for undo/redo related actions. + * Value: <code>"group.undo"</code> + */ + static final String GROUP_UNDO= "group.undo"; //$NON-NLS-1$ + + /** + * Context menu group for copy/paste related actions. + * Value: <code>"group.copy"</code> + */ + static final String GROUP_COPY= "group.copy"; //$NON-NLS-1$ + + /** + * Context menu group for text manipulation actions. + * Value: <code>"group.edit"</code> + */ + static final String GROUP_EDIT= "group.edit"; //$NON-NLS-1$ + + /** + * Context menu group for print related actions. + * Value: <code>"group.print"</code> + */ + static final String GROUP_PRINT= "group.print"; //$NON-NLS-1$ + + /** + * Context menu group for find/replace related actions. + * Value: <code>"group.find"</code> + */ + static final String GROUP_FIND= "group.find"; //$NON-NLS-1$ + + /** + * Context menu group for save related actions. + * Value: <code>"group.save"</code> + */ + static final String GROUP_SAVE= "group.save"; //$NON-NLS-1$ + + /** + * Context menu group for actions which do not fit in one of the other categories. + * Value: <code>"group.rest"</code> + */ + static final String GROUP_REST= "group.rest"; //$NON-NLS-1$ + + + + /** + * Name of the action for shifting text blocks to the right. + * Value: <code>"ShiftRight"</code> + */ + static final String SHIFT_RIGHT= "ShiftRight"; //$NON-NLS-1$ + + /** + * Name of the action for shifting text blocks to the left. + * Value: <code>"ShiftLeft"</code> + */ + static final String SHIFT_LEFT= "ShiftLeft"; //$NON-NLS-1$ + + /** + * Name of the action for re-establishing the state after the + * most recent save operation. + * Value: <code>"IWorkbenchActionConstants.REVERT"</code> + */ + static final String REVERT_TO_SAVED= REVERT; + + /** + * Name of the action to delete the current line. + * Value: <code>"DeleteLine"</code> + * @since 2.0 + */ + static final String DELETE_LINE= "DeleteLine"; //$NON-NLS-1$ + + /** + * Name of the action to delete line to beginning. + * Value: <code>"DeleteLineToBeginning"</code> + * @since 2.0 + */ + static final String DELETE_LINE_TO_BEGINNING= "DeleteLineToBeginning"; //$NON-NLS-1$ + + /** + * Name of the action to delete line to end. + * Value: <code>"DeleteLineToEnd"</code> + * @since 2.0 + */ + static final String DELETE_LINE_TO_END= "DeleteLineToEnd"; //$NON-NLS-1$ + + /** + * Name of the action to set the mark. + * Value: <code>"SetMark"</code> + * @since 2.0 + */ + static final String SET_MARK= "SetMark"; //$NON-NLS-1$ + + /** + * Name of the action to set the mark. + * Value: <code>"ClearMark"</code> + * @since 2.0 + */ + static final String CLEAR_MARK= "ClearMark"; //$NON-NLS-1$ + + /** + * Name of the action to swap the mark with the cursor position. + * Value: <code>"SwapMark"</code> + * @since 2.0 + */ + static final String SWAP_MARK= "SwapMark"; //$NON-NLS-1$ + + /** + * Name of the action to jump to a certain text line. + * Value: <code>"GotoLine"</code> + */ + static final String GOTO_LINE= "GotoLine"; //$NON-NLS-1$ + + /** + * Name of the action to find next. + * Value: <code>"FindNext"</code> + * @since 2.0 + */ + static final String FIND_NEXT= "FindNext"; //$NON-NLS-1$ + + /** + * Name of the action to find previous. + * Value: <code>"FindPrevious"</code> + * @since 2.0 + */ + static final String FIND_PREVIOUS= "FindPrevious"; //$NON-NLS-1$ + + /** + * Name of the action to incremental find. + * Value: <code>"FindIncremental"</code> + * @since 2.0 + */ + static final String FIND_INCREMENTAL= "FindIncremental"; //$NON-NLS-1$ + + /** + * Name of the action to convert line delimiters to Windows. + * Value: <code>"ConvertLineDelimitersToWindows"</code> + * @since 2.0 + */ + static final String CONVERT_LINE_DELIMITERS_TO_WINDOWS= "ConvertLineDelimitersToWindows"; //$NON-NLS-1$ + + /** + * Name of the action to convert line delimiters to UNIX. + * Value: <code>"ConvertLineDelimitersToUNIX"</code> + * @since 2.0 + */ + static final String CONVERT_LINE_DELIMITERS_TO_UNIX= "ConvertLineDelimitersToUNIX"; //$NON-NLS-1$ + + /** + * Name of the action to convert line delimiters to MAC. + * Value: <code>"ConvertLineDelimitersToMAC"</code> + * @since 2.0 + */ + static final String CONVERT_LINE_DELIMITERS_TO_MAC= "ConvertLineDelimitersToMAC"; //$NON-NLS-1$ + + + + /** + * Name of the ruler action performed when double clicking the editor's vertical ruler. + * Value: <code>"RulerDoubleClick"</code> + */ + static final String RULER_DOUBLE_CLICK= "RulerDoubleClick"; //$NON-NLS-1$ + + /** + * Name of the ruler action performed when clicking the editor's vertical ruler. + * Value: <code>"RulerClick"</code> + * @since 2.0 + */ + static final String RULER_CLICK= "RulerClick"; //$NON-NLS-1$ + + /** + * Name of the ruler action to manage tasks. + * Value: <code>"ManageTasks"</code> + */ + static final String RULER_MANAGE_TASKS= "ManageTasks"; //$NON-NLS-1$ + + /** + * Name of the ruler action to manage bookmarks. + * Value: <code>"ManageBookmarks"</code> + */ + static final String RULER_MANAGE_BOOKMARKS= "ManageBookmarks"; //$NON-NLS-1$ + + + /** + * Status line category "input position". + * Value: <code>"InputPosition"</code> + * @since 2.0 + */ + static final String STATUS_CATEGORY_INPUT_POSITION= "InputPosition"; //$NON-NLS-1$ + + /** + * Status line category "input mode". + * Value: <code>"InputMode"</code> + * @since 2.0 + */ + static final String STATUS_CATEGORY_INPUT_MODE= "InputMode"; //$NON-NLS-1$ + + /** + * Status line category "element state". + * Value: <code>"ElementState"</code> + * @since 2.0 + */ + static final String STATUS_CATEGORY_ELEMENT_STATE= "ElementState"; //$NON-NLS-1$ +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ITextEditorActionDefinitionIds.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ITextEditorActionDefinitionIds.java new file mode 100644 index 00000000000..933c0975b94 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ITextEditorActionDefinitionIds.java @@ -0,0 +1,310 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + +/** + * Defines the definitions ids for the text editor actions. These actions are + * navigation, selection, and modification actions. + * @since 2.0 + */ +public interface ITextEditorActionDefinitionIds extends IWorkbenchActionDefinitionIds { + + // edit + + /** + * Action definition id of the edit delete line action. + * Value: <code>"org.eclipse.ui.edit.text.delete.line"</code> + */ + public static final String DELETE_LINE= "org.eclipse.ui.edit.text.delete.line"; //$NON-NLS-1$ + + /** + * Action definition id of the edit delete line to beginning action. + * Value: <code>"org.eclipse.ui.edit.text.delete.line.to.beginning"</code> + */ + public static final String DELETE_LINE_TO_BEGINNING= "org.eclipse.ui.edit.text.delete.line.to.beginning"; //$NON-NLS-1$ + + /** + * Action definition id of the edit delete line to end action. + * Value: <code>"org.eclipse.ui.edit.text.delete.line.to.end"</code> + */ + public static final String DELETE_LINE_TO_END= "org.eclipse.ui.edit.text.delete.line.to.end"; //$NON-NLS-1$ + + /** + * Action definition id of the edit set mark action. + * Value: <code>"org.eclipse.ui.edit.text.set.mark"</code> + */ + public static final String SET_MARK= "org.eclipse.ui.edit.text.set.mark"; //$NON-NLS-1$ + + /** + * Action definition id of the edit clear mark action. + * Value: <code>"org.eclipse.ui.edit.text.clear.mark"</code> + */ + public static final String CLEAR_MARK= "org.eclipse.ui.edit.text.clear.mark"; //$NON-NLS-1$ + + /** + * Action definition id of the edit swap mark action. + * Value: <code>"org.eclipse.ui.edit.text.swap.mark"</code> + */ + public static final String SWAP_MARK= "org.eclipse.ui.edit.text.swap.mark"; //$NON-NLS-1$ + + + // navigation + + /** + * Action definition id of the navigate goto previous line action. + * Value: <code>"org.eclipse.ui.edit.text.goto.lineUp"</code> + */ + public static final String LINE_UP= "org.eclipse.ui.edit.text.goto.lineUp"; //$NON-NLS-1$ + + /** + * Action definition id of the navigate goto next line action. + * Value: <code>"org.eclipse.ui.edit.text.goto.lineDown"</code> + */ + public static final String LINE_DOWN= "org.eclipse.ui.edit.text.goto.lineDown"; //$NON-NLS-1$ + + /** + * Action definition id of the navigate goto line start action. + * Value: <code>"org.eclipse.ui.edit.text.goto.lineStart"</code> + */ + public static final String LINE_START= "org.eclipse.ui.edit.text.goto.lineStart"; //$NON-NLS-1$ + + /** + * Action definition id of the navigate goto line end action. + * Value: <code>"org.eclipse.ui.edit.text.goto.lineEnd"</code> + */ + public static final String LINE_END= "org.eclipse.ui.edit.text.goto.lineEnd"; //$NON-NLS-1$ + + /** + * Action definition id of the navigate goto line action. + * Value: <code>"org.eclipse.ui.edit.text.goto.line"</code> + */ + public static final String LINE_GOTO= "org.eclipse.ui.edit.text.goto.line"; //$NON-NLS-1$ + + /** + * Action definition id of the navigate goto previous column action. + * Value: <code>"org.eclipse.ui.edit.text.goto.columnPrevious"</code> + */ + public static final String COLUMN_PREVIOUS= "org.eclipse.ui.edit.text.goto.columnPrevious"; //$NON-NLS-1$ + + /** + * Action definition id of the navigate goto next column action. + * Value: <code>"org.eclipse.ui.edit.text.goto.columnNext"</code> + */ + public static final String COLUMN_NEXT= "org.eclipse.ui.edit.text.goto.columnNext"; //$NON-NLS-1$ + + /** + * Action definition id of the navigate goto previous page action. + * Value: <code>"org.eclipse.ui.edit.text.goto.pageUp"</code> + */ + public static final String PAGE_UP= "org.eclipse.ui.edit.text.goto.pageUp"; //$NON-NLS-1$ + + /** + * Action definition id of the navigate goto next page action. + * Value: <code>"org.eclipse.ui.edit.text.goto.pageDown"</code> + */ + public static final String PAGE_DOWN= "org.eclipse.ui.edit.text.goto.pageDown"; //$NON-NLS-1$ + + /** + * Action definition id of the navigate goto previous word action. + * Value: <code>"org.eclipse.ui.edit.text.goto.wordPrevious"</code> + */ + public static final String WORD_PREVIOUS= "org.eclipse.ui.edit.text.goto.wordPrevious"; //$NON-NLS-1$ + + /** + * Action definition id of the navigate goto next word action. + * Value: <code>"org.eclipse.ui.edit.text.goto.wordNext"</code> + */ + public static final String WORD_NEXT= "org.eclipse.ui.edit.text.goto.wordNext"; //$NON-NLS-1$ + + /** + * Action definition id of the navigate goto text start action. + * Value: <code>"org.eclipse.ui.edit.text.goto.textStart"</code> + */ + public static final String TEXT_START= "org.eclipse.ui.edit.text.goto.textStart"; //$NON-NLS-1$ + + /** + * Action definition id of the navigate goto text end action. + * Value: <code>"org.eclipse.ui.edit.text.goto.textEnd"</code> + */ + public static final String TEXT_END= "org.eclipse.ui.edit.text.goto.textEnd"; //$NON-NLS-1$ + + /** + * Action definition id of the navigate goto start of window action. + * Value: <code>"org.eclipse.ui.edit.text.goto.windowStart"</code> + */ + public static final String WINDOW_START= "org.eclipse.ui.edit.text.goto.windowStart"; //$NON-NLS-1$ + + /** + * Action definition id of the navigate goto end of window action. + * Value: <code>"org.eclipse.ui.edit.text.goto.windowEnd"</code> + */ + public static final String WINDOW_END= "org.eclipse.ui.edit.text.goto.windowEnd"; //$NON-NLS-1$ + + /** + * Action definition id of the navigate scroll line up action. + * Value: <code>"org.eclipse.ui.edit.text.scroll.lineUp"</code> + */ + public static final String SCROLL_LINE_UP= "org.eclipse.ui.edit.text.scroll.lineUp"; //$NON-NLS-1$ + + /** + * Action definition id of the navigate scroll line down action. + * Value: <code>"org.eclipse.ui.edit.text.scroll.lineDown"</code> + */ + public static final String SCROLL_LINE_DOWN= "org.eclipse.ui.edit.text.scroll.lineDown"; //$NON-NLS-1$ + + + // selection + + /** + * Action definition id of the select line up action. + * Value: <code>"org.eclipse.ui.edit.text.select.lineUp"</code> + */ + public static final String SELECT_LINE_UP= "org.eclipse.ui.edit.text.select.lineUp"; //$NON-NLS-1$ + + /** + * Action definition id of the select line down action. + * Value: <code>"org.eclipse.ui.edit.text.select.lineDown"</code> + */ + public static final String SELECT_LINE_DOWN= "org.eclipse.ui.edit.text.select.lineDown"; //$NON-NLS-1$ + + /** + * Action definition id of the select line start action. + * Value: <code>"org.eclipse.ui.edit.text.select.lineStart"</code> + */ + public static final String SELECT_LINE_START= "org.eclipse.ui.edit.text.select.lineStart"; //$NON-NLS-1$ + + /** + * Action definition id of the select line end action. + * Value: <code>"org.eclipse.ui.edit.text.select.lineEnd"</code> + */ + public static final String SELECT_LINE_END= "org.eclipse.ui.edit.text.select.lineEnd"; //$NON-NLS-1$ + + /** + * Action definition id of the select previous column action. + * Value: <code>"org.eclipse.ui.edit.text.select.columnPrevious"</code> + */ + public static final String SELECT_COLUMN_PREVIOUS= "org.eclipse.ui.edit.text.select.columnPrevious"; //$NON-NLS-1$ + + /** + * Action definition id of the select next column action. + * Value: <code>"org.eclipse.ui.edit.text.select.columnNext"</code> + */ + public static final String SELECT_COLUMN_NEXT= "org.eclipse.ui.edit.text.select.columnNext"; //$NON-NLS-1$ + + /** + * Action definition id of the select page up action. + * Value: <code>"org.eclipse.ui.edit.text.select.pageUp"</code> + */ + public static final String SELECT_PAGE_UP= "org.eclipse.ui.edit.text.select.pageUp"; //$NON-NLS-1$ + + /** + * Action definition id of the select page down action. + * Value: <code>"org.eclipse.ui.edit.text.select.pageDown"</code> + */ + public static final String SELECT_PAGE_DOWN= "org.eclipse.ui.edit.text.select.pageDown"; //$NON-NLS-1$ + + /** + * Action definition id of the select previous word action. + * Value: <code>"org.eclipse.ui.edit.text.select.wordPrevious"</code> + */ + public static final String SELECT_WORD_PREVIOUS= "org.eclipse.ui.edit.text.select.wordPrevious"; //$NON-NLS-1$ + + /** + * Action definition id of the select next word action. + * Value: <code>"org.eclipse.ui.edit.text.select.wordNext"</code> + */ + public static final String SELECT_WORD_NEXT= "org.eclipse.ui.edit.text.select.wordNext"; //$NON-NLS-1$ + + /** + * Action definition id of the select text start action. + * Value: <code>"org.eclipse.ui.edit.text.select.textStart"</code> + */ + public static final String SELECT_TEXT_START= "org.eclipse.ui.edit.text.select.textStart"; //$NON-NLS-1$ + + /** + * Action definition id of the select text end action. + * Value: <code>"org.eclipse.ui.edit.text.select.textEnd"</code> + */ + public static final String SELECT_TEXT_END= "org.eclipse.ui.edit.text.select.textEnd"; //$NON-NLS-1$ + + /** + * Action definition id of the select window start action. + * Value: <code>"org.eclipse.ui.edit.text.select.windowStart"</code> + */ + public static final String SELECT_WINDOW_START= "org.eclipse.ui.edit.text.select.windowStart"; //$NON-NLS-1$ + + /** + * Action definition id of the select window end action. + * Value: <code>"org.eclipse.ui.edit.text.select.windowEnd"</code> + */ + public static final String SELECT_WINDOW_END= "org.eclipse.ui.edit.text.select.windowEnd"; //$NON-NLS-1$ + + + // modification + + /** + * Action definition id of the edit delet previous character action. + * Value: <code>"org.eclipse.ui.edit.text.deletePrevious"</code> + */ + public static final String DELETE_PREVIOUS= "org.eclipse.ui.edit.text.deletePrevious"; //$NON-NLS-1$ + + /** + * Action definition id of the edit delete next character action. + * Value: <code>"org.eclipse.ui.edit.text.deleteNext"</code> + */ + public static final String DELETE_NEXT= "org.eclipse.ui.edit.text.deleteNext"; //$NON-NLS-1$ + + /** + * Action definition id of the edit shift right action. + * Value: <code>"org.eclipse.ui.edit.text.shiftRight"</code> + */ + public static final String SHIFT_RIGHT= "org.eclipse.ui.edit.text.shiftRight"; //$NON-NLS-1$ + + /** + * Action definition id of the edit shift left action. + * Value: <code>"org.eclipse.ui.edit.text.shiftLeft"</code> + */ + public static final String SHIFT_LEFT= "org.eclipse.ui.edit.text.shiftLeft"; //$NON-NLS-1$ + + /** + * Action definition id of the edit convert to window's line delimiter action. + * Value: <code>"org.eclipse.ui.edit.text.convert.lineDelimiters.toWindows"</code> + */ + public static final String CONVERT_LINE_DELIMITERS_TO_WINDOWS= "org.eclipse.ui.edit.text.convert.lineDelimiters.toWindows"; //$NON-NLS-1$ + + /** + * Action definition id of the edit convert to unix' line delimiter action. + * Value: <code>"org.eclipse.ui.edit.text.convert.lineDelimiters.toUNIX"</code> + */ + public static final String CONVERT_LINE_DELIMITERS_TO_UNIX= "org.eclipse.ui.edit.text.convert.lineDelimiters.toUNIX"; //$NON-NLS-1$ + + /** + * Action definition id of the edit convert to mac' line delimiter action. + * Value: <code>"org.eclipse.ui.edit.text.convert.lineDelimiters.toMac"</code> + */ + public static final String CONVERT_LINE_DELIMITERS_TO_MAC= "org.eclipse.ui.edit.text.convert.lineDelimiters.toMac"; //$NON-NLS-1$ + + + // miscellaneous + + /** + * Action definition id of the toggle input mode action. + * Value: <code>"org.eclipse.ui.edit.text.toggleOverwrite"</code> + */ + public static final String TOGGLE_OVERWRITE= "org.eclipse.ui.edit.text.toggleOverwrite"; //$NON-NLS-1$ + + /** + * Action definition id of the show ruler context menu action. + * Value: <code>"org.eclipse.ui.edit.text.showRulerContextMenu"</code> + */ + public static final String SHOW_RULER_CONTEXT_MENU= "org.eclipse.ui.edit.text.showRulerContextMenu"; //$NON-NLS-1$ +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ITextEditorExtension.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ITextEditorExtension.java new file mode 100644 index 00000000000..bce3995c399 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ITextEditorExtension.java @@ -0,0 +1,64 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import org.eclipse.jface.action.IMenuListener; + + +/** + * Extension interface for <code>ITextEditor</code>. Adds the following functions: + * <ul> + * <li> status fields + * <li> read-only state of the editor's input + * <li> ruler context menu listeners. + * </ul> + * + * @since 2.0 + */ +public interface ITextEditorExtension { + + /** + * Informs the editor which status field is to be used when posting status + * information in the given category. + * + * @param field the status field to be used + * @param category the status information category + * @see ITextEditorActionConstants + */ + void setStatusField(IStatusField field, String category); + + /** + * Returns whether the editor's input is read-only. The semantics of + * this method is orthogonal to <code>isEditable</code> as it talks about the + * editor input, i.e. the domain element, and <b>not</b> about the editor + * document. + * + * @return <code>true</code> if the editor input is read-only + */ + boolean isEditorInputReadOnly(); + + /** + * Adds a ruler context menu listener to the editor. + * + * @param listener the listener + */ + void addRulerContextMenuListener(IMenuListener listener); + + /** + * Removes a ruler context menu listener from the editor. + * + * @param listener the listener + */ + void removeRulerContextMenuListener(IMenuListener listener); +} + diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IUpdate.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IUpdate.java new file mode 100644 index 00000000000..cda92839ce6 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IUpdate.java @@ -0,0 +1,18 @@ +package org.eclipse.ui.texteditor; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + + +/** + * Indicates the support of an update method. + */ +public interface IUpdate { + + /** + * Requests that this object update itself. + */ + void update(); +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IWorkbenchActionDefinitionIds.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IWorkbenchActionDefinitionIds.java new file mode 100644 index 00000000000..231fb4c38c1 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IWorkbenchActionDefinitionIds.java @@ -0,0 +1,124 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + + +package org.eclipse.ui.texteditor; + + +/** + * Defines the definitions ids for workbench actions. + * @since 2.0 + */ +public interface IWorkbenchActionDefinitionIds { + + // workbench file actions + + /** + * Action definition id of the file print action. + * Value: <code>"org.eclipse.ui.file.print"</code> + */ + public static final String PRINT= "org.eclipse.ui.file.print"; //$NON-NLS-1$ + + /** + * Action definition id of the file save action. + * Value: <code>"org.eclipse.file.save"</code> + */ + public static final String SAVE= "org.eclipse.file.save"; //$NON-NLS-1$ + + /** + * Action definition id of the file revert action. + * Value: <code>"org.eclipse.ui.edit.revertToSaved"</code> + */ + public static final String REVERT_TO_SAVED= "org.eclipse.ui.edit.revertToSaved"; //$NON-NLS-1$ + + + + // workbench edit actions + + /** + * Action definition id of the edit cut action. + * Value: <code>"org.eclipse.ui.edit.cut"</code> + */ + public static final String CUT= "org.eclipse.ui.edit.cut"; //$NON-NLS-1$ + + /** + * Action definition id of the edit copy action. + * Value: <code>"org.eclipse.ui.edit.copy"</code> + */ + public static final String COPY= "org.eclipse.ui.edit.copy"; //$NON-NLS-1$ + + /** + * Action definition id of the edit past action. + * Value: <code>"org.eclipse.ui.edit.paste"</code> + */ + public static final String PASTE= "org.eclipse.ui.edit.paste"; //$NON-NLS-1$ + + /** + * Action definition id of the edit undo action. + * Value: <code>"org.eclipse.ui.edit.undo"</code> + */ + public static final String UNDO= "org.eclipse.ui.edit.undo"; //$NON-NLS-1$ + + /** + * Action definition id of the edit redo action. + * Value: <code>"org.eclipse.ui.edit.redo"</code> + */ + public static final String REDO= "org.eclipse.ui.edit.redo"; //$NON-NLS-1$ + + /** + * Action definition id of the edit delete action. + * Value: <code>"org.eclipse.ui.edit.delete"</code> + */ + public static final String DELETE= "org.eclipse.ui.edit.delete"; //$NON-NLS-1$ + + /** + * Action definition id of the edit select all action. + * Value: <code>"org.eclipse.ui.edit.selectAll"</code> + */ + public static final String SELECT_ALL= "org.eclipse.ui.edit.selectAll"; //$NON-NLS-1$ + + /** + * Action definition id of the edit find/replace action. + * Value: <code>"org.eclipse.ui.edit.findReplace"</code> + */ + public static final String FIND_REPLACE= "org.eclipse.ui.edit.findReplace"; //$NON-NLS-1$ + + /** + * Action definition id of the edit add bookmark action. + * Value: <code>"org.eclipse.ui.edit.addBookmark"</code> + */ + public static final String ADD_BOOKMARK= "org.eclipse.ui.edit.addBookmark"; //$NON-NLS-1$ + + /** + * Action definition id of the edit add task action. + * Value: <code>"org.eclipse.ui.edit.addTask"</code> + */ + public static final String ADD_TASK= "org.eclipse.ui.edit.addTask"; //$NON-NLS-1$ + + + // future workbench edit actions + + /** + * Action definition id of the edit find next action. + * Value: <code>"org.eclipse.ui.edit.findNext"</code> + */ + public static final String FIND_NEXT= "org.eclipse.ui.edit.findNext"; //$NON-NLS-1$ + /** + * Action definition id of the edit find previous action. + * Value: <code>"org.eclipse.ui.edit.findPrevious"</code> + */ + public static final String FIND_PREVIOUS= "org.eclipse.ui.edit.findPrevious"; //$NON-NLS-1$ + /** + * Action definition id of the edit incremental find action. + * Value: <code>"org.eclipse.ui.edit.findIncremental"</code> + */ + public static final String FIND_INCREMENTAL= "org.eclipse.ui.edit.findIncremental"; //$NON-NLS-1$ +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IncrementalFindAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IncrementalFindAction.java new file mode 100644 index 00000000000..354a65298ed --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IncrementalFindAction.java @@ -0,0 +1,101 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import java.util.ResourceBundle; + +import org.eclipse.jface.text.IFindReplaceTarget; +import org.eclipse.jface.text.IFindReplaceTargetExtension; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; + + +/** + * An action which enters the incremental find mode a la emacs. + * <p> + * This class may be instantiated; it is not intended to be subclassed. + * </p> + * @since 2.0 + */ +public class IncrementalFindAction extends ResourceAction implements IUpdate { + + /** The action's target */ + private IFindReplaceTarget fTarget; + /** The part the action is bound to */ + private IWorkbenchPart fWorkbenchPart; + /** The workbench window */ + private IWorkbenchWindow fWorkbenchWindow; + + /** + * Creates a new incremental find action for the given text editor. + * The action configures its visual representation from the given + * resource bundle. + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys + * (described in <code>ResourceAction</code> constructor), or + * <code>null</code> if none + * @param editor the text editor + * @see ResourceAction#ResourceAction + */ + public IncrementalFindAction(ResourceBundle bundle, String prefix, IWorkbenchPart workbenchPart) { + super(bundle, prefix); + fWorkbenchPart= workbenchPart; + update(); + } + + /** + * Creates a new incremental find action for the given text editor. + * The action configures its visual representation from the given + * resource bundle. + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys + * (described in <code>ResourceAction</code> constructor), or + * <code>null</code> if none + * @param workbenchWindow the workbench window + * @see ResourceAction#ResourceAction + * + * @deprecated use FindReplaceAction(ResourceBundle, String, IWorkbenchPart) instead + */ + public IncrementalFindAction(ResourceBundle bundle, String prefix, IWorkbenchWindow workbenchWindow) { + super(bundle, prefix); + fWorkbenchWindow= workbenchWindow; + update(); + } + + /* + * @see IAction#run + */ + public void run() { + if (fTarget != null && fTarget instanceof IFindReplaceTargetExtension) + ((IFindReplaceTargetExtension) fTarget).beginSession(); + } + + /* + * @see IUpdate#update() + */ + public void update() { + + if (fWorkbenchPart == null && fWorkbenchWindow != null) + fWorkbenchPart= fWorkbenchWindow.getPartService().getActivePart(); + + if (fWorkbenchPart != null) + fTarget= (IFindReplaceTarget) fWorkbenchPart.getAdapter(IncrementalFindTarget.class); + else + fTarget= null; + + setEnabled(fTarget != null && fTarget.canPerformFind()); + } + +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IncrementalFindTarget.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IncrementalFindTarget.java new file mode 100644 index 00000000000..45ff4a807da --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IncrementalFindTarget.java @@ -0,0 +1,577 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + +import java.text.MessageFormat; +import java.util.Stack; + +import org.eclipse.jface.action.IStatusLineManager; +import org.eclipse.jface.text.IFindReplaceTarget; +import org.eclipse.jface.text.IFindReplaceTargetExtension; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextListener; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.ITextViewerExtension; +import org.eclipse.jface.text.TextEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.custom.VerifyKeyListener; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.VerifyEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Point; + +/** + * An incremental find target. Replace is always disabled. + * @since 2.0 + */ +class IncrementalFindTarget implements IFindReplaceTarget, IFindReplaceTargetExtension, VerifyKeyListener, MouseListener, FocusListener, ITextListener { + + /** The string representing rendered tab */ + private final static String TAB= EditorMessages.getString("Editor.FindIncremental.render.tab"); //$NON-NLS-1$ + + /** The text viewer to operate on */ + private final ITextViewer fTextViewer; + /** The status line manager for output */ + private final IStatusLineManager fStatusLine; + /** The find replace target to delegate find requests */ + private final IFindReplaceTarget fTarget; + + /** The current find string */ + private StringBuffer fFindString= new StringBuffer(); + /** The position to start the find from */ + private int fBasePosition; + /** The position of the first upper case character, -1 if none */ + private int fCasePosition; + /** The position of the last successful find */ + private int fCurrentIndex; + /** A flag indicating if last find was successful */ + private boolean fFound; + /** A flag indicating listeners are installed. */ + private boolean fInstalled; + /** The current find stack */ + private Stack fSessionStack; + /** A constant representing a find-next operation */ + private static final Object NEXT = new Object(); + /** A constant representing a find-previous operation */ + private static final Object PREVIOUS = new Object(); + /** A constant representing adding a character to the find pattern */ + private static final Object CHAR = new Object(); + /** A constant representing a wrap operation */ + private static final Object WRAPPED = new Object(); + + /** + * Creates an instance of an incremental find target. + * + * @param viewer the text viewer to operate on + * @param manager the status line manager for output + */ + public IncrementalFindTarget(ITextViewer viewer, IStatusLineManager manager) { + fTextViewer= viewer; + fStatusLine= manager; + fTarget= viewer.getFindReplaceTarget(); + } + + /* + * @see IFindReplaceTarget#canPerformFind() + */ + public boolean canPerformFind() { + return fTarget.canPerformFind(); + } + + /* + * @see IFindReplaceTarget#findAndSelect(int, String, boolean, boolean, boolean) + */ + public int findAndSelect(int offset, String findString, boolean searchForward, boolean caseSensitive, boolean wholeWord) { + return fTarget.findAndSelect(offset, findString, searchForward, caseSensitive, wholeWord); + } + + /* + * @see IFindReplaceTarget#getSelection() + */ + public Point getSelection() { + return fTarget.getSelection(); + } + + /* + * @see IFindReplaceTarget#getSelectionText() + */ + public String getSelectionText() { + return fTarget.getSelectionText(); + } + + /* + * @see IFindReplaceTarget#isEditable() + */ + public boolean isEditable() { + return false; + } + + /* + * @see IFindReplaceTarget#replaceSelection(String) + */ + public void replaceSelection(String text) { + } + + /* + * @see IFindReplaceTargetExtension#beginSession() + */ + public void beginSession() { + fFindString.setLength(0); + fSessionStack = new Stack(); + fCasePosition= -1; + fBasePosition= fTarget.getSelection().x; + fCurrentIndex= fBasePosition; + fFound= true; + + install(); + performFindNext(true, fBasePosition, false, false); + updateStatus(fFound); + + if (fTarget instanceof IFindReplaceTargetExtension) + ((IFindReplaceTargetExtension) fTarget).beginSession(); + } + + /* + * @see IFindReplaceTargetExtension#endSession() + */ + public void endSession() { + if (fTarget instanceof IFindReplaceTargetExtension) + ((IFindReplaceTargetExtension) fTarget).endSession(); + + // will uninstall itself + } + + /* + * @see IFindReplaceTargetExtension#getScope() + */ + public IRegion getScope() { + return null; + } + + /* + * @see IFindReplaceTargetExtension#setGlobal(boolean) + */ + public void setGlobal(boolean global) { + } + + /* + * @see IFindReplaceTargetExtension#setScope(IRegion) + */ + public void setScope(IRegion scope) { + } + + /* + * @see IFindReplaceTargetExtension#setReplaceAllMode(boolean) + */ + public void setReplaceAllMode(boolean replaceAll) { + } + + /** + * Installs this target. I.e. adds all required listeners. + */ + private void install() { + if (fInstalled) + return; + + StyledText text= fTextViewer.getTextWidget(); + if (text == null) + return; + + text.addMouseListener(this); + fTextViewer.addTextListener(this); + + if (fTextViewer instanceof ITextViewerExtension) { + ITextViewerExtension e= (ITextViewerExtension) fTextViewer; + e.prependVerifyKeyListener(this); + } else { + text.addVerifyKeyListener(this); + } + + fInstalled= true; + } + + /** + * Uninstalls itself. I.e. removes all listeners installed in <code>install</code>. + */ + private void uninstall() { + fTextViewer.removeTextListener(this); + + StyledText text= fTextViewer.getTextWidget(); + text.removeMouseListener(this); + + if (fTextViewer instanceof ITextViewerExtension) { + ITextViewerExtension e= (ITextViewerExtension) fTextViewer; + e.removeVerifyKeyListener(this); + } else { + text.removeVerifyKeyListener(this); + } + + fInstalled= false; + } + + /** + * Returns whether the find string can be found using the given options. + * + * @param forward the search direction + * @param position the start offset + * @param wrapSearch should the search wrap to start/end if end/start is reached + * @param takeBack is the find string shortend + * @return <code>true</code> if find string can be found using the options + */ + private boolean performFindNext(boolean forward, int position, boolean wrapSearch, boolean takeBack) { + String string= fFindString.toString(); + + int index; + if (string.length() == 0) { + + // workaround for empty selection in target + fTextViewer.setSelectedRange(fBasePosition + fTextViewer.getVisibleRegion().getOffset(), 0); + index= fBasePosition; + + } else { + + if (!forward) + position--; + + index= findIndex(string, position, forward, fCasePosition != -1, wrapSearch, takeBack); + } + + boolean found = (index != -1); + if (found) fCurrentIndex = index; + + return found; + } + + /** + * Updates the status line appropriate for the indicated success. + * @param found the success + */ + private void updateStatus(boolean found) { + if (fSessionStack == null) return; + + String string= fFindString.toString(); + String prefix = ""; //$NON-NLS-1$ + if (fSessionStack.search(WRAPPED) != -1) { + prefix = EditorMessages.getString("Editor.FindIncremental.wrapped"); //$NON-NLS-1$ + } + if (!found) { + String pattern= EditorMessages.getString("Editor.FindIncremental.not_found.pattern"); //$NON-NLS-1$ + statusError(MessageFormat.format(pattern, new Object[] { prefix, string })); + + } else { + String pattern= EditorMessages.getString("Editor.FindIncremental.found.pattern"); //$NON-NLS-1$ + statusMessage(MessageFormat.format(pattern, new Object[] { prefix, string })); + } + } + + /** + * Retuns the offset at which the search string is found next using the given options + * @param findString the string to search for + * @param startPosition the start offset + * @param forward the search direction + * @param caseSensitive is the search case sensitive + * @param wrapSearch should the search wrap to start/end if end/start is reached + * @param takeBack is the find string shortend + * @return the offset of the next match or <code>-1</code> + */ + private int findIndex(String findString, int startPosition, boolean forwardSearch, boolean caseSensitive, boolean wrapSearch, boolean takeBack) { + + if (fTarget == null) + return -1; + + int startIndex = (forwardSearch) ? startPosition : startPosition - 1; + int index= fTarget.findAndSelect(startIndex, findString, forwardSearch, caseSensitive, false); + + if (index != -1) { + fFound = true; + return index; + } + + if (fFound) { + // beep once + StyledText text= fTextViewer.getTextWidget(); + if (!takeBack && text != null && !text.isDisposed()) + text.getDisplay().beep(); + } + + if (!wrapSearch || (fFound && !takeBack)) { + fFound = false; + return index; + } + + index = fTarget.findAndSelect(-1, findString, forwardSearch, caseSensitive, false); + fFound = (index != -1); + if (!takeBack && fFound) wrap(); + return index; + } + + /** + * Remembers wrapping in the session stack. + */ + private void wrap() { + fSessionStack.push(WRAPPED); + } + + /* + * @see VerifyKeyListener#verifyKey(VerifyEvent) + */ + public void verifyKey(VerifyEvent event) { + if (!event.doit) + return; + + if (event.character == 0) { + + switch (event.keyCode) { + + // ALT, CTRL, ARROW_LEFT, ARROW_RIGHT == leave + case SWT.ALT: + case SWT.CTRL: + case SWT.ARROW_LEFT: + case SWT.ARROW_RIGHT: + case SWT.HOME: + case SWT.END: + case SWT.PAGE_DOWN: + case SWT.PAGE_UP: + leave(); + break; + + // find next + case SWT.ARROW_DOWN: + if (performFindNext(true, fTarget.getSelection().x + fTarget.getSelection().y, true, false)) + fSessionStack.push(NEXT); + event.doit= false; + break; + + // find previous + case SWT.ARROW_UP: + if (performFindNext(false, fTarget.getSelection().x, true, false)) + fSessionStack.push(PREVIOUS); + + if (fCurrentIndex != -1 && fCurrentIndex < fBasePosition) + fBasePosition= fCurrentIndex; + + event.doit= false; + break; + } + + // event.character != 0 + } else { + + switch (event.character) { + + // ESC, CR = quit + case 0x1B: + case 0x0D: + leave(); + event.doit= false; + break; + + // backspace and delete + case 0x08: + case 0x7F: + { + if (fSessionStack.empty()) { + StyledText text= fTextViewer.getTextWidget(); + if (text != null && !text.isDisposed()) + text.getDisplay().beep(); + event.doit= false; + break; + } + + Object last = popSessionStack(); + while (!fSessionStack.empty() && fSessionStack.peek() == WRAPPED) + popSessionStack(); + + // Last event repeated a search + if (last == PREVIOUS) { + performFindNext(true, fTarget.getSelection().x + fTarget.getSelection().y, true, true); + } else if (last == NEXT) { + performFindNext(false, fTarget.getSelection().x, true, true); + + if (fCurrentIndex != -1 && fCurrentIndex < fBasePosition) + fBasePosition= fCurrentIndex; + } else if (last == CHAR) { + // Last event added a character + performFindNext(true, fBasePosition, true, true); + } + event.doit= false; + } + break; + + default: + if (event.stateMask == 0 || event.stateMask == SWT.SHIFT) { + + pushChar(event.character); + performFindNext(true, fBasePosition, false, false); + event.doit= false; + } + break; + } + } + updateStatus(fFound); + } + + + /** + * Extends the incremental search by the given character. + * @param character the character to append to the searhc string + */ + private void pushChar(char character) { + if (fCasePosition == -1 && Character.isUpperCase(character) && Character.toLowerCase(character) != character) + fCasePosition= fFindString.length(); + fFindString.append(character); + fSessionStack.push(CHAR); + } + + /** + * Pops the top element from the session stack. + * @return the top element from the session stack + */ + private Object popSessionStack() { + Object o = fSessionStack.pop(); + if (o == CHAR) { + int newLength = fFindString.length() -1; + fFindString.deleteCharAt(newLength); + if (fCasePosition == newLength) + fCasePosition= -1; + } + return o; + } + + /** + * Leaves this incremental search session. + */ + private void leave() { + statusClear(); + uninstall(); + fSessionStack = null; + } + + /* + * @see ITextListener#textChanged(TextEvent) + */ + public void textChanged(TextEvent event) { + leave(); + } + + /* + * @see MouseListener#mouseDoubleClick(MouseEvent) + */ + public void mouseDoubleClick(MouseEvent e) { + leave(); + } + + /* + * @see MouseListener#mouseDown(MouseEvent) + */ + public void mouseDown(MouseEvent e) { + leave(); + } + + /* + * @see MouseListener#mouseUp(MouseEvent) + */ + public void mouseUp(MouseEvent e) { + leave(); + } + + /* + * @see FocusListener#focusGained(FocusEvent) + */ + public void focusGained(FocusEvent e) { + leave(); + } + + /* + * @see FocusListener#focusLost(FocusEvent) + */ + public void focusLost(FocusEvent e) { + leave(); + } + + /** + * Sets the given string as status message, clears the status error message. + * @param string the status message + */ + private void statusMessage(String string) { + fStatusLine.setErrorMessage(""); //$NON-NLS-1$ + fStatusLine.setMessage(escapeTabs(string)); + } + + /** + * Sets the status error message, clears the status message. + * @param string the status error message + */ + private void statusError(String string) { + fStatusLine.setErrorMessage(escapeTabs(string)); + fStatusLine.setMessage(""); //$NON-NLS-1$ + } + + /** + * Clears the status message and the status error message. + */ + private void statusClear() { + fStatusLine.setErrorMessage(""); //$NON-NLS-1$ + fStatusLine.setMessage(""); //$NON-NLS-1$ + } + + /** + * Translates all tab characters into a proper status line presentation. + * @param string the string in which to translate the tabs + * @return the given string with all tab characters replace with a proper status line presentation + */ + private String escapeTabs(String string) { + StringBuffer buffer= new StringBuffer(); + + int begin= 0; + int end= string.indexOf('\t', begin); + + while (end >= 0) { + buffer.append(string.substring(begin, end)); + buffer.append(TAB); + begin= end + 1; + end= string.indexOf('\t', begin); + } + buffer.append(string.substring(begin)); + + return buffer.toString(); + } + + /* + * @see IFindReplaceTargetExtension#getLineSelection() + */ + public Point getLineSelection() { + if (fTarget instanceof IFindReplaceTargetExtension) + return ((IFindReplaceTargetExtension) fTarget).getLineSelection(); + + return null; // XXX should not return null + } + + /* + * @see IFindReplaceTargetExtension#setSelection(int, int) + */ + public void setSelection(int offset, int length) { + if (fTarget instanceof IFindReplaceTargetExtension) + ((IFindReplaceTargetExtension) fTarget).setSelection(offset, length); + } + + /* + * @see IFindReplaceTargetExtension#setScopeHighlightColor(Color) + */ + public void setScopeHighlightColor(Color color) { + } + +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/InfoForm.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/InfoForm.java new file mode 100644 index 00000000000..1f7362b4953 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/InfoForm.java @@ -0,0 +1,227 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + + +package org.eclipse.ui.texteditor; + + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; + +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; + + +/** + * A form consisting of a title, a banner, and a info text. Banner and info text are + * separated by a separator line. This form must be handled like a SWT widget. + * @since 2.0 + */ +public class InfoForm { + + /** The form's root widget */ + private ScrolledComposite fScrolledComposite; + /** The background color */ + private Color fBackgroundColor; + /** The foreground color */ + private Color fForegroundColor; + /** The separator's color */ + private Color fSeparatorColor; + /** The form header */ + private Label fHeader; + /** The form banner */ + private Label fBanner; + /** The form text */ + private Label fText; + /** The preference change listener */ + private IPropertyChangeListener fPropertyChangeListener; + + /** + * Creates a new info form. + * @param parent the parent + */ + public InfoForm(Composite parent) { + + Display display= parent.getDisplay(); + fBackgroundColor= display.getSystemColor(SWT.COLOR_LIST_BACKGROUND); + fForegroundColor= display.getSystemColor(SWT.COLOR_LIST_FOREGROUND); + fSeparatorColor= new Color(display, 152, 170, 203); + + fPropertyChangeListener= new IPropertyChangeListener() { + public void propertyChange(PropertyChangeEvent event) { + handlePropertyChange(event); + } + }; + JFaceResources.getFontRegistry().addListener(fPropertyChangeListener); + + fScrolledComposite= new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL); + fScrolledComposite.setAlwaysShowScrollBars(false); + fScrolledComposite.setExpandHorizontal(true); + fScrolledComposite.setExpandVertical(true); + fScrolledComposite.addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + JFaceResources.getFontRegistry().removeListener(fPropertyChangeListener); + fScrolledComposite= null; + fSeparatorColor.dispose(); + fSeparatorColor= null; + fHeader= null; + fBanner= null; + fText= null; + } + }); + + Composite composite= createComposite(fScrolledComposite); + composite.setLayout(new GridLayout()); + + fHeader= createHeader(composite, null); + createLabel(composite, null); + createLabel(composite, null); + + fBanner= createBanner(composite, null); + + Composite separator= createCompositeSeparator(composite); + GridData data= new GridData(GridData.FILL_HORIZONTAL); + data.heightHint= 2; + separator.setLayoutData(data); + + fText= createLabel(composite, null); + createLabel(composite, null); + + fScrolledComposite.setContent(composite); + fScrolledComposite.setMinSize(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT)); + + createActionControls(composite); + } + + /** + * Hook method for creating an appropriate action control. + * @param parent the action control's parent control + */ + protected void createActionControls(Composite parent) { + } + + /** + * Returns the control of this form. + * @return the root control of this form + */ + public Control getControl() { + return fScrolledComposite; + } + + /** + * Sets the header text of this info form. + * @param header the header text + */ + public void setHeaderText(String header) { + fHeader.setText(header); + } + + /** + * Sets the banner text of this info form. + * @param banner the banner text + */ + public void setBannerText(String banner) { + fBanner.setText(banner); + } + + /** + * Sets the info of this info form + * @param info the info text + */ + public void setInfo(String info) { + fText.setText(info); + } + + /* + * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent) + */ + protected void handlePropertyChange(PropertyChangeEvent event) { + + if (fHeader != null) + fHeader.setFont(JFaceResources.getHeaderFont()); + + if (fBanner != null) + fBanner.setFont(JFaceResources.getBannerFont()); + + Control control= fScrolledComposite.getContent(); + fScrolledComposite.setMinSize(control.computeSize(SWT.DEFAULT, SWT.DEFAULT)); + fScrolledComposite.setContent(control); + + fScrolledComposite.layout(true); + fScrolledComposite.redraw(); + } + + /* + * @see org.eclipse.update.ui.forms.internal.FormWidgetFactory#createComposite(Composite) + */ + private Composite createComposite(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + composite.setBackground(fBackgroundColor); + return composite; + } + + /* + * @see org.eclipse.update.ui.forms.internal.FormWidgetFactory#createCompositeSeparator(Composite) + */ + private Composite createCompositeSeparator(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + composite.setBackground(fSeparatorColor); + return composite; + } + + /* + * @see org.eclipse.update.ui.forms.internal.FormWidgetFactory#createLabel(Composite, String) + */ + private Label createLabel(Composite parent, String text) { + Label label = new Label(parent, SWT.NONE); + if (text != null) + label.setText(text); + label.setBackground(fBackgroundColor); + label.setForeground(fForegroundColor); + return label; + } + + /* + * @see org.eclipse.update.ui.forms.internal.FormWidgetFactory#createHeader(Composite, String) + */ + private Label createHeader(Composite parent, String text) { + Label label = new Label(parent, SWT.NONE); + if (text != null) + label.setText(text); + label.setBackground(fBackgroundColor); + label.setForeground(fForegroundColor); + label.setFont(JFaceResources.getHeaderFont()); + return label; + } + + /* + * @see org.eclipse.update.ui.forms.internal.FormWidgetFactory#createBanner(Composite, String) + */ + private Label createBanner(Composite parent, String text) { + Label label = new Label(parent, SWT.NONE); + if (text != null) + label.setText(text); + label.setBackground(fBackgroundColor); + label.setForeground(fForegroundColor); + label.setFont(JFaceResources.getBannerFont()); + return label; + } +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkAction.java new file mode 100644 index 00000000000..84d63b6fcdc --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkAction.java @@ -0,0 +1,73 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import java.util.ResourceBundle; + +import org.eclipse.jface.text.IMarkRegionTarget; + +/** + * An action to handle emacs-like marked regions. + * @since 2.0 + */ +public class MarkAction extends TextEditorAction { + + /** Sets the mark. */ + public static final int SET_MARK= 0; + /** Clears the mark. */ + public static final int CLEAR_MARK= 1; + /** Swaps the mark and the cursor position. */ + public static final int SWAP_MARK= 2; + + /** The mark action type. */ + private final int fType; + + /** + * Constructor for MarkAction. + * + * @param type the mark action type, must be one of + * <code>SET_MARK</code>, <code>CLEAR_MARK</code> or <code>SWAP_MARK</code>. + */ + public MarkAction(ResourceBundle bundle, String prefix, ITextEditor editor, int type) { + super(bundle, prefix, editor); + fType= type; + } + + /* + * @see IAction#run() + */ + public void run() { + + ITextEditor editor= getTextEditor(); + if (editor == null) + return; + + IMarkRegionTarget target= (IMarkRegionTarget) editor.getAdapter(IMarkRegionTarget.class); + if (target == null) + return; + + switch (fType) { + case SET_MARK: + target.setMarkAtCursor(true); + break; + + case CLEAR_MARK: + target.setMarkAtCursor(false); + break; + + case SWAP_MARK: + target.swapMarkAndCursor(); + break; + } + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkRegionTarget.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkRegionTarget.java new file mode 100644 index 00000000000..369f54184b7 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkRegionTarget.java @@ -0,0 +1,107 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import org.eclipse.swt.graphics.Point; + +import org.eclipse.jface.action.IStatusLineManager; +import org.eclipse.jface.text.IMarkRegionTarget; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.ITextViewerExtension; + +/** + * Default implementation of <code>IMarkRegionTarget</code> using <code>ITextViewer</code> + * and <code>IStatusLineManager</code>. + * @since 2.0 + */ +public class MarkRegionTarget implements IMarkRegionTarget { + + /** The text viewer. */ + private final ITextViewer fViewer; + /** The status line. */ + private final IStatusLineManager fStatusLine; + + /** + * Creates a MarkRegionTaret. + * + * @param viewer the text viewer + * @param manager the status line manager + */ + public MarkRegionTarget(ITextViewer viewer, IStatusLineManager manager) { + fViewer= viewer; + fStatusLine= manager; + } + + /* + * @see IMarkregion#setMarkAtCursor(boolean) + */ + public void setMarkAtCursor(boolean set) { + + if (!(fViewer instanceof ITextViewerExtension)) + return; + + ITextViewerExtension viewerExtension= ((ITextViewerExtension) fViewer); + + if (set) { + Point selection= fViewer.getSelectedRange(); + viewerExtension.setMark(selection.x); + + fStatusLine.setErrorMessage(""); //$NON-NLS-1$ + fStatusLine.setMessage(EditorMessages.getString("Editor.mark.status.message.mark.set")); //$NON-NLS-1$ + + } else { + viewerExtension.setMark(-1); + + fStatusLine.setErrorMessage(""); //$NON-NLS-1$ + fStatusLine.setMessage(EditorMessages.getString("Editor.mark.status.message.mark.cleared")); //$NON-NLS-1$ + } + } + + /* + * @see IMarkregion#swapMarkAndCursor() + */ + public void swapMarkAndCursor() { + + if (!(fViewer instanceof ITextViewerExtension)) + return; + + ITextViewerExtension viewerExtension= ((ITextViewerExtension) fViewer); + + int markPosition= viewerExtension.getMark(); + if (markPosition == -1) { + fStatusLine.setErrorMessage("mark not set"); + fStatusLine.setMessage(""); //$NON-NLS-1$ + return; + } + + IRegion region= fViewer.getVisibleRegion(); + int offset= region.getOffset(); + int length= region.getLength(); + + if (markPosition < offset || markPosition > offset + length) { + fStatusLine.setErrorMessage("mark not in visible region"); + fStatusLine.setMessage(""); //$NON-NLS-1$ + return; + } + + Point selection= fViewer.getSelectedRange(); + viewerExtension.setMark(selection.x); + + fViewer.setSelectedRange(markPosition, 0); + fViewer.revealRange(markPosition, 0); + + fStatusLine.setErrorMessage(""); //$NON-NLS-1$ + fStatusLine.setMessage(EditorMessages.getString("Editor.mark.status.message.mark.swapped")); //$NON-NLS-1$ + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkerAnnotation.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkerAnnotation.java new file mode 100644 index 00000000000..c531c2a39ac --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkerAnnotation.java @@ -0,0 +1,262 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Display; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.text.source.Annotation; + +import org.eclipse.core.resources.IMarker; + +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.model.IWorkbenchAdapter; + + +/** + * Annotation representing a marker on a resource in the workspace. + * This class may be instantiated or be subclassed. + * + * @see IMarker + */ +public class MarkerAnnotation extends Annotation { + + /** + * The layer in which markers representing problem are located. + * @since 2.0 + */ + public final static int PROBLEM_LAYER= 5; + + /** Internal image registry */ + private static Map fgImageRegistry; + + /** + * Returns an image for the given display as specified by the given image descriptor. + * @param display the display + * @param descriptor the image descriptor + * @return an image for the display as specified by the descriptor + */ + protected static Image getImage(Display display, ImageDescriptor descriptor) { + Map map= getImageRegistry(display); + Image image= (Image) map.get(descriptor); + if (image == null) { + image= descriptor.createImage(); + map.put(descriptor, image); + } + return image; + } + + /** + * Returns an image registry for the given display. If no such registry exists + * the resgitry is created. + * @param display the display + * @return the image registry for the given display + */ + protected static Map getImageRegistry(Display display) { + if (fgImageRegistry == null) { + fgImageRegistry= new HashMap(); + display.disposeExec(new Runnable() { + public void run() { + if (fgImageRegistry != null) { + Map map= fgImageRegistry; + fgImageRegistry= null; + Iterator e= map.values().iterator(); + while (e.hasNext()) { + Image image= (Image) e.next(); + if (!image.isDisposed()) + image.dispose(); + } + } + } + }); + } + return fgImageRegistry; + } + + + /** The marker this annotation represents */ + private IMarker fMarker; + /** The image, i.e., visual presentation of this annotation */ + private Image fImage; + /** The image name */ + private String fImageName; + + /** + * Creates a new annotation for the given marker. + * + * @param marker the marker + */ + public MarkerAnnotation(IMarker marker) { + fMarker= marker; + initialize(); + } + + /** + * Sets the marker image to the given image. + * + * @param image the new marker image + */ + protected void setImage(Image image) { + fImage= image; + } + + /** + * The <code>MarkerAnnotation</code> implementation of this + * <code>Object</code> method returns <code>true</code> iff the other + * object is also a <code>MarkerAnnotation</code> and the marker handles are + * equal. + */ + public boolean equals(Object o) { + if (o != null && o.getClass() == getClass()) + return fMarker.equals(((MarkerAnnotation) o).fMarker); + return false; + } + + /* + * @see Object#hashCode + */ + public int hashCode() { + return fMarker.hashCode(); + } + + /** + * Returns this annotation's underlying marker. + * + * @return the marker + */ + public IMarker getMarker() { + return fMarker; + } + + /** + * Initializes the annotation's icon representation and its drawing layer + * based upon the properties of the underlying marker. + */ + protected void initialize() { + + String name= null; + int layer= -1; + + if (MarkerUtilities.isMarkerType(fMarker, IMarker.TASK)) { + name= ISharedImages.IMG_OBJS_TASK_TSK; + layer= 1; + } else if (MarkerUtilities.isMarkerType(fMarker, IMarker.BOOKMARK)) { + name= ISharedImages.IMG_OBJS_BKMRK_TSK; + layer= 2; + } else if (MarkerUtilities.isMarkerType(fMarker, IMarker.PROBLEM)) { + switch (fMarker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO)) { + case IMarker.SEVERITY_INFO: + name= ISharedImages.IMG_OBJS_INFO_TSK; + layer= 3; + break; + case IMarker.SEVERITY_WARNING: + layer= 3; + name= ISharedImages.IMG_OBJS_WARN_TSK; + break; + case IMarker.SEVERITY_ERROR: + layer= PROBLEM_LAYER; + name= ISharedImages.IMG_OBJS_ERROR_TSK; + break; + }; + } else { + name= getUnknownImageName(fMarker); + layer= 1; + } + + // setImage(getImage(name)); + fImage= null; + fImageName= name; + setLayer(layer); + } + + /* + * @see Annotation#paint + */ + public void paint(GC gc, Canvas canvas, Rectangle r) { + Image image= getImage(canvas.getDisplay()); + if (image != null) { + // http://dev.eclipse.org/bugs/show_bug.cgi?id=19184 + drawImage(image, gc, canvas, r, SWT.CENTER, SWT.TOP); + } + } + + /** + * Informs this annotation about changes applied to its underlying marker + * and adapts to those changes. + */ + public void update() { + initialize(); + } + + /** + * Returns the name of an image used to visually represent markers of + * unknown type. This implementation returns <code>null</code>. + * Subclasses may replace this method. + * + * @param marker the marker of unkown type + * @return the name of an image for markers of unknown type. + */ + protected String getUnknownImageName(IMarker marker) { + return null; + } + + /** + * Returns the image of the given name. Subclasses may extend this method. + * If so, subclasses must assume responsibility for disposing the images + * they create. + * + * @param name the name of the requested image + * @return the image or <code>null</code> if there is no such image + */ + protected Image getImage(String name) { + if (name != null) + return PlatformUI.getWorkbench().getSharedImages().getImage(name); + return null; + } + + + /** + * Returns an image for this annotation. It first consults the workbench adapter + * for this annotation's marker. If none is defined, it tries to find an image for + * the image name of this annotation. + * + * @param display the display for which the image is requested + * @return the image for this annotation + */ + protected Image getImage(Display display) { + if (fImage == null) { + + IWorkbenchAdapter adapter= (IWorkbenchAdapter) fMarker.getAdapter(IWorkbenchAdapter.class); + if (adapter != null) { + ImageDescriptor descriptor= adapter.getImageDescriptor(fMarker); + if (descriptor != null) + fImage= getImage(display, descriptor); + } + + if (fImage == null) + fImage= getImage(fImageName); + + } + return fImage; + } +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkerRulerAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkerRulerAction.java new file mode 100644 index 00000000000..a92c57bd309 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkerRulerAction.java @@ -0,0 +1,417 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.ResourceBundle; + +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRunnable; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; + +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.dialogs.IInputValidator; +import org.eclipse.jface.dialogs.InputDialog; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.jface.text.source.IVerticalRuler; +import org.eclipse.jface.text.source.IVerticalRulerInfo; +import org.eclipse.jface.window.Window; + +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.PlatformUI; + + + +/** + * A ruler action which can add and remove markers which have a visual + * representation in the ruler. + * <p> + * This class may be instantiated but is not intended for subclassing. + * </p> + */ +public class MarkerRulerAction extends ResourceAction implements IUpdate { + + /** The vertical ruler info of the editor */ + private IVerticalRulerInfo fRuler; + /** The associated editor */ + private ITextEditor fTextEditor; + /** The of the marker to be created/removed */ + private String fMarkerType; + /** The cached list of markers covering a particular vertical ruler position */ + private List fMarkers; + /** The flag indicating whether user interaction is required. */ + private boolean fAskForLabel; + /** The action's resource bundle */ + private ResourceBundle fBundle; + /** The prefix used for resource bundle look ups */ + private String fPrefix; + /** The cached action label when adding a marker */ + private String fAddLabel; + /** The cached action label when removing a marker */ + private String fRemoveLabel; + + + /** + * Creates a new action for the given ruler and editor. The action configures + * its visual representation from the given resource bundle. + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys + * (described in <code>ResourceAction</code> constructor), or <code>null</code> if none + * @param editor the editor + * @param ruler the ruler + * @param markerType the type of marker + * @param askForLabel <code>true</code> if the user should be asked for a label when a new marker is created + * @see ResourceAction#ResourceAction + * @since 2.0 + */ + public MarkerRulerAction(ResourceBundle bundle, String prefix, ITextEditor editor, IVerticalRulerInfo ruler, String markerType, boolean askForLabel) { + super(bundle, prefix); + fRuler= ruler; + fTextEditor= editor; + fMarkerType= markerType; + fAskForLabel= askForLabel; + + fBundle= bundle; + fPrefix= prefix; + + fAddLabel= getString(bundle, prefix + "add.label", prefix + "add.label"); //$NON-NLS-2$ //$NON-NLS-1$ + fRemoveLabel= getString(bundle, prefix + "remove.label", prefix + "remove.label"); //$NON-NLS-2$ //$NON-NLS-1$ + } + + /** + * @deprecated use <code>MarkerRulerAction(ResourceBundle, String, ITextEditor, IVerticalRulerInfo, String, boolean)</code> instead + */ + public MarkerRulerAction(ResourceBundle bundle, String prefix, IVerticalRuler ruler, ITextEditor editor, String markerType, boolean askForLabel) { + this(bundle, prefix, editor, ruler, markerType, askForLabel); + } + + + /** + * Returns this action's editor. + * + * @return this action's editor + */ + protected ITextEditor getTextEditor() { + return fTextEditor; + } + + /** + * Returns this action's vertical ruler. + * + * @return this action's vertical ruler + * @deprecated use <code>getVerticalRulerInfo</code> instead + */ + protected IVerticalRuler getVerticalRuler() { + if (fRuler instanceof IVerticalRuler) + return (IVerticalRuler) fRuler; + return null; + } + + /** + * Returns this action's vertical ruler info. + * + * @return this action's vertical ruler info + * @since 2.0 + */ + protected IVerticalRulerInfo getVerticalRulerInfo() { + return fRuler; + } + + /** + * Returns this action's resource bundle. + * + * @return this action's resource bundle + */ + protected ResourceBundle getResourceBundle() { + return fBundle; + } + + /** + * Returns this action's resource key prefix. + * + * @return this action's resource key prefix + */ + protected String getResourceKeyPrefix() { + return fPrefix; + } + + /* + * @see IUpdate#update() + */ + public void update() { + fMarkers= getMarkers(); + setText(fMarkers.isEmpty() ? fAddLabel : fRemoveLabel); + } + + /* + * @see Action#run() + */ + public void run() { + if (fMarkers.isEmpty()) + addMarker(); + else + removeMarkers(fMarkers); + } + + /** + * Returns the resource for which to create the marker, + * or <code>null</code> if there is no applicable resource. + * + * @return the resource for which to create the marker or <code>null</code> + */ + protected IResource getResource() { + IEditorInput input= fTextEditor.getEditorInput(); + + IResource resource= (IResource) input.getAdapter(IFile.class); + + if (resource == null) + resource= (IResource) input.getAdapter(IResource.class); + + return resource; + } + + /** + * Returns the <code>AbstractMarkerAnnotationModel</code> of the editor's input. + * + * @return the marker annotation model + */ + protected AbstractMarkerAnnotationModel getAnnotationModel() { + IDocumentProvider provider= fTextEditor.getDocumentProvider(); + IAnnotationModel model= provider.getAnnotationModel(fTextEditor.getEditorInput()); + if (model instanceof AbstractMarkerAnnotationModel) + return (AbstractMarkerAnnotationModel) model; + return null; + } + + /** + * Returns the <code>IDocument</code> of the editor's input. + * + * @return the document of the editor's input + */ + protected IDocument getDocument() { + IDocumentProvider provider= fTextEditor.getDocumentProvider(); + return provider.getDocument(fTextEditor.getEditorInput()); + } + + /** + * Checks whether a position includes the ruler's line of activity. + * + * @param position the position to be checked + * @param document the document the position refers to + * @return <code>true</code> if the line is included by the given position + */ + protected boolean includesRulerLine(Position position, IDocument document) { + + if (position != null) { + try { + int markerLine= document.getLineOfOffset(position.getOffset()); + int line= fRuler.getLineOfLastMouseButtonActivity(); + if (line == markerLine) + return true; + // commented because of "1GEUOZ9: ITPJUI:ALL - Confusing UI for multiline Bookmarks and Tasks" + // return (markerLine <= line && line <= document.getLineOfOffset(position.getOffset() + position.getLength())); + } catch (BadLocationException x) { + } + } + + return false; + } + + /** + * Handles core exceptions. This implementation logs the exceptions + * with the workbech plugin and shows an error dialog. + * + * @param exception the exception to be handled + * @param message the message to be logged with the given exception + */ + protected void handleCoreException(CoreException exception, String message) { + ILog log= Platform.getPlugin(PlatformUI.PLUGIN_ID).getLog(); + + if (message != null) + log.log(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, 0, message, null)); + + log.log(exception.getStatus()); + + + Shell shell= getTextEditor().getSite().getShell(); + String title= getString(fBundle, fPrefix + "error.dialog.title", fPrefix + "error.dialog.title"); //$NON-NLS-2$ //$NON-NLS-1$ + String msg= getString(fBundle, fPrefix + "error.dialog.message", fPrefix + "error.dialog.message"); //$NON-NLS-2$ //$NON-NLS-1$ + + ErrorDialog.openError(shell, title, msg, exception.getStatus()); + } + + /** + * Returns all markers which include the ruler's line of activity. + * + * @return all markers which include the ruler's line of activity + */ + protected List getMarkers() { + + List markers= new ArrayList(); + + IResource resource= getResource(); + IDocument document= getDocument(); + AbstractMarkerAnnotationModel model= getAnnotationModel(); + + if (resource != null && model != null && resource.exists()) { + try { + IMarker[] allMarkers= resource.findMarkers(fMarkerType, true, IResource.DEPTH_ZERO); + if (allMarkers != null) { + for (int i= 0; i < allMarkers.length; i++) { + if (includesRulerLine(model.getMarkerPosition(allMarkers[i]), document)) { + markers.add(allMarkers[i]); + } + } + } + } catch (CoreException x) { + handleCoreException(x, EditorMessages.getString("MarkerRulerAction.getMarker")); //$NON-NLS-1$ + } + } + + return markers; + } + + /** + * Creates a new marker according to the specification of this action and + * adds it to the marker resource. + */ + protected void addMarker() { + IResource resource= getResource(); + if (resource == null) + return; + Map attributes= getInitialAttributes(); + if (fAskForLabel) { + if (!askForLabel(attributes)) + return; + } + + try { + MarkerUtilities.createMarker(resource, attributes, fMarkerType); + } catch (CoreException x) { + handleCoreException(x, EditorMessages.getString("MarkerRulerAction.addMarker")); //$NON-NLS-1$ + } + } + + /** + * Removes the given markers. + * + * @param markers the markers to be deleted + */ + protected void removeMarkers(final List markers) { + try { + getResource().getWorkspace().run(new IWorkspaceRunnable() { + public void run(IProgressMonitor monitor) throws CoreException { + for (int i= 0; i < markers.size(); ++i) { + IMarker marker= (IMarker) markers.get(i); + marker.delete(); + } + } + }, null); + } catch (CoreException x) { + handleCoreException(x, EditorMessages.getString("MarkerRulerAction.removeMarkers")); //$NON-NLS-1$ + } + } + + /** + * Asks the user for a marker label. Returns <code>true</code> if a label + * is entered, <code>false</code> if the user cancels the input dialog. + * Sets the value of the attribute <code>message</code> in the given + * map of attributes. + * + * @param attributes the map of attributes + * @return <code>true</code> if the map of attributes has successfuly been initialized + */ + protected boolean askForLabel(Map attributes) { + + Object o= attributes.get("message"); //$NON-NLS-1$ + String proposal= (o instanceof String) ? (String) o : ""; //$NON-NLS-1$ + if (proposal == null) + proposal= ""; //$NON-NLS-1$ + + String title= getString(fBundle, fPrefix + "add.dialog.title", fPrefix + "add.dialog.title"); //$NON-NLS-2$ //$NON-NLS-1$ + String message= getString(fBundle, fPrefix + "add.dialog.message", fPrefix + "add.dialog.message"); //$NON-NLS-2$ //$NON-NLS-1$ + IInputValidator inputValidator = new IInputValidator() { + public String isValid(String newText) { + return (newText == null || newText.length() == 0) ? " " : null; //$NON-NLS-1$ + } + }; + InputDialog dialog= new InputDialog(fTextEditor.getSite().getShell(), title, message, proposal, inputValidator); + + String label= null; + if (dialog.open() != Window.CANCEL) + label= dialog.getValue(); + + if (label == null) + return false; + + label= label.trim(); + if (label.length() == 0) + return false; + + MarkerUtilities.setMessage(attributes, label); + return true; + } + + /** + * Returns the attributes with which a newly created marker will be + * initialized. + * + * @return the initial marker attributes + */ + protected Map getInitialAttributes() { + + Map attributes= new HashMap(11); + + IDocumentProvider provider= fTextEditor.getDocumentProvider(); + IDocument document= provider.getDocument(fTextEditor.getEditorInput()); + int line= fRuler.getLineOfLastMouseButtonActivity(); + int start= -1; + int end= -1; + + try { + + IRegion lineInformation= document.getLineInformation(line); + start= lineInformation.getOffset(); + int length= lineInformation.getLength(); + + end= start + length; + + } catch (BadLocationException x) { + } + + // marker line numbers are 1-based + MarkerUtilities.setLineNumber(attributes, line + 1); + MarkerUtilities.setCharStart(attributes, start); + MarkerUtilities.setCharEnd(attributes, end); + + return attributes; + } +} + diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkerRulerInfoAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkerRulerInfoAction.java new file mode 100644 index 00000000000..9a19004249d --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkerRulerInfoAction.java @@ -0,0 +1,31 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import java.util.ResourceBundle; +import org.eclipse.jface.text.source.IVerticalRulerInfo; + + +/** + * @deprecated use <code>MarkerRulerAction</code> instead + * @since 2.0 + */ +public class MarkerRulerInfoAction extends MarkerRulerAction { + + /** + * @deprecated use super class instead + */ + public MarkerRulerInfoAction(ResourceBundle bundle, String prefix, IVerticalRulerInfo ruler, ITextEditor editor, String markerType, boolean askForLabel) { + super(bundle, prefix, editor, ruler, markerType, askForLabel); + } +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkerUpdater.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkerUpdater.java new file mode 100644 index 00000000000..5a18799e1cf --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkerUpdater.java @@ -0,0 +1,59 @@ +package org.eclipse.ui.texteditor; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.Position; + +import org.eclipse.core.resources.IMarker; + +/** + * Updates a marker's positional attributes which are start position, end position, + * and line number. + */ +class MarkerUpdater implements IMarkerUpdater { + + private final static String[] ATTRIBUTES= {IMarker.CHAR_START, IMarker.CHAR_END, IMarker.LINE_NUMBER}; + + /* + * @see IMarkerUpdater#getAttribute() + */ + public String[] getAttribute() { + return ATTRIBUTES; + } + + /* + * @see IMarkerUpdater#getMarkerType() + */ + public String getMarkerType() { + return null; + } + + /* + * @see IMarkerUpdater#updateMarker(IMarker, IDocument, Position) + */ + public boolean updateMarker(IMarker marker, IDocument document, Position position) { + + if (position.isDeleted()) + return false; + + if (MarkerUtilities.getCharStart(marker) != -1 && MarkerUtilities.getCharEnd(marker) != -1) { + MarkerUtilities.setCharStart(marker, position.getOffset()); + MarkerUtilities.setCharEnd(marker, position.getOffset() + position.getLength()); + } + if (MarkerUtilities.getLineNumber(marker) != -1) { + try { + // marker line numbers are 1-based + MarkerUtilities.setLineNumber(marker, document.getLineOfOffset(position.getOffset()) + 1); + } catch (BadLocationException x) { + } + } + + return true; + } + +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkerUtilities.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkerUtilities.java new file mode 100644 index 00000000000..c67c7ee0736 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MarkerUtilities.java @@ -0,0 +1,242 @@ +package org.eclipse.ui.texteditor; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + + +import java.util.Map; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRunnable; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Platform; + +import org.eclipse.ui.PlatformUI; + + + +/** + * Utility class for accessing marker attributes. The static methods provided + * on this class provide internal exception handling (unexpected + * <code>CoreException</code>s are logged to workbench). + * <p> + * This class provides static methods only; it is not intended to be + * instantiated or subclassed by clients. + * </p> + */ +public final class MarkerUtilities { + + /** + * Don't allow instantiation. + */ + private MarkerUtilities() { + } + + /** + * Returns the ending character offset of the given marker. + * + * @param marker the marker + * @return the ending character offset, or <code>-1</code> if not set + * @see IMarker#CHAR_END + * @see IMarker#getAttribute(java.lang.String,int) + */ + public static int getCharEnd(IMarker marker) { + return getIntAttribute(marker, IMarker.CHAR_END, -1); + } + + /** + * Returns the starting character offset of the given marker. + * + * @param marker the marker + * @return the starting character offset, or <code>-1</code> if not set + * @see IMarker#CHAR_START + * @see IMarker#getAttribute(java.lang.String,int) + */ + public static int getCharStart(IMarker marker) { + return getIntAttribute(marker, IMarker.CHAR_START, -1); + } + + /** + * Returns the specified attribute of the given marker as an integer. + * Returns the given default if the attribute value is not an integer. + */ + private static int getIntAttribute(IMarker marker, String attributeName, int defaultValue) { + return marker.getAttribute(attributeName, defaultValue); + } + + /** + * Returns the line number of the given marker. + * + * @param marker the marker + * @return the line number, or <code>-1</code> if not set + * @see IMarker#LINE_NUMBER + * @see IMarker#getAttribute(java.lang.String,int) + */ + public static int getLineNumber(IMarker marker) { + return getIntAttribute(marker, IMarker.LINE_NUMBER, -1); + } + + /** + * Returns the priority of the given marker. + * + * @param marker the marker + * @return the priority, or <code>IMarker.PRIORITY_NORMAL</code> if not set + * @see IMarker#PRIORITY + * @see IMarker#PRIORITY_NORMAL + * @see IMarker#getAttribute(java.lang.String,int) + */ + public static int getPriority(IMarker marker) { + return getIntAttribute(marker, IMarker.PRIORITY, IMarker.PRIORITY_NORMAL); + } + + /** + * Handles a core exception which occurs when accessing marker attributes. + */ + private static void handleCoreException(CoreException e) { + Platform.getPlugin(PlatformUI.PLUGIN_ID).getLog().log(e.getStatus()); + } + + /** + * Returns whether the given marker is of the given type (either directly or indirectly). + * + * @param marker the marker to be checked + * @param type the reference type + * @return <code>true</code>if maker is an instance of the reference type + */ + public static boolean isMarkerType(IMarker marker, String type) { + try { + return marker.isSubtypeOf(type); + } catch (CoreException e) { + handleCoreException(e); + return false; + } + } + + /** + * Sets the ending character offset of the given marker. + * + * @param marker the marker + * @param charEnd the ending character offset + * @see IMarker#CHAR_END + * @see IMarker#setAttribute(java.lang.String,int) + */ + public static void setCharEnd(IMarker marker, int charEnd) { + setIntAttribute(marker, IMarker.CHAR_END, charEnd); + } + + /** + * Sets the ending character offset in the given map using the standard + * marker attribute name as the key. + * + * @param map the map (key type: <code>String</code>, value type: + * <code>Object</code>) + * @param charEnd the ending character offset + * @see IMarker#CHAR_END + */ + public static void setCharEnd(Map map, int charEnd) { + map.put(IMarker.CHAR_END, new Integer(charEnd)); + } + + /** + * Sets the starting character offset of the given marker. + * + * @param marker the marker + * @param charStart the starting character offset + * @see IMarker#CHAR_START + * @see IMarker#setAttribute(java.lang.String,int) + */ + public static void setCharStart(IMarker marker, int charStart) { + setIntAttribute(marker, IMarker.CHAR_START, charStart); + } + + /** + * Sets the starting character offset in the given map using the standard + * marker attribute name as the key. + * + * @param map the map (key type: <code>String</code>, value type: + * <code>Object</code>) + * @param charStart the starting character offset + * @see IMarker#CHAR_START + */ + public static void setCharStart(Map map, int charStart) { + map.put(IMarker.CHAR_START, new Integer(charStart)); + } + + /** + * Sets the specified attribute of the given marker as an integer. + */ + private static void setIntAttribute(IMarker marker, String attributeName, int value) { + try { + marker.setAttribute(attributeName, value); + } catch (CoreException e) { + handleCoreException(e); + } + } + + /** + * Sets the line number of the given marker. + * + * @param marker the marker + * @param lineNum the line number + * @see IMarker#LINE_NUMBER + * @see IMarker#setAttribute(java.lang.String,int) + */ + public static void setLineNumber(IMarker marker, int lineNum) { + setIntAttribute(marker, IMarker.LINE_NUMBER, lineNum); + } + + /** + * Sets the line number in the given map using the standard marker attribute + * name as the key. + * + * @param map the map (key type: <code>String</code>, value type: + * <code>Object</code>) + * @param lineNum the line number + * @see IMarker#LINE_NUMBER + */ + public static void setLineNumber(Map map, int lineNum) { + map.put(IMarker.LINE_NUMBER, new Integer(lineNum)); + } + + /** + * Sets the message in the given map using the standard marker attribute name + * as the key. + * + * @param map the map (key type: <code>String</code>, value type: + * <code>Object</code>) + * @param message the message + * @see IMarker#MESSAGE + */ + public static void setMessage(Map map, String message) { + map.put(IMarker.MESSAGE, message); + } + + /** + * Creates a marker on the given resource with the given type and attributes. + * <p> + * This method modifies the workspace (progress is not reported to the user). + * </p> + * + * @param resource the resource + * @param attributes the attribute map (key type: <code>String</code>, + * value type: <code>Object</code>) + * @param markerType the type of marker + * @exception CoreException if this method fails + * @see IResource#createMarker + */ + public static void createMarker(final IResource resource, final Map attributes, final String markerType) throws CoreException { + + IWorkspaceRunnable r= new IWorkspaceRunnable() { + public void run(IProgressMonitor monitor) throws CoreException { + IMarker marker= resource.createMarker(markerType); + marker.setAttributes(attributes); + } + }; + + resource.getWorkspace().run(r, null); + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/PropagatingFontFieldEditor.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/PropagatingFontFieldEditor.java new file mode 100644 index 00000000000..a9f76ce480b --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/PropagatingFontFieldEditor.java @@ -0,0 +1,23 @@ +package org.eclipse.ui.texteditor; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + + +import org.eclipse.swt.widgets.Composite; + + +/** + * @deprecated use WorkbenchChainedTextFontFieldEditor + */ +public final class PropagatingFontFieldEditor extends WorkbenchChainedTextFontFieldEditor { + + /** + * @deprecated use WorkbenchChainedTextFontFieldEditor + */ + public PropagatingFontFieldEditor(String name, String labelText, Composite parent) { + super(name, labelText, parent); + } +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ResourceAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ResourceAction.java new file mode 100644 index 00000000000..f03712d7ad8 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ResourceAction.java @@ -0,0 +1,101 @@ +package org.eclipse.ui.texteditor; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.resource.ImageDescriptor; + +import org.eclipse.ui.help.WorkbenchHelp; + + +/** + * An action which configures its label, image, tooltip, and description from + * a resource bundle using known keys. + * <p> + * Clients may subclass this abstract class to define new kinds of actions. As + * with <code>Action</code>, subclasses must implement the + * <code>IAction.run</code> method to carry out the action's semantics. + * </p> + */ +public abstract class ResourceAction extends Action { + + /** + * Retrieves and returns the value with the given key from the given resource + * bundle, or returns the given default value if there is no such resource. + * Convenience method for dealing gracefully with missing resources. + * + * @param bundle the resource bundle + * @param key the resource key + * @param defaultValue the default value, or <code>null</code> + * @return the resource value, or the given default value (which may be + * <code>null</code>) + */ + protected static String getString(ResourceBundle bundle, String key, String defaultValue) { + + String value= defaultValue; + try { + value= bundle.getString(key); + } catch (MissingResourceException x) { + } + + return value; + } + + /** + * Creates a new action that configures itself from the given resource + * bundle. + * <p> + * The following keys, prepended by the given option prefix, + * are used for retrieving resources from the given bundle: + * <ul> + * <li><code>"label"</code> - <code>setText</code></li> + * <li><code>"tooltip"</code> - <code>setToolTipText</code></li> + * <li><code>"image"</code> - <code>setImageDescriptor</code></li> + * <li><code>"description"</code> - <code>setDescription</code></li> + * </ul> + * </p> + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys, or + * <code>null</code> if none + */ + public ResourceAction(ResourceBundle bundle, String prefix) { + super(); + + String labelKey= "label"; //$NON-NLS-1$ + String tooltipKey= "tooltip"; //$NON-NLS-1$ + String imageKey= "image"; //$NON-NLS-1$ + String descriptionKey= "description"; //$NON-NLS-1$ + + if (prefix != null && prefix.length() > 0) { + labelKey= prefix + labelKey; + tooltipKey= prefix + tooltipKey; + imageKey= prefix + imageKey; + descriptionKey= prefix + descriptionKey; + } + + setText(getString(bundle, labelKey, labelKey)); + setToolTipText(getString(bundle, tooltipKey, null)); + setDescription(getString(bundle, descriptionKey, null)); + + String file= getString(bundle, imageKey, null); + if (file != null && file.trim().length() > 0) + setImageDescriptor(ImageDescriptor.createFromFile(getClass(), file)); + } + + /** + * Sets the action's help context id. + * + * @param contextId the help context id + */ + public final void setHelpContextId(String contextId) { + WorkbenchHelp.setHelp(this, contextId); + } +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ResourceMarkerAnnotationModel.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ResourceMarkerAnnotationModel.java new file mode 100644 index 00000000000..a934e68af3a --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ResourceMarkerAnnotationModel.java @@ -0,0 +1,171 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import org.eclipse.jface.util.Assert; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IMarkerDelta; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceChangeEvent; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.IResourceDeltaVisitor; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRunnable; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + + + +/** + * A marker annotation model whose underlying source of markers is + * a resource in the workspace. + * <p> + * This class may be instantiated; it is not intended to be subclassed. + * </p> + */ +public class ResourceMarkerAnnotationModel extends AbstractMarkerAnnotationModel { + + + /** + * Internal resource change listener. + */ + class ResourceChangeListener implements IResourceChangeListener { + /* + * @see IResourceChangeListener#resourceChanged + */ + public void resourceChanged(IResourceChangeEvent e) { + IResourceDelta delta= e.getDelta(); + try { + if (delta != null) + delta.accept(fResourceDeltaVisitor); + } catch (CoreException x) { + handleCoreException(x, EditorMessages.getString("ResourceMarkerAnnotationModel.resourceChanged")); //$NON-NLS-1$ + } + } + }; + + /** + * Internal resource delta visitor. + */ + class ResourceDeltaVisitor implements IResourceDeltaVisitor { + /* + * @see IResourceDeltaVisitor#visit + */ + public boolean visit(IResourceDelta delta) throws CoreException { + if (delta != null && fResource.equals(delta.getResource())) { + update(delta.getMarkerDeltas()); + return false; + } + return true; + } + }; + + /** The workspace */ + private IWorkspace fWorkspace; + /** The resource */ + private IResource fResource; + /** The resource change listener */ + private IResourceChangeListener fResourceChangeListener= new ResourceChangeListener(); + /** The resource delta visitor */ + private IResourceDeltaVisitor fResourceDeltaVisitor= new ResourceDeltaVisitor(); + + + /** + * Creates a marker annotation model with the given resource as the source + * of the markers. + * + * @param resource the resource + */ + public ResourceMarkerAnnotationModel(IResource resource) { + Assert.isNotNull(resource); + fResource= resource; + fWorkspace= resource.getWorkspace(); + } + + /* + * @see AnnotationModel#isAcceptable + */ + protected boolean isAcceptable(IMarker marker) { + return marker != null && fResource.equals(marker.getResource()); + } + + /** + * Updates this model to the given marker deltas. + * + * @param markerDeltas the list of marker deltas + */ + private void update(IMarkerDelta[] markerDeltas) { + + if (markerDeltas.length == 0) + return; + + for (int i= 0; i < markerDeltas.length; i++) { + IMarkerDelta delta= markerDeltas[i]; + switch (delta.getKind()) { + case IResourceDelta.ADDED : + addMarkerAnnotation(delta.getMarker()); + break; + case IResourceDelta.REMOVED : + removeMarkerAnnotation(delta.getMarker()); + break; + case IResourceDelta.CHANGED : + modifyMarkerAnnotation(delta.getMarker()); + break; + } + } + + fireModelChanged(); + } + + /* + * @see AbstractMarkerAnnotationModel#listenToMarkerChanges(boolean) + */ + protected void listenToMarkerChanges(boolean listen) { + if (listen) + fWorkspace.addResourceChangeListener(fResourceChangeListener); + else + fWorkspace.removeResourceChangeListener(fResourceChangeListener); + } + + /* + * @see AbstractMarkerAnnotationModel#deleteMarkers(IMarker[]) + */ + protected void deleteMarkers(final IMarker[] markers) throws CoreException { + fWorkspace.run(new IWorkspaceRunnable() { + public void run(IProgressMonitor monitor) throws CoreException { + for (int i= 0; i < markers.length; ++i) { + markers[i].delete(); + } + } + }, null); + } + + /* + * @see AbstractMarkerAnnotationModel#retrieveMarkers() + */ + protected IMarker[] retrieveMarkers() throws CoreException { + return fResource.findMarkers(IMarker.MARKER, true, IResource.DEPTH_ZERO); + } + + /** + * Returns the resource serving as the source of markers for this annotation model. + * + * @return the resource serving as the source of markers for this annotation model + * @since 2.0 + */ + protected IResource getResource() { + return fResource; + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/RetargetTextEditorAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/RetargetTextEditorAction.java new file mode 100644 index 00000000000..b408da74032 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/RetargetTextEditorAction.java @@ -0,0 +1,149 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + + +package org.eclipse.ui.texteditor; + + +import java.util.ResourceBundle; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; + + +/** + * Action used by an editor action bar contributor to establish placeholders in + * menus or action bars which can be retargeted to dynamically changing actions, + * for example, those which come from the active editor. This action assumes that + * the "wrapped" action sends out property change events in response to state + * changes. It uses these change notification to adapt its enabling state and + * its visual presentation. + * <p> + * This class may be instantiated; it is not intended to be subclassed. + * </p> + */ +public final class RetargetTextEditorAction extends ResourceAction { + + /** The target action */ + private IAction fAction; + /** The default label if there is no target action */ + private String fDefaultText; + /** The listener to pick up changes of the target action */ + private IPropertyChangeListener fListener= new IPropertyChangeListener() { + public void propertyChange(PropertyChangeEvent event) { + update(event); + } + }; + + /** + * Creates a new action. The action configures its initial visual + * representation from the given resource bundle. If this action's + * wrapped action is set to <code>null</code> it also uses the + * information in the resource bundle. + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys + * (described in <code>ResourceAction</code> constructor), or + * <code>null</code> if none + * @see ResourceAction#ResourceAction + */ + public RetargetTextEditorAction(ResourceBundle bundle, String prefix) { + super(bundle, prefix); + fDefaultText= getText(); + } + + /** + * Creates a new action. The action configures its initial visual + * representation from the given resource bundle. If this action's + * wrapped action is set to <code>null</code> it also uses the + * information in the resource bundle. The action gets the given + * action id. + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys + * (described in <code>ResourceAction</code> constructor), or <code>null</code> if none + * @param actionId the action id + * @see ResourceAction#ResourceAction + * @since 2.0 + */ + public RetargetTextEditorAction(ResourceBundle bundle, String prefix, String actionId) { + super(bundle, prefix); + fDefaultText= getText(); + setId(actionId); + } + + /** + * Updates to the changes of the underlying action. + * + * @param event the change event describing the state change + */ + private void update(PropertyChangeEvent event) { + if (ENABLED.equals(event.getProperty())) { + Boolean bool= (Boolean) event.getNewValue(); + setEnabled(bool.booleanValue()); + } else if (TEXT.equals(event.getProperty())) + setText((String) event.getNewValue()); + else if (TOOL_TIP_TEXT.equals(event.getProperty())) + setToolTipText((String) event.getNewValue()); + else if (CHECKED.equals(event.getProperty())) { + Boolean bool= (Boolean) event.getNewValue(); + setChecked(bool.booleanValue()); + } + } + + /** + * Sets the underlying action. + * + * @param action the underlying action + */ + public void setAction(IAction action) { + + if (fAction != null) { + fAction.removePropertyChangeListener(fListener); + fAction= null; + } + + fAction= action; + + if (fAction == null) { + + setEnabled(false); + setText(fDefaultText); + setToolTipText(""); //$NON-NLS-1$ + + } else { + + setEnabled(fAction.isEnabled()); + setText(fAction.getText()); + setToolTipText(fAction.getToolTipText()); + fAction.addPropertyChangeListener(fListener); + } + } + + /* + * @see Action#run() + */ + public void run() { + if (fAction != null) + fAction.run(); + } + + /* + * @see IAction#getActionDefinitionId() + * @since 2.0 + */ + public String getActionDefinitionId() { + if(fAction != null) + return fAction.getActionDefinitionId(); + return null; + } +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/RevertToSavedAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/RevertToSavedAction.java new file mode 100644 index 00000000000..7fe34b01e8e --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/RevertToSavedAction.java @@ -0,0 +1,50 @@ +package org.eclipse.ui.texteditor; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + + +import java.util.ResourceBundle; + + +/** + * Action for abandoning changes made in the text editor since the last save + * operation. The action is initially associated with a text editor via the + * constructor, but that can be subsequently changed using <code>setEditor</code>. + * <p> + * This class may be instantiated; it is not intended to be subclassed. + * </p> + */ +public class RevertToSavedAction extends TextEditorAction { + + /** + * Creates a new action for the given text editor. The action configures its + * visual representation from the given resource bundle. + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys + * (described in <code>ResourceAction</code> constructor), or + * <code>null</code> if none + * @param editor the text editor + * @see ResourceAction#ResourceAction + */ + public RevertToSavedAction(ResourceBundle bundle, String prefix, ITextEditor editor) { + super(bundle, prefix, editor); + } + + /* + * @see IAction#run + */ + public void run() { + getTextEditor().doRevertToSaved(); + } + + /* + * @see TextEditorAction#update + */ + public void update() { + setEnabled(getTextEditor().isDirty()); + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/SaveAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/SaveAction.java new file mode 100644 index 00000000000..a4e71601cc3 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/SaveAction.java @@ -0,0 +1,50 @@ +package org.eclipse.ui.texteditor; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + + +import java.util.ResourceBundle; + + +/** + * Action for saving recent changes made in the text editor. The action is + * initially associated with a text editor via the constructor, but that can be + * subsequently changed using <code>setEditor</code>. + * <p> + * This class may be instantiated; it is not intended to be subclassed. + * </p> + */ +public class SaveAction extends TextEditorAction { + + /** + * Creates a new action for the given text editor. The action configures its + * visual representation from the given resource bundle. + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys + * (described in <code>ResourceAction</code> constructor), or + * <code>null</code> if none + * @param editor the text editor + * @see ResourceAction#ResourceAction + */ + public SaveAction(ResourceBundle bundle, String prefix, ITextEditor editor) { + super(bundle, prefix, editor); + } + + /* + * @see IAction#run + */ + public void run() { + getTextEditor().getSite().getPage().saveEditor(getTextEditor(), false); + } + + /* + * @see TextEditorAction#update + */ + public void update() { + setEnabled(getTextEditor().isDirty()); + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ShiftAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ShiftAction.java new file mode 100644 index 00000000000..7a498afa7ba --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ShiftAction.java @@ -0,0 +1,136 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + + +package org.eclipse.ui.texteditor; + + +import java.util.ResourceBundle; + +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.jface.text.ITextOperationTarget; + +import org.eclipse.ui.IWorkbenchPartSite; + + + +/** + * Action for shifting code to the right or left by one indentation level. + * @since 2.0 + */ +public final class ShiftAction extends TextEditorAction implements IReadOnlyDependent { + + /** The text operation code */ + private int fOperationCode= -1; + /** The text operation target */ + private ITextOperationTarget fOperationTarget; + + /** + * Creates and initializes the action for the given text editor and operation + * code. The action configures its visual representation from the given resource + * bundle. The action works by asking the text editor at the time for its + * text operation target adapter (using + * <code>getAdapter(ITextOperationTarget.class)</code>. The action runs that + * operation with the given opcode. + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys + * (described in <code>ResourceAction</code> constructor), or <code>null</code> if none + * @param editor the text editor + * @param operationCode the operation code + * @see ResourceAction#ResourceAction + */ + public ShiftAction(ResourceBundle bundle, String prefix, ITextEditor editor, int operationCode) { + super(bundle, prefix, editor); + fOperationCode= operationCode; + update(); + } + + /** + * The <code>TextOperationAction</code> implementation of this + * <code>IAction</code> method runs the operation with the current + * operation code. + */ + public void run() { + if (fOperationCode != -1 && fOperationTarget != null) { + + ITextEditor editor= getTextEditor(); + if (editor != null) { + + Display display= null; + + IWorkbenchPartSite site= editor.getSite(); + Shell shell= site.getShell(); + if (shell != null && !shell.isDisposed()) + display= shell.getDisplay(); + + BusyIndicator.showWhile(display, new Runnable() { + public void run() { + fOperationTarget.doOperation(fOperationCode); + } + }); + } + } + } + + /* + * @see IUpdate#update() + */ + public void update() { + + ITextEditor editor= getTextEditor(); + if (editor instanceof ITextEditorExtension) { + ITextEditorExtension extension= (ITextEditorExtension) editor; + if (extension.isEditorInputReadOnly()) { + setEnabled(false); + return; + } + } + + if (fOperationTarget == null && editor!= null && fOperationCode != -1) + fOperationTarget= (ITextOperationTarget) editor.getAdapter(ITextOperationTarget.class); + + boolean isEnabled= (fOperationTarget != null && fOperationTarget.canDoOperation(fOperationCode)); + setEnabled(isEnabled); + } + + /* + * @see TextEditorAction#setEditor(ITextEditor) + */ + public void setEditor(ITextEditor editor) { + super.setEditor(editor); + fOperationTarget= null; + } + + /* + * @see IReadOnlyDependent#isEnabled(boolean) + */ + public boolean isEnabled(boolean isWritable) { + + if (!isWritable) + return false; + + /* + * Note that this implementation still honors the result returned by canDoOperation. + * I.e. if the viewer is set to read-only, this method still returns false. + * It covers the case in which the viewer is also writable. + * + */ + ITextEditor editor= getTextEditor(); + if (fOperationTarget == null && editor!= null && fOperationCode != -1) + fOperationTarget= (ITextOperationTarget) editor.getAdapter(ITextOperationTarget.class); + + return (fOperationTarget != null && fOperationTarget.canDoOperation(fOperationCode)); + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/StatusLineContributionItem.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/StatusLineContributionItem.java new file mode 100644 index 00000000000..62e8db9506c --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/StatusLineContributionItem.java @@ -0,0 +1,114 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + + +package org.eclipse.ui.texteditor; + + + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CLabel; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Composite; + +import org.eclipse.jface.action.ContributionItem; + + +/** + * Contribution item for the status line. + * @since 2.0 + */ +public class StatusLineContributionItem extends ContributionItem implements IStatusField { + + /** + * Specific label for the status line. + */ + static class StatusLineLabel extends CLabel { + + /** Left and right margin used in CLabel */ + private static int INDENT= 3; + /** Precomputed label size */ + private Point fFixedSize; + + /** + * Creates a new status line label. + * @param parent parent control + * @param style the swt style bits + */ + public StatusLineLabel(Composite parent, int style) { + super(parent, style); + + GC gc= new GC(parent); + gc.setFont(parent.getFont()); + Point extent= gc.textExtent("MMMMMMMMM"); //$NON-NLS-1$ + gc.dispose(); + + fFixedSize= new Point(extent.x + INDENT * 2, 10); + } + + /* + * @see Control#computeSize(int, int, boolean) + */ + public Point computeSize(int wHint, int hHint, boolean changed) { + return fFixedSize; + } + }; + + /** The label text */ + private String fText; + /** The label image */ + private Image fImage; + /** The status line label widget */ + private StatusLineLabel fLabel; + + /** + * Creates a new item with the given id. + * + * @param id the item's id + */ + public StatusLineContributionItem(String id) { + super(id); + } + + /* + * @see IStatusField#setText + */ + public void setText(String text) { + fText= text; + if (fLabel != null && !fLabel.isDisposed()) { + fLabel.setText(fText); + } + } + + /* + * @see IStatusField#setImage(Image) + */ + public void setImage(Image image) { + fImage= image; + if (fLabel != null && !fLabel.isDisposed()) { + fLabel.setImage(fImage); + } + } + + /* + * @see IContributionItem#fill(Composite) + */ + public void fill(Composite parent) { + fLabel= new StatusLineLabel(parent, SWT.SHADOW_IN); + fLabel.setData(this); + + if (fText != null) + fLabel.setText(fText); + } +} + diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/StatusTextEditor.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/StatusTextEditor.java new file mode 100644 index 00000000000..034ee969d96 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/StatusTextEditor.java @@ -0,0 +1,196 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.custom.StackLayout; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; + +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; + +import org.eclipse.ui.IEditorInput; + + +/** + * Capable of handling input elements that have an associated status with them. + * @since 2.0 + */ +public class StatusTextEditor extends AbstractTextEditor { + + /** The root composite of this editor */ + private Composite fParent; + /** The layout used to manage the regular and the status page */ + private StackLayout fStackLayout; + /** The root composite for the regular page */ + private Composite fDefaultComposite; + /** The status page */ + private Control fStatusControl; + + /* + * @see IWorkbenchPart#createPartControl(Composite) + */ + public void createPartControl(Composite parent) { + + fParent= new Composite(parent, SWT.NONE); + fStackLayout= new StackLayout(); + fParent.setLayout(fStackLayout); + + fDefaultComposite= new Composite(fParent, SWT.NONE); + fDefaultComposite.setLayout(new FillLayout()); + super.createPartControl(fDefaultComposite); + + updatePartControl(getEditorInput()); + } + + /** + * Checks if the status of the given input is OK. If not the + * status control is shown rather than the default control. + * + * @param input the input whose status is checked + */ + public void updatePartControl(IEditorInput input) { + + if (fStatusControl != null) { + fStatusControl.dispose(); + fStatusControl= null; + } + + Control front= null; + if (fParent != null && input != null) { + if (getDocumentProvider() instanceof IDocumentProviderExtension) { + IDocumentProviderExtension extension= (IDocumentProviderExtension) getDocumentProvider(); + IStatus status= extension.getStatus(input); + if (status.isOK()) { + front= fDefaultComposite; + } else { + fStatusControl= createStatusControl(fParent, status); + front= fStatusControl; + } + } + } + + if (fStackLayout.topControl != front) { + fStackLayout.topControl= front; + fParent.layout(); + updateStatusFields(); + } + } + + /** + * Creates the status control for the given status. May be overridden by subclasses. + * + * @param parent the parent control + * @param status the status + */ + protected Control createStatusControl(Composite parent, IStatus status) { + InfoForm infoForm= new InfoForm(parent); + infoForm.setHeaderText(getStatusHeader(status)); + infoForm.setBannerText(getStatusBanner(status)); + infoForm.setInfo(getStatusMessage(status)); + return infoForm.getControl(); + } + + /** + * Returns a header for the given status + * + * @param status the status whose message is returned + * @return a header for the given status + */ + protected String getStatusHeader(IStatus status) { + return ""; //$NON-NLS-1$ + } + + /** + * Returns a banner for the given status. + * + * @param status the status whose message is returned + * @return a banner for the given status + */ + protected String getStatusBanner(IStatus status) { + return ""; //$NON-NLS-1$ + } + + /** + * Returns a message for the given status. + * + * @param status the status whose message is returned + * @return a message for the given status + */ + protected String getStatusMessage(IStatus status) { + return status.getMessage(); + } + + /* + * @see AbstractTextEditor#updateStatusField(String) + */ + protected void updateStatusField(String category) { + IDocumentProvider provider= getDocumentProvider(); + if (provider instanceof IDocumentProviderExtension) { + IDocumentProviderExtension extension= (IDocumentProviderExtension) provider; + IStatus status= extension.getStatus(getEditorInput()); + if (status != null && !status.isOK()) { + IStatusField field= getStatusField(category); + if (field != null) { + field.setText(fErrorLabel); + return; + } + } + } + + super.updateStatusField(category); + } + + /* + * @see AbstractTextEditor#doSetInput(IEditorInput) + */ + protected void doSetInput(IEditorInput input) throws CoreException { + super.doSetInput(input); + if (fParent != null && !fParent.isDisposed()) + updatePartControl(getEditorInput()); + } + + /* + * @see ITextEditor#doRevertToSaved() + */ + public void doRevertToSaved() { + // http://dev.eclipse.org/bugs/show_bug.cgi?id=19014 + super.doRevertToSaved(); + if (fParent != null && !fParent.isDisposed()) + updatePartControl(getEditorInput()); + } + + /* + * @see AbstractTextEditor#sanityCheckState(IEditorInput) + */ + protected void sanityCheckState(IEditorInput input) { + // http://dev.eclipse.org/bugs/show_bug.cgi?id=19014 + super.sanityCheckState(input); + if (fParent != null && !fParent.isDisposed()) + updatePartControl(getEditorInput()); + } +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/TextEditorAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/TextEditorAction.java new file mode 100644 index 00000000000..09e8db7faaf --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/TextEditorAction.java @@ -0,0 +1,66 @@ +package org.eclipse.ui.texteditor; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + +import java.util.ResourceBundle; + +/** + * Sekelleton of a standard text editor action. The action is + * initially associated with a text editor via the constructor, + * but can subsequently be changed using <code>setEditor</code>. + * Subclasses must implement the <code>run</code> method and if + * required override the <code>update</code> method. + * </p> + */ +public abstract class TextEditorAction extends ResourceAction implements IUpdate { + + /** The action's editor */ + private ITextEditor fTextEditor; + + /** + * Creates and initializes the action for the given text editor. The action + * configures its visual representation from the given resource bundle. + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys + * (described in <code>ResourceAction</code> constructor), or + * <code>null</code> if none + * @param editor the text editor + * @see ResourceAction#ResourceAction + */ + protected TextEditorAction(ResourceBundle bundle, String prefix, ITextEditor editor) { + super(bundle, prefix); + setEditor(editor); + update(); + } + + /** + * Returns the action's text editor. + * + * @return the action's text editor + */ + protected ITextEditor getTextEditor() { + return fTextEditor; + } + + /** + * Retargets this action to the given editor. + * + * @param editor the new editor, or <code>null</code> if none + */ + public void setEditor(ITextEditor editor) { + fTextEditor= editor; + } + + /** + * Always enables this action if it is connected to a text editor. + * If the asocciated editor is <code>null</code>, the action is disabled. + * Subclasses may override. + */ + public void update() { + setEnabled(getTextEditor() != null); + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/TextNavigationAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/TextNavigationAction.java new file mode 100644 index 00000000000..499f537029d --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/TextNavigationAction.java @@ -0,0 +1,258 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + + +package org.eclipse.ui.texteditor; + + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuCreator; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.events.HelpListener; +import org.eclipse.swt.widgets.Event; + + +/** + * An <code>IAction</code> wrapper for text widget navigational and selection actions. + * @since 2.0 + */ +public class TextNavigationAction implements IAction { + + /** The text widget */ + private StyledText fTextWidget; + /** The styled text action id */ + private int fAction; + /** The action's action id */ + private String fActionId; + /** This action's action definition id */ + private String fActionDefinitionId; + + + /** + * Creates a new <code>TextNavigationAction</code>. + * @param textWidget the text widget + * @param action the styled text widget action + */ + public TextNavigationAction(StyledText textWidget, int action) { + fTextWidget= textWidget; + fAction= action; + } + + /* + * @see IAction#run() + */ + public void run() { + fTextWidget.invokeAction(fAction); + } + + /* + * @see IAction#runWithEvent(Event) + */ + public void runWithEvent(Event event) { + run(); + } + + /* + * @see IAction#setActionDefinitionId(String) + */ + public void setActionDefinitionId(String id) { + fActionDefinitionId= id; + } + + /* + * @see IAction#getActionDefinitionId() + */ + public String getActionDefinitionId() { + return fActionDefinitionId; + } + + /* + * @see IAction#setId(String) + */ + public void setId(String id) { + fActionId= id; + } + + /* + * @see IAction#getId() + */ + public String getId() { + return fActionId; + } + + +// ---------------------------------------------------------------------------------------------------------------------------------- +// All the subsequent methods are just empty method bodies. + + /* + * @see IAction#addPropertyChangeListener(IPropertyChangeListener) + */ + public void addPropertyChangeListener(IPropertyChangeListener listener) { + } + + /* + * @see IAction#getAccelerator() + */ + public int getAccelerator() { + return 0; + } + + /* + * @see IAction#getDescription() + */ + public String getDescription() { + return null; + } + + /* + * @see IAction#getDisabledImageDescriptor() + */ + public ImageDescriptor getDisabledImageDescriptor() { + return null; + } + + /* + * @see IAction#getHelpListener() + */ + public HelpListener getHelpListener() { + return null; + } + + /* + * @see IAction#getHoverImageDescriptor() + */ + public ImageDescriptor getHoverImageDescriptor() { + return null; + } + + /* + * @see IAction#getImageDescriptor() + */ + public ImageDescriptor getImageDescriptor() { + return null; + } + + /* + * @see IAction#getMenuCreator() + */ + public IMenuCreator getMenuCreator() { + return null; + } + + /* + * @see IAction#getStyle() + */ + public int getStyle() { + return 0; + } + + /* + * @see IAction#getText() + */ + public String getText() { + return null; + } + + /* + * @see IAction#getToolTipText() + */ + public String getToolTipText() { + return null; + } + + /* + * @see IAction#isChecked() + */ + public boolean isChecked() { + return false; + } + + /* + * @see IAction#isEnabled() + */ + public boolean isEnabled() { + return true; + } + + /* + * @see IAction#removePropertyChangeListener(IPropertyChangeListener) + */ + public void removePropertyChangeListener(IPropertyChangeListener listener) { + } + + /* + * @see IAction#setAccelerator(int) + */ + public void setAccelerator(int keycode) { + } + + /* + * @see IAction#setChecked(boolean) + */ + public void setChecked(boolean checked) { + } + + /* + * @see IAction#setDescription(String) + */ + public void setDescription(String text) { + } + + /* + * @see IAction#setDisabledImageDescriptor(ImageDescriptor) + */ + public void setDisabledImageDescriptor(ImageDescriptor newImage) { + } + + /* + * @see IAction#setEnabled(boolean) + */ + public void setEnabled(boolean enabled) { + } + + /* + * @see IAction#setHelpListener(HelpListener) + */ + public void setHelpListener(HelpListener listener) { + } + + /* + * @see IAction#setHoverImageDescriptor(ImageDescriptor) + */ + public void setHoverImageDescriptor(ImageDescriptor newImage) { + } + + /* + * @see IAction#setImageDescriptor(ImageDescriptor) + */ + public void setImageDescriptor(ImageDescriptor newImage) { + } + + /* + * @see IAction#setMenuCreator(IMenuCreator) + */ + public void setMenuCreator(IMenuCreator creator) { + } + + /* + * @see IAction#setText(String) + */ + public void setText(String text) { + } + + /* + * @see IAction#setToolTipText(String) + */ + public void setToolTipText(String text) { + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/TextOperationAction.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/TextOperationAction.java new file mode 100644 index 00000000000..462f123abf7 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/TextOperationAction.java @@ -0,0 +1,157 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + + +package org.eclipse.ui.texteditor; + + +import java.util.ResourceBundle; + +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.jface.text.ITextOperationTarget; + +import org.eclipse.ui.IWorkbenchPartSite; + + +/** + * An action which gets a text operation target from its text editor. + * <p> + * The action is initially associated with a text editor via the constructor, + * but can subsequently be changed using <code>setEditor</code>. + * </p> + * <p> + * If this class is used as is, it works by asking the text editor for its + * text operation target adapter (using <code>getAdapter(ITextOperationTarget.class)</code>. + * The action runs this operation with the pre-configured opcode. + * </p> + */ +public final class TextOperationAction extends TextEditorAction { + + /** The text operation code */ + private int fOperationCode= -1; + /** The text operation target */ + private ITextOperationTarget fOperationTarget; + /** + * Indicates whether this action can be executed on read only editors + * @since 2.0 + */ + private boolean fRunsOnReadOnly= false; + + /** + * Creates and initializes the action for the given text editor and operation + * code. The action configures its visual representation from the given resource + * bundle. The action works by asking the text editor at the time for its + * text operation target adapter (using + * <code>getAdapter(ITextOperationTarget.class)</code>. The action runs that + * operation with the given opcode. + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys + * (described in <code>ResourceAction</code> constructor), or + * <code>null</code> if none + * @param editor the text editor + * @param operationCode the operation code + * @see ResourceAction#ResourceAction + */ + public TextOperationAction(ResourceBundle bundle, String prefix, ITextEditor editor, int operationCode) { + super(bundle, prefix, editor); + fOperationCode= operationCode; + update(); + } + + /** + * Creates and initializes the action for the given text editor and operation + * code. The action configures its visual representation from the given resource + * bundle. The action works by asking the text editor at the time for its + * text operation target adapter (using + * <code>getAdapter(ITextOperationTarget.class)</code>. The action runs that + * operation with the given opcode. + * + * @param bundle the resource bundle + * @param prefix a prefix to be prepended to the various resource keys + * (described in <code>ResourceAction</code> constructor), or + * <code>null</code> if none + * @param editor the text editor + * @param operationCode the operation code + * @param runsOnReadOnly <code>true</code> if action can be executed on read-only files + * + * @see ResourceAction#ResourceAction + * @since 2.0 + */ + public TextOperationAction(ResourceBundle bundle, String prefix, ITextEditor editor, int operationCode, boolean runsOnReadOnly) { + super(bundle, prefix, editor); + fOperationCode= operationCode; + fRunsOnReadOnly= runsOnReadOnly; + update(); + } + + /** + * The <code>TextOperationAction</code> implementation of this + * <code>IAction</code> method runs the operation with the current + * operation code. + */ + public void run() { + if (fOperationCode != -1 && fOperationTarget != null) { + + ITextEditor editor= getTextEditor(); + if (editor != null) { + + Display display= null; + + IWorkbenchPartSite site= editor.getSite(); + Shell shell= site.getShell(); + if (shell != null && !shell.isDisposed()) + display= shell.getDisplay(); + + BusyIndicator.showWhile(display, new Runnable() { + public void run() { + fOperationTarget.doOperation(fOperationCode); + } + }); + } + } + } + + /** + * The <code>TextOperationAction</code> implementation of this + * <code>IUpdate</code> method discovers the operation through the current + * editor's <code>ITextOperationTarget</code> adapter, and sets the + * enabled state accordingly. + */ + public void update() { + + ITextEditor editor= getTextEditor(); + if (editor instanceof ITextEditorExtension) { + ITextEditorExtension extension= (ITextEditorExtension) editor; + if (extension.isEditorInputReadOnly() && !fRunsOnReadOnly) { + setEnabled(false); + return; + } + } + + if (fOperationTarget == null && editor!= null && fOperationCode != -1) + fOperationTarget= (ITextOperationTarget) editor.getAdapter(ITextOperationTarget.class); + + boolean isEnabled= (fOperationTarget != null && fOperationTarget.canDoOperation(fOperationCode)); + setEnabled(isEnabled); + } + + /* + * @see TextEditorAction#setEditor(ITextEditor) + */ + public void setEditor(ITextEditor editor) { + super.setEditor(editor); + fOperationTarget= null; + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/TextSelectionNavigationLocation.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/TextSelectionNavigationLocation.java new file mode 100644 index 00000000000..bb4c6795d00 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/TextSelectionNavigationLocation.java @@ -0,0 +1,238 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + +package org.eclipse.ui.texteditor; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.BadPositionCategoryException; +import org.eclipse.jface.text.DefaultPositionUpdater; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IPositionUpdater; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IMemento; +import org.eclipse.ui.NavigationLocation; +import org.eclipse.ui.internal.IWorkbenchConstants; + + +/** + * 2.1 - WORK_IN_PROGRESS do not use. + */ +public class TextSelectionNavigationLocation extends NavigationLocation { + + + private final static String DELETED= "deleted"; + private final static String NOT_DELETED= "not_deleted"; + + private final static String CATEGORY= "__navigation_" + TextSelectionNavigationLocation.class.hashCode(); + private static IPositionUpdater fgPositionUpdater= new DefaultPositionUpdater(CATEGORY); + + private Position fPosition; + private IDocument fDocument; + private Position fSavedPosition; + + + public TextSelectionNavigationLocation(ITextEditor part) { + + super(part); + + ISelection s= part.getSelectionProvider().getSelection(); + ITextSelection selection= (ITextSelection) s; + IDocument document= getDocument(part); + + Position position= new Position(selection.getOffset(), selection.getLength()); + if (installOnDocument(document, position)) { + fDocument= document; + fPosition= position; + + if (!part.isDirty()) + fSavedPosition= new Position(fPosition.offset, fPosition.length); + } + } + + private IDocument getDocument(ITextEditor part) { + IDocumentProvider provider= part.getDocumentProvider(); + return provider.getDocument(part.getEditorInput()); + } + + private boolean installOnDocument(IDocument document, Position position) { + + if (document != null && position != null) { + + if (!document.containsPositionCategory(CATEGORY)) { + document.addPositionCategory(CATEGORY); + document.addPositionUpdater(fgPositionUpdater); + } + + try { + document.addPosition(CATEGORY, position); + return true; + } catch (BadLocationException e) { + } catch (BadPositionCategoryException e) { + } + } + + return false; + } + + private boolean uninstallFromDocument(IDocument document, Position position) { + + if (document != null && position != null) { + try { + + document.removePosition(CATEGORY, position); + + Position[] category= document.getPositions(CATEGORY); + if (category == null || category.length == 0) { + document.removePositionCategory(CATEGORY); + document.removePositionUpdater(fgPositionUpdater); + } + return true; + + } catch (BadPositionCategoryException e) { + } + } + + return false; + } + + public String toString() { + return "Selection<" + fPosition + ">"; + } + + public boolean equalsLocationOf(IEditorPart part) { + + if (fPosition == null) + return true; + + if (fPosition.isDeleted) + return false; + + ISelectionProvider provider= part.getSite().getSelectionProvider(); + ISelection selection= provider.getSelection(); + if (selection instanceof ITextSelection) { + ITextSelection textSelection= (ITextSelection) selection; + if (textSelection.getOffset() == fPosition.offset && textSelection.getLength() == fPosition.length) { + String text= textSelection.getText(); + if (text != null) { + try { + return text.equals(fDocument.get(fPosition.offset, fPosition.length)); + } catch (BadLocationException e) { + } + } + } + } + + return false; + } + + public void dispose() { + uninstallFromDocument(fDocument, fPosition); + fDocument= null; + fPosition= null; + fSavedPosition= null; + super.dispose(); + } + + public void clearState() { + fDocument= null; + fPosition= null; + fSavedPosition= null; + super.clearState(); + } + + public boolean mergeInto(NavigationLocation location) { + + if (location == null) + return false; + + if (getClass() != location.getClass()) + return false; + + TextSelectionNavigationLocation s= (TextSelectionNavigationLocation) location; + + if (fPosition == null || fPosition.isDeleted) + return true; + + if (s.fPosition == null || s.fPosition.isDeleted) { + uninstallFromDocument(fDocument, fPosition); + s.fDocument= fDocument; + s. fPosition= fPosition; + s.fSavedPosition= fSavedPosition; + return true; + } + + return s.fDocument == fDocument && s.fPosition.equals(fPosition); + } + + public void restore() { + if (fPosition == null || fPosition.isDeleted) + return; + + if (getEditorPart() instanceof ITextEditor) { + ITextEditor editor= (ITextEditor) getEditorPart(); + editor.selectAndReveal(fPosition.offset, fPosition.length); + } + } + + public void restoreState(IMemento memento) { + + IEditorPart part= getEditorPart(); + if (part instanceof ITextEditor) { + + // restore + fDocument= getDocument((ITextEditor) part); + + Integer offset= memento.getInteger(IWorkbenchConstants.TAG_X); + Integer length= memento.getInteger(IWorkbenchConstants.TAG_Y); + String deleted= memento.getString(IWorkbenchConstants.TAG_INFO); + + if (offset != null && length != null) { + fPosition= new Position(offset.intValue(), length.intValue()); + if (deleted != null) + fPosition.isDeleted= DELETED.equals(deleted) ? true : false; + } + + // activate + if (installOnDocument(fDocument, fPosition) && !part.isDirty()) + fSavedPosition= new Position(fPosition.offset, fPosition.length); + } + } + + public void saveState(IMemento memento) { + + // save + if (fSavedPosition != null) { + memento.putInteger(IWorkbenchConstants.TAG_X, fSavedPosition.offset); + memento.putInteger(IWorkbenchConstants.TAG_Y, fSavedPosition.length); + memento.putString(IWorkbenchConstants.TAG_INFO, (fSavedPosition.isDeleted ? DELETED : NOT_DELETED)); + } + + // deactivate + uninstallFromDocument(fDocument, fPosition); + } + + public void partSaved(IEditorPart part) { + + if (fPosition == null) + return; + + if (fSavedPosition == null) + fSavedPosition= new Position(0, 0); + + fSavedPosition.offset= fPosition.offset; + fSavedPosition.length= fPosition.length; + fSavedPosition.isDeleted= fPosition.isDeleted; + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/TextUtilities.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/TextUtilities.java new file mode 100644 index 00000000000..89651ab3f1c --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/TextUtilities.java @@ -0,0 +1,109 @@ +package org.eclipse.ui.texteditor; + +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ + + +/** + * Collection of text functions. + */ +class TextUtilities { + + /* + * 1GF86V3: ITPUI:WINNT - Internal errors using Find/Replace Dialog + * Copied from JFace text + */ + + + public final static String[] fgDelimiters= new String[] { "\n", "\r", "\r\n" }; //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ + + + /** + * Determines which one of fgDelimiters appears first in the list. If none of them the + * hint is returned. + */ + public static String determineLineDelimiter(String text, String hint) { + try { + int[] info= indexOf(fgDelimiters, text, 0); + return fgDelimiters[info[1]]; + } catch (ArrayIndexOutOfBoundsException x) { + } + return hint; + } + + /** + * Returns the position in the string greater than offset + * of the longest matching search string. + */ + public static int[] indexOf(String[] searchStrings, String text, int offset) { + + int[] result= { -1, -1 }; + + for (int i= 0; i < searchStrings.length; i++) { + int index= text.indexOf(searchStrings[i], offset); + if (index >= 0) { + + if (result[0] == -1) { + result[0]= index; + result[1]= i; + } else if (index < result[0]) { + result[0]= index; + result[1]= i; + } else if (index == result[0] && searchStrings[i].length() > searchStrings[result[1]].length()) { + result[0]= index; + result[1]= i; + } + } + } + + return result; + + } + + /** + * Returns the longest search string with which the given text ends. + */ + public static int endsWith(String[] searchStrings, String text) { + + int index= -1; + + for (int i= 0; i < searchStrings.length; i++) { + if (text.endsWith(searchStrings[i])) { + if (index == -1 || searchStrings[i].length() > searchStrings[index].length()) + index= i; + } + } + + return index; + } + + /** + * Returns the longest search string with which the given text starts. + */ + public static int startsWith(String[] searchStrings, String text) { + + int index= -1; + + for (int i= 0; i < searchStrings.length; i++) { + if (text.startsWith(searchStrings[i])) { + if (index == -1 || searchStrings[i].length() > searchStrings[index].length()) + index= i; + } + } + + return index; + } + + /** + * Returns whether the text equals one of the given compare strings. + */ + public static int equals(String[] compareStrings, String text) { + for (int i= 0; i < compareStrings.length; i++) { + if (text.equals(compareStrings[i])) + return i; + } + return -1; + } +}
\ No newline at end of file diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/WorkbenchChainedTextFontFieldEditor.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/WorkbenchChainedTextFontFieldEditor.java new file mode 100644 index 00000000000..f1eafb1c8aa --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/WorkbenchChainedTextFontFieldEditor.java @@ -0,0 +1,70 @@ +/********************************************************************** +Copyright (c) 2000, 2002 IBM Corp. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Common Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/cpl-v10.html + +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ + + +package org.eclipse.ui.texteditor; + + +import org.eclipse.swt.widgets.Composite; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Plugin; + +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.text.PropagatingFontFieldEditor; + +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.plugin.AbstractUIPlugin; + + +/** + * This font field editor implements chaining between the workbench's preference + * store and a given target preference store. Any time the workbench's preference + * for the text font changes, the change is propagated to the target store. + * Propagation means that the actual text font stored in the workbench store is set as + * default text font in the target store. If the target store does not contain a value + * rather than the default text font, the new default text font is immediately effective. + * + * @see org.eclipse.jface.preference.FontFieldEditor + * @since 2.0 + */ +public class WorkbenchChainedTextFontFieldEditor extends PropagatingFontFieldEditor { + + /** + * Creates a new font field editor with the given parameters. + * + * @param name the editor's name + * @param labelText the text shown as editor description + * @param parent the editor's parent widget + */ + public WorkbenchChainedTextFontFieldEditor(String name, String labelText, Composite parent) { + super(name, labelText, parent, EditorMessages.getString("WorkbenchChainedTextFontFieldEditor.defaultWorkbenchTextFont")); //$NON-NLS-1$ + } + + /** + * Starts the propagation of the text font preference set in the workbench + * to given target preference store using the given preference key. + * + * @param target the target preference store + * @param targetKey the key to be used in the target preference store + */ + public static void startPropagate(IPreferenceStore target, String targetKey) { + Plugin plugin= Platform.getPlugin(PlatformUI.PLUGIN_ID); + if (plugin instanceof AbstractUIPlugin) { + AbstractUIPlugin uiPlugin= (AbstractUIPlugin) plugin; + IPreferenceStore store= uiPlugin.getPreferenceStore(); + if (store != null) + PropagatingFontFieldEditor.startPropagate(store, JFaceResources.TEXT_FONT, target, targetKey); + } + } +} + diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/package.html b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/package.html new file mode 100644 index 00000000000..0ebf7f2db7f --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/package.html @@ -0,0 +1,30 @@ +<!doctype html public "-//w3c//dtd html 4.0 transitional//en"> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <meta name="Author" content="IBM"> + <meta name="GENERATOR" content="Mozilla/4.51 [en] (WinNT; I) [Netscape]"> + <title>Package-level Javadoc</title> +</head> +<body> +Provides a framework for text editors obeying to the +desktop rules. +<h2> +Package Specification</h2> +<tt>ITextEditor</tt> extends <tt>IEditorPart</tt> with text editor specific +functionality. Text editors use source viewers (<tt>ISourceViewer</tt>) +for displaying and editing the given editor input. In order to translate +the editor input into a document and vice versa (e.g., for saving a change), +a text editor uses document providers (<tt>IDocumentProvider</tt>). A document +provider is responsible not just for providing the document for a given +editor input but for the complete translation between the domain model +and the editor's internal document-based model. Document provider can be +shared between editors. +<br>The package provides a default implementation of <tt>ITextEditor</tt> +(<tt>AbstractTextEditor</tt>). This default implementation also covers +the editor's complete action management and activation. The package contains +a number of configurable and predefined actions. <tt>AbstractMarkerAnnotationModel</tt> +establishs a link between text annotations (<tt>Annotation</tt>) and desktop +markers (<tt>IMarker</tt>). +</body> +</html> |