/*******************************************************************************
* Copyright (c) 2013 Pivotal Software, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springsource.ide.eclipse.commons.livexp.ui;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.StyledString.Styler;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.TextStyle;
import org.eclipse.swt.widgets.Composite;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import org.springsource.ide.eclipse.commons.livexp.core.ValidationResult;
import org.springsource.ide.eclipse.commons.livexp.core.Validator;
/**
* An attempt at creating Comment section that allows styling the text
* in the comment using simple tags like <b> and <i>
*
* At present the following tags are supported:
* <b>bold</b>
*
* Other kinds of style tags not yet supported.
*
* @author Kris De Volder
*/
public class StyledCommentSection extends WizardPageSection {
private String htmlText;
private Font fBoldFont;
/**
* Helper for splitting a String into tokens. By looking for 'html tags'.
* This is an extremely dumb implementation which assumes only tags
* are "<b>" and "</b>"
*/
class TokenIzer implements Iterator<String> {
String input;
int pos;
Pattern tagPattern = Pattern.compile("\\<(\\/)?b\\>");
private Matcher matcher;
public TokenIzer(String input) {
this.input = input;
this.matcher = tagPattern.matcher(input);
}
@Override
public boolean hasNext() {
return pos < input.length();
}
@Override
public String next() {
String token;
Assert.isLegal(hasNext());
if (matcher.find(pos)) {
int nextTag = matcher.start();
if (nextTag==pos) {
//tag found at current inpit position
token = input.substring(nextTag, matcher.end());
pos = matcher.end();
} else {
//found a tag but not at current position.
token = input.substring(pos, nextTag);
pos = nextTag;
}
} else {
//no tag found until end of input
token = input.substring(pos);
pos = input.length();
}
return token;
}
@Override
public void remove() {
throw new Error("Not supported");
}
}
public StyledCommentSection(IPageWithSections owner, String htmlText) {
super(owner);
this.htmlText = htmlText;
}
private Styler createBoldStyler(final Font font) {
return new Styler() {
@Override
public void applyStyles(TextStyle textStyle) {
textStyle.font= getBoldFont(font);
}
};
}
/**
* Create the bold variant of the currently used font.
*
* @return the bold font
* @since 3.5
*/
private Font getBoldFont(Font font) {
if (fBoldFont == null) {
FontData[] data= font.getFontData();
for (int i= 0; i < data.length; i++) {
data[i].setStyle(SWT.BOLD);
}
fBoldFont= new Font(font.getDevice(), data);
}
return fBoldFont;
}
@Override
public void createContents(Composite page) {
StyledText label = new StyledText(page, SWT.READ_ONLY|SWT.WRAP) {
/**
* Prevents this control for getting keyboard focus because, since it is readonly
* it makes no sense to give it keyboard focus.
* <p>
* By doing this next widget will be given focus instead. That's usually what you want.
*/
@Override
public boolean setFocus() {
return false;
};
@Override
public boolean forceFocus() {
return false;
}
};
label.setBackground(label.getParent().getBackground());
StyledString content = new StyledString();
parse(htmlText, content, label.getFont());
label.setText(content.getString());
label.setStyleRanges(content.getStyleRanges());
label.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
if (fBoldFont!=null) {
fBoldFont.dispose();
}
}
});
GridDataFactory
.fillDefaults()
.grab(true, false)
.hint(UIConstants.DIALOG_WIDTH_HINT, SWT.DEFAULT)
.applyTo(label);
}
private void parse(String htmlText, StyledString content, Font baseFont) {
TokenIzer input = new TokenIzer(htmlText);
while (input.hasNext()) {
String token = input.next();
if ("<b>".equals(token)) {
while (input.hasNext()) {
String boldedThing = input.next();
if (boldedThing.equals("</b>")) {
break;
} else {
content.append(boldedThing, createBoldStyler(baseFont));
}
}
} else {
content.append(token);
}
}
}
@Override
public LiveExpression<ValidationResult> getValidator() {
return Validator.OK;
}
}