PHPEditor changed to PHPUnitEditor
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / phpeditor / PHPEditor.java
1 package net.sourceforge.phpeclipse.phpeditor;
2
3 /**********************************************************************
4 Copyright (c) 2000, 2002 IBM Corp. and others.
5 All rights reserved. This program and the accompanying materials
6 are made available under the terms of the Common Public License v1.0
7 which accompanies this distribution, and is available at
8 http://www.eclipse.org/legal/cpl-v10.html
9
10 Contributors:
11     IBM Corporation - Initial implementation
12     Klaus Hartlage - www.eclipseproject.de
13 **********************************************************************/
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.ResourceBundle;
17 import java.util.StringTokenizer;
18
19 import net.sourceforge.phpdt.internal.ui.actions.CompositeActionGroup;
20 import net.sourceforge.phpdt.internal.ui.text.HTMLTextPresenter;
21 import net.sourceforge.phpdt.internal.ui.text.PHPPairMatcher;
22 import net.sourceforge.phpdt.internal.ui.viewsupport.IViewPartInputProvider;
23 import net.sourceforge.phpdt.ui.IContextMenuConstants;
24 import net.sourceforge.phpdt.ui.PreferenceConstants;
25 import net.sourceforge.phpdt.ui.actions.GenerateActionGroup;
26 import net.sourceforge.phpdt.ui.actions.GotoMatchingBracketAction;
27 import net.sourceforge.phpdt.ui.text.IColorManager;
28 import net.sourceforge.phpdt.ui.text.JavaTextTools;
29 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
30 import net.sourceforge.phpeclipse.phpeditor.php.IPHPPartitionScannerConstants;
31
32 import org.eclipse.core.resources.IResource;
33 import org.eclipse.core.runtime.CoreException;
34 import org.eclipse.core.runtime.IProgressMonitor;
35 import org.eclipse.core.runtime.IStatus;
36 import org.eclipse.core.runtime.Preferences;
37 import org.eclipse.jface.action.Action;
38 import org.eclipse.jface.action.GroupMarker;
39 import org.eclipse.jface.action.IAction;
40 import org.eclipse.jface.action.MenuManager;
41 import org.eclipse.jface.action.Separator;
42 import org.eclipse.jface.preference.IPreferenceStore;
43 import org.eclipse.jface.preference.PreferenceConverter;
44 import org.eclipse.jface.text.BadLocationException;
45 import org.eclipse.jface.text.DefaultInformationControl;
46 import org.eclipse.jface.text.DocumentEvent;
47 import org.eclipse.jface.text.IDocument;
48 import org.eclipse.jface.text.IDocumentListener;
49 import org.eclipse.jface.text.IInformationControl;
50 import org.eclipse.jface.text.IInformationControlCreator;
51 import org.eclipse.jface.text.IRegion;
52 import org.eclipse.jface.text.ITextHover;
53 import org.eclipse.jface.text.ITextInputListener;
54 import org.eclipse.jface.text.ITextOperationTarget;
55 import org.eclipse.jface.text.ITextViewer;
56 import org.eclipse.jface.text.ITextViewerExtension2;
57 import org.eclipse.jface.text.ITextViewerExtension3;
58 import org.eclipse.jface.text.ITypedRegion;
59 import org.eclipse.jface.text.Position;
60 import org.eclipse.jface.text.Region;
61 import org.eclipse.jface.text.information.IInformationProvider;
62 import org.eclipse.jface.text.information.InformationPresenter;
63 import org.eclipse.jface.text.source.Annotation;
64 import org.eclipse.jface.text.source.AnnotationRulerColumn;
65 import org.eclipse.jface.text.source.CompositeRuler;
66 import org.eclipse.jface.text.source.IAnnotationAccess;
67 import org.eclipse.jface.text.source.IOverviewRuler;
68 import org.eclipse.jface.text.source.ISharedTextColors;
69 import org.eclipse.jface.text.source.ISourceViewer;
70 import org.eclipse.jface.text.source.ISourceViewerExtension;
71 import org.eclipse.jface.text.source.IVerticalRuler;
72 import org.eclipse.jface.text.source.IVerticalRulerColumn;
73 import org.eclipse.jface.text.source.LineNumberRulerColumn;
74 import org.eclipse.jface.text.source.OverviewRuler;
75 import org.eclipse.jface.text.source.SourceViewer;
76 import org.eclipse.jface.text.source.SourceViewerConfiguration;
77 import org.eclipse.jface.util.IPropertyChangeListener;
78 import org.eclipse.jface.util.PropertyChangeEvent;
79 import org.eclipse.jface.viewers.ISelectionChangedListener;
80 import org.eclipse.jface.viewers.SelectionChangedEvent;
81 import org.eclipse.swt.SWT;
82 import org.eclipse.swt.custom.BidiSegmentEvent;
83 import org.eclipse.swt.custom.BidiSegmentListener;
84 import org.eclipse.swt.custom.StyleRange;
85 import org.eclipse.swt.custom.StyledText;
86 import org.eclipse.swt.events.FocusEvent;
87 import org.eclipse.swt.events.FocusListener;
88 import org.eclipse.swt.events.KeyEvent;
89 import org.eclipse.swt.events.KeyListener;
90 import org.eclipse.swt.events.MouseEvent;
91 import org.eclipse.swt.events.MouseListener;
92 import org.eclipse.swt.events.MouseMoveListener;
93 import org.eclipse.swt.events.PaintEvent;
94 import org.eclipse.swt.events.PaintListener;
95 import org.eclipse.swt.graphics.Color;
96 import org.eclipse.swt.graphics.Cursor;
97 import org.eclipse.swt.graphics.GC;
98 import org.eclipse.swt.graphics.Point;
99 import org.eclipse.swt.graphics.RGB;
100 import org.eclipse.swt.widgets.Composite;
101 import org.eclipse.swt.widgets.Control;
102 import org.eclipse.swt.widgets.Display;
103 import org.eclipse.swt.widgets.Shell;
104 import org.eclipse.ui.IEditorInput;
105 import org.eclipse.ui.IPartService;
106 import org.eclipse.ui.IWorkbenchPart;
107 import org.eclipse.ui.IWorkbenchWindow;
108 import org.eclipse.ui.actions.ActionContext;
109 import org.eclipse.ui.actions.ActionGroup;
110 import org.eclipse.ui.editors.text.DefaultEncodingSupport;
111 import org.eclipse.ui.editors.text.IEncodingSupport;
112 import org.eclipse.ui.texteditor.AddTaskAction;
113 import org.eclipse.ui.texteditor.ContentAssistAction;
114 import org.eclipse.ui.texteditor.DefaultRangeIndicator;
115 import org.eclipse.ui.texteditor.IAbstractTextEditorHelpContextIds;
116 import org.eclipse.ui.texteditor.IDocumentProvider;
117 import org.eclipse.ui.texteditor.IEditorStatusLine;
118 import org.eclipse.ui.texteditor.ITextEditorActionConstants;
119 import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
120 import org.eclipse.ui.texteditor.ResourceAction;
121 import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
122 import org.eclipse.ui.texteditor.StatusTextEditor;
123 import org.eclipse.ui.texteditor.TextEditorAction;
124 import org.eclipse.ui.texteditor.TextOperationAction;
125 import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
126
127 /**
128  * PHP specific text editor.
129  */
130 public abstract class PHPEditor extends StatusTextEditor implements IViewPartInputProvider { // extends TextEditor {
131   class SelectionChangedListener implements ISelectionChangedListener {
132     public void selectionChanged(SelectionChangedEvent event) {
133       doSelectionChanged(event);
134     }
135   };
136
137   /*
138   * Link mode.  
139   */
140   class MouseClickListener
141     implements
142       KeyListener,
143       MouseListener,
144       MouseMoveListener,
145       FocusListener,
146       PaintListener,
147       IPropertyChangeListener,
148       IDocumentListener,
149       ITextInputListener {
150
151     /** The session is active. */
152     private boolean fActive;
153
154     /** The currently active style range. */
155     private IRegion fActiveRegion;
156     /** The currently active style range as position. */
157     private Position fRememberedPosition;
158     /** The hand cursor. */
159     private Cursor fCursor;
160
161     /** The link color. */
162     private Color fColor;
163     /** The key modifier mask. */
164     private int fKeyModifierMask;
165
166     public void deactivate() {
167       deactivate(false);
168     }
169
170     public void deactivate(boolean redrawAll) {
171       if (!fActive)
172         return;
173
174       repairRepresentation(redrawAll);
175       fActive = false;
176     }
177
178     public void install() {
179
180       ISourceViewer sourceViewer = getSourceViewer();
181       if (sourceViewer == null)
182         return;
183
184       StyledText text = sourceViewer.getTextWidget();
185       if (text == null || text.isDisposed())
186         return;
187
188       updateColor(sourceViewer);
189
190       sourceViewer.addTextInputListener(this);
191
192       IDocument document = sourceViewer.getDocument();
193       if (document != null)
194         document.addDocumentListener(this);
195
196       text.addKeyListener(this);
197       text.addMouseListener(this);
198       text.addMouseMoveListener(this);
199       text.addFocusListener(this);
200       text.addPaintListener(this);
201
202       updateKeyModifierMask();
203
204       IPreferenceStore preferenceStore = getPreferenceStore();
205       preferenceStore.addPropertyChangeListener(this);
206     }
207
208     private void updateKeyModifierMask() {
209       String modifiers = getPreferenceStore().getString(BROWSER_LIKE_LINKS_KEY_MODIFIER);
210       fKeyModifierMask = computeStateMask(modifiers);
211       if (fKeyModifierMask == -1) {
212         // Fallback to stored state mask
213         fKeyModifierMask = getPreferenceStore().getInt(BROWSER_LIKE_LINKS_KEY_MODIFIER_MASK);
214       };
215     }
216
217     private int computeStateMask(String modifiers) {
218       if (modifiers == null)
219         return -1;
220
221       if (modifiers.length() == 0)
222         return SWT.NONE;
223
224       int stateMask = 0;
225       StringTokenizer modifierTokenizer = new StringTokenizer(modifiers, ",;.:+-* "); //$NON-NLS-1$
226       while (modifierTokenizer.hasMoreTokens()) {
227         int modifier = EditorUtility.findLocalizedModifier(modifierTokenizer.nextToken());
228         if (modifier == 0 || (stateMask & modifier) == modifier)
229           return -1;
230         stateMask = stateMask | modifier;
231       }
232       return stateMask;
233     }
234
235     public void uninstall() {
236
237       if (fColor != null) {
238         fColor.dispose();
239         fColor = null;
240       }
241
242       if (fCursor != null) {
243         fCursor.dispose();
244         fCursor = null;
245       }
246
247       ISourceViewer sourceViewer = getSourceViewer();
248       if (sourceViewer == null)
249         return;
250
251       sourceViewer.removeTextInputListener(this);
252
253       IDocument document = sourceViewer.getDocument();
254       if (document != null)
255         document.removeDocumentListener(this);
256
257       IPreferenceStore preferenceStore = getPreferenceStore();
258       if (preferenceStore != null)
259         preferenceStore.removePropertyChangeListener(this);
260
261       StyledText text = sourceViewer.getTextWidget();
262       if (text == null || text.isDisposed())
263         return;
264
265       text.removeKeyListener(this);
266       text.removeMouseListener(this);
267       text.removeMouseMoveListener(this);
268       text.removeFocusListener(this);
269       text.removePaintListener(this);
270     }
271
272     /*
273      * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent)
274      */
275     public void propertyChange(PropertyChangeEvent event) {
276       if (event.getProperty().equals(PHPEditor.LINK_COLOR)) {
277         ISourceViewer viewer = getSourceViewer();
278         if (viewer != null)
279           updateColor(viewer);
280       } else if (event.getProperty().equals(BROWSER_LIKE_LINKS_KEY_MODIFIER)) {
281         updateKeyModifierMask();
282       }
283     }
284
285     private void updateColor(ISourceViewer viewer) {
286       if (fColor != null)
287         fColor.dispose();
288
289       StyledText text = viewer.getTextWidget();
290       if (text == null || text.isDisposed())
291         return;
292
293       Display display = text.getDisplay();
294       fColor = createColor(getPreferenceStore(), PHPEditor.LINK_COLOR, display);
295     }
296
297     /**
298      * Creates a color from the information stored in the given preference store.
299      * Returns <code>null</code> if there is no such information available.
300      */
301     private Color createColor(IPreferenceStore store, String key, Display display) {
302
303       RGB rgb = null;
304
305       if (store.contains(key)) {
306
307         if (store.isDefault(key))
308           rgb = PreferenceConverter.getDefaultColor(store, key);
309         else
310           rgb = PreferenceConverter.getColor(store, key);
311
312         if (rgb != null)
313           return new Color(display, rgb);
314       }
315
316       return null;
317     }
318
319     private void repairRepresentation() {
320       repairRepresentation(false);
321     }
322
323     private void repairRepresentation(boolean redrawAll) {
324
325       if (fActiveRegion == null)
326         return;
327
328       ISourceViewer viewer = getSourceViewer();
329       if (viewer != null) {
330         resetCursor(viewer);
331
332         int offset = fActiveRegion.getOffset();
333         int length = fActiveRegion.getLength();
334
335         // remove style
336         if (!redrawAll && viewer instanceof ITextViewerExtension2)
337            ((ITextViewerExtension2) viewer).invalidateTextPresentation(offset, length);
338         else
339           viewer.invalidateTextPresentation();
340
341         // remove underline                             
342         if (viewer instanceof ITextViewerExtension3) {
343           ITextViewerExtension3 extension = (ITextViewerExtension3) viewer;
344           offset = extension.modelOffset2WidgetOffset(offset);
345         } else {
346           offset -= viewer.getVisibleRegion().getOffset();
347         }
348
349         StyledText text = viewer.getTextWidget();
350         try {
351           text.redrawRange(offset, length, true);
352         } catch (IllegalArgumentException x) {
353           PHPeclipsePlugin.log(x);
354         }
355       }
356
357       fActiveRegion = null;
358     }
359
360     // will eventually be replaced by a method provided by jdt.core             
361     private IRegion selectWord(IDocument document, int anchor) {
362
363       try {
364         int offset = anchor;
365         char c;
366
367         while (offset >= 0) {
368           c = document.getChar(offset);
369           if (!Character.isJavaIdentifierPart(c))
370             break;
371           --offset;
372         }
373
374         int start = offset;
375
376         offset = anchor;
377         int length = document.getLength();
378
379         while (offset < length) {
380           c = document.getChar(offset);
381           if (!Character.isJavaIdentifierPart(c))
382             break;
383           ++offset;
384         }
385
386         int end = offset;
387
388         if (start == end)
389           return new Region(start, 0);
390         else
391           return new Region(start + 1, end - start - 1);
392
393       } catch (BadLocationException x) {
394         return null;
395       }
396     }
397
398     IRegion getCurrentTextRegion(ISourceViewer viewer) {
399
400       int offset = getCurrentTextOffset(viewer);
401       if (offset == -1)
402         return null;
403
404       return null;
405       //                                IJavaElement input= SelectionConverter.getInput(PHPEditor.this);
406       //                                if (input == null)
407       //                                        return null;
408       //
409       //                                try {
410       //                                
411       //                                        IJavaElement[] elements= null;
412       //                                        synchronized (input) {
413       //                                                elements= ((ICodeAssist) input).codeSelect(offset, 0);
414       //                                        }
415       //                                
416       //                                        if (elements == null || elements.length == 0)
417       //                                                return null;
418       //                                        
419       //                                        return selectWord(viewer.getDocument(), offset);
420       //                                        
421       //                                } catch (JavaModelException e) {
422       //                                        return null;    
423       //                                }
424     }
425
426     private int getCurrentTextOffset(ISourceViewer viewer) {
427
428       try {
429         StyledText text = viewer.getTextWidget();
430         if (text == null || text.isDisposed())
431           return -1;
432
433         Display display = text.getDisplay();
434         Point absolutePosition = display.getCursorLocation();
435         Point relativePosition = text.toControl(absolutePosition);
436
437         int widgetOffset = text.getOffsetAtLocation(relativePosition);
438         if (viewer instanceof ITextViewerExtension3) {
439           ITextViewerExtension3 extension = (ITextViewerExtension3) viewer;
440           return extension.widgetOffset2ModelOffset(widgetOffset);
441         } else {
442           return widgetOffset + viewer.getVisibleRegion().getOffset();
443         }
444
445       } catch (IllegalArgumentException e) {
446         return -1;
447       }
448     }
449
450     private void highlightRegion(ISourceViewer viewer, IRegion region) {
451
452       if (region.equals(fActiveRegion))
453         return;
454
455       repairRepresentation();
456
457       StyledText text = viewer.getTextWidget();
458       if (text == null || text.isDisposed())
459         return;
460
461       // highlight region
462       int offset = 0;
463       int length = 0;
464
465       if (viewer instanceof ITextViewerExtension3) {
466         ITextViewerExtension3 extension = (ITextViewerExtension3) viewer;
467         IRegion widgetRange = extension.modelRange2WidgetRange(region);
468         if (widgetRange == null)
469           return;
470
471         offset = widgetRange.getOffset();
472         length = widgetRange.getLength();
473
474       } else {
475         offset = region.getOffset() - viewer.getVisibleRegion().getOffset();
476         length = region.getLength();
477       }
478
479       StyleRange oldStyleRange = text.getStyleRangeAtOffset(offset);
480       Color foregroundColor = fColor;
481       Color backgroundColor = oldStyleRange == null ? text.getBackground() : oldStyleRange.background;
482       StyleRange styleRange = new StyleRange(offset, length, foregroundColor, backgroundColor);
483       text.setStyleRange(styleRange);
484
485       // underline
486       text.redrawRange(offset, length, true);
487
488       fActiveRegion = region;
489     }
490
491     private void activateCursor(ISourceViewer viewer) {
492       StyledText text = viewer.getTextWidget();
493       if (text == null || text.isDisposed())
494         return;
495       Display display = text.getDisplay();
496       if (fCursor == null)
497         fCursor = new Cursor(display, SWT.CURSOR_HAND);
498       text.setCursor(fCursor);
499     }
500
501     private void resetCursor(ISourceViewer viewer) {
502       StyledText text = viewer.getTextWidget();
503       if (text != null && !text.isDisposed())
504         text.setCursor(null);
505
506       if (fCursor != null) {
507         fCursor.dispose();
508         fCursor = null;
509       }
510     }
511
512     /*
513      * @see org.eclipse.swt.events.KeyListener#keyPressed(org.eclipse.swt.events.KeyEvent)
514      */
515     public void keyPressed(KeyEvent event) {
516
517       if (fActive) {
518         deactivate();
519         return;
520       }
521
522       if (event.keyCode != fKeyModifierMask) {
523         deactivate();
524         return;
525       }
526
527       fActive = true;
528
529       //                                removed for #25871                      
530       //
531       //                                ISourceViewer viewer= getSourceViewer();
532       //                                if (viewer == null)
533       //                                        return;
534       //                        
535       //                                IRegion region= getCurrentTextRegion(viewer);
536       //                                if (region == null)
537       //                                        return;
538       //                        
539       //                                highlightRegion(viewer, region);
540       //                                activateCursor(viewer);                                                                                         
541     }
542
543     /*
544      * @see org.eclipse.swt.events.KeyListener#keyReleased(org.eclipse.swt.events.KeyEvent)
545      */
546     public void keyReleased(KeyEvent event) {
547
548       if (!fActive)
549         return;
550
551       deactivate();
552     }
553
554     /*
555      * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
556      */
557     public void mouseDoubleClick(MouseEvent e) {
558     }
559     /*
560      * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
561      */
562     public void mouseDown(MouseEvent event) {
563
564       if (!fActive)
565         return;
566
567       if (event.stateMask != fKeyModifierMask) {
568         deactivate();
569         return;
570       }
571
572       if (event.button != 1) {
573         deactivate();
574         return;
575       }
576     }
577
578     /*
579      * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
580      */
581     public void mouseUp(MouseEvent e) {
582
583       if (!fActive)
584         return;
585
586       if (e.button != 1) {
587         deactivate();
588         return;
589       }
590
591       boolean wasActive = fCursor != null;
592
593       deactivate();
594
595       if (wasActive) {
596         IAction action = getAction("OpenEditor"); //$NON-NLS-1$
597         if (action != null)
598           action.run();
599       }
600     }
601
602     /*
603      * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
604      */
605     public void mouseMove(MouseEvent event) {
606
607       if (event.widget instanceof Control && !((Control) event.widget).isFocusControl()) {
608         deactivate();
609         return;
610       }
611
612       if (!fActive) {
613         if (event.stateMask != fKeyModifierMask)
614           return;
615         // modifier was already pressed
616         fActive = true;
617       }
618
619       ISourceViewer viewer = getSourceViewer();
620       if (viewer == null) {
621         deactivate();
622         return;
623       }
624
625       StyledText text = viewer.getTextWidget();
626       if (text == null || text.isDisposed()) {
627         deactivate();
628         return;
629       }
630
631       if ((event.stateMask & SWT.BUTTON1) != 0 && text.getSelectionCount() != 0) {
632         deactivate();
633         return;
634       }
635
636       IRegion region = getCurrentTextRegion(viewer);
637       if (region == null || region.getLength() == 0) {
638         repairRepresentation();
639         return;
640       }
641
642       highlightRegion(viewer, region);
643       activateCursor(viewer);
644     }
645
646     /*
647      * @see org.eclipse.swt.events.FocusListener#focusGained(org.eclipse.swt.events.FocusEvent)
648      */
649     public void focusGained(FocusEvent e) {
650     }
651
652     /*
653      * @see org.eclipse.swt.events.FocusListener#focusLost(org.eclipse.swt.events.FocusEvent)
654      */
655     public void focusLost(FocusEvent event) {
656       deactivate();
657     }
658
659     /*
660      * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
661      */
662     public void documentAboutToBeChanged(DocumentEvent event) {
663       if (fActive && fActiveRegion != null) {
664         fRememberedPosition = new Position(fActiveRegion.getOffset(), fActiveRegion.getLength());
665         try {
666           event.getDocument().addPosition(fRememberedPosition);
667         } catch (BadLocationException x) {
668           fRememberedPosition = null;
669         }
670       }
671     }
672
673     /*
674      * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
675      */
676     public void documentChanged(DocumentEvent event) {
677       if (fRememberedPosition != null && !fRememberedPosition.isDeleted()) {
678         event.getDocument().removePosition(fRememberedPosition);
679         fActiveRegion = new Region(fRememberedPosition.getOffset(), fRememberedPosition.getLength());
680       }
681       fRememberedPosition = null;
682
683       ISourceViewer viewer = getSourceViewer();
684       if (viewer != null) {
685         StyledText widget = viewer.getTextWidget();
686         if (widget != null && !widget.isDisposed()) {
687           widget.getDisplay().asyncExec(new Runnable() {
688             public void run() {
689               deactivate();
690             }
691           });
692         }
693       }
694     }
695
696     /*
697      * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
698      */
699     public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
700       if (oldInput == null)
701         return;
702       deactivate();
703       oldInput.removeDocumentListener(this);
704     }
705
706     /*
707      * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
708      */
709     public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
710       if (newInput == null)
711         return;
712       newInput.addDocumentListener(this);
713     }
714
715     /*
716      * @see PaintListener#paintControl(PaintEvent)
717      */
718     public void paintControl(PaintEvent event) {
719       if (fActiveRegion == null)
720         return;
721
722       ISourceViewer viewer = getSourceViewer();
723       if (viewer == null)
724         return;
725
726       StyledText text = viewer.getTextWidget();
727       if (text == null || text.isDisposed())
728         return;
729
730       int offset = 0;
731       int length = 0;
732
733       if (viewer instanceof ITextViewerExtension3) {
734
735         ITextViewerExtension3 extension = (ITextViewerExtension3) viewer;
736         IRegion widgetRange = extension.modelRange2WidgetRange(new Region(offset, length));
737         if (widgetRange == null)
738           return;
739
740         offset = widgetRange.getOffset();
741         length = widgetRange.getLength();
742
743       } else {
744
745         IRegion region = viewer.getVisibleRegion();
746         if (!includes(region, fActiveRegion))
747           return;
748
749         offset = fActiveRegion.getOffset() - region.getOffset();
750         length = fActiveRegion.getLength();
751       }
752
753       // support for bidi
754       Point minLocation = getMinimumLocation(text, offset, length);
755       Point maxLocation = getMaximumLocation(text, offset, length);
756
757       int x1 = minLocation.x;
758       int x2 = minLocation.x + maxLocation.x - minLocation.x - 1;
759       int y = minLocation.y + text.getLineHeight() - 1;
760
761       GC gc = event.gc;
762       if (fColor != null && !fColor.isDisposed())
763         gc.setForeground(fColor);
764       gc.drawLine(x1, y, x2, y);
765     }
766
767     private boolean includes(IRegion region, IRegion position) {
768       return position.getOffset() >= region.getOffset()
769         && position.getOffset() + position.getLength() <= region.getOffset() + region.getLength();
770     }
771
772     private Point getMinimumLocation(StyledText text, int offset, int length) {
773       Point minLocation = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE);
774
775       for (int i = 0; i <= length; i++) {
776         Point location = text.getLocationAtOffset(offset + i);
777
778         if (location.x < minLocation.x)
779           minLocation.x = location.x;
780         if (location.y < minLocation.y)
781           minLocation.y = location.y;
782       }
783
784       return minLocation;
785     }
786
787     private Point getMaximumLocation(StyledText text, int offset, int length) {
788       Point maxLocation = new Point(Integer.MIN_VALUE, Integer.MIN_VALUE);
789
790       for (int i = 0; i <= length; i++) {
791         Point location = text.getLocationAtOffset(offset + i);
792
793         if (location.x > maxLocation.x)
794           maxLocation.x = location.x;
795         if (location.y > maxLocation.y)
796           maxLocation.y = location.y;
797       }
798
799       return maxLocation;
800     }
801   };
802
803   /**
804    * This action dispatches into two behaviours: If there is no current text
805    * hover, the javadoc is displayed using information presenter. If there is
806    * a current text hover, it is converted into a information presenter in
807    * order to make it sticky.
808    */
809   class InformationDispatchAction extends TextEditorAction {
810
811     /** The wrapped text operation action. */
812     private final TextOperationAction fTextOperationAction;
813
814     /**
815      * Creates a dispatch action.
816      */
817     public InformationDispatchAction(ResourceBundle resourceBundle, String prefix, final TextOperationAction textOperationAction) {
818       super(resourceBundle, prefix, PHPEditor.this);
819       if (textOperationAction == null)
820         throw new IllegalArgumentException();
821       fTextOperationAction = textOperationAction;
822     }
823
824     /*
825      * @see org.eclipse.jface.action.IAction#run()
826      */
827     public void run() {
828
829       ISourceViewer sourceViewer = getSourceViewer();
830       if (sourceViewer == null) {
831         fTextOperationAction.run();
832         return;
833       }
834
835       if (!(sourceViewer instanceof ITextViewerExtension2)) {
836         fTextOperationAction.run();
837         return;
838       }
839
840       ITextViewerExtension2 textViewerExtension2 = (ITextViewerExtension2) sourceViewer;
841
842       // does a text hover exist?
843       ITextHover textHover = textViewerExtension2.getCurrentTextHover();
844       if (textHover == null) {
845         fTextOperationAction.run();
846         return;
847       }
848
849       Point hoverEventLocation = textViewerExtension2.getHoverEventLocation();
850       int offset = computeOffsetAtLocation(sourceViewer, hoverEventLocation.x, hoverEventLocation.y);
851       if (offset == -1) {
852         fTextOperationAction.run();
853         return;
854       }
855
856       try {
857         // get the text hover content
858         IDocument document = sourceViewer.getDocument();
859         String contentType = document.getContentType(offset);
860
861         final IRegion hoverRegion = textHover.getHoverRegion(sourceViewer, offset);
862         if (hoverRegion == null)
863           return;
864
865         final String hoverInfo = textHover.getHoverInfo(sourceViewer, hoverRegion);
866
867         // with information provider
868         IInformationProvider informationProvider = new IInformationProvider() {
869           /*
870            * @see org.eclipse.jface.text.information.IInformationProvider#getSubject(org.eclipse.jface.text.ITextViewer, int)
871            */
872           public IRegion getSubject(ITextViewer textViewer, int offset) {
873             return hoverRegion;
874           }
875           /*
876            * @see org.eclipse.jface.text.information.IInformationProvider#getInformation(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion)
877            */
878           public String getInformation(ITextViewer textViewer, IRegion subject) {
879             return hoverInfo;
880           }
881         };
882
883         fInformationPresenter.setOffset(offset);
884         fInformationPresenter.setInformationProvider(informationProvider, contentType);
885         fInformationPresenter.showInformation();
886
887       } catch (BadLocationException e) {
888       }
889     }
890
891     // modified version from TextViewer
892     private int computeOffsetAtLocation(ITextViewer textViewer, int x, int y) {
893
894       StyledText styledText = textViewer.getTextWidget();
895       IDocument document = textViewer.getDocument();
896
897       if (document == null)
898         return -1;
899
900       try {
901         int widgetLocation = styledText.getOffsetAtLocation(new Point(x, y));
902         if (textViewer instanceof ITextViewerExtension3) {
903           ITextViewerExtension3 extension = (ITextViewerExtension3) textViewer;
904           return extension.widgetOffset2ModelOffset(widgetLocation);
905         } else {
906           IRegion visibleRegion = textViewer.getVisibleRegion();
907           return widgetLocation + visibleRegion.getOffset();
908         }
909       } catch (IllegalArgumentException e) {
910         return -1;
911       }
912
913     }
914   };
915
916   static protected class AnnotationAccess implements IAnnotationAccess {
917     /*
918      * @see org.eclipse.jface.text.source.IAnnotationAccess#getType(org.eclipse.jface.text.source.Annotation)
919      */
920     public Object getType(Annotation annotation) {
921       if (annotation instanceof IJavaAnnotation) {
922         IJavaAnnotation javaAnnotation = (IJavaAnnotation) annotation;
923         if (javaAnnotation.isRelevant())
924           return javaAnnotation.getAnnotationType();
925       }
926       return null;
927     }
928
929     /*
930      * @see org.eclipse.jface.text.source.IAnnotationAccess#isMultiLine(org.eclipse.jface.text.source.Annotation)
931      */
932     public boolean isMultiLine(Annotation annotation) {
933       return true;
934     }
935
936     /*
937      * @see org.eclipse.jface.text.source.IAnnotationAccess#isTemporary(org.eclipse.jface.text.source.Annotation)
938      */
939     public boolean isTemporary(Annotation annotation) {
940       if (annotation instanceof IJavaAnnotation) {
941         IJavaAnnotation javaAnnotation = (IJavaAnnotation) annotation;
942         if (javaAnnotation.isRelevant())
943           return javaAnnotation.isTemporary();
944       }
945       return false;
946     }
947   };
948
949   private class PropertyChangeListener implements org.eclipse.core.runtime.Preferences.IPropertyChangeListener {
950     /*
951      * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent)
952      */
953     public void propertyChange(org.eclipse.core.runtime.Preferences.PropertyChangeEvent event) {
954       handlePreferencePropertyChanged(event);
955     }
956   };
957   /** Preference key for showing the line number ruler */
958   private final static String LINE_NUMBER_RULER = PreferenceConstants.EDITOR_LINE_NUMBER_RULER;
959   /** Preference key for the foreground color of the line numbers */
960   private final static String LINE_NUMBER_COLOR = PreferenceConstants.EDITOR_LINE_NUMBER_RULER_COLOR;
961   /** Preference key for the link color */
962   private final static String LINK_COLOR = PreferenceConstants.EDITOR_LINK_COLOR;
963
964   // protected PHPActionGroup fActionGroups;
965   /** The outline page */
966   private AbstractContentOutlinePage fOutlinePage;
967
968   //  protected PHPSyntaxParserThread fValidationThread = null;
969
970   // private IPreferenceStore fPHPPrefStore;
971   /** The selection changed listener */
972   protected ISelectionChangedListener fSelectionChangedListener = new SelectionChangedListener();
973
974   /** The editor's bracket matcher */
975   private PHPPairMatcher fBracketMatcher = new PHPPairMatcher(BRACKETS);
976   /** The line number ruler column */
977   private LineNumberRulerColumn fLineNumberRulerColumn;
978   /** This editor's encoding support */
979   private DefaultEncodingSupport fEncodingSupport;
980   /** The mouse listener */
981   private MouseClickListener fMouseListener;
982
983   protected CompositeActionGroup fActionGroups;
984   /** The standard action groups added to the menu */
985   protected GenerateActionGroup fGenerateActionGroup;
986   protected CompositeActionGroup fContextMenuGroup;
987
988   /** The information presenter. */
989   private InformationPresenter fInformationPresenter;
990   /** The annotation access */
991   protected IAnnotationAccess fAnnotationAccess = new AnnotationAccess();
992   /** The overview ruler */
993   protected OverviewRuler isOverviewRulerVisible;
994   /** The source viewer decoration support */
995   protected SourceViewerDecorationSupport fSourceViewerDecorationSupport;
996   /** The overview ruler */
997   protected OverviewRuler fOverviewRuler;
998
999   /** The preference property change listener for java core. */
1000   private org.eclipse.core.runtime.Preferences.IPropertyChangeListener fPropertyChangeListener = new PropertyChangeListener();
1001
1002   /**
1003    * Default constructor.
1004    */
1005   public PHPEditor() {
1006     super();
1007     JavaTextTools textTools = PHPeclipsePlugin.getDefault().getJavaTextTools();
1008     setSourceViewerConfiguration(new PHPSourceViewerConfiguration(textTools, this));
1009     setRangeIndicator(new DefaultRangeIndicator());
1010     setPreferenceStore(PHPeclipsePlugin.getDefault().getPreferenceStore());
1011
1012     // don't activate this scope without synchronizing plugin.xml !!!
1013     //    setKeyBindingScopes(new String[] { "net.sourceforge.phpdt.ui.phpEditorScope" }); //$NON-NLS-1$
1014
1015     //    if (PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SYNC_OUTLINE_ON_CURSOR_MOVE))
1016     //      fUpdater= new OutlinePageSelectionUpdater();
1017   }
1018   //
1019   //    /**
1020   //     * @see IMember#getCompilationUnit()
1021   //     */
1022   //    public ICompilationUnit getCompilationUnit() {
1023   //            return this; 
1024   //    }
1025   //    /**
1026   //     * @see org.phpeclipse.phpdt.internal.compiler.env.ICompilationUnit#getContents()
1027   //     */
1028   //    public char[] getContents() {
1029   //            IDocument doc = this.getDocumentProvider().getDocument(this.getEditorInput());
1030   //    
1031   //            return doc.get().toCharArray();
1032   //    }
1033   /*
1034    * @see org.eclipse.ui.texteditor.AbstractTextEditor#updatePropertyDependentActions()
1035    */
1036   protected void updatePropertyDependentActions() {
1037     super.updatePropertyDependentActions();
1038     if (fEncodingSupport != null)
1039       fEncodingSupport.reset();
1040   }
1041   
1042   /*
1043    * Update the hovering behavior depending on the preferences.
1044    */
1045   private void updateHoverBehavior() {
1046     SourceViewerConfiguration configuration = getSourceViewerConfiguration();
1047     String[] types = configuration.getConfiguredContentTypes(getSourceViewer());
1048
1049     for (int i = 0; i < types.length; i++) {
1050
1051       String t = types[i];
1052
1053       int[] stateMasks = configuration.getConfiguredTextHoverStateMasks(getSourceViewer(), t);
1054
1055       ISourceViewer sourceViewer = getSourceViewer();
1056       if (sourceViewer instanceof ITextViewerExtension2) {
1057         if (stateMasks != null) {
1058           for (int j = 0; j < stateMasks.length; j++) {
1059             int stateMask = stateMasks[j];
1060             ITextHover textHover = configuration.getTextHover(sourceViewer, t, stateMask);
1061             ((ITextViewerExtension2) sourceViewer).setTextHover(textHover, t, stateMask);
1062           }
1063         } else {
1064           ITextHover textHover = configuration.getTextHover(sourceViewer, t);
1065           ((ITextViewerExtension2) sourceViewer).setTextHover(textHover, t, ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK);
1066         }
1067       } else
1068         sourceViewer.setTextHover(configuration.getTextHover(sourceViewer, t), t);
1069     }
1070   }
1071
1072   /*
1073    * @see net.sourceforge.phpdt.internal.ui.viewsupport.IViewPartInputProvider#getViewPartInput()
1074    */
1075   public Object getViewPartInput() {
1076     return getEditorInput().getAdapter(IResource.class);
1077   }
1078
1079   /*
1080    * @see org.eclipse.ui.IWorkbenchPart#createPartControl(org.eclipse.swt.
1081    * widgets.Composite)
1082    */
1083   public void createPartControl(Composite parent) {
1084     super.createPartControl(parent);
1085
1086     fSourceViewerDecorationSupport.install(getPreferenceStore());
1087
1088     Preferences preferences = PHPeclipsePlugin.getDefault().getPluginPreferences();
1089     preferences.addPropertyChangeListener(fPropertyChangeListener);
1090
1091     IInformationControlCreator informationControlCreator = new IInformationControlCreator() {
1092       public IInformationControl createInformationControl(Shell parent) {
1093         boolean cutDown = false;
1094         int style = cutDown ? SWT.NONE : (SWT.V_SCROLL | SWT.H_SCROLL);
1095         return new DefaultInformationControl(parent, SWT.RESIZE, style, new HTMLTextPresenter(cutDown));
1096       }
1097     };
1098
1099     fInformationPresenter = new InformationPresenter(informationControlCreator);
1100     fInformationPresenter.setSizeConstraints(60, 10, true, true);
1101     fInformationPresenter.install(getSourceViewer());
1102   }
1103
1104   /**
1105    * Returns this document's complete text.
1106    *
1107    * @return the document's complete text
1108    */
1109   public String get() {
1110     IDocument doc = this.getDocumentProvider().getDocument(this.getEditorInput());
1111     return doc.get();
1112   }
1113
1114   /**
1115    *  Returns the standard action group of this editor.
1116    */
1117   protected ActionGroup getActionGroup() {
1118     return fActionGroups;
1119   }
1120
1121   public AbstractContentOutlinePage getfOutlinePage() {
1122     return fOutlinePage;
1123   }
1124
1125   /** The <code>PHPEditor</code> implementation of this 
1126    * <code>AbstractTextEditor</code> method extend the 
1127    * actions to add those specific to the receiver
1128    */
1129   protected void createActions() {
1130     super.createActions();
1131
1132     ResourceAction resAction = new AddTaskAction(PHPEditorMessages.getResourceBundle(), "AddTask.", this); //$NON-NLS-1$
1133     resAction.setHelpContextId(IAbstractTextEditorHelpContextIds.ADD_TASK_ACTION);
1134     resAction.setActionDefinitionId(ITextEditorActionDefinitionIds.ADD_TASK);
1135     setAction(ITextEditorActionConstants.ADD_TASK, resAction);
1136
1137     resAction = new TextOperationAction(PHPEditorMessages.getResourceBundle(), "ShowJavaDoc.", this, ISourceViewer.INFORMATION, true); //$NON-NLS-1$
1138     resAction = new InformationDispatchAction(PHPEditorMessages.getResourceBundle(), "ShowJavaDoc.", (TextOperationAction) resAction); //$NON-NLS-1$
1139     resAction.setActionDefinitionId(PHPEditorActionDefinitionIds.SHOW_JAVADOC);
1140     setAction("ShowJavaDoc", resAction); //$NON-NLS-1$
1141     //                                          WorkbenchHelp.setHelp(resAction, IJavaHelpContextIds.SHOW_JAVADOC_ACTION);
1142
1143     Action action;
1144
1145     setAction(
1146       "ContentAssistTip",
1147       new TextOperationAction(
1148         PHPEditorMessages.getResourceBundle(),
1149         "ContentAssistTip.",
1150         this,
1151         ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION));
1152
1153     action = new ContentAssistAction(PHPEditorMessages.getResourceBundle(), "ContentAssistProposal.", this); //$NON-NLS-1$
1154     action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
1155     setAction("ContentAssistProposal", action); //$NON-NLS-1$
1156     markAsStateDependentAction("ContentAssistProposal", true); //$NON-NLS-1$
1157     //  WorkbenchHelp.setHelp(action, IJavaHelpContextIds.CONTENT_ASSIST_ACTION);
1158
1159     fEncodingSupport = new DefaultEncodingSupport();
1160     fEncodingSupport.initialize(this);
1161
1162     action = new TextOperationAction(PHPEditorMessages.getResourceBundle(), "Comment.", this, ITextOperationTarget.PREFIX); //$NON-NLS-1$
1163     action.setActionDefinitionId(PHPEditorActionDefinitionIds.COMMENT);
1164     setAction("Comment", action); //$NON-NLS-1$
1165     markAsStateDependentAction("Comment", true); //$NON-NLS-1$
1166     //          WorkbenchHelp.setHelp(action, IJavaHelpContextIds.COMMENT_ACTION);
1167
1168     action = new TextOperationAction(PHPEditorMessages.getResourceBundle(), "Uncomment.", this, ITextOperationTarget.STRIP_PREFIX); //$NON-NLS-1$
1169     action.setActionDefinitionId(PHPEditorActionDefinitionIds.UNCOMMENT);
1170     setAction("Uncomment", action); //$NON-NLS-1$
1171     markAsStateDependentAction("Uncomment", true); //$NON-NLS-1$
1172     //          WorkbenchHelp.setHelp(action, IJavaHelpContextIds.UNCOMMENT_ACTION);
1173
1174     action = new TextOperationAction(PHPEditorMessages.getResourceBundle(), "Format.", this, ISourceViewer.FORMAT); //$NON-NLS-1$
1175     action.setActionDefinitionId(PHPEditorActionDefinitionIds.FORMAT);
1176     setAction("Format", action); //$NON-NLS-1$
1177     markAsStateDependentAction("Format", true); //$NON-NLS-1$
1178     markAsSelectionDependentAction("Format", true); //$NON-NLS-1$               
1179     //  WorkbenchHelp.setHelp(action, IJavaHelpContextIds.FORMAT_ACTION);
1180
1181     action = new GotoMatchingBracketAction(this);
1182     action.setActionDefinitionId(PHPEditorActionDefinitionIds.GOTO_MATCHING_BRACKET);
1183     setAction(GotoMatchingBracketAction.GOTO_MATCHING_BRACKET, action);
1184
1185     fGenerateActionGroup = new GenerateActionGroup(this, ITextEditorActionConstants.GROUP_EDIT);
1186
1187     fActionGroups = new CompositeActionGroup(new ActionGroup[] { fGenerateActionGroup });
1188
1189     // We have to keep the context menu group separate to have better control over positioning
1190     fContextMenuGroup = new CompositeActionGroup(new ActionGroup[] { fGenerateActionGroup });
1191     //      rg, 
1192     //      new LocalHistoryActionGroup(this, ITextEditorActionConstants.GROUP_EDIT)});
1193
1194     //    if (fValidationThread == null) {
1195     //      fValidationThread =
1196     //        new PHPSyntaxParserThread(this, getSourceViewer());
1197     //      //Thread defaults
1198     //
1199     //      fValidationThread.start();
1200     //    }
1201     //
1202     //    fValidationThread.setText(getSourceViewer().getTextWidget().getText());
1203   }
1204
1205   /** The <code>PHPEditor</code> implementation of this 
1206    * <code>AbstractTextEditor</code> method performs any extra 
1207    * disposal actions required by the php editor.
1208    */
1209   public void dispose() {
1210     //   PHPEditorEnvironment.disconnect(this);
1211     if (fOutlinePage != null)
1212       fOutlinePage.setInput(null);
1213
1214     if (fActionGroups != null)
1215       fActionGroups.dispose();
1216
1217     if (isBrowserLikeLinks())
1218       disableBrowserLikeLinks();
1219
1220     if (fEncodingSupport != null) {
1221       fEncodingSupport.dispose();
1222       fEncodingSupport = null;
1223     }
1224
1225     if (fPropertyChangeListener != null) {
1226       Preferences preferences = PHPeclipsePlugin.getDefault().getPluginPreferences();
1227       preferences.removePropertyChangeListener(fPropertyChangeListener);
1228       fPropertyChangeListener = null;
1229     }
1230
1231     if (fSourceViewerDecorationSupport != null) {
1232       fSourceViewerDecorationSupport.dispose();
1233       fSourceViewerDecorationSupport = null;
1234     }
1235
1236     if (fBracketMatcher != null) {
1237       fBracketMatcher.dispose();
1238       fBracketMatcher = null;
1239     }
1240     super.dispose();
1241   }
1242
1243   /** The <code>PHPEditor</code> implementation of this 
1244    * <code>AbstractTextEditor</code> method performs any extra 
1245    * revert behavior required by the php editor.
1246    */
1247   public void doRevertToSaved() {
1248     super.doRevertToSaved();
1249     if (fOutlinePage != null)
1250       fOutlinePage.update();
1251   }
1252
1253   /** The <code>PHPEditor</code> implementation of this 
1254    * <code>AbstractTextEditor</code> method performs any extra 
1255    * save behavior required by the php editor.
1256    */
1257   public void doSave(IProgressMonitor monitor) {
1258     super.doSave(monitor);
1259     // compile or not, according to the user preferences
1260     // IPreferenceStore store = getPreferenceStore(); 
1261
1262     // the parse on save was changed to the eclipse "builders" concept
1263     //    if (store.getBoolean(PHPeclipsePlugin.PHP_PARSE_ON_SAVE)) {
1264     //      IAction a = PHPParserAction.getInstance();
1265     //      if (a != null)
1266     //        a.run();
1267     //    }
1268
1269     //    if (SWT.getPlatform().equals("win32")) {
1270     //      IAction a = ShowExternalPreviewAction.getInstance();
1271     //      if (a != null)
1272     //        a.run();
1273     //    }
1274     if (fOutlinePage != null)
1275       fOutlinePage.update();
1276   }
1277
1278   /** The <code>PHPEditor</code> implementation of this 
1279    * <code>AbstractTextEditor</code> method performs any extra 
1280    * save as behavior required by the php editor.
1281    */
1282   public void doSaveAs() {
1283     super.doSaveAs();
1284     if (fOutlinePage != null)
1285       fOutlinePage.update();
1286   }
1287   /*
1288          * @see StatusTextEditor#getStatusHeader(IStatus)
1289          */
1290   protected String getStatusHeader(IStatus status) {
1291     if (fEncodingSupport != null) {
1292       String message = fEncodingSupport.getStatusHeader(status);
1293       if (message != null)
1294         return message;
1295     }
1296     return super.getStatusHeader(status);
1297   }
1298
1299   /*
1300    * @see StatusTextEditor#getStatusBanner(IStatus)
1301    */
1302   protected String getStatusBanner(IStatus status) {
1303     if (fEncodingSupport != null) {
1304       String message = fEncodingSupport.getStatusBanner(status);
1305       if (message != null)
1306         return message;
1307     }
1308     return super.getStatusBanner(status);
1309   }
1310
1311   /*
1312    * @see StatusTextEditor#getStatusMessage(IStatus)
1313    */
1314   protected String getStatusMessage(IStatus status) {
1315     if (fEncodingSupport != null) {
1316       String message = fEncodingSupport.getStatusMessage(status);
1317       if (message != null)
1318         return message;
1319     }
1320     return super.getStatusMessage(status);
1321   }
1322   /** The <code>PHPEditor</code> implementation of this 
1323    * <code>AbstractTextEditor</code> method performs sets the 
1324    * input of the outline page after AbstractTextEditor has set input.
1325    */
1326   protected void doSetInput(IEditorInput input) throws CoreException {
1327     super.doSetInput(input);
1328
1329     if (fEncodingSupport != null)
1330       fEncodingSupport.reset();
1331     if (fOutlinePage != null)
1332       fOutlinePage.setInput(input);
1333     //          setOutlinePageInput(fOutlinePage, input);
1334   }
1335
1336   /*
1337    * @see org.phpeclipse.phpdt.internal.ui.viewsupport.IViewPartInputProvider#getViewPartInput()
1338    */
1339   //  public Object getViewPartInput() {
1340   //    return getEditorInput().getAdapter(IFile.class);
1341   //  }
1342
1343   /** The <code>PHPEditor</code> implementation of this 
1344    * <code>AbstractTextEditor</code> method adds any 
1345    * PHPEditor specific entries.
1346    */
1347   public void editorContextMenuAboutToShow(MenuManager menu) {
1348     super.editorContextMenuAboutToShow(menu);
1349     menu.appendToGroup(ITextEditorActionConstants.GROUP_UNDO, new Separator(IContextMenuConstants.GROUP_OPEN));
1350     menu.insertAfter(IContextMenuConstants.GROUP_OPEN, new GroupMarker(IContextMenuConstants.GROUP_SHOW));
1351
1352     ActionContext context = new ActionContext(getSelectionProvider().getSelection());
1353     fContextMenuGroup.setContext(context);
1354     fContextMenuGroup.fillContextMenu(menu);
1355     fContextMenuGroup.setContext(null);
1356     //    addAction(menu, ITextEditorActionConstants.GROUP_EDIT, "Format"); //$NON-NLS-1$
1357     //
1358     //    ActionContext context =
1359     //      new ActionContext(getSelectionProvider().getSelection());
1360     //    fContextMenuGroup.setContext(context);
1361     //    fContextMenuGroup.fillContextMenu(menu);
1362     //    fContextMenuGroup.setContext(null);
1363   }
1364
1365   /**
1366    * Creates the outline page used with this editor.
1367    */
1368   protected AbstractContentOutlinePage createOutlinePage() {
1369
1370     AbstractContentOutlinePage page = new PHPContentOutlinePage(getDocumentProvider(), this);
1371
1372     page.addSelectionChangedListener(fSelectionChangedListener);
1373     //    setOutlinePageInput(page, getEditorInput());
1374     if (getEditorInput() != null)
1375       fOutlinePage.setInput(getEditorInput());
1376
1377     return page;
1378   }
1379
1380   /**
1381    * Informs the editor that its outliner has been closed.
1382    */
1383   public void outlinePageClosed() {
1384     if (fOutlinePage != null) {
1385       fOutlinePage.removeSelectionChangedListener(fSelectionChangedListener);
1386       fOutlinePage = null;
1387       resetHighlightRange();
1388     }
1389   }
1390   protected void updateStateDependentActions() {
1391     super.updateStateDependentActions();
1392     fGenerateActionGroup.editorStateChanged();
1393   }
1394
1395   /** The <code>PHPEditor</code> implementation of this 
1396    * <code>AbstractTextEditor</code> method performs gets
1397    * the java content outline page if request is for a an 
1398    * outline page.
1399    */
1400   public Object getAdapter(Class required) {
1401     if (IContentOutlinePage.class.equals(required)) {
1402       if (fOutlinePage == null) {
1403         fOutlinePage = new PHPContentOutlinePage(getDocumentProvider(), this);
1404         if (getEditorInput() != null)
1405           fOutlinePage.setInput(getEditorInput());
1406       }
1407       return fOutlinePage;
1408     }
1409
1410     if (IEncodingSupport.class.equals(required))
1411       return fEncodingSupport;
1412
1413     return super.getAdapter(required);
1414   }
1415
1416   protected void doSelectionChanged(SelectionChangedEvent event) {
1417
1418     //                  ISourceReference reference= null;
1419     //          
1420     //                  ISelection selection= event.getSelection();
1421     //                  Iterator iter= ((IStructuredSelection) selection).iterator();
1422     //                  while (iter.hasNext()) {
1423     //                          Object o= iter.next();
1424     //                          if (o instanceof ISourceReference) {
1425     //                                  reference= (ISourceReference) o;
1426     //                                  break;
1427     //                          }
1428     //                  }
1429     if (!isActivePart() && PHPeclipsePlugin.getActivePage() != null)
1430       PHPeclipsePlugin.getActivePage().bringToTop(this);
1431
1432     //                  try {
1433     //                          editingScriptStarted();
1434     //                          setSelection(reference, !isActivePart());
1435     //                  } finally {
1436     //                          editingScriptEnded();
1437     //                  }
1438   }
1439
1440   protected boolean isActivePart() {
1441     IWorkbenchWindow window = getSite().getWorkbenchWindow();
1442     IPartService service = window.getPartService();
1443     IWorkbenchPart part = service.getActivePart();
1444     return part != null && part.equals(this);
1445   }
1446
1447                 
1448   //  public void openContextHelp() {
1449   //    IDocument doc = this.getDocumentProvider().getDocument(this.getEditorInput());
1450   //    ITextSelection selection = (ITextSelection) this.getSelectionProvider().getSelection();
1451   //    int pos = selection.getOffset();
1452   //    String word = getFunctionName(doc, pos);
1453   //    openContextHelp(word);
1454   //  }
1455   //
1456   //  private void openContextHelp(String word) {
1457   //    open(word);
1458   //  }
1459   //
1460   //  public static void open(String word) {
1461   //    IHelp help = WorkbenchHelp.getHelpSupport();
1462   //    if (help != null) {
1463   //      IHelpResource helpResource = new PHPFunctionHelpResource(word);
1464   //      WorkbenchHelp.getHelpSupport().displayHelpResource(helpResource);
1465   //    } else {
1466   //      //   showMessage(shell, dialogTitle, ActionMessages.getString("Open help not available"), false); //$NON-NLS-1$
1467   //    }
1468   //  }
1469
1470   //    private String getFunctionName(IDocument doc, int pos) {
1471   //            Point word = PHPWordExtractor.findWord(doc, pos);
1472   //            if (word != null) {
1473   //                    try {
1474   //                            return doc.get(word.x, word.y).replace('_', '-');
1475   //                    } catch (BadLocationException e) {
1476   //                    }
1477   //            }
1478   //            return "";
1479   //    }
1480
1481   /*
1482    * @see AbstractTextEditor#handlePreferenceStoreChanged(PropertyChangeEvent)
1483    */
1484   protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
1485
1486     try {
1487
1488       ISourceViewer sourceViewer = getSourceViewer();
1489       if (sourceViewer == null)
1490         return;
1491
1492       String property = event.getProperty();
1493
1494       if (PreferenceConstants.EDITOR_TAB_WIDTH.equals(property)) {
1495         Object value = event.getNewValue();
1496         if (value instanceof Integer) {
1497           sourceViewer.getTextWidget().setTabs(((Integer) value).intValue());
1498         } else if (value instanceof String) {
1499           sourceViewer.getTextWidget().setTabs(Integer.parseInt((String) value));
1500         }
1501         return;
1502       }
1503
1504       if (OVERVIEW_RULER.equals(property)) {
1505         if (isOverviewRulerVisible())
1506           showOverviewRuler();
1507         else
1508           hideOverviewRuler();
1509         return;
1510       }
1511
1512       if (LINE_NUMBER_RULER.equals(property)) {
1513         if (isLineNumberRulerVisible())
1514           showLineNumberRuler();
1515         else
1516           hideLineNumberRuler();
1517         return;
1518       }
1519
1520       if (fLineNumberRulerColumn != null
1521         && (LINE_NUMBER_COLOR.equals(property)
1522           || PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT.equals(property)
1523           || PREFERENCE_COLOR_BACKGROUND.equals(property))) {
1524
1525         initializeLineNumberRulerColumn(fLineNumberRulerColumn);
1526       }
1527
1528       if (isJavaEditorHoverProperty(property)) {
1529         updateHoverBehavior();
1530       }
1531
1532     } finally {
1533       super.handlePreferenceStoreChanged(event);
1534     }
1535   }
1536
1537   //  /*
1538   //     * @see AbstractTextEditor#handlePreferenceStoreChanged(PropertyChangeEvent)
1539   //     */
1540   //  protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
1541   //
1542   //    try {
1543   //
1544   //      ISourceViewer sourceViewer = getSourceViewer();
1545   //      if (sourceViewer == null)
1546   //        return;
1547   //
1548   //      String property = event.getProperty();
1549   //
1550   //      //      if (JavaSourceViewerConfiguration.PREFERENCE_TAB_WIDTH.equals(property)) {
1551   //      //        Object value= event.getNewValue();
1552   //      //        if (value instanceof Integer) {
1553   //      //          sourceViewer.getTextWidget().setTabs(((Integer) value).intValue());
1554   //      //        } else if (value instanceof String) {
1555   //      //          sourceViewer.getTextWidget().setTabs(Integer.parseInt((String) value));
1556   //      //        }
1557   //      //        return;
1558   //      //      }
1559   //
1560   //      if (IPreferenceConstants.LINE_NUMBER_RULER.equals(property)) {
1561   //        if (isLineNumberRulerVisible())
1562   //          showLineNumberRuler();
1563   //        else
1564   //          hideLineNumberRuler();
1565   //        return;
1566   //      }
1567   //
1568   //      if (fLineNumberRulerColumn != null
1569   //        && (IPreferenceConstants.LINE_NUMBER_COLOR.equals(property)
1570   //          || PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT.equals(property)
1571   //          || PREFERENCE_COLOR_BACKGROUND.equals(property))) {
1572   //
1573   //        initializeLineNumberRulerColumn(fLineNumberRulerColumn);
1574   //      }
1575   //
1576   //    } finally {
1577   //      super.handlePreferenceStoreChanged(event);
1578   //    }
1579   //  }
1580
1581   private boolean isJavaEditorHoverProperty(String property) {
1582     return PreferenceConstants.EDITOR_DEFAULT_HOVER.equals(property)
1583       || PreferenceConstants.EDITOR_NONE_HOVER.equals(property)
1584       || PreferenceConstants.EDITOR_CTRL_HOVER.equals(property)
1585       || PreferenceConstants.EDITOR_SHIFT_HOVER.equals(property)
1586       || PreferenceConstants.EDITOR_CTRL_ALT_HOVER.equals(property)
1587       || PreferenceConstants.EDITOR_CTRL_SHIFT_HOVER.equals(property)
1588       || PreferenceConstants.EDITOR_CTRL_ALT_SHIFT_HOVER.equals(property)
1589       || PreferenceConstants.EDITOR_ALT_SHIFT_HOVER.equals(property);
1590   }
1591
1592   /**
1593    * Shows the line number ruler column.
1594    */
1595   private void showLineNumberRuler() {
1596     IVerticalRuler v = getVerticalRuler();
1597     if (v instanceof CompositeRuler) {
1598       CompositeRuler c = (CompositeRuler) v;
1599       c.addDecorator(1, createLineNumberRulerColumn());
1600     }
1601   }
1602   /**
1603          * Return whether the browser like links should be enabled
1604          * according to the preference store settings.
1605          * @return <code>true</code> if the browser like links should be enabled
1606          */
1607   private boolean isBrowserLikeLinks() {
1608     IPreferenceStore store = getPreferenceStore();
1609     return store.getBoolean(BROWSER_LIKE_LINKS);
1610   }
1611
1612   /**
1613    * Enables browser like links.
1614    */
1615   private void enableBrowserLikeLinks() {
1616     if (fMouseListener == null) {
1617       fMouseListener = new MouseClickListener();
1618       fMouseListener.install();
1619     }
1620   }
1621
1622   /**
1623    * Disables browser like links.
1624    */
1625   private void disableBrowserLikeLinks() {
1626     if (fMouseListener != null) {
1627       fMouseListener.uninstall();
1628       fMouseListener = null;
1629     }
1630   }
1631   /**
1632    * Handles a property change event describing a change
1633    * of the java core's preferences and updates the preference
1634    * related editor properties.
1635    * 
1636    * @param event the property change event
1637    */
1638   protected void handlePreferencePropertyChanged(org.eclipse.core.runtime.Preferences.PropertyChangeEvent event) {
1639     //          if (COMPILER_TASK_TAGS.equals(event.getProperty())) {
1640     //                  ISourceViewer sourceViewer= getSourceViewer();
1641     //                  if (sourceViewer != null && affectsTextPresentation(new PropertyChangeEvent(event.getSource(), event.getProperty(), event.getOldValue(), event.getNewValue())))
1642     //                          sourceViewer.invalidateTextPresentation();
1643     //          }
1644   }
1645
1646   /**
1647    * Return whether the line number ruler column should be 
1648    * visible according to the preference store settings.
1649    * @return <code>true</code> if the line numbers should be visible
1650    */
1651   private boolean isLineNumberRulerVisible() {
1652     IPreferenceStore store = getPreferenceStore();
1653     return store.getBoolean(LINE_NUMBER_RULER);
1654   }
1655   /**
1656    * Hides the line number ruler column.
1657    */
1658   private void hideLineNumberRuler() {
1659     IVerticalRuler v = getVerticalRuler();
1660     if (v instanceof CompositeRuler) {
1661       CompositeRuler c = (CompositeRuler) v;
1662       try {
1663         c.removeDecorator(1);
1664       } catch (Throwable e) {
1665       }
1666     }
1667   }
1668
1669   /**
1670    * Initializes the given line number ruler column from the preference store.
1671    * @param rulerColumn the ruler column to be initialized
1672    */
1673   protected void initializeLineNumberRulerColumn(LineNumberRulerColumn rulerColumn) {
1674     JavaTextTools textTools = PHPeclipsePlugin.getDefault().getJavaTextTools();
1675     IColorManager manager = textTools.getColorManager();
1676
1677     IPreferenceStore store = getPreferenceStore();
1678     if (store != null) {
1679
1680       RGB rgb = null;
1681       // foreground color
1682       if (store.contains(LINE_NUMBER_COLOR)) {
1683         if (store.isDefault(LINE_NUMBER_COLOR))
1684           rgb = PreferenceConverter.getDefaultColor(store, LINE_NUMBER_COLOR);
1685         else
1686           rgb = PreferenceConverter.getColor(store, LINE_NUMBER_COLOR);
1687       }
1688       rulerColumn.setForeground(manager.getColor(rgb));
1689
1690       rgb = null;
1691       // background color
1692       if (!store.getBoolean(PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT)) {
1693         if (store.contains(PREFERENCE_COLOR_BACKGROUND)) {
1694           if (store.isDefault(PREFERENCE_COLOR_BACKGROUND))
1695             rgb = PreferenceConverter.getDefaultColor(store, PREFERENCE_COLOR_BACKGROUND);
1696           else
1697             rgb = PreferenceConverter.getColor(store, PREFERENCE_COLOR_BACKGROUND);
1698         }
1699       }
1700       rulerColumn.setBackground(manager.getColor(rgb));
1701     }
1702   }
1703
1704   /**
1705    * Creates a new line number ruler column that is appropriately initialized.
1706    */
1707   protected IVerticalRulerColumn createLineNumberRulerColumn() {
1708     fLineNumberRulerColumn = new LineNumberRulerColumn();
1709     initializeLineNumberRulerColumn(fLineNumberRulerColumn);
1710     return fLineNumberRulerColumn;
1711   }
1712
1713   /*
1714    * @see AbstractTextEditor#createVerticalRuler()
1715    */
1716   protected IVerticalRuler createVerticalRuler() {
1717     CompositeRuler ruler = new CompositeRuler();
1718     ruler.addDecorator(0, new AnnotationRulerColumn(VERTICAL_RULER_WIDTH));
1719     if (isLineNumberRulerVisible())
1720       ruler.addDecorator(1, createLineNumberRulerColumn());
1721     return ruler;
1722   }
1723
1724   private static IRegion getSignedSelection(ITextViewer viewer) {
1725
1726     StyledText text = viewer.getTextWidget();
1727     int caretOffset = text.getCaretOffset();
1728     Point selection = text.getSelection();
1729
1730     // caret left
1731     int offset, length;
1732     if (caretOffset == selection.x) {
1733       offset = selection.y;
1734       length = selection.x - selection.y;
1735
1736       // caret right
1737     } else {
1738       offset = selection.x;
1739       length = selection.y - selection.x;
1740     }
1741
1742     return new Region(offset, length);
1743   }
1744
1745   /** Preference key for matching brackets */
1746   protected final static String MATCHING_BRACKETS = PreferenceConstants.EDITOR_MATCHING_BRACKETS;
1747   /** Preference key for matching brackets color */
1748   protected final static String MATCHING_BRACKETS_COLOR = PreferenceConstants.EDITOR_MATCHING_BRACKETS_COLOR;
1749   /** Preference key for highlighting current line */
1750   protected final static String CURRENT_LINE = PreferenceConstants.EDITOR_CURRENT_LINE;
1751   /** Preference key for highlight color of current line */
1752   protected final static String CURRENT_LINE_COLOR = PreferenceConstants.EDITOR_CURRENT_LINE_COLOR;
1753   /** Preference key for showing print marging ruler */
1754   protected final static String PRINT_MARGIN = PreferenceConstants.EDITOR_PRINT_MARGIN;
1755   /** Preference key for print margin ruler color */
1756   protected final static String PRINT_MARGIN_COLOR = PreferenceConstants.EDITOR_PRINT_MARGIN_COLOR;
1757   /** Preference key for print margin ruler column */
1758   protected final static String PRINT_MARGIN_COLUMN = PreferenceConstants.EDITOR_PRINT_MARGIN_COLUMN;
1759   /** Preference key for error indication */
1760   protected final static String ERROR_INDICATION = PreferenceConstants.EDITOR_PROBLEM_INDICATION;
1761   /** Preference key for error color */
1762   protected final static String ERROR_INDICATION_COLOR = PreferenceConstants.EDITOR_PROBLEM_INDICATION_COLOR;
1763   /** Preference key for warning indication */
1764   protected final static String WARNING_INDICATION = PreferenceConstants.EDITOR_WARNING_INDICATION;
1765   /** Preference key for warning color */
1766   protected final static String WARNING_INDICATION_COLOR = PreferenceConstants.EDITOR_WARNING_INDICATION_COLOR;
1767   /** Preference key for task indication */
1768   protected final static String TASK_INDICATION = PreferenceConstants.EDITOR_TASK_INDICATION;
1769   /** Preference key for task color */
1770   protected final static String TASK_INDICATION_COLOR = PreferenceConstants.EDITOR_TASK_INDICATION_COLOR;
1771   /** Preference key for bookmark indication */
1772   protected final static String BOOKMARK_INDICATION = PreferenceConstants.EDITOR_BOOKMARK_INDICATION;
1773   /** Preference key for bookmark color */
1774   protected final static String BOOKMARK_INDICATION_COLOR = PreferenceConstants.EDITOR_BOOKMARK_INDICATION_COLOR;
1775   /** Preference key for search result indication */
1776   protected final static String SEARCH_RESULT_INDICATION = PreferenceConstants.EDITOR_SEARCH_RESULT_INDICATION;
1777   /** Preference key for search result color */
1778   protected final static String SEARCH_RESULT_INDICATION_COLOR = PreferenceConstants.EDITOR_SEARCH_RESULT_INDICATION_COLOR;
1779   /** Preference key for unknown annotation indication */
1780   protected final static String UNKNOWN_INDICATION = PreferenceConstants.EDITOR_UNKNOWN_INDICATION;
1781   /** Preference key for unknown annotation color */
1782   protected final static String UNKNOWN_INDICATION_COLOR = PreferenceConstants.EDITOR_UNKNOWN_INDICATION_COLOR;
1783   /** Preference key for shwoing the overview ruler */
1784   protected final static String OVERVIEW_RULER = PreferenceConstants.EDITOR_OVERVIEW_RULER;
1785   /** Preference key for error indication in overview ruler */
1786   protected final static String ERROR_INDICATION_IN_OVERVIEW_RULER = PreferenceConstants.EDITOR_ERROR_INDICATION_IN_OVERVIEW_RULER;
1787   /** Preference key for warning indication in overview ruler */
1788   protected final static String WARNING_INDICATION_IN_OVERVIEW_RULER =
1789     PreferenceConstants.EDITOR_WARNING_INDICATION_IN_OVERVIEW_RULER;
1790   /** Preference key for task indication in overview ruler */
1791   protected final static String TASK_INDICATION_IN_OVERVIEW_RULER = PreferenceConstants.EDITOR_TASK_INDICATION_IN_OVERVIEW_RULER;
1792   /** Preference key for bookmark indication in overview ruler */
1793   protected final static String BOOKMARK_INDICATION_IN_OVERVIEW_RULER =
1794     PreferenceConstants.EDITOR_BOOKMARK_INDICATION_IN_OVERVIEW_RULER;
1795   /** Preference key for search result indication in overview ruler */
1796   protected final static String SEARCH_RESULT_INDICATION_IN_OVERVIEW_RULER =
1797     PreferenceConstants.EDITOR_SEARCH_RESULT_INDICATION_IN_OVERVIEW_RULER;
1798   /** Preference key for unknown annotation indication in overview ruler */
1799   protected final static String UNKNOWN_INDICATION_IN_OVERVIEW_RULER =
1800     PreferenceConstants.EDITOR_UNKNOWN_INDICATION_IN_OVERVIEW_RULER;
1801   //            /** Preference key for compiler task tags */
1802   //            private final static String COMPILER_TASK_TAGS= JavaCore.COMPILER_TASK_TAGS;
1803   /** Preference key for browser like links */
1804   private final static String BROWSER_LIKE_LINKS = PreferenceConstants.EDITOR_BROWSER_LIKE_LINKS;
1805   /** Preference key for key modifier of browser like links */
1806   private final static String BROWSER_LIKE_LINKS_KEY_MODIFIER = PreferenceConstants.EDITOR_BROWSER_LIKE_LINKS_KEY_MODIFIER;
1807   /**
1808    * Preference key for key modifier mask of browser like links.
1809    * The value is only used if the value of <code>EDITOR_BROWSER_LIKE_LINKS</code>
1810    * cannot be resolved to valid SWT modifier bits.
1811    * 
1812    * @since 2.1.1
1813    */
1814   private final static String BROWSER_LIKE_LINKS_KEY_MODIFIER_MASK =
1815     PreferenceConstants.EDITOR_BROWSER_LIKE_LINKS_KEY_MODIFIER_MASK;
1816
1817   private final static char[] BRACKETS = { '{', '}', '(', ')', '[', ']' };
1818
1819   private static boolean isBracket(char character) {
1820     for (int i = 0; i != BRACKETS.length; ++i)
1821       if (character == BRACKETS[i])
1822         return true;
1823     return false;
1824   }
1825
1826   private static boolean isSurroundedByBrackets(IDocument document, int offset) {
1827     if (offset == 0 || offset == document.getLength())
1828       return false;
1829
1830     try {
1831       return isBracket(document.getChar(offset - 1)) && isBracket(document.getChar(offset));
1832
1833     } catch (BadLocationException e) {
1834       return false;
1835     }
1836   }
1837
1838   protected void configureSourceViewerDecorationSupport() {
1839
1840     fSourceViewerDecorationSupport.setCharacterPairMatcher(fBracketMatcher);
1841
1842     fSourceViewerDecorationSupport.setAnnotationPainterPreferenceKeys(
1843       AnnotationType.UNKNOWN,
1844       UNKNOWN_INDICATION_COLOR,
1845       UNKNOWN_INDICATION,
1846       UNKNOWN_INDICATION_IN_OVERVIEW_RULER,
1847       0);
1848     fSourceViewerDecorationSupport.setAnnotationPainterPreferenceKeys(
1849       AnnotationType.BOOKMARK,
1850       BOOKMARK_INDICATION_COLOR,
1851       BOOKMARK_INDICATION,
1852       BOOKMARK_INDICATION_IN_OVERVIEW_RULER,
1853       1);
1854     fSourceViewerDecorationSupport.setAnnotationPainterPreferenceKeys(
1855       AnnotationType.TASK,
1856       TASK_INDICATION_COLOR,
1857       TASK_INDICATION,
1858       TASK_INDICATION_IN_OVERVIEW_RULER,
1859       2);
1860     fSourceViewerDecorationSupport.setAnnotationPainterPreferenceKeys(
1861       AnnotationType.SEARCH,
1862       SEARCH_RESULT_INDICATION_COLOR,
1863       SEARCH_RESULT_INDICATION,
1864       SEARCH_RESULT_INDICATION_IN_OVERVIEW_RULER,
1865       3);
1866     fSourceViewerDecorationSupport.setAnnotationPainterPreferenceKeys(
1867       AnnotationType.WARNING,
1868       WARNING_INDICATION_COLOR,
1869       WARNING_INDICATION,
1870       WARNING_INDICATION_IN_OVERVIEW_RULER,
1871       4);
1872     fSourceViewerDecorationSupport.setAnnotationPainterPreferenceKeys(
1873       AnnotationType.ERROR,
1874       ERROR_INDICATION_COLOR,
1875       ERROR_INDICATION,
1876       ERROR_INDICATION_IN_OVERVIEW_RULER,
1877       5);
1878
1879     fSourceViewerDecorationSupport.setCursorLinePainterPreferenceKeys(CURRENT_LINE, CURRENT_LINE_COLOR);
1880     fSourceViewerDecorationSupport.setMarginPainterPreferenceKeys(PRINT_MARGIN, PRINT_MARGIN_COLOR, PRINT_MARGIN_COLUMN);
1881     fSourceViewerDecorationSupport.setMatchingCharacterPainterPreferenceKeys(MATCHING_BRACKETS, MATCHING_BRACKETS_COLOR);
1882
1883     fSourceViewerDecorationSupport.setSymbolicFontName(getFontPropertyPreferenceKey());
1884   }
1885   /**
1886     * Jumps to the matching bracket.
1887     */
1888   public void gotoMatchingBracket() {
1889
1890     ISourceViewer sourceViewer = getSourceViewer();
1891     IDocument document = sourceViewer.getDocument();
1892     if (document == null)
1893       return;
1894
1895     IRegion selection = getSignedSelection(sourceViewer);
1896
1897     int selectionLength = Math.abs(selection.getLength());
1898     if (selectionLength > 1) {
1899       setStatusLineErrorMessage(PHPEditorMessages.getString("GotoMatchingBracket.error.invalidSelection")); //$NON-NLS-1$               
1900       sourceViewer.getTextWidget().getDisplay().beep();
1901       return;
1902     }
1903
1904     // #26314
1905     int sourceCaretOffset = selection.getOffset() + selection.getLength();
1906     if (isSurroundedByBrackets(document, sourceCaretOffset))
1907       sourceCaretOffset -= selection.getLength();
1908
1909     IRegion region = fBracketMatcher.match(document, sourceCaretOffset);
1910     if (region == null) {
1911       setStatusLineErrorMessage(PHPEditorMessages.getString("GotoMatchingBracket.error.noMatchingBracket")); //$NON-NLS-1$              
1912       sourceViewer.getTextWidget().getDisplay().beep();
1913       return;
1914     }
1915
1916     int offset = region.getOffset();
1917     int length = region.getLength();
1918
1919     if (length < 1)
1920       return;
1921
1922     int anchor = fBracketMatcher.getAnchor();
1923     int targetOffset = (PHPPairMatcher.RIGHT == anchor) ? offset : offset + length - 1;
1924
1925     boolean visible = false;
1926     if (sourceViewer instanceof ITextViewerExtension3) {
1927       ITextViewerExtension3 extension = (ITextViewerExtension3) sourceViewer;
1928       visible = (extension.modelOffset2WidgetOffset(targetOffset) > -1);
1929     } else {
1930       IRegion visibleRegion = sourceViewer.getVisibleRegion();
1931       visible = (targetOffset >= visibleRegion.getOffset() && targetOffset < visibleRegion.getOffset() + visibleRegion.getLength());
1932     }
1933
1934     if (!visible) {
1935       setStatusLineErrorMessage(PHPEditorMessages.getString("GotoMatchingBracket.error.bracketOutsideSelectedElement")); //$NON-NLS-1$          
1936       sourceViewer.getTextWidget().getDisplay().beep();
1937       return;
1938     }
1939
1940     if (selection.getLength() < 0)
1941       targetOffset -= selection.getLength();
1942
1943     sourceViewer.setSelectedRange(targetOffset, selection.getLength());
1944     sourceViewer.revealRange(targetOffset, selection.getLength());
1945   }
1946   /**
1947      * Ses the given message as error message to this editor's status line.
1948      * @param msg message to be set
1949      */
1950   protected void setStatusLineErrorMessage(String msg) {
1951     IEditorStatusLine statusLine = (IEditorStatusLine) getAdapter(IEditorStatusLine.class);
1952     if (statusLine != null)
1953       statusLine.setMessage(true, msg, null);
1954   }
1955
1956   /**
1957      * Returns a segmentation of the line of the given document appropriate for bidi rendering.
1958      * The default implementation returns only the string literals of a php code line as segments.
1959      * 
1960      * @param document the document
1961      * @param lineOffset the offset of the line
1962      * @return the line's bidi segmentation
1963      * @throws BadLocationException in case lineOffset is not valid in document
1964      */
1965   public static int[] getBidiLineSegments(IDocument document, int lineOffset) throws BadLocationException {
1966
1967     IRegion line = document.getLineInformationOfOffset(lineOffset);
1968     ITypedRegion[] linePartitioning = document.computePartitioning(lineOffset, line.getLength());
1969
1970     List segmentation = new ArrayList();
1971     for (int i = 0; i < linePartitioning.length; i++) {
1972       if (IPHPPartitionScannerConstants.PHP_STRING.equals(linePartitioning[i].getType()))
1973         segmentation.add(linePartitioning[i]);
1974     }
1975
1976     if (segmentation.size() == 0)
1977       return null;
1978
1979     int size = segmentation.size();
1980     int[] segments = new int[size * 2 + 1];
1981
1982     int j = 0;
1983     for (int i = 0; i < size; i++) {
1984       ITypedRegion segment = (ITypedRegion) segmentation.get(i);
1985
1986       if (i == 0)
1987         segments[j++] = 0;
1988
1989       int offset = segment.getOffset() - lineOffset;
1990       if (offset > segments[j - 1])
1991         segments[j++] = offset;
1992
1993       if (offset + segment.getLength() >= line.getLength())
1994         break;
1995
1996       segments[j++] = offset + segment.getLength();
1997     }
1998
1999     if (j < segments.length) {
2000       int[] result = new int[j];
2001       System.arraycopy(segments, 0, result, 0, j);
2002       segments = result;
2003     }
2004
2005     return segments;
2006   }
2007   /**
2008      * Returns a segmentation of the given line appropriate for bidi rendering. The default
2009      * implementation returns only the string literals of a php code line as segments.
2010      * 
2011      * @param lineOffset the offset of the line
2012      * @param line the content of the line
2013      * @return the line's bidi segmentation
2014      */
2015   protected int[] getBidiLineSegments(int lineOffset, String line) {
2016     IDocumentProvider provider = getDocumentProvider();
2017     if (provider != null && line != null && line.length() > 0) {
2018       IDocument document = provider.getDocument(getEditorInput());
2019       if (document != null)
2020         try {
2021           return getBidiLineSegments(document, lineOffset);
2022         } catch (BadLocationException x) {
2023           // ignore
2024         }
2025     }
2026     return null;
2027   }
2028
2029   /*
2030    * @see AbstractTextEditor#createSourceViewer(Composite, IVerticalRuler, int)
2031    */
2032   //  protected final ISourceViewer createSourceViewer(
2033   //    Composite parent,
2034   //    IVerticalRuler ruler,
2035   //    int styles) {
2036   //    ISourceViewer viewer = createJavaSourceViewer(parent, ruler, styles);
2037   //    StyledText text = viewer.getTextWidget();
2038   //    text.addBidiSegmentListener(new BidiSegmentListener() {
2039   //      public void lineGetSegments(BidiSegmentEvent event) {
2040   //        event.segments = getBidiLineSegments(event.lineOffset, event.lineText);
2041   //      }
2042   //    });
2043   //    //   JavaUIHelp.setHelp(this, text, IJavaHelpContextIds.JAVA_EDITOR);
2044   //    return viewer;
2045   //  }
2046   protected final ISourceViewer createSourceViewer(Composite parent, IVerticalRuler verticalRuler, int styles) {
2047
2048     ISharedTextColors sharedColors = PHPeclipsePlugin.getDefault().getJavaTextTools().getColorManager();
2049
2050     fOverviewRuler = new OverviewRuler(fAnnotationAccess, VERTICAL_RULER_WIDTH, sharedColors);
2051     fOverviewRuler.addHeaderAnnotationType(AnnotationType.WARNING);
2052     fOverviewRuler.addHeaderAnnotationType(AnnotationType.ERROR);
2053
2054     ISourceViewer viewer = createJavaSourceViewer(parent, verticalRuler, fOverviewRuler, isOverviewRulerVisible(), styles);
2055
2056     StyledText text = viewer.getTextWidget();
2057     text.addBidiSegmentListener(new BidiSegmentListener() {
2058       public void lineGetSegments(BidiSegmentEvent event) {
2059         event.segments = getBidiLineSegments(event.lineOffset, event.lineText);
2060       }
2061     });
2062
2063     //          JavaUIHelp.setHelp(this, text, IJavaHelpContextIds.JAVA_EDITOR);
2064
2065     fSourceViewerDecorationSupport = new SourceViewerDecorationSupport(viewer, fOverviewRuler, fAnnotationAccess, sharedColors);
2066     configureSourceViewerDecorationSupport();
2067
2068     return viewer;
2069   }
2070
2071   protected void showOverviewRuler() {
2072     if (fOverviewRuler != null) {
2073       if (getSourceViewer() instanceof ISourceViewerExtension) {
2074         ((ISourceViewerExtension) getSourceViewer()).showAnnotationsOverview(true);
2075         fSourceViewerDecorationSupport.updateOverviewDecorations();
2076       }
2077     }
2078   }
2079
2080   protected void hideOverviewRuler() {
2081     if (getSourceViewer() instanceof ISourceViewerExtension) {
2082       fSourceViewerDecorationSupport.hideAnnotationOverview();
2083       ((ISourceViewerExtension) getSourceViewer()).showAnnotationsOverview(false);
2084     }
2085   }
2086
2087   protected boolean isOverviewRulerVisible() {
2088     IPreferenceStore store = getPreferenceStore();
2089     return store.getBoolean(OVERVIEW_RULER);
2090   }
2091   /*
2092    * @see AbstractTextEditor#createSourceViewer(Composite, IVerticalRuler, int)
2093    */
2094   protected ISourceViewer createJavaSourceViewer(
2095     Composite parent,
2096     IVerticalRuler ruler,
2097     IOverviewRuler overviewRuler,
2098     boolean isOverviewRulerVisible,
2099     int styles) {
2100     return new SourceViewer(parent, ruler, overviewRuler, isOverviewRulerVisible(), styles);
2101     //    return super.createSourceViewer(parent, ruler, styles);
2102   }
2103
2104   /*
2105    * @see AbstractTextEditor#affectsTextPresentation(PropertyChangeEvent)
2106    */
2107   protected boolean affectsTextPresentation(PropertyChangeEvent event) {
2108     JavaTextTools textTools = PHPeclipsePlugin.getDefault().getJavaTextTools();
2109     return textTools.affectsBehavior(event);
2110   }
2111
2112 }