OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/gtk/omnibox/omnibox_popup_view_gtk.h" | 5 #include "chrome/browser/ui/gtk/omnibox/omnibox_popup_view_gtk.h" |
6 | 6 |
7 #include <gtk/gtk.h> | 7 #include <gtk/gtk.h> |
8 | 8 |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <string> | 10 #include <string> |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
91 // UTF-8 Left-to-right embedding. | 91 // UTF-8 Left-to-right embedding. |
92 const char* kLRE = "\xe2\x80\xaa"; | 92 const char* kLRE = "\xe2\x80\xaa"; |
93 | 93 |
94 // Return a Rect covering the whole area of |window|. | 94 // Return a Rect covering the whole area of |window|. |
95 gfx::Rect GetWindowRect(GdkWindow* window) { | 95 gfx::Rect GetWindowRect(GdkWindow* window) { |
96 gint width = gdk_window_get_width(window); | 96 gint width = gdk_window_get_width(window); |
97 gint height = gdk_window_get_height(window); | 97 gint height = gdk_window_get_height(window); |
98 return gfx::Rect(width, height); | 98 return gfx::Rect(width, height); |
99 } | 99 } |
100 | 100 |
101 // Return a Rect for the space for a result line. This excludes the border, | |
102 // but includes the padding. This is the area that is colored for a selection. | |
103 gfx::Rect GetRectForLine(size_t line, int width) { | |
104 return gfx::Rect(kBorderThickness, | |
105 (line * kHeightPerResult) + kBorderThickness, | |
106 width - (kBorderThickness * 2), | |
107 kHeightPerResult); | |
108 } | |
109 | |
110 // TODO(deanm): Find some better home for this, and make it more efficient. | 101 // TODO(deanm): Find some better home for this, and make it more efficient. |
111 size_t GetUTF8Offset(const string16& text, size_t text_offset) { | 102 size_t GetUTF8Offset(const string16& text, size_t text_offset) { |
112 return UTF16ToUTF8(text.substr(0, text_offset)).length(); | 103 return UTF16ToUTF8(text.substr(0, text_offset)).length(); |
113 } | 104 } |
114 | 105 |
115 // Generates the normal URL color, a green color used in unhighlighted URL | 106 // Generates the normal URL color, a green color used in unhighlighted URL |
116 // text. It is a mix of |kURLTextColor| and the current text color. Unlike the | 107 // text. It is a mix of |kURLTextColor| and the current text color. Unlike the |
117 // selected text color, it is more important to match the qualities of the | 108 // selected text color, it is more important to match the qualities of the |
118 // foreground typeface color instead of taking the background into account. | 109 // foreground typeface color instead of taking the background into account. |
119 GdkColor NormalURLColor(GdkColor foreground) { | 110 GdkColor NormalURLColor(GdkColor foreground) { |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
163 // The luminance should match the luminance of the foreground text. Again, | 154 // The luminance should match the luminance of the foreground text. Again, |
164 // we clamp so as to have at some amount of color (green) in the text. | 155 // we clamp so as to have at some amount of color (green) in the text. |
165 double opposite_l = fg_hsl.l; | 156 double opposite_l = fg_hsl.l; |
166 double l = std::max(0.1, std::min(0.9, opposite_l)); | 157 double l = std::max(0.1, std::min(0.9, opposite_l)); |
167 | 158 |
168 color_utils::HSL output = { hue_hsl.h, s, l }; | 159 color_utils::HSL output = { hue_hsl.h, s, l }; |
169 return gfx::SkColorToGdkColor(color_utils::HSLToSkColor(output, 255)); | 160 return gfx::SkColorToGdkColor(color_utils::HSLToSkColor(output, 255)); |
170 } | 161 } |
171 } // namespace | 162 } // namespace |
172 | 163 |
164 // static | |
173 void OmniboxPopupViewGtk::SetupLayoutForMatch( | 165 void OmniboxPopupViewGtk::SetupLayoutForMatch( |
174 PangoLayout* layout, | 166 PangoLayout* layout, |
175 const string16& text, | 167 const string16& text, |
176 const AutocompleteMatch::ACMatchClassifications& classifications, | 168 const AutocompleteMatch::ACMatchClassifications& classifications, |
177 const GdkColor* base_color, | 169 const GdkColor* base_color, |
178 const GdkColor* dim_color, | 170 const GdkColor* dim_color, |
179 const GdkColor* url_color, | 171 const GdkColor* url_color, |
180 const std::string& prefix_text) { | 172 const std::string& prefix_text) { |
181 // In RTL, mark text with left-to-right embedding mark if there is no strong | 173 // In RTL, mark text with left-to-right embedding mark if there is no strong |
182 // RTL characters inside it, so the ending punctuation displays correctly | 174 // RTL characters inside it, so the ending punctuation displays correctly |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
261 pango_layout_set_text(layout, text_utf8.data(), text_utf8.length()); | 253 pango_layout_set_text(layout, text_utf8.data(), text_utf8.length()); |
262 pango_layout_set_attributes(layout, attrs); // Ref taken. | 254 pango_layout_set_attributes(layout, attrs); // Ref taken. |
263 pango_attr_list_unref(attrs); | 255 pango_attr_list_unref(attrs); |
264 } | 256 } |
265 | 257 |
266 OmniboxPopupViewGtk::OmniboxPopupViewGtk(const gfx::Font& font, | 258 OmniboxPopupViewGtk::OmniboxPopupViewGtk(const gfx::Font& font, |
267 OmniboxView* omnibox_view, | 259 OmniboxView* omnibox_view, |
268 OmniboxEditModel* edit_model, | 260 OmniboxEditModel* edit_model, |
269 GtkWidget* location_bar) | 261 GtkWidget* location_bar) |
270 : signal_registrar_(new ui::GtkSignalRegistrar), | 262 : signal_registrar_(new ui::GtkSignalRegistrar), |
271 model_(new OmniboxPopupModel(this, edit_model)), | |
272 omnibox_view_(omnibox_view), | 263 omnibox_view_(omnibox_view), |
273 location_bar_(location_bar), | 264 location_bar_(location_bar), |
274 window_(gtk_window_new(GTK_WINDOW_POPUP)), | 265 window_(gtk_window_new(GTK_WINDOW_POPUP)), |
275 layout_(NULL), | 266 layout_(NULL), |
276 theme_service_(GtkThemeService::GetFrom(edit_model->profile())), | 267 theme_service_(NULL), |
277 font_(font.DeriveFont(kEditFontAdjust)), | 268 font_(font.DeriveFont(kEditFontAdjust)), |
278 ignore_mouse_drag_(false), | 269 ignore_mouse_drag_(false), |
279 opened_(false) { | 270 opened_(false) { |
271 // edit_model may be NULL in unit tests. | |
272 if (edit_model) { | |
273 model_.reset(new OmniboxPopupModel(this, edit_model)); | |
274 theme_service_ = GtkThemeService::GetFrom(edit_model->profile()); | |
275 } | |
276 } | |
277 | |
278 void OmniboxPopupViewGtk::Init() { | |
280 gtk_widget_set_can_focus(window_, FALSE); | 279 gtk_widget_set_can_focus(window_, FALSE); |
281 // Don't allow the window to be resized. This also forces the window to | 280 // Don't allow the window to be resized. This also forces the window to |
282 // shrink down to the size of its child contents. | 281 // shrink down to the size of its child contents. |
283 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE); | 282 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE); |
284 gtk_widget_set_app_paintable(window_, TRUE); | 283 gtk_widget_set_app_paintable(window_, TRUE); |
285 // Have GTK double buffer around the expose signal. | 284 // Have GTK double buffer around the expose signal. |
286 gtk_widget_set_double_buffered(window_, TRUE); | 285 gtk_widget_set_double_buffered(window_, TRUE); |
287 | 286 |
288 // Cache the layout so we don't have to create it for every expose. If we | 287 // Cache the layout so we don't have to create it for every expose. If we |
289 // were a real widget we should handle changing directions, but we're not | 288 // were a real widget we should handle changing directions, but we're not |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
327 | 326 |
328 OmniboxPopupViewGtk::~OmniboxPopupViewGtk() { | 327 OmniboxPopupViewGtk::~OmniboxPopupViewGtk() { |
329 // Stop listening to our signals before we destroy the model. I suspect that | 328 // Stop listening to our signals before we destroy the model. I suspect that |
330 // we can race window destruction, otherwise. | 329 // we can race window destruction, otherwise. |
331 signal_registrar_.reset(); | 330 signal_registrar_.reset(); |
332 | 331 |
333 // Explicitly destroy our model here, before we destroy our GTK widgets. | 332 // Explicitly destroy our model here, before we destroy our GTK widgets. |
334 // This is because the model destructor can call back into us, and we need | 333 // This is because the model destructor can call back into us, and we need |
335 // to make sure everything is still valid when it does. | 334 // to make sure everything is still valid when it does. |
336 model_.reset(); | 335 model_.reset(); |
337 g_object_unref(layout_); | 336 // layout_ may be NULL in unit tests. |
338 gtk_widget_destroy(window_); | 337 if (layout_) { |
338 g_object_unref(layout_); | |
339 gtk_widget_destroy(window_); | |
340 } | |
339 } | 341 } |
340 | 342 |
341 bool OmniboxPopupViewGtk::IsOpen() const { | 343 bool OmniboxPopupViewGtk::IsOpen() const { |
342 return opened_; | 344 return opened_; |
343 } | 345 } |
344 | 346 |
345 void OmniboxPopupViewGtk::InvalidateLine(size_t line) { | 347 void OmniboxPopupViewGtk::InvalidateLine(size_t line) { |
346 // TODO(deanm): Is it possible to use some constant for the width, instead | 348 // TODO(deanm): Is it possible to use some constant for the width, instead |
347 // of having to query the width of the window? | 349 // of having to query the width of the window? |
348 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_)); | 350 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_)); |
349 GdkRectangle line_rect = GetRectForLine( | 351 GdkRectangle line_rect = GetRectForLine( |
350 line, GetWindowRect(gdk_window).width()).ToGdkRectangle(); | 352 line, GetWindowRect(gdk_window).width()).ToGdkRectangle(); |
351 gdk_window_invalidate_rect(gdk_window, &line_rect, FALSE); | 353 gdk_window_invalidate_rect(gdk_window, &line_rect, FALSE); |
352 } | 354 } |
353 | 355 |
354 void OmniboxPopupViewGtk::UpdatePopupAppearance() { | 356 void OmniboxPopupViewGtk::UpdatePopupAppearance() { |
355 const AutocompleteResult& result = model_->result(); | 357 const AutocompleteResult& result = GetResult(); |
356 if (result.empty()) { | 358 const size_t hidden_matches = GetHiddenMatchCount(); |
359 if (result.size() <= hidden_matches) { | |
357 Hide(); | 360 Hide(); |
358 return; | 361 return; |
359 } | 362 } |
360 | 363 |
361 Show(result.size()); | 364 Show(result.size() - hidden_matches); |
362 gtk_widget_queue_draw(window_); | 365 gtk_widget_queue_draw(window_); |
363 } | 366 } |
364 | 367 |
365 gfx::Rect OmniboxPopupViewGtk::GetTargetBounds() { | 368 gfx::Rect OmniboxPopupViewGtk::GetTargetBounds() { |
366 if (!gtk_widget_get_realized(window_)) | 369 if (!gtk_widget_get_realized(window_)) |
367 return gfx::Rect(); | 370 return gfx::Rect(); |
368 | 371 |
369 gfx::Rect retval = ui::GetWidgetScreenBounds(window_); | 372 gfx::Rect retval = ui::GetWidgetScreenBounds(window_); |
370 | 373 |
371 // The widget bounds don't update synchronously so may be out of sync with | 374 // The widget bounds don't update synchronously so may be out of sync with |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
461 void OmniboxPopupViewGtk::StackWindow() { | 464 void OmniboxPopupViewGtk::StackWindow() { |
462 gfx::NativeView omnibox_view = omnibox_view_->GetNativeView(); | 465 gfx::NativeView omnibox_view = omnibox_view_->GetNativeView(); |
463 DCHECK(GTK_IS_WIDGET(omnibox_view)); | 466 DCHECK(GTK_IS_WIDGET(omnibox_view)); |
464 GtkWidget* toplevel = gtk_widget_get_toplevel(omnibox_view); | 467 GtkWidget* toplevel = gtk_widget_get_toplevel(omnibox_view); |
465 DCHECK(gtk_widget_is_toplevel(toplevel)); | 468 DCHECK(gtk_widget_is_toplevel(toplevel)); |
466 ui::StackPopupWindow(window_, toplevel); | 469 ui::StackPopupWindow(window_, toplevel); |
467 } | 470 } |
468 | 471 |
469 size_t OmniboxPopupViewGtk::LineFromY(int y) { | 472 size_t OmniboxPopupViewGtk::LineFromY(int y) { |
470 size_t line = std::max(y - kBorderThickness, 0) / kHeightPerResult; | 473 size_t line = std::max(y - kBorderThickness, 0) / kHeightPerResult; |
471 return std::min(line, model_->result().size() - 1); | 474 return std::min(line + GetHiddenMatchCount(), GetResult().size() - 1); |
472 } | 475 } |
473 | 476 |
474 void OmniboxPopupViewGtk::AcceptLine(size_t line, | 477 void OmniboxPopupViewGtk::AcceptLine(size_t line, |
475 WindowOpenDisposition disposition) { | 478 WindowOpenDisposition disposition) { |
476 // OpenMatch() may close the popup, which will clear the result set and, by | 479 // OpenMatch() may close the popup, which will clear the result set and, by |
477 // extension, |match| and its contents. So copy the relevant match out to | 480 // extension, |match| and its contents. So copy the relevant match out to |
478 // make sure it stays alive until the call completes. | 481 // make sure it stays alive until the call completes. |
479 AutocompleteMatch match = model_->result().match_at(line); | 482 AutocompleteMatch match = GetResult().match_at(line); |
480 omnibox_view_->OpenMatch(match, disposition, GURL(), line); | 483 omnibox_view_->OpenMatch(match, disposition, GURL(), line); |
481 } | 484 } |
482 | 485 |
483 gfx::Image OmniboxPopupViewGtk::IconForMatch( | 486 gfx::Image OmniboxPopupViewGtk::IconForMatch( |
484 const AutocompleteMatch& match, | 487 const AutocompleteMatch& match, |
485 bool selected, | 488 bool selected, |
486 bool is_selected_keyword) { | 489 bool is_selected_keyword) { |
487 const gfx::Image image = model_->GetIconIfExtensionMatch(match); | 490 const gfx::Image image = model_->GetIconIfExtensionMatch(match); |
488 if (!image.IsEmpty()) | 491 if (!image.IsEmpty()) |
489 return image; | 492 return image; |
(...skipping 29 matching lines...) Expand all Loading... | |
519 } | 522 } |
520 } | 523 } |
521 | 524 |
522 return theme_service_->GetImageNamed(icon); | 525 return theme_service_->GetImageNamed(icon); |
523 } | 526 } |
524 | 527 |
525 void OmniboxPopupViewGtk::GetVisibleMatchForInput( | 528 void OmniboxPopupViewGtk::GetVisibleMatchForInput( |
526 size_t index, | 529 size_t index, |
527 const AutocompleteMatch** match, | 530 const AutocompleteMatch** match, |
528 bool* is_selected_keyword) { | 531 bool* is_selected_keyword) { |
529 const AutocompleteResult& result = model_->result(); | 532 const AutocompleteResult& result = GetResult(); |
530 | 533 |
531 if (result.match_at(index).associated_keyword.get() && | 534 if (result.match_at(index).associated_keyword.get() && |
532 model_->selected_line() == index && | 535 model_->selected_line() == index && |
533 model_->selected_line_state() == OmniboxPopupModel::KEYWORD) { | 536 model_->selected_line_state() == OmniboxPopupModel::KEYWORD) { |
534 *match = result.match_at(index).associated_keyword.get(); | 537 *match = result.match_at(index).associated_keyword.get(); |
535 *is_selected_keyword = true; | 538 *is_selected_keyword = true; |
536 return; | 539 return; |
537 } | 540 } |
538 | 541 |
539 *match = &result.match_at(index); | 542 *match = &result.match_at(index); |
540 *is_selected_keyword = false; | 543 *is_selected_keyword = false; |
541 } | 544 } |
542 | 545 |
546 gfx::Rect OmniboxPopupViewGtk::GetRectForLine(size_t line, int width) { | |
547 size_t visible_line = line - GetHiddenMatchCount(); | |
548 return gfx::Rect(kBorderThickness, | |
549 (visible_line * kHeightPerResult) + kBorderThickness, | |
550 width - (kBorderThickness * 2), | |
551 kHeightPerResult); | |
552 } | |
553 | |
554 size_t OmniboxPopupViewGtk::GetHiddenMatchCount() { | |
555 return GetResult().ShouldHideTopMatch() ? 1 : 0; | |
556 } | |
557 | |
558 const AutocompleteResult& OmniboxPopupViewGtk::GetResult() const { | |
Evan Stade
2013/08/15 01:05:12
make the definition order match the declaration or
Jered
2013/08/15 16:08:49
Done.
| |
559 return model_->result(); | |
560 } | |
561 | |
543 gboolean OmniboxPopupViewGtk::HandleMotion(GtkWidget* widget, | 562 gboolean OmniboxPopupViewGtk::HandleMotion(GtkWidget* widget, |
544 GdkEventMotion* event) { | 563 GdkEventMotion* event) { |
545 // TODO(deanm): Windows has a bunch of complicated logic here. | 564 // TODO(deanm): Windows has a bunch of complicated logic here. |
546 size_t line = LineFromY(static_cast<int>(event->y)); | 565 size_t line = LineFromY(static_cast<int>(event->y)); |
547 // There is both a hovered and selected line, hovered just means your mouse | 566 // There is both a hovered and selected line, hovered just means your mouse |
548 // is over it, but selected is what's showing in the location edit. | 567 // is over it, but selected is what's showing in the location edit. |
549 model_->SetHoveredLine(line); | 568 model_->SetHoveredLine(line); |
550 // Select the line if the user has the left mouse button down. | 569 // Select the line if the user has the left mouse button down. |
551 if (!ignore_mouse_drag_ && (event->state & GDK_BUTTON1_MASK)) | 570 if (!ignore_mouse_drag_ && (event->state & GDK_BUTTON1_MASK)) |
552 model_->SetSelectedLine(line, false, false); | 571 model_->SetSelectedLine(line, false, false); |
(...skipping 30 matching lines...) Expand all Loading... | |
583 default: | 602 default: |
584 // Don't open the result. | 603 // Don't open the result. |
585 break; | 604 break; |
586 } | 605 } |
587 return TRUE; | 606 return TRUE; |
588 } | 607 } |
589 | 608 |
590 gboolean OmniboxPopupViewGtk::HandleExpose(GtkWidget* widget, | 609 gboolean OmniboxPopupViewGtk::HandleExpose(GtkWidget* widget, |
591 GdkEventExpose* event) { | 610 GdkEventExpose* event) { |
592 bool ltr = !base::i18n::IsRTL(); | 611 bool ltr = !base::i18n::IsRTL(); |
593 const AutocompleteResult& result = model_->result(); | 612 const AutocompleteResult& result = GetResult(); |
594 | 613 |
595 gfx::Rect window_rect = GetWindowRect(event->window); | 614 gfx::Rect window_rect = GetWindowRect(event->window); |
596 gfx::Rect damage_rect = gfx::Rect(event->area); | 615 gfx::Rect damage_rect = gfx::Rect(event->area); |
597 // Handle when our window is super narrow. A bunch of the calculations | 616 // Handle when our window is super narrow. A bunch of the calculations |
598 // below would go negative, and really we're not going to fit anything | 617 // below would go negative, and really we're not going to fit anything |
599 // useful in such a small window anyway. Just don't paint anything. | 618 // useful in such a small window anyway. Just don't paint anything. |
600 // This means we won't draw the border, but, yeah, whatever. | 619 // This means we won't draw the border, but, yeah, whatever. |
601 // TODO(deanm): Make the code more robust and remove this check. | 620 // TODO(deanm): Make the code more robust and remove this check. |
602 if (window_rect.width() < (kIconAreaWidth * 3)) | 621 if (window_rect.width() < (kIconAreaWidth * 3)) |
603 return TRUE; | 622 return TRUE; |
604 | 623 |
605 cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(widget)); | 624 cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(widget)); |
606 gdk_cairo_rectangle(cr, &event->area); | 625 gdk_cairo_rectangle(cr, &event->area); |
607 cairo_clip(cr); | 626 cairo_clip(cr); |
608 | 627 |
609 // This assert is kinda ugly, but it would be more currently unneeded work | 628 // This assert is kinda ugly, but it would be more currently unneeded work |
610 // to support painting a border that isn't 1 pixel thick. There is no point | 629 // to support painting a border that isn't 1 pixel thick. There is no point |
611 // in writing that code now, and explode if that day ever comes. | 630 // in writing that code now, and explode if that day ever comes. |
612 COMPILE_ASSERT(kBorderThickness == 1, border_1px_implied); | 631 COMPILE_ASSERT(kBorderThickness == 1, border_1px_implied); |
613 // Draw the 1px border around the entire window. | 632 // Draw the 1px border around the entire window. |
614 gdk_cairo_set_source_color(cr, &border_color_); | 633 gdk_cairo_set_source_color(cr, &border_color_); |
615 cairo_rectangle(cr, 0, 0, window_rect.width(), window_rect.height()); | 634 cairo_rectangle(cr, 0, 0, window_rect.width(), window_rect.height()); |
616 cairo_stroke(cr); | 635 cairo_stroke(cr); |
617 | 636 |
618 pango_layout_set_height(layout_, kHeightPerResult * PANGO_SCALE); | 637 pango_layout_set_height(layout_, kHeightPerResult * PANGO_SCALE); |
619 | 638 |
620 for (size_t i = 0; i < result.size(); ++i) { | 639 for (size_t i = GetHiddenMatchCount(); i < result.size(); ++i) { |
621 gfx::Rect line_rect = GetRectForLine(i, window_rect.width()); | 640 gfx::Rect line_rect = GetRectForLine(i, window_rect.width()); |
622 // Only repaint and layout damaged lines. | 641 // Only repaint and layout damaged lines. |
623 if (!line_rect.Intersects(damage_rect)) | 642 if (!line_rect.Intersects(damage_rect)) |
624 continue; | 643 continue; |
625 | 644 |
626 const AutocompleteMatch* match = NULL; | 645 const AutocompleteMatch* match = NULL; |
627 bool is_selected_keyword = false; | 646 bool is_selected_keyword = false; |
628 GetVisibleMatchForInput(i, &match, &is_selected_keyword); | 647 GetVisibleMatchForInput(i, &match, &is_selected_keyword); |
629 bool is_selected = (model_->selected_line() == i); | 648 bool is_selected = (model_->selected_line() == i); |
630 bool is_hovered = (model_->hovered_line() == i); | 649 bool is_hovered = (model_->hovered_line() == i); |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
721 theme_service_->GetImageNamed( | 740 theme_service_->GetImageNamed( |
722 is_selected ? IDR_OMNIBOX_TTS_DARK : | 741 is_selected ? IDR_OMNIBOX_TTS_DARK : |
723 IDR_OMNIBOX_TTS), | 742 IDR_OMNIBOX_TTS), |
724 icon_start_x, line_rect.y() + kIconTopPadding); | 743 icon_start_x, line_rect.y() + kIconTopPadding); |
725 } | 744 } |
726 } | 745 } |
727 | 746 |
728 cairo_destroy(cr); | 747 cairo_destroy(cr); |
729 return TRUE; | 748 return TRUE; |
730 } | 749 } |
OLD | NEW |