1
|
/*******************************************************************************
|
2
|
* Copyright (c) 2000, 2010 IBM Corporation and others.
|
3
|
* All rights reserved. This program and the accompanying materials
|
4
|
* are made available under the terms of the Eclipse Public License v1.0
|
5
|
* which accompanies this distribution, and is available at
|
6
|
* http://www.eclipse.org/legal/epl-v10.html
|
7
|
*
|
8
|
* Contributors:
|
9
|
* IBM Corporation - initial API and implementation
|
10
|
*******************************************************************************/
|
11
|
package org.eclipse.draw2d.text;
|
12
|
|
13
|
import java.util.List;
|
14
|
|
15
|
import org.eclipse.swt.graphics.Font;
|
16
|
|
17
|
/**
|
18
|
* The layout for {@link TextFlow}.
|
19
|
*
|
20
|
* @author hudsonr
|
21
|
* @since 2.1
|
22
|
*/
|
23
|
public class ParagraphTextLayout extends TextLayout {
|
24
|
|
25
|
/**
|
26
|
* Wrapping will ONLY occur at valid line breaks
|
27
|
*/
|
28
|
public static final int WORD_WRAP_HARD = 0;
|
29
|
|
30
|
/**
|
31
|
* Wrapping will always occur at the end of the available space, breaking in
|
32
|
* the middle of a word.
|
33
|
*/
|
34
|
public static final int WORD_WRAP_SOFT = 1;
|
35
|
|
36
|
/**
|
37
|
* Wrapping will always occur at the end of available space, truncating a
|
38
|
* word if it doesn't fit. Note that truncation is not supported across
|
39
|
* multiple figures and with BiDi. Undesired effects may result if that is
|
40
|
* the case.
|
41
|
*/
|
42
|
public static final int WORD_WRAP_TRUNCATE = 2;
|
43
|
|
44
|
private int wrappingStyle = WORD_WRAP_HARD;
|
45
|
|
46
|
/**
|
47
|
* Constructs a new ParagraphTextLayout on the specified TextFlow.
|
48
|
*
|
49
|
* @param flow
|
50
|
* the TextFlow
|
51
|
*/
|
52
|
public ParagraphTextLayout(TextFlow flow) {
|
53
|
super(flow);
|
54
|
}
|
55
|
|
56
|
/**
|
57
|
* Constructs the layout with the specified TextFlow and wrapping style. The
|
58
|
* wrapping style must be one of:
|
59
|
* <UL>
|
60
|
* <LI>{@link #WORD_WRAP_HARD}</LI>
|
61
|
* <LI>{@link #WORD_WRAP_SOFT}</LI>
|
62
|
* <LI>{@link #WORD_WRAP_TRUNCATE}</LI>
|
63
|
* </UL>
|
64
|
*
|
65
|
* @param flow
|
66
|
* the textflow
|
67
|
* @param style
|
68
|
* the style of wrapping
|
69
|
*/
|
70
|
public ParagraphTextLayout(TextFlow flow, int style) {
|
71
|
this(flow);
|
72
|
wrappingStyle = style;
|
73
|
}
|
74
|
|
75
|
/**
|
76
|
* Given the Bidi levels of the given text, this method breaks the given
|
77
|
* text up by its level runs.
|
78
|
*
|
79
|
* @param text
|
80
|
* the String that needs to be broken up into its level runs
|
81
|
* @param levelInfo
|
82
|
* the Bidi levels
|
83
|
* @return the requested segment
|
84
|
*/
|
85
|
private String[] getSegments(String text, int levelInfo[]) {
|
86
|
if (levelInfo.length == 1)
|
87
|
return new String[] { text };
|
88
|
|
89
|
String result[] = new String[levelInfo.length / 2 + 1];
|
90
|
|
91
|
int i;
|
92
|
int endOffset;
|
93
|
int beginOffset = 0;
|
94
|
|
95
|
for (i = 0; i < result.length - 1; i++) {
|
96
|
endOffset = levelInfo[i * 2 + 1];
|
97
|
result[i] = text.substring(beginOffset, endOffset);
|
98
|
beginOffset = endOffset;
|
99
|
}
|
100
|
endOffset = text.length();
|
101
|
result[i] = text.substring(beginOffset, endOffset);
|
102
|
return result;
|
103
|
}
|
104
|
|
105
|
class SegmentLookahead implements FlowUtilities.LookAhead {
|
106
|
private int seg = -1;
|
107
|
private String segs[];
|
108
|
private int[] width;
|
109
|
private final int trailingBorderSize;
|
110
|
|
111
|
SegmentLookahead(String segs[], int trailingBorderSize) {
|
112
|
this.segs = segs;
|
113
|
this.trailingBorderSize = trailingBorderSize;
|
114
|
}
|
115
|
|
116
|
public int getWidth() {
|
117
|
if (width == null) {
|
118
|
width = new int[1];
|
119
|
int startingIndex = seg + 1;
|
120
|
TextFlow textFlow = (TextFlow) getFlowFigure();
|
121
|
|
122
|
if (startingIndex == segs.length) {
|
123
|
width[0] += trailingBorderSize;
|
124
|
getContext().getWidthLookahead(textFlow, width);
|
125
|
} else {
|
126
|
String rest = segs[startingIndex];
|
127
|
for (int k = startingIndex + 1; k < segs.length; k++)
|
128
|
rest += segs[k];
|
129
|
if (!textFlow.addLeadingWordWidth(rest, width)) {
|
130
|
width[0] += trailingBorderSize;
|
131
|
getContext().getWidthLookahead(textFlow, width);
|
132
|
}
|
133
|
}
|
134
|
}
|
135
|
return width[0];
|
136
|
}
|
137
|
|
138
|
public void setIndex(int value) {
|
139
|
this.seg = value;
|
140
|
width = null;
|
141
|
}
|
142
|
}
|
143
|
|
144
|
/**
|
145
|
* @see org.eclipse.draw2d.text.FlowFigureLayout#layout()
|
146
|
*/
|
147
|
protected void layout() {
|
148
|
TextFlow textFlow = (TextFlow) getFlowFigure();
|
149
|
int offset = 0;
|
150
|
|
151
|
FlowContext context = getContext();
|
152
|
List fragments = textFlow.getFragments();
|
153
|
Font font = textFlow.getFont();
|
154
|
int fragIndex = 0;
|
155
|
int advance = 0;
|
156
|
|
157
|
TextFragmentBox fragment;
|
158
|
int levelInfo[] = (textFlow.getBidiInfo() == null) ? new int[] { -1 }
|
159
|
: textFlow.getBidiInfo().levelInfo;
|
160
|
|
161
|
String segment, segments[] = getSegments(textFlow.getText(), levelInfo);
|
162
|
FlowBorder border = null;
|
163
|
if (textFlow.getBorder() instanceof FlowBorder)
|
164
|
border = (FlowBorder) textFlow.getBorder();
|
165
|
|
166
|
SegmentLookahead lookahead = new SegmentLookahead(segments,
|
167
|
border == null ? 0 : border.getRightMargin());
|
168
|
int seg;
|
169
|
|
170
|
if (border != null) {
|
171
|
fragment = getFragment(fragIndex++, fragments);
|
172
|
fragment.setBidiLevel(levelInfo[0]);
|
173
|
fragment.setTruncated(false);
|
174
|
fragment.offset = fragment.length = -1;
|
175
|
fragment.setWidth(border.getLeftMargin()
|
176
|
+ border.getInsets(textFlow).left);
|
177
|
if (context.getRemainingLineWidth() < fragment.getWidth()
|
178
|
+ lookahead.getWidth())
|
179
|
context.endLine();
|
180
|
context.addToCurrentLine(fragment);
|
181
|
}
|
182
|
|
183
|
FlowUtilities flowUtilities = textFlow.getFlowUtilities();
|
184
|
for (seg = 0; seg < segments.length; seg++) {
|
185
|
segment = segments[seg];
|
186
|
lookahead.setIndex(seg);
|
187
|
|
188
|
do {
|
189
|
fragment = getFragment(fragIndex++, fragments);
|
190
|
|
191
|
fragment.offset = offset;
|
192
|
fragment.setBidiLevel(levelInfo[seg * 2]);
|
193
|
|
194
|
advance = flowUtilities.wrapFragmentInContext(fragment,
|
195
|
segment, context, lookahead, font, wrappingStyle);
|
196
|
segment = segment.substring(advance);
|
197
|
offset += advance;
|
198
|
if ((segment.length() > 0 || fragment.length < advance)
|
199
|
|| fragment.isTruncated())
|
200
|
context.endLine();
|
201
|
} while (segment.length() > 0
|
202
|
|| (!fragment.isTruncated() && fragment.length < advance));
|
203
|
}
|
204
|
|
205
|
if (border != null) {
|
206
|
fragment = getFragment(fragIndex++, fragments);
|
207
|
fragment.setBidiLevel(levelInfo[0]);
|
208
|
fragment.setTruncated(false);
|
209
|
fragment.offset = fragment.length = -1;
|
210
|
fragment.setWidth(border.getRightMargin()
|
211
|
+ border.getInsets(textFlow).right);
|
212
|
context.addToCurrentLine(fragment);
|
213
|
}
|
214
|
|
215
|
// Remove the remaining unused fragments.
|
216
|
while (fragIndex < fragments.size())
|
217
|
fragments.remove(fragments.size() - 1);
|
218
|
}
|
219
|
|
220
|
}
|