intial source from ttp://www.sf.net/projects/wdte
[phpeclipse.git] / archive / net.sourceforge.phpeclipse.css.ui / src / net / sourceforge / phpeclipse / css / ui / internal / editor / CssEditor.java
1 /*
2  * Copyright (c) 2003-2004 Christopher Lenz and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     Christopher Lenz - initial API and implementation
10  * 
11  * $Id: CssEditor.java,v 1.1 2004-09-02 18:11:50 jsurfer Exp $
12  */
13
14 package net.sourceforge.phpeclipse.css.ui.internal.editor;
15
16 import net.sourceforge.phpeclipse.core.model.ISourceReference;
17 import net.sourceforge.phpeclipse.css.core.model.IAtRule;
18 import net.sourceforge.phpeclipse.css.core.model.IRule;
19 import net.sourceforge.phpeclipse.css.core.model.IStyleRule;
20 import net.sourceforge.phpeclipse.css.core.model.IStyleSheet;
21 import net.sourceforge.phpeclipse.css.ui.CssUI;
22 import net.sourceforge.phpeclipse.css.ui.internal.CssDocumentProvider;
23 import net.sourceforge.phpeclipse.css.ui.internal.CssUIMessages;
24 import net.sourceforge.phpeclipse.css.ui.internal.CssUIPreferences;
25 import net.sourceforge.phpeclipse.css.ui.internal.ICssUIHelpContextIds;
26 import net.sourceforge.phpeclipse.css.ui.internal.outline.CssOutlinePage;
27 import net.sourceforge.phpeclipse.css.ui.internal.text.CssPairMatcher;
28 import net.sourceforge.phpeclipse.css.ui.internal.text.CssSourceViewerConfiguration;
29 import net.sourceforge.phpeclipse.css.ui.internal.text.IReconcilingParticipant;
30
31 import org.eclipse.jface.action.IAction;
32 import org.eclipse.jface.preference.IPreferenceStore;
33 import org.eclipse.jface.text.IRegion;
34 import org.eclipse.jface.text.source.ISourceViewer;
35 import org.eclipse.jface.util.PropertyChangeEvent;
36 import org.eclipse.jface.viewers.ISelectionChangedListener;
37 import org.eclipse.jface.viewers.IStructuredSelection;
38 import org.eclipse.jface.viewers.SelectionChangedEvent;
39 import org.eclipse.swt.custom.StyledText;
40 import org.eclipse.swt.widgets.Shell;
41 import org.eclipse.ui.editors.text.TextEditor;
42 import org.eclipse.ui.texteditor.ContentAssistAction;
43 import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
44 import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
45 import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
46
47 /**
48  * Implementation of a CSS editor based on the text editor infrastructure
49  * provided by the platform.
50  * 
51  * TODO Reacting to every caret move seems rather expensive. Ideally, we'd
52  *      collect the caret position changes and react after a specific delay.
53  */
54 public class CssEditor extends TextEditor implements IReconcilingParticipant {
55
56         // Inner Classes -----------------------------------------------------------
57
58         /**
59          * Listens to changes to the selection in the outline page, and changes the
60          * selection and highlight range in the editor accordingly.
61          */
62         private class OutlineSelectionChangedListener
63                 implements ISelectionChangedListener {
64
65                 /*
66                  * @see ISelectionChangedListener#selectionChanged(SelectionChangedEvent)
67                  */
68                 public void selectionChanged(SelectionChangedEvent event) {
69                         IStructuredSelection selection =
70                                 (IStructuredSelection) event.getSelection();
71                         if (selection.isEmpty()) {
72                                 resetHighlightRange();
73                         } else {
74                                 ISourceReference element =
75                                         (ISourceReference) selection.getFirstElement();
76                                 highlightElement(element, true);
77
78                                 IRegion selectedRegion = null;
79                                 if (element instanceof IAtRule) {
80                                         IAtRule atRule = (IAtRule) element;
81                                         selectedRegion = atRule.getValue().getSourceRegion();
82                                 } else if (element instanceof IStyleRule) {
83                                         IStyleRule styleRule = (IStyleRule) element;
84                                         selectedRegion = styleRule.getSelector().getSourceRegion();
85                                 }
86
87                                 if (selectedRegion != null) {
88                                         selectAndReveal(selectedRegion.getOffset(),
89                                         selectedRegion.getLength());
90                                 }
91                         }
92                 }
93         }
94
95         // Constants ---------------------------------------------------------------
96
97         /**
98          * Alias for the preference constant <code>OUTLINE_LINK_WITH_EDITOR</code>.
99          */
100         private static final String LINK_WITH_OUTLINE =
101                 CssUIPreferences.OUTLINE_LINK_WITH_EDITOR;
102
103         // Instance Variables ------------------------------------------------------
104
105         /**
106          * The associated outline page.
107          */
108         CssOutlinePage outlinePage;
109
110         /**
111          * Listens to changes in the outline page's selection to update the editor
112          * selection and highlight range.
113          */
114         private ISelectionChangedListener outlineSelectionChangedListener;
115
116         // Constructors ------------------------------------------------------------
117
118         /**
119          * Default constructor.
120          */
121         public CssEditor() {
122                 setSourceViewerConfiguration(
123                         new CssSourceViewerConfiguration(
124                                         CssUI.getDefault().getTextTools(),
125                                         getPreferenceStore(), this));
126         }
127
128         // AbstractTextEditor Implementation ---------------------------------------
129
130         /*
131          * @see org.eclipse.ui.texteditor.AbstractTextEditor#affectsTextPresentation(PropertyChangeEvent)
132          */
133         protected boolean affectsTextPresentation(PropertyChangeEvent event) {
134                 String p = event.getProperty();
135                 if (CssUIPreferences.EDITOR_DEFAULT_COLOR.equals(p) ||
136                         CssUIPreferences.EDITOR_DEFAULT_BOLD.equals(p) ||
137                         CssUIPreferences.EDITOR_COMMENT_COLOR.equals(p) ||
138                         CssUIPreferences.EDITOR_COMMENT_BOLD.equals(p) ||
139                         CssUIPreferences.EDITOR_STRING_COLOR.equals(p) ||
140                         CssUIPreferences.EDITOR_STRING_BOLD.equals(p) ||
141                         CssUIPreferences.EDITOR_PROPERTY_COLOR.equals(p) ||
142                         CssUIPreferences.EDITOR_PROPERTY_BOLD.equals(p) ||
143                         CssUIPreferences.EDITOR_AT_KEYWORD_COLOR.equals(p) ||
144                         CssUIPreferences.EDITOR_AT_KEYWORD_BOLD.equals(p) ||
145                         CssUIPreferences.EDITOR_PSEUDO_CLASS_COLOR.equals(p) ||
146                         CssUIPreferences.EDITOR_PSEUDO_CLASS_BOLD.equals(p)
147                 ) {
148                         return true;
149                 }
150
151                 return super.affectsTextPresentation(event);
152         }
153
154         /*
155          * @see org.eclipse.ui.texteditor.AbstractTextEditor#createActions()
156          */
157         protected void createActions() {
158                 super.createActions();
159
160                 IAction action;
161
162                 action = new ContentAssistAction(
163                         CssUIMessages.getResourceBundle(),
164                         "CssEditor.contentAssist.", this); //$NON-NLS-1$
165                 action.setActionDefinitionId(
166                         ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
167                 setAction(ICssEditorActionConstants.CONTENT_ASSIST, action);
168
169                 action = new CommentAction(
170                         CssUIMessages.getResourceBundle(),
171                         "CssEditor.comment.", this); //$NON-NLS-1$
172                 action.setActionDefinitionId(ICssEditorActionDefinitionIds.COMMENT);
173                 setAction(ICssEditorActionConstants.COMMENT, action);
174                 markAsStateDependentAction(ICssEditorActionConstants.COMMENT, true);
175                 markAsSelectionDependentAction(ICssEditorActionConstants.COMMENT, true);
176
177                 action = new UncommentAction(
178                         CssUIMessages.getResourceBundle(),
179                         "CssEditor.uncomment.", this); //$NON-NLS-1$
180                 action.setActionDefinitionId(ICssEditorActionDefinitionIds.UNCOMMENT);
181                 setAction(ICssEditorActionConstants.UNCOMMENT, action);
182                 markAsStateDependentAction(ICssEditorActionConstants.UNCOMMENT, true);
183                 markAsSelectionDependentAction(ICssEditorActionConstants.UNCOMMENT,
184                         true);
185         }
186
187         /*
188          * @see org.eclipse.ui.texteditor.ExtendedTextEditor#configureSourceViewerDecorationSupport(SourceViewerDecorationSupport)
189          */
190         protected void configureSourceViewerDecorationSupport(
191                 SourceViewerDecorationSupport support
192         ) {
193                 super.configureSourceViewerDecorationSupport(support);
194
195                 support.setCharacterPairMatcher(new CssPairMatcher());
196                 support.setMatchingCharacterPainterPreferenceKeys(
197                         CssUIPreferences.EDITOR_MATCHING_BRACKETS,
198                         CssUIPreferences.EDITOR_MATCHING_BRACKETS_COLOR);
199                 support.setSymbolicFontName(getFontPropertyPreferenceKey());
200         }
201
202         /*
203          * @see org.eclipse.ui.texteditor.AbstractTextEditor#getAdapter(Class)
204          */
205         public Object getAdapter(Class adapter) {
206                 if (IContentOutlinePage.class.equals(adapter)) {
207                         if (outlinePage == null) {
208                                 outlinePage = new CssOutlinePage(this);
209                                 outlineSelectionChangedListener =
210                                         new OutlineSelectionChangedListener();
211                                 outlinePage.addSelectionChangedListener(
212                                         outlineSelectionChangedListener);
213                         }
214
215                         return outlinePage;
216                 }
217
218                 return super.getAdapter(adapter);
219         }
220
221         /*
222          * @see org.eclipse.ui.texteditor.AbstractTextEditor#handleCursorPositionChanged()
223          */
224         protected void handleCursorPositionChanged() {
225                 super.handleCursorPositionChanged();
226
227                 highlightElement(computeHighlightRangeSourceReference(), false);
228                 synchronizeOutlinePageSelection();
229         }
230
231         /*
232          * @see org.eclipse.ui.texteditor.ExtendedTextEditor#initializeEditor()
233          */
234         protected void initializeEditor() {
235                 super.initializeEditor();
236
237                 setHelpContextId(ICssUIHelpContextIds.EDITOR);
238                 setPreferenceStore(CssUI.getDefault().getPreferenceStore());
239                 configureInsertMode(SMART_INSERT, true);
240                 setInsertMode(SMART_INSERT);
241         }
242
243         // IReconcilingParticipant Implementation ----------------------------------
244
245         /* 
246          * @see IReconcilingParticipant#reconciled()
247          */
248         public void reconciled() {
249                 Shell shell = getSite().getShell();
250                 if ((shell != null) && !shell.isDisposed()) {
251                         shell.getDisplay().asyncExec(new Runnable() {
252                                 public void run() {
253                                         if (outlinePage != null) {
254                                                 outlinePage.update();
255                                         }
256                                         synchronizeOutlinePageSelection();
257                                 }
258                         });
259                 }
260         }
261
262         // Public Methods ----------------------------------------------------------
263
264         /**
265          * Computes and returns the source reference that includes the caret and
266          * serves as provider for the outline page selection and the editor range
267          * indication.
268          * 
269          * @return the computed source reference
270          */
271         public ISourceReference computeHighlightRangeSourceReference() {
272                 ISourceViewer sourceViewer = getSourceViewer();
273                 if (sourceViewer == null) {
274                         return null;
275                 }
276
277                 StyledText styledText = sourceViewer.getTextWidget();
278                 if ((styledText == null) || styledText.isDisposed()) {
279                         return null;
280                 }
281
282                 int offset = sourceViewer.getVisibleRegion().getOffset();
283                 int caret = offset + styledText.getCaretOffset();
284
285                 return getRuleAt(caret);
286         }
287
288         /**
289          * Informs the editor that its outliner has been closed.
290          * 
291          * TODO There must be a more elegant way to get notified when the outline 
292          *      page was closed. Otherwise move this method into an interface
293          */
294         public void outlinePageClosed() {
295                 if (outlinePage != null) {
296                         outlinePage.removeSelectionChangedListener(
297                                         outlineSelectionChangedListener);
298                         outlinePage = null;
299                         resetHighlightRange();
300                 }
301         }
302
303         /**
304          * Synchronizes the outliner selection with the given element position in 
305          * the editor.
306          * 
307          * @param element the java element to select
308          */
309         public void synchronizeOutlinePage(ISourceReference element) {
310                 if (outlinePage != null) {
311                         outlinePage.removeSelectionChangedListener(
312                                 outlineSelectionChangedListener);
313                         outlinePage.select(element);
314                         outlinePage.addSelectionChangedListener(
315                                 outlineSelectionChangedListener);
316                 }
317         }
318
319         /**
320          * Synchronizes the outliner selection with the currently highlighted source
321          * reference.
322          */
323         public void synchronizeOutlinePage() {
324                 ISourceReference element = computeHighlightRangeSourceReference();
325                 synchronizeOutlinePage(element);
326         }
327
328         // Private Methods ---------------------------------------------------------
329
330         private IRule getRuleAt(int offset) {
331                 IStyleSheet styleSheet = getStyleSheet();
332                 if (styleSheet == null) {
333                         return null;
334                 }
335
336                 return styleSheet.getRuleAt(offset);
337         }
338
339         private IStyleSheet getStyleSheet() {
340                 if (getDocumentProvider() instanceof CssDocumentProvider) {
341                         CssDocumentProvider p = (CssDocumentProvider) getDocumentProvider();
342                         return p.getStyleSheet(getEditorInput());
343                 }
344
345                 return null;
346         }
347
348         void highlightElement(ISourceReference element, boolean moveCursor) {
349                 if (element != null) {
350                         IRegion highlightRegion = element.getSourceRegion();
351                         setHighlightRange(highlightRegion.getOffset(),
352                                 highlightRegion.getLength(), moveCursor);
353                 } else {
354                         resetHighlightRange();
355                 }
356         }
357
358         void synchronizeOutlinePageSelection() {
359                 IPreferenceStore store = getPreferenceStore();
360                 if (store != null) {
361                         boolean linkWithEditor = store.getBoolean(LINK_WITH_OUTLINE);
362                         if (linkWithEditor) {
363                                 synchronizeOutlinePage(computeHighlightRangeSourceReference());
364                         }
365                 }
366         }
367 }