fixed bug in template expansion
authorkhartlage <khartlage>
Mon, 9 Dec 2002 18:23:11 +0000 (18:23 +0000)
committerkhartlage <khartlage>
Mon, 9 Dec 2002 18:23:11 +0000 (18:23 +0000)
net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/link/LinkedPositionListener.java [new file with mode: 0644]
net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/link/LinkedPositionManager.java [new file with mode: 0644]
net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/link/LinkedPositionMessages.java [new file with mode: 0644]
net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/link/LinkedPositionMessages.properties [new file with mode: 0644]
net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/link/LinkedPositionUI.java [new file with mode: 0644]
net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/template/TemplateProposal.java
net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/IPreferenceConstants.java
net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/PHPeclipsePlugin.java
net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/util/PHPColorProvider.java

diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/link/LinkedPositionListener.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/link/LinkedPositionListener.java
new file mode 100644 (file)
index 0000000..94028c9
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * (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);
+
+}
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/link/LinkedPositionManager.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/link/LinkedPositionManager.java
new file mode 100644 (file)
index 0000000..4ce53ee
--- /dev/null
@@ -0,0 +1,448 @@
+/*
+ * (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);
+       }
+       
+}
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/link/LinkedPositionMessages.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/link/LinkedPositionMessages.java
new file mode 100644 (file)
index 0000000..4a6b8fb
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * (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);
+       }
+}
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/link/LinkedPositionMessages.properties b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/link/LinkedPositionMessages.properties
new file mode 100644 (file)
index 0000000..267939d
--- /dev/null
@@ -0,0 +1,9 @@
+#########################################
+# (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.
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/link/LinkedPositionUI.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/link/LinkedPositionUI.java
new file mode 100644 (file)
index 0000000..a9389ea
--- /dev/null
@@ -0,0 +1,526 @@
+/*
+ * (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
index 5178caa..ca3ac38 100644 (file)
@@ -9,10 +9,13 @@ import net.sourceforge.phpdt.internal.corext.template.TemplateBuffer;
 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;
@@ -22,8 +25,6 @@ import org.eclipse.jface.text.contentassist.IContextInformation;
 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;
@@ -41,7 +42,7 @@ public class TemplateProposal implements IJavaCompletionProposal {
 
        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.
@@ -78,7 +79,7 @@ public class TemplateProposal implements IJavaCompletionProposal {
                        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];
@@ -89,15 +90,15 @@ public class TemplateProposal implements IJavaCompletionProposal {
                                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);        
@@ -124,8 +125,8 @@ public class TemplateProposal implements IJavaCompletionProposal {
         * @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;
   }
 
        /*
index ac605f0..88f3dd7 100644 (file)
@@ -59,5 +59,7 @@ public interface IPreferenceConstants {
   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
index 7c46c83..dc81130 100644 (file)
@@ -270,8 +270,9 @@ public class PHPeclipsePlugin extends AbstractUIPlugin implements IPreferenceCon
     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);
 
   }
   
index 48d7527..607d90d 100644 (file)
@@ -30,6 +30,8 @@ public class PHPColorProvider {
        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);