/*
* Copyright (c) 2013, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.tools.ui.web.yaml;
import com.google.common.collect.Lists;
import com.google.dart.tools.core.pub.PubspecException;
import com.google.dart.tools.ui.PreferenceConstants;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.ITextViewerExtension;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.IVerticalRulerExtension;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Caret;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import java.util.List;
/**
* An editor used for the source tab in Pubspec forms editor
*/
public class PubspecYamlEditor extends YamlEditor {
class FontPropertyChangeListener implements IPropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent event) {
if (getSourceViewer() == null) {
return;
}
String property = event.getProperty();
if (PreferenceConstants.EDITOR_TEXT_FONT.equals(property)) {
initializeViewerFont(getSourceViewer());
updateCaret();
return;
}
}
}
private static final String ANNOTATION_TYPE = "com.google.dart.tools.ui.pubspecIssue";
private IPropertyChangeListener fontPropertyChangeListener = new FontPropertyChangeListener();
private static final int WIDE_CARET_WIDTH = 2;
private static final int SINGLE_CARET_WIDTH = 1;
private Caret nonDefaultCaret;
private Image nonDefaultCaretImage;
private Caret initialCaret;
private final List<Annotation> lastAnnotations = Lists.newArrayList();
@Override
public void createPartControl(Composite parent) {
super.createPartControl(parent);
initialCaret = getSourceViewer().getTextWidget().getCaret();
initializeViewerFont(getSourceViewer());
updateCaret();
JFaceResources.getFontRegistry().addListener(fontPropertyChangeListener);
}
@Override
public void dispose() {
super.dispose();
if (fontPropertyChangeListener != null) {
JFaceResources.getFontRegistry().removeListener(fontPropertyChangeListener);
}
fontPropertyChangeListener = null;
initialCaret = null;
}
public void updateExceptionAnnotations(List<PubspecException> exceptions) {
IAnnotationModel annotationModel = getSourceViewer().getAnnotationModel();
for (Annotation annotation : lastAnnotations) {
annotationModel.removeAnnotation(annotation);
}
for (PubspecException exception : exceptions) {
Annotation annotation = new Annotation(ANNOTATION_TYPE, false, exception.getMessage());
lastAnnotations.add(annotation);
int offset = exception.getCharStart();
int length = exception.getCharEnd() - offset;
annotationModel.addAnnotation(annotation, new Position(offset, length));
}
}
@Override
protected void handleInsertModeChanged() {
updateInsertModeAction();
updateCaret();
updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_MODE);
}
@Override
protected boolean isPubspecEditor() {
IEditorInput input = getEditorInput();
if (input != null) {
return "pubspec.yaml".equals(input.getName());
} else {
return false;
}
}
private Caret createInsertCaret(StyledText styledText) {
Caret caret = new Caret(styledText, SWT.NULL);
caret.setSize(getCaretWidthPreference(), styledText.getLineHeight());
caret.setFont(styledText.getFont());
return caret;
}
private Caret createOverwriteCaret(StyledText styledText) {
Caret caret = new Caret(styledText, SWT.NULL);
GC gc = new GC(styledText);
Point charSize = gc.stringExtent("a"); //$NON-NLS-1$
caret.setSize(charSize.x, styledText.getLineHeight());
caret.setFont(styledText.getFont());
gc.dispose();
return caret;
}
private Caret createRawInsertModeCaret(StyledText styledText) {
if (!getLegalInsertModes().contains(SMART_INSERT)) {
return createInsertCaret(styledText);
}
Caret caret = new Caret(styledText, SWT.NULL);
Image image = createRawInsertModeCaretImage(styledText);
if (image != null) {
caret.setImage(image);
} else {
caret.setSize(getCaretWidthPreference(), styledText.getLineHeight());
}
caret.setFont(styledText.getFont());
return caret;
}
private Image createRawInsertModeCaretImage(StyledText styledText) {
PaletteData caretPalette = new PaletteData(new RGB[] {new RGB(0, 0, 0), new RGB(255, 255, 255)});
int width = getCaretWidthPreference();
int widthOffset = width - 1;
ImageData imageData = new ImageData(
4 + widthOffset,
styledText.getLineHeight(),
1,
caretPalette);
Display display = styledText.getDisplay();
Image bracketImage = new Image(display, imageData);
GC gc = new GC(bracketImage);
gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
gc.setLineWidth(0); // NOTE: 0 means width is 1 but with optimized performance
int height = imageData.height / 3;
for (int i = 0; i < width; i++) {
gc.drawLine(i, 0, i, height - 1);
gc.drawLine(i, imageData.height - height, i, imageData.height - 1);
}
gc.dispose();
return bracketImage;
}
private void disposeNonDefaultCaret() {
if (nonDefaultCaretImage != null) {
nonDefaultCaretImage.dispose();
nonDefaultCaretImage = null;
}
if (nonDefaultCaret != null) {
nonDefaultCaret.dispose();
nonDefaultCaret = null;
}
}
private int getCaretWidthPreference() {
if (getPreferenceStore() != null && getPreferenceStore().getBoolean(PREFERENCE_WIDE_CARET)) {
return WIDE_CARET_WIDTH;
}
return SINGLE_CARET_WIDTH;
}
private void initializeViewerFont(ISourceViewer viewer) {
Font font = null;
String symbolicFontName = PreferenceConstants.EDITOR_TEXT_FONT;
font = JFaceResources.getFont(symbolicFontName);
if (font == null) {
font = JFaceResources.getTextFont();
}
if (!font.equals(getSourceViewer().getTextWidget().getFont())) {
setFont(viewer, font);
} else {
font.dispose();
}
}
private void setFont(ISourceViewer sourceViewer, Font font) {
if (sourceViewer.getDocument() != null) {
ISelectionProvider provider = sourceViewer.getSelectionProvider();
ISelection selection = provider.getSelection();
int topIndex = sourceViewer.getTopIndex();
StyledText styledText = sourceViewer.getTextWidget();
Control parent = styledText;
if (sourceViewer instanceof ITextViewerExtension) {
ITextViewerExtension extension = (ITextViewerExtension) sourceViewer;
parent = extension.getControl();
}
parent.setRedraw(false);
styledText.setFont(font);
if (getVerticalRuler() instanceof IVerticalRulerExtension) {
IVerticalRulerExtension e = (IVerticalRulerExtension) getVerticalRuler();
e.setFont(font);
}
provider.setSelection(selection);
sourceViewer.setTopIndex(topIndex);
if (parent instanceof Composite) {
Composite composite = (Composite) parent;
composite.layout(true);
}
parent.setRedraw(true);
} else {
StyledText styledText = sourceViewer.getTextWidget();
styledText.setFont(font);
if (getVerticalRuler() instanceof IVerticalRulerExtension) {
IVerticalRulerExtension e = (IVerticalRulerExtension) getVerticalRuler();
e.setFont(font);
}
}
}
private void updateCaret() {
if (getSourceViewer() == null) {
return;
}
StyledText styledText = getSourceViewer().getTextWidget();
InsertMode mode = getInsertMode();
styledText.setCaret(null);
disposeNonDefaultCaret();
if (!isInInsertMode()) {
nonDefaultCaret = createOverwriteCaret(styledText);
} else if (SMART_INSERT == mode) {
nonDefaultCaret = createInsertCaret(styledText);
} else if (INSERT == mode) {
nonDefaultCaret = createRawInsertModeCaret(styledText);
}
if (nonDefaultCaret != null) {
styledText.setCaret(nonDefaultCaret);
nonDefaultCaretImage = nonDefaultCaret.getImage();
} else if (initialCaret != styledText.getCaret()) {
styledText.setCaret(initialCaret);
}
}
private void updateInsertModeAction() {
if (getSite() == null) {
return;
}
IAction action = getAction(ITextEditorActionConstants.TOGGLE_INSERT_MODE);
if (action != null) {
action.setEnabled(isInInsertMode());
action.setChecked(getInsertMode() == SMART_INSERT);
}
}
}