autocomplete_result_view.cc revision dc0f95d653279beabeb9817299e2902918ba123e
1dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// Use of this source code is governed by a BSD-style license that can be 3dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// found in the LICENSE file. 4dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 5dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "chrome/browser/ui/views/autocomplete/autocomplete_result_view.h" 6dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 7dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "base/i18n/bidi_line_iterator.h" 8dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "chrome/browser/ui/views/autocomplete/autocomplete_result_view_model.h" 9dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "chrome/browser/ui/views/location_bar/location_bar_view.h" 10dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "grit/generated_resources.h" 11dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "grit/theme_resources.h" 12dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "ui/base/l10n/l10n_util.h" 13dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "ui/base/resource/resource_bundle.h" 14dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "ui/base/text/text_elider.h" 15dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "ui/gfx/canvas_skia.h" 16dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "ui/gfx/color_utils.h" 17dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 18dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#if defined(OS_LINUX) 19dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "chrome/browser/ui/gtk/gtk_util.h" 20dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "ui/gfx/skia_utils_gtk.h" 21dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#endif 22dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 23dc0f95d653279beabeb9817299e2902918ba123eKristian Monsennamespace { 24dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 25dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenconst char16 kEllipsis[] = { 0x2026 }; 26dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 27dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// The minimum distance between the top and bottom of the {icon|text} and the 28dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// top or bottom of the row. 29dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenconst int kMinimumIconVerticalPadding = 2; 30dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenconst int kMinimumTextVerticalPadding = 3; 31dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 32dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} // namespace 33dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 34dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen//////////////////////////////////////////////////////////////////////////////// 35dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// AutocompleteResultView, public: 36dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 37dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// Precalculated data used to draw the portion of a match classification that 38dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// fits entirely within one run. 39dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenstruct AutocompleteResultView::ClassificationData { 40dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen string16 text; 41dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const gfx::Font* font; 42dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen SkColor color; 43dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int pixel_width; 44dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}; 45dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 46dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// Precalculated data used to draw a complete visual run within the match. 47dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// This will include all or part of at leasdt one, and possibly several, 48dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// classifications. 49dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenstruct AutocompleteResultView::RunData { 50dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen size_t run_start; // Offset within the match text where this run begins. 51dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int visual_order; // Where this run occurs in visual order. The earliest 52dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // run drawn is run 0. 53dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen bool is_rtl; 54dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int pixel_width; 55dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen Classifications classifications; // Classification pieces within this run, 56dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // in logical order. 57dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}; 58dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 59dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// This class is a utility class for calculations affected by whether the result 60dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// view is horizontally mirrored. The drawing functions can be written as if 61dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// all drawing occurs left-to-right, and then use this class to get the actual 62dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// coordinates to begin drawing onscreen. 63dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenclass AutocompleteResultView::MirroringContext { 64dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen public: 65dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen MirroringContext() : center_(0), right_(0) {} 66dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 67dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Tells the mirroring context to use the provided range as the physical 68dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // bounds of the drawing region. When coordinate mirroring is needed, the 69dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // mirror point will be the center of this range. 70dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen void Initialize(int x, int width) { 71dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen center_ = x + width / 2; 72dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen right_ = x + width; 73dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 74dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 75dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Given a logical range within the drawing region, returns the coordinate of 76dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // the possibly-mirrored "left" side. (This functions exactly like 77dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // View::MirroredLeftPointForRect().) 78dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int mirrored_left_coord(int left, int right) const { 79dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return base::i18n::IsRTL() ? (center_ + (center_ - right)) : left; 80dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 81dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 82dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Given a logical coordinate within the drawing region, returns the remaining 83dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // width available. 84dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int remaining_width(int x) const { 85dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return right_ - x; 86dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 87dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 88dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen private: 89dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int center_; 90dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int right_; 91dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 92dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DISALLOW_COPY_AND_ASSIGN(MirroringContext); 93dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}; 94dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 95dc0f95d653279beabeb9817299e2902918ba123eKristian MonsenAutocompleteResultView::AutocompleteResultView( 96dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen AutocompleteResultViewModel* model, 97dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int model_index, 98dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const gfx::Font& font, 99dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const gfx::Font& bold_font) 100dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen : icon_vertical_padding_(kMinimumIconVerticalPadding), 101dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen text_vertical_padding_(kMinimumTextVerticalPadding), 102dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen model_(model), 103dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen model_index_(model_index), 104dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen normal_font_(font), 105dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen bold_font_(bold_font), 106dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen ellipsis_width_(font.GetStringWidth(string16(kEllipsis))), 107dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen mirroring_context_(new MirroringContext()), 108dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen match_(NULL, 0, false, AutocompleteMatch::URL_WHAT_YOU_TYPED) { 109dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen CHECK_GE(model_index, 0); 110dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (icon_size_ == 0) { 111dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen icon_size_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( 112dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen AutocompleteMatch::TypeToIcon(AutocompleteMatch::URL_WHAT_YOU_TYPED))-> 113dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen width(); 114dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 115dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 116dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 117dc0f95d653279beabeb9817299e2902918ba123eKristian MonsenAutocompleteResultView::~AutocompleteResultView() { 118dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 119dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 120dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid AutocompleteResultView::OnPaint(gfx::Canvas* canvas) { 121dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const ResultViewState state = GetState(); 122dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (state != NORMAL) 123dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen canvas->AsCanvasSkia()->drawColor(GetColor(state, BACKGROUND)); 124dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 125dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Paint the icon. 126dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen canvas->DrawBitmapInt(*GetIcon(), GetMirroredXForRect(icon_bounds_), 127dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen icon_bounds_.y()); 128dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 129dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Paint the text. 130dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int x = GetMirroredXForRect(text_bounds_); 131dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen mirroring_context_->Initialize(x, text_bounds_.width()); 132dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen PaintMatch(canvas, match_, x); 133dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 134dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 135dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid AutocompleteResultView::Layout() { 136dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen icon_bounds_.SetRect(LocationBarView::kEdgeItemPadding, 137dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen (height() - icon_size_) / 2, icon_size_, icon_size_); 138dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int text_x = icon_bounds_.right() + LocationBarView::kItemPadding; 139dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int font_height = std::max(normal_font_.GetHeight(), bold_font_.GetHeight()); 140dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen text_bounds_.SetRect(text_x, std::max(0, (height() - font_height) / 2), 141dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen std::max(bounds().width() - text_x - LocationBarView::kEdgeItemPadding, 142dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 0), font_height); 143dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 144dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 145dc0f95d653279beabeb9817299e2902918ba123eKristian Monsengfx::Size AutocompleteResultView::GetPreferredSize() { 146dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return gfx::Size(0, GetPreferredHeight(normal_font_, bold_font_)); 147dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 148dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 149dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenint AutocompleteResultView::GetPreferredHeight( 150dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const gfx::Font& font, 151dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const gfx::Font& bold_font) { 152dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int text_height = std::max(font.GetHeight(), bold_font.GetHeight()) + 153dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen (text_vertical_padding_ * 2); 154dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int icon_height = icon_size_ + (icon_vertical_padding_ * 2); 155dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return std::max(icon_height, text_height); 156dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 157dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 158dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// static 159dc0f95d653279beabeb9817299e2902918ba123eKristian MonsenSkColor AutocompleteResultView::GetColor(ResultViewState state, 160dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen ColorKind kind) { 161dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen static bool initialized = false; 162dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen static SkColor colors[NUM_STATES][NUM_KINDS]; 163dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (!initialized) { 164dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#if defined(OS_WIN) 165dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen colors[NORMAL][BACKGROUND] = color_utils::GetSysSkColor(COLOR_WINDOW); 166dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen colors[SELECTED][BACKGROUND] = color_utils::GetSysSkColor(COLOR_HIGHLIGHT); 167dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen colors[NORMAL][TEXT] = color_utils::GetSysSkColor(COLOR_WINDOWTEXT); 168dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen colors[SELECTED][TEXT] = color_utils::GetSysSkColor(COLOR_HIGHLIGHTTEXT); 169dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#elif defined(OS_LINUX) 170dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen GdkColor bg_color, selected_bg_color, text_color, selected_text_color; 171dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen gtk_util::GetTextColors( 172dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen &bg_color, &selected_bg_color, &text_color, &selected_text_color); 173dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen colors[NORMAL][BACKGROUND] = gfx::GdkColorToSkColor(bg_color); 174dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen colors[SELECTED][BACKGROUND] = gfx::GdkColorToSkColor(selected_bg_color); 175dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen colors[NORMAL][TEXT] = gfx::GdkColorToSkColor(text_color); 176dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen colors[SELECTED][TEXT] = gfx::GdkColorToSkColor(selected_text_color); 177dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#else 178dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // TODO(beng): source from theme provider. 179dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen colors[NORMAL][BACKGROUND] = SK_ColorWHITE; 180dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen colors[SELECTED][BACKGROUND] = SK_ColorBLUE; 181dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen colors[NORMAL][TEXT] = SK_ColorBLACK; 182dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen colors[SELECTED][TEXT] = SK_ColorWHITE; 183dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#endif 184dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen colors[HOVERED][BACKGROUND] = 185dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen color_utils::AlphaBlend(colors[SELECTED][BACKGROUND], 186dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen colors[NORMAL][BACKGROUND], 64); 187dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen colors[HOVERED][TEXT] = colors[NORMAL][TEXT]; 188dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen for (int i = 0; i < NUM_STATES; ++i) { 189dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen colors[i][DIMMED_TEXT] = 190dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen color_utils::AlphaBlend(colors[i][TEXT], colors[i][BACKGROUND], 128); 191dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen colors[i][URL] = color_utils::GetReadableColor(SkColorSetRGB(0, 128, 0), 192dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen colors[i][BACKGROUND]); 193dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 194dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen initialized = true; 195dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 196dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 197dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return colors[state][kind]; 198dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 199dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 200dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen//////////////////////////////////////////////////////////////////////////////// 201dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// AutocompleteResultView, protected: 202dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 203dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid AutocompleteResultView::PaintMatch(gfx::Canvas* canvas, 204dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const AutocompleteMatch& match, 205dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int x) { 206dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen x = DrawString(canvas, match.contents, match.contents_class, false, x, 207dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen text_bounds_.y()); 208dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 209dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Paint the description. 210dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // TODO(pkasting): Because we paint in multiple separate pieces, we can wind 211dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // up with no space even for an ellipsis for one or both of these pieces. 212dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Instead, we should paint the entire match as a single long string. This 213dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // would also let us use a more properly-localizable string than we get with 214dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // just the IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR. 215dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (!match.description.empty()) { 216dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen string16 separator = 217dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR); 218dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen ACMatchClassifications classifications; 219dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen classifications.push_back( 220dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen ACMatchClassification(0, ACMatchClassification::NONE)); 221dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen x = DrawString(canvas, separator, classifications, true, x, 222dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen text_bounds_.y()); 223dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 224dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DrawString(canvas, match.description, match.description_class, true, x, 225dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen text_bounds_.y()); 226dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 227dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 228dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 229dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// static 230dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenbool AutocompleteResultView::SortRunsLogically(const RunData& lhs, 231dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const RunData& rhs) { 232dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return lhs.run_start < rhs.run_start; 233dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 234dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 235dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// static 236dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenbool AutocompleteResultView::SortRunsVisually(const RunData& lhs, 237dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const RunData& rhs) { 238dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return lhs.visual_order < rhs.visual_order; 239dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 240dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 241dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// static 242dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenint AutocompleteResultView::icon_size_ = 0; 243dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 244dc0f95d653279beabeb9817299e2902918ba123eKristian MonsenAutocompleteResultView::ResultViewState 245dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen AutocompleteResultView::GetState() const { 246dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (model_->IsSelectedIndex(model_index_)) 247dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return SELECTED; 248dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return model_->IsHoveredIndex(model_index_) ? HOVERED : NORMAL; 249dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 250dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 251dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenconst SkBitmap* AutocompleteResultView::GetIcon() const { 252dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const SkBitmap* bitmap = model_->GetSpecialIcon(model_index_); 253dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (bitmap) 254dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return bitmap; 255dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 256dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int icon = match_.starred ? 257dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen IDR_OMNIBOX_STAR : AutocompleteMatch::TypeToIcon(match_.type); 258dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (model_->IsSelectedIndex(model_index_)) { 259dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen switch (icon) { 260dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen case IDR_OMNIBOX_HTTP: icon = IDR_OMNIBOX_HTTP_SELECTED; break; 261dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen case IDR_OMNIBOX_HISTORY: icon = IDR_OMNIBOX_HISTORY_SELECTED; break; 262dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen case IDR_OMNIBOX_SEARCH: icon = IDR_OMNIBOX_SEARCH_SELECTED; break; 263dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen case IDR_OMNIBOX_STAR: icon = IDR_OMNIBOX_STAR_SELECTED; break; 264dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen default: NOTREACHED(); break; 265dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 266dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 267dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return ResourceBundle::GetSharedInstance().GetBitmapNamed(icon); 268dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 269dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 270dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenint AutocompleteResultView::DrawString( 271dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen gfx::Canvas* canvas, 272dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const string16& text, 273dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const ACMatchClassifications& classifications, 274dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen bool force_dim, 275dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int x, 276dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int y) { 277dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (text.empty()) 278dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return x; 279dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 280dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Check whether or not this text is a URL. URLs are always displayed LTR 281dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // regardless of locale. 282dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen bool is_url = true; 283dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen for (ACMatchClassifications::const_iterator i(classifications.begin()); 284dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen i != classifications.end(); ++i) { 285dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (!(i->style & ACMatchClassification::URL)) { 286dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen is_url = false; 287dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen break; 288dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 289dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 290dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 291dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Split the text into visual runs. We do this first so that we don't need to 292dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // worry about whether our eliding might change the visual display in 293dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // unintended ways, e.g. by removing directional markings or by adding an 294dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // ellipsis that's not enclosed in appropriate markings. 295dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen base::i18n::BiDiLineIterator bidi_line; 296dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (!bidi_line.Open(text, base::i18n::IsRTL(), is_url)) 297dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return x; 298dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const int num_runs = bidi_line.CountRuns(); 299dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen Runs runs; 300dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen for (int run = 0; run < num_runs; ++run) { 301dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int run_start_int = 0, run_length_int = 0; 302dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // The index we pass to GetVisualRun corresponds to the position of the run 303dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // in the displayed text. For example, the string "Google in HEBREW" (where 304dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // HEBREW is text in the Hebrew language) has two runs: "Google in " which 305dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // is an LTR run, and "HEBREW" which is an RTL run. In an LTR context, the 306dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // run "Google in " has the index 0 (since it is the leftmost run 307dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // displayed). In an RTL context, the same run has the index 1 because it 308dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // is the rightmost run. This is why the order in which we traverse the 309dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // runs is different depending on the locale direction. 310dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const UBiDiDirection run_direction = bidi_line.GetVisualRun( 311dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen (base::i18n::IsRTL() && !is_url) ? (num_runs - run - 1) : run, 312dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen &run_start_int, &run_length_int); 313dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DCHECK_GT(run_length_int, 0); 314dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen runs.push_back(RunData()); 315dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen RunData* current_run = &runs.back(); 316dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen current_run->run_start = run_start_int; 317dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const size_t run_end = current_run->run_start + run_length_int; 318dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen current_run->visual_order = run; 319dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen current_run->is_rtl = !is_url && (run_direction == UBIDI_RTL); 320dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen current_run->pixel_width = 0; 321dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 322dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Compute classifications for this run. 323dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen for (size_t i = 0; i < classifications.size(); ++i) { 324dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const size_t text_start = 325dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen std::max(classifications[i].offset, current_run->run_start); 326dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (text_start >= run_end) 327dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen break; // We're past the last classification in the run. 328dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 329dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const size_t text_end = (i < (classifications.size() - 1)) ? 330dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen std::min(classifications[i + 1].offset, run_end) : run_end; 331dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (text_end <= current_run->run_start) 332dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen continue; // We haven't reached the first classification in the run. 333dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 334dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen current_run->classifications.push_back(ClassificationData()); 335dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen ClassificationData* current_data = 336dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen ¤t_run->classifications.back(); 337dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen current_data->text = text.substr(text_start, text_end - text_start); 338dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 339dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Calculate style-related data. 340dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const int style = classifications[i].style; 341dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const bool use_bold_font = !!(style & ACMatchClassification::MATCH); 342dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen current_data->font = &(use_bold_font ? bold_font_ : normal_font_); 343dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const ResultViewState state = GetState(); 344dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (style & ACMatchClassification::URL) 345dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen current_data->color = GetColor(state, URL); 346dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen else if (style & ACMatchClassification::DIM) 347dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen current_data->color = GetColor(state, DIMMED_TEXT); 348dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen else 349dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen current_data->color = GetColor(state, force_dim ? DIMMED_TEXT : TEXT); 350dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen current_data->pixel_width = 351dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen current_data->font->GetStringWidth(current_data->text); 352dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen current_run->pixel_width += current_data->pixel_width; 353dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 354dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DCHECK(!current_run->classifications.empty()); 355dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 356dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DCHECK(!runs.empty()); 357dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 358dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Sort into logical order so we can elide logically. 359dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen std::sort(runs.begin(), runs.end(), &SortRunsLogically); 360dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 361dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Now determine what to elide, if anything. Several subtle points: 362dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // * Because we have the run data, we can get edge cases correct, like 363dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // whether to place an ellipsis before or after the end of a run when the 364dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // text needs to be elided at the run boundary. 365dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // * The "or one before it" comments below refer to cases where an earlier 366dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // classification fits completely, but leaves too little space for an 367dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // ellipsis that turns out to be needed later. These cases are commented 368dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // more completely in Elide(). 369dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int remaining_width = mirroring_context_->remaining_width(x); 370dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen for (Runs::iterator i(runs.begin()); i != runs.end(); ++i) { 371dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (i->pixel_width > remaining_width) { 372dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // This run or one before it needs to be elided. 373dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen for (Classifications::iterator j(i->classifications.begin()); 374dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen j != i->classifications.end(); ++j) { 375dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (j->pixel_width > remaining_width) { 376dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // This classification or one before it needs to be elided. Erase all 377dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // further classifications and runs so Elide() can simply reverse- 378dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // iterate over everything to find the specific classification to 379dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // elide. 380dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen i->classifications.erase(++j, i->classifications.end()); 381dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen runs.erase(++i, runs.end()); 382dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen Elide(&runs, remaining_width); 383dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen break; 384dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 385dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen remaining_width -= j->pixel_width; 386dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 387dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen break; 388dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 389dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen remaining_width -= i->pixel_width; 390dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 391dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 392dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Sort back into visual order so we can display the runs correctly. 393dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen std::sort(runs.begin(), runs.end(), &SortRunsVisually); 394dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 395dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Draw the runs. 396dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen for (Runs::iterator i(runs.begin()); i != runs.end(); ++i) { 397dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const bool reverse_visible_order = (i->is_rtl != base::i18n::IsRTL()); 398dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int flags = gfx::Canvas::NO_ELLIPSIS; // We've already elided. 399dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (reverse_visible_order) { 400dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen std::reverse(i->classifications.begin(), i->classifications.end()); 401dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (i->is_rtl) 402dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen flags |= gfx::Canvas::FORCE_RTL_DIRECTIONALITY; 403dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 404dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen for (Classifications::const_iterator j(i->classifications.begin()); 405dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen j != i->classifications.end(); ++j) { 406dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int left = mirroring_context_->mirrored_left_coord(x, x + j->pixel_width); 407dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen canvas->DrawStringInt(j->text, *j->font, j->color, left, 408dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen y, j->pixel_width, j->font->GetHeight(), flags); 409dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen x += j->pixel_width; 410dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 411dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 412dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 413dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return x; 414dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 415dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 416dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid AutocompleteResultView::Elide(Runs* runs, int remaining_width) const { 417dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // The complexity of this function is due to edge cases like the following: 418dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // We have 100 px of available space, an initial classification that takes 86 419dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // px, and a font that has a 15 px wide ellipsis character. Now if the first 420dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // classification is followed by several very narrow classifications (e.g. 3 421dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // px wide each), we don't know whether we need to elide or not at the time we 422dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // see the first classification -- it depends on how many subsequent 423dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // classifications follow, and some of those may be in the next run (or 424dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // several runs!). This is why instead we let our caller move forward until 425dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // we know we definitely need to elide, and then in this function we move 426dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // backward again until we find a string that we can successfully do the 427dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // eliding on. 428dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen bool first_classification = true; 429dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen for (Runs::reverse_iterator i(runs->rbegin()); i != runs->rend(); ++i) { 430dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen for (Classifications::reverse_iterator j(i->classifications.rbegin()); 431dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen j != i->classifications.rend(); ++j) { 432dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (!first_classification) { 433dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // For all but the first classification we consider, we need to append 434dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // an ellipsis, since there isn't enough room to draw it after this 435dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // classification. 436dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen j->text += kEllipsis; 437dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 438dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // We also add this classification's width (sans ellipsis) back to the 439dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // available width since we want to consider the available space we'll 440dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // have when we draw this classification. 441dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen remaining_width += j->pixel_width; 442dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 443dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen first_classification = false; 444dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 445dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Can we fit at least an ellipsis? 446dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen string16 elided_text = 447dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen ui::ElideText(j->text, *j->font, remaining_width, false); 448dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen Classifications::reverse_iterator prior_classification(j); 449dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen ++prior_classification; 450dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const bool on_first_classification = 451dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen (prior_classification == i->classifications.rend()); 452dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (elided_text.empty() && (remaining_width >= ellipsis_width_) && 453dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen on_first_classification) { 454dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Edge case: This classification is bold, we can't fit a bold ellipsis 455dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // but we can fit a normal one, and this is the first classification in 456dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // the run. We should display a lone normal ellipsis, because appending 457dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // one to the end of the previous run might put it in the wrong visual 458dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // location (if the previous run is reversed from the normal visual 459dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // order). 460dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // NOTE: If this isn't the first classification in the run, we don't 461dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // need to bother with this; see note below. 462dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen elided_text = kEllipsis; 463dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 464dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (!elided_text.empty()) { 465dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Success. Elide this classification and stop. 466dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen j->text = elided_text; 467dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 468dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // If we could only fit an ellipsis, then only make it bold if there was 469dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // an immediate prior classification in this run that was also bold, or 470dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // it will look orphaned. 471dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if ((elided_text.length() == 1) && 472dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen (on_first_classification || 473dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen (prior_classification->font == &normal_font_))) 474dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen j->font = &normal_font_; 475dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 476dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen j->pixel_width = j->font->GetStringWidth(elided_text); 477dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 478dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Erase any other classifications that come after the elided one. 479dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen i->classifications.erase(j.base(), i->classifications.end()); 480dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen runs->erase(i.base(), runs->end()); 481dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return; 482dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 483dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 484dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // We couldn't fit an ellipsis. Move back one classification, 485dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // append an ellipsis, and try again. 486dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // NOTE: In the edge case that a bold ellipsis doesn't fit but a 487dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // normal one would, and we reach here, then there is a previous 488dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // classification in this run, and so either: 489dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // * It's normal, and will be able to draw successfully with the 490dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // ellipsis we'll append to it, or 491dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // * It is also bold, in which case we don't want to fall back 492dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // to a normal ellipsis anyway (see comment above). 493dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 494dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 495dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 496dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // We couldn't draw anything. 497dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen runs->clear(); 498dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 499