/* * 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 OUTLINE_LINK_WITH_EDITOR. */ 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()); } } } }