blob: aea2da4cd42e680977fd836c9de0b5cce68a61b2 [file] [log] [blame]
package org.eclipse.help.internal.ui;
/*
* Licensed Materials - Property of IBM,
* WebSphere Studio Workbench
* (c) Copyright IBM Corp 2000
*/
import java.text.*;
import java.util.*;
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.custom.*;
import org.eclipse.help.internal.ui.util.TString;
import org.eclipse.help.internal.contributors.ContextContributor;
/**
*/
public class InfopopText extends Canvas {
/** Text to display */
private String text;
/** Line breaker */
private static BreakIterator lineBreaker = BreakIterator.getLineInstance();
/** Lines after splitting */
ArrayList lines = new ArrayList(10);
/** Style ranges, per line */
ArrayList lineStyleRanges = new ArrayList(10);
/**
* Constructor
*/
public InfopopText(Composite parent, int style) {
super(parent, style);
//setBackground();
// cleanup any resources here
addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
//white.dispose();
}
});
addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
InfopopText.this.paintControl(e);
}
});
}
/**
* Adjusts the style ranges from a line that was split
*/
private void adjustStyles(int oldLineIndex) {
ArrayList styles = (ArrayList) lineStyleRanges.get(oldLineIndex);
if (styles.isEmpty())
lineStyleRanges.add(oldLineIndex + 1, styles);
else {
String line = (String) lines.get(oldLineIndex);
for (int i = 0; i < styles.size(); i++) {
StyleRange style = (StyleRange) styles.get(i);
if (style.start >= line.length()) {
// move all the remaining styles to the new line
ArrayList newStyles = new ArrayList(styles.size() - i + 1);
for (int j = i; j < styles.size();) {
StyleRange s = (StyleRange) styles.get(j);
styles.remove(s);
s.start = s.start - line.length();
newStyles.add(s);
}
lineStyleRanges.add(oldLineIndex + 1, newStyles);
return;
} else
if (style.start + style.length > line.length()) {
// split this style over two lines and
// move the remaining styles to the new line as well
ArrayList newStyles = new ArrayList(styles.size() - i + 1);
StyleRange splitRange1 =
new StyleRange(style.start, line.length() - style.start, null, null);
StyleRange splitRange2 =
new StyleRange(0, style.start + style.length - line.length(), null, null);
styles.remove(style);
styles.add(splitRange1);
newStyles.add(splitRange2);
for (int j = i + 1; j < styles.size();) {
StyleRange s = (StyleRange) styles.get(j);
styles.remove(s);
s.start = s.start - line.length();
newStyles.add(s);
}
lineStyleRanges.add(oldLineIndex + 1, newStyles);
return;
}
}
// all the styles were applied to the remaining of the line
lineStyleRanges.add(oldLineIndex + 1, new ArrayList(0));
}
}
private Point computeLineSize(int lineIndex, int wHint, int hHint) {
String line = (String) lines.get(lineIndex);
ArrayList styles = (ArrayList) lineStyleRanges.get(lineIndex);
if (styles.isEmpty())
return computeStringSize(line, wHint, hHint, SWT.NONE);
// Compute the styled and unstyled string sizes
int offset = 0;
StringBuffer unstyled = new StringBuffer();
StringBuffer styled = new StringBuffer();
for (Iterator iterator = styles.iterator(); iterator.hasNext();) {
StyleRange range = (StyleRange) iterator.next();
unstyled.append(line.substring(offset, range.start));
styled.append(line.substring(range.start, range.start + range.length));
offset = range.start + range.length;
}
if (offset < line.length())
unstyled.append(line.substring(offset));
String unstyledText = unstyled.toString();
String styledText = styled.toString();
Point unstyledSize = computeStringSize(unstyledText, wHint, hHint, SWT.NONE);
Point styledSize = computeStringSize(styledText, wHint, hHint, SWT.BOLD);
return new Point(
unstyledSize.x + styledSize.x,
Math.max(unstyledSize.y, styledSize.y));
}
public Point computeSize(int wHint, int hHint, boolean changed) {
Point[] lineSizes = new Point[lines.size()];
for (int i = 0; i < lineSizes.length; i++)
lineSizes[i] = computeLineSize(i, wHint, hHint);
int maxWidth = 0, maxHeight = 0;
for (int i = 0; i < lineSizes.length; i++) {
maxWidth = Math.max(maxWidth, lineSizes[i].x);
maxHeight += lineSizes[i].y;
}
return new Point(maxWidth + 2, maxHeight + 2);
}
private Point computeStringSize(
String string,
int wHint,
int hHint,
int style) {
int width = 0, height = 0;
GC gc = new GC(this);
Font defaultFont = gc.getFont();
FontData fd = gc.getFont().getFontData()[0];
fd.setStyle(SWT.BOLD);
Font boldFont = new Font(Display.getCurrent(), fd);
if (string != null) {
if (style == SWT.BOLD)
gc.setFont(boldFont);
Point extent = gc.stringExtent(string);
if (style == SWT.BOLD)
gc.setFont(defaultFont);
width += extent.x;
height = Math.max(height, extent.y);
}
if (wHint != SWT.DEFAULT)
width = wHint;
if (hHint != SWT.DEFAULT)
height = hHint;
// cleanup
boldFont.dispose();
gc.dispose();
return new Point(width, height);
}
private static int getLineBreak(String line) {
int max = getMaxLineChars();
lineBreaker.setText(line);
int lastGoodIndex = 0;
int currentIndex = lineBreaker.first();
while (currentIndex < max && currentIndex != BreakIterator.DONE) {
lastGoodIndex = currentIndex;
currentIndex = lineBreaker.next();
}
return lastGoodIndex;
}
/**
* Returns the maximum number of characters on a line
*/
private static int getMaxLineChars() {
return 72;
}
/**
* Returns the styles ranges from the text.
* Ranges are relative to the stripped text.
*/
private static ArrayList getStyleRanges(String styledText) {
ArrayList styles = new ArrayList(4);
int offset = 0;
int adjustment = 0;
int tagLength = ContextContributor.BOLD_TAG.length();
int tagEndLength = ContextContributor.BOLD_CLOSE_TAG.length();
while (offset < styledText.length()) {
int begin = styledText.indexOf(ContextContributor.BOLD_TAG, offset);
if (begin == -1) {
int end =
styledText.indexOf(ContextContributor.BOLD_CLOSE_TAG, offset + tagLength);
if (end == -1) {
break; // no tags left in the string
} else {
//.... text ... </b> ....
StyleRange range =
new StyleRange(offset - adjustment, end - offset, null, null, SWT.BOLD);
styles.add(range);
offset = end + tagEndLength;
adjustment += tagEndLength;
}
} else {
int end =
styledText.indexOf(ContextContributor.BOLD_CLOSE_TAG, offset + tagLength);
if (end == -1) {
// .... text <b> ... [EOS]
StyleRange range =
new StyleRange(
offset + tagLength - adjustment,
styledText.length() - offset - tagLength,
null,
null,
SWT.BOLD);
styles.add(range);
break; // no tags left in the string
} else {
//.... text ..<b> .. text.. </b> ....
StyleRange range =
new StyleRange(
begin - adjustment,
end - begin - tagLength,
null,
null,
SWT.BOLD);
styles.add(range);
offset = end + tagEndLength;
adjustment += tagLength + tagEndLength;
}
}
}
return styles;
}
public String getText() {
return text;
}
/**
* Returns the text without the style
*/
private static String getUnstyledText(String styledText) {
String s = TString.change(styledText, ContextContributor.BOLD_TAG, "");
s = TString.change(s, ContextContributor.BOLD_CLOSE_TAG, "");
s = s.trim();
return s;
}
/**
* Paint the control when asked for.
*/
void paintControl(PaintEvent e) {
GC gc = e.gc;
if (text != null) {
Font defaultFont = gc.getFont();
FontData fd = gc.getFont().getFontData()[0];
fd.setStyle(SWT.BOLD);
Font boldFont = new Font(Display.getCurrent(), fd);
int fontHeight = gc.getFontMetrics().getHeight();
for (int i = 0; i < lines.size(); i++) {
Point lineSize = computeLineSize(i, e.width, e.height);
String line = (String) lines.get(i);
ArrayList styles = (ArrayList) lineStyleRanges.get(i);
if (styles.isEmpty()) {
gc.drawString(line, e.x, e.y + fontHeight * i);
continue;
}
// Compute the styled and unstyled string sizes
int offset = 0;
int x = 0;
for (Iterator iterator = styles.iterator(); iterator.hasNext();) {
StyleRange range = (StyleRange) iterator.next();
String unstyled = line.substring(offset, range.start);
String styled = line.substring(range.start, range.start + range.length);
offset = range.start + range.length;
gc.drawString(unstyled, e.x + x, e.y + fontHeight * i);
x += gc.stringExtent(unstyled).x;
gc.setFont(boldFont);
gc.drawString(styled, e.x + x, e.y + fontHeight * i);
x += gc.stringExtent(styled).x;
gc.setFont(defaultFont);
}
if (offset < line.length())
gc.drawString(line.substring(offset), x, e.y + fontHeight * i);
}
boldFont.dispose();
}
}
private void processLineBreaks(String text) {
// Create the original lines with style stripped
// and keep track of styles
StringTokenizer st = new StringTokenizer(text, "\r\n");
while (st.hasMoreTokens()) {
String line = st.nextToken();
lines.add(getUnstyledText(line));
lineStyleRanges.add(getStyleRanges(line));
}
// Break long lines
for (int i = 0; i < lines.size(); i++) {
String line = (String) lines.get(i);
while (line.length() > 0) {
int linebreak = getLineBreak(line);
if (linebreak == 0 || linebreak == line.length())
break;
String newline = line.substring(0, linebreak);
lines.remove(i);
lines.add(i, newline);
line = line.substring(linebreak);
lines.add(++i, line);
i--; // may need to split more...
adjustStyles(i);
}
}
}
/**
* Set the text to display
*/
public void setText(String text) {
// Break long lines, and also update the style ranges
// if new lines have been introduced.
processLineBreaks(text);
StringBuffer sb = new StringBuffer();
Iterator it = lines.iterator();
if (it.hasNext())
sb.append((String) it.next());
while (it.hasNext())
sb.append('\n').append((String) it.next());
this.text = sb.toString();
redraw();
}
}