1 /*******************************************************************************
 
   2  * Copyright (c) 2000, 2003 IBM Corporation and others.
 
   3  * All rights reserved. This program and the accompanying materials 
 
   4  * are made available under the terms of the Common Public License v1.0
 
   5  * which accompanies this distribution, and is available at
 
   6  * http://www.eclipse.org/legal/cpl-v10.html
 
   9  *     IBM Corporation - initial API and implementation
 
  10  *******************************************************************************/
 
  11 package net.sourceforge.phpdt.internal.ui.text.link;
 
  13 import java.lang.reflect.InvocationTargetException;
 
  15 import net.sourceforge.phpdt.internal.ui.util.ExceptionHandler;
 
  16 import net.sourceforge.phpdt.ui.PreferenceConstants;
 
  17 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
 
  19 import org.eclipse.core.runtime.CoreException;
 
  20 import org.eclipse.jface.dialogs.MessageDialog;
 
  21 import org.eclipse.jface.preference.IPreferenceStore;
 
  22 import org.eclipse.jface.preference.PreferenceConverter;
 
  23 import org.eclipse.jface.text.Assert;
 
  24 import org.eclipse.jface.text.BadLocationException;
 
  25 import org.eclipse.jface.text.BadPositionCategoryException;
 
  26 import org.eclipse.jface.text.DefaultPositionUpdater;
 
  27 import org.eclipse.jface.text.IDocument;
 
  28 import org.eclipse.jface.text.IPositionUpdater;
 
  29 import org.eclipse.jface.text.IRegion;
 
  30 import org.eclipse.jface.text.IRewriteTarget;
 
  31 import org.eclipse.jface.text.ITextInputListener;
 
  32 import org.eclipse.jface.text.ITextListener;
 
  33 import org.eclipse.jface.text.ITextViewer;
 
  34 import org.eclipse.jface.text.ITextViewerExtension;
 
  35 import org.eclipse.jface.text.ITextViewerExtension2;
 
  36 import org.eclipse.jface.text.ITextViewerExtension3;
 
  37 import org.eclipse.jface.text.Position;
 
  38 import org.eclipse.jface.text.Region;
 
  39 import org.eclipse.jface.text.TextEvent;
 
  40 import org.eclipse.jface.text.TextUtilities;
 
  41 import org.eclipse.jface.text.contentassist.ICompletionProposal;
 
  42 import org.eclipse.jface.util.IPropertyChangeListener;
 
  43 import org.eclipse.jface.util.PropertyChangeEvent;
 
  44 import org.eclipse.swt.SWT;
 
  45 import org.eclipse.swt.custom.StyledText;
 
  46 import org.eclipse.swt.custom.VerifyKeyListener;
 
  47 import org.eclipse.swt.events.ModifyEvent;
 
  48 import org.eclipse.swt.events.ModifyListener;
 
  49 import org.eclipse.swt.events.PaintEvent;
 
  50 import org.eclipse.swt.events.PaintListener;
 
  51 import org.eclipse.swt.events.ShellEvent;
 
  52 import org.eclipse.swt.events.ShellListener;
 
  53 import org.eclipse.swt.events.VerifyEvent;
 
  54 import org.eclipse.swt.events.VerifyListener;
 
  55 import org.eclipse.swt.graphics.Color;
 
  56 import org.eclipse.swt.graphics.GC;
 
  57 import org.eclipse.swt.graphics.Point;
 
  58 import org.eclipse.swt.graphics.RGB;
 
  59 import org.eclipse.swt.widgets.Display;
 
  60 import org.eclipse.swt.widgets.Shell;
 
  65  * A user interface for <code>LinkedPositionManager</code>, using <code>ITextViewer</code>.
 
  67 public class LinkedPositionUI implements ILinkedPositionListener,
 
  68         ITextInputListener, ITextListener, ModifyListener, VerifyListener, VerifyKeyListener, PaintListener, IPropertyChangeListener, ShellListener {
 
  71          * A listener for notification when the user cancelled the edit operation.
 
  73         public interface ExitListener {
 
  74                 void exit(boolean accept);
 
  77         public static class ExitFlags {
 
  80                 public ExitFlags(int flags, boolean doit) {
 
  86         public interface ExitPolicy {
 
  87                 ExitFlags doExit(LinkedPositionManager manager, VerifyEvent event, int offset, int length);
 
  91         private static final int UNINSTALL= 1;                  // uninstall linked position manager
 
  92         public static final int COMMIT= 2;                              // commit changes
 
  93         private static final int DOCUMENT_CHANGED= 4;   // document has changed
 
  94         public static final int UPDATE_CARET= 8;                // update caret
 
  96         private static final IPreferenceStore fgStore= PHPeclipsePlugin.getDefault().getPreferenceStore();
 
  97         private static final String CARET_POSITION_PREFIX= "LinkedPositionUI.caret.position"; //$NON-NLS-1$
 
  98         private static int fgCounter= 0;
 
 101         private final ITextViewer fViewer;
 
 102         private final LinkedPositionManager fManager;
 
 103         private final IPositionUpdater fUpdater;
 
 104         private final String fPositionCategoryName;
 
 105         private Color fFrameColor;
 
 107         private int fFinalCaretOffset= -1; // no final caret offset
 
 108         private Position fFinalCaretPosition;
 
 110         private Position fFramePosition;
 
 111         private int fInitialOffset= -1;
 
 112         private int fCaretOffset;
 
 114         private ExitPolicy fExitPolicy;
 
 115         private ExitListener fExitListener;
 
 117         private boolean fNeedRedraw;
 
 119         private String fContentType;
 
 120         private Position fPreviousPosition;
 
 121 //      private ContentAssistant2 fAssistant;
 
 124          * Flag that records the state of this ui object. As there are many different entities that may
 
 125          * call leave or exit, these cannot always be sure whether the linked position infrastructure is
 
 126          * still active. This is especially true for multithreaded situations. 
 
 128         private boolean fIsActive= false;
 
 131          * Creates a user interface for <code>LinkedPositionManager</code>.
 
 133          * @param viewer  the text viewer.
 
 134          * @param manager the <code>LinkedPositionManager</code> managing a <code>IDocument</code> of the <code>ITextViewer</code>.
 
 136         public LinkedPositionUI(ITextViewer viewer, LinkedPositionManager manager) {
 
 137                 Assert.isNotNull(viewer);
 
 138                 Assert.isNotNull(manager);
 
 143                 fPositionCategoryName= CARET_POSITION_PREFIX + (fgCounter++);
 
 144                 fUpdater= new DefaultPositionUpdater(fPositionCategoryName);
 
 146                 fManager.setLinkedPositionListener(this);
 
 148                 initializeHighlightColor(viewer);
 
 152          * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent)
 
 154         public void propertyChange(PropertyChangeEvent event) {
 
 155                 if (event.getProperty().equals(PreferenceConstants.EDITOR_LINKED_POSITION_COLOR)) {
 
 156                         initializeHighlightColor(fViewer);
 
 161         private void initializeHighlightColor(ITextViewer viewer) {
 
 163                 if (fFrameColor != null)
 
 164                         fFrameColor.dispose();
 
 166                 StyledText text= viewer.getTextWidget();
 
 168                         Display display= text.getDisplay();
 
 169                         fFrameColor= createColor(fgStore, PreferenceConstants.EDITOR_LINKED_POSITION_COLOR, display);
 
 174          * Creates a color from the information stored in the given preference store.
 
 175          * Returns <code>null</code> if there is no such information available.
 
 177         private Color createColor(IPreferenceStore store, String key, Display display) {
 
 181                 if (store.contains(key)) {
 
 183                         if (store.isDefault(key))
 
 184                                 rgb= PreferenceConverter.getDefaultColor(store, key);
 
 186                                 rgb= PreferenceConverter.getColor(store, key);
 
 189                                 return new Color(display, rgb);
 
 196          * Sets the initial offset.
 
 199         public void setInitialOffset(int offset) {
 
 200                 fInitialOffset= offset; 
 
 204          * Sets the final position of the caret when the linked mode is exited
 
 205          * successfully by leaving the last linked position using TAB.
 
 206          * The set position will be a TAB stop as well as the positions configured in the
 
 207          * <code>LinkedPositionManager</code>.
 
 209         public void setFinalCaretOffset(int offset) {
 
 210                 fFinalCaretOffset= offset;      
 
 214          * Sets a <code>CancelListener</code> which is notified if the linked mode
 
 215          * is exited unsuccessfully by hitting ESC.
 
 217         public void setCancelListener(ExitListener listener) {
 
 218                 fExitListener= listener;
 
 222          * Sets an <code>ExitPolicy</code> which decides when and how
 
 223          * the linked mode is exited.
 
 225         public void setExitPolicy(ExitPolicy policy) {
 
 230          * @see LinkedPositionManager.LinkedPositionListener#setCurrentPositions(Position, int)
 
 232         public void setCurrentPosition(Position position, int caretOffset) {
 
 234                         ;//JavaPlugin.log(new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, "LinkedPositionUI is not active: "+fPositionCategoryName, new IllegalStateException())); //$NON-NLS-1$
 
 236                 if (!fFramePosition.equals(position)) {
 
 238                         fFramePosition= position;
 
 241                 fCaretOffset= caretOffset;
 
 245          * Enters the linked mode. The linked mode can be left by calling
 
 248          * @see #exit(boolean)
 
 250         public void enter() {
 
 252                         ;//JavaPlugin.log(new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, "LinkedPositionUI is already active: "+fPositionCategoryName, new IllegalStateException())); //$NON-NLS-1$
 
 255                         // JavaPlugin.log(new Status(IStatus.INFO, JavaPlugin.getPluginId(), IStatus.OK, "LinkedPositionUI activated: "+fPositionCategoryName, new Exception())); //$NON-NLS-1$
 
 260                 IDocument document= fViewer.getDocument();
 
 261                 document.addPositionCategory(fPositionCategoryName);
 
 262                 document.addPositionUpdater(fUpdater);
 
 265                         if (fFinalCaretOffset != -1) {
 
 266                                 fFinalCaretPosition= new Position(fFinalCaretOffset);
 
 267                                 document.addPosition(fPositionCategoryName, fFinalCaretPosition);
 
 269                 } catch (BadLocationException e) {
 
 270                         handleException(fViewer.getTextWidget().getShell(), e);
 
 272                 } catch (BadPositionCategoryException e) {
 
 273                   PHPeclipsePlugin.log(e);
 
 274                         Assert.isTrue(false);
 
 277                 fViewer.addTextInputListener(this);
 
 278                 fViewer.addTextListener(this);
 
 280                 ITextViewerExtension extension= (ITextViewerExtension) fViewer;
 
 281                 extension.prependVerifyKeyListener(this);
 
 283                 StyledText text= fViewer.getTextWidget();                       
 
 284                 text.addVerifyListener(this);
 
 285                 text.addModifyListener(this);
 
 286                 text.addPaintListener(this);
 
 287                 text.showSelection();
 
 289                 Shell shell= text.getShell();
 
 290                 shell.addShellListener(this);
 
 292                 fFramePosition= (fInitialOffset == -1) ? fManager.getFirstPosition() : fManager.getPosition(fInitialOffset);
 
 293                 if (fFramePosition == null) {
 
 294                         leave(UNINSTALL | COMMIT | UPDATE_CARET);
 
 298                 fgStore.addPropertyChangeListener(this);
 
 301 //                      fContentType= TextUtilities.getContentType(document, IJavaPartitions.JAVA_PARTITIONING, fFramePosition.offset);
 
 302 //                      if (fViewer instanceof ITextViewerExtension2) {
 
 303 //                              ((ITextViewerExtension2) fViewer).prependAutoEditStrategy(fManager, fContentType);
 
 305 //                              Assert.isTrue(false);
 
 308 //              } catch (BadLocationException e) {
 
 309 //                      handleException(fViewer.getTextWidget().getShell(), e);
 
 312                   fContentType= document.getContentType(fFramePosition.offset);
 
 313                   if (fViewer instanceof ITextViewerExtension2) {
 
 314                     ((ITextViewerExtension2) fViewer).prependAutoEditStrategy(fManager, fContentType);
 
 316                     Assert.isTrue(false);
 
 319                 } catch (BadLocationException e) {
 
 320                   handleException(fViewer.getTextWidget().getShell(), e);
 
 323 //              triggerContentAssist();
 
 327          * @see org.eclipse.jdt.internal.ui.text.link.ILinkedPositionListener#exit(boolean)
 
 329         public void exit(int flags) {
 
 334          * Returns the cursor selection, after having entered the linked mode.
 
 335          * <code>enter()</code> must be called prior to a call to this method.
 
 337         public IRegion getSelectedRegion() {
 
 339                         ;//JavaPlugin.log(new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, "LinkedPositionUI is not active: "+fPositionCategoryName, new IllegalStateException())); //$NON-NLS-1$
 
 341                 if (fFramePosition == null)
 
 342                         return new Region(fFinalCaretOffset, 0);
 
 344                         return new Region(fFramePosition.getOffset(), fFramePosition.getLength());
 
 347         private void leave(int flags) {
 
 349                         ;//JavaPlugin.log(new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, "LinkedPositionUI is not active: "+fPositionCategoryName, new IllegalStateException())); //$NON-NLS-1$
 
 352                         //JavaPlugin.log(new Status(IStatus.INFO, JavaPlugin.getPluginId(), IStatus.OK, "LinkedPositionUI deactivated: "+fPositionCategoryName, new Exception())); //$NON-NLS-1$
 
 358                 if ((flags & UNINSTALL) != 0)
 
 359                         fManager.uninstall((flags & COMMIT) != 0);
 
 361                 fgStore.removePropertyChangeListener(this);
 
 363                 if (fFrameColor != null) {
 
 364                         fFrameColor.dispose();
 
 368                 StyledText text= fViewer.getTextWidget();
 
 369                 // bail out if the styled text is null, meaning the viewer has been disposed (-> document is null as well)
 
 370                 // see pr https://bugs.eclipse.org/bugs/show_bug.cgi?id=46821
 
 374                 text.removePaintListener(this);
 
 375                 text.removeModifyListener(this);
 
 376                 text.removeVerifyListener(this);
 
 378                 Shell shell= text.getShell();
 
 379                 shell.removeShellListener(this);
 
 381 //              if (fAssistant != null) {
 
 382 //                      Display display= text.getDisplay();
 
 383 //                      if (display != null && !display.isDisposed()) {
 
 384 //                              display.asyncExec(new Runnable() {
 
 385 //                                      public void run() {
 
 386 //                                              if (fAssistant != null)  {
 
 387 //                                                      fAssistant.uninstall();
 
 395                 ITextViewerExtension extension= (ITextViewerExtension) fViewer;
 
 396                 extension.removeVerifyKeyListener(this);
 
 398                 IRewriteTarget target= extension.getRewriteTarget();
 
 399                 target.endCompoundChange();
 
 401                 if (fViewer instanceof ITextViewerExtension2 && fContentType != null)
 
 402                         ((ITextViewerExtension2) fViewer).removeAutoEditStrategy(fManager, fContentType);
 
 405                 fViewer.removeTextListener(this);
 
 406                 fViewer.removeTextInputListener(this);
 
 409                         IDocument document= fViewer.getDocument();
 
 411                         if (((flags & COMMIT) != 0) &&
 
 412                                 ((flags & DOCUMENT_CHANGED) == 0) &&
 
 413                                 ((flags & UPDATE_CARET) != 0))
 
 415                                 Position[] positions= document.getPositions(fPositionCategoryName);
 
 416                                 if ((positions != null) && (positions.length != 0)) {
 
 418                                         if (fViewer instanceof ITextViewerExtension3) {
 
 419                                                 ITextViewerExtension3 extension3= (ITextViewerExtension3) fViewer;
 
 420                                                 int widgetOffset= extension3.modelOffset2WidgetOffset(positions[0].getOffset());
 
 421                                                 if (widgetOffset >= 0)
 
 422                                                         text.setSelection(widgetOffset, widgetOffset);
 
 425                                                 IRegion region= fViewer.getVisibleRegion();
 
 426                                                 int offset= positions[0].getOffset() - region.getOffset();
 
 427                                                 if ((offset >= 0) && (offset <= region.getLength()))
 
 428                                                         text.setSelection(offset, offset);
 
 433                         document.removePositionUpdater(fUpdater);
 
 434                         document.removePositionCategory(fPositionCategoryName);
 
 436                         if (fExitListener != null)
 
 438                                         ((flags & COMMIT) != 0) ||
 
 439                                         ((flags & DOCUMENT_CHANGED) != 0));
 
 441                 } catch (BadPositionCategoryException e) {
 
 442                         PHPeclipsePlugin.log(e);
 
 443                         Assert.isTrue(false);
 
 446                 if ((flags & DOCUMENT_CHANGED) == 0)
 
 450         private void next() {
 
 452                         ;//JavaPlugin.log(new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, "LinkedPositionUI is not active: "+fPositionCategoryName, new IllegalStateException())); //$NON-NLS-1$
 
 456                 if (fFramePosition == fFinalCaretPosition)
 
 457                         fFramePosition= fManager.getFirstPosition();
 
 459                         fFramePosition= fManager.getNextPosition(fFramePosition.getOffset());
 
 460                 if (fFramePosition == null) {
 
 461                         if (fFinalCaretPosition != null)
 
 462                                 fFramePosition= fFinalCaretPosition;
 
 464                                 fFramePosition= fManager.getFirstPosition();
 
 466                 if (fFramePosition == null) {
 
 467                         leave(UNINSTALL | COMMIT | UPDATE_CARET);
 
 470 //                      triggerContentAssist();
 
 475         private void previous() {
 
 477                         ;//JavaPlugin.log(new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, "LinkedPositionUI is not active: "+fPositionCategoryName, new IllegalStateException())); //$NON-NLS-1$
 
 481                 fFramePosition= fManager.getPreviousPosition(fFramePosition.getOffset());
 
 482                 if (fFramePosition == null) {
 
 483                         if (fFinalCaretPosition != null)
 
 484                                 fFramePosition= fFinalCaretPosition;
 
 486                                 fFramePosition= fManager.getLastPosition();
 
 488                 if (fFramePosition == null) {
 
 489                         leave(UNINSTALL | COMMIT | UPDATE_CARET);
 
 492 //                      triggerContentAssist();
 
 497         /** Trigger content assist on choice positions */
 
 498 //      private void triggerContentAssist() {
 
 499 //              if (fFramePosition instanceof ProposalPosition) {
 
 501 //                      ProposalPosition pp= (ProposalPosition) fFramePosition;
 
 502 //                      initializeContentAssistant();
 
 503 //                      if (fAssistant == null)
 
 505 //                      fAssistant.setCompletions(pp.getChoices());
 
 506 //                      fAssistant.showPossibleCompletions();
 
 508 //                      if (fAssistant != null)
 
 509 //                              fAssistant.setCompletions(new ICompletionProposal[0]);
 
 513         /** Lazy initialize content assistant for this linked ui */
 
 514 //      private void initializeContentAssistant() {
 
 515 //              if (fAssistant != null)
 
 517 //              fAssistant= new ContentAssistant2();
 
 518 //              fAssistant.setDocumentPartitioning(IJavaPartitions.JAVA_PARTITIONING);
 
 519 //              fAssistant.install(fViewer);
 
 523          * @see VerifyKeyListener#verifyKey(VerifyEvent)
 
 525         public void verifyKey(VerifyEvent event) {
 
 527                 if (!event.doit || !fIsActive)
 
 530                 Point selection= fViewer.getSelectedRange();
 
 531                 int offset= selection.x;
 
 532                 int length= selection.y;
 
 534                 ExitFlags exitFlags= fExitPolicy == null ? null : fExitPolicy.doExit(fManager, event, offset, length);
 
 535                 if (exitFlags != null) {
 
 536                         leave(UNINSTALL | exitFlags.flags);
 
 537                         event.doit= exitFlags.doit;
 
 541                 switch (event.character) {
 
 542                 // [SHIFT-]TAB = hop between edit boxes
 
 545                                 // if tab was treated as a document change, would it exceed variable range?
 
 546                                 if (!LinkedPositionManager.includes(fFramePosition, offset, length)) {
 
 547                                         leave(UNINSTALL | COMMIT);
 
 552                         if (event.stateMask == SWT.SHIFT)
 
 561                 case 0x0A: // Ctrl+Enter
 
 564 //                      if (fAssistant != null && fAssistant.wasProposalChosen()) {
 
 566 //                              event.doit= false;
 
 570                                 // if enter was treated as a document change, would it exceed variable range?
 
 571                                 if (!LinkedPositionManager.includes(fFramePosition, offset, length)
 
 572                                                 || (fFramePosition == fFinalCaretPosition)) {
 
 573                                         leave(UNINSTALL | COMMIT);
 
 578                         leave(UNINSTALL | COMMIT | UPDATE_CARET);
 
 584                         leave(UNINSTALL | COMMIT);
 
 589                         leave(UNINSTALL | COMMIT);
 
 594                         if (event.character != 0) {
 
 595                                 if (!controlUndoBehavior(offset, length) || fFramePosition == fFinalCaretPosition) {
 
 596                                         leave(UNINSTALL | COMMIT);
 
 603         private boolean controlUndoBehavior(int offset, int length) {
 
 605                 Position position= fManager.getEmbracingPosition(offset, length);
 
 606                 if (position != null) {
 
 608                         ITextViewerExtension extension= (ITextViewerExtension) fViewer;
 
 609                         IRewriteTarget target= extension.getRewriteTarget();
 
 611                         if (fPreviousPosition != null && !fPreviousPosition.equals(position))
 
 612                                 target.endCompoundChange();
 
 613                         target.beginCompoundChange();
 
 616                 fPreviousPosition= position;
 
 617                 return fPreviousPosition != null;
 
 621          * @see VerifyListener#verifyText(VerifyEvent)
 
 623         public void verifyText(VerifyEvent event) {
 
 631                 if (fViewer instanceof ITextViewerExtension3) {
 
 632                         ITextViewerExtension3 extension= (ITextViewerExtension3) fViewer;
 
 633                         IRegion modelRange= extension.widgetRange2ModelRange(new Region(event.start, event.end - event.start));
 
 634                         if (modelRange == null)
 
 637                         offset= modelRange.getOffset();
 
 638                         length= modelRange.getLength();
 
 641                         IRegion visibleRegion= fViewer.getVisibleRegion();
 
 642                         offset= event.start + visibleRegion.getOffset();
 
 643                         length= event.end - event.start;
 
 646                 // allow changes only within linked positions when coming through UI
 
 647                 if (!fManager.anyPositionIncludes(offset, length))
 
 648                         leave(UNINSTALL | COMMIT);
 
 652          * @see PaintListener#paintControl(PaintEvent)
 
 654         public void paintControl(PaintEvent event) {    
 
 655                 if (fFramePosition == null)
 
 658                 IRegion widgetRange= asWidgetRange(fFramePosition);
 
 659                 if (widgetRange == null) {
 
 660                         leave(UNINSTALL | COMMIT | DOCUMENT_CHANGED);
 
 664                 int offset= widgetRange.getOffset();
 
 665                 int length= widgetRange.getLength();
 
 667                 StyledText text= fViewer.getTextWidget();
 
 670                 Point minLocation= getMinimumLocation(text, offset, length);
 
 671                 Point maxLocation= getMaximumLocation(text, offset, length);
 
 673                 int x1= minLocation.x;
 
 674                 int x2= minLocation.x + maxLocation.x - minLocation.x - 1;
 
 675                 int y= minLocation.y + text.getLineHeight() - 1;
 
 678                 gc.setForeground(fFrameColor);
 
 679                 gc.drawLine(x1, y, x2, y);
 
 682         protected IRegion asWidgetRange(Position position) {
 
 683                 if (fViewer instanceof ITextViewerExtension3) {
 
 685                         ITextViewerExtension3 extension= (ITextViewerExtension3) fViewer;
 
 686                         return extension.modelRange2WidgetRange(new Region(position.getOffset(), position.getLength()));
 
 690                         IRegion region= fViewer.getVisibleRegion();
 
 691                         if (includes(region, position))
 
 692                                 return new Region(position.getOffset() -  region.getOffset(), position.getLength());
 
 698         private static Point getMinimumLocation(StyledText text, int offset, int length) {
 
 699                 Point minLocation= new Point(Integer.MAX_VALUE, Integer.MAX_VALUE);
 
 701                 for (int i= 0; i <= length; i++) {
 
 702                         Point location= text.getLocationAtOffset(offset + i);
 
 704                         if (location.x < minLocation.x)
 
 705                                 minLocation.x= location.x;                      
 
 706                         if (location.y < minLocation.y)
 
 707                                 minLocation.y= location.y;                      
 
 713         private static Point getMaximumLocation(StyledText text, int offset, int length) {
 
 714                 Point maxLocation= new Point(Integer.MIN_VALUE, Integer.MIN_VALUE);
 
 716                 for (int i= 0; i <= length; i++) {
 
 717                         Point location= text.getLocationAtOffset(offset + i);
 
 719                         if (location.x > maxLocation.x)
 
 720                                 maxLocation.x= location.x;                      
 
 721                         if (location.y > maxLocation.y)
 
 722                                 maxLocation.y= location.y;                      
 
 728         private void redrawRegion() {
 
 729                 IRegion widgetRange= asWidgetRange(fFramePosition);
 
 730                 if (widgetRange == null) {
 
 731                         leave(UNINSTALL | COMMIT | DOCUMENT_CHANGED);
 
 735                 StyledText text= fViewer.getTextWidget();
 
 736                 if (text != null && !text.isDisposed()) 
 
 737                         text.redrawRange(widgetRange.getOffset(), widgetRange.getLength(), true);
 
 740         private void selectRegion() {
 
 742                 IRegion widgetRange= asWidgetRange(fFramePosition);
 
 743                 if (widgetRange == null) {
 
 744                         leave(UNINSTALL | COMMIT | DOCUMENT_CHANGED);
 
 748                 StyledText text= fViewer.getTextWidget();
 
 749                 if (text != null && !text.isDisposed()) {
 
 750                         int start= widgetRange.getOffset();
 
 751                         int end= widgetRange.getLength() + start;
 
 752                         text.setSelection(start, end);
 
 756         private void updateCaret() {
 
 758                 IRegion widgetRange= asWidgetRange(fFramePosition);
 
 759                 if (widgetRange == null) {
 
 760                         leave(UNINSTALL | COMMIT | DOCUMENT_CHANGED);
 
 764                 int offset= widgetRange.getOffset() + fCaretOffset;
 
 765                 StyledText text= fViewer.getTextWidget();
 
 766                 if (text != null && !text.isDisposed())
 
 767                         text.setCaretOffset(offset);
 
 771          * @see ModifyListener#modifyText(ModifyEvent)
 
 773         public void modifyText(ModifyEvent e) {
 
 774                 // reposition caret after StyledText
 
 779         private static void handleException(Shell shell, Exception e) {
 
 780                 String title= LinkedPositionMessages.getString("LinkedPositionUI.error.title"); //$NON-NLS-1$
 
 781                 if (e instanceof CoreException)
 
 782                         ExceptionHandler.handle((CoreException)e, shell, title, null);
 
 783                 else if (e instanceof InvocationTargetException)
 
 784                         ExceptionHandler.handle((InvocationTargetException)e, shell, title, null);
 
 786                         MessageDialog.openError(shell, title, e.getMessage());
 
 787                         PHPeclipsePlugin.log(e);
 
 792          * @see ITextInputListener#inputDocumentAboutToBeChanged(IDocument, IDocument)
 
 794         public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
 
 795                 // 5326: leave linked mode on document change
 
 796                 int flags= UNINSTALL | COMMIT | (oldInput.equals(newInput) ? 0 : DOCUMENT_CHANGED);
 
 801          * @see ITextInputListener#inputDocumentChanged(IDocument, IDocument)
 
 803         public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
 
 806         private static boolean includes(IRegion region, Position position) {
 
 808                         position.getOffset() >= region.getOffset() &&
 
 809                         position.getOffset() + position.getLength() <= region.getOffset() + region.getLength();
 
 813          * @see org.eclipse.jface.text.ITextListener#textChanged(TextEvent)
 
 815         public void textChanged(TextEvent event) {
 
 824          * @see org.eclipse.swt.events.ShellListener#shellActivated(org.eclipse.swt.events.ShellEvent)
 
 826         public void shellActivated(ShellEvent event) {
 
 830          * @see org.eclipse.swt.events.ShellListener#shellClosed(org.eclipse.swt.events.ShellEvent)
 
 832         public void shellClosed(ShellEvent event) {
 
 833                 leave(UNINSTALL | COMMIT | DOCUMENT_CHANGED);
 
 837          * @see org.eclipse.swt.events.ShellListener#shellDeactivated(org.eclipse.swt.events.ShellEvent)
 
 839         public void shellDeactivated(ShellEvent event) {
 
 840                 // don't deactivate on focus lost, since the proposal popups may take focus
 
 841                 // plus: it doesn't hurt if you can check with another window without losing linked mode
 
 842                 // since there is no intrusive popup sticking out.
 
 844                 // need to check first what happens on reentering based on an open action
 
 845                 // Seems to be no problem
 
 847                 // TODO check whether we can leave it or uncomment it after debugging
 
 848                 // PS: why DOCUMENT_CHANGED? We want to trigger a redraw! (Shell deactivated does not mean
 
 849                 // it is not visible any longer.
 
 850 //              leave(UNINSTALL | COMMIT | DOCUMENT_CHANGED);
 
 853                 // Check with content assistant and only leave if its not the proposal shell that took the 
 
 859 //              if (fAssistant == null || fViewer == null || (text= fViewer.getTextWidget()) == null 
 
 860 //                              || (display= text.getDisplay()) == null || display.isDisposed()) {
 
 861                 if ( fViewer == null || (text= fViewer.getTextWidget()) == null 
 
 862                       || (display= text.getDisplay()) == null || display.isDisposed()) {
 
 863                         leave(UNINSTALL | COMMIT);
 
 865                         // Post in UI thread since the assistant popup will only get the focus after we lose it.
 
 866                         display.asyncExec(new Runnable() {
 
 868                                         // TODO add isDisposed / isUninstalled / hasLeft check? for now: check for content type,
 
 869                                         // since it gets nullified in leave()
 
 870                                         if (fIsActive) {// && (fAssistant == null || !fAssistant.hasFocus()))  {
 
 871                                                 leave(UNINSTALL | COMMIT);
 
 879          * @see org.eclipse.swt.events.ShellListener#shellDeiconified(org.eclipse.swt.events.ShellEvent)
 
 881         public void shellDeiconified(ShellEvent event) {
 
 885          * @see org.eclipse.swt.events.ShellListener#shellIconified(org.eclipse.swt.events.ShellEvent)
 
 887         public void shellIconified(ShellEvent event) {
 
 888                 leave(UNINSTALL | COMMIT | DOCUMENT_CHANGED);