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          &current_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