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