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
diff --git a/archive/net.sourceforge.phpeclipse.css.ui/src/net/sourceforge/phpeclipse/css/ui/internal/editor/CssEditor.java b/archive/net.sourceforge.phpeclipse.css.ui/src/net/sourceforge/phpeclipse/css/ui/internal/editor/CssEditor.java
new file mode 100644 (file)
index 0000000..41b8129
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2003-2004 Christopher Lenz and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     Christopher Lenz - initial API and implementation
+ * 
+ * $Id: CssEditor.java,v 1.1 2004-09-02 18:11:50 jsurfer Exp $
+ */
+
+package net.sourceforge.phpeclipse.css.ui.internal.editor;
+
+import net.sourceforge.phpeclipse.core.model.ISourceReference;
+import net.sourceforge.phpeclipse.css.core.model.IAtRule;
+import net.sourceforge.phpeclipse.css.core.model.IRule;
+import net.sourceforge.phpeclipse.css.core.model.IStyleRule;
+import net.sourceforge.phpeclipse.css.core.model.IStyleSheet;
+import net.sourceforge.phpeclipse.css.ui.CssUI;
+import net.sourceforge.phpeclipse.css.ui.internal.CssDocumentProvider;
+import net.sourceforge.phpeclipse.css.ui.internal.CssUIMessages;
+import net.sourceforge.phpeclipse.css.ui.internal.CssUIPreferences;
+import net.sourceforge.phpeclipse.css.ui.internal.ICssUIHelpContextIds;
+import net.sourceforge.phpeclipse.css.ui.internal.outline.CssOutlinePage;
+import net.sourceforge.phpeclipse.css.ui.internal.text.CssPairMatcher;
+import net.sourceforge.phpeclipse.css.ui.internal.text.CssSourceViewerConfiguration;
+import net.sourceforge.phpeclipse.css.ui.internal.text.IReconcilingParticipant;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.editors.text.TextEditor;
+import org.eclipse.ui.texteditor.ContentAssistAction;
+import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
+import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
+import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
+
+/**
+ * Implementation of a CSS editor based on the text editor infrastructure
+ * provided by the platform.
+ * 
+ * TODO Reacting to every caret move seems rather expensive. Ideally, we'd
+ *      collect the caret position changes and react after a specific delay.
+ */
+public class CssEditor extends TextEditor implements IReconcilingParticipant {
+
+       // Inner Classes -----------------------------------------------------------
+
+       /**
+        * Listens to changes to the selection in the outline page, and changes the
+        * selection and highlight range in the editor accordingly.
+        */
+       private class OutlineSelectionChangedListener
+               implements ISelectionChangedListener {
+
+               /*
+                * @see ISelectionChangedListener#selectionChanged(SelectionChangedEvent)
+                */
+               public void selectionChanged(SelectionChangedEvent event) {
+                       IStructuredSelection selection =
+                               (IStructuredSelection) event.getSelection();
+                       if (selection.isEmpty()) {
+                               resetHighlightRange();
+                       } else {
+                               ISourceReference element =
+                                       (ISourceReference) selection.getFirstElement();
+                               highlightElement(element, true);
+
+                               IRegion selectedRegion = null;
+                               if (element instanceof IAtRule) {
+                                       IAtRule atRule = (IAtRule) element;
+                                       selectedRegion = atRule.getValue().getSourceRegion();
+                               } else if (element instanceof IStyleRule) {
+                                       IStyleRule styleRule = (IStyleRule) element;
+                                       selectedRegion = styleRule.getSelector().getSourceRegion();
+                               }
+
+                               if (selectedRegion != null) {
+                                       selectAndReveal(selectedRegion.getOffset(),
+                                       selectedRegion.getLength());
+                               }
+                       }
+               }
+       }
+
+       // Constants ---------------------------------------------------------------
+
+       /**
+        * Alias for the preference constant <code>OUTLINE_LINK_WITH_EDITOR</code>.
+        */
+       private static final String LINK_WITH_OUTLINE =
+               CssUIPreferences.OUTLINE_LINK_WITH_EDITOR;
+
+       // Instance Variables ------------------------------------------------------
+
+       /**
+        * The associated outline page.
+        */
+       CssOutlinePage outlinePage;
+
+       /**
+        * Listens to changes in the outline page's selection to update the editor
+        * selection and highlight range.
+        */
+       private ISelectionChangedListener outlineSelectionChangedListener;
+
+       // Constructors ------------------------------------------------------------
+
+       /**
+        * Default constructor.
+        */
+       public CssEditor() {
+               setSourceViewerConfiguration(
+                       new CssSourceViewerConfiguration(
+                                       CssUI.getDefault().getTextTools(),
+                                       getPreferenceStore(), this));
+       }
+
+       // AbstractTextEditor Implementation ---------------------------------------
+
+       /*
+        * @see org.eclipse.ui.texteditor.AbstractTextEditor#affectsTextPresentation(PropertyChangeEvent)
+        */
+       protected boolean affectsTextPresentation(PropertyChangeEvent event) {
+               String p = event.getProperty();
+               if (CssUIPreferences.EDITOR_DEFAULT_COLOR.equals(p) ||
+                       CssUIPreferences.EDITOR_DEFAULT_BOLD.equals(p) ||
+                       CssUIPreferences.EDITOR_COMMENT_COLOR.equals(p) ||
+                       CssUIPreferences.EDITOR_COMMENT_BOLD.equals(p) ||
+                       CssUIPreferences.EDITOR_STRING_COLOR.equals(p) ||
+                       CssUIPreferences.EDITOR_STRING_BOLD.equals(p) ||
+                       CssUIPreferences.EDITOR_PROPERTY_COLOR.equals(p) ||
+                       CssUIPreferences.EDITOR_PROPERTY_BOLD.equals(p) ||
+                       CssUIPreferences.EDITOR_AT_KEYWORD_COLOR.equals(p) ||
+                       CssUIPreferences.EDITOR_AT_KEYWORD_BOLD.equals(p) ||
+                       CssUIPreferences.EDITOR_PSEUDO_CLASS_COLOR.equals(p) ||
+                       CssUIPreferences.EDITOR_PSEUDO_CLASS_BOLD.equals(p)
+               ) {
+                       return true;
+               }
+
+               return super.affectsTextPresentation(event);
+       }
+
+       /*
+        * @see org.eclipse.ui.texteditor.AbstractTextEditor#createActions()
+        */
+       protected void createActions() {
+               super.createActions();
+
+               IAction action;
+
+               action = new ContentAssistAction(
+                       CssUIMessages.getResourceBundle(),
+                       "CssEditor.contentAssist.", this); //$NON-NLS-1$
+               action.setActionDefinitionId(
+                       ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
+               setAction(ICssEditorActionConstants.CONTENT_ASSIST, action);
+
+               action = new CommentAction(
+                       CssUIMessages.getResourceBundle(),
+                       "CssEditor.comment.", this); //$NON-NLS-1$
+               action.setActionDefinitionId(ICssEditorActionDefinitionIds.COMMENT);
+               setAction(ICssEditorActionConstants.COMMENT, action);
+               markAsStateDependentAction(ICssEditorActionConstants.COMMENT, true);
+               markAsSelectionDependentAction(ICssEditorActionConstants.COMMENT, true);
+
+               action = new UncommentAction(
+                       CssUIMessages.getResourceBundle(),
+                       "CssEditor.uncomment.", this); //$NON-NLS-1$
+               action.setActionDefinitionId(ICssEditorActionDefinitionIds.UNCOMMENT);
+               setAction(ICssEditorActionConstants.UNCOMMENT, action);
+               markAsStateDependentAction(ICssEditorActionConstants.UNCOMMENT, true);
+               markAsSelectionDependentAction(ICssEditorActionConstants.UNCOMMENT,
+                       true);
+       }
+
+       /*
+        * @see org.eclipse.ui.texteditor.ExtendedTextEditor#configureSourceViewerDecorationSupport(SourceViewerDecorationSupport)
+        */
+       protected void configureSourceViewerDecorationSupport(
+               SourceViewerDecorationSupport support
+       ) {
+               super.configureSourceViewerDecorationSupport(support);
+
+               support.setCharacterPairMatcher(new CssPairMatcher());
+               support.setMatchingCharacterPainterPreferenceKeys(
+                       CssUIPreferences.EDITOR_MATCHING_BRACKETS,
+                       CssUIPreferences.EDITOR_MATCHING_BRACKETS_COLOR);
+               support.setSymbolicFontName(getFontPropertyPreferenceKey());
+       }
+
+       /*
+        * @see org.eclipse.ui.texteditor.AbstractTextEditor#getAdapter(Class)
+        */
+       public Object getAdapter(Class adapter) {
+               if (IContentOutlinePage.class.equals(adapter)) {
+                       if (outlinePage == null) {
+                               outlinePage = new CssOutlinePage(this);
+                               outlineSelectionChangedListener =
+                                       new OutlineSelectionChangedListener();
+                               outlinePage.addSelectionChangedListener(
+                                       outlineSelectionChangedListener);
+                       }
+
+                       return outlinePage;
+               }
+
+               return super.getAdapter(adapter);
+       }
+
+       /*
+        * @see org.eclipse.ui.texteditor.AbstractTextEditor#handleCursorPositionChanged()
+        */
+       protected void handleCursorPositionChanged() {
+               super.handleCursorPositionChanged();
+
+               highlightElement(computeHighlightRangeSourceReference(), false);
+               synchronizeOutlinePageSelection();
+       }
+
+       /*
+        * @see org.eclipse.ui.texteditor.ExtendedTextEditor#initializeEditor()
+        */
+       protected void initializeEditor() {
+               super.initializeEditor();
+
+               setHelpContextId(ICssUIHelpContextIds.EDITOR);
+               setPreferenceStore(CssUI.getDefault().getPreferenceStore());
+               configureInsertMode(SMART_INSERT, true);
+               setInsertMode(SMART_INSERT);
+       }
+
+       // IReconcilingParticipant Implementation ----------------------------------
+
+       /* 
+        * @see IReconcilingParticipant#reconciled()
+        */
+       public void reconciled() {
+               Shell shell = getSite().getShell();
+               if ((shell != null) && !shell.isDisposed()) {
+                       shell.getDisplay().asyncExec(new Runnable() {
+                               public void run() {
+                                       if (outlinePage != null) {
+                                               outlinePage.update();
+                                       }
+                                       synchronizeOutlinePageSelection();
+                               }
+                       });
+               }
+       }
+
+       // Public Methods ----------------------------------------------------------
+
+       /**
+        * Computes and returns the source reference that includes the caret and
+        * serves as provider for the outline page selection and the editor range
+        * indication.
+        * 
+        * @return the computed source reference
+        */
+       public ISourceReference computeHighlightRangeSourceReference() {
+               ISourceViewer sourceViewer = getSourceViewer();
+               if (sourceViewer == null) {
+                       return null;
+               }
+
+               StyledText styledText = sourceViewer.getTextWidget();
+               if ((styledText == null) || styledText.isDisposed()) {
+                       return null;
+               }
+
+               int offset = sourceViewer.getVisibleRegion().getOffset();
+               int caret = offset + styledText.getCaretOffset();
+
+               return getRuleAt(caret);
+       }
+
+       /**
+        * Informs the editor that its outliner has been closed.
+        * 
+        * TODO There must be a more elegant way to get notified when the outline 
+        *      page was closed. Otherwise move this method into an interface
+        */
+       public void outlinePageClosed() {
+               if (outlinePage != null) {
+                       outlinePage.removeSelectionChangedListener(
+                                       outlineSelectionChangedListener);
+                       outlinePage = null;
+                       resetHighlightRange();
+               }
+       }
+
+       /**
+        * Synchronizes the outliner selection with the given element position in 
+        * the editor.
+        * 
+        * @param element the java element to select
+        */
+       public void synchronizeOutlinePage(ISourceReference element) {
+               if (outlinePage != null) {
+                       outlinePage.removeSelectionChangedListener(
+                               outlineSelectionChangedListener);
+                       outlinePage.select(element);
+                       outlinePage.addSelectionChangedListener(
+                               outlineSelectionChangedListener);
+               }
+       }
+
+       /**
+        * Synchronizes the outliner selection with the currently highlighted source
+        * reference.
+        */
+       public void synchronizeOutlinePage() {
+               ISourceReference element = computeHighlightRangeSourceReference();
+               synchronizeOutlinePage(element);
+       }
+
+       // Private Methods ---------------------------------------------------------
+
+       private IRule getRuleAt(int offset) {
+               IStyleSheet styleSheet = getStyleSheet();
+               if (styleSheet == null) {
+                       return null;
+               }
+
+               return styleSheet.getRuleAt(offset);
+       }
+
+       private IStyleSheet getStyleSheet() {
+               if (getDocumentProvider() instanceof CssDocumentProvider) {
+                       CssDocumentProvider p = (CssDocumentProvider) getDocumentProvider();
+                       return p.getStyleSheet(getEditorInput());
+               }
+
+               return null;
+       }
+
+       void highlightElement(ISourceReference element, boolean moveCursor) {
+               if (element != null) {
+                       IRegion highlightRegion = element.getSourceRegion();
+                       setHighlightRange(highlightRegion.getOffset(),
+                               highlightRegion.getLength(), moveCursor);
+               } else {
+                       resetHighlightRange();
+               }
+       }
+
+       void synchronizeOutlinePageSelection() {
+               IPreferenceStore store = getPreferenceStore();
+               if (store != null) {
+                       boolean linkWithEditor = store.getBoolean(LINK_WITH_OUTLINE);
+                       if (linkWithEditor) {
+                               synchronizeOutlinePage(computeHighlightRangeSourceReference());
+                       }
+               }
+       }
+}