OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/gfx/render_text_linux.h" | 5 #include "ui/gfx/render_text_linux.h" |
6 | 6 |
7 #include <pango/pangocairo.h> | |
8 #include <algorithm> | |
9 | |
10 #include "base/logging.h" | |
11 #include "ui/gfx/canvas_skia.h" | |
12 #include "ui/gfx/gtk_util.h" | |
13 #include "unicode/uchar.h" | |
14 | |
15 #define U16_IS_LEAD(c) (((c)&0xfffffc00)==0xd800) | |
16 #define U16_IS_TRAIL(c) (((c)&0xfffffc00)==0xdc00) | |
oshima
2011/08/19 18:44:07
space around operators
xji
2011/08/20 00:53:05
these are actually not needed. defined in include
| |
17 // TODO(xji): massage ARGB to RGB. | |
18 #define COLOR_16_TO_32_BIT(c) ((c)/0xff * 0xffff) | |
oshima
2011/08/19 18:44:07
If this is to convert ARGB to RGB, please name acc
xji
2011/08/20 00:53:05
It is just convert R/G/B from 16 bit to 32 bit.
bu
| |
19 | |
20 // TODO(xji): index saved in upper layer are utf16 index. Pango uses utf8 index. | |
21 // Since caret_pos is used internally, we could save utf8 index for caret_pos | |
22 // to avoid conversion. | |
23 | |
7 namespace gfx { | 24 namespace gfx { |
8 | 25 |
9 RenderTextLinux::RenderTextLinux() | 26 RenderTextLinux::RenderTextLinux() |
10 : RenderText() { | 27 : layout_(NULL), |
28 layout_line_(NULL) { | |
11 } | 29 } |
12 | 30 |
13 RenderTextLinux::~RenderTextLinux() { | 31 RenderTextLinux::~RenderTextLinux() { |
32 ResetLayout(); | |
14 } | 33 } |
15 | 34 |
16 RenderText* RenderText::CreateRenderText() { | 35 RenderText* RenderText::CreateRenderText() { |
17 return new RenderTextLinux; | 36 return new RenderTextLinux; |
18 } | 37 } |
19 | 38 |
39 int RenderTextLinux::GetStringWidth() { | |
40 PangoLayout* layout = EnsureLayout(); | |
41 int width, height; | |
42 pango_layout_get_pixel_size(layout, &width, &height); | |
43 return width; | |
44 } | |
45 | |
46 void RenderTextLinux::Draw(Canvas* canvas) { | |
47 PangoLayout* layout = EnsureLayout(); | |
48 Rect bounds(display_rect()); | |
49 | |
50 // Clip the canvas to the text display area. | |
51 CanvasSkia* canvas_skia = static_cast<CanvasSkia*>(canvas); | |
oshima
2011/08/19 18:44:07
canvas->AsCanvasSkia()
xji
2011/08/20 00:53:05
Done.
| |
52 canvas_skia->ClipRectInt( | |
53 bounds.x(), bounds.y(), bounds.width(), bounds.height()); | |
54 | |
55 // TODO(xji): is this Begin/EndPlatformPaint correct? How about performance? | |
oshima
2011/08/19 18:44:07
This seems to be fine (assuming this class replace
xji
2011/08/20 00:53:05
comments removed.
| |
56 cairo_t* cr = skia::BeginPlatformPaint(canvas_skia); | |
57 cairo_save(cr); | |
58 cairo_rectangle(cr, bounds.x(), bounds.y(), bounds.width(), bounds.height()); | |
59 cairo_clip(cr); | |
60 | |
61 int text_width, text_height; | |
62 pango_layout_get_pixel_size(layout, &text_width, &text_height); | |
63 // Vertically centered. | |
64 int text_y = bounds.y() + ((bounds.height() - text_height) / 2); | |
65 int text_x = bounds.x() + GetUpdatedDisplayOffset().x(); | |
66 cairo_move_to(cr, text_x, text_y); | |
67 pango_cairo_show_layout(cr, layout); | |
68 | |
69 // Destructor. | |
70 cairo_restore(cr); | |
71 skia::EndPlatformPaint(canvas_skia); | |
72 | |
73 // Paint cursor. | |
74 bounds = GetUpdatedCursorBounds(); | |
75 if (cursor_visible() && focused() && !bounds.IsEmpty()) { | |
76 if (!bounds.IsEmpty()) | |
77 canvas->DrawRectInt(kCursorColor, | |
78 bounds.x(), | |
79 bounds.y(), | |
80 bounds.width(), | |
81 bounds.height()); | |
82 } | |
83 } | |
84 | |
85 SelectionModel RenderTextLinux::FindCursorPosition(const Point& point) { | |
86 // TODO(xji): when points outside of text, return HOME/END position. | |
87 PangoLayout* layout = EnsureLayout(); | |
88 | |
89 if (text().length() == 0) | |
90 return SelectionModel(0, 0, SelectionModel::LEADING); | |
91 | |
92 int adjusted_x = | |
93 point.x() - display_rect().x() - GetUpdatedDisplayOffset().x(); | |
94 int caret_pos, trailing; | |
95 pango_layout_xy_to_index(layout, | |
96 adjusted_x * PANGO_SCALE, | |
97 point.y() * PANGO_SCALE, | |
98 &caret_pos, | |
99 &trailing); | |
100 | |
101 size_t selection_end = caret_pos; | |
102 if (trailing > 0) { | |
103 const char* layout_text = pango_layout_get_text(layout); | |
104 selection_end = g_utf8_offset_to_pointer(layout_text + caret_pos, trailing) | |
105 - layout_text; | |
106 } | |
107 | |
108 return SelectionModel( | |
109 Utf8IndexToUtf16Index(text(), selection_end), | |
110 Utf8IndexToUtf16Index(text(), caret_pos), | |
111 trailing > 0 ? SelectionModel::TRAILING : SelectionModel::LEADING); | |
112 } | |
113 | |
114 Rect RenderTextLinux::GetCursorBounds(const SelectionModel& selection, | |
115 bool insert_mode) { | |
116 PangoLayout* layout = EnsureLayout(); | |
117 | |
118 PangoRectangle pos; | |
119 pango_layout_index_to_pos( | |
120 layout, Utf16IndexToUtf8Index(text(), selection.caret_pos()), &pos); | |
121 | |
122 int x; | |
123 SelectionModel::CaretPlacement caret_placement = selection.caret_placement(); | |
124 if (caret_placement == SelectionModel::TRAILING) | |
125 x = (pos.x + pos.width) / PANGO_SCALE; | |
126 else | |
127 x = pos.x / PANGO_SCALE; | |
128 x += display_rect().x() + GetUpdatedDisplayOffset().x(); | |
oshima
2011/08/19 18:44:07
these can be
int x = (caret_placement == Selectio
xji
2011/08/20 00:53:05
I'd prefer the old way.
moderate use of tertiary i
| |
129 | |
130 const Font& font = default_style().font; | |
131 int h = std::min(display_rect().height(), font.GetHeight()); | |
132 Rect bounds(x, (display_rect().height() - h) / 2, 1, h); | |
133 | |
134 size_t position = selection.selection_end(); | |
135 if (!insert_mode && text().length() != position) | |
136 AdjustBoundsForNonInsertMode(selection, pos, &bounds); | |
137 | |
138 return bounds; | |
139 } | |
140 | |
141 SelectionModel RenderTextLinux::GetLeftSelectionModel( | |
142 const SelectionModel& current, BreakType break_type) { | |
143 EnsureLayout(); | |
144 | |
145 if (break_type == LINE_BREAK) | |
146 return GetSelectionModelForVisualLeftmost(); | |
oshima
2011/08/19 18:44:07
indent
xji
2011/08/20 00:53:05
Done.
| |
147 if (break_type == CHARACTER_BREAK) | |
148 return GetLeftSelectionModelByGrapheme(current); | |
149 // TODO(xji): implementation. | |
150 return RenderText::GetLeftSelectionModel(current, break_type); | |
151 } | |
152 | |
153 SelectionModel RenderTextLinux::GetRightSelectionModel( | |
154 const SelectionModel& current, BreakType break_type) { | |
155 EnsureLayout(); | |
156 | |
157 if (break_type == LINE_BREAK) { | |
158 const char* layout_text = pango_layout_get_text(layout_); | |
159 PangoDirection base_dir = | |
160 pango_find_base_dir(layout_text, strlen(layout_text)); | |
161 if (base_dir == PANGO_DIRECTION_RTL || | |
162 base_dir == PANGO_DIRECTION_WEAK_RTL) | |
163 return SelectionModel(0, 0, SelectionModel::LEADING); | |
164 return SelectionModel( | |
165 text().length(), text().length(), SelectionModel::LEADING); | |
166 } | |
167 if (break_type == CHARACTER_BREAK) | |
168 return GetRightSelectionModelByGrapheme(current); | |
169 // TODO(xji): implementation. | |
oshima
2011/08/19 18:44:07
can you update the comment to be more descriptive?
xji
2011/08/20 00:53:05
changed to "not implemented yet".
| |
170 return RenderText::GetRightSelectionModel(current, break_type); | |
171 } | |
172 | |
173 size_t RenderTextLinux::GetIndexOfPreviousGrapheme(size_t position) { | |
174 EnsureLayout(); | |
175 return Utf16IndexOfAdjacentGrapheme(position, PREVIOUS); | |
176 } | |
177 | |
178 SelectionModel RenderTextLinux::GetSelectionModelForVisualLeftmost() const { | |
179 const char* layout_text = pango_layout_get_text(layout_); | |
180 PangoDirection base_dir = | |
181 pango_find_base_dir(layout_text, strlen(layout_text)); | |
182 | |
183 if (base_dir == PANGO_DIRECTION_RTL || base_dir == PANGO_DIRECTION_WEAK_RTL) { | |
184 GSList* first_visual_run = layout_line_->runs; | |
185 if (first_visual_run) { | |
186 PangoItem* item = | |
187 reinterpret_cast<PangoLayoutRun*>(first_visual_run->data)->item; | |
188 if (item->analysis.level % 2 == 0) { // LTR. | |
189 size_t utf16_index = Utf8IndexToUtf16Index(text(), item->offset); | |
190 return SelectionModel( | |
191 text().length(), utf16_index, SelectionModel::LEADING); | |
192 } else { // RTL. | |
193 size_t utf16_index = Utf8IndexToUtf16Index( | |
194 text(), item->offset + item->length); | |
195 utf16_index = Utf16IndexOfAdjacentGrapheme(utf16_index, PREVIOUS); | |
196 return SelectionModel( | |
197 text().length(), utf16_index, SelectionModel::TRAILING); | |
198 } | |
199 } | |
200 } | |
201 return SelectionModel(0, 0, SelectionModel::LEADING); | |
202 } | |
203 | |
204 GSList* RenderTextLinux::GetRunContainsCaretPos(const SelectionModel& current, | |
205 CursorMovementDirection dir, | |
206 bool* at_boundary) const { | |
207 GSList* run_contains_caret_pos = NULL; | |
208 *at_boundary = false; | |
209 | |
210 GSList* run = layout_line_->runs; | |
211 while (run) { | |
212 PangoItem* pango_item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; | |
213 size_t caret_pos = current.caret_pos(); | |
214 size_t run_end_utf16_index = | |
215 Utf8IndexToUtf16Index(text(), pango_item->offset + pango_item->length); | |
216 size_t run_start_utf16_index = | |
217 Utf8IndexToUtf16Index(text(), pango_item->offset); | |
218 | |
219 if (caret_pos >= run_start_utf16_index && caret_pos < run_end_utf16_index) { | |
220 run_contains_caret_pos = run; | |
221 | |
222 size_t selection_end = current.selection_end(); | |
223 SelectionModel::CaretPlacement caret_placement = | |
224 current.caret_placement(); | |
225 | |
226 if (caret_placement == SelectionModel::LEADING && | |
227 caret_pos == run_start_utf16_index) { | |
228 *at_boundary = (dir == RIGHT && pango_item->analysis.level % 2) || | |
229 (dir == LEFT && pango_item->analysis.level % 2 == 0); | |
230 } else if (caret_placement == SelectionModel::TRAILING && | |
231 selection_end == run_end_utf16_index) { | |
232 *at_boundary = (dir == RIGHT && pango_item->analysis.level % 2 == 0) || | |
233 (dir == LEFT && pango_item->analysis.level % 2); | |
234 } | |
235 break; | |
236 } | |
237 run = run->next; | |
238 } | |
239 return run_contains_caret_pos; | |
240 } | |
241 | |
242 size_t RenderTextLinux::Utf8IndexOfAdjacentGrapheme( | |
243 size_t utf16_index_of_current_grapheme, RelativeLogicalPosition pos) const { | |
244 PangoLogAttr* log_attrs; | |
245 gint n_attrs; | |
246 pango_layout_get_log_attrs(layout_, &log_attrs, &n_attrs); | |
247 | |
248 const char* layout_text = pango_layout_get_text(layout_); | |
249 const char* ch = layout_text + | |
250 Utf16IndexToUtf8Index(text(), utf16_index_of_current_grapheme); | |
251 int char_offset = static_cast<int>(g_utf8_pointer_to_offset(layout_text, ch)); | |
252 | |
253 if (pos == PREVIOUS) { | |
254 if (ch > layout_text) { | |
255 do { | |
256 ch = g_utf8_find_prev_char(layout_text, ch); | |
257 --char_offset; | |
258 } while (ch && *ch && !log_attrs[char_offset].is_cursor_position); | |
259 if (!ch) | |
260 ch = layout_text; | |
261 } | |
262 } else { | |
263 if (ch < layout_text + strlen(layout_text)) { | |
264 do { | |
265 ch = g_utf8_find_next_char(ch, NULL); | |
266 ++char_offset; | |
267 } while (ch && *ch && !log_attrs[char_offset].is_cursor_position); | |
268 if (ch >= layout_text + strlen(layout_text)) | |
269 ch = layout_text + strlen(layout_text); | |
270 } | |
271 } | |
272 | |
273 size_t utf8_index = static_cast<size_t>(ch - layout_text); | |
274 // TODO(xji): how costly those g_free is? | |
oshima
2011/08/19 18:44:07
negligible compared to other stuff we're doing.
xji
2011/08/20 00:53:05
comment removed.
| |
275 g_free(log_attrs); | |
276 return utf8_index; | |
277 } | |
278 | |
279 size_t RenderTextLinux::Utf16IndexOfAdjacentGrapheme( | |
280 size_t utf16_index_of_current_grapheme, RelativeLogicalPosition pos) const { | |
281 size_t utf8_index = Utf8IndexOfAdjacentGrapheme( | |
282 utf16_index_of_current_grapheme, pos); | |
283 return Utf8IndexToUtf16Index(text(), utf8_index); | |
284 } | |
285 | |
286 SelectionModel RenderTextLinux::FirstSelectionModelInsideRun( | |
287 const PangoItem* run) const { | |
288 size_t utf16_index = Utf8IndexToUtf16Index(text(), run->offset); | |
oshima
2011/08/19 18:44:07
indnet
xji
2011/08/20 00:53:05
Done.
| |
289 return SelectionModel(Utf16IndexOfAdjacentGrapheme(utf16_index, NEXT), | |
290 utf16_index, | |
291 SelectionModel::TRAILING); | |
292 } | |
293 | |
294 SelectionModel RenderTextLinux::LastSelectionModelInsideRun( | |
295 const PangoItem* run) const { | |
296 size_t utf16_index = Utf8IndexToUtf16Index( | |
297 text(), run->offset + run->length); | |
oshima
2011/08/19 18:44:07
ditto
xji
2011/08/20 00:53:05
Done.
| |
298 utf16_index = Utf16IndexOfAdjacentGrapheme(utf16_index, PREVIOUS); | |
299 return SelectionModel(utf16_index, utf16_index, SelectionModel::LEADING); | |
300 } | |
301 | |
302 SelectionModel RenderTextLinux::LeftmostSelectionModelInsideRun( | |
303 const PangoItem* run) const { | |
304 if (run->analysis.level % 2 == 0) | |
305 return FirstSelectionModelInsideRun(run); | |
306 return LastSelectionModelInsideRun(run); | |
oshima
2011/08/19 18:44:07
can these be
return run->analysis.level % 2 == 0 ?
xji
2011/08/20 00:53:05
Done.
| |
307 } | |
308 | |
309 SelectionModel RenderTextLinux::RightmostSelectionModelInsideRun( | |
310 const PangoItem* run) const { | |
311 if (run->analysis.level % 2) | |
312 return FirstSelectionModelInsideRun(run); | |
313 return LastSelectionModelInsideRun(run); | |
314 } | |
315 | |
316 bool RenderTextLinux::GetVisuallyAdjacentCursorPositionForEnd( | |
317 const SelectionModel& current, | |
318 CursorMovementDirection dir, | |
319 SelectionModel* adjacent) const { | |
320 size_t text_length = text().length(); | |
321 if (current.selection_end() == text_length && | |
322 current.caret_pos() == text_length && | |
323 current.caret_placement() == SelectionModel::LEADING) { | |
324 // Visually rightmost END position. | |
325 if (dir == LEFT) { | |
326 GSList* last_run = GetLastRun(); | |
327 if (last_run) { | |
328 *adjacent = RightmostSelectionModelInsideRun( | |
329 reinterpret_cast<PangoLayoutRun*>(last_run->data)->item); | |
330 } | |
331 } | |
332 adjacent->set_selection_start(adjacent->selection_end()); | |
333 return true; | |
334 } | |
335 return false; | |
336 } | |
337 | |
338 // Assume caret_pos in |current| is n, 'l' represents leading in | |
339 // caret_placement and 't' represents trailing in caret_placement. Following | |
340 // is the calculation from (caret_pos, caret_placement) in |current| to | |
341 // (caret_pos, caret_placement, selection_end) when moving cursor left by | |
342 // one grapheme (for simplilcity, assume each grapheme is one character). | |
343 // If n is in LTR run, | |
344 // (n, t) ---> (n, l, n) | |
345 // (n, l) ---> (n-1, l, n-1) if n is inside run (not at boundary). | |
346 // (n, l) ---> goto across run case if n is at run boundary; | |
347 // If n is in RTL run, | |
348 // (n, l) --> (n, t, n+1); | |
349 // (n, t) --> (n+1, t n+2) if n is inside run; | |
350 // (n, t) --> goto across run case if n is at run boundar; | |
351 // If n is at run boudary, get its visually left run, | |
352 // if left run is LTR run, | |
353 // (n, t) --> (left run's end, l, left run's end); | |
354 // If rght run is RTL run, | |
355 // (n, t) --> (left run's begin, t, left run's begin + 1); | |
356 // | |
357 // TODO(xji): duplication with GetRightSelectionModelByGrapheme. | |
oshima
2011/08/19 18:44:07
do you mean you will consolidate two?
xji
2011/08/20 00:53:05
I am not sure whether to consolidate them. Althoug
| |
358 SelectionModel RenderTextLinux::GetLeftSelectionModelByGrapheme( | |
359 const SelectionModel& current) const { | |
360 SelectionModel left(current); | |
361 if (GetVisuallyAdjacentCursorPositionForEnd(current, LEFT, &left)) | |
362 return left; | |
363 | |
364 bool at_run_boundary = false; | |
365 GSList* run = GetRunContainsCaretPos(current, LEFT, &at_run_boundary); | |
366 if (run == NULL) { | |
367 left.set_selection_start(left.selection_end()); | |
368 return left; | |
369 } | |
370 | |
371 PangoItem* box = reinterpret_cast<PangoLayoutRun*>(run->data)->item; | |
372 if (box->analysis.level % 2 == 0) { // LTR box. | |
373 if (current.caret_placement() == SelectionModel::TRAILING) { | |
374 left.set_caret_placement(SelectionModel::LEADING); | |
375 left.set_selection_end(left.caret_pos()); | |
376 } else { // caret_placement == TRAILING. | |
377 if (at_run_boundary) { | |
378 GSList* prev_run = GetPreviousRun(run); | |
379 if (prev_run) { | |
380 return RightmostSelectionModelInsideRun( | |
381 reinterpret_cast<PangoLayoutRun*>(prev_run->data)->item); | |
382 } | |
oshima
2011/08/19 18:44:07
nuke {}
xji
2011/08/20 00:53:05
Done.
| |
383 } else { // !at_run_boundary. | |
384 size_t prev = Utf16IndexOfAdjacentGrapheme(left.caret_pos(), PREVIOUS); | |
385 left.set_caret_pos(prev); | |
386 left.set_selection_end(prev); | |
387 } | |
388 } | |
389 } else { // RTL box. | |
390 if (current.caret_placement() == SelectionModel::LEADING) { | |
391 left.set_caret_placement(SelectionModel::TRAILING); | |
392 left.set_selection_end( | |
393 Utf16IndexOfAdjacentGrapheme(current.caret_pos(), NEXT)); | |
394 } else { // caret_placement == LEADING | |
395 if (at_run_boundary) { | |
396 GSList* prev_run = GetPreviousRun(run); | |
397 if (prev_run) { | |
398 return RightmostSelectionModelInsideRun( | |
399 reinterpret_cast<PangoLayoutRun*>(prev_run->data)->item); | |
400 } | |
oshima
2011/08/19 18:44:07
ditto
xji
2011/08/20 00:53:05
Done.
| |
401 } else { // !at_run_boundary | |
402 // current.seletion_end - current.caret_pos | |
403 // == NumOfCharsInGrapheme(caret_pos); | |
oshima
2011/08/19 18:44:07
is this comment or dead code?
xji
2011/08/20 00:53:05
it is comment, changed "==" to "equals to"
| |
404 left.set_caret_pos(current.selection_end()); | |
405 left.set_selection_end( | |
406 Utf16IndexOfAdjacentGrapheme(left.caret_pos(), NEXT)); | |
407 } | |
408 } | |
409 } | |
410 | |
411 left.set_selection_start(left.selection_end()); | |
412 return left; | |
413 } | |
414 | |
415 // Assume caret_pos in |current| is n, 'l' represents leading in | |
416 // caret_placement and 't' represents trailing in caret_placement. Following | |
417 // is the calculation from (caret_pos, caret_placement) in |current| to | |
418 // (caret_pos, caret_placement, selection_end) when moving cursor right by | |
419 // one grapheme (for simplilcity, assume each grapheme is one character). | |
420 // If n is in LTR run, | |
421 // (n, l) ---> (n, t, n+1) | |
422 // (n, t) ---> (n+1, t, n+2) if n is inside run (not at boundary). | |
423 // (n, t) ---> goto across run case if n is at run boundary; | |
424 // If n is in RTL run, | |
425 // (n, t) --> (n, l, n); | |
426 // (n, l) --> (n-1, l, n-1) if n is inside run; | |
427 // (n, l) --> goto across run case if n is at run boundar; | |
428 // If n is at run boudary, get its visually right run, | |
429 // if right run is LTR run, | |
430 // (n, t) --> (right run's begin, t, right run's begin + 1); | |
431 // If rght run is RTL run, | |
432 // (n, t) --> (right run's end, l, right run's end); | |
433 SelectionModel RenderTextLinux::GetRightSelectionModelByGrapheme( | |
434 const SelectionModel& current) const { | |
435 SelectionModel right(current); | |
436 if (GetVisuallyAdjacentCursorPositionForEnd(current, RIGHT, &right)) | |
437 return right; | |
438 | |
439 bool at_run_boundary = false; | |
440 GSList* run = GetRunContainsCaretPos(current, RIGHT, &at_run_boundary); | |
441 if (run == NULL) { | |
442 right.set_selection_start(right.selection_end()); | |
443 return right; | |
444 } | |
445 | |
446 PangoItem* box = reinterpret_cast<PangoLayoutRun*>(run->data)->item; | |
447 if (box->analysis.level % 2 == 0) { // LTR box. | |
448 if (current.caret_placement() == SelectionModel::LEADING) { | |
449 right.set_caret_placement(SelectionModel::TRAILING); | |
450 right.set_selection_end( | |
451 Utf16IndexOfAdjacentGrapheme(current.caret_pos(), NEXT)); | |
452 } else { // caret_placement == TRAILING. | |
453 if (at_run_boundary) { | |
454 if (run->next) { | |
455 return LeftmostSelectionModelInsideRun( | |
456 reinterpret_cast<PangoLayoutRun*>(run->next->data)->item); | |
457 } | |
458 } else { // !at_run_boundary. | |
459 // current.seletion_end - current.caret_pos | |
460 // == NumOfCharsInGrapheme(caret_pos); | |
461 right.set_caret_pos(current.selection_end()); | |
462 right.set_selection_end( | |
463 Utf16IndexOfAdjacentGrapheme(right.caret_pos(), NEXT)); | |
464 } | |
465 } | |
466 } else { // RTL box. | |
467 if (current.caret_placement() == SelectionModel::TRAILING) { | |
468 right.set_caret_placement(SelectionModel::LEADING); | |
469 right.set_selection_end(right.caret_pos()); | |
470 } else { // caret_placement == LEADING | |
471 if (at_run_boundary) { | |
472 if (run->next) { | |
473 return LeftmostSelectionModelInsideRun( | |
474 reinterpret_cast<PangoLayoutRun*>(run->next->data)->item); | |
475 } | |
476 } else { // !at_run_boundary | |
477 // current.seletion_end - current.caret_pos | |
478 // == NumOfCharsInGrapheme(caret_pos); | |
479 size_t prev = Utf16IndexOfAdjacentGrapheme(right.caret_pos(), PREVIOUS); | |
480 right.set_caret_pos(prev); | |
481 right.set_selection_end(prev); | |
482 } | |
483 } | |
484 } | |
485 | |
486 right.set_selection_start(right.selection_end()); | |
487 return right; | |
488 } | |
489 | |
490 PangoLayout* RenderTextLinux::EnsureLayout() { | |
491 if (layout_ == NULL) { | |
492 CanvasSkia canvas(display_rect().width(), display_rect().height(), false); | |
493 // TODO(xji): how costly is the Begin/EndPlatformPaint? Can I get cairo | |
494 // context in another way? | |
495 cairo_t* cr = skia::BeginPlatformPaint(&canvas); | |
496 | |
497 layout_ = pango_cairo_create_layout(cr); | |
498 SetupPangoLayout(layout_, text(), default_style().font, | |
499 display_rect().width(), GetTextDirection(), | |
500 CanvasSkia::DefaultCanvasTextAlignment()); | |
501 pango_layout_set_height(layout_, display_rect().height() * PANGO_SCALE); | |
502 SetupPangoAttributes(layout_); | |
503 | |
504 skia::EndPlatformPaint(&canvas); | |
505 | |
506 layout_line_ = pango_layout_get_line_readonly(layout_, 0); | |
507 } | |
508 return layout_; | |
509 } | |
510 | |
511 void RenderTextLinux::ResetLayout() { | |
512 // set_cached_bounds_and_offset_valid(false) is done in RenderText for every | |
513 // operation that trigger ResetLayout(). | |
514 if (layout_) { | |
515 g_object_unref(layout_); | |
516 layout_ = NULL; | |
517 } | |
518 if (layout_line_) { | |
519 // TODO(xji): do I need ref/unref? | |
520 // pango_layout_line_unref(layout_line_); | |
oshima
2011/08/19 18:44:07
you may want to run test with valgrind bots. (linu
xji
2011/08/20 00:53:05
submitted.
| |
521 layout_line_ = NULL; | |
522 } | |
523 } | |
524 | |
525 void RenderTextLinux::SetupPangoAttributes(PangoLayout* layout) { | |
526 PangoAttrList* attrs = pango_attr_list_new(); | |
527 // Set selection background color. | |
528 SkColor selection_color = | |
529 focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor; | |
530 PangoAttribute* pango_attr = pango_attr_background_new( | |
531 COLOR_16_TO_32_BIT(SkColorGetR(selection_color)), | |
532 COLOR_16_TO_32_BIT(SkColorGetG(selection_color)), | |
533 COLOR_16_TO_32_BIT(SkColorGetB(selection_color))); | |
534 AppendPangoAttribute(MinOfSelection(), MaxOfSelection(), | |
535 pango_attr, attrs); | |
536 | |
537 StyleRanges ranges_of_style(style_ranges()); | |
538 ApplyCompositionAndSelectionStyles(&ranges_of_style); | |
539 | |
540 for (StyleRanges::const_iterator i = ranges_of_style.begin(); | |
541 i < ranges_of_style.end(); ++i) { | |
542 const Font& font = !i->underline ? i->font : | |
543 i->font.DeriveFont(0, i->font.GetStyle() | Font::UNDERLINED); | |
544 pango_attr = pango_attr_font_desc_new( | |
545 reinterpret_cast<PangoFontDescription*>(font.GetNativeFont())); | |
546 AppendPangoAttribute(i->range.start(), i->range.end(), pango_attr, attrs); | |
547 | |
548 SkColor foreground = i->foreground; | |
549 pango_attr = pango_attr_foreground_new( | |
550 COLOR_16_TO_32_BIT(SkColorGetR(foreground)), | |
551 COLOR_16_TO_32_BIT(SkColorGetG(foreground)), | |
552 COLOR_16_TO_32_BIT(SkColorGetB(foreground))); | |
553 AppendPangoAttribute(i->range.start(), i->range.end(), pango_attr, attrs); | |
554 | |
555 if (i->strike) { | |
556 pango_attr = pango_attr_strikethrough_new(true); | |
557 AppendPangoAttribute(i->range.start(), i->range.end(), pango_attr, attrs); | |
558 } | |
559 } | |
560 | |
561 pango_layout_set_attributes(layout, attrs); | |
562 pango_attr_list_unref(attrs); | |
563 } | |
564 | |
565 void RenderTextLinux::AppendPangoAttribute(size_t start, | |
566 size_t end, | |
567 PangoAttribute* pango_attr, | |
568 PangoAttrList* attrs) { | |
569 pango_attr->start_index = Utf16IndexToUtf8Index(text(), start); | |
570 pango_attr->end_index = Utf16IndexToUtf8Index(text(), end); | |
571 pango_attr_list_insert(attrs, pango_attr); | |
572 } | |
573 | |
574 GSList* RenderTextLinux::GetPreviousRun(GSList* run) const { | |
575 GSList* current = layout_line_->runs; | |
576 GSList* prev = NULL; | |
577 while (current) { | |
578 if (current == run) | |
579 return prev; | |
580 prev = current; | |
581 current = current->next; | |
582 } | |
583 return NULL; | |
584 } | |
585 | |
586 GSList* RenderTextLinux::GetLastRun() const { | |
587 GSList* current = layout_line_->runs; | |
588 while (current && current->next) { | |
589 current = current->next; | |
590 } | |
591 return current; | |
592 } | |
593 | |
594 size_t RenderTextLinux::Utf16IndexToUtf8Index(const string16& text, | |
595 size_t index) const { | |
oshima
2011/08/19 18:44:07
so there is no library which does this?
xji
2011/08/20 00:53:05
there is no ICU library does that directly without
| |
596 int utf8_index = 0; | |
597 for (size_t i = 0; i < index; ++i) { | |
598 if (text[i] < 0x80) { | |
599 ++utf8_index; | |
600 } else if (text[i] < 0x800) { | |
601 utf8_index += 2; | |
602 } else { | |
603 if ((U16_IS_LEAD(text[i]))) { | |
604 if (i + 1 < index) { | |
605 if (U16_IS_TRAIL(text[i + 1])) { | |
606 // A surrogate pair. | |
607 utf8_index += 4; | |
608 ++i; | |
609 continue; | |
610 } | |
611 } else if (i + 1 < text.length() && U16_IS_TRAIL(text[i + 1])) { | |
612 // A split surrogate pair, stop at the leading. | |
613 break; | |
614 } | |
615 } | |
616 utf8_index += 3; | |
617 } | |
618 } | |
619 return utf8_index; | |
620 } | |
621 | |
622 | |
623 size_t RenderTextLinux::Utf8IndexToUtf16Index(const string16& text, | |
624 size_t index) const { | |
625 // TODO(xji): DUP with utf16IndexToUtf8Index. | |
626 size_t utf8_index = 0; | |
627 size_t utf16_index = 0; | |
628 for (; utf16_index < text.length(); ++utf16_index) { | |
629 if (text[utf16_index] < 0x80) { | |
630 ++utf8_index; | |
631 } else if (text[utf16_index] < 0x800) { | |
632 utf8_index += 2; | |
633 } else { | |
634 if ((U16_IS_LEAD(text[utf16_index]))) { | |
635 if (utf16_index + 1 < text.length()) { | |
636 if (U16_IS_TRAIL(text[utf16_index + 1])) { | |
637 // A surrogate pair. | |
638 utf8_index += 4; | |
639 if (utf8_index >= index) | |
640 break; | |
641 ++utf16_index; | |
642 continue; | |
643 } | |
644 } | |
645 } | |
646 utf8_index += 3; | |
647 } | |
648 if (utf8_index > index) | |
649 break; | |
650 } | |
651 return utf16_index; | |
652 } | |
653 | |
654 void RenderTextLinux::AdjustBoundsForNonInsertMode( | |
655 const SelectionModel& selection, | |
656 const PangoRectangle& pos, | |
657 Rect* bounds) const { | |
658 SelectionModel::CaretPlacement caret_placement = selection.caret_placement(); | |
659 if (caret_placement == SelectionModel::LEADING) { | |
660 if (pos.width > 0) { | |
661 bounds->set_width(pos.width); | |
662 } else { | |
663 bounds->set_x(pos.x + pos.width); | |
664 bounds->set_width(-pos.width); | |
665 } | |
666 } else { | |
667 // TODO(xji): what is the expected behavior? For example, "abcFED", | |
668 // when |selection| == SelectionModel(3, 2, TRAILING), in insert-mode, | |
669 // cursor is at right of 'c'. In non-insert-mode, is the bounds around 'D'? | |
670 } | |
671 } | |
672 | |
20 } // namespace gfx | 673 } // namespace gfx |
OLD | NEW |