/**
* Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.debug.ui;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.contentassist.IContentAssistant;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.custom.BidiSegmentEvent;
import org.eclipse.swt.custom.BidiSegmentListener;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.texteditor.AbstractTextEditor;
import org.python.pydev.editor.autoedit.DefaultIndentPrefs;
import org.python.pydev.editor.codefolding.PyAbstractIndentGuidePreferencesProvider;
import org.python.pydev.plugin.preferences.PydevPrefs;
import org.python.pydev.shared_ui.editor.BaseSourceViewer;
/**
* Source viewer for the breakpoints editor
*
* @author Fabio
*/
public class PythonSourceViewer extends BaseSourceViewer {
private Font fFont;
private Color fBackgroundColor;
private Color fForegroundColor;
private IPropertyChangeListener propertyChangeListener = new IPropertyChangeListener() {
/**
* @see IPropertyChangeListener#propertyChange(PropertyChangeEvent)
*/
@Override
public void propertyChange(PropertyChangeEvent event) {
String property = event.getProperty();
if (JFaceResources.TEXT_FONT.equals(property)) {
updateViewerFont();
}
if (AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND.equals(property)
|| AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT.equals(property)
|| AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND.equals(property)
|| AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT.equals(property)) {
updateViewerColors();
}
if (affectsTextPresentation(event)) {
invalidateTextPresentation();
}
}
};
public PythonSourceViewer(Composite parent, IVerticalRuler ruler, int styles) {
super(parent, ruler, null, false, styles, new PyAbstractIndentGuidePreferencesProvider() {
@Override
public int getTabWidth() {
return DefaultIndentPrefs.get(null).getTabWidth();
}
});
StyledText text = this.getTextWidget();
text.addBidiSegmentListener(new BidiSegmentListener() {
@Override
public void lineGetSegments(BidiSegmentEvent event) {
try {
event.segments = getBidiLineSegments(event.lineOffset);
} catch (BadLocationException x) {
// ignore
}
}
});
updateViewerFont();
updateViewerColors();
getPreferenceStore().addPropertyChangeListener(propertyChangeListener);
}
/**
* Updates the viewer's font to match the preferences.
*/
private void updateViewerFont() {
IPreferenceStore store = getPreferenceStore();
if (store != null) {
FontData data = null;
if (store.contains(JFaceResources.TEXT_FONT) && !store.isDefault(JFaceResources.TEXT_FONT)) {
data = PreferenceConverter.getFontData(store, JFaceResources.TEXT_FONT);
} else {
data = PreferenceConverter.getDefaultFontData(store, JFaceResources.TEXT_FONT);
}
if (data != null) {
Font font = new Font(getTextWidget().getDisplay(), data);
applyFont(font);
if (getFont() != null) {
getFont().dispose();
}
setFont(font);
return;
}
}
// if all the preferences failed
applyFont(JFaceResources.getTextFont());
}
/**
* Sets the current font.
*
* @param font the new font
*/
private void setFont(Font font) {
fFont = font;
}
/**
* Returns the current font.
*
* @return the current font
*/
private Font getFont() {
return fFont;
}
/**
* Sets the font for the given viewer sustaining selection and scroll position.
*
* @param font the font
*/
private void applyFont(Font font) {
IDocument doc = getDocument();
if (doc != null && doc.getLength() > 0) {
Point selection = getSelectedRange();
int topIndex = getTopIndex();
StyledText styledText = getTextWidget();
styledText.setRedraw(false);
styledText.setFont(font);
setSelectedRange(selection.x, selection.y);
setTopIndex(topIndex);
styledText.setRedraw(true);
} else {
getTextWidget().setFont(font);
}
}
/**
* Updates the given viewer's colors to match the preferences.
*/
public void updateViewerColors() {
IPreferenceStore store = getPreferenceStore();
if (store != null) {
StyledText styledText = getTextWidget();
if (styledText == null || styledText.isDisposed()) {
return;
}
Color color = store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT) ? null
: createColor(store, AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND, styledText.getDisplay());
styledText.setForeground(color);
if (getForegroundColor() != null) {
getForegroundColor().dispose();
}
setForegroundColor(color);
color = store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT) ? null
: createColor(store, AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND, styledText.getDisplay());
styledText.setBackground(color);
if (getBackgroundColor() != null) {
getBackgroundColor().dispose();
}
setBackgroundColor(color);
}
}
/**
* Creates a color from the information stored in the given preference store. Returns <code>null</code> if there is no such information available.
*/
private Color createColor(IPreferenceStore store, String key, Display display) {
RGB rgb = null;
if (store.contains(key)) {
if (store.isDefault(key)) {
rgb = PreferenceConverter.getDefaultColor(store, key);
} else {
rgb = PreferenceConverter.getColor(store, key);
}
if (rgb != null) {
return new Color(display, rgb);
}
}
return null;
}
/**
* Returns the current background color.
*
* @return the current background color
*/
protected Color getBackgroundColor() {
return fBackgroundColor;
}
/**
* Sets the current background color.
*
* @param backgroundColor the new background color
*/
protected void setBackgroundColor(Color backgroundColor) {
fBackgroundColor = backgroundColor;
}
/**
* Returns the current foreground color.
*
* @return the current foreground color
*/
protected Color getForegroundColor() {
return fForegroundColor;
}
/**
* Sets the current foreground color.
*
* @param foregroundColor the new foreground color
*/
protected void setForegroundColor(Color foregroundColor) {
fForegroundColor = foregroundColor;
}
/**
* Returns the preference store used to configure this source viewer. The JDISourceViewer uses the Java UI preferences.
*
* @return the Java UI preferences
*/
protected IPreferenceStore getPreferenceStore() {
return PydevPrefs.getChainedPrefStore();
}
/**
* @see AbstractTextEditor#affectsTextPresentation(PropertyChangeEvent)
*/
protected boolean affectsTextPresentation(PropertyChangeEvent event) {
return true;
}
/**
* Returns the current content assistant.
*
* @return the current content assistant
*/
public IContentAssistant getContentAssistant() {
return fContentAssistant;
}
/**
* Returns a segmentation of the line of the given document appropriate for bidi rendering. The default implementation returns only the string literals of a Java code line as segments.
*
* @param document the document
* @param lineOffset the offset of the line
* @return the line's bidi segmentation
* @throws BadLocationException in case lineOffset is not valid in document
*/
protected int[] getBidiLineSegments(int lineOffset) throws BadLocationException {
IDocument document = getDocument();
if (document == null) {
return null;
}
IRegion line = document.getLineInformationOfOffset(lineOffset);
ITypedRegion[] linePartitioning = document.computePartitioning(lineOffset, line.getLength());
/*
* List segmentation= new ArrayList(); for (int i= 0; i < linePartitioning.length; i++) { // if (IJavaPartitions.JAVA_STRING.equals(linePartitioning[i].getType())) //
* segmentation.add(linePartitioning[i]); }
*
*
* if (segmentation.size() == 0) return null;
*/
int size = linePartitioning.length;
int[] segments = new int[size * 2 + 1];
int j = 0;
for (int i = 0; i < size; i++) {
// ITypedRegion segment= (ITypedRegion) segmentation.get(i);
ITypedRegion segment = linePartitioning[i];
if (i == 0) {
segments[j++] = 0;
}
int offset = segment.getOffset() - lineOffset;
if (offset > segments[j - 1]) {
segments[j++] = offset;
}
if (offset + segment.getLength() >= line.getLength()) {
break;
}
segments[j++] = offset + segment.getLength();
}
if (j < segments.length) {
int[] result = new int[j];
System.arraycopy(segments, 0, result, 0, j);
segments = result;
}
return segments;
}
/**
* Disposes the system resources currently in use by this viewer.
*/
public void dispose() {
if (getFont() != null) {
getFont().dispose();
setFont(null);
}
if (getBackgroundColor() != null) {
getBackgroundColor().dispose();
setBackgroundColor(null);
}
if (getForegroundColor() != null) {
getForegroundColor().dispose();
setForegroundColor(null);
}
getPreferenceStore().removePropertyChangeListener(propertyChangeListener);
}
}