/******************************************************************************* * Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is 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: * Exadel, Inc. and Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.common.model.ui.widgets; import java.util.ArrayList; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.jboss.tools.common.util.SwtUtil; public class TextAndReferenceComponent extends Canvas implements PaintListener, MouseMoveListener { int defaultWidth = 100; int width; String text = ""; //$NON-NLS-1$ Line[] lines = new Line[0]; int lineHeight = 12; ReferenceListener listener; Font plain; Font bold; Token[] tokens = new Token[0]; public TextAndReferenceComponent(Composite parent, int style) { super (parent, style); addPaintListener(this); addMouseMoveListener(this); addMouseListener(new MA()); plain = getFont(); FontData d = plain.getFontData()[0]; bold = new Font(null, d.getName(), d.getHeight(), d.getStyle() | SWT.BOLD); SwtUtil.bindDisposal(bold, this); } public void mouseMove(MouseEvent e) { int c = (overReference(e.x, e.y)) ? SWT.CURSOR_HAND : SWT.CURSOR_ARROW; setCursor(getShell().getDisplay().getSystemCursor(c)); } private boolean overReference(int x, int y) { Token t = findToken(x, y); return t != null && t.isReference; } public Point computeSize(int wHint, int hHint, boolean changed) { width = (wHint == SWT.DEFAULT) ? defaultWidth : wHint; GC g = new GC(this); g.setFont(getFont()); lines = Tokenizer.breakIntoLines(g, tokens, width); g.dispose(); return new Point(width + 10, lines.length * lineHeight + 6); } public int getStringWidth(Font font, String str){ GC g = new GC(this); int size = getStringWidth(g, font, str); g.dispose(); return size; } public static int getStringWidth(GC g, Font font, String str){ int size = 0; g.setFont(font); for(int i = 0; i < str.length(); i++) { char c = str.charAt(i); size += g.getCharWidth(c) + 1; if(c == ' ') size += 1; } return size; } public void setText(String text, int width) { this.text = text; defaultWidth = width; this.width = width; tokens = Tokenizer.tokenize(text, plain, bold); pack(); } public void paintControl(PaintEvent ev) { if(lines.length == 0) return; GC g = ev.gc; Font f = getFont(); g.setFont(f); for (int i = 0; i < lines.length; i++) { int y = 2 + i * lineHeight; lines[i].paint(tokens, g, y); } } public void addReferenceListener(ReferenceListener listener) { this.listener = listener; } class MA extends MouseAdapter { public void mouseDown(MouseEvent e) { Token t = findToken(e.x, e.y); if(t != null && listener != null && t.isReference) { listener.referenceSelected(t.text); } } } Token findToken(int x, int y) { for (int i = 0; i < tokens.length; i++) if(tokens[i].contains(x, y)) return tokens[i]; return null; } } class Context { ArrayList<Token> l = new ArrayList<Token>(); StringBuffer word = new StringBuffer(); boolean isReference = false; boolean isBold = false; String text; int i = -1; char c = '\0'; Font plain; Font bold; boolean nextChar() { ++i; if(i >= text.length()) return false; c = text.charAt(i); return true; } void appendToWord() { word.append(c); } boolean isReferenceStart() { boolean b = (c == '<' && text.indexOf("<a>", i) == i && !isReference); //$NON-NLS-1$ if(b) { getLastToken(); isReference = true; i += 2; } return b; } boolean isReferenceEnd() { boolean b = (c == '<' && text.indexOf("</a>", i) == i && isReference); //$NON-NLS-1$ if(b) { getLastToken(); isReference = false; i += 3; } return b; } boolean isBoldStart() { boolean b = (c == '<' && text.indexOf("<b>", i) == i && !isBold); //$NON-NLS-1$ if(b) { getLastToken(); isBold = true; i += 2; } return b; } boolean isBoldEnd() { boolean b = (c == '<' && text.indexOf("</b>", i) == i && isBold); //$NON-NLS-1$ if(b) { getLastToken(); isBold = false; i += 3; } return b; } boolean isWordEnd() { boolean b = (c == ' '); if(b) { getLastToken(); } return b; } boolean isBreakLine() { boolean b = (c == '\n'); if(b) { getLastToken(); l.add(Token.createBreakToken()); } return b; } String readLastWord() { String w = word.toString(); word.setLength(0); return w; } Token getLastToken() { String w = readLastWord(); if(w.length() == 0) return null; Token t = Token.createToken(w, isReference, (isBold) ? bold : plain); l.add(t); return t; } } class Tokenizer { public static Token[] tokenize(String text, Font plain, Font bold) { Context context = new Context(); context.bold = bold; context.plain = plain; context.text = text; while(context.nextChar()) { if(context.isBreakLine()) { } else if(context.isWordEnd()) { } else if(context.isReferenceStart()) { } else if(context.isReferenceEnd()) { } else if(context.isBoldStart()) { } else if(context.isBoldEnd()) { } else { context.appendToWord(); } } context.getLastToken(); return (Token[])context.l.toArray(new Token[0]); } public static Line[] breakIntoLines(GC g, Token[] tokens, int width) { ArrayList<Line> l = new ArrayList<Line>(); Line line = null; int x = 0; for (int i = 0; i < tokens.length; i++) { if(line == null) { line = new Line(); line.firstToken = i; line.lastToken = i; x = 0; } if(tokens[i].isBreak) { l.add(line); line = null; } else { int dx = TextAndReferenceComponent.getStringWidth(g, tokens[i].font, tokens[i].text); if(x > 0 && x + 5 + dx > width) { l.add(line); line = null; --i; } else { line.lastToken = i; if(x == 0) x = dx; else x = x + 5 + dx; } } } if(line != null) l.add(line); return (Line[])l.toArray(new Line[0]); } } class Line { int firstToken = -1; int lastToken = -1; public void paint(Token[] tokens, GC gc, int y) { int x = 0; for (int i = firstToken; i <= lastToken && i < tokens.length; i++) { x = tokens[i].paint(gc, y, x) + 4; } } } class Token { String text = ""; //$NON-NLS-1$ boolean isReference = false; boolean isBreak = false; Rectangle r = new Rectangle(0, 0, 0, 0); Font font; public int paint(GC g, int y, int x) { if(isBreak) return x; g.setFont(font); r.x = x - 2; r.y = y; int color = (isReference) ? SWT.COLOR_BLUE : SWT.COLOR_BLACK; g.setForeground(Display.getDefault().getSystemColor(color)); g.drawString(text, x, y); int w = TextAndReferenceComponent.getStringWidth(g, g.getFont(), text); int x1 = x; int x2 = x1 + w + 2; int y1 = y + g.getFont().getFontData()[0].getHeight() + 4; if(isReference) g.drawLine(x1 - 2, y1, x2, y1); r.x = x1; r.y = y; r.width = x2 - x1 + 2; r.height = y1 - y; return x1 + w; } public boolean contains(int x, int y) { return x >= r.x && x <= r.x + r.width && y >= r.y && y < r.y + r.height; } public static Token createBreakToken() { Token t = new Token(); t.isBreak = true; return t; } public static Token createToken(String text, boolean reference, Font font) { Token t = new Token(); t.text = text; t.isReference = reference; t.font = font; return t; } }