--- /dev/null
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+package net.sourceforge.phpdt.internal.ui.text.link;
+
+import org.eclipse.jface.text.IDocumentExtension;
+import org.eclipse.jface.text.Position;
+
+/**
+ * A listener for highlight change notification and exititing linked mode.
+ */
+public interface LinkedPositionListener {
+
+ /**
+ * Notifies that the linked mode has been left. On success, all changes
+ * are kept, otherwise all changes made to the linked positions are restored
+ * to the state before entering linked mode.
+ */
+ void exit(boolean success);
+
+ /**
+ * Notifies the changed linked position. The listener is asked
+ * to reposition the caret at the given offset.
+ *
+ * @param position the linked position which initiated the change.
+ * @param caretOffset the caret offset relative to the position.
+ */
+ void setCurrentPosition(Position position, int caretOffset);
+
+}
--- /dev/null
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+package net.sourceforge.phpdt.internal.ui.text.link;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.sourceforge.phpeclipse.PHPeclipsePlugin;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentExtension;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.IPositionUpdater;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.TypedPosition;
+import org.eclipse.jface.util.Assert;
+
+//import org.eclipse.jdt.internal.ui.JavaPlugin;
+
+/**
+ * This class manages linked positions in a document. Positions are linked
+ * by type names. If positions have the same type name, they are considered
+ * as <em>linked</em>.
+ *
+ * The manager remains active on a document until any of the following actions
+ * occurs:
+ *
+ * <ul>
+ * <li>A document change is performed which would invalidate any of the
+ * above constraints.</li>
+ *
+ * <li>The method <code>uninstall()</code> is called.</li>
+ *
+ * <li>Another instance of <code>LinkedPositionManager</code> tries to
+ * gain control of the same document.
+ * </ul>
+ */
+public class LinkedPositionManager implements IDocumentListener, IPositionUpdater {
+
+ private static class PositionComparator implements Comparator {
+ /*
+ * @see Comparator#compare(Object, Object)
+ */
+ public int compare(Object object0, Object object1) {
+ Position position0= (Position) object0;
+ Position position1= (Position) object1;
+
+ return position0.getOffset() - position1.getOffset();
+ }
+ }
+
+ private class Replace implements IDocumentExtension.IReplace {
+
+ private Position fReplacePosition;
+ private int fReplaceDeltaOffset;
+ private int fReplaceLength;
+ private String fReplaceText;
+
+ public Replace(Position position, int deltaOffset, int length, String text) {
+ fReplacePosition= position;
+ fReplaceDeltaOffset= deltaOffset;
+ fReplaceLength= length;
+ fReplaceText= text;
+ }
+
+ public void perform(IDocument document, IDocumentListener owner) {
+ document.removeDocumentListener(owner);
+ try {
+ document.replace(fReplacePosition.getOffset() + fReplaceDeltaOffset, fReplaceLength, fReplaceText);
+ } catch (BadLocationException e) {
+ PHPeclipsePlugin.log(e);
+ // TBD
+ }
+ document.addDocumentListener(owner);
+ }
+ }
+
+ private static final String LINKED_POSITION= "LinkedPositionManager.linked.position"; //$NON-NLS-1$
+ private static final Comparator fgPositionComparator= new PositionComparator();
+ private static final Map fgActiveManagers= new HashMap();
+
+ private IDocument fDocument;
+
+ private LinkedPositionListener fListener;
+
+ /**
+ * Creates a <code>LinkedPositionManager</code> for a <code>IDocument</code>.
+ *
+ * @param document the document to use with linked positions.
+ */
+ public LinkedPositionManager(IDocument document) {
+ Assert.isNotNull(document);
+
+ fDocument= document;
+ install();
+ }
+
+ /**
+ * Sets a listener to notify changes of current linked position.
+ */
+ public void setLinkedPositionListener(LinkedPositionListener listener) {
+ fListener= listener;
+ }
+
+ /**
+ * Adds a linked position to the manager.
+ * There are the following constraints for linked positions:
+ *
+ * <ul>
+ * <li>Any two positions have spacing of at least one character.
+ * This implies that two positions must not overlap.</li>
+ *
+ * <li>The string at any position must not contain line delimiters.</li>
+ * </ul>
+ *
+ * @param offset the offset of the position.
+ * @param length the length of the position.
+ */
+ public void addPosition(int offset, int length) throws BadLocationException {
+ Position[] positions= getPositions(fDocument);
+
+ if (positions != null) {
+ for (int i = 0; i < positions.length; i++)
+ if (collides(positions[i], offset, length))
+ throw new BadLocationException(LinkedPositionMessages.getString(("LinkedPositionManager.error.position.collision"))); //$NON-NLS-1$
+ }
+
+ String type= fDocument.get(offset, length);
+
+ if (containsLineDelimiters(type))
+ throw new BadLocationException(LinkedPositionMessages.getString(("LinkedPositionManager.error.contains.line.delimiters"))); //$NON-NLS-1$
+
+ try {
+ fDocument.addPosition(LINKED_POSITION, new TypedPosition(offset, length, type));
+ } catch (BadPositionCategoryException e) {
+ PHPeclipsePlugin.log(e);
+ Assert.isTrue(false);
+ }
+ }
+
+ /**
+ * Tests if a manager is already active for a document.
+ */
+ public static boolean hasActiveManager(IDocument document) {
+ return fgActiveManagers.get(document) != null;
+ }
+
+ private void install() {
+ LinkedPositionManager manager= (LinkedPositionManager) fgActiveManagers.get(fDocument);
+ if (manager != null)
+ manager.leave(true);
+
+ fgActiveManagers.put(fDocument, this);
+
+ fDocument.addPositionCategory(LINKED_POSITION);
+ fDocument.addPositionUpdater(this);
+ fDocument.addDocumentListener(this);
+ }
+
+ /**
+ * Leaves the linked mode. If unsuccessful, the linked positions
+ * are restored to the values at the time they were added.
+ */
+ public void uninstall(boolean success) {
+ fDocument.removeDocumentListener(this);
+
+ try {
+ Position[] positions= getPositions(fDocument);
+ if ((!success) && (positions != null)) {
+ // restore
+ for (int i= 0; i != positions.length; i++) {
+ TypedPosition position= (TypedPosition) positions[i];
+ fDocument.replace(position.getOffset(), position.getLength(), position.getType());
+ }
+ }
+
+ fDocument.removePositionCategory(LINKED_POSITION);
+
+ } catch (BadLocationException e) {
+ PHPeclipsePlugin.log(e);
+ Assert.isTrue(false);
+
+ } catch (BadPositionCategoryException e) {
+ PHPeclipsePlugin.log(e);
+ Assert.isTrue(false);
+
+ } finally {
+ fDocument.removePositionUpdater(this);
+ fgActiveManagers.remove(fDocument);
+ }
+ }
+
+ /**
+ * Returns the first linked position.
+ *
+ * @return returns <code>null</code> if no linked position exist.
+ */
+ public Position getFirstPosition() {
+ return getNextPosition(-1);
+ }
+
+ /**
+ * Returns the next linked position with an offset greater than <code>offset</code>.
+ * If another position with the same type and offset lower than <code>offset</code>
+ * exists, the position is skipped.
+ *
+ * @return returns <code>null</code> if no linked position exist.
+ */
+ public Position getNextPosition(int offset) {
+ Position[] positions= getPositions(fDocument);
+ return findNextPosition(positions, offset);
+ }
+
+ private static Position findNextPosition(Position[] positions, int offset) {
+ // skip already visited types
+ for (int i= 0; i != positions.length; i++) {
+ if (positions[i].getOffset() > offset) {
+ String type= ((TypedPosition) positions[i]).getType();
+ int j;
+ for (j = 0; j != i; j++)
+ if (((TypedPosition) positions[j]).getType().equals(type))
+ break;
+
+ if (j == i)
+ return positions[i];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the position with the greatest offset smaller than <code>offset</code>.
+ *
+ * @return returns <code>null</code> if no linked position exist.
+ */
+ public Position getPreviousPosition(int offset) {
+ Position[] positions= getPositions(fDocument);
+ if (positions == null)
+ return null;
+
+ Position lastPosition= null;
+ Position position= getFirstPosition();
+
+ while ((position != null) && (position.getOffset() < offset)) {
+ lastPosition= position;
+ position= findNextPosition(positions, position.getOffset());
+ }
+
+ return lastPosition;
+ }
+
+ private static Position[] getPositions(IDocument document) {
+ try {
+ Position[] positions= document.getPositions(LINKED_POSITION);
+ Arrays.sort(positions, fgPositionComparator);
+ return positions;
+
+ } catch (BadPositionCategoryException e) {
+ PHPeclipsePlugin.log(e);
+ Assert.isTrue(false);
+ }
+
+ return null;
+ }
+
+ public static boolean includes(Position position, int offset, int length) {
+ return
+ (offset >= position.getOffset()) &&
+ (offset + length <= position.getOffset() + position.getLength());
+ }
+
+ public static boolean excludes(Position position, int offset, int length) {
+ return
+ (offset + length <= position.getOffset()) ||
+ (position.getOffset() + position.getLength() <= offset);
+ }
+
+ /*
+ * Collides if spacing if positions intersect each other or are adjacent.
+ */
+ private static boolean collides(Position position, int offset, int length) {
+ return
+ (offset <= position.getOffset() + position.getLength()) &&
+ (position.getOffset() <= offset + length);
+ }
+
+ private void leave(boolean success) {
+ uninstall(success);
+
+ if (fListener != null)
+ fListener.exit(success);
+ }
+
+ /*
+ * @see IDocumentListener#documentAboutToBeChanged(DocumentEvent)
+ */
+ public void documentAboutToBeChanged(DocumentEvent event) {
+ IDocument document= event.getDocument();
+
+ Position[] positions= getPositions(document);
+ Position position= findCurrentEditablePosition(positions, event.getOffset());
+
+ // modification outside editable position
+ if (position == null) {
+ position= findCurrentPosition(positions, event.getOffset());
+
+ // modification outside any position
+ if (position == null) {
+ // check for destruction of constraints (spacing of at least 1)
+ if ((event.getText().length() == 0) &&
+ (findCurrentPosition(positions, event.getOffset()) != null) &&
+ (findCurrentPosition(positions, event.getOffset() + event.getLength()) != null))
+ {
+ leave(true);
+ }
+
+ // modification intersects non-editable position
+ } else {
+ leave(true);
+ }
+
+ // modification intersects editable position
+ } else {
+ // modificaction inside editable position
+ if (includes(position, event.getOffset(), event.getLength())) {
+ if (containsLineDelimiters(event.getText()))
+ leave(true);
+
+ // modificaction exceeds editable position
+ } else {
+ leave(true);
+ }
+ }
+ }
+
+ /*
+ * @see IDocumentListener#documentChanged(DocumentEvent)
+ */
+ public void documentChanged(DocumentEvent event) {
+ IDocument document= event.getDocument();
+
+ Position[] positions= getPositions(document);
+ TypedPosition currentPosition= (TypedPosition) findCurrentEditablePosition(positions, event.getOffset());
+
+ // ignore document changes (assume it won't invalidate constraints)
+ if (currentPosition == null)
+ return;
+
+ int deltaOffset= event.getOffset() - currentPosition.getOffset();
+
+ if (fListener != null)
+ fListener.setCurrentPosition(currentPosition, deltaOffset + event.getText().length());
+
+ for (int i= 0; i != positions.length; i++) {
+ TypedPosition p= (TypedPosition) positions[i];
+
+ if (p.getType().equals(currentPosition.getType()) && !p.equals(currentPosition)) {
+ Replace replace= new Replace(p, deltaOffset, event.getLength(), event.getText());
+ ((IDocumentExtension) document).registerPostNotificationReplace(this, replace);
+ }
+ }
+ }
+
+ /*
+ * @see IPositionUpdater#update(DocumentEvent)
+ */
+ public void update(DocumentEvent event) {
+ int deltaLength= event.getText().length() - event.getLength();
+
+ Position[] positions= getPositions(event.getDocument());
+ TypedPosition currentPosition= (TypedPosition) findCurrentPosition(positions, event.getOffset());
+
+ // document change outside positions
+ if (currentPosition == null) {
+
+ for (int i= 0; i != positions.length; i++) {
+ TypedPosition position= (TypedPosition) positions[i];
+ int offset= position.getOffset();
+
+ if (offset >= event.getOffset())
+ position.setOffset(offset + deltaLength);
+ }
+
+ // document change within a position
+ } else {
+ int length= currentPosition.getLength();
+
+ for (int i= 0; i != positions.length; i++) {
+ TypedPosition position= (TypedPosition) positions[i];
+ int offset= position.getOffset();
+
+ if (position.equals(currentPosition)) {
+ position.setLength(length + deltaLength);
+ } else if (offset > currentPosition.getOffset()) {
+ position.setOffset(offset + deltaLength);
+ }
+ }
+ }
+ }
+
+ private static Position findCurrentPosition(Position[] positions, int offset) {
+ for (int i= 0; i != positions.length; i++)
+ if (includes(positions[i], offset, 0))
+ return positions[i];
+
+ return null;
+ }
+
+ private static Position findCurrentEditablePosition(Position[] positions, int offset) {
+ Position position= positions[0];
+
+ while ((position != null) && !includes(position, offset, 0))
+ position= findNextPosition(positions, position.getOffset());
+
+ return position;
+ }
+
+ private boolean containsLineDelimiters(String string) {
+ String[] delimiters= fDocument.getLegalLineDelimiters();
+
+ for (int i= 0; i != delimiters.length; i++)
+ if (string.indexOf(delimiters[i]) != -1)
+ return true;
+
+ return false;
+ }
+
+ /**
+ * Test if ok to modify through UI.
+ */
+ public boolean anyPositionIncludes(int offset, int length) {
+ Position[] positions= getPositions(fDocument);
+
+ Position position= findCurrentEditablePosition(positions, offset);
+ if (position == null)
+ return false;
+
+ return includes(position, offset, length);
+ }
+
+}
--- /dev/null
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+package net.sourceforge.phpdt.internal.ui.text.link;
+
+import java.text.MessageFormat;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+public class LinkedPositionMessages {
+
+ private static final String RESOURCE_BUNDLE= LinkedPositionMessages.class.getName();
+ private static ResourceBundle fgResourceBundle= ResourceBundle.getBundle(RESOURCE_BUNDLE);
+
+ private LinkedPositionMessages() {
+ }
+
+ public static String getString(String key) {
+ try {
+ return fgResourceBundle.getString(key);
+ } catch (MissingResourceException e) {
+ return '!' + key + '!';
+ }
+ }
+
+ /**
+ * Gets a string from the resource bundle and formats it with the argument
+ *
+ * @param key the string used to get the bundle value, must not be null
+ */
+ public static String getFormattedString(String key, Object arg) {
+ return MessageFormat.format(getString(key), new Object[] { arg });
+ }
+
+
+ /**
+ * Gets a string from the resource bundle and formats it with arguments
+ */
+ public static String getFormattedString(String key, Object[] args) {
+ return MessageFormat.format(getString(key), args);
+ }
+}
--- /dev/null
+#########################################
+# (c) Copyright IBM Corp. 2000, 2001.
+# All Rights Reserved.
+#########################################
+
+LinkedPositionUI.error.title=Error in LinkedPositionError
+
+LinkedPositionManager.error.contains.line.delimiters=String contains line delimiters.
+LinkedPositionManager.error.position.collision=Linked position collides with another linked position.
--- /dev/null
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+package net.sourceforge.phpdt.internal.ui.text.link;
+
+import java.lang.reflect.InvocationTargetException;
+
+import net.sourceforge.phpeclipse.PHPeclipsePlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceConverter;
+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.IRegion;
+import org.eclipse.jface.text.ITextInputListener;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.util.Assert;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.custom.VerifyKeyListener;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.events.VerifyListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * A user interface for <code>LinkedPositionManager</code>, using <code>ITextViewer</code>.
+ */
+public class LinkedPositionUI implements LinkedPositionListener,
+ ITextInputListener, ModifyListener, VerifyListener, VerifyKeyListener, PaintListener, IPropertyChangeListener {
+
+ /**
+ * A listener for notification when the user cancelled the edit operation.
+ */
+ public interface ExitListener {
+ void exit(boolean accept);
+ }
+
+ /** Preference key for linked position color */
+ // public final static String LINKED_POSITION_COLOR= "_linkedPositionColor"; //$NON-NLS-1$
+ // leave flags
+ private static final int UNINSTALL= 1; // uninstall linked position manager
+ private static final int COMMIT= 2; // commit changes
+ private static final int DOCUMENT_CHANGED= 4; // document has changed
+ private static final int UPDATE_CARET= 8; // update caret
+
+ private static final String CARET_POSITION= "LinkedPositionUI.caret.position"; //$NON-NLS-1$
+ private static final IPositionUpdater fgUpdater= new DefaultPositionUpdater(CARET_POSITION);
+ private static final IPreferenceStore fgStore= PHPeclipsePlugin.getDefault().getPreferenceStore();
+
+ private final ITextViewer fViewer;
+ private final LinkedPositionManager fManager;
+ private Color fFrameColor;
+
+ private int fFinalCaretOffset= -1; // no final caret offset
+
+ private Position fFramePosition;
+ private int fCaretOffset;
+
+ private ExitListener fExitListener;
+
+ /**
+ * Creates a user interface for <code>LinkedPositionManager</code>.
+ *
+ * @param viewer the text viewer.
+ * @param manager the <code>LinkedPositionManager</code> managing a <code>IDocument</code> of the <code>ITextViewer</code>.
+ */
+ public LinkedPositionUI(ITextViewer viewer, LinkedPositionManager manager) {
+ Assert.isNotNull(viewer);
+ Assert.isNotNull(manager);
+
+ fViewer= viewer;
+ fManager= manager;
+
+ fManager.setLinkedPositionListener(this);
+
+ initializeHighlightColor(viewer);
+ }
+
+ /*
+ * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent event) {
+ // if (event.getProperty().equals(CompilationUnitEditor.LINKED_POSITION_COLOR)) {
+ if (event.getProperty().equals(PHPeclipsePlugin.LINKED_POSITION_COLOR)) {
+ initializeHighlightColor(fViewer);
+ redrawRegion();
+ }
+ }
+
+ private void initializeHighlightColor(ITextViewer viewer) {
+
+ if (fFrameColor != null)
+ fFrameColor.dispose();
+
+ StyledText text= viewer.getTextWidget();
+ if (text != null) {
+ Display display= text.getDisplay();
+ // fFrameColor= createColor(fgStore, CompilationUnitEditor.LINKED_POSITION_COLOR, display);
+ fFrameColor= createColor(fgStore, PHPeclipsePlugin.LINKED_POSITION_COLOR, display);
+ }
+ }
+
+ /**
+ * Creates a color from the information stored in the given preference store.
+ * Returns <code>null</code> if there is no such information available.
+ */
+ 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;
+ }
+
+ /**
+ * Sets the final position of the caret when the linked mode is exited
+ * successfully by leaving the last linked position using TAB.
+ */
+ public void setFinalCaretOffset(int offset) {
+ fFinalCaretOffset= offset;
+ }
+
+ /**
+ * Sets a <code>CancelListener</code> which is notified if the linked mode
+ * is exited unsuccessfully by hitting ESC.
+ */
+ public void setCancelListener(ExitListener listener) {
+ fExitListener= listener;
+ }
+
+ /*
+ * @see LinkedPositionManager.LinkedPositionListener#setCurrentPositions(Position, int)
+ */
+ public void setCurrentPosition(Position position, int caretOffset) {
+ if (!fFramePosition.equals(position)) {
+ redrawRegion();
+ fFramePosition= position;
+ }
+
+ fCaretOffset= caretOffset;
+ }
+
+ /**
+ * Enters the linked mode. The linked mode can be left by calling
+ * <code>exit</code>.
+ *
+ * @see #exit(boolean)
+ */
+ public void enter() {
+ // track final caret
+ IDocument document= fViewer.getDocument();
+ document.addPositionCategory(CARET_POSITION);
+ document.addPositionUpdater(fgUpdater);
+ try {
+ if (fFinalCaretOffset != -1)
+ document.addPosition(CARET_POSITION, new Position(fFinalCaretOffset));
+ } catch (BadLocationException e) {
+ handleException(fViewer.getTextWidget().getShell(), e);
+
+ } catch (BadPositionCategoryException e) {
+ PHPeclipsePlugin.log(e);
+ Assert.isTrue(false);
+ }
+
+ fViewer.addTextInputListener(this);
+
+ ITextViewerExtension extension= (ITextViewerExtension) fViewer;
+ extension.prependVerifyKeyListener(this);
+
+ StyledText text= fViewer.getTextWidget();
+ text.addVerifyListener(this);
+ text.addModifyListener(this);
+ text.addPaintListener(this);
+ text.showSelection();
+
+ fFramePosition= fManager.getFirstPosition();
+ if (fFramePosition == null)
+ leave(UNINSTALL | COMMIT | UPDATE_CARET);
+
+ fgStore.addPropertyChangeListener(this);
+ }
+
+ /*
+ * @see LinkedPositionManager.LinkedPositionListener#exit(boolean)
+ */
+ public void exit(boolean success) {
+ // no UNINSTALL since manager has already uninstalled itself
+ leave((success ? COMMIT : 0) | UPDATE_CARET);
+ }
+
+ /**
+ * Returns the cursor selection, after having entered the linked mode.
+ * <code>enter()</code> must be called prior to a call to this method.
+ */
+ public IRegion getSelectedRegion() {
+ if (fFramePosition == null)
+ return new Region(fFinalCaretOffset, 0);
+ else
+ return new Region(fFramePosition.getOffset(), fFramePosition.getLength());
+ }
+
+ private void leave(int flags) {
+ if ((flags & UNINSTALL) != 0)
+ fManager.uninstall((flags & COMMIT) != 0);
+
+ fgStore.removePropertyChangeListener(this);
+
+ if (fFrameColor != null) {
+ fFrameColor.dispose();
+ fFrameColor= null;
+ }
+
+ StyledText text= fViewer.getTextWidget();
+ text.removePaintListener(this);
+ text.removeModifyListener(this);
+ text.removeVerifyListener(this);
+
+ ITextViewerExtension extension= (ITextViewerExtension) fViewer;
+ extension.removeVerifyKeyListener(this);
+
+ fViewer.removeTextInputListener(this);
+
+ try {
+ IRegion region= fViewer.getVisibleRegion();
+ IDocument document= fViewer.getDocument();
+
+ if (((flags & COMMIT) != 0) &&
+ ((flags & DOCUMENT_CHANGED) == 0) &&
+ ((flags & UPDATE_CARET) != 0))
+ {
+ Position[] positions= document.getPositions(CARET_POSITION);
+
+ if ((positions != null) && (positions.length != 0)) {
+ int offset= positions[0].getOffset() - region.getOffset();
+ if ((offset >= 0) && (offset <= region.getLength()))
+ text.setSelection(offset, offset);
+ }
+ }
+
+ document.removePositionUpdater(fgUpdater);
+ document.removePositionCategory(CARET_POSITION);
+
+ if (fExitListener != null)
+ fExitListener.exit(
+ ((flags & COMMIT) != 0) ||
+ ((flags & DOCUMENT_CHANGED) != 0));
+
+ } catch (BadPositionCategoryException e) {
+ PHPeclipsePlugin.log(e);
+ Assert.isTrue(false);
+ }
+
+ if ((flags & DOCUMENT_CHANGED) == 0)
+ text.redraw();
+ }
+
+ private void next() {
+ redrawRegion();
+
+ fFramePosition= fManager.getNextPosition(fFramePosition.getOffset());
+ if (fFramePosition == null) {
+ leave(UNINSTALL | COMMIT | UPDATE_CARET);
+ } else {
+ selectRegion();
+ redrawRegion();
+ }
+ }
+
+ private void previous() {
+ redrawRegion();
+
+ Position position= fManager.getPreviousPosition(fFramePosition.getOffset());
+ if (position == null) {
+ fViewer.getTextWidget().getDisplay().beep();
+ } else {
+ fFramePosition= position;
+ selectRegion();
+ redrawRegion();
+ }
+ }
+
+ /*
+ * @see VerifyKeyListener#verifyKey(VerifyEvent)
+ */
+ public void verifyKey(VerifyEvent event) {
+ switch (event.character) {
+ // [SHIFT-]TAB = hop between edit boxes
+ case 0x09:
+ {
+ Point selection= fViewer.getTextWidget().getSelection();
+ IRegion region= fViewer.getVisibleRegion();
+ int offset= selection.x + region.getOffset();
+ int length= selection.y - selection.x;
+
+ // if tab was treated as a document change, would it exceed variable range?
+ if (!LinkedPositionManager.includes(fFramePosition, offset, length)) {
+ leave(UNINSTALL | COMMIT | UPDATE_CARET);
+ return;
+ }
+ }
+
+ if (event.stateMask == SWT.SHIFT)
+ previous();
+ else
+ next();
+
+ event.doit= false;
+ break;
+
+ // ENTER
+ case 0x0D:
+ leave(UNINSTALL | COMMIT | UPDATE_CARET);
+ event.doit= false;
+ break;
+
+ // ESC
+ case 0x1B:
+ leave(UNINSTALL | COMMIT);
+ event.doit= false;
+ break;
+ }
+ }
+
+ /*
+ * @see VerifyListener#verifyText(VerifyEvent)
+ */
+ public void verifyText(VerifyEvent event) {
+ if (!event.doit)
+ return;
+
+ IRegion region= fViewer.getVisibleRegion();
+
+ int offset= event.start + region.getOffset();
+ int length= event.end - event.start;
+
+ // allow changes only within linked positions when coming through UI
+ if (!fManager.anyPositionIncludes(offset, length))
+ leave(UNINSTALL | COMMIT);
+ }
+
+ /*
+ * @see PaintListener#paintControl(PaintEvent)
+ */
+ public void paintControl(PaintEvent event) {
+ if (fFramePosition == null)
+ return;
+
+ IRegion region= fViewer.getVisibleRegion();
+
+ // #6824
+ if (!includes(region, fFramePosition)) {
+ leave(UNINSTALL | COMMIT | DOCUMENT_CHANGED);
+ return;
+ }
+
+ int offset= fFramePosition.getOffset() - region.getOffset();
+ int length= fFramePosition.getLength();
+
+ StyledText text= fViewer.getTextWidget();
+
+ // support for bidi
+ Point minLocation= getMinimumLocation(text, offset, length);
+ Point maxLocation= getMaximumLocation(text, offset, length);
+
+ int x1= minLocation.x;
+ int x2= minLocation.x + maxLocation.x - minLocation.x - 1;
+ int y= minLocation.y + text.getLineHeight() - 1;
+
+ GC gc= event.gc;
+ gc.setForeground(fFrameColor);
+ gc.drawLine(x1, y, x2, y);
+ }
+
+ private static Point getMinimumLocation(StyledText text, int offset, int length) {
+ Point minLocation= new Point(Integer.MAX_VALUE, Integer.MAX_VALUE);
+
+ for (int i= 0; i <= length; i++) {
+ Point location= text.getLocationAtOffset(offset + i);
+
+ if (location.x < minLocation.x)
+ minLocation.x= location.x;
+ if (location.y < minLocation.y)
+ minLocation.y= location.y;
+ }
+
+ return minLocation;
+ }
+
+ private static Point getMaximumLocation(StyledText text, int offset, int length) {
+ Point maxLocation= new Point(Integer.MIN_VALUE, Integer.MIN_VALUE);
+
+ for (int i= 0; i <= length; i++) {
+ Point location= text.getLocationAtOffset(offset + i);
+
+ if (location.x > maxLocation.x)
+ maxLocation.x= location.x;
+ if (location.y > maxLocation.y)
+ maxLocation.y= location.y;
+ }
+
+ return maxLocation;
+ }
+
+ private void redrawRegion() {
+ IRegion region= fViewer.getVisibleRegion();
+
+ if (!includes(region, fFramePosition)) {
+ leave(UNINSTALL | COMMIT | DOCUMENT_CHANGED);
+ return;
+ }
+
+ int offset= fFramePosition.getOffset() - region.getOffset();
+ int length= fFramePosition.getLength();
+
+ StyledText text= fViewer.getTextWidget();
+ if (text != null && !text.isDisposed())
+ text.redrawRange(offset, length, true);
+ }
+
+ private void selectRegion() {
+ IRegion region= fViewer.getVisibleRegion();
+
+ if (!includes(region, fFramePosition)) {
+ leave(UNINSTALL | COMMIT | DOCUMENT_CHANGED);
+ return;
+ }
+
+ int start= fFramePosition.getOffset() - region.getOffset();
+ int end= fFramePosition.getLength() + start;
+
+ StyledText text= fViewer.getTextWidget();
+ if (text != null && !text.isDisposed())
+ text.setSelection(start, end);
+ }
+
+ private void updateCaret() {
+ IRegion region= fViewer.getVisibleRegion();
+
+ if (!includes(region, fFramePosition)) {
+ leave(UNINSTALL | COMMIT | DOCUMENT_CHANGED);
+ return;
+ }
+
+ int offset= fFramePosition.getOffset() + fCaretOffset - region.getOffset();
+
+ if ((offset >= 0) && (offset <= region.getLength())) {
+ StyledText text= fViewer.getTextWidget();
+ if (text != null && !text.isDisposed())
+ text.setCaretOffset(offset);
+ }
+ }
+
+ /*
+ * @see ModifyListener#modifyText(ModifyEvent)
+ */
+ public void modifyText(ModifyEvent e) {
+ // reposition caret after StyledText
+ redrawRegion();
+ updateCaret();
+ }
+
+ private static void handleException(Shell shell, Exception e) {
+ String title= LinkedPositionMessages.getString("LinkedPositionUI.error.title"); //$NON-NLS-1$
+ if (e instanceof CoreException)
+ PHPeclipsePlugin.log(e);
+ // ExceptionHandler.handle((CoreException)e, shell, title, null);
+ else if (e instanceof InvocationTargetException)
+ PHPeclipsePlugin.log(e);
+ // ExceptionHandler.handle((InvocationTargetException)e, shell, title, null);
+ else {
+ MessageDialog.openError(shell, title, e.getMessage());
+ PHPeclipsePlugin.log(e);
+ }
+ }
+
+ /*
+ * @see ITextInputListener#inputDocumentAboutToBeChanged(IDocument, IDocument)
+ */
+ public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
+ // 5326: leave linked mode on document change
+ int flags= UNINSTALL | COMMIT | (oldInput.equals(newInput) ? 0 : DOCUMENT_CHANGED);
+ leave(flags);
+ }
+
+ /*
+ * @see ITextInputListener#inputDocumentChanged(IDocument, IDocument)
+ */
+ public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
+ }
+
+ private static boolean includes(IRegion region, Position position) {
+ return
+ position.getOffset() >= region.getOffset() &&
+ position.getOffset() + position.getLength() <= region.getOffset() + region.getLength();
+ }
+
+}
\ No newline at end of file
import net.sourceforge.phpdt.internal.corext.template.TemplateContext;
import net.sourceforge.phpdt.internal.corext.template.TemplateMessages;
import net.sourceforge.phpdt.internal.corext.template.TemplatePosition;
-import net.sourceforge.phpeclipse.PHPeclipsePlugin;
-import org.eclipse.core.runtime.CoreException;
import net.sourceforge.phpdt.internal.corext.template.java.CompilationUnitContext;
import net.sourceforge.phpdt.internal.corext.template.java.JavaTemplateMessages;
+import net.sourceforge.phpdt.internal.ui.text.java.IJavaCompletionProposal;
+import net.sourceforge.phpeclipse.PHPeclipsePlugin;
+import org.eclipse.core.runtime.CoreException;
+import net.sourceforge.phpdt.internal.ui.text.link.LinkedPositionManager;
+import net.sourceforge.phpdt.internal.ui.text.link.LinkedPositionUI;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Shell;
-//import org.eclipse.jdt.internal.ui.JavaPlugin;
-import net.sourceforge.phpdt.internal.ui.text.java.IJavaCompletionProposal;
//import org.eclipse.jdt.internal.ui.text.link.LinkedPositionManager;
//import org.eclipse.jdt.internal.ui.text.link.LinkedPositionUI;
//import org.eclipse.jdt.internal.ui.util.ExceptionHandler;
private TemplateBuffer fTemplateBuffer;
private String fOldText;
-// private IRegion fSelectedRegion; // initialized by apply()
+ private IRegion fSelectedRegion; // initialized by apply()
/**
* Creates a template proposal with a template and its context.
document.replace(start, end - start, templateString);
// translate positions
- // LinkedPositionManager manager= new LinkedPositionManager(document);
+ LinkedPositionManager manager= new LinkedPositionManager(document);
TemplatePosition[] variables= fTemplateBuffer.getVariables();
for (int i= 0; i != variables.length; i++) {
TemplatePosition variable= variables[i];
int[] offsets= variable.getOffsets();
int length= variable.getLength();
-// for (int j= 0; j != offsets.length; j++)
-// manager.addPosition(offsets[j] + start, length);
+ for (int j= 0; j != offsets.length; j++)
+ manager.addPosition(offsets[j] + start, length);
}
-// LinkedPositionUI editor= new LinkedPositionUI(fViewer, manager);
-// editor.setFinalCaretOffset(getCaretOffset(fTemplateBuffer) + start);
-// editor.enter();
+ LinkedPositionUI editor= new LinkedPositionUI(fViewer, manager);
+ editor.setFinalCaretOffset(getCaretOffset(fTemplateBuffer) + start);
+ editor.enter();
-// fSelectedRegion= editor.getSelectedRegion();
+ fSelectedRegion= editor.getSelectedRegion();
} catch (BadLocationException e) {
PHPeclipsePlugin.log(e);
* @see ICompletionProposal#getSelection(IDocument)
*/
public Point getSelection(IDocument document) {
-// return new Point(fSelectedRegion.getOffset(), fSelectedRegion.getLength());
- return null;
+ return new Point(fSelectedRegion.getOffset(), fSelectedRegion.getLength());
+// return null;
}
/*
public static final String PHP_STRING = "_php_string";
public static final String PHP_DEFAULT = "_php_default";
+ public static final String LINKED_POSITION_COLOR= "_linkedPositionColor";
+
}
\ No newline at end of file
PreferenceConverter.setDefault(store, PHP_KEYWORD, PHPColorProvider.KEYWORD);
PreferenceConverter.setDefault(store, PHP_VARIABLE, PHPColorProvider.VARIABLE);
PreferenceConverter.setDefault(store, PHP_FUNCTIONNAME, PHPColorProvider.FUNCTION_NAME);
- PreferenceConverter.setDefault(store, PHP_STRING, PHPColorProvider.STRING);
+ PreferenceConverter.setDefault(store, PHP_STRING, PHPColorProvider.STRING);
PreferenceConverter.setDefault(store, PHP_DEFAULT, PHPColorProvider.DEFAULT);
+ PreferenceConverter.setDefault(store, LINKED_POSITION_COLOR, PHPColorProvider.LINKED_POSITION_COLOR);
}
public static final RGB FUNCTION_NAME= new RGB(127, 127, 159);
public static final RGB STRING= new RGB(42, 0, 255);
public static final RGB DEFAULT= new RGB(0, 0, 0);
+
+ public static final RGB LINKED_POSITION_COLOR= new RGB(0, 0, 0);
protected Map fColorTable= new HashMap(10);