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.util.IPropertyChangeListener;
 
  41 import org.eclipse.jface.util.PropertyChangeEvent;
 
  42 import org.eclipse.swt.SWT;
 
  43 import org.eclipse.swt.custom.StyledText;
 
  44 import org.eclipse.swt.custom.VerifyKeyListener;
 
  45 import org.eclipse.swt.events.ModifyEvent;
 
  46 import org.eclipse.swt.events.ModifyListener;
 
  47 import org.eclipse.swt.events.PaintEvent;
 
  48 import org.eclipse.swt.events.PaintListener;
 
  49 import org.eclipse.swt.events.ShellEvent;
 
  50 import org.eclipse.swt.events.ShellListener;
 
  51 import org.eclipse.swt.events.VerifyEvent;
 
  52 import org.eclipse.swt.events.VerifyListener;
 
  53 import org.eclipse.swt.graphics.Color;
 
  54 import org.eclipse.swt.graphics.GC;
 
  55 import org.eclipse.swt.graphics.Point;
 
  56 import org.eclipse.swt.graphics.RGB;
 
  57 import org.eclipse.swt.widgets.Display;
 
  58 import org.eclipse.swt.widgets.Shell;
 
  63  * A user interface for <code>LinkedPositionManager</code>, using <code>ITextViewer</code>.
 
  65 public class LinkedPositionUI implements ILinkedPositionListener,
 
  66         ITextInputListener, ITextListener, ModifyListener, VerifyListener, VerifyKeyListener, PaintListener, IPropertyChangeListener, ShellListener {
 
  69          * A listener for notification when the user cancelled the edit operation.
 
  71         public interface ExitListener {
 
  72                 void exit(boolean accept);
 
  75         public static class ExitFlags {
 
  78                 public ExitFlags(int flags, boolean doit) {
 
  84         public interface ExitPolicy {
 
  85                 ExitFlags doExit(LinkedPositionManager manager, VerifyEvent event, int offset, int length);
 
  89         private static final int UNINSTALL= 1;                  // uninstall linked position manager
 
  90         public static final int COMMIT= 2;                              // commit changes
 
  91         private static final int DOCUMENT_CHANGED= 4;   // document has changed
 
  92         public static final int UPDATE_CARET= 8;                // update caret
 
  94         private static final IPreferenceStore fgStore= PHPeclipsePlugin.getDefault().getPreferenceStore();
 
  95         private static final String CARET_POSITION_PREFIX= "LinkedPositionUI.caret.position"; //$NON-NLS-1$
 
  96         private static int fgCounter= 0;
 
  99         private final ITextViewer fViewer;
 
 100         private final LinkedPositionManager fManager;
 
 101         private final IPositionUpdater fUpdater;
 
 102         private final String fPositionCategoryName;
 
 103         private Color fFrameColor;
 
 105         private int fFinalCaretOffset= -1; // no final caret offset
 
 106         private Position fFinalCaretPosition;
 
 108         private Position fFramePosition;
 
 109         private int fInitialOffset= -1;
 
 110         private int fCaretOffset;
 
 112         private ExitPolicy fExitPolicy;
 
 113         private ExitListener fExitListener;
 
 115         private boolean fNeedRedraw;
 
 117         private String fContentType;
 
 118         private Position fPreviousPosition;
 
 119 //      private ContentAssistant2 fAssistant;
 
 122          * Flag that records the state of this ui object. As there are many different entities that may
 
 123          * call leave or exit, these cannot always be sure whether the linked position infrastructure is
 
 124          * still active. This is especially true for multithreaded situations. 
 
 126         private boolean fIsActive= false;
 
 129          * Creates a user interface for <code>LinkedPositionManager</code>.
 
 131          * @param viewer  the text viewer.
 
 132          * @param manager the <code>LinkedPositionManager</code> managing a <code>IDocument</code> of the <code>ITextViewer</code>.
 
 134         public LinkedPositionUI(ITextViewer viewer, LinkedPositionManager manager) {
 
 135                 Assert.isNotNull(viewer);
 
 136                 Assert.isNotNull(manager);
 
 141                 fPositionCategoryName= CARET_POSITION_PREFIX + (fgCounter++);
 
 142                 fUpdater= new DefaultPositionUpdater(fPositionCategoryName);
 
 144                 fManager.setLinkedPositionListener(this);
 
 146                 initializeHighlightColor(viewer);
 
 150          * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent)
 
 152         public void propertyChange(PropertyChangeEvent event) {
 
 153                 if (event.getProperty().equals(PreferenceConstants.EDITOR_LINKED_POSITION_COLOR)) {
 
 154                         initializeHighlightColor(fViewer);
 
 159         private void initializeHighlightColor(ITextViewer viewer) {
 
 161                 if (fFrameColor != null)
 
 162                         fFrameColor.dispose();
 
 164                 StyledText text= viewer.getTextWidget();
 
 166                         Display display= text.getDisplay();
 
 167                         fFrameColor= createColor(fgStore, PreferenceConstants.EDITOR_LINKED_POSITION_COLOR, display);
 
 172          * Creates a color from the information stored in the given preference store.
 
 173          * Returns <code>null</code> if there is no such information available.
 
 175         private Color createColor(IPreferenceStore store, String key, Display display) {
 
 179                 if (store.contains(key)) {
 
 181                         if (store.isDefault(key))
 
 182                                 rgb= PreferenceConverter.getDefaultColor(store, key);
 
 184                                 rgb= PreferenceConverter.getColor(store, key);
 
 187                                 return new Color(display, rgb);
 
 194          * Sets the initial offset.
 
 197         public void setInitialOffset(int offset) {
 
 198                 fInitialOffset= offset; 
 
 202          * Sets the final position of the caret when the linked mode is exited
 
 203          * successfully by leaving the last linked position using TAB.
 
 204          * The set position will be a TAB stop as well as the positions configured in the
 
 205          * <code>LinkedPositionManager</code>.
 
 207         public void setFinalCaretOffset(int offset) {
 
 208                 fFinalCaretOffset= offset;      
 
 212          * Sets a <code>CancelListener</code> which is notified if the linked mode
 
 213          * is exited unsuccessfully by hitting ESC.
 
 215         public void setCancelListener(ExitListener listener) {
 
 216                 fExitListener= listener;
 
 220          * Sets an <code>ExitPolicy</code> which decides when and how
 
 221          * the linked mode is exited.
 
 223         public void setExitPolicy(ExitPolicy policy) {
 
 228          * @see LinkedPositionManager.LinkedPositionListener#setCurrentPositions(Position, int)
 
 230         public void setCurrentPosition(Position position, int caretOffset) {
 
 232                         ;//JavaPlugin.log(new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, "LinkedPositionUI is not active: "+fPositionCategoryName, new IllegalStateException())); //$NON-NLS-1$
 
 234                 if (!fFramePosition.equals(position)) {
 
 236                         fFramePosition= position;
 
 239                 fCaretOffset= caretOffset;
 
 243          * Enters the linked mode. The linked mode can be left by calling
 
 246          * @see #exit(boolean)
 
 248         public void enter() {
 
 250                         ;//JavaPlugin.log(new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, "LinkedPositionUI is already active: "+fPositionCategoryName, new IllegalStateException())); //$NON-NLS-1$
 
 253                         // JavaPlugin.log(new Status(IStatus.INFO, JavaPlugin.getPluginId(), IStatus.OK, "LinkedPositionUI activated: "+fPositionCategoryName, new Exception())); //$NON-NLS-1$
 
 258                 IDocument document= fViewer.getDocument();
 
 259                 document.addPositionCategory(fPositionCategoryName);
 
 260                 document.addPositionUpdater(fUpdater);
 
 263                         if (fFinalCaretOffset != -1) {
 
 264                                 fFinalCaretPosition= new Position(fFinalCaretOffset);
 
 265                                 document.addPosition(fPositionCategoryName, fFinalCaretPosition);
 
 267                 } catch (BadLocationException e) {
 
 268                         handleException(fViewer.getTextWidget().getShell(), e);
 
 270                 } catch (BadPositionCategoryException e) {
 
 271                   PHPeclipsePlugin.log(e);
 
 272                         Assert.isTrue(false);
 
 275                 fViewer.addTextInputListener(this);
 
 276                 fViewer.addTextListener(this);
 
 278                 ITextViewerExtension extension= (ITextViewerExtension) fViewer;
 
 279                 extension.prependVerifyKeyListener(this);
 
 281                 StyledText text= fViewer.getTextWidget();                       
 
 282                 text.addVerifyListener(this);
 
 283                 text.addModifyListener(this);
 
 284                 text.addPaintListener(this);
 
 285                 text.showSelection();
 
 287                 Shell shell= text.getShell();
 
 288                 shell.addShellListener(this);
 
 290                 fFramePosition= (fInitialOffset == -1) ? fManager.getFirstPosition() : fManager.getPosition(fInitialOffset);
 
 291                 if (fFramePosition == null) {
 
 292                         leave(UNINSTALL | COMMIT | UPDATE_CARET);
 
 296                 fgStore.addPropertyChangeListener(this);
 
 299 //                      fContentType= TextUtilities.getContentType(document, IJavaPartitions.JAVA_PARTITIONING, fFramePosition.offset);
 
 300 //                      if (fViewer instanceof ITextViewerExtension2) {
 
 301 //                              ((ITextViewerExtension2) fViewer).prependAutoEditStrategy(fManager, fContentType);
 
 303 //                              Assert.isTrue(false);
 
 306 //              } catch (BadLocationException e) {
 
 307 //                      handleException(fViewer.getTextWidget().getShell(), e);
 
 310                   fContentType= document.getContentType(fFramePosition.offset);
 
 311                   if (fViewer instanceof ITextViewerExtension2) {
 
 312                     ((ITextViewerExtension2) fViewer).prependAutoEditStrategy(fManager, fContentType);
 
 314                     Assert.isTrue(false);
 
 317                 } catch (BadLocationException e) {
 
 318                   handleException(fViewer.getTextWidget().getShell(), e);
 
 321 //              triggerContentAssist();
 
 325          * @see net.sourceforge.phpdt.internal.ui.text.link.ILinkedPositionListener#exit(boolean)
 
 327         public void exit(int flags) {
 
 332          * Returns the cursor selection, after having entered the linked mode.
 
 333          * <code>enter()</code> must be called prior to a call to this method.
 
 335         public IRegion getSelectedRegion() {
 
 337                         ;//JavaPlugin.log(new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, "LinkedPositionUI is not active: "+fPositionCategoryName, new IllegalStateException())); //$NON-NLS-1$
 
 339                 if (fFramePosition == null)
 
 340                         return new Region(fFinalCaretOffset, 0);
 
 342                         return new Region(fFramePosition.getOffset(), fFramePosition.getLength());
 
 345         private void leave(int flags) {
 
 347                         ;//JavaPlugin.log(new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, "LinkedPositionUI is not active: "+fPositionCategoryName, new IllegalStateException())); //$NON-NLS-1$
 
 350                         //JavaPlugin.log(new Status(IStatus.INFO, JavaPlugin.getPluginId(), IStatus.OK, "LinkedPositionUI deactivated: "+fPositionCategoryName, new Exception())); //$NON-NLS-1$
 
 356                 if ((flags & UNINSTALL) != 0)
 
 357                         fManager.uninstall((flags & COMMIT) != 0);
 
 359                 fgStore.removePropertyChangeListener(this);
 
 361                 if (fFrameColor != null) {
 
 362                         fFrameColor.dispose();
 
 366                 StyledText text= fViewer.getTextWidget();
 
 367                 // bail out if the styled text is null, meaning the viewer has been disposed (-> document is null as well)
 
 368                 // see pr https://bugs.eclipse.org/bugs/show_bug.cgi?id=46821
 
 372                 text.removePaintListener(this);
 
 373                 text.removeModifyListener(this);
 
 374                 text.removeVerifyListener(this);
 
 376                 Shell shell= text.getShell();
 
 377                 shell.removeShellListener(this);
 
 379 //              if (fAssistant != null) {
 
 380 //                      Display display= text.getDisplay();
 
 381 //                      if (display != null && !display.isDisposed()) {
 
 382 //                              display.asyncExec(new Runnable() {
 
 383 //                                      public void run() {
 
 384 //                                              if (fAssistant != null)  {
 
 385 //                                                      fAssistant.uninstall();
 
 393                 ITextViewerExtension extension= (ITextViewerExtension) fViewer;
 
 394                 extension.removeVerifyKeyListener(this);
 
 396                 IRewriteTarget target= extension.getRewriteTarget();
 
 397                 target.endCompoundChange();
 
 399                 if (fViewer instanceof ITextViewerExtension2 && fContentType != null)
 
 400                         ((ITextViewerExtension2) fViewer).removeAutoEditStrategy(fManager, fContentType);
 
 403                 fViewer.removeTextListener(this);
 
 404                 fViewer.removeTextInputListener(this);
 
 407                         IDocument document= fViewer.getDocument();
 
 409                         if (((flags & COMMIT) != 0) &&
 
 410                                 ((flags & DOCUMENT_CHANGED) == 0) &&
 
 411                                 ((flags & UPDATE_CARET) != 0))
 
 413                                 Position[] positions= document.getPositions(fPositionCategoryName);
 
 414                                 if ((positions != null) && (positions.length != 0)) {
 
 416                                         if (fViewer instanceof ITextViewerExtension3) {
 
 417                                                 ITextViewerExtension3 extension3= (ITextViewerExtension3) fViewer;
 
 418                                                 int widgetOffset= extension3.modelOffset2WidgetOffset(positions[0].getOffset());
 
 419                                                 if (widgetOffset >= 0)
 
 420                                                         text.setSelection(widgetOffset, widgetOffset);
 
 423                                                 IRegion region= fViewer.getVisibleRegion();
 
 424                                                 int offset= positions[0].getOffset() - region.getOffset();
 
 425                                                 if ((offset >= 0) && (offset <= region.getLength()))
 
 426                                                         text.setSelection(offset, offset);
 
 431                         document.removePositionUpdater(fUpdater);
 
 432                         document.removePositionCategory(fPositionCategoryName);
 
 434                         if (fExitListener != null)
 
 436                                         ((flags & COMMIT) != 0) ||
 
 437                                         ((flags & DOCUMENT_CHANGED) != 0));
 
 439                 } catch (BadPositionCategoryException e) {
 
 440                         PHPeclipsePlugin.log(e);
 
 441                         Assert.isTrue(false);
 
 444                 if ((flags & DOCUMENT_CHANGED) == 0)
 
 448         private void next() {
 
 450                         ;//JavaPlugin.log(new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, "LinkedPositionUI is not active: "+fPositionCategoryName, new IllegalStateException())); //$NON-NLS-1$
 
 454                 if (fFramePosition == fFinalCaretPosition)
 
 455                         fFramePosition= fManager.getFirstPosition();
 
 457                         fFramePosition= fManager.getNextPosition(fFramePosition.getOffset());
 
 458                 if (fFramePosition == null) {
 
 459                         if (fFinalCaretPosition != null)
 
 460                                 fFramePosition= fFinalCaretPosition;
 
 462                                 fFramePosition= fManager.getFirstPosition();
 
 464                 if (fFramePosition == null) {
 
 465                         leave(UNINSTALL | COMMIT | UPDATE_CARET);
 
 468 //                      triggerContentAssist();
 
 473         private void previous() {
 
 475                         ;//JavaPlugin.log(new Status(IStatus.WARNING, JavaPlugin.getPluginId(), IStatus.OK, "LinkedPositionUI is not active: "+fPositionCategoryName, new IllegalStateException())); //$NON-NLS-1$
 
 479                 fFramePosition= fManager.getPreviousPosition(fFramePosition.getOffset());
 
 480                 if (fFramePosition == null) {
 
 481                         if (fFinalCaretPosition != null)
 
 482                                 fFramePosition= fFinalCaretPosition;
 
 484                                 fFramePosition= fManager.getLastPosition();
 
 486                 if (fFramePosition == null) {
 
 487                         leave(UNINSTALL | COMMIT | UPDATE_CARET);
 
 490 //                      triggerContentAssist();
 
 495         /** Trigger content assist on choice positions */
 
 496 //      private void triggerContentAssist() {
 
 497 //              if (fFramePosition instanceof ProposalPosition) {
 
 499 //                      ProposalPosition pp= (ProposalPosition) fFramePosition;
 
 500 //                      initializeContentAssistant();
 
 501 //                      if (fAssistant == null)
 
 503 //                      fAssistant.setCompletions(pp.getChoices());
 
 504 //                      fAssistant.showPossibleCompletions();
 
 506 //                      if (fAssistant != null)
 
 507 //                              fAssistant.setCompletions(new ICompletionProposal[0]);
 
 511         /** Lazy initialize content assistant for this linked ui */
 
 512 //      private void initializeContentAssistant() {
 
 513 //              if (fAssistant != null)
 
 515 //              fAssistant= new ContentAssistant2();
 
 516 //              fAssistant.setDocumentPartitioning(IJavaPartitions.JAVA_PARTITIONING);
 
 517 //              fAssistant.install(fViewer);
 
 521          * @see VerifyKeyListener#verifyKey(VerifyEvent)
 
 523         public void verifyKey(VerifyEvent event) {
 
 525                 if (!event.doit || !fIsActive)
 
 528                 Point selection= fViewer.getSelectedRange();
 
 529                 int offset= selection.x;
 
 530                 int length= selection.y;
 
 532                 ExitFlags exitFlags= fExitPolicy == null ? null : fExitPolicy.doExit(fManager, event, offset, length);
 
 533                 if (exitFlags != null) {
 
 534                         leave(UNINSTALL | exitFlags.flags);
 
 535                         event.doit= exitFlags.doit;
 
 539                 switch (event.character) {
 
 540                 // [SHIFT-]TAB = hop between edit boxes
 
 543                                 // if tab was treated as a document change, would it exceed variable range?
 
 544                                 if (!LinkedPositionManager.includes(fFramePosition, offset, length)) {
 
 545                                         leave(UNINSTALL | COMMIT);
 
 550                         if (event.stateMask == SWT.SHIFT)
 
 559                 case 0x0A: // Ctrl+Enter
 
 562 //                      if (fAssistant != null && fAssistant.wasProposalChosen()) {
 
 564 //                              event.doit= false;
 
 568                                 // if enter was treated as a document change, would it exceed variable range?
 
 569                                 if (!LinkedPositionManager.includes(fFramePosition, offset, length)
 
 570                                                 || (fFramePosition == fFinalCaretPosition)) {
 
 571                                         leave(UNINSTALL | COMMIT);
 
 576                         leave(UNINSTALL | COMMIT | UPDATE_CARET);
 
 582                         leave(UNINSTALL | COMMIT);
 
 587                         leave(UNINSTALL | COMMIT);
 
 592                         if (event.character != 0) {
 
 593                                 if (!controlUndoBehavior(offset, length) || fFramePosition == fFinalCaretPosition) {
 
 594                                         leave(UNINSTALL | COMMIT);
 
 601         private boolean controlUndoBehavior(int offset, int length) {
 
 603                 Position position= fManager.getEmbracingPosition(offset, length);
 
 604                 if (position != null) {
 
 606                         ITextViewerExtension extension= (ITextViewerExtension) fViewer;
 
 607                         IRewriteTarget target= extension.getRewriteTarget();
 
 609                         if (fPreviousPosition != null && !fPreviousPosition.equals(position))
 
 610                                 target.endCompoundChange();
 
 611                         target.beginCompoundChange();
 
 614                 fPreviousPosition= position;
 
 615                 return fPreviousPosition != null;
 
 619          * @see VerifyListener#verifyText(VerifyEvent)
 
 621         public void verifyText(VerifyEvent event) {
 
 629                 if (fViewer instanceof ITextViewerExtension3) {
 
 630                         ITextViewerExtension3 extension= (ITextViewerExtension3) fViewer;
 
 631                         IRegion modelRange= extension.widgetRange2ModelRange(new Region(event.start, event.end - event.start));
 
 632                         if (modelRange == null)
 
 635                         offset= modelRange.getOffset();
 
 636                         length= modelRange.getLength();
 
 639                         IRegion visibleRegion= fViewer.getVisibleRegion();
 
 640                         offset= event.start + visibleRegion.getOffset();
 
 641                         length= event.end - event.start;
 
 644                 // allow changes only within linked positions when coming through UI
 
 645                 if (!fManager.anyPositionIncludes(offset, length))
 
 646                         leave(UNINSTALL | COMMIT);
 
 650          * @see PaintListener#paintControl(PaintEvent)
 
 652         public void paintControl(PaintEvent event) {    
 
 653                 if (fFramePosition == null)
 
 656                 IRegion widgetRange= asWidgetRange(fFramePosition);
 
 657                 if (widgetRange == null) {
 
 658                         leave(UNINSTALL | COMMIT | DOCUMENT_CHANGED);
 
 662                 int offset= widgetRange.getOffset();
 
 663                 int length= widgetRange.getLength();
 
 665                 StyledText text= fViewer.getTextWidget();
 
 668                 Point minLocation= getMinimumLocation(text, offset, length);
 
 669                 Point maxLocation= getMaximumLocation(text, offset, length);
 
 671                 int x1= minLocation.x;
 
 672                 int x2= minLocation.x + maxLocation.x - minLocation.x - 1;
 
 673                 int y= minLocation.y + text.getLineHeight() - 1;
 
 676                 gc.setForeground(fFrameColor);
 
 677                 gc.drawLine(x1, y, x2, y);
 
 680         protected IRegion asWidgetRange(Position position) {
 
 681                 if (fViewer instanceof ITextViewerExtension3) {
 
 683                         ITextViewerExtension3 extension= (ITextViewerExtension3) fViewer;
 
 684                         return extension.modelRange2WidgetRange(new Region(position.getOffset(), position.getLength()));
 
 688                         IRegion region= fViewer.getVisibleRegion();
 
 689                         if (includes(region, position))
 
 690                                 return new Region(position.getOffset() -  region.getOffset(), position.getLength());
 
 696         private static Point getMinimumLocation(StyledText text, int offset, int length) {
 
 697                 Point minLocation= new Point(Integer.MAX_VALUE, Integer.MAX_VALUE);
 
 699                 for (int i= 0; i <= length; i++) {
 
 700                         Point location= text.getLocationAtOffset(offset + i);
 
 702                         if (location.x < minLocation.x)
 
 703                                 minLocation.x= location.x;                      
 
 704                         if (location.y < minLocation.y)
 
 705                                 minLocation.y= location.y;                      
 
 711         private static Point getMaximumLocation(StyledText text, int offset, int length) {
 
 712                 Point maxLocation= new Point(Integer.MIN_VALUE, Integer.MIN_VALUE);
 
 714                 for (int i= 0; i <= length; i++) {
 
 715                         Point location= text.getLocationAtOffset(offset + i);
 
 717                         if (location.x > maxLocation.x)
 
 718                                 maxLocation.x= location.x;                      
 
 719                         if (location.y > maxLocation.y)
 
 720                                 maxLocation.y= location.y;                      
 
 726         private void redrawRegion() {
 
 727                 IRegion widgetRange= asWidgetRange(fFramePosition);
 
 728                 if (widgetRange == null) {
 
 729                         leave(UNINSTALL | COMMIT | DOCUMENT_CHANGED);
 
 733                 StyledText text= fViewer.getTextWidget();
 
 734                 if (text != null && !text.isDisposed()) 
 
 735                         text.redrawRange(widgetRange.getOffset(), widgetRange.getLength(), true);
 
 738         private void selectRegion() {
 
 740                 IRegion widgetRange= asWidgetRange(fFramePosition);
 
 741                 if (widgetRange == null) {
 
 742                         leave(UNINSTALL | COMMIT | DOCUMENT_CHANGED);
 
 746                 StyledText text= fViewer.getTextWidget();
 
 747                 if (text != null && !text.isDisposed()) {
 
 748                         int start= widgetRange.getOffset();
 
 749                         int end= widgetRange.getLength() + start;
 
 750                         text.setSelection(start, end);
 
 754         private void updateCaret() {
 
 756                 IRegion widgetRange= asWidgetRange(fFramePosition);
 
 757                 if (widgetRange == null) {
 
 758                         leave(UNINSTALL | COMMIT | DOCUMENT_CHANGED);
 
 762                 int offset= widgetRange.getOffset() + fCaretOffset;
 
 763                 StyledText text= fViewer.getTextWidget();
 
 764                 if (text != null && !text.isDisposed())
 
 765                         text.setCaretOffset(offset);
 
 769          * @see ModifyListener#modifyText(ModifyEvent)
 
 771         public void modifyText(ModifyEvent e) {
 
 772                 // reposition caret after StyledText
 
 777         private static void handleException(Shell shell, Exception e) {
 
 778                 String title= LinkedPositionMessages.getString("LinkedPositionUI.error.title"); //$NON-NLS-1$
 
 779                 if (e instanceof CoreException)
 
 780                         ExceptionHandler.handle((CoreException)e, shell, title, null);
 
 781                 else if (e instanceof InvocationTargetException)
 
 782                         ExceptionHandler.handle((InvocationTargetException)e, shell, title, null);
 
 784                         MessageDialog.openError(shell, title, e.getMessage());
 
 785                         PHPeclipsePlugin.log(e);
 
 790          * @see ITextInputListener#inputDocumentAboutToBeChanged(IDocument, IDocument)
 
 792         public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
 
 793                 // 5326: leave linked mode on document change
 
 794                 int flags= UNINSTALL | COMMIT | (oldInput.equals(newInput) ? 0 : DOCUMENT_CHANGED);
 
 799          * @see ITextInputListener#inputDocumentChanged(IDocument, IDocument)
 
 801         public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
 
 804         private static boolean includes(IRegion region, Position position) {
 
 806                         position.getOffset() >= region.getOffset() &&
 
 807                         position.getOffset() + position.getLength() <= region.getOffset() + region.getLength();
 
 811          * @see org.eclipse.jface.text.ITextListener#textChanged(TextEvent)
 
 813         public void textChanged(TextEvent event) {
 
 822          * @see org.eclipse.swt.events.ShellListener#shellActivated(org.eclipse.swt.events.ShellEvent)
 
 824         public void shellActivated(ShellEvent event) {
 
 828          * @see org.eclipse.swt.events.ShellListener#shellClosed(org.eclipse.swt.events.ShellEvent)
 
 830         public void shellClosed(ShellEvent event) {
 
 831                 leave(UNINSTALL | COMMIT | DOCUMENT_CHANGED);
 
 835          * @see org.eclipse.swt.events.ShellListener#shellDeactivated(org.eclipse.swt.events.ShellEvent)
 
 837         public void shellDeactivated(ShellEvent event) {
 
 838                 // don't deactivate on focus lost, since the proposal popups may take focus
 
 839                 // plus: it doesn't hurt if you can check with another window without losing linked mode
 
 840                 // since there is no intrusive popup sticking out.
 
 842                 // need to check first what happens on reentering based on an open action
 
 843                 // Seems to be no problem
 
 845                 // TODO check whether we can leave it or uncomment it after debugging
 
 846                 // PS: why DOCUMENT_CHANGED? We want to trigger a redraw! (Shell deactivated does not mean
 
 847                 // it is not visible any longer.
 
 848 //              leave(UNINSTALL | COMMIT | DOCUMENT_CHANGED);
 
 851                 // Check with content assistant and only leave if its not the proposal shell that took the 
 
 857 //              if (fAssistant == null || fViewer == null || (text= fViewer.getTextWidget()) == null 
 
 858 //                              || (display= text.getDisplay()) == null || display.isDisposed()) {
 
 859                 if ( fViewer == null || (text= fViewer.getTextWidget()) == null 
 
 860                       || (display= text.getDisplay()) == null || display.isDisposed()) {
 
 861                         leave(UNINSTALL | COMMIT);
 
 863                         // Post in UI thread since the assistant popup will only get the focus after we lose it.
 
 864                         display.asyncExec(new Runnable() {
 
 866                                         // TODO add isDisposed / isUninstalled / hasLeft check? for now: check for content type,
 
 867                                         // since it gets nullified in leave()
 
 868                                         if (fIsActive) {// && (fAssistant == null || !fAssistant.hasFocus()))  {
 
 869                                                 leave(UNINSTALL | COMMIT);
 
 877          * @see org.eclipse.swt.events.ShellListener#shellDeiconified(org.eclipse.swt.events.ShellEvent)
 
 879         public void shellDeiconified(ShellEvent event) {
 
 883          * @see org.eclipse.swt.events.ShellListener#shellIconified(org.eclipse.swt.events.ShellEvent)
 
 885         public void shellIconified(ShellEvent event) {
 
 886                 leave(UNINSTALL | COMMIT | DOCUMENT_CHANGED);