Merge "Bug 501951 - Add word wrapping support in text painters"
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/painter/cell/AbstractTextPainter.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/painter/cell/AbstractTextPainter.java
index f9108ff..7fc026a 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/painter/cell/AbstractTextPainter.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/painter/cell/AbstractTextPainter.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2015 Original authors and others.
+ * Copyright (c) 2012, 2016 Original authors and others.
  * 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
@@ -49,6 +49,10 @@
 
     public static final String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$
 
+    /**
+     * @since 1.5
+     */
+    protected boolean wordWrapping = false;
     protected boolean wrapText;
     protected final boolean paintBg;
     protected boolean paintFg = true;
@@ -334,7 +338,31 @@
 
         // take the whole width of the text
         int textLength = getLengthFromCache(gc, text);
-        if (this.calculateByTextLength && this.wrapText) {
+
+        if (this.wordWrapping) {
+            if (availableLength < textLength) {
+                String[] lines = text.split(NEW_LINE_REGEX);
+                for (String textLine : lines) {
+                    if (output.length() > 0) {
+                        output.append(LINE_SEPARATOR);
+                    }
+
+                    StringBuilder line = new StringBuilder();
+                    for (char c : textLine.toCharArray()) {
+                        line.append(c);
+                        int length = getLengthFromCache(gc, line.toString());
+                        if (length >= availableLength) {
+                            output.append(line.substring(0, line.length() - 1)).append(LINE_SEPARATOR);
+                            line = new StringBuilder();
+                            line.append(c);
+                        }
+                    }
+                    output.append(line);
+                }
+            } else {
+                output.append(text);
+            }
+        } else if (this.calculateByTextLength && this.wrapText) {
             if (availableLength < textLength) {
                 // calculate length by finding the longest word in text
                 textLength = (availableLength - (2 * this.spacing));
@@ -397,7 +425,7 @@
     }
 
     /**
-     * This method gets only called if word wrapping is enabled. Concatenates
+     * This method gets only called if text wrapping is enabled. Concatenates
      * the two given words by taking the availableSpace into account. If
      * concatenating those two words with a space as delimiter does fit into the
      * available space the return value is exactly this. Else instead of a space
@@ -716,4 +744,54 @@
         }
     }
 
+    /**
+     * Return whether word wrapping is enabled or not.
+     * <p>
+     * Word wrapping is the wrapping behavior similar to spreadsheet
+     * applications where words are wrapped if there is not enough space. Text
+     * wrapping on the other hand only wraps whole words.
+     * </p>
+     * <p>
+     * Enabling this feature could result in slow rendering performance. It is
+     * therefore disabled by default.
+     * </p>
+     * <p>
+     * <b>Note:</b> If word wrapping is enabled, features like automatic size
+     * calculation by text length and text wrapping are ignored.
+     * </p>
+     *
+     * @return <code>true</code> if word wrapping is enabled, <code>false</code>
+     *         if not.
+     *
+     * @since 1.5
+     */
+    public boolean isWordWrapping() {
+        return this.wordWrapping;
+    }
+
+    /**
+     * Configure whether word wrapping should be enabled or not.
+     * <p>
+     * Word wrapping is the wrapping behavior similar to spreadsheet
+     * applications where words are wrapped if there is not enough space. Text
+     * wrapping on the other hand only wraps whole words.
+     * </p>
+     * <p>
+     * Enabling this feature could result in slow rendering performance. It is
+     * therefore disabled by default.
+     * </p>
+     * <p>
+     * <b>Note:</b> If word wrapping is enabled, features like automatic size
+     * calculation by text length and text wrapping are ignored.
+     * </p>
+     *
+     * @param wordWrapping
+     *            <code>true</code> to enable word wrapping, <code>false</code>
+     *            to disable it.
+     *
+     * @since 1.5
+     */
+    public void setWordWrapping(boolean wordWrapping) {
+        this.wordWrapping = wordWrapping;
+    }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.examples.e4.product/org.eclipse.nebula.widgets.nattable.examples.e4.product b/org.eclipse.nebula.widgets.nattable.examples.e4.product/org.eclipse.nebula.widgets.nattable.examples.e4.product
index 552796a..31b6d73 100644
--- a/org.eclipse.nebula.widgets.nattable.examples.e4.product/org.eclipse.nebula.widgets.nattable.examples.e4.product
+++ b/org.eclipse.nebula.widgets.nattable.examples.e4.product/org.eclipse.nebula.widgets.nattable.examples.e4.product
@@ -31,7 +31,7 @@
       <feature id="org.eclipse.emf.ecore"/>
       <feature id="org.eclipse.emf.common"/>
       <feature id="org.eclipse.nebula.widgets.nattable.core.feature" version="1.5.0.qualifier"/>
-      <feature id="org.eclipse.nebula.widgets.nattable.extension.e4.feature" version="1.0.0.qualifier"/>
+      <feature id="org.eclipse.nebula.widgets.nattable.extension.e4.feature" version="1.1.0.qualifier"/>
       <feature id="org.eclipse.nebula.widgets.nattable.extension.glazedlists.feature" version="1.4.0.qualifier"/>
       <feature id="org.eclipse.nebula.widgets.nattable.examples.e4.feature" version="1.5.0.qualifier"/>
       <feature id="org.eclipse.nebula.widgets.nattable.extension.nebula.feature" version="1.1.0.qualifier"/>
diff --git a/org.eclipse.nebula.widgets.nattable.extension.e4.feature/feature.xml b/org.eclipse.nebula.widgets.nattable.extension.e4.feature/feature.xml
index 61340c6..4a185d6 100644
--- a/org.eclipse.nebula.widgets.nattable.extension.e4.feature/feature.xml
+++ b/org.eclipse.nebula.widgets.nattable.extension.e4.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.nebula.widgets.nattable.extension.e4.feature"
       label="NatTable Eclipse 4 Extension Feature"
-      version="1.0.0.qualifier">
+      version="1.1.0.qualifier">
 
    <description url="http://www.example.com/description">
       NatTable Eclipse 4 Extension
diff --git a/org.eclipse.nebula.widgets.nattable.extension.e4.source.feature/feature.xml b/org.eclipse.nebula.widgets.nattable.extension.e4.source.feature/feature.xml
index d9283d3..4a00287 100644
--- a/org.eclipse.nebula.widgets.nattable.extension.e4.source.feature/feature.xml
+++ b/org.eclipse.nebula.widgets.nattable.extension.e4.source.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.nebula.widgets.nattable.extension.e4.source.feature"
       label="NatTable Eclipse 4 Extension Source Feature"
-      version="1.0.0.qualifier">
+      version="1.1.0.qualifier">
 
    <description url="http://www.example.com/description">
       NatTable Eclipse 4 Extension Source
diff --git a/org.eclipse.nebula.widgets.nattable.extension.e4/META-INF/MANIFEST.MF b/org.eclipse.nebula.widgets.nattable.extension.e4/META-INF/MANIFEST.MF
index f9947ac..a071b31 100644
--- a/org.eclipse.nebula.widgets.nattable.extension.e4/META-INF/MANIFEST.MF
+++ b/org.eclipse.nebula.widgets.nattable.extension.e4/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: NatTable extension for Eclipse 4
 Bundle-SymbolicName: org.eclipse.nebula.widgets.nattable.extension.e4;singleton:=true
-Bundle-Version: 1.0.0.qualifier
+Bundle-Version: 1.1.0.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Require-Bundle: org.eclipse.swt;bundle-version="3.103.0",
  org.eclipse.e4.ui.css.core;bundle-version="0.11.0",
@@ -38,6 +38,6 @@
  org.eclipse.nebula.widgets.nattable.ui.util;version="[1.4.0,2.0.0)",
  org.eclipse.nebula.widgets.nattable.util;version="[1.4.0,2.0.0)"
 Bundle-ActivationPolicy: lazy
-Export-Package: org.eclipse.nebula.widgets.nattable.extension.e4.css;version="1.0.0",
- org.eclipse.nebula.widgets.nattable.extension.e4.painterfactory;version="1.0.0",
+Export-Package: org.eclipse.nebula.widgets.nattable.extension.e4.css;version="1.1.0",
+ org.eclipse.nebula.widgets.nattable.extension.e4.painterfactory;version="1.1.0",
  org.eclipse.nebula.widgets.nattable.extension.e4.selection;version="1.0.0"
diff --git a/org.eclipse.nebula.widgets.nattable.extension.e4/plugin.xml b/org.eclipse.nebula.widgets.nattable.extension.e4/plugin.xml
index 1f5188b..19d4d58 100644
--- a/org.eclipse.nebula.widgets.nattable.extension.e4/plugin.xml
+++ b/org.eclipse.nebula.widgets.nattable.extension.e4/plugin.xml
@@ -97,6 +97,9 @@
                name="text-decoration">
          </property-name>
          <property-name
+               name="word-wrap">
+         </property-name>
+         <property-name
                name="text-wrap">
          </property-name>
          <property-name
@@ -251,6 +254,9 @@
                name="text-decoration">
          </property-name>
          <property-name
+               name="word-wrap">
+         </property-name>
+         <property-name
                name="text-wrap">
          </property-name>
          <property-name
diff --git a/org.eclipse.nebula.widgets.nattable.extension.e4/src/org/eclipse/nebula/widgets/nattable/extension/e4/css/NatTableCSSConstants.java b/org.eclipse.nebula.widgets.nattable.extension.e4/src/org/eclipse/nebula/widgets/nattable/extension/e4/css/NatTableCSSConstants.java
index 92e4179..24159ba 100644
--- a/org.eclipse.nebula.widgets.nattable.extension.e4/src/org/eclipse/nebula/widgets/nattable/extension/e4/css/NatTableCSSConstants.java
+++ b/org.eclipse.nebula.widgets.nattable.extension.e4/src/org/eclipse/nebula/widgets/nattable/extension/e4/css/NatTableCSSConstants.java
@@ -185,6 +185,16 @@
      */
     String RENDER_GRID_LINES = "render-grid-lines";
     /**
+     * CSS property to specify whether words should automatically or not.
+     * Default is <code>false</code>.
+     * <p>
+     * Available values: <code>true, false</code>
+     * </p>
+     *
+     * @since 1.1
+     */
+    String WORD_WRAP = "word-wrap";
+    /**
      * CSS property to specify whether text should automatically wrapped between
      * words or not. Default is <code>false</code>.
      * <p>
diff --git a/org.eclipse.nebula.widgets.nattable.extension.e4/src/org/eclipse/nebula/widgets/nattable/extension/e4/css/NatTableCSSHandler.java b/org.eclipse.nebula.widgets.nattable.extension.e4/src/org/eclipse/nebula/widgets/nattable/extension/e4/css/NatTableCSSHandler.java
index fbd0258..162a33d 100644
--- a/org.eclipse.nebula.widgets.nattable.extension.e4/src/org/eclipse/nebula/widgets/nattable/extension/e4/css/NatTableCSSHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.extension.e4/src/org/eclipse/nebula/widgets/nattable/extension/e4/css/NatTableCSSHandler.java
@@ -536,6 +536,11 @@
                         newColor,
                         displayMode,
                         label);
+            } else if (NatTableCSSConstants.WORD_WRAP.equalsIgnoreCase(property)
+                    && (value.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE)) {
+                NatTableCSSHelper
+                        .getPainterProperties(context, displayMode)
+                        .put(NatTableCSSConstants.WORD_WRAP, NatTableCSSHelper.getBoolean(value, false));
             } else if (NatTableCSSConstants.TEXT_WRAP.equalsIgnoreCase(property)
                     && (value.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE)) {
                 NatTableCSSHelper
@@ -1190,6 +1195,24 @@
                         style.getAttributeValue(CellStyleAttributes.FOREGROUND_COLOR),
                         engine,
                         null);
+            } else if (NatTableCSSConstants.WORD_WRAP.equalsIgnoreCase(property)) {
+                Boolean wrap = Boolean.FALSE;
+
+                ICellPainter painter = natTable.getConfigRegistry().getConfigAttribute(
+                        CellConfigAttributes.CELL_PAINTER,
+                        displayMode,
+                        label);
+                if (painter != null) {
+                    while (painter instanceof CellPainterWrapper) {
+                        painter = ((CellPainterWrapper) painter).getWrappedPainter();
+                    }
+                }
+
+                if (painter instanceof AbstractTextPainter) {
+                    wrap = ((AbstractTextPainter) painter).isWordWrapping();
+                }
+
+                return wrap.toString();
             } else if (NatTableCSSConstants.TEXT_WRAP.equalsIgnoreCase(property)) {
                 Boolean wrap = Boolean.FALSE;
 
diff --git a/org.eclipse.nebula.widgets.nattable.extension.e4/src/org/eclipse/nebula/widgets/nattable/extension/e4/painterfactory/CellPainterFactory.java b/org.eclipse.nebula.widgets.nattable.extension.e4/src/org/eclipse/nebula/widgets/nattable/extension/e4/painterfactory/CellPainterFactory.java
index 16b5a36..44c334c 100644
--- a/org.eclipse.nebula.widgets.nattable.extension.e4/src/org/eclipse/nebula/widgets/nattable/extension/e4/painterfactory/CellPainterFactory.java
+++ b/org.eclipse.nebula.widgets.nattable.extension.e4/src/org/eclipse/nebula/widgets/nattable/extension/e4/painterfactory/CellPainterFactory.java
@@ -315,7 +315,7 @@
      */
     public ICellPainter getCellPainter(List<String> painterValues, Map<String, Object> painterProperties) {
         String backgroundKey = null;
-        List<String> decoratorKeys = new ArrayList<String>();
+        List<String> decoratorKeys = new ArrayList<>();
         String contentKey = null;
 
         if (isBackgroundPainterKey(painterValues.get(0))) {
@@ -493,6 +493,12 @@
      *            The painter properties to apply.
      */
     public void initTextPainter(AbstractTextPainter painter, Map<String, Object> painterProperties) {
+        boolean wrapWord = false;
+        if (painterProperties.containsKey(NatTableCSSConstants.WORD_WRAP)) {
+            wrapWord = (Boolean) painterProperties.get(NatTableCSSConstants.WORD_WRAP);
+        }
+        painter.setWordWrapping(wrapWord);
+
         boolean wrapText = false;
         if (painterProperties.containsKey(NatTableCSSConstants.TEXT_WRAP)) {
             wrapText = (Boolean) painterProperties.get(NatTableCSSConstants.TEXT_WRAP);
diff --git a/org.eclipse.nebula.widgets.nattable.extension.poi/src/org/eclipse/nebula/widgets/nattable/extension/poi/PoiExcelExporter.java b/org.eclipse.nebula.widgets.nattable.extension.poi/src/org/eclipse/nebula/widgets/nattable/extension/poi/PoiExcelExporter.java
index 823de5b..10fe2fa 100644
--- a/org.eclipse.nebula.widgets.nattable.extension.poi/src/org/eclipse/nebula/widgets/nattable/extension/poi/PoiExcelExporter.java
+++ b/org.eclipse.nebula.widgets.nattable.extension.poi/src/org/eclipse/nebula/widgets/nattable/extension/poi/PoiExcelExporter.java
@@ -273,7 +273,8 @@
 
     private boolean wrapText(ICellPainter cellPainter) {
         if (cellPainter instanceof AbstractTextPainter) {
-            return ((AbstractTextPainter) cellPainter).isWrapText();
+            return ((AbstractTextPainter) cellPainter).isWordWrapping()
+                    || ((AbstractTextPainter) cellPainter).isWrapText();
         } else if (cellPainter instanceof CellPainterWrapper) {
             return wrapText(((CellPainterWrapper) cellPainter).getWrappedPainter());
         } else if (cellPainter instanceof CellPainterDecorator) {
diff --git a/org.eclipse.nebula.widgets.nattable.updatesite/category.xml b/org.eclipse.nebula.widgets.nattable.updatesite/category.xml
index 023163d..17527c4 100644
--- a/org.eclipse.nebula.widgets.nattable.updatesite/category.xml
+++ b/org.eclipse.nebula.widgets.nattable.updatesite/category.xml
@@ -12,12 +12,6 @@
    <feature url="features/org.eclipse.nebula.widgets.nattable.extension.poi.source.feature_1.5.0.qualifier.jar" id="org.eclipse.nebula.widgets.nattable.extension.poi.source.feature" version="1.5.0.qualifier">
       <category name="nattable-extensions"/>
    </feature>
-   <feature url="features/org.eclipse.nebula.widgets.nattable.extension.e4.feature_1.0.0.qualifier.jar" id="org.eclipse.nebula.widgets.nattable.extension.e4.feature" version="1.0.0.qualifier">
-      <category name="nattable-extensions"/>
-   </feature>
-   <feature url="features/org.eclipse.nebula.widgets.nattable.extension.e4.source.feature_1.0.0.qualifier.jar" id="org.eclipse.nebula.widgets.nattable.extension.e4.source.feature" version="1.0.0.qualifier">
-      <category name="nattable-extensions"/>
-   </feature>
    <feature url="features/org.eclipse.nebula.widgets.nattable.core.feature_1.5.0.qualifier.jar" id="org.eclipse.nebula.widgets.nattable.core.feature" version="1.5.0.qualifier">
       <category name="nattable-core"/>
    </feature>
@@ -30,6 +24,12 @@
    <feature url="features/org.eclipse.nebula.widgets.nattable.extension.nebula.source.feature_1.1.0.qualifier.jar" id="org.eclipse.nebula.widgets.nattable.extension.nebula.source.feature" version="1.1.0.qualifier">
       <category name="nattable-extensions"/>
    </feature>
+   <feature url="features/org.eclipse.nebula.widgets.nattable.extension.e4.feature_1.1.0.qualifier.jar" id="org.eclipse.nebula.widgets.nattable.extension.e4.feature" version="1.1.0.qualifier">
+      <category name="nattable-extensions"/>
+   </feature>
+   <feature url="features/org.eclipse.nebula.widgets.nattable.extension.e4.source.feature_1.1.0.qualifier.jar" id="org.eclipse.nebula.widgets.nattable.extension.e4.source.feature" version="1.1.0.qualifier">
+      <category name="nattable-extensions"/>
+   </feature>
    <category-def name="nattable-core" label="NatTable Core">
       <description>
          NatTable Core plugin