Draw texts with TextStyle support

Change-Id: I1c65c8810ac7b182d6693936b288bfd131cf11f7
diff --git a/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/figures/GFFigureUtil.java b/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/figures/GFFigureUtil.java
index ac76666..7a91b4c 100644
--- a/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/figures/GFFigureUtil.java
+++ b/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/figures/GFFigureUtil.java
@@ -24,13 +24,21 @@
 import org.eclipse.draw2d.geometry.PointList;
 import org.eclipse.draw2d.geometry.Rectangle;
 import org.eclipse.draw2d.geometry.Vector;
+import org.eclipse.graphiti.mm.algorithms.AbstractText;
 import org.eclipse.graphiti.mm.algorithms.styles.GradientColoredArea;
+import org.eclipse.graphiti.mm.algorithms.styles.TextStyle;
+import org.eclipse.graphiti.mm.algorithms.styles.TextStyleRegion;
 import org.eclipse.graphiti.ui.internal.IResourceRegistry;
 import org.eclipse.graphiti.ui.internal.IResourceRegistryHolder;
+import org.eclipse.graphiti.ui.internal.config.IConfigurationProviderInternal;
 import org.eclipse.graphiti.ui.internal.util.DataTypeTransformation;
 import org.eclipse.graphiti.util.PredefinedColoredAreas;
+import org.eclipse.swt.SWT;
 import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
 import org.eclipse.swt.graphics.Path;
+import org.eclipse.swt.graphics.TextLayout;
+import org.eclipse.swt.widgets.Display;
 
 /**
  * A utility class containing static helper-methods for Graphiti figures.
@@ -621,4 +629,85 @@
 			s.y = Math.round(c.y + (float) y * vy);
 		}
 	}
+
+	protected static void drawRichText(Graphics g, String draw, int x, int y,
+			IConfigurationProviderInternal configurationProvider, AbstractText text) {
+		drawRichText(g, draw, x, y, -1, false, 0, configurationProvider, text);
+	}
+
+	protected static void drawRichText(Graphics g, String draw, int x, int y, int bidiLevel, boolean mirrored,
+			int currentOffset, IConfigurationProviderInternal configurationProvider, AbstractText text) {
+		if (bidiLevel == -1) {
+			TextLayout tl = new TextLayout(Display.getDefault());
+			if (mirrored)
+				tl.setOrientation(SWT.RIGHT_TO_LEFT);
+			tl.setFont(g.getFont());
+			tl.setText(draw);
+			List<Font> fontsToDispose = new ArrayList<Font>();
+
+			for (TextStyleRegion style : text.getStyleRegions()) {
+				int start = style.getStart() - currentOffset;
+				int end = style.getEnd() - currentOffset;
+				if (start >= draw.length())
+					continue;
+				if (end < 0)
+					continue;
+
+				org.eclipse.swt.graphics.TextStyle textStyle = new org.eclipse.swt.graphics.TextStyle();
+				TextStyle gTextStyle = style.getStyle();
+
+				textStyle.underline = gTextStyle.isUnderline();
+				textStyle.strikeout = gTextStyle.isStrikeout();
+				textStyle.underlineStyle = gTextStyle.getUnderlineStyle().getValue();
+
+				org.eclipse.graphiti.mm.algorithms.styles.Font font = gTextStyle.getFont();
+				if (font != null) {
+					textStyle.font = DataTypeTransformation.toSwtFont(font);
+					fontsToDispose.add(textStyle.font);
+				}
+
+				org.eclipse.graphiti.mm.algorithms.styles.Color foreground = gTextStyle.getForeground();
+				if (foreground != null) {
+					textStyle.foreground = DataTypeTransformation.toSwtColor(
+							configurationProvider.getResourceRegistry(), foreground);
+				}
+
+				org.eclipse.graphiti.mm.algorithms.styles.Color background = gTextStyle.getBackground();
+				if (background != null) {
+					textStyle.background = DataTypeTransformation.toSwtColor(
+							configurationProvider.getResourceRegistry(), background);
+				}
+
+				org.eclipse.graphiti.mm.algorithms.styles.Color underlineColor = gTextStyle.getUnderlineColor();
+				if (underlineColor != null) {
+					textStyle.underlineColor = DataTypeTransformation.toSwtColor(
+							configurationProvider.getResourceRegistry(), underlineColor);
+				}
+
+				org.eclipse.graphiti.mm.algorithms.styles.Color strikeoutColor = gTextStyle.getStrikeoutColor();
+				if (strikeoutColor != null) {
+					textStyle.strikeoutColor = DataTypeTransformation.toSwtColor(
+							configurationProvider.getResourceRegistry(), strikeoutColor);
+				}
+
+				tl.setStyle(textStyle, start, end);
+			}
+
+			g.drawTextLayout(tl, x, y);
+			tl.dispose();
+			for (Font font : fontsToDispose) {
+				font.dispose();
+			}
+
+		} else {
+			TextLayout tl = new TextLayout(Display.getDefault());
+			if (mirrored)
+				tl.setOrientation(SWT.RIGHT_TO_LEFT);
+			tl.setFont(g.getFont());
+			tl.setText(draw);
+			g.drawTextLayout(tl, x, y);
+			tl.dispose();
+		}
+	}
+
 }
diff --git a/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/figures/GFMultilineText.java b/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/figures/GFMultilineText.java
index 3b55d11..5406966 100644
--- a/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/figures/GFMultilineText.java
+++ b/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/figures/GFMultilineText.java
@@ -23,6 +23,7 @@
 import org.eclipse.draw2d.StackLayout;
 import org.eclipse.draw2d.text.ParagraphTextLayout;
 import org.eclipse.graphiti.mm.algorithms.MultiText;
+import org.eclipse.graphiti.ui.internal.parts.IPictogramElementDelegate;
 
 /**
  * @noinstantiate This class is not intended to be instantiated by clients.
@@ -34,11 +35,12 @@
 
 	private GFFlowPage flowPage;
 
-	public GFMultilineText(MultiText multiText) {
+	public GFMultilineText(IPictogramElementDelegate pictogramElementDelegate, MultiText multiText) {
 		setBorder(new MarginBorder(2));
 
-		textFlow = new GFTextFlow(multiText);
+		textFlow = new GFTextFlow(multiText, pictogramElementDelegate.getConfigurationProvider());
 		textFlow.setLayoutManager(new ParagraphTextLayout(textFlow, ParagraphTextLayout.WORD_WRAP_SOFT));
+//		setText(multiText.getValue());
 
 		flowPage = new GFFlowPage(textFlow);
 		flowPage.add(textFlow);
diff --git a/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/figures/GFText.java b/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/figures/GFText.java
index 4aa2ffd..81dcf6a 100644
--- a/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/figures/GFText.java
+++ b/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/figures/GFText.java
@@ -31,6 +31,7 @@
 import org.eclipse.graphiti.mm.algorithms.Text;
 import org.eclipse.graphiti.mm.pictograms.ConnectionDecorator;
 import org.eclipse.graphiti.services.Graphiti;
+import org.eclipse.graphiti.ui.internal.config.IConfigurationProviderInternal;
 import org.eclipse.graphiti.ui.internal.parts.IPictogramElementDelegate;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.graphics.Font;
@@ -40,7 +41,7 @@
  * @noextend This class is not intended to be subclassed by clients.
  */
 public class GFText extends Label implements RotatableDecoration {
-	private GraphicsAlgorithm graphicsAlgorithm;
+	private Text text;
 
 	private int labelAlignment = CENTER;
 
@@ -51,8 +52,11 @@
 	// rotation angle if text is used as an passive connection decorator
 	private double rotationAngle = 0d;
 
-	public GFText(IPictogramElementDelegate pictogramElementDelegate, GraphicsAlgorithm graphicsAlgorithm) {
-		this.graphicsAlgorithm = graphicsAlgorithm;
+	private IConfigurationProviderInternal configurationProvider;
+
+	public GFText(IPictogramElementDelegate pictogramElementDelegate, Text text) {
+		this.text = text;
+		configurationProvider = pictogramElementDelegate.getConfigurationProvider();
 	}
 
 	@Override
@@ -62,8 +66,8 @@
 
 	@Override
 	public void paintFigure(Graphics graphics) {
-		if (graphicsAlgorithm != null && GraphitiInternal.getEmfService().isObjectAlive(graphicsAlgorithm)) {
-			double transparency = Graphiti.getGaService().getTransparency(graphicsAlgorithm, true);
+		if (text != null && GraphitiInternal.getEmfService().isObjectAlive(text)) {
+			double transparency = Graphiti.getGaService().getTransparency(text, true);
 			int alpha = (int) ((1.0 - transparency) * 255.0);
 			graphics.setAlpha(alpha);
 
@@ -72,8 +76,8 @@
 			if (graphics instanceof ScaledGraphics)
 				graphics.setTextAntialias(SWT.ON);
 
-			if (rotationAngle != 0 && graphicsAlgorithm.eContainer() instanceof ConnectionDecorator
-					&& !((ConnectionDecorator) graphicsAlgorithm.eContainer()).isActive()) {
+			if (rotationAngle != 0 && text.eContainer() instanceof ConnectionDecorator
+					&& !((ConnectionDecorator) text.eContainer()).isActive()) {
 				Rectangle rect = new Rectangle();
 				graphics.getClip(rect);
 				graphics.pushState();
@@ -87,17 +91,18 @@
 				// real clip rectangle
 				// from the angle
 				graphics.setClip(rect);
-				graphics.drawText(getSubStringText(), getTextLocation());
-
+				if (text.getStyleRegions().isEmpty()) {
+					graphics.drawText(getSubStringText(), getTextLocation());
+				} else {
+					GFFigureUtil.drawRichText(graphics, getSubStringText(), getTextLocation().x(), getTextLocation()
+							.y(), configurationProvider, text);
+				}
 				graphics.popState();
 				return;
 			}
 
 			int angle = 0;
-			if (graphicsAlgorithm instanceof Text) {
-				Text textGa = (Text) graphicsAlgorithm;
-				angle = Graphiti.getGaService().getAngle(textGa, true);
-			}
+			angle = Graphiti.getGaService().getAngle(text, true);
 
 			if (angle != 0) {
 				Rectangle rect = new Rectangle();
@@ -117,7 +122,12 @@
 				// real clip rectangle
 				// from the angle
 				graphics.setClip(rect);
-				graphics.drawText(getSubStringText(), getTextLocation());
+				if (text.getStyleRegions().isEmpty()) {
+					graphics.drawText(getSubStringText(), getTextLocation());
+				} else {
+					GFFigureUtil.drawRichText(graphics, getSubStringText(), getTextLocation().x(), getTextLocation()
+							.y(), configurationProvider, text);
+				}
 
 				bounds.height = h;
 				bounds.width = w;
@@ -128,8 +138,19 @@
 			}
 		}
 
-		super.paintFigure(graphics);
+		if (text.getStyleRegions().isEmpty()) {
+			super.paintFigure(graphics);
+		} else {
+			if (isOpaque())
+				super.paintFigure(graphics);
+			Rectangle bounds = getBounds();
+			graphics.translate(bounds.x, bounds.y);
 
+			GFFigureUtil.drawRichText(graphics, getSubStringText(), getTextLocation().x(), getTextLocation().y(),
+					configurationProvider, text);
+
+			graphics.translate(-bounds.x, -bounds.y);
+		}
 	}
 
 	@Override
@@ -184,6 +205,6 @@
 	}
 
 	public GraphicsAlgorithm getGraphicsAlgorithm() {
-		return graphicsAlgorithm;
+		return text;
 	}
 }
diff --git a/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/figures/GFTextFlow.java b/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/figures/GFTextFlow.java
index e1d695f..4b2f035 100644
--- a/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/figures/GFTextFlow.java
+++ b/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/figures/GFTextFlow.java
@@ -21,7 +21,10 @@
 import org.eclipse.draw2d.geometry.Point;

 import org.eclipse.draw2d.geometry.Rectangle;

 import org.eclipse.draw2d.text.TextFlow;

+import org.eclipse.draw2d.text.TextFragmentBox;

 import org.eclipse.graphiti.mm.algorithms.MultiText;

+import org.eclipse.graphiti.services.Graphiti;

+import org.eclipse.graphiti.ui.internal.config.IConfigurationProviderInternal;

 

 public class GFTextFlow extends TextFlow {

 

@@ -29,14 +32,22 @@
 

 	private boolean suppressText = false;

 

-	public GFTextFlow(MultiText multiText) {

+	private int currentOffset;

+

+	private IConfigurationProviderInternal configurationProvider;

+

+	public GFTextFlow(MultiText multiText, IConfigurationProviderInternal configurationProvider) {

 		this.multiText = multiText;

+		this.configurationProvider = configurationProvider;

 	}

 

 	@Override

 	public void paint(Graphics graphics) {

 

-		if (multiText.getAngle() != 0) {

+		int angle = 0;

+		angle = Graphiti.getGaService().getAngle(multiText, true);

+

+		if (angle != 0) {

 			if (getLocalBackgroundColor() != null)

 				graphics.setBackgroundColor(getLocalBackgroundColor());

 			if (getLocalForegroundColor() != null)

@@ -67,23 +78,42 @@
 	}

 

 	@Override

+	protected String getBidiSubstring(TextFragmentBox box, int index) {

+		currentOffset = box.offset;

+		return super.getBidiSubstring(box, index);

+	}

+

+	@Override

 	protected void paintText(Graphics g, String draw, int x, int y, int bidiLevel) {

 		if (suppressText) {

 			return;

 		}

-		if (bidiLevel == -1 && multiText.getAngle() != 0) {

+		int angle = 0;

+		angle = Graphiti.getGaService().getAngle(multiText, true);

+

+		if (bidiLevel == -1 && angle != 0) {

 			g.pushState();

 

 			int xOff = getParent().getBounds().width() / 2;

 			int yOff = getBounds().height() / 2;

 			g.translate(xOff, yOff);

-			g.rotate(multiText.getAngle());

+			g.rotate(angle);

 

-			g.drawText(draw, x - xOff, y - yOff);

+			if (multiText.getStyleRegions().isEmpty()) {

+				g.drawText(draw, x - xOff, y - yOff);

+			} else {

+				GFFigureUtil.drawRichText(g, draw, x - xOff, y - yOff, bidiLevel, isMirrored(), currentOffset,

+						configurationProvider, multiText);

+			}

 

 			g.popState();

 		} else {

-			super.paintText(g, draw, x, y, bidiLevel);

+			if (multiText.getStyleRegions().isEmpty()) {

+				super.paintText(g, draw, x, y, bidiLevel);

+			} else {

+				GFFigureUtil.drawRichText(g, draw, x, y, bidiLevel, isMirrored(), currentOffset, configurationProvider,

+					multiText);

+			}

 		}

 	}

 

diff --git a/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/parts/PictogramElementDelegate.java b/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/parts/PictogramElementDelegate.java
index a7a017a..3c416d6 100644
--- a/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/parts/PictogramElementDelegate.java
+++ b/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/parts/PictogramElementDelegate.java
@@ -791,9 +791,9 @@
 			} else if (graphicsAlgorithm instanceof RoundedRectangle) {
 				ret = new GFRoundedRectangle(this, graphicsAlgorithm);
 			} else if (graphicsAlgorithm instanceof MultiText) {
-				ret = new GFMultilineText((MultiText) graphicsAlgorithm);
+				ret = new GFMultilineText(this, (MultiText) graphicsAlgorithm);
 			} else if (graphicsAlgorithm instanceof Text) {
-				ret = new GFText(this, graphicsAlgorithm);
+				ret = new GFText(this, (Text) graphicsAlgorithm);
 			} else if (graphicsAlgorithm instanceof PlatformGraphicsAlgorithm) {
 				PlatformGraphicsAlgorithm pga = (PlatformGraphicsAlgorithm) graphicsAlgorithm;
 				IGraphicsAlgorithmRendererFactory factory = getGraphicsAlgorithmRendererFactory();
diff --git a/tests/org.eclipse.graphiti.bot.tests/src/org/eclipse/graphiti/bot/tests/GFOtherTests.java b/tests/org.eclipse.graphiti.bot.tests/src/org/eclipse/graphiti/bot/tests/GFOtherTests.java
index 70f1ecd..9afab19 100644
--- a/tests/org.eclipse.graphiti.bot.tests/src/org/eclipse/graphiti/bot/tests/GFOtherTests.java
+++ b/tests/org.eclipse.graphiti.bot.tests/src/org/eclipse/graphiti/bot/tests/GFOtherTests.java
@@ -1157,6 +1157,7 @@
 			robot.setAutoDelay(500);
 			try {
 				robot.keyPress(KeyEvent.VK_ALT);
+				robot.setAutoDelay(0);
 				robot.keyPress(KeyEvent.VK_LEFT);
 			} catch (RuntimeException e) {
 				fail(e.getMessage());