Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(79)

Side by Side Diff: ui/gfx/render_text.h

Issue 7461102: modification to RenderText for inheritance/SelectionModel (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: sync and change non-const reference to pointer Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | ui/gfx/render_text.cc » ('j') | ui/gfx/render_text.cc » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 #ifndef UI_GFX_RENDER_TEXT_H_ 5 #ifndef UI_GFX_RENDER_TEXT_H_
6 #define UI_GFX_RENDER_TEXT_H_ 6 #define UI_GFX_RENDER_TEXT_H_
7 #pragma once 7 #pragma once
8 8
9 #include <algorithm>
9 #include <vector> 10 #include <vector>
10 11
11 #include "base/gtest_prod_util.h" 12 #include "base/gtest_prod_util.h"
12 #include "base/i18n/rtl.h" 13 #include "base/i18n/rtl.h"
13 #include "base/string16.h" 14 #include "base/string16.h"
14 #include "third_party/skia/include/core/SkColor.h" 15 #include "third_party/skia/include/core/SkColor.h"
15 #include "ui/base/range/range.h" 16 #include "ui/base/range/range.h"
16 #include "ui/gfx/font.h" 17 #include "ui/gfx/font.h"
17 #include "ui/gfx/rect.h" 18 #include "ui/gfx/rect.h"
18 #include "ui/gfx/point.h" 19 #include "ui/gfx/point.h"
19 20
20 namespace { 21 namespace gfx {
21 22
22 // Strike line width. 23 // Strike line width.
23 const int kStrikeWidth = 2; 24 const int kStrikeWidth = 2;
24 25
25 // Color settings for text, backgrounds and cursor. 26 // Color settings for text, backgrounds and cursor.
26 // These are tentative, and should be derived from theme, system 27 // These are tentative, and should be derived from theme, system
27 // settings and current settings. 28 // settings and current settings.
28 // TODO(oshima): Change this to match the standard chrome 29 // TODO(oshima): Change this to match the standard chrome
29 // before dogfooding textfield views. 30 // before dogfooding textfield views.
30 const SkColor kSelectedTextColor = SK_ColorWHITE; 31 const SkColor kSelectedTextColor = SK_ColorWHITE;
31 const SkColor kFocusedSelectionColor = SK_ColorCYAN; 32 const SkColor kFocusedSelectionColor = SK_ColorCYAN;
32 const SkColor kUnfocusedSelectionColor = SK_ColorLTGRAY; 33 const SkColor kUnfocusedSelectionColor = SK_ColorLTGRAY;
33 const SkColor kCursorColor = SK_ColorBLACK; 34 const SkColor kCursorColor = SK_ColorBLACK;
34 35
35 } // namespace
36
37 namespace gfx {
38
39 class Canvas; 36 class Canvas;
40 class RenderTextTest; 37 class RenderTextTest;
41 38
42 // A visual style applicable to a range of text. 39 // A visual style applicable to a range of text.
43 struct UI_API StyleRange { 40 struct UI_API StyleRange {
44 StyleRange(); 41 StyleRange();
45 42
46 Font font; 43 Font font;
47 SkColor foreground; 44 SkColor foreground;
48 bool strike; 45 bool strike;
49 bool underline; 46 bool underline;
50 ui::Range range; 47 ui::Range range;
51 }; 48 };
52 49
53 typedef std::vector<StyleRange> StyleRanges; 50 typedef std::vector<StyleRange> StyleRanges;
54 51
55 // TODO(msw): Distinguish between logical character and glyph? 52 // TODO(msw): Distinguish between logical character and glyph?
56 enum BreakType { 53 enum BreakType {
57 CHARACTER_BREAK, 54 CHARACTER_BREAK,
58 WORD_BREAK, 55 WORD_BREAK,
59 LINE_BREAK, 56 LINE_BREAK,
60 }; 57 };
61 58
59 // TODO(xji): publish bidi-editing guide line and replace the place holder.
60 // SelectionModel is used to represent the logical selection and visual
61 // position of cursor.
62 //
63 // For bi-directional text, the mapping between visual position and logical
64 // position is not one-to-one. For example, logical text "abcDEF" where capital
65 // letters stand for Hebrew, the visual display is "abcFED". According to the
66 // bidi editing guide (http://bidi-editing-guideline):
67 // 1. If pointing to the right half of the cell of a LTR character, the current
68 // position must be set after this character and the caret must be displayed
69 // after this character.
70 // 2. If pointing to the right half of the cell of a RTL character, the current
71 // position must be set before this character and the caret must be displayed
72 // before this character.
73 //
74 // Pointing to the right half of 'c' and pointing to the right half of 'D' both
75 // set the logical cursor position to 3. But the cursor displayed visually at
76 // different places:
77 // Pointing to the right half of 'c' displays the cursor right of 'c' as
78 // "abc|FED".
79 // Pointing to the right half of 'D' displays the cursor right of 'D' as
80 // "abcFED|".
81 // So, besides the logical selection start point and end point, we need extra
82 // information to specify to which character and on which edge of the character
83 // the visual cursor is bound to. For example, the visual cursor is bound to
84 // the trailing side of the 2nd character 'c' when pointing to right half of
85 // 'c'. And it is bound to the leading edge of the 3rd character 'D' when
86 // pointing to right of 'D'.
87 class UI_API SelectionModel {
88 public:
89 enum LeadingTrailingStatus {
msw 2011/08/03 02:13:06 Optional: rename CaretPlacement / CursorPlacement.
xji 2011/08/03 18:47:40 changed to CaretPlacement
90 // PREVIOUS_GRAPHEME_TRAILING means cursor is visually attached to the
91 // trailing edge of previous grapheme.
92 PREVIOUS_GRAPHEME_TRAILING,
93 LEADING,
94 TRAILING,
95 };
96
97 SelectionModel();
98 explicit SelectionModel(size_t pos);
99 SelectionModel(size_t end, size_t pos, LeadingTrailingStatus status);
100 SelectionModel(size_t start, size_t end,
101 size_t pos, LeadingTrailingStatus status);
102
103 virtual ~SelectionModel();
104
105 size_t selection_start() const { return selection_start_; }
106 void set_selection_start(size_t start) { selection_start_ = start; }
msw 2011/08/03 02:13:06 Nit: use consistent argument names for these three
xji 2011/08/03 18:47:40 used |pos| for all.
107
108 size_t selection_end() const { return selection_end_; }
109 void set_selection_end(size_t pos) { selection_end_ = pos; }
110
111 size_t cursor_pos() const { return cursor_pos_; }
112 void set_cursor_pos(size_t index) { cursor_pos_ = index; }
113
114 LeadingTrailingStatus cursor_placement() const { return cursor_placement_; }
115 void set_cursor_placement(LeadingTrailingStatus placement) {
116 cursor_placement_ = placement;
117 }
118
119 // If |select| is true, set selection_start_ to |selection_start|, otherwise,
120 // empty the selection.
121 void SetSelectionStart(size_t selection_start, bool select) {
msw 2011/08/03 02:13:06 Remove this; it's unused and isn't needed.
xji 2011/08/03 18:47:40 Done.
122 if (select)
123 selection_start_ = selection_start;
124 else
125 selection_start_ = selection_end_;
126 }
127
128 void SetSelectionEmpty() { selection_start_ = selection_end_; }
msw 2011/08/03 02:13:06 I'd prefer that we remove this; fewer mutators is
xji 2011/08/03 18:47:40 removed
129
130 bool operator!=(const SelectionModel& sel) const {
msw 2011/08/03 02:13:06 Sorry if I told you to do operator overloading, ma
xji 2011/08/03 18:47:40 Done.
131 return selection_start_ != sel.selection_start_
132 || selection_end_ != sel.selection_end()
133 || cursor_pos_ != sel.cursor_pos()
134 || cursor_placement_ != sel.cursor_placement();
135 }
136
137 private:
138 void Init(size_t start, size_t end, size_t pos,
139 LeadingTrailingStatus status);
140
141 // Logical selection start. If there is non-empty selection, the selection
142 // always starts visually at the leading edge of the selection_start. So, we
143 // do not need extra information for visual cursor bounding.
msw 2011/08/03 02:13:06 Optional: "visual selection bounding".
xji 2011/08/03 18:47:40 Done.
144 size_t selection_start_;
145
146 // The logical cursor position that next character will be inserted into.
147 // It is also the end of the selection.
148 size_t selection_end_;
149
150 // The following two fields are used to guide cursor visual position.
151 // The index of the character that cursor is visually attached to.
152 size_t cursor_pos_;
msw 2011/08/03 02:13:06 I'm really sorry, but upon further reflection, I t
xji 2011/08/03 18:47:40 Done and it looks much better.
153 // The visual placement of the cursor, relative to its associated character.
154 LeadingTrailingStatus cursor_placement_;
msw 2011/08/03 02:13:06 If we rename cursor_pos_ to caret_pos_ this ought
xji 2011/08/03 18:47:40 Done.
155 };
156
62 // TODO(msw): Implement RenderText[Win|Linux] for Uniscribe/Pango BiDi... 157 // TODO(msw): Implement RenderText[Win|Linux] for Uniscribe/Pango BiDi...
63 158
64 // RenderText represents an abstract model of styled text and its corresponding 159 // RenderText represents an abstract model of styled text and its corresponding
65 // visual layout. Support is built in for a cursor, a selection, simple styling, 160 // visual layout. Support is built in for a cursor, a selection, simple styling,
66 // complex scripts, and bi-directional text. Implementations provide mechanisms 161 // complex scripts, and bi-directional text. Implementations provide mechanisms
67 // for rendering and translation between logical and visual data. 162 // for rendering and translation between logical and visual data.
68 class UI_API RenderText { 163 class UI_API RenderText {
69
70 public: 164 public:
71 virtual ~RenderText(); 165 virtual ~RenderText();
72 166
73 // Creates a platform-specific RenderText instance. 167 // Creates a platform-specific RenderText instance.
74 static RenderText* CreateRenderText(); 168 static RenderText* CreateRenderText();
75 169
76 const string16& text() const { return text_; } 170 const string16& text() const { return text_; }
77 virtual void SetText(const string16& text); 171 virtual void SetText(const string16& text);
78 172
173 SelectionModel selection_model() const { return selection_model_; }
msw 2011/08/03 02:13:06 Return a const reference.
xji 2011/08/03 18:47:40 Done.
174 void SetSelectionModel(const SelectionModel& sel);
175
79 bool cursor_visible() const { return cursor_visible_; } 176 bool cursor_visible() const { return cursor_visible_; }
80 void set_cursor_visible(bool visible) { cursor_visible_ = visible; } 177 void set_cursor_visible(bool visible) { cursor_visible_ = visible; }
81 178
82 bool insert_mode() const { return insert_mode_; } 179 bool insert_mode() const { return insert_mode_; }
83 void toggle_insert_mode() { insert_mode_ = !insert_mode_; } 180 void toggle_insert_mode() { insert_mode_ = !insert_mode_; }
84 181
85 bool focused() const { return focused_; } 182 bool focused() const { return focused_; }
86 void set_focused(bool focused) { focused_ = focused; } 183 void set_focused(bool focused) { focused_ = focused; }
87 184
88 const StyleRange& default_style() const { return default_style_; } 185 const StyleRange& default_style() const { return default_style_; }
89 void set_default_style(StyleRange style) { default_style_ = style; } 186 void set_default_style(StyleRange style) { default_style_ = style; }
90 187
91 const Rect& display_rect() const { return display_rect_; } 188 const Rect& display_rect() const { return display_rect_; }
92 void set_display_rect(const Rect& r) { display_rect_ = r; } 189 virtual void set_display_rect(const Rect& r) { display_rect_ = r; }
93 190
94 size_t GetCursorPosition() const; 191 size_t GetCursorPosition() const;
95 void SetCursorPosition(const size_t position); 192 void SetCursorPosition(const size_t position);
96 193
194 SelectionModel::LeadingTrailingStatus GetCursorPlacement() const {
msw 2011/08/03 02:13:06 Remove this, see my comment for its only use in Te
xji 2011/08/03 18:47:40 Done.
195 return selection_model_.cursor_placement();
196 }
197 void SetCursorPlacement(SelectionModel::LeadingTrailingStatus placement) {
msw 2011/08/03 02:13:06 Remove this, it's unused.
xji 2011/08/03 18:47:40 This is used in RenderTextLinux. caret_placement i
msw 2011/08/03 19:46:38 OK
198 selection_model_.set_cursor_placement(placement);
199 }
200
201 // TODO(xji): a better name? GetCursorPos() is too similar to
202 // GetCursorPosition.
203 size_t GetCursorBoundingCharIndex() const {
msw 2011/08/03 02:13:06 Remove this, see my comment for its only use in Te
xji 2011/08/03 18:47:40 Done.
204 return selection_model_.cursor_pos();
205 }
206
97 // Moves the cursor left or right. Cursor movement is visual, meaning that 207 // Moves the cursor left or right. Cursor movement is visual, meaning that
98 // left and right are relative to screen, not the directionality of the text. 208 // left and right are relative to screen, not the directionality of the text.
99 // If |select| is false, the selection range is emptied at the new position. 209 // If |select| is false, the selection range is emptied at the new position.
100 // If |break_type| is CHARACTER_BREAK, move to the neighboring character. 210 // If |break_type| is CHARACTER_BREAK, move to the neighboring character.
101 // If |break_type| is WORD_BREAK, move to the nearest word boundary. 211 // If |break_type| is WORD_BREAK, move to the nearest word boundary.
102 // If |break_type| is LINE_BREAK, move to text edge as shown on screen. 212 // If |break_type| is LINE_BREAK, move to text edge as shown on screen.
103 void MoveCursorLeft(BreakType break_type, bool select); 213 void MoveCursorLeft(BreakType break_type, bool select);
104 void MoveCursorRight(BreakType break_type, bool select); 214 void MoveCursorRight(BreakType break_type, bool select);
105 215
106 // Moves the cursor to the specified logical |position|. 216 // Set the selection_model_ to the value of |selection|.
107 // If |select| is false, the selection range is emptied at the new position.
108 // Returns true if the cursor position or selection range changed. 217 // Returns true if the cursor position or selection range changed.
109 bool MoveCursorTo(size_t position, bool select); 218 bool MoveCursorTo(const SelectionModel& selection);
110 219
111 // Move the cursor to the position associated with the clicked point. 220 // Move the cursor to the position associated with the clicked point.
112 // If |select| is false, the selection range is emptied at the new position. 221 // If |select| is false, the selection range is emptied at the new position.
113 bool MoveCursorTo(const Point& point, bool select); 222 bool MoveCursorTo(const Point& point, bool select);
114 223
115 const ui::Range& GetSelection() const; 224 size_t GetSelectionStart() const {
116 void SetSelection(const ui::Range& range); 225 return selection_model_.selection_start();
226 }
227 size_t MinOfSelection() const {
228 return std::min(GetSelectionStart(), GetCursorPosition());
229 }
230 size_t MaxOfSelection() const {
231 return std::max(GetSelectionStart(), GetCursorPosition());
232 }
233 bool EmptySelection() const {
234 return GetSelectionStart() == GetCursorPosition();
235 }
117 236
118 // Returns true if the local point is over selected text. 237 // Returns true if the local point is over selected text.
119 bool IsPointInSelection(const Point& point) const; 238 bool IsPointInSelection(const Point& point);
120 239
121 // Selects no text, all text, or the word at the current cursor position. 240 // Selects no text, all text, or the word at the current cursor position.
122 void ClearSelection(); 241 void ClearSelection();
123 void SelectAll(); 242 void SelectAll();
124 void SelectWord(); 243 void SelectWord();
125 244
126 const ui::Range& GetCompositionRange() const; 245 const ui::Range& GetCompositionRange() const;
127 void SetCompositionRange(const ui::Range& composition_range); 246 void SetCompositionRange(const ui::Range& composition_range);
128 247
129 // Apply |style_range| to the internal style model. 248 // Apply |style_range| to the internal style model.
130 virtual void ApplyStyleRange(StyleRange style_range); 249 virtual void ApplyStyleRange(StyleRange style_range);
131 250
132 // Apply |default_style_| over the entire text range. 251 // Apply |default_style_| over the entire text range.
133 virtual void ApplyDefaultStyle(); 252 virtual void ApplyDefaultStyle();
134 253
135 base::i18n::TextDirection GetTextDirection() const; 254 base::i18n::TextDirection GetTextDirection() const;
136 255
137 // Get the width of the entire string. 256 // Get the width of the entire string.
138 int GetStringWidth() const; 257 virtual int GetStringWidth();
139 258
140 virtual void Draw(Canvas* canvas); 259 virtual void Draw(Canvas* canvas);
141 260
142 // TODO(msw): Deprecate this function. Logical and visual cursors are not 261 // Gets the SelectionModel from a visual point in local coordinates.
143 // mapped one-to-one. See the selection_range_ TODO for more information. 262 virtual SelectionModel FindCursorPosition(const Point& point);
144 // Get the logical cursor position from a visual point in local coordinates.
145 virtual size_t FindCursorPosition(const Point& point) const;
146 263
147 // Get the visual bounds containing the logical substring within |range|. 264 // Get the visual bounds containing the logical substring within |from| to
148 // These bounds could be visually discontiguous if the logical selection range 265 // |to|. These bounds could be visually discontinuous if the logical
149 // is split by an odd number of LTR/RTL level change. 266 // selection range is split by an odd number of LTR/RTL level change.
150 virtual std::vector<Rect> GetSubstringBounds( 267 virtual std::vector<Rect> GetSubstringBounds(
151 const ui::Range& range) const; 268 size_t from, size_t to) const;
152 269
153 // Get the visual bounds describing the cursor at |position|. These bounds 270 // Get the visual bounds describing the cursor at |selection|. These bounds
154 // typically represent a vertical line, but if |insert_mode| is true they 271 // typically represent a vertical line, but if |insert_mode| is true they
155 // contain the bounds of the associated glyph. 272 // contain the bounds of the associated glyph.
156 virtual Rect GetCursorBounds(size_t position, bool insert_mode) const; 273 virtual Rect GetCursorBounds(const SelectionModel& selection,
274 bool insert_mode);
275
276 // Compute cursor_bounds_ and update display_offset_ when necessary. Cache
277 // the values for later use and return cursor_bounds_.
278 Rect CursorBounds();
157 279
158 protected: 280 protected:
159 RenderText(); 281 RenderText();
160 282
283 void set_cursor_bounds_valid(bool valid) { cursor_bounds_valid_ = valid; }
284
161 const StyleRanges& style_ranges() const { return style_ranges_; } 285 const StyleRanges& style_ranges() const { return style_ranges_; }
162 286
163 const Point& display_offset() const { return display_offset_; } 287 const Point& display_offset() const { return display_offset_; }
164 288
165 // Get the cursor position that visually neighbors |position|. 289 // Get the cursor position that visually neighbors |position|.
166 // If |move_by_word| is true, return the neighboring word delimiter position. 290 // If |move_by_word| is true, return the neighboring word delimiter position.
167 virtual size_t GetLeftCursorPosition(size_t position, 291 virtual SelectionModel GetLeftCursorPosition(const SelectionModel& current,
168 bool move_by_word) const; 292 bool move_by_word);
169 virtual size_t GetRightCursorPosition(size_t position, 293 virtual SelectionModel GetRightCursorPosition(const SelectionModel& current,
170 bool move_by_word) const; 294 bool move_by_word);
295
296 // Apply composition style (underline) to composition range and selection
297 // style (foreground) to selection range.
298 void ApplyCompositionAndSelectionStyles(StyleRanges* style_ranges) const;
171 299
172 private: 300 private:
173 friend class RenderTextTest; 301 friend class RenderTextTest;
174 302
175 FRIEND_TEST_ALL_PREFIXES(RenderTextTest, DefaultStyle); 303 FRIEND_TEST_ALL_PREFIXES(RenderTextTest, DefaultStyle);
176 FRIEND_TEST_ALL_PREFIXES(RenderTextTest, CustomDefaultStyle); 304 FRIEND_TEST_ALL_PREFIXES(RenderTextTest, CustomDefaultStyle);
177 FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ApplyStyleRange); 305 FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ApplyStyleRange);
178 FRIEND_TEST_ALL_PREFIXES(RenderTextTest, StyleRangesAdjust); 306 FRIEND_TEST_ALL_PREFIXES(RenderTextTest, StyleRangesAdjust);
179 307
180 // Clear out |style_ranges_|. 308 // Clear out |style_ranges_|.
181 void ClearStyleRanges(); 309 void ClearStyleRanges();
182 310
183 bool IsPositionAtWordSelectionBoundary(size_t pos); 311 bool IsPositionAtWordSelectionBoundary(size_t pos);
184 312
313 void UpdateCursorBoundsAndDisplayOffset();
314
315 // Set the selection logical start and end in selection_model_.
316 // And invalidate cursor_bounds_.
317 void SetSelection(size_t start, size_t end);
msw 2011/08/03 02:13:06 We should eliminate this function and use SetSelec
xji 2011/08/03 18:47:40 removed.
318
185 // Logical UTF-16 string data to be drawn. 319 // Logical UTF-16 string data to be drawn.
186 string16 text_; 320 string16 text_;
187 321
188 // TODO(msw): A single logical cursor position doesn't support two potential 322 // Logical selection range and visual cursor position.
189 // visual cursor positions. For example, clicking right of 'c' & 'D' yeilds: 323 SelectionModel selection_model_;
190 // (visually: 'abc|FEDghi' and 'abcFED|ghi', both logically: 'abc|DEFghi'). 324
191 // Similarly, one visual position may have two associated logical positions. 325 // The cached cursor bounds.
192 // For example, clicking the right side of 'D' and left side of 'g' yields: 326 Rect cursor_bounds_;
193 // (both visually: 'abcFED|ghi', logically: 'abc|DEFghi' and 'abcDEF|ghi'). 327 // cursor_bounds_ is computed when needed and cached afterwards. And it is
194 // Update the cursor model with a leading/trailing flag, a level association, 328 // invalidated in operations such as SetCursorPosition, SetSelection, Font
195 // or a disjoint visual position to satisfy the proposed visual behavior. 329 // related style change, and other operations that trigger re-layout.
196 // Logical selection range; the range end is also the logical cursor position. 330 bool cursor_bounds_valid_;
197 ui::Range selection_range_;
198 331
199 // The cursor visibility and insert mode. 332 // The cursor visibility and insert mode.
200 bool cursor_visible_; 333 bool cursor_visible_;
201 bool insert_mode_; 334 bool insert_mode_;
202 335
203 // The focus state of the text. 336 // The focus state of the text.
204 bool focused_; 337 bool focused_;
205 338
206 // Composition text range. 339 // Composition text range.
207 ui::Range composition_range_; 340 ui::Range composition_range_;
208 341
209 // List of style ranges. Elements in the list never overlap each other. 342 // List of style ranges. Elements in the list never overlap each other.
210 StyleRanges style_ranges_; 343 StyleRanges style_ranges_;
211 // The default text style. 344 // The default text style.
212 StyleRange default_style_; 345 StyleRange default_style_;
213 346
214 // The local display area for rendering the text. 347 // The local display area for rendering the text.
215 Rect display_rect_; 348 Rect display_rect_;
216 // The offset for the text to be drawn, relative to the display area. 349 // The offset for the text to be drawn, relative to the display area.
217 Point display_offset_; 350 Point display_offset_;
218 351
219 DISALLOW_COPY_AND_ASSIGN(RenderText); 352 DISALLOW_COPY_AND_ASSIGN(RenderText);
220 }; 353 };
221 354
222 } // namespace gfx 355 } // namespace gfx
223 356
224 #endif // UI_GFX_RENDER_TEXT_H_ 357 #endif // UI_GFX_RENDER_TEXT_H_
OLDNEW
« no previous file with comments | « no previous file | ui/gfx/render_text.cc » ('j') | ui/gfx/render_text.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698