Return to site

How to Wrap Text inside the Column in SWT Java-Based Framework?

Does Enable to wrap text within JFace TableViewerColumn?

· Technology,Java

Introduction

SWT stands for Software Widget Toolkit, its Java-based open source framework initially developed by IBM now maintained by the Eclipse community.

Eclipse itself is developed in SWT framework. This framework is used to develop the desktop applications. It resembles Java Swings, SWT.

Main topics in this framework are Shell, Display, Perspective, Plugin, Composite, Group…

You can search plenty of tutorials on the internet

@http://www.vogella.com/tutorials/SWT/article.html

@http://www.java2s.com/Tutorial/Java/0280__SWT/Catalog0280__SWT.htm

@https://www.eclipse.org/swt/examples.php

Problem:

The Typical SWT desktop application will be in the form tree structure or table. There is a functionality that the text inside the cell should be wrapped if you resize the cell. SWT framework does not give this functionality directly so that we can start on a fly. So we have to create our own implementation on top of SWT framework. In general, if the values inside the column are larger than the column size, then SWT display it as dots appending at the last positions of the column. If we want to see the entire text, then we have to drag the header and see it. If we want to see the entire text, then without dragging the column header, then we have to start our customized label providers because SWT does not support this feature directly.

Solution:

We have solved this issue by implementing our own label provider. We will explain in the sample code, that's how we solved this issue. One should have good knowledge about the SWT framework, then only he can understand this solution. The below code sample will give the solution for this problem. One needs to have SWT, SWTX jar files to run this example. First Create a table using SWT, set the label provider and make the paint listener so that column size will be bigger.

Code snippet to create table:

Paint Listener:

privatefinal Listener paintListener = new Listener()

{
intheightValue = 69;
intimgX = 40;
privateintimgY = 5;;
privateinttxtX = 4;
privateinttxtY = 5;
@Override
publicvoidhandleEvent(Event event) {
finalTableItemitem = (TableItem) event.item;
switch (event.type) {
caseSWT.MeasureItem: {
String itemText = item.getText(event.index);
Point size = event.gc.textExtent(itemText);
event.width = size.x;
event.height = heightValue;
break;
}
caseSWT.PaintItem: {
final String itemText = item.getText(event.index);
final Image img = item.getImage(event.index);
finalintoffset2 = 0;
intoffsetx = 0;
if (img != null) {
event.gc.drawImage(img, event.x + imgX, event.y + imgY + offset2);
offsetx = 19;
}
if (itemText != null) {
event.gc.drawText(itemText, event.x + txtX + offsetx, event.y + txtY + offset2, true);
}
break;
}
caseSWT.EraseItem: {
event.detail&= ~SWT.FOREGROUND;
break;
}
default:
break;
}
}
<strong>Table Creation:</strong>
tableViewer = newTableViewer(container, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI);
tableViewer.setContentProvider(newContentProvider());
ColumnViewerToolTipSupport.enableFor(tableViewer, ToolTip.RECREATE);
table = tableViewer.getTable();
finalFormDatafdFroTable = newFormData();
fdFroTable.top = newFormAttachment(0, 5);
fdFroTable.left = newFormAttachment(0, 4);
fdFroTable.right = newFormAttachment(100, -15);
fdFroTable.bottom = newFormAttachment(100, -35);
table.setLayoutData(fdFroTable);
table.setLinesVisible(true);
table.setHeaderVisible(true);
shell.setSize(400, 400);
shell.open();
finalTableViewerColumntableViewerColumn = newTableViewerColumn(tableViewer, SWT.NONE);
finalTableColumntableColumn = tableViewerColumn.getColumn();
tableColumn.setWidth(100);
ValueLabelProviderlabelProvider1 = newValueLabelProvider(0);
tableViewerColumn.setLabelProvider(labelProvider1);
tableColumn.setText("Column1");
tableViewerColumn.setLabelProvider(newTextWrapperLabelProvider(tableViewer, 0, labelProvider1));
tableViewerColumn.getColumn().addControlListener(newControlAdapter() {
@Override
publicvoidcontrolResized(ControlEvente) {
tableViewer.refresh(true);
}
});
addColResizeListener(tableColumn);
Collection<ValuObject>valuObjects = newArrayList<>();
for (inti = 0; i< 3; i++) {
ValuObjectvaluObject = newValuObject();
valuObject.setValue1(" Hi this is test program for text wrapper example" + i);
valuObjects.add(valuObject);
}
table.addListener(SWT.MeasureItem, paintListener);
table.addListener(SWT.PaintItem, paintListener);
table.addListener(SWT.EraseItem, paintListener);
tableViewer.setInput(valuObjects);


Set the label to provide for each column otherwise it will throw exception and use this label provider to get its corresponding values while wrapping.


Set text wrapper label provider, so that text will be wrapped for each change in the header of the column.Don't forget to add Resize listener, otherwise, label provider will not be called for each change.


finalstaticclassValueLabelProviderextendsColumnLabelProvider {
privatefinalintcolumnIndex;
protectedValueLabelProvider(intcolumn) {
super();
this.columnIndex = column;
}
/**
* {@inheritDoc}
*/
@Override
public String getText(Object element) {
String text = null;
finalValuObjectvaluObject = (ValuObject) element;
switch (columnIndex) {
case 0:
text = valuObject.getValue1();
break;
default:
text = null;
}
returntext;
}
}
<strong>Content Provider:</strong>
/**
* The Class ContentProvider.
*/
publicclassContentProviderimplementsIStructuredContentProvider {
@Override
publicvoid dispose() {
}
@Override
@SuppressWarnings("rawtypes")
public Object[] getElements(Object inputElement) {
if (inputElementinstanceof Collection) {
return ((Collection) inputElement).toArray();
} else {
returnnull;
}
}
@Override
publicvoidinputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
}
publicclassTextWrapperLabelProviderextendsColumnLabelProvider {
privatefinalintcolumnIndex;
privatefinalColumnViewerviewer;
privateColumnLabelProviderlabelProvider;
publicTextWrapperLabelProvider(ColumnViewerviewer, intindex, ColumnLabelProviderlp) {
this.labelProvider = lp;
this.viewer = viewer;
this.columnIndex = index;
}
/**
* {@inheritDoc}
*/
@Override
public String getText(Object element) {
GC gc = null;
try {
gc = new GC(viewer.getControl());
intcolumnWidth = getColumnWidth();
intcolumnHeight = findHeight();
String columnText = "";
Font columnFont = null;
columnText = labelProvider.getText(element);
columnFont = labelProvider.getFont(element);
gc.setFont(columnFont);
final String text = TextWrapperExample.wrapColumnText(gc, columnText, columnWidth, columnHeight);
returntext;
} finally {
if (gc != null) {
gc.dispose();
}
}
}
privateintfindHeight() {
intitemHeight;
if (viewerinstanceofTableViewer) {
TableViewertableViewer = (TableViewer) viewer;
final Table table = tableViewer.getTable();
itemHeight = table.getItemHeight();
} elseif (viewerinstanceofTreeViewer) {
TreeViewertreeViewer = (TreeViewer) viewer;
final Tree tree = treeViewer.getTree();
itemHeight = tree.getItemHeight();
} else {
itemHeight = 0;
}
returnitemHeight;
}
privateintgetColumnWidth() {
intwidth;
if (viewerinstanceofTableViewer) {
TableViewertableViewer = (TableViewer) viewer;
final Table table = tableViewer.getTable();
TableColumncolumn = table.getColumn(columnIndex);
width = column.getWidth() - table.getBorderWidth() - table.getBorderWidth();
} elseif (viewerinstanceofTreeViewer) {
TreeViewertreeViewer = (TreeViewer) viewer;
final Tree tree = treeViewer.getTree();
TreeColumncolumn = tree.getColumn(columnIndex);
width = column.getWidth() - tree.getBorderWidth() - tree.getBorderWidth();
} else {
width = 0;
}
returnwidth;
}
}

VERY important:

Table viewer should be refreshed for each header change. That’s why control listener for table viewer column. Create data for the table column, In this case, there is only one column, so I am setting only one column value. If you have many columns, then need to set many values to value objects After table creation without wrapper if you run the program, the output will be like this-

broken image

If you see the above screenshot the text inside the column1 is not wrapped if the text exceeds the length of the column.
 

If you set the text wrapper label provider, then text inside the table cell will be wrapped according to the column header change. After the screenshot will look below.

  • First, check the number of possible lines for the changed header size. If the number of possible lines is greater than 1 then only wrapped the text.
  • To wrap text first, the string inside the cell is spitted by delimiter either space or tab space, they will be stored inside a collection. So that they will be used while wrapping based on header size.
  • / Based on the new column header size get the text wrapper to be wrap add that to the end of the string by appending \n that is a new line so that the wrapped text segment will be displayed in a new.
  • It’s very important to add a new line, then only it will display as a new line that is like a wrapped line. SWT can handle this case.

protectedstatic String wrapColumnText(GC gc, final String inputString, intlineWidth, intitemHeight) {
intfontHeight = gc.getFontMetrics().getHeight();
intleadingAreaLenght = gc.getFontMetrics().getLeading();
intlineHeight = fontHeight - leadingAreaLenght + 4;
intnoOfLinesPossible = itemHeight / lineHeight;
if (noOfLinesPossible == 1) {
returninputString;
}
Point resizePoint = getResizedPoint(gc, inputString);
if (resizePoint.x<= lineWidth&& (itemHeight == 0 || resizePoint.y<= itemHeight)) {
returninputString;
}
intlines = 1;
Pattern p = END_LINE;
String input = p.matcher(inputString).replaceAll("\n");
List<WrappedText>wrappedTextCol = getWrappedTextCol(input);
StringBufferbuffer = newStringBuffer();
intstart = 0;
intwrappedTextIndex = -1;
while (true) {
intwrappingIndex = findWrappingTextIndex(gc, lineWidth, lineHeight, input, wrappedTextCol,
start, wrappedTextIndex);
if (wrappingIndex<= wrappedTextIndex) {
wrappingIndex++;
}
booleanisLast = wrappingIndex>= wrappedTextCol.size();
intend;
intnextStart;
if (isLast) {
end = input.length();
nextStart = end + 1;
} else {
WrappedTextwrappedText = wrappedTextCol.get(wrappingIndex);
end = wrappedText.getStartPos();
nextStart = wrappedText.getEndPos();
}
addText(buffer, input, start, end);
lines++;
if (isLast) {
break;
} else {
start = nextStart;
wrappedTextIndex = wrappingIndex;
}
if (noOfLinesPossible> 0 &&lines>= noOfLinesPossible) {
end = input.length();
addText(buffer, input, start, end);
break;
}
}
String string = buffer.toString();
returnstring;
}

Split the input by empty space or minus delimiter and add to the wrapped text collection

privatestatic List<WrappedText>getWrappedTextCol(String input) {
List<WrappedText>wrappedTextCol = newArrayList<WrappedText>();
Pattern p = Pattern.compile("[ \t]+|[^ \t\n]-|[\n]|[,]");
Matcher matcher = p.matcher(input);
WrappedTextwrappedText;
while (matcher.find()) {
booleanminus = '-' == input.charAt(matcher.end() - 1);
if (minus) {
wrappedText = newWrappedText(matcher.end(), matcher.end());
} else {
wrappedText = newWrappedText(matcher.start(), matcher.end());
}
wrappedTextCol.add(wrappedText);
}
returnwrappedTextCol;
}

 

After getting the wrapping segments check whether the changed header length is greater than the text inside the cell, then we need to wrap the text by calculating which wrapped text of collection should be wrapped.

 

To calculate which wrap text logic is as below:

 

privatestaticintfindWrappingTextIndexRec(GC gc, intlineWidth, intlineHeight, String input,
List<WrappedText>wrappingCol, inttextStartPos, intstartIndex, intendIndex) {
inttestIndex = (startIndex + endIndex) / 2;
inttextEndPos = testIndex< 0 ? textStartPos
: testIndex>= wrappingCol.size() ? input.length()
: wrappingCol.get(testIndex).getStartPos();
String text = input.substring(textStartPos, textEndPos);
intnextStart = startIndex;
intnextEnd = endIndex;
booleantooBig = checkIfStringLongerThanResize(gc, lineWidth, lineHeight, text);
if (tooBig) {
nextEnd = testIndex;
} else {
nextStart = testIndex;
}
if (nextEnd - nextStart<= 1) {
returnnextStart;
} else {
intindex = findWrappingTextIndexRec(gc, lineWidth, lineHeight, input, wrappingCol, textStartPos,
nextStart, nextEnd);
returnindex;
}
}

Helper methods:

privatestaticbooleancheckIfStringLongerThanResize(GC gc, intlineWidth, intlineHeight, String text) {

Point textSize = getResizedPoint(gc, text);

booleanwidth = textSize.x>= lineWidth;

booleanheight = textSize.y>lineHeight;

returnwidth || height;

}

privatestatic Point getResizedPoint(GC gc, String string) {

Point extend = SWTX.getCachedStringExtent(gc, string);

returnextend;

}

After setting text wrapping label provider if you run the program output will be like this -

broken image
broken image

After wrapping the screen shot will look like this. If you observe the text inside the cell is wrapped according to the column header size.

Java Web Development India experts just shared the guide to wrap text inside the column in SWT Java based framework. You can try and experiment your own and share the results with our readers by commenting below.