/******************************************************************************* * Copyright (c) Emil Crumhorn - Hexapixel.com - emil.crumhorn@gmail.com * 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: * emil.crumhorn@gmail.com - initial API and implementation *******************************************************************************/ package com.hexapixel.widgets.ribbon; import java.util.StringTokenizer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.graphics.Region; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import com.hexapixel.widgets.generic.ColorCache; import com.hexapixel.widgets.generic.Utils; public class RibbonTooltipDialog { private static Shell shell; private static Color mDivider = ColorCache.getInstance().getColor(158, 187, 221); private static Color mDividerShadow = ColorCache.getInstance().getColor(255, 255, 255); private static Color mInnerFillTop = ColorCache.getInstance().getColor(255, 251, 252); private static Color mInnerFillBottom = ColorCache.getInstance().getColor(204, 217, 234); private static Color mTextColor = ColorCache.getInstance().getColor(79, 77, 78); private static Color mTooltipBorder = ColorCache.getInstance().getColor(118, 118, 118); private static Color mTooltipShadowCornerInner = ColorCache.getInstance().getColor(131, 131, 131); private static Color mTooltipShadowCornerOuter = ColorCache.getInstance().getColor(148, 148, 148); private static Color mTooltipShadowInnerCorner = ColorCache.getInstance().getColor(186, 186, 186); public static void makeDialog(final RibbonTooltip toolTip, Point location) { if (shell != null && !shell.isDisposed()) shell.dispose(); shell = new Shell(Display.getDefault().getActiveShell(), SWT.ON_TOP | SWT.TOOL | SWT.NO_TRIM); shell.setLayout(new FillLayout()); final Composite comp = new Composite(shell, SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED); comp.addPaintListener(new PaintListener() { public void paintControl(PaintEvent e) { Region region = new Region(shell.getDisplay()); GC gc = e.gc; Rectangle bounds = comp.getBounds(); // draw borders drawBorders(gc, bounds); // this is the margins for all content int marginTop = 8; int marginLeft = 6; int marginRight = 6; int marginBottom = 12; int x = marginLeft; int y = marginTop; int xMax = 0; int yMax = 0; // == TITLE == // title is bold Font bold = null; Font old = gc.getFont(); bold = Utils.applyBoldFont(old); if (toolTip.getTitle() != null && toolTip.getTitle().length() > 0) { gc.setForeground(mTextColor); gc.setFont(bold); Point p = gc.stringExtent(toolTip.getTitle()); gc.drawString(toolTip.getTitle(), x, y, true); gc.setFont(old); y += p.y; xMax = Math.max(xMax, x + p.x); yMax = Math.max(yMax, y); } Image bigImage = toolTip.getImage(); int imageY = y; if (bigImage != null) { // draw the image, as well as tell the normal text where it will have to go depending on image size // space it first, regardless of size x += 9; Rectangle imBounds = bigImage.getBounds(); // we push it down a bit, but these don't add to the overall y position as the image // is (somewhat) horizontally aligned with the content text gc.drawImage(bigImage, x, y+12); x += imBounds.width; imageY += imBounds.height+12; } // == DRAW TEXT == int textY = y; if (toolTip.getContent() != null && toolTip.getContent().length() > 0) { // if we had an image, space out this text, otherwise a little less if (bigImage != null) x += 13; else x += 8; // first we space it vertically textY += 13; StringTokenizer st = new StringTokenizer(toolTip.getContent(), "\n"); int widestLine = 0; while (st.hasMoreTokens()) { String token = st.nextToken(); Point extent = drawText(gc, token, x, textY); textY += extent.y; widestLine = Math.max(widestLine, extent.x); } x += widestLine; } // now add the image height to Y unless the text Y was bigger y = Math.max(textY, imageY); xMax = Math.max(xMax, x); yMax = Math.max(yMax, y); if (toolTip.getHelpImage() != null && toolTip.getHelpText() != null) { y += 8; // draw divider gc.setForeground(mDivider); gc.drawLine(marginLeft, y, marginLeft+xMax, y); y++; gc.setForeground(mDividerShadow); gc.drawLine(marginLeft, y, marginLeft+xMax, y); y += 7; gc.setForeground(mTextColor); gc.setFont(bold); int curX = marginLeft; int widthUsed = 0; if (toolTip.getHelpImage() != null) { gc.drawImage(toolTip.getHelpImage(), marginLeft, y); curX += toolTip.getHelpImage().getBounds().width; curX += 9; widthUsed += curX - marginLeft; } if (toolTip.getHelpText() != null) { Point helpSize = gc.stringExtent(toolTip.getHelpText()); gc.drawString(toolTip.getHelpText(), curX, y, true); widthUsed += helpSize.x; } xMax = Math.max(xMax, widthUsed); yMax = Math.max(yMax, y); } xMax += marginLeft + marginRight; yMax += marginTop + marginBottom; region.add(0, 0, xMax, yMax); region.subtract(0, 0, 1, 1); region.subtract(xMax-1, yMax-1, 1, 1); region.subtract(0, yMax-1, 1, 1); region.subtract(xMax-1, 0, 1, 1); shell.setRegion(region); Rectangle size = region.getBounds(); shell.setSize(size.width, size.height); if (bold != null) bold.dispose(); } }); shell.pack(); shell.setLocation(location); shell.setVisible(true); } private static void drawBorders(GC gc, Rectangle bounds) { gc.setForeground(mInnerFillTop); gc.setBackground(mInnerFillBottom); gc.fillGradientRectangle(bounds.x, bounds.y, bounds.width, bounds.height, true); // draw border gc.setForeground(mTooltipBorder); gc.drawRectangle(bounds.x, bounds.y, bounds.width-1, bounds.height-1); // what would would the world be without faded gradient corners? boring! so let's draw a few. // draw corners gc.setForeground(mTooltipShadowCornerOuter); // top left gc.drawLine(bounds.x+1, bounds.y, bounds.x+1, bounds.y); gc.drawLine(bounds.x, bounds.y+1, bounds.x, bounds.y+1); // top right gc.drawLine(bounds.x+bounds.width-2, bounds.y, bounds.x+bounds.width-2, bounds.y); gc.drawLine(bounds.x+bounds.width-1, bounds.y+1, bounds.x+bounds.width-1, bounds.y+1); // bottom right gc.drawLine(bounds.x+bounds.width-1, bounds.y+bounds.height-2, bounds.x+bounds.width-1, bounds.y+bounds.height-2); gc.drawLine(bounds.x+bounds.width-2, bounds.y+bounds.height-1, bounds.x+bounds.width-2, bounds.y+bounds.height-1); // bottom left gc.drawLine(bounds.x+1, bounds.y+bounds.height-1, bounds.x+1, bounds.y+bounds.height-1); gc.drawLine(bounds.x, bounds.y+bounds.height-2, bounds.x, bounds.y+bounds.height-2); // shadowed corner inside the above gc.setForeground(mTooltipShadowCornerInner); // top left gc.drawLine(bounds.x+2, bounds.y, bounds.x+2, bounds.y); gc.drawLine(bounds.x, bounds.y+2, bounds.x, bounds.y+2); // top right gc.drawLine(bounds.x+bounds.width-3, bounds.y, bounds.x+bounds.width-3, bounds.y); gc.drawLine(bounds.x+bounds.width-1, bounds.y+2, bounds.x+bounds.width-1, bounds.y+2); // bottom right gc.drawLine(bounds.x+bounds.width-1, bounds.y+bounds.height-3, bounds.x+bounds.width-1, bounds.y+bounds.height-3); gc.drawLine(bounds.x+bounds.width-3, bounds.y+bounds.height-1, bounds.x+bounds.width-3, bounds.y+bounds.height-1); // bottom left gc.drawLine(bounds.x+2, bounds.y+bounds.height-1, bounds.x+2, bounds.y+bounds.height-1); gc.drawLine(bounds.x, bounds.y+bounds.height-3, bounds.x, bounds.y+bounds.height-3); // draw inner corner pixel in each corner gc.setForeground(mTooltipShadowInnerCorner); // top left gc.drawLine(bounds.x+1, bounds.y+1, bounds.x+1, bounds.y+1); // top right gc.drawLine(bounds.x+bounds.width-2, bounds.y+1, bounds.x+bounds.width-2, bounds.y+1); // bottom right gc.drawLine(bounds.x+bounds.width-2, bounds.y+bounds.height-2, bounds.x+bounds.width-2, bounds.y+bounds.height-2); // bottom left gc.drawLine(bounds.x+1, bounds.y+bounds.height-2, bounds.x+1, bounds.y+bounds.height-2); } private static Point drawText(GC gc, String text, int x, int y) { StringTokenizer sub = new StringTokenizer(text, " "); Font old = gc.getFont(); Font used = null; String oldName = old.getFontData()[0].getName(); int oldSize = (int)old.getFontData()[0].height; int curX = x; boolean bold = false; boolean italic = false; int size = oldSize; Color fg = ColorCache.getInstance().getBlack(); int maxWidth = 0; int maxHeight = 0; int tokens = sub.countTokens(); int cnt = 0; while (sub.hasMoreElements()) { String token = sub.nextToken(); if (isNormalize(token)) { bold = false; italic = false; size = oldSize; fg = ColorCache.getInstance().getBlack(); } else { int newSize = getSize(token); if (newSize != size) newSize = size; boolean newBold = isBold(token); if (bold && !isBold(token)) bold = true; else bold = newBold; boolean newItalic = isItalic(token); if (italic && !isItalic(token)) italic = true; else italic = newItalic; Color newColor = getColor(token); if (newColor != fg) fg = newColor; } if (fg != null) gc.setForeground(fg); token = cleanUp(token); int style = SWT.NORMAL; if (bold) style |= SWT.BOLD; if (italic) style |= SWT.ITALIC; used = new Font(Display.getDefault(), oldName, size, style); gc.setFont(used); gc.drawString(token, curX, y, true); int extX = gc.stringExtent(token).x + ((cnt != tokens - 1) ? gc.stringExtent(" ").x : 0); int extY = gc.stringExtent(token).y; curX += extX; maxWidth = Math.max(maxWidth, curX); maxHeight = Math.max(maxHeight, extY); if (used != null) used.dispose(); cnt++; } gc.setFont(old); return new Point(maxWidth-x, maxHeight); } private static String cleanUp(String str) { int start = str.indexOf("\\s"); if (start != -1) { String left = str.substring(0, start); String right = str.substring(start + 4, str.length()); str = left + right; } start = str.indexOf("\\c"); str = str.replaceAll("\\\\c[0-9]{9}", ""); str = str.replaceAll("\\\\x", ""); str = str.replaceAll("\\\\b", ""); return str; } private static boolean isNormalize(String str) { return str.indexOf("\\x") > -1; } private static boolean isBold(String str) { return str.indexOf("\\b") > -1; } private static boolean isItalic(String str) { return str.indexOf("\\i") > -1; } private static int getSize(String str) { int start = str.indexOf("\\s"); if (start == -1) { return -1; } String size = str.substring(start + 2, start + 4); try { return Integer.parseInt(size); } catch (Exception badParse) { badParse.printStackTrace(); } return -1; } private static Color getColor(String str) { int start = str.indexOf("\\c"); if (start == -1) return null; int r = Integer.parseInt(str.substring(start + 2, start + 5)); int g = Integer.parseInt(str.substring(start + 5, start + 8)); int b = Integer.parseInt(str.substring(start + 8, start + 11)); return ColorCache.getInstance().getColor(r, g, b); } public static void kill() { if (shell != null && shell.isDisposed() == false) { shell.dispose(); } } public static boolean isActive() { return (shell != null && !shell.isDisposed()); } }