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

Side by Side Diff: ui/base/text/text_elider.cc

Issue 8917011: Change signature of |ui::ElideText()|. Add a truncate mode. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years 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 | « ui/base/text/text_elider.h ('k') | ui/base/text/text_elider_unittest.cc » ('j') | no next file with comments »
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 #include "ui/base/text/text_elider.h" 5 #include "ui/base/text/text_elider.h"
6 6
7 #include <string> 7 #include <string>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/file_path.h" 10 #include "base/file_path.h"
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
87 int available_pixel_width) { 87 int available_pixel_width) {
88 size_t url_path_number_of_elements = url_path_elements.size(); 88 size_t url_path_number_of_elements = url_path_elements.size();
89 89
90 const string16 kEllipsisAndSlash = UTF8ToUTF16(kEllipsis) + kForwardSlash; 90 const string16 kEllipsisAndSlash = UTF8ToUTF16(kEllipsis) + kForwardSlash;
91 91
92 for (size_t i = url_path_number_of_elements - 1; i > 0; --i) { 92 for (size_t i = url_path_number_of_elements - 1; i > 0; --i) {
93 string16 elided_path = BuildPathFromComponents(url_path_prefix, 93 string16 elided_path = BuildPathFromComponents(url_path_prefix,
94 url_path_elements, url_filename, i); 94 url_path_elements, url_filename, i);
95 if (available_pixel_width >= font.GetStringWidth(elided_path)) 95 if (available_pixel_width >= font.GetStringWidth(elided_path))
96 return ElideText(elided_path + url_query, 96 return ElideText(elided_path + url_query,
97 font, available_pixel_width, false); 97 font, available_pixel_width, ui::ELIDE_AT_END);
98 } 98 }
99 99
100 return string16(); 100 return string16();
101 } 101 }
102 102
103 } // namespace 103 } // namespace
104 104
105 // This function takes a GURL object and elides it. It returns a string 105 // This function takes a GURL object and elides it. It returns a string
106 // which composed of parts from subdomain, domain, path, filename and query. 106 // which composed of parts from subdomain, domain, path, filename and query.
107 // A "..." is added automatically at the end if the elided string is bigger 107 // A "..." is added automatically at the end if the elided string is bigger
(...skipping 10 matching lines...) Expand all
118 const std::string& languages) { 118 const std::string& languages) {
119 // Get a formatted string and corresponding parsing of the url. 119 // Get a formatted string and corresponding parsing of the url.
120 url_parse::Parsed parsed; 120 url_parse::Parsed parsed;
121 string16 url_string = net::FormatUrl(url, languages, net::kFormatUrlOmitAll, 121 string16 url_string = net::FormatUrl(url, languages, net::kFormatUrlOmitAll,
122 net::UnescapeRule::SPACES, &parsed, NULL, NULL); 122 net::UnescapeRule::SPACES, &parsed, NULL, NULL);
123 if (available_pixel_width <= 0) 123 if (available_pixel_width <= 0)
124 return url_string; 124 return url_string;
125 125
126 // If non-standard or not file type, return plain eliding. 126 // If non-standard or not file type, return plain eliding.
127 if (!(url.SchemeIsFile() || url.IsStandard())) 127 if (!(url.SchemeIsFile() || url.IsStandard()))
128 return ElideText(url_string, font, available_pixel_width, false); 128 return ElideText(url_string, font, available_pixel_width, ui::ELIDE_AT_END);
129 129
130 // Now start eliding url_string to fit within available pixel width. 130 // Now start eliding url_string to fit within available pixel width.
131 // Fist pass - check to see whether entire url_string fits. 131 // Fist pass - check to see whether entire url_string fits.
132 int pixel_width_url_string = font.GetStringWidth(url_string); 132 int pixel_width_url_string = font.GetStringWidth(url_string);
133 if (available_pixel_width >= pixel_width_url_string) 133 if (available_pixel_width >= pixel_width_url_string)
134 return url_string; 134 return url_string;
135 135
136 // Get the path substring, including query and reference. 136 // Get the path substring, including query and reference.
137 size_t path_start_index = parsed.path.begin; 137 size_t path_start_index = parsed.path.begin;
138 size_t path_len = parsed.path.len; 138 size_t path_len = parsed.path.len;
139 string16 url_path_query_etc = url_string.substr(path_start_index); 139 string16 url_path_query_etc = url_string.substr(path_start_index);
140 string16 url_path = url_string.substr(path_start_index, path_len); 140 string16 url_path = url_string.substr(path_start_index, path_len);
141 141
142 // Return general elided text if url minus the query fits. 142 // Return general elided text if url minus the query fits.
143 string16 url_minus_query = url_string.substr(0, path_start_index + path_len); 143 string16 url_minus_query = url_string.substr(0, path_start_index + path_len);
144 if (available_pixel_width >= font.GetStringWidth(url_minus_query)) 144 if (available_pixel_width >= font.GetStringWidth(url_minus_query))
145 return ElideText(url_string, font, available_pixel_width, false); 145 return ElideText(url_string, font, available_pixel_width, ui::ELIDE_AT_END);
146 146
147 // Get Host. 147 // Get Host.
148 string16 url_host = UTF8ToUTF16(url.host()); 148 string16 url_host = UTF8ToUTF16(url.host());
149 149
150 // Get domain and registry information from the URL. 150 // Get domain and registry information from the URL.
151 string16 url_domain = UTF8ToUTF16( 151 string16 url_domain = UTF8ToUTF16(
152 net::RegistryControlledDomainService::GetDomainAndRegistry(url)); 152 net::RegistryControlledDomainService::GetDomainAndRegistry(url));
153 if (url_domain.empty()) 153 if (url_domain.empty())
154 url_domain = url_host; 154 url_domain = url_host;
155 155
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
206 // Query element. 206 // Query element.
207 string16 url_query; 207 string16 url_query;
208 const int kPixelWidthDotsTrailer = 208 const int kPixelWidthDotsTrailer =
209 font.GetStringWidth(UTF8ToUTF16(kEllipsis)); 209 font.GetStringWidth(UTF8ToUTF16(kEllipsis));
210 if (parsed.query.is_nonempty()) { 210 if (parsed.query.is_nonempty()) {
211 url_query = UTF8ToUTF16("?") + url_string.substr(parsed.query.begin); 211 url_query = UTF8ToUTF16("?") + url_string.substr(parsed.query.begin);
212 if (available_pixel_width >= (pixel_width_url_subdomain + 212 if (available_pixel_width >= (pixel_width_url_subdomain +
213 pixel_width_url_domain + pixel_width_url_path - 213 pixel_width_url_domain + pixel_width_url_path -
214 font.GetStringWidth(url_query))) { 214 font.GetStringWidth(url_query))) {
215 return ElideText(url_subdomain + url_domain + url_path_query_etc, 215 return ElideText(url_subdomain + url_domain + url_path_query_etc,
216 font, available_pixel_width, false); 216 font, available_pixel_width, ui::ELIDE_AT_END);
217 } 217 }
218 } 218 }
219 219
220 // Parse url_path using '/'. 220 // Parse url_path using '/'.
221 std::vector<string16> url_path_elements; 221 std::vector<string16> url_path_elements;
222 base::SplitString(url_path, kForwardSlash, &url_path_elements); 222 base::SplitString(url_path, kForwardSlash, &url_path_elements);
223 223
224 // Get filename - note that for a path ending with / 224 // Get filename - note that for a path ending with /
225 // such as www.google.com/intl/ads/, the file name is ads/. 225 // such as www.google.com/intl/ads/, the file name is ads/.
226 size_t url_path_number_of_elements = url_path_elements.size(); 226 size_t url_path_number_of_elements = url_path_elements.size();
227 DCHECK(url_path_number_of_elements != 0); 227 DCHECK(url_path_number_of_elements != 0);
228 string16 url_filename; 228 string16 url_filename;
229 if ((url_path_elements.at(url_path_number_of_elements - 1)).length() > 0) { 229 if ((url_path_elements.at(url_path_number_of_elements - 1)).length() > 0) {
230 url_filename = *(url_path_elements.end() - 1); 230 url_filename = *(url_path_elements.end() - 1);
231 } else if (url_path_number_of_elements > 1) { // Path ends with a '/'. 231 } else if (url_path_number_of_elements > 1) { // Path ends with a '/'.
232 url_filename = url_path_elements.at(url_path_number_of_elements - 2) + 232 url_filename = url_path_elements.at(url_path_number_of_elements - 2) +
233 kForwardSlash; 233 kForwardSlash;
234 url_path_number_of_elements--; 234 url_path_number_of_elements--;
235 } 235 }
236 DCHECK(url_path_number_of_elements != 0); 236 DCHECK(url_path_number_of_elements != 0);
237 237
238 const size_t kMaxNumberOfUrlPathElementsAllowed = 1024; 238 const size_t kMaxNumberOfUrlPathElementsAllowed = 1024;
239 if (url_path_number_of_elements <= 1 || 239 if (url_path_number_of_elements <= 1 ||
240 url_path_number_of_elements > kMaxNumberOfUrlPathElementsAllowed) { 240 url_path_number_of_elements > kMaxNumberOfUrlPathElementsAllowed) {
241 // No path to elide, or too long of a path (could overflow in loop below) 241 // No path to elide, or too long of a path (could overflow in loop below)
242 // Just elide this as a text string. 242 // Just elide this as a text string.
243 return ElideText(url_subdomain + url_domain + url_path_query_etc, font, 243 return ElideText(url_subdomain + url_domain + url_path_query_etc, font,
244 available_pixel_width, false); 244 available_pixel_width, ui::ELIDE_AT_END);
245 } 245 }
246 246
247 // Start eliding the path and replacing elements by ".../". 247 // Start eliding the path and replacing elements by ".../".
248 const string16 kEllipsisAndSlash = UTF8ToUTF16(kEllipsis) + kForwardSlash; 248 const string16 kEllipsisAndSlash = UTF8ToUTF16(kEllipsis) + kForwardSlash;
249 int pixel_width_ellipsis_slash = font.GetStringWidth(kEllipsisAndSlash); 249 int pixel_width_ellipsis_slash = font.GetStringWidth(kEllipsisAndSlash);
250 250
251 // Check with both subdomain and domain. 251 // Check with both subdomain and domain.
252 string16 elided_path = ElideComponentizedPath(url_subdomain + url_domain, 252 string16 elided_path = ElideComponentizedPath(url_subdomain + url_domain,
253 url_path_elements, url_filename, url_query, font, available_pixel_width); 253 url_path_elements, url_filename, url_query, font, available_pixel_width);
254 if (!elided_path.empty()) 254 if (!elided_path.empty())
(...skipping 26 matching lines...) Expand all
281 // A hack to prevent trailing ".../...". 281 // A hack to prevent trailing ".../...".
282 if ((available_pixel_width - url_elided_domain_width) > 282 if ((available_pixel_width - url_elided_domain_width) >
283 pixel_width_ellipsis_slash + kPixelWidthDotsTrailer + 283 pixel_width_ellipsis_slash + kPixelWidthDotsTrailer +
284 font.GetStringWidth(ASCIIToUTF16("UV"))) { 284 font.GetStringWidth(ASCIIToUTF16("UV"))) {
285 final_elided_url_string += BuildPathFromComponents(string16(), 285 final_elided_url_string += BuildPathFromComponents(string16(),
286 url_path_elements, url_filename, 1); 286 url_path_elements, url_filename, 1);
287 } else { 287 } else {
288 final_elided_url_string += url_path; 288 final_elided_url_string += url_path;
289 } 289 }
290 290
291 return ElideText(final_elided_url_string, font, available_pixel_width, false); 291 return ElideText(final_elided_url_string, font, available_pixel_width,
292 ui::ELIDE_AT_END);
292 } 293 }
293 294
294 string16 ElideFilename(const FilePath& filename, 295 string16 ElideFilename(const FilePath& filename,
295 const gfx::Font& font, 296 const gfx::Font& font,
296 int available_pixel_width) { 297 int available_pixel_width) {
297 #if defined(OS_WIN) 298 #if defined(OS_WIN)
298 string16 filename_utf16 = filename.value(); 299 string16 filename_utf16 = filename.value();
299 string16 extension = filename.Extension(); 300 string16 extension = filename.Extension();
300 string16 rootname = filename.BaseName().RemoveExtension().value(); 301 string16 rootname = filename.BaseName().RemoveExtension().value();
301 #elif defined(OS_POSIX) 302 #elif defined(OS_POSIX)
302 string16 filename_utf16 = WideToUTF16(base::SysNativeMBToWide( 303 string16 filename_utf16 = WideToUTF16(base::SysNativeMBToWide(
303 filename.value())); 304 filename.value()));
304 string16 extension = WideToUTF16(base::SysNativeMBToWide( 305 string16 extension = WideToUTF16(base::SysNativeMBToWide(
305 filename.Extension())); 306 filename.Extension()));
306 string16 rootname = WideToUTF16(base::SysNativeMBToWide( 307 string16 rootname = WideToUTF16(base::SysNativeMBToWide(
307 filename.BaseName().RemoveExtension().value())); 308 filename.BaseName().RemoveExtension().value()));
308 #endif 309 #endif
309 310
310 int full_width = font.GetStringWidth(filename_utf16); 311 int full_width = font.GetStringWidth(filename_utf16);
311 if (full_width <= available_pixel_width) 312 if (full_width <= available_pixel_width)
312 return base::i18n::GetDisplayStringInLTRDirectionality(filename_utf16); 313 return base::i18n::GetDisplayStringInLTRDirectionality(filename_utf16);
313 314
314 if (rootname.empty() || extension.empty()) { 315 if (rootname.empty() || extension.empty()) {
315 string16 elided_name = ElideText(filename_utf16, font, 316 string16 elided_name = ElideText(filename_utf16, font,
316 available_pixel_width, false); 317 available_pixel_width, ui::ELIDE_AT_END);
317 return base::i18n::GetDisplayStringInLTRDirectionality(elided_name); 318 return base::i18n::GetDisplayStringInLTRDirectionality(elided_name);
318 } 319 }
319 320
320 int ext_width = font.GetStringWidth(extension); 321 int ext_width = font.GetStringWidth(extension);
321 int root_width = font.GetStringWidth(rootname); 322 int root_width = font.GetStringWidth(rootname);
322 323
323 // We may have trimmed the path. 324 // We may have trimmed the path.
324 if (root_width + ext_width <= available_pixel_width) { 325 if (root_width + ext_width <= available_pixel_width) {
325 string16 elided_name = rootname + extension; 326 string16 elided_name = rootname + extension;
326 return base::i18n::GetDisplayStringInLTRDirectionality(elided_name); 327 return base::i18n::GetDisplayStringInLTRDirectionality(elided_name);
327 } 328 }
328 329
329 if (ext_width >= available_pixel_width) { 330 if (ext_width >= available_pixel_width) {
330 string16 elided_name = ElideText(rootname + extension, font, 331 string16 elided_name = ElideText(rootname + extension, font,
331 available_pixel_width, true); 332 available_pixel_width,
333 ui::ELIDE_IN_MIDDLE);
332 return base::i18n::GetDisplayStringInLTRDirectionality(elided_name); 334 return base::i18n::GetDisplayStringInLTRDirectionality(elided_name);
333 } 335 }
334 336
335 int available_root_width = available_pixel_width - ext_width; 337 int available_root_width = available_pixel_width - ext_width;
336 string16 elided_name = 338 string16 elided_name =
337 ElideText(rootname, font, available_root_width, false); 339 ElideText(rootname, font, available_root_width, ui::ELIDE_AT_END);
338 elided_name += extension; 340 elided_name += extension;
339 return base::i18n::GetDisplayStringInLTRDirectionality(elided_name); 341 return base::i18n::GetDisplayStringInLTRDirectionality(elided_name);
340 } 342 }
341 343
342 // This function adds an ellipsis at the end of the text if the text 344 // This function adds an ellipsis at the end of the text if the text
343 // does not fit the given pixel width. 345 // does not fit the given pixel width.
344 string16 ElideText(const string16& text, 346 string16 ElideText(const string16& text,
345 const gfx::Font& font, 347 const gfx::Font& font,
346 int available_pixel_width, 348 int available_pixel_width,
347 bool elide_in_middle) { 349 ElideBehavior elide_behavior) {
348 if (text.empty()) 350 if (text.empty())
349 return text; 351 return text;
350 352
351 int current_text_pixel_width = font.GetStringWidth(text); 353 int current_text_pixel_width = font.GetStringWidth(text);
354 bool elide_in_middle = (elide_behavior == ui::ELIDE_IN_MIDDLE);
355 bool insert_ellipsis = (elide_behavior != ui::TRUNCATE_AT_END);
352 356
353 // Pango will return 0 width for absurdly long strings. Cut the string in 357 // Pango will return 0 width for absurdly long strings. Cut the string in
354 // half and try again. 358 // half and try again.
355 // This is caused by an int overflow in Pango (specifically, in 359 // This is caused by an int overflow in Pango (specifically, in
356 // pango_glyph_string_extents_range). It's actually more subtle than just 360 // pango_glyph_string_extents_range). It's actually more subtle than just
357 // returning 0, since on super absurdly long strings, the int can wrap and 361 // returning 0, since on super absurdly long strings, the int can wrap and
358 // return positive numbers again. Detecting that is probably not worth it 362 // return positive numbers again. Detecting that is probably not worth it
359 // (eliding way too much from a ridiculous string is probably still 363 // (eliding way too much from a ridiculous string is probably still
360 // ridiculous), but we should check other widths for bogus values as well. 364 // ridiculous), but we should check other widths for bogus values as well.
361 if (current_text_pixel_width <= 0 && !text.empty()) { 365 if (current_text_pixel_width <= 0 && !text.empty()) {
362 return ElideText(CutString(text, text.length() / 2, elide_in_middle, false), 366 return ElideText(CutString(text, text.length() / 2, elide_in_middle, false),
363 font, available_pixel_width, false); 367 font, available_pixel_width, elide_behavior);
364 } 368 }
365 369
366 if (current_text_pixel_width <= available_pixel_width) 370 if (current_text_pixel_width <= available_pixel_width)
367 return text; 371 return text;
368 372
369 if (font.GetStringWidth(UTF8ToUTF16(kEllipsis)) > available_pixel_width) 373 if (font.GetStringWidth(UTF8ToUTF16(kEllipsis)) > available_pixel_width)
370 return string16(); 374 return string16();
371 375
372 // Use binary search to compute the elided text. 376 // Use binary search to compute the elided text.
373 size_t lo = 0; 377 size_t lo = 0;
374 size_t hi = text.length() - 1; 378 size_t hi = text.length() - 1;
375 for (size_t guess = (lo + hi) / 2; guess != lo; guess = (lo + hi) / 2) { 379 size_t guess;
380 for (guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) {
376 // We check the length of the whole desired string at once to ensure we 381 // We check the length of the whole desired string at once to ensure we
377 // handle kerning/ligatures/etc. correctly. 382 // handle kerning/ligatures/etc. correctly.
378 int guess_length = font.GetStringWidth( 383 string16 cut = CutString(text, guess, elide_in_middle, insert_ellipsis);
379 CutString(text, guess, elide_in_middle, true)); 384 int guess_length = font.GetStringWidth(cut);
380 // Check again that we didn't hit a Pango width overflow. If so, cut the 385 // Check again that we didn't hit a Pango width overflow. If so, cut the
381 // current string in half and start over. 386 // current string in half and start over.
382 if (guess_length <= 0) { 387 if (guess_length <= 0) {
383 return ElideText(CutString(text, guess / 2, elide_in_middle, false), 388 return ElideText(CutString(text, guess / 2, elide_in_middle, false),
384 font, available_pixel_width, elide_in_middle); 389 font, available_pixel_width, elide_behavior);
385 } 390 }
386 if (guess_length > available_pixel_width) 391 if (guess_length > available_pixel_width)
387 hi = guess; 392 hi = guess - 1;
388 else 393 else
389 lo = guess; 394 lo = guess + 1;
390 } 395 }
391 396
392 return CutString(text, lo, elide_in_middle, true); 397 return CutString(text, guess, elide_in_middle, insert_ellipsis);
393 } 398 }
394 399
395 SortedDisplayURL::SortedDisplayURL(const GURL& url, 400 SortedDisplayURL::SortedDisplayURL(const GURL& url,
396 const std::string& languages) { 401 const std::string& languages) {
397 net::AppendFormattedHost(url, languages, &sort_host_); 402 net::AppendFormattedHost(url, languages, &sort_host_);
398 string16 host_minus_www = net::StripWWW(sort_host_); 403 string16 host_minus_www = net::StripWWW(sort_host_);
399 url_parse::Parsed parsed; 404 url_parse::Parsed parsed;
400 display_url_ = net::FormatUrl(url, languages, 405 display_url_ = net::FormatUrl(url, languages,
401 net::kFormatUrlOmitAll, net::UnescapeRule::SPACES, &parsed, &prefix_end_, 406 net::kFormatUrlOmitAll, net::UnescapeRule::SPACES, &parsed, &prefix_end_,
402 NULL); 407 NULL);
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after
569 size_t current_col_; 574 size_t current_col_;
570 575
571 // True when we do whitespace to newline conversions ourselves. 576 // True when we do whitespace to newline conversions ourselves.
572 bool strict_; 577 bool strict_;
573 578
574 // True when some of the input has been truncated. 579 // True when some of the input has been truncated.
575 bool suppressed_; 580 bool suppressed_;
576 581
577 // String onto which the output is accumulated. 582 // String onto which the output is accumulated.
578 string16* output_; 583 string16* output_;
584
585 DISALLOW_COPY_AND_ASSIGN(RectangleString);
579 }; 586 };
580 587
581 void RectangleString::AddString(const string16& input) { 588 void RectangleString::AddString(const string16& input) {
582 base::i18n::BreakIterator lines(input, 589 base::i18n::BreakIterator lines(input,
583 base::i18n::BreakIterator::BREAK_NEWLINE); 590 base::i18n::BreakIterator::BREAK_NEWLINE);
584 if (lines.Init()) { 591 if (lines.Init()) {
585 while (lines.Advance()) 592 while (lines.Advance())
586 AddLine(lines.GetString()); 593 AddLine(lines.GetString());
587 } else { 594 } else {
588 NOTREACHED() << "BreakIterator (lines) init failed"; 595 NOTREACHED() << "BreakIterator (lines) init failed";
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after
730 index = char_iterator.getIndex(); 737 index = char_iterator.getIndex();
731 } else { 738 } else {
732 // String has leading whitespace, return the elide string. 739 // String has leading whitespace, return the elide string.
733 return kElideString; 740 return kElideString;
734 } 741 }
735 } 742 }
736 return string.substr(0, index) + kElideString; 743 return string.substr(0, index) + kElideString;
737 } 744 }
738 745
739 } // namespace ui 746 } // namespace ui
OLDNEW
« no previous file with comments | « ui/base/text/text_elider.h ('k') | ui/base/text/text_elider_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698