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

Side by Side Diff: chrome/browser/autocomplete/autocomplete_popup_view_win.cc

Issue 160378: Make TOOLKIT_VIEWS port use views-based Autocomplete popup.... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 11 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/autocomplete/autocomplete_popup_view_win.h"
6
7 // TODO(deanm): Clean up these includes, not going to fight it now.
8 #include <atlbase.h>
9 #include <atlapp.h>
10 #include <atlcrack.h>
11 #include <atlmisc.h>
12 #include <cmath>
13
14 #include "app/gfx/canvas.h"
15 #include "app/gfx/font.h"
16 #include "app/l10n_util.h"
17 #include "app/resource_bundle.h"
18 #include "base/command_line.h"
19 #include "base/string_util.h"
20 #include "base/win_util.h"
21 #include "chrome/browser/autocomplete/autocomplete.h"
22 #include "chrome/browser/autocomplete/autocomplete_edit.h"
23 #include "chrome/browser/autocomplete/autocomplete_popup_model.h"
24 #include "chrome/browser/browser_process.h"
25 #include "chrome/browser/net/dns_global.h"
26 #include "chrome/browser/profile.h"
27 #include "chrome/browser/search_engines/template_url.h"
28 #include "chrome/browser/search_engines/template_url_model.h"
29 #include "chrome/browser/views/autocomplete/autocomplete_popup_contents_view.h"
30 #include "chrome/browser/views/location_bar_view.h"
31 #include "chrome/common/chrome_switches.h"
32 #include "chrome/common/notification_service.h"
33 #include "grit/theme_resources.h"
34 #include "third_party/icu38/public/common/unicode/ubidi.h"
35 #include "views/view.h"
36
37 // Padding between text and the star indicator, in pixels.
38 static const int kStarPadding = 4;
39
40 // This class implements a utility used for mirroring x-coordinates when the
41 // application language is a right-to-left one.
42 class AutocompletePopupViewWin::MirroringContext {
43 public:
44 MirroringContext() : min_x_(0), center_x_(0), max_x_(0), enabled_(false) { }
45
46 // Initializes the bounding region used for mirroring coordinates.
47 // This class uses the center of this region as an axis for calculating
48 // mirrored coordinates.
49 void Initialize(int x1, int x2, bool enabled);
50
51 // Return the "left" side of the specified region.
52 // When the application language is a right-to-left one, this function
53 // calculates the mirrored coordinates of the input region and returns the
54 // left side of the mirrored region.
55 // The input region must be in the bounding region specified in the
56 // Initialize() function.
57 int GetLeft(int x1, int x2) const;
58
59 // Returns whether or not we are mirroring the x coordinate.
60 bool enabled() const {
61 return enabled_;
62 }
63
64 private:
65 int min_x_;
66 int center_x_;
67 int max_x_;
68 bool enabled_;
69
70 DISALLOW_EVIL_CONSTRUCTORS(MirroringContext);
71 };
72
73 void AutocompletePopupViewWin::MirroringContext::Initialize(int x1,
74 int x2,
75 bool enabled) {
76 min_x_ = std::min(x1, x2);
77 max_x_ = std::max(x1, x2);
78 center_x_ = min_x_ + (max_x_ - min_x_) / 2;
79 enabled_ = enabled;
80 }
81
82 int AutocompletePopupViewWin::MirroringContext::GetLeft(int x1, int x2) const {
83 return enabled_ ?
84 (center_x_ + (center_x_ - std::max(x1, x2))) : std::min(x1, x2);
85 }
86
87 const wchar_t AutocompletePopupViewWin::DrawLineInfo::ellipsis_str[] =
88 L"\x2026";
89
90 AutocompletePopupViewWin::DrawLineInfo::DrawLineInfo(const gfx::Font& font) {
91 // Create regular and bold fonts.
92 regular_font = font.DeriveFont(-1);
93 bold_font = regular_font.DeriveFont(0, gfx::Font::BOLD);
94
95 // The total padding added to each line (bottom padding is what is
96 // left over after DrawEntry() specifies its top offset).
97 static const int kTotalLinePadding = 5;
98 font_height = std::max(regular_font.height(), bold_font.height());
99 line_height = font_height + kTotalLinePadding;
100 ave_char_width = regular_font.GetExpectedTextWidth(1);
101 ellipsis_width = std::max(regular_font.GetStringWidth(ellipsis_str),
102 bold_font.GetStringWidth(ellipsis_str));
103
104 // Create background colors.
105 background_colors[NORMAL] = GetSysColor(COLOR_WINDOW);
106 background_colors[SELECTED] = GetSysColor(COLOR_HIGHLIGHT);
107 background_colors[HOVERED] =
108 AlphaBlend(background_colors[SELECTED], background_colors[NORMAL], 0x40);
109
110 // Create text colors.
111 text_colors[NORMAL] = GetSysColor(COLOR_WINDOWTEXT);
112 text_colors[HOVERED] = text_colors[NORMAL];
113 text_colors[SELECTED] = GetSysColor(COLOR_HIGHLIGHTTEXT);
114
115 // Create brushes and url colors.
116 const COLORREF dark_url(0x008000);
117 const COLORREF light_url(0xd0ffd0);
118 for (int i = 0; i < MAX_STATUS_ENTRIES; ++i) {
119 // Pick whichever URL color contrasts better.
120 const double dark_contrast =
121 LuminosityContrast(dark_url, background_colors[i]);
122 const double light_contrast =
123 LuminosityContrast(light_url, background_colors[i]);
124 url_colors[i] = (dark_contrast > light_contrast) ? dark_url : light_url;
125
126 brushes[i] = CreateSolidBrush(background_colors[i]);
127 }
128 }
129
130 AutocompletePopupViewWin::DrawLineInfo::~DrawLineInfo() {
131 for (int i = 0; i < MAX_STATUS_ENTRIES; ++i)
132 DeleteObject(brushes[i]);
133 }
134
135 // static
136 double AutocompletePopupViewWin::DrawLineInfo::LuminosityContrast(
137 COLORREF color1,
138 COLORREF color2) {
139 // This algorithm was adapted from the following text at
140 // http://juicystudio.com/article/luminositycontrastratioalgorithm.php :
141 //
142 // "[Luminosity contrast can be calculated as] (L1+.05) / (L2+.05) where L is
143 // luminosity and is defined as .2126*R + .7152*G + .0722B using linearised
144 // R, G, and B values. Linearised R (for example) = (R/FS)^2.2 where FS is
145 // full scale value (255 for 8 bit color channels). L1 is the higher value
146 // (of text or background) and L2 is the lower value.
147 //
148 // The Gamma correction and RGB constants are derived from the Standard
149 // Default Color Space for the Internet (sRGB), and the 0.05 offset is
150 // included to compensate for contrast ratios that occur when a value is at
151 // or near zero, and for ambient light effects.
152 const double l1 = Luminosity(color1);
153 const double l2 = Luminosity(color2);
154 return (l1 > l2) ? ((l1 + 0.05) / (l2 + 0.05)) : ((l2 + 0.05) / (l1 + 0.05));
155 }
156
157 // static
158 double AutocompletePopupViewWin::DrawLineInfo::Luminosity(COLORREF color) {
159 // See comments in LuminosityContrast().
160 const double linearised_r =
161 pow(static_cast<double>(GetRValue(color)) / 255.0, 2.2);
162 const double linearised_g =
163 pow(static_cast<double>(GetGValue(color)) / 255.0, 2.2);
164 const double linearised_b =
165 pow(static_cast<double>(GetBValue(color)) / 255.0, 2.2);
166 return (0.2126 * linearised_r) + (0.7152 * linearised_g) +
167 (0.0722 * linearised_b);
168 }
169
170 COLORREF AutocompletePopupViewWin::DrawLineInfo::AlphaBlend(
171 COLORREF foreground,
172 COLORREF background,
173 BYTE alpha) {
174 if (alpha == 0)
175 return background;
176 else if (alpha == 0xff)
177 return foreground;
178
179 return RGB(
180 ((GetRValue(foreground) * alpha) +
181 (GetRValue(background) * (0xff - alpha))) / 0xff,
182 ((GetGValue(foreground) * alpha) +
183 (GetGValue(background) * (0xff - alpha))) / 0xff,
184 ((GetBValue(foreground) * alpha) +
185 (GetBValue(background) * (0xff - alpha))) / 0xff);
186 }
187
188 AutocompletePopupViewWin::AutocompletePopupViewWin(
189 const gfx::Font& font,
190 AutocompleteEditViewWin* edit_view,
191 AutocompleteEditModel* edit_model,
192 Profile* profile)
193 : model_(new AutocompletePopupModel(this, edit_model, profile)),
194 edit_view_(edit_view),
195 line_info_(font),
196 mirroring_context_(new MirroringContext()),
197 star_(ResourceBundle::GetSharedInstance().GetBitmapNamed(
198 IDR_CONTENT_STAR_ON)) {
199 }
200
201 void AutocompletePopupViewWin::InvalidateLine(size_t line) {
202 RECT rc;
203 GetClientRect(&rc);
204 rc.top = LineTopPixel(line);
205 rc.bottom = rc.top + line_info_.line_height;
206 InvalidateRect(&rc, false);
207 }
208
209 void AutocompletePopupViewWin::UpdatePopupAppearance() {
210 const AutocompleteResult& result = model_->result();
211 if (result.empty()) {
212 // No matches, close any existing popup.
213 if (m_hWnd) {
214 DestroyWindow();
215 m_hWnd = NULL;
216 }
217 return;
218 }
219
220 // Figure the coordinates of the popup:
221 // Get the coordinates of the location bar view; these are returned relative
222 // to its parent.
223 // TODO(pkasting): http://b/1345937 All this use of editor accessors should
224 // die once this class is a true ChromeView.
225 CRect rc = edit_view_->parent_view()->bounds().ToRECT();
226 // Subtract the top left corner to make the coordinates relative to the
227 // location bar view itself, and convert to screen coordinates.
228 gfx::Point top_left(-rc.TopLeft());
229 views::View::ConvertPointToScreen(edit_view_->parent_view(), &top_left);
230 rc.OffsetRect(top_left.ToPOINT());
231 // Expand by one pixel on each side since that's the amount the location bar
232 // view is inset from the divider line that edges the adjacent buttons.
233 // Deflate the top and bottom by the height of the extra graphics around the
234 // edit.
235 // TODO(pkasting): http://b/972786 This shouldn't be hardcoded to rely on
236 // LocationBarView constants. Instead we should just make the edit be "at the
237 // right coordinates", or something else generic.
238 rc.InflateRect(1, -LocationBarView::kVertMargin);
239 // Now rc is the exact width we want and is positioned like the edit would
240 // be, so shift the top and bottom downwards so the new top is where the old
241 // bottom is and the rect has the height we need for all our entries, plus a
242 // one-pixel border on top and bottom.
243 rc.top = rc.bottom;
244 rc.bottom += static_cast<int>(result.size()) * line_info_.line_height + 2;
245
246 if (!m_hWnd) {
247 // To prevent this window from being activated, we create an invisible
248 // window and manually show it without activating it.
249 Create(edit_view_->m_hWnd, rc, AUTOCOMPLETEPOPUPVIEW_CLASSNAME, WS_POPUP,
250 WS_EX_TOOLWINDOW);
251 // When an IME is attached to the rich-edit control, retrieve its window
252 // handle and show this popup window under the IME windows.
253 // Otherwise, show this popup window under top-most windows.
254 // TODO(hbono): http://b/1111369 if we exclude this popup window from the
255 // display area of IME windows, this workaround becomes unnecessary.
256 HWND ime_window = ImmGetDefaultIMEWnd(edit_view_->m_hWnd);
257 SetWindowPos(ime_window ? ime_window : HWND_NOTOPMOST, 0, 0, 0, 0,
258 SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
259 } else {
260 // Already open, just resize the window. This is a bit tricky; we want to
261 // repaint the whole window, since the contents may have changed, but
262 // MoveWindow() won't repaint portions that haven't moved or been
263 // added/removed. So we first call InvalidateRect(), so the next repaint
264 // paints the whole window, then tell MoveWindow() to do the actual
265 // repaint, which will also properly repaint Windows formerly under the
266 // popup.
267 InvalidateRect(NULL, false);
268 MoveWindow(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, true);
269 }
270
271 // TODO(pkasting): http://b/1111369 We should call ImmSetCandidateWindow() on
272 // the edit_view_'s IME context here, and exclude ourselves from its display
273 // area. Not clear what to pass for the lpCandidate->ptCurrentPos member,
274 // though...
275 }
276
277 void AutocompletePopupViewWin::OnHoverEnabledOrDisabled(bool disabled) {
278 TRACKMOUSEEVENT tme;
279 tme.cbSize = sizeof(TRACKMOUSEEVENT);
280 if (disabled) {
281 // Save the current mouse position to check against for re-enabling.
282 GetCursorPos(&last_hover_coordinates_); // Returns screen coordinates
283
284 // Cancel existing registration for WM_MOUSELEAVE notifications.
285 tme.dwFlags = TME_CANCEL | TME_LEAVE;
286 } else {
287 // Register for WM_MOUSELEAVE notifications.
288 tme.dwFlags = TME_LEAVE;
289 }
290 tme.hwndTrack = m_hWnd;
291 tme.dwHoverTime = HOVER_DEFAULT; // Not actually used
292 TrackMouseEvent(&tme);
293 }
294
295 void AutocompletePopupViewWin::OnLButtonDown(UINT keys, const CPoint& point) {
296 const size_t new_hovered_line = PixelToLine(point.y);
297 model_->SetHoveredLine(new_hovered_line);
298 model_->SetSelectedLine(new_hovered_line, false);
299 }
300
301 void AutocompletePopupViewWin::OnMButtonDown(UINT keys, const CPoint& point) {
302 model_->SetHoveredLine(PixelToLine(point.y));
303 }
304
305 void AutocompletePopupViewWin::OnLButtonUp(UINT keys, const CPoint& point) {
306 OnButtonUp(point, CURRENT_TAB);
307 }
308
309 void AutocompletePopupViewWin::OnMButtonUp(UINT keys, const CPoint& point) {
310 OnButtonUp(point, NEW_BACKGROUND_TAB);
311 }
312
313 LRESULT AutocompletePopupViewWin::OnMouseActivate(HWND window,
314 UINT hit_test,
315 UINT mouse_message) {
316 return MA_NOACTIVATE;
317 }
318
319 void AutocompletePopupViewWin::OnMouseLeave() {
320 // The mouse has left the window, so no line is hovered.
321 model_->SetHoveredLine(AutocompletePopupModel::kNoMatch);
322 }
323
324 void AutocompletePopupViewWin::OnMouseMove(UINT keys, const CPoint& point) {
325 // Track hover when
326 // (a) The left or middle button is down (the user is interacting via the
327 // mouse)
328 // (b) The user moves the mouse from where we last stopped tracking hover
329 // (c) We started tracking previously due to (a) or (b) and haven't stopped
330 // yet (user hasn't used the keyboard to interact again)
331 const bool action_button_pressed = !!(keys & (MK_MBUTTON | MK_LBUTTON));
332 CPoint screen_point(point);
333 ClientToScreen(&screen_point);
334 if (action_button_pressed || (last_hover_coordinates_ != screen_point) ||
335 (model_->hovered_line() != AutocompletePopupModel::kNoMatch)) {
336 // Determine the hovered line from the y coordinate of the event. We don't
337 // need to check whether the x coordinates are within the window since if
338 // they weren't someone else would have received the WM_MOUSEMOVE.
339 const size_t new_hovered_line = PixelToLine(point.y);
340 model_->SetHoveredLine(new_hovered_line);
341
342 // When the user has the left button down, update their selection
343 // immediately (don't wait for mouseup).
344 if (keys & MK_LBUTTON)
345 model_->SetSelectedLine(new_hovered_line, false);
346 }
347 }
348
349 void AutocompletePopupViewWin::OnPaint(HDC other_dc) {
350 const AutocompleteResult& result = model_->result();
351 CHECK(!result.empty()); // Shouldn't be drawing an empty popup; any empty
352 // result set should have synchronously closed us.
353
354 CPaintDC dc(m_hWnd);
355
356 RECT rc;
357 GetClientRect(&rc);
358 mirroring_context_->Initialize(rc.left, rc.right,
359 l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT);
360 DrawBorder(rc, dc);
361
362 bool all_descriptions_empty = true;
363 for (AutocompleteResult::const_iterator i(result.begin()); i != result.end();
364 ++i) {
365 if (!i->description.empty()) {
366 all_descriptions_empty = false;
367 break;
368 }
369 }
370
371 // Only repaint the invalid lines.
372 // In rare cases, it seems possible to get line offsets off the end of the
373 // popup. I suspect this can happen when the user invalidates a new line
374 // (e.g. by moving the mouse) and, before the paint request is serviced, hits
375 // a key that causes autocomplete to run, causing the results list to become
376 // shorter (at least initially). So sanitize the line numbers here.
377 const size_t last_valid_line = result.size() - 1;
378 const size_t first_line = PixelToLine(dc.m_ps.rcPaint.top);
379 if (first_line > last_valid_line)
380 return;
381 const size_t last_line =
382 std::min(PixelToLine(dc.m_ps.rcPaint.bottom), last_valid_line);
383
384 for (size_t i = first_line; i <= last_line; ++i) {
385 DrawLineInfo::LineStatus status;
386 // Selection should take precedence over hover.
387 if (i == model_->selected_line())
388 status = DrawLineInfo::SELECTED;
389 else if (i == model_->hovered_line())
390 status = DrawLineInfo::HOVERED;
391 else
392 status = DrawLineInfo::NORMAL;
393 DrawEntry(dc, rc, i, status, all_descriptions_empty,
394 result.match_at(i).starred);
395 }
396 }
397
398 void AutocompletePopupViewWin::OnButtonUp(const CPoint& point,
399 WindowOpenDisposition disposition) {
400 const size_t line = PixelToLine(point.y);
401 const AutocompleteMatch& match = model_->result().match_at(line);
402 // OpenURL() may close the popup, which will clear the result set and, by
403 // extension, |match| and its contents. So copy the relevant strings out to
404 // make sure they stay alive until the call completes.
405 const GURL url(match.destination_url);
406 std::wstring keyword;
407 const bool is_keyword_hint = model_->GetKeywordForMatch(match, &keyword);
408 edit_view_->OpenURL(url, disposition, match.transition, GURL(), line,
409 is_keyword_hint ? std::wstring() : keyword);
410 }
411
412 int AutocompletePopupViewWin::LineTopPixel(size_t line) const {
413 // The popup has a 1 px top border.
414 return line_info_.line_height * static_cast<int>(line) + 1;
415 }
416
417 size_t AutocompletePopupViewWin::PixelToLine(int y) const {
418 const size_t line = std::max(y - 1, 0) / line_info_.line_height;
419 return std::min(line, model_->result().size() - 1);
420 }
421
422 // Draws a light border around the inside of the window with the given client
423 // rectangle and DC.
424 void AutocompletePopupViewWin::DrawBorder(const RECT& rc, HDC dc) {
425 HPEN hpen = CreatePen(PS_SOLID, 1, RGB(199, 202, 206));
426 HGDIOBJ old_pen = SelectObject(dc, hpen);
427
428 int width = rc.right - rc.left - 1;
429 int height = rc.bottom - rc.top - 1;
430
431 MoveToEx(dc, 0, 0, NULL);
432 LineTo(dc, 0, height);
433 LineTo(dc, width, height);
434 LineTo(dc, width, 0);
435 LineTo(dc, 0, 0);
436
437 SelectObject(dc, old_pen);
438 DeleteObject(hpen);
439 }
440
441 int AutocompletePopupViewWin::DrawString(HDC dc,
442 int x,
443 int y,
444 int max_x,
445 const wchar_t* text,
446 int length,
447 int style,
448 const DrawLineInfo::LineStatus status,
449 const MirroringContext* context,
450 bool text_direction_is_rtl) const {
451 if (length <= 0)
452 return 0;
453
454 // Set up the text decorations.
455 SelectObject(dc, (style & ACMatchClassification::MATCH) ?
456 line_info_.bold_font.hfont() : line_info_.regular_font.hfont());
457 const COLORREF foreground = (style & ACMatchClassification::URL) ?
458 line_info_.url_colors[status] : line_info_.text_colors[status];
459 const COLORREF background = line_info_.background_colors[status];
460 SetTextColor(dc, (style & ACMatchClassification::DIM) ?
461 DrawLineInfo::AlphaBlend(foreground, background, 0xAA) : foreground);
462
463 // Retrieve the width of the decorated text and display it. When we cannot
464 // display this fragment in the given width, we trim the fragment and add an
465 // ellipsis.
466 //
467 // TODO(hbono): http:///b/1222425 We should change the following eliding code
468 // with more aggressive one.
469 int text_x = x;
470 int max_length = 0;
471 SIZE text_size = {0};
472 GetTextExtentExPoint(dc, text, length,
473 max_x - line_info_.ellipsis_width - text_x, &max_length,
474 NULL, &text_size);
475
476 if (max_length < length)
477 GetTextExtentPoint32(dc, text, max_length, &text_size);
478
479 const int mirrored_x = context->GetLeft(text_x, text_x + text_size.cx);
480 RECT text_bounds = {mirrored_x,
481 0,
482 mirrored_x + text_size.cx,
483 line_info_.line_height};
484
485 int flags = DT_SINGLELINE | DT_NOPREFIX;
486 if (text_direction_is_rtl)
487 // In order to make sure RTL text is displayed correctly (for example, a
488 // trailing space should be displayed on the left and not on the right), we
489 // pass the flag DT_RTLREADING.
490 flags |= DT_RTLREADING;
491
492 DrawText(dc, text, length, &text_bounds, flags);
493 text_x += text_size.cx;
494
495 // Draw the ellipsis. Note that since we use the mirroring context, the
496 // ellipsis are drawn either to the right or to the left of the text.
497 if (max_length < length) {
498 TextOut(dc, context->GetLeft(text_x, text_x + line_info_.ellipsis_width),
499 0, line_info_.ellipsis_str, arraysize(line_info_.ellipsis_str) - 1);
500 text_x += line_info_.ellipsis_width;
501 }
502
503 return text_x - x;
504 }
505
506 void AutocompletePopupViewWin::DrawMatchFragments(
507 HDC dc,
508 const std::wstring& text,
509 const ACMatchClassifications& classifications,
510 int x,
511 int y,
512 int max_x,
513 DrawLineInfo::LineStatus status) const {
514 if (!text.length())
515 return;
516
517 // Check whether or not this text is a URL string.
518 // A URL string is basically in English with possible included words in
519 // Arabic or Hebrew. For such case, ICU provides a special algorithm and we
520 // should use it.
521 bool url = false;
522 for (ACMatchClassifications::const_iterator i = classifications.begin();
523 i != classifications.end(); ++i) {
524 if (i->style & ACMatchClassification::URL)
525 url = true;
526 }
527
528 // Initialize a bidirectional line iterator of ICU and split the text into
529 // visual runs. (A visual run is consecutive characters which have the same
530 // display direction and should be displayed at once.)
531 l10n_util::BiDiLineIterator bidi_line;
532 if (!bidi_line.Open(text, mirroring_context_->enabled(), url))
533 return;
534 const int runs = bidi_line.CountRuns();
535
536 // Draw the visual runs.
537 // This loop splits each run into text fragments with the given
538 // classifications and draws the text fragments.
539 // When the direction of a run is right-to-left, we have to mirror the
540 // x-coordinate of this run and render the fragments in the right-to-left
541 // reading order. To handle this display order independently from the one of
542 // this popup window, this loop renders a run with the steps below:
543 // 1. Create a local display context for each run;
544 // 2. Render the run into the local display context, and;
545 // 3. Copy the local display context to the one of the popup window.
546 int run_x = x;
547 for (int run = 0; run < runs; ++run) {
548 int run_start = 0;
549 int run_length = 0;
550
551 // The index we pass to GetVisualRun corresponds to the position of the run
552 // in the displayed text. For example, the string "Google in HEBREW" (where
553 // HEBREW is text in the Hebrew language) has two runs: "Google in " which
554 // is an LTR run, and "HEBREW" which is an RTL run. In an LTR context, the
555 // run "Google in " has the index 0 (since it is the leftmost run
556 // displayed). In an RTL context, the same run has the index 1 because it
557 // is the rightmost run. This is why the order in which we traverse the
558 // runs is different depending on the locale direction.
559 //
560 // Note that for URLs we always traverse the runs from lower to higher
561 // indexes because the return order of runs for a URL always matches the
562 // physical order of the context.
563 int current_run =
564 (mirroring_context_->enabled() && !url) ? (runs - run - 1) : run;
565 const UBiDiDirection run_direction = bidi_line.GetVisualRun(current_run,
566 &run_start,
567 &run_length);
568 const int run_end = run_start + run_length;
569
570 // Set up a local display context for rendering this run.
571 int text_x = 0;
572 const int text_max_x = max_x - run_x;
573 MirroringContext run_context;
574 run_context.Initialize(0, text_max_x, run_direction == UBIDI_RTL);
575
576 // In addition to creating a mirroring context for the run, we indicate
577 // whether the run needs to be rendered as RTL text. The mirroring context
578 // alone in not sufficient because there are cases where a mirrored RTL run
579 // needs to be rendered in an LTR context (for example, an RTL run within
580 // an URL).
581 bool run_direction_is_rtl = (run_direction == UBIDI_RTL) && !url;
582 CDC text_dc(CreateCompatibleDC(dc));
583 CBitmap text_bitmap(CreateCompatibleBitmap(dc, text_max_x,
584 line_info_.font_height));
585 SelectObject(text_dc, text_bitmap);
586 const RECT text_rect = {0, 0, text_max_x, line_info_.line_height};
587 FillRect(text_dc, &text_rect, line_info_.brushes[status]);
588 SetBkMode(text_dc, TRANSPARENT);
589
590 // Split this run with the given classifications and draw the fragments
591 // into the local display context.
592 for (ACMatchClassifications::const_iterator i = classifications.begin();
593 i != classifications.end(); ++i) {
594 const int text_start = std::max(run_start, static_cast<int>(i->offset));
595 const int text_end = std::min(run_end, (i != classifications.end() - 1) ?
596 static_cast<int>((i + 1)->offset) : run_end);
597 text_x += DrawString(text_dc, text_x, 0, text_max_x, &text[text_start],
598 text_end - text_start, i->style, status,
599 &run_context, run_direction_is_rtl);
600 }
601
602 // Copy the local display context to the one of the popup window and
603 // delete the local display context.
604 BitBlt(dc, mirroring_context_->GetLeft(run_x, run_x + text_x), y, text_x,
605 line_info_.line_height, text_dc, run_context.GetLeft(0, text_x), 0,
606 SRCCOPY);
607 run_x += text_x;
608 }
609 }
610
611 void AutocompletePopupViewWin::DrawEntry(HDC dc,
612 const RECT& client_rect,
613 size_t line,
614 DrawLineInfo::LineStatus status,
615 bool all_descriptions_empty,
616 bool starred) const {
617 // Calculate outer bounds of entry, and fill background.
618 const int top_pixel = LineTopPixel(line);
619 const RECT rc = {1, top_pixel, client_rect.right - client_rect.left - 1,
620 top_pixel + line_info_.line_height};
621 FillRect(dc, &rc, line_info_.brushes[status]);
622
623 // Calculate and display contents/description sections as follows:
624 // * 2 px top margin, bottom margin is handled by line_height.
625 const int y = rc.top + 2;
626
627 // * 1 char left/right margin.
628 const int side_margin = line_info_.ave_char_width;
629
630 // * 50% of the remaining width is initially allocated to each section, with
631 // a 2 char margin followed by the star column and kStarPadding padding.
632 const int content_min_x = rc.left + side_margin;
633 const int description_max_x = rc.right - side_margin;
634 const int mid_line = (description_max_x - content_min_x) / 2 + content_min_x;
635 const int star_col_width = kStarPadding + star_->width();
636 const int content_right_margin = line_info_.ave_char_width * 2;
637
638 // * If this would make the content section display fewer than 40 characters,
639 // the content section is increased to that minimum at the expense of the
640 // description section.
641 const int content_width =
642 std::max(mid_line - content_min_x - content_right_margin,
643 line_info_.ave_char_width * 40);
644 const int description_width = description_max_x - content_min_x -
645 content_width - star_col_width;
646
647 // * If this would make the description section display fewer than 20
648 // characters, or if there are no descriptions to display or the result is
649 // the HISTORY_SEARCH shortcut, the description section is eliminated, and
650 // all the available width is used for the content section.
651 int star_x;
652 const AutocompleteMatch& match = model_->result().match_at(line);
653 if ((description_width < (line_info_.ave_char_width * 20)) ||
654 all_descriptions_empty ||
655 (match.type == AutocompleteMatch::OPEN_HISTORY_PAGE)) {
656 star_x = description_max_x - star_col_width + kStarPadding;
657 DrawMatchFragments(dc, match.contents, match.contents_class, content_min_x,
658 y, star_x - kStarPadding, status);
659 } else {
660 star_x = description_max_x - description_width - star_col_width;
661 DrawMatchFragments(dc, match.contents, match.contents_class, content_min_x,
662 y, content_min_x + content_width, status);
663 DrawMatchFragments(dc, match.description, match.description_class,
664 description_max_x - description_width, y,
665 description_max_x, status);
666 }
667 if (starred)
668 DrawStar(dc, star_x,
669 (line_info_.line_height - star_->height()) / 2 + top_pixel);
670 }
671
672 void AutocompletePopupViewWin::DrawStar(HDC dc, int x, int y) const {
673 gfx::Canvas canvas(star_->width(), star_->height(), false);
674 // Make the background completely transparent.
675 canvas.drawColor(SK_ColorBLACK, SkXfermode::kClear_Mode);
676 canvas.DrawBitmapInt(*star_, 0, 0);
677 canvas.getTopPlatformDevice().drawToHDC(
678 dc, mirroring_context_->GetLeft(x, x + star_->width()), y, NULL);
679 }
680
681 // static
682 AutocompletePopupView* AutocompletePopupView::CreatePopupView(
683 const gfx::Font& font,
684 AutocompleteEditViewWin* edit_view,
685 AutocompleteEditModel* edit_model,
686 Profile* profile,
687 AutocompletePopupPositioner* popup_positioner) {
688 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableOmnibox2))
689 return new AutocompletePopupViewWin(font, edit_view, edit_model, profile);
690 return new AutocompletePopupContentsView(font, edit_view, edit_model,
691 profile, popup_positioner);
692 }
OLDNEW
« no previous file with comments | « chrome/browser/autocomplete/autocomplete_popup_view_win.h ('k') | chrome/browser/autocomplete/search_provider.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698