A lot of changes
[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
17 import net.sourceforge.phpdt.internal.ui.actions.CompositeActionGroup;
18 import net.sourceforge.phpdt.internal.ui.text.HTMLTextPresenter;
19 import net.sourceforge.phpdt.internal.ui.text.PHPPairMatcher;
20 import net.sourceforge.phpdt.internal.ui.viewsupport.IViewPartInputProvider;
21 import net.sourceforge.phpdt.ui.PreferenceConstants;
22 import net.sourceforge.phpdt.ui.actions.GenerateActionGroup;
23 import net.sourceforge.phpdt.ui.actions.GotoMatchingBracketAction;
24 import net.sourceforge.phpdt.ui.text.IColorManager;
25 import net.sourceforge.phpdt.ui.text.JavaTextTools;
26 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
27 import net.sourceforge.phpeclipse.phpeditor.php.IPHPPartitionScannerConstants;
28
29 import org.eclipse.core.resources.IResource;
30 import org.eclipse.core.runtime.CoreException;
31 import org.eclipse.core.runtime.IProgressMonitor;
32 import org.eclipse.jface.action.Action;
33 import org.eclipse.jface.action.IAction;
34 import org.eclipse.jface.action.MenuManager;
35 import org.eclipse.jface.preference.IPreferenceStore;
36 import org.eclipse.jface.preference.PreferenceConverter;
37 import org.eclipse.jface.text.BadLocationException;
38 import org.eclipse.jface.text.DefaultInformationControl;
39 import org.eclipse.jface.text.IDocument;
40 import org.eclipse.jface.text.IInformationControl;
41 import org.eclipse.jface.text.IInformationControlCreator;
42 import org.eclipse.jface.text.IRegion;
43 import org.eclipse.jface.text.ITextHover;
44 import org.eclipse.jface.text.ITextOperationTarget;
45 import org.eclipse.jface.text.ITextViewer;
46 import org.eclipse.jface.text.ITextViewerExtension2;
47 import org.eclipse.jface.text.ITextViewerExtension3;
48 import org.eclipse.jface.text.ITypedRegion;
49 import org.eclipse.jface.text.Region;
50 import org.eclipse.jface.text.information.InformationPresenter;
51 import org.eclipse.jface.text.source.AnnotationRulerColumn;
52 import org.eclipse.jface.text.source.CompositeRuler;
53 import org.eclipse.jface.text.source.ISourceViewer;
54 import org.eclipse.jface.text.source.IVerticalRuler;
55 import org.eclipse.jface.text.source.IVerticalRulerColumn;
56 import org.eclipse.jface.text.source.LineNumberRulerColumn;
57 import org.eclipse.jface.text.source.SourceViewerConfiguration;
58 import org.eclipse.jface.util.PropertyChangeEvent;
59 import org.eclipse.swt.SWT;
60 import org.eclipse.swt.custom.BidiSegmentEvent;
61 import org.eclipse.swt.custom.BidiSegmentListener;
62 import org.eclipse.swt.custom.StyledText;
63 import org.eclipse.swt.graphics.Point;
64 import org.eclipse.swt.graphics.RGB;
65 import org.eclipse.swt.widgets.Composite;
66 import org.eclipse.swt.widgets.Shell;
67 import org.eclipse.ui.IEditorInput;
68 import org.eclipse.ui.actions.ActionContext;
69 import org.eclipse.ui.actions.ActionGroup;
70 import org.eclipse.ui.texteditor.ContentAssistAction;
71 import org.eclipse.ui.texteditor.DefaultRangeIndicator;
72 import org.eclipse.ui.texteditor.IDocumentProvider;
73 import org.eclipse.ui.texteditor.IEditorStatusLine;
74 import org.eclipse.ui.texteditor.ITextEditorActionConstants;
75 import org.eclipse.ui.texteditor.StatusTextEditor;
76 import org.eclipse.ui.texteditor.TextOperationAction;
77 import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
78 /**
79  * PHP specific text editor.
80  */
81 public class PHPEditor
82   extends StatusTextEditor
83   implements IViewPartInputProvider { // extends TextEditor {
84
85   /** Preference key for showing the line number ruler */
86   private final static String LINE_NUMBER_RULER =
87     PreferenceConstants.EDITOR_LINE_NUMBER_RULER;
88   /** Preference key for the foreground color of the line numbers */
89   private final static String LINE_NUMBER_COLOR =
90     PreferenceConstants.EDITOR_LINE_NUMBER_RULER_COLOR;
91   /** Preference key for the link color */
92   private final static String LINK_COLOR =
93     PreferenceConstants.EDITOR_LINK_COLOR;
94
95   // protected PHPActionGroup fActionGroups;
96   /** The outline page */
97   private PHPContentOutlinePage fOutlinePage;
98
99   //  protected PHPSyntaxParserThread fValidationThread = null;
100
101   // private IPreferenceStore fPHPPrefStore;
102
103   /** The editor's bracket matcher */
104   private PHPPairMatcher fBracketMatcher;
105   /** The line number ruler column */
106   private LineNumberRulerColumn fLineNumberRulerColumn;
107
108   protected CompositeActionGroup fActionGroups;
109   /** The standard action groups added to the menu */
110   private GenerateActionGroup fGenerateActionGroup;
111   private CompositeActionGroup fContextMenuGroup;
112
113   /** The information presenter. */
114   private InformationPresenter fInformationPresenter;
115
116   /**
117    * Default constructor.
118    */
119   public PHPEditor() {
120     super();
121     JavaTextTools textTools = PHPeclipsePlugin.getDefault().getJavaTextTools();
122     setSourceViewerConfiguration(
123       new PHPSourceViewerConfiguration(textTools, this));
124     setRangeIndicator(new DefaultRangeIndicator());
125     setPreferenceStore(PHPeclipsePlugin.getDefault().getPreferenceStore());
126
127     //    if (PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SYNC_OUTLINE_ON_CURSOR_MOVE))
128     //      fUpdater= new OutlinePageSelectionUpdater();
129
130     initializeEditor();
131   }
132   //
133   //    /**
134   //     * @see IMember#getCompilationUnit()
135   //     */
136   //    public ICompilationUnit getCompilationUnit() {
137   //            return this; 
138   //    }
139   //    /**
140   //     * @see org.phpeclipse.phpdt.internal.compiler.env.ICompilationUnit#getContents()
141   //     */
142   //    public char[] getContents() {
143   //            IDocument doc = this.getDocumentProvider().getDocument(this.getEditorInput());
144   //    
145   //            return doc.get().toCharArray();
146   //    }
147
148   /*
149    * Update the hovering behavior depending on the preferences.
150    */
151   private void updateHoverBehavior() {
152     SourceViewerConfiguration configuration= getSourceViewerConfiguration();
153     String[] types= configuration.getConfiguredContentTypes(getSourceViewer());
154
155     for (int i= 0; i < types.length; i++) {
156                         
157       String t= types[i];
158                                 
159       int[] stateMasks= configuration.getConfiguredTextHoverStateMasks(getSourceViewer(), t);
160
161       ISourceViewer sourceViewer= getSourceViewer();
162       if (sourceViewer instanceof ITextViewerExtension2) {
163         if (stateMasks != null) {
164           for (int j= 0; j < stateMasks.length; j++)    {
165             int stateMask= stateMasks[j];
166             ITextHover textHover= configuration.getTextHover(sourceViewer, t, stateMask);
167             ((ITextViewerExtension2)sourceViewer).setTextHover(textHover, t, stateMask);
168           }
169         } else {
170           ITextHover textHover= configuration.getTextHover(sourceViewer, t);
171           ((ITextViewerExtension2)sourceViewer).setTextHover(textHover, t, ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK);
172         }
173       } else
174         sourceViewer.setTextHover(configuration.getTextHover(sourceViewer, t), t);
175     }
176   }
177   
178   /*
179    * @see net.sourceforge.phpdt.internal.ui.viewsupport.IViewPartInputProvider#getViewPartInput()
180    */
181   public Object getViewPartInput() {
182     return getEditorInput().getAdapter(IResource.class);
183   }
184
185   /*
186    * @see org.eclipse.ui.IWorkbenchPart#createPartControl(org.eclipse.swt.
187    * widgets.Composite)
188    */
189   public void createPartControl(Composite parent) {
190     super.createPartControl(parent);
191
192     IInformationControlCreator informationControlCreator =
193       new IInformationControlCreator() {
194       public IInformationControl createInformationControl(Shell parent) {
195         boolean cutDown = false;
196         int style = cutDown ? SWT.NONE : (SWT.V_SCROLL | SWT.H_SCROLL);
197         return new DefaultInformationControl(
198           parent,
199           SWT.RESIZE,
200           style,
201           new HTMLTextPresenter(cutDown));
202       }
203     };
204
205     fInformationPresenter = new InformationPresenter(informationControlCreator);
206     fInformationPresenter.setSizeConstraints(60, 10, true, true);
207     fInformationPresenter.install(getSourceViewer());
208   }
209
210   /**
211    * Returns this document's complete text.
212    *
213    * @return the document's complete text
214    */
215   public String get() {
216     IDocument doc =
217       this.getDocumentProvider().getDocument(this.getEditorInput());
218     return doc.get();
219   }
220
221   /**
222    *  Returns the standard action group of this editor.
223    */
224   protected ActionGroup getActionGroup() {
225     return fActionGroups;
226   }
227
228   public PHPContentOutlinePage getfOutlinePage() {
229     return fOutlinePage;
230   }
231
232   /** The <code>PHPEditor</code> implementation of this 
233    * <code>AbstractTextEditor</code> method extend the 
234    * actions to add those specific to the receiver
235    */
236   protected void createActions() {
237     super.createActions();
238
239     Action action;
240     //    setAction(
241     //      "ContentAssistProposal",
242     //      new TextOperationAction(
243     //        PHPEditorMessages.getResourceBundle(),
244     //        "ContentAssistProposal.",
245     //        this,
246     //        ISourceViewer.CONTENTASSIST_PROPOSALS));
247     action = new ContentAssistAction(PHPEditorMessages.getResourceBundle(), "ContentAssistProposal.", this); //$NON-NLS-1$
248     action.setActionDefinitionId(
249       PHPEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
250     setAction("ContentAssistProposal", action); //$NON-NLS-1$
251
252     setAction(
253       "ContentAssistTip",
254       new TextOperationAction(
255         PHPEditorMessages.getResourceBundle(),
256         "ContentAssistTip.",
257         this,
258         ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION));
259
260     action =
261       new TextOperationAction(
262         PHPEditorMessages.getResourceBundle(),
263         "Comment.",
264         this,
265         ITextOperationTarget.PREFIX);
266     action.setActionDefinitionId(PHPEditorActionDefinitionIds.COMMENT);
267     setAction("Comment", action);
268
269     action =
270       new TextOperationAction(
271         PHPEditorMessages.getResourceBundle(),
272         "Uncomment.",
273         this,
274         ITextOperationTarget.STRIP_PREFIX);
275     action.setActionDefinitionId(PHPEditorActionDefinitionIds.UNCOMMENT);
276     setAction("Uncomment", action);
277
278     action = new TextOperationAction(PHPEditorMessages.getResourceBundle(), "Format.", this, ISourceViewer.FORMAT); //$NON-NLS-1$
279     action.setActionDefinitionId(PHPEditorActionDefinitionIds.FORMAT);
280     setAction("Format", action); //$NON-NLS-1$
281
282     markAsStateDependentAction("ContentAssistProposal", true); //$NON-NLS-1$
283     markAsStateDependentAction("Comment", true); //$NON-NLS-1$
284     markAsStateDependentAction("Uncomment", true); //$NON-NLS-1$
285     markAsStateDependentAction("Format", true); //$NON-NLS-1$
286
287     action = new GotoMatchingBracketAction(this);
288     action.setActionDefinitionId(
289       PHPEditorActionDefinitionIds.GOTO_MATCHING_BRACKET);
290     setAction(GotoMatchingBracketAction.GOTO_MATCHING_BRACKET, action);
291
292     fGenerateActionGroup =
293       new GenerateActionGroup(this, ITextEditorActionConstants.GROUP_EDIT);
294
295     fActionGroups =
296       new CompositeActionGroup(new ActionGroup[] { fGenerateActionGroup });
297
298     // We have to keep the context menu group separate to have better control over positioning
299     fContextMenuGroup =
300       new CompositeActionGroup(new ActionGroup[] { fGenerateActionGroup });
301     //      rg, 
302     //      new LocalHistoryActionGroup(this, ITextEditorActionConstants.GROUP_EDIT)});
303
304     //    if (fValidationThread == null) {
305     //      fValidationThread =
306     //        new PHPSyntaxParserThread(this, getSourceViewer());
307     //      //Thread defaults
308     //
309     //      fValidationThread.start();
310     //    }
311     //
312     //    fValidationThread.setText(getSourceViewer().getTextWidget().getText());
313   }
314
315   /** The <code>PHPEditor</code> implementation of this 
316    * <code>AbstractTextEditor</code> method performs any extra 
317    * disposal actions required by the php editor.
318    */
319   public void dispose() {
320     PHPEditorEnvironment.disconnect(this);
321     if (fOutlinePage != null)
322       fOutlinePage.setInput(null);
323
324     if (fActionGroups != null)
325       fActionGroups.dispose();
326
327     super.dispose();
328   }
329
330   /** The <code>PHPEditor</code> implementation of this 
331    * <code>AbstractTextEditor</code> method performs any extra 
332    * revert behavior required by the php editor.
333    */
334   public void doRevertToSaved() {
335     super.doRevertToSaved();
336     if (fOutlinePage != null)
337       fOutlinePage.update();
338   }
339
340   /** The <code>PHPEditor</code> implementation of this 
341    * <code>AbstractTextEditor</code> method performs any extra 
342    * save behavior required by the php editor.
343    */
344   public void doSave(IProgressMonitor monitor) {
345     super.doSave(monitor);
346     // compile or not, according to the user preferences
347     IPreferenceStore store = getPreferenceStore(); // fPHPPrefStore;
348     if (store.getBoolean(PHPeclipsePlugin.PHP_PARSE_ON_SAVE)) {
349       IAction a = PHPParserAction.getInstance();
350       if (a != null)
351         a.run();
352     }
353     if (fOutlinePage != null)
354       fOutlinePage.update();
355   }
356
357   /** The <code>PHPEditor</code> implementation of this 
358    * <code>AbstractTextEditor</code> method performs any extra 
359    * save as behavior required by the php editor.
360    */
361   public void doSaveAs() {
362     super.doSaveAs();
363     if (fOutlinePage != null)
364       fOutlinePage.update();
365   }
366
367   /** The <code>PHPEditor</code> implementation of this 
368    * <code>AbstractTextEditor</code> method performs sets the 
369    * input of the outline page after AbstractTextEditor has set input.
370    */
371   protected void doSetInput(IEditorInput input) throws CoreException {
372     super.doSetInput(input);
373     if (fOutlinePage != null)
374       fOutlinePage.setInput(input);
375   }
376
377   /*
378    * @see org.phpeclipse.phpdt.internal.ui.viewsupport.IViewPartInputProvider#getViewPartInput()
379    */
380   //  public Object getViewPartInput() {
381   //    return getEditorInput().getAdapter(IFile.class);
382   //  }
383
384   /** The <code>PHPEditor</code> implementation of this 
385    * <code>AbstractTextEditor</code> method adds any 
386    * PHPEditor specific entries.
387    */
388   public void editorContextMenuAboutToShow(MenuManager menu) {
389     super.editorContextMenuAboutToShow(menu);
390
391     addAction(menu, ITextEditorActionConstants.GROUP_EDIT, "Format"); //$NON-NLS-1$
392
393     ActionContext context =
394       new ActionContext(getSelectionProvider().getSelection());
395     fContextMenuGroup.setContext(context);
396     fContextMenuGroup.fillContextMenu(menu);
397     fContextMenuGroup.setContext(null);
398   }
399
400   protected void updateStateDependentActions() {
401     super.updateStateDependentActions();
402     fGenerateActionGroup.editorStateChanged();
403   }
404
405   /** The <code>PHPEditor</code> implementation of this 
406    * <code>AbstractTextEditor</code> method performs gets
407    * the java content outline page if request is for a an 
408    * outline page.
409    */
410   public Object getAdapter(Class required) {
411     if (IContentOutlinePage.class.equals(required)) {
412       if (fOutlinePage == null) {
413         fOutlinePage = new PHPContentOutlinePage(getDocumentProvider(), this);
414         if (getEditorInput() != null)
415           fOutlinePage.setInput(getEditorInput());
416       }
417       return fOutlinePage;
418     }
419     return super.getAdapter(required);
420   }
421
422   //  public void openContextHelp() {
423   //    IDocument doc = this.getDocumentProvider().getDocument(this.getEditorInput());
424   //    ITextSelection selection = (ITextSelection) this.getSelectionProvider().getSelection();
425   //    int pos = selection.getOffset();
426   //    String word = getFunctionName(doc, pos);
427   //    openContextHelp(word);
428   //  }
429   //
430   //  private void openContextHelp(String word) {
431   //    open(word);
432   //  }
433   //
434   //  public static void open(String word) {
435   //    IHelp help = WorkbenchHelp.getHelpSupport();
436   //    if (help != null) {
437   //      IHelpResource helpResource = new PHPFunctionHelpResource(word);
438   //      WorkbenchHelp.getHelpSupport().displayHelpResource(helpResource);
439   //    } else {
440   //      //   showMessage(shell, dialogTitle, ActionMessages.getString("Open help not available"), false); //$NON-NLS-1$
441   //    }
442   //  }
443
444   //    private String getFunctionName(IDocument doc, int pos) {
445   //            Point word = PHPWordExtractor.findWord(doc, pos);
446   //            if (word != null) {
447   //                    try {
448   //                            return doc.get(word.x, word.y).replace('_', '-');
449   //                    } catch (BadLocationException e) {
450   //                    }
451   //            }
452   //            return "";
453   //    }
454
455   /*
456    * @see AbstractTextEditor#handlePreferenceStoreChanged(PropertyChangeEvent)
457    */
458   protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
459
460     try {
461
462       ISourceViewer sourceViewer = getSourceViewer();
463       if (sourceViewer == null)
464         return;
465
466       String property = event.getProperty();
467
468       if (PreferenceConstants.EDITOR_TAB_WIDTH.equals(property)) {
469         Object value = event.getNewValue();
470         if (value instanceof Integer) {
471           sourceViewer.getTextWidget().setTabs(((Integer) value).intValue());
472         } else if (value instanceof String) {
473           sourceViewer.getTextWidget().setTabs(
474             Integer.parseInt((String) value));
475         }
476         return;
477       }
478
479       if (LINE_NUMBER_RULER.equals(property)) {
480         if (isLineNumberRulerVisible())
481           showLineNumberRuler();
482         else
483           hideLineNumberRuler();
484         return;
485       }
486
487       if (fLineNumberRulerColumn != null
488         && (LINE_NUMBER_COLOR.equals(property)
489           || PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT.equals(property)
490           || PREFERENCE_COLOR_BACKGROUND.equals(property))) {
491
492         initializeLineNumberRulerColumn(fLineNumberRulerColumn);
493       }
494
495       if (isJavaEditorHoverProperty(property)) {
496         updateHoverBehavior();
497       }
498
499     } finally {
500       super.handlePreferenceStoreChanged(event);
501     }
502   }
503
504   //  /*
505   //     * @see AbstractTextEditor#handlePreferenceStoreChanged(PropertyChangeEvent)
506   //     */
507   //  protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
508   //
509   //    try {
510   //
511   //      ISourceViewer sourceViewer = getSourceViewer();
512   //      if (sourceViewer == null)
513   //        return;
514   //
515   //      String property = event.getProperty();
516   //
517   //      //      if (JavaSourceViewerConfiguration.PREFERENCE_TAB_WIDTH.equals(property)) {
518   //      //        Object value= event.getNewValue();
519   //      //        if (value instanceof Integer) {
520   //      //          sourceViewer.getTextWidget().setTabs(((Integer) value).intValue());
521   //      //        } else if (value instanceof String) {
522   //      //          sourceViewer.getTextWidget().setTabs(Integer.parseInt((String) value));
523   //      //        }
524   //      //        return;
525   //      //      }
526   //
527   //      if (IPreferenceConstants.LINE_NUMBER_RULER.equals(property)) {
528   //        if (isLineNumberRulerVisible())
529   //          showLineNumberRuler();
530   //        else
531   //          hideLineNumberRuler();
532   //        return;
533   //      }
534   //
535   //      if (fLineNumberRulerColumn != null
536   //        && (IPreferenceConstants.LINE_NUMBER_COLOR.equals(property)
537   //          || PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT.equals(property)
538   //          || PREFERENCE_COLOR_BACKGROUND.equals(property))) {
539   //
540   //        initializeLineNumberRulerColumn(fLineNumberRulerColumn);
541   //      }
542   //
543   //    } finally {
544   //      super.handlePreferenceStoreChanged(event);
545   //    }
546   //  }
547
548   private boolean isJavaEditorHoverProperty(String property) {
549     return PreferenceConstants.EDITOR_DEFAULT_HOVER.equals(property)
550       || PreferenceConstants.EDITOR_NONE_HOVER.equals(property)
551       || PreferenceConstants.EDITOR_CTRL_HOVER.equals(property)
552       || PreferenceConstants.EDITOR_SHIFT_HOVER.equals(property)
553       || PreferenceConstants.EDITOR_CTRL_ALT_HOVER.equals(property)
554       || PreferenceConstants.EDITOR_CTRL_SHIFT_HOVER.equals(property)
555       || PreferenceConstants.EDITOR_CTRL_ALT_SHIFT_HOVER.equals(property)
556       || PreferenceConstants.EDITOR_ALT_SHIFT_HOVER.equals(property);
557   }
558
559   /**
560    * Shows the line number ruler column.
561    */
562   private void showLineNumberRuler() {
563     IVerticalRuler v = getVerticalRuler();
564     if (v instanceof CompositeRuler) {
565       CompositeRuler c = (CompositeRuler) v;
566       c.addDecorator(1, createLineNumberRulerColumn());
567     }
568   }
569
570   /**
571    * Return whether the line number ruler column should be 
572    * visible according to the preference store settings.
573    * @return <code>true</code> if the line numbers should be visible
574    */
575   private boolean isLineNumberRulerVisible() {
576     IPreferenceStore store = getPreferenceStore();
577     return store.getBoolean(LINE_NUMBER_RULER);
578   }
579   /**
580    * Hides the line number ruler column.
581    */
582   private void hideLineNumberRuler() {
583     IVerticalRuler v = getVerticalRuler();
584     if (v instanceof CompositeRuler) {
585       CompositeRuler c = (CompositeRuler) v;
586       try {
587         c.removeDecorator(1);
588       } catch (Throwable e) {
589       }
590     }
591   }
592
593   /**
594    * Initializes the given line number ruler column from the preference store.
595    * @param rulerColumn the ruler column to be initialized
596    */
597   protected void initializeLineNumberRulerColumn(LineNumberRulerColumn rulerColumn) {
598     JavaTextTools textTools = PHPeclipsePlugin.getDefault().getJavaTextTools();
599     IColorManager manager = textTools.getColorManager();
600
601     IPreferenceStore store = getPreferenceStore();
602     if (store != null) {
603
604       RGB rgb = null;
605       // foreground color
606       if (store.contains(LINE_NUMBER_COLOR)) {
607         if (store.isDefault(LINE_NUMBER_COLOR))
608           rgb = PreferenceConverter.getDefaultColor(store, LINE_NUMBER_COLOR);
609         else
610           rgb = PreferenceConverter.getColor(store, LINE_NUMBER_COLOR);
611       }
612       rulerColumn.setForeground(manager.getColor(rgb));
613
614       rgb = null;
615       // background color
616       if (!store.getBoolean(PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT)) {
617         if (store.contains(PREFERENCE_COLOR_BACKGROUND)) {
618           if (store.isDefault(PREFERENCE_COLOR_BACKGROUND))
619             rgb =
620               PreferenceConverter.getDefaultColor(
621                 store,
622                 PREFERENCE_COLOR_BACKGROUND);
623           else
624             rgb =
625               PreferenceConverter.getColor(store, PREFERENCE_COLOR_BACKGROUND);
626         }
627       }
628       rulerColumn.setBackground(manager.getColor(rgb));
629     }
630   }
631
632   /**
633    * Creates a new line number ruler column that is appropriately initialized.
634    */
635   protected IVerticalRulerColumn createLineNumberRulerColumn() {
636     fLineNumberRulerColumn = new LineNumberRulerColumn();
637     initializeLineNumberRulerColumn(fLineNumberRulerColumn);
638     return fLineNumberRulerColumn;
639   }
640
641   /*
642    * @see AbstractTextEditor#createVerticalRuler()
643    */
644   protected IVerticalRuler createVerticalRuler() {
645     CompositeRuler ruler = new CompositeRuler();
646     ruler.addDecorator(0, new AnnotationRulerColumn(VERTICAL_RULER_WIDTH));
647     if (isLineNumberRulerVisible())
648       ruler.addDecorator(1, createLineNumberRulerColumn());
649     return ruler;
650   }
651
652   /* (non-Javadoc)
653    * Method declared on TextEditor
654    */
655   protected void initializeEditor() {
656     IPreferenceStore store = PHPeclipsePlugin.getDefault().getPreferenceStore();
657     PHPEditorEnvironment.connect(this);
658
659     //    store.addPropertyChangeListener(new IPropertyChangeListener() {
660     //      public void propertyChange(PropertyChangeEvent event) {
661     //        PHPCodeScanner scanner = PHPEditorEnvironment.getPHPCodeScanner();
662     //        if (scanner != null) {
663     //          scanner.updateToken(PHPEditorEnvironment.getPHPColorProvider());
664     //        }
665     //        if (getSourceViewer() != null) {
666     //          getSourceViewer().invalidateTextPresentation();
667     //        }
668     //
669     //        String property = event.getProperty();
670     //        if (IPreferenceConstants.LINE_NUMBER_RULER.equals(property)) {
671     //          if (isLineNumberRulerVisible())
672     //            showLineNumberRuler();
673     //          else
674     //            hideLineNumberRuler();
675     //          return;
676     //        }
677     //      }
678     //    });
679   }
680
681   private static IRegion getSignedSelection(ITextViewer viewer) {
682
683     StyledText text = viewer.getTextWidget();
684     int caretOffset = text.getCaretOffset();
685     Point selection = text.getSelection();
686
687     // caret left
688     int offset, length;
689     if (caretOffset == selection.x) {
690       offset = selection.y;
691       length = selection.x - selection.y;
692
693       // caret right
694     } else {
695       offset = selection.x;
696       length = selection.y - selection.x;
697     }
698
699     return new Region(offset, length);
700   }
701
702   private final static char[] BRACKETS = { '{', '}', '(', ')', '[', ']' };
703
704   private static boolean isBracket(char character) {
705     for (int i = 0; i != BRACKETS.length; ++i)
706       if (character == BRACKETS[i])
707         return true;
708     return false;
709   }
710
711   private static boolean isSurroundedByBrackets(
712     IDocument document,
713     int offset) {
714     if (offset == 0 || offset == document.getLength())
715       return false;
716
717     try {
718       return isBracket(document.getChar(offset - 1))
719         && isBracket(document.getChar(offset));
720
721     } catch (BadLocationException e) {
722       return false;
723     }
724   }
725   /**
726     * Jumps to the matching bracket.
727     */
728   public void gotoMatchingBracket() {
729
730     if (fBracketMatcher == null)
731       fBracketMatcher = new PHPPairMatcher(BRACKETS);
732
733     ISourceViewer sourceViewer = getSourceViewer();
734     IDocument document = sourceViewer.getDocument();
735     if (document == null)
736       return;
737
738     IRegion selection = getSignedSelection(sourceViewer);
739
740     int selectionLength = Math.abs(selection.getLength());
741     if (selectionLength > 1) {
742       setStatusLineErrorMessage(PHPEditorMessages.getString("GotoMatchingBracket.error.invalidSelection")); //$NON-NLS-1$               
743       sourceViewer.getTextWidget().getDisplay().beep();
744       return;
745     }
746
747     // #26314
748     int sourceCaretOffset = selection.getOffset() + selection.getLength();
749     if (isSurroundedByBrackets(document, sourceCaretOffset))
750       sourceCaretOffset -= selection.getLength();
751
752     IRegion region = fBracketMatcher.match(document, sourceCaretOffset);
753     if (region == null) {
754       setStatusLineErrorMessage(PHPEditorMessages.getString("GotoMatchingBracket.error.noMatchingBracket")); //$NON-NLS-1$              
755       sourceViewer.getTextWidget().getDisplay().beep();
756       return;
757     }
758
759     int offset = region.getOffset();
760     int length = region.getLength();
761
762     if (length < 1)
763       return;
764
765     int anchor = fBracketMatcher.getAnchor();
766     int targetOffset =
767       (PHPPairMatcher.RIGHT == anchor) ? offset : offset + length - 1;
768
769     boolean visible = false;
770     if (sourceViewer instanceof ITextViewerExtension3) {
771       ITextViewerExtension3 extension = (ITextViewerExtension3) sourceViewer;
772       visible = (extension.modelOffset2WidgetOffset(targetOffset) > -1);
773     } else {
774       IRegion visibleRegion = sourceViewer.getVisibleRegion();
775       visible =
776         (targetOffset >= visibleRegion.getOffset()
777           && targetOffset < visibleRegion.getOffset() + visibleRegion.getLength());
778     }
779
780     if (!visible) {
781       setStatusLineErrorMessage(PHPEditorMessages.getString("GotoMatchingBracket.error.bracketOutsideSelectedElement")); //$NON-NLS-1$          
782       sourceViewer.getTextWidget().getDisplay().beep();
783       return;
784     }
785
786     if (selection.getLength() < 0)
787       targetOffset -= selection.getLength();
788
789     sourceViewer.setSelectedRange(targetOffset, selection.getLength());
790     sourceViewer.revealRange(targetOffset, selection.getLength());
791   }
792   /**
793      * Ses the given message as error message to this editor's status line.
794      * @param msg message to be set
795      */
796   protected void setStatusLineErrorMessage(String msg) {
797     IEditorStatusLine statusLine =
798       (IEditorStatusLine) getAdapter(IEditorStatusLine.class);
799     if (statusLine != null)
800       statusLine.setMessage(true, msg, null);
801   }
802
803   /**
804      * Returns a segmentation of the line of the given document appropriate for bidi rendering.
805      * The default implementation returns only the string literals of a php code line as segments.
806      * 
807      * @param document the document
808      * @param lineOffset the offset of the line
809      * @return the line's bidi segmentation
810      * @throws BadLocationException in case lineOffset is not valid in document
811      */
812   public static int[] getBidiLineSegments(IDocument document, int lineOffset)
813     throws BadLocationException {
814
815     IRegion line = document.getLineInformationOfOffset(lineOffset);
816     ITypedRegion[] linePartitioning =
817       document.computePartitioning(lineOffset, line.getLength());
818
819     List segmentation = new ArrayList();
820     for (int i = 0; i < linePartitioning.length; i++) {
821       if (IPHPPartitionScannerConstants
822         .PHP_STRING
823         .equals(linePartitioning[i].getType()))
824         segmentation.add(linePartitioning[i]);
825     }
826
827     if (segmentation.size() == 0)
828       return null;
829
830     int size = segmentation.size();
831     int[] segments = new int[size * 2 + 1];
832
833     int j = 0;
834     for (int i = 0; i < size; i++) {
835       ITypedRegion segment = (ITypedRegion) segmentation.get(i);
836
837       if (i == 0)
838         segments[j++] = 0;
839
840       int offset = segment.getOffset() - lineOffset;
841       if (offset > segments[j - 1])
842         segments[j++] = offset;
843
844       if (offset + segment.getLength() >= line.getLength())
845         break;
846
847       segments[j++] = offset + segment.getLength();
848     }
849
850     if (j < segments.length) {
851       int[] result = new int[j];
852       System.arraycopy(segments, 0, result, 0, j);
853       segments = result;
854     }
855
856     return segments;
857   }
858   /**
859      * Returns a segmentation of the given line appropriate for bidi rendering. The default
860      * implementation returns only the string literals of a php code line as segments.
861      * 
862      * @param lineOffset the offset of the line
863      * @param line the content of the line
864      * @return the line's bidi segmentation
865      */
866   protected int[] getBidiLineSegments(int lineOffset, String line) {
867     IDocumentProvider provider = getDocumentProvider();
868     if (provider != null && line != null && line.length() > 0) {
869       IDocument document = provider.getDocument(getEditorInput());
870       if (document != null)
871         try {
872           return getBidiLineSegments(document, lineOffset);
873         } catch (BadLocationException x) {
874           // ignore
875         }
876     }
877     return null;
878   }
879
880   /*
881    * @see AbstractTextEditor#createSourceViewer(Composite, IVerticalRuler, int)
882    */
883   protected final ISourceViewer createSourceViewer(
884     Composite parent,
885     IVerticalRuler ruler,
886     int styles) {
887     ISourceViewer viewer = createJavaSourceViewer(parent, ruler, styles);
888     StyledText text = viewer.getTextWidget();
889     text.addBidiSegmentListener(new BidiSegmentListener() {
890       public void lineGetSegments(BidiSegmentEvent event) {
891         event.segments = getBidiLineSegments(event.lineOffset, event.lineText);
892       }
893     });
894     //   JavaUIHelp.setHelp(this, text, IJavaHelpContextIds.JAVA_EDITOR);
895     return viewer;
896   }
897
898   /*
899    * @see AbstractTextEditor#createSourceViewer(Composite, IVerticalRuler, int)
900    */
901   protected ISourceViewer createJavaSourceViewer(
902     Composite parent,
903     IVerticalRuler ruler,
904     int styles) {
905     return super.createSourceViewer(parent, ruler, styles);
906   }
907 }