--- /dev/null
+/*
+ * 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());
+ }
+ }
+ }
+}