1 // Copyright (c) 2011 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.
5 #include "chrome/browser/ui/gtk/infobars/infobar_container_gtk.h"
11 #include "chrome/browser/infobars/infobar_delegate.h"
12 #include "chrome/browser/platform_util.h"
13 #include "chrome/browser/ui/browser_window.h"
14 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
15 #include "chrome/browser/ui/gtk/gtk_util.h"
16 #include "chrome/browser/ui/gtk/infobars/infobar_gtk.h"
17 #include "third_party/skia/include/effects/SkGradientShader.h"
18 #include "ui/gfx/canvas_skia_paint.h"
19 #include "ui/gfx/color_utils.h"
20 #include "ui/gfx/gtk_compat.h"
21 #include "ui/gfx/rect.h"
22 #include "ui/gfx/skia_utils_gtk.h"
24 InfoBarContainerGtk::InfoBarContainerGtk(InfoBarContainer::Delegate
* delegate
,
26 : InfoBarContainer(delegate
),
28 container_(gtk_vbox_new(FALSE
, 0)) {
29 gtk_widget_show(widget());
32 InfoBarContainerGtk::~InfoBarContainerGtk() {
33 RemoveAllInfoBarsForDestruction();
37 int InfoBarContainerGtk::TotalHeightOfAnimatingBars() const {
40 for (std::vector
<InfoBarGtk
*>::const_iterator it
= infobars_gtk_
.begin();
41 it
!= infobars_gtk_
.end(); ++it
) {
42 sum
+= (*it
)->AnimatingHeight();
48 bool InfoBarContainerGtk::ContainsInfobars() const {
49 return !infobars_gtk_
.empty();
52 void InfoBarContainerGtk::PlatformSpecificAddInfoBar(InfoBar
* infobar
,
54 InfoBarGtk
* infobar_gtk
= static_cast<InfoBarGtk
*>(infobar
);
55 infobars_gtk_
.insert(infobars_gtk_
.begin() + position
, infobar_gtk
);
57 if (infobars_gtk_
.back() == infobar_gtk
) {
58 gtk_box_pack_start(GTK_BOX(widget()), infobar_gtk
->widget(),
61 // Clear out our container and then repack it to make sure everything is in
63 gtk_util::RemoveAllChildren(widget());
65 // Repack our container.
66 for (std::vector
<InfoBarGtk
*>::const_iterator it
= infobars_gtk_
.begin();
67 it
!= infobars_gtk_
.end(); ++it
) {
68 gtk_box_pack_start(GTK_BOX(widget()), (*it
)->widget(),
74 void InfoBarContainerGtk::PlatformSpecificRemoveInfoBar(InfoBar
* infobar
) {
75 InfoBarGtk
* infobar_gtk
= static_cast<InfoBarGtk
*>(infobar
);
76 gtk_container_remove(GTK_CONTAINER(widget()), infobar_gtk
->widget());
78 std::vector
<InfoBarGtk
*>::iterator it
=
79 std::find(infobars_gtk_
.begin(), infobars_gtk_
.end(), infobar
);
80 if (it
!= infobars_gtk_
.end())
81 infobars_gtk_
.erase(it
);
84 void InfoBarContainerGtk::PlatformSpecificInfoBarStateChanged(
86 // Force a redraw of all infobars since something has a new height and we
87 // need to make sure we animate our arrows.
88 for (std::vector
<InfoBarGtk
*>::iterator it
= infobars_gtk_
.begin();
89 it
!= infobars_gtk_
.end(); ++it
) {
90 gtk_widget_queue_draw((*it
)->widget());
94 void InfoBarContainerGtk::PaintInfobarBitsOn(GtkWidget
* sender
,
95 GdkEventExpose
* expose
,
96 InfoBarGtk
* infobar
) {
97 if (infobars_gtk_
.empty())
100 // For each infobar after |infobar| (or starting from the beginning if NULL),
101 // we draw each every arrow and rely on clipping rects to ignore overdraw.
102 std::vector
<InfoBarGtk
*>::iterator it
;
104 it
= std::find(infobars_gtk_
.begin(), infobars_gtk_
.end(), infobar
);
105 if (it
== infobars_gtk_
.end()) {
111 if (it
== infobars_gtk_
.end()) {
112 // |infobar| is the last infobar in the list and thus doesn't need to
113 // paint the next infobar's arrow.
117 it
= infobars_gtk_
.begin();
120 // Figure out the x location so that that arrow is over the location item.
121 GtkWindow
* parent
= platform_util::GetTopLevel(sender
);
122 BrowserWindowGtk
* browser_window
=
123 BrowserWindowGtk::GetBrowserWindowForNativeWindow(parent
);
124 int x
= browser_window
?
125 browser_window
->GetXPositionOfLocationIcon(sender
) : 0;
127 for (; it
!= infobars_gtk_
.end(); ++it
) {
128 // Find the location of the arrow in |sender|'s coordinate space relative
131 gtk_widget_translate_coordinates((*it
)->widget(), sender
,
134 if (!gtk_widget_get_has_window(sender
)) {
135 GtkAllocation allocation
;
136 gtk_widget_get_allocation(sender
, &allocation
);
140 // We rely on the +1 in the y calculation so we hide the bottom of the drawn
141 // triangle just right outside the view bounds.
142 gfx::Rect
bounds(x
- (*it
)->arrow_half_width(),
143 y
- (*it
)->arrow_height() + 1,
144 2 * (*it
)->arrow_half_width(),
145 (*it
)->arrow_target_height());
147 PaintArrowOn(sender
, expose
, bounds
, *it
);
151 void InfoBarContainerGtk::PaintArrowOn(GtkWidget
* widget
,
152 GdkEventExpose
* expose
,
153 const gfx::Rect
& bounds
,
154 InfoBarGtk
* source
) {
155 // TODO(erg): All of this could be rewritten in cairo.
157 path
.moveTo(bounds
.x() + 0.5, bounds
.bottom() + 0.5);
158 path
.rLineTo(bounds
.width() / 2.0, -bounds
.height());
159 path
.lineTo(bounds
.right() + 0.5, bounds
.bottom() + 0.5);
163 paint
.setStrokeWidth(1);
164 paint
.setStyle(SkPaint::kFill_Style
);
165 paint
.setAntiAlias(true);
167 SkPoint grad_points
[2];
168 grad_points
[0].set(SkIntToScalar(0), SkIntToScalar(bounds
.bottom()));
169 grad_points
[1].set(SkIntToScalar(0),
170 SkIntToScalar(bounds
.bottom() +
171 source
->arrow_target_height()));
173 SkColor grad_colors
[2];
174 grad_colors
[0] = source
->ConvertGetColor(&InfoBarGtk::GetTopColor
);
175 grad_colors
[1] = source
->ConvertGetColor(&InfoBarGtk::GetBottomColor
);
177 skia::RefPtr
<SkShader
> gradient_shader
= skia::AdoptRef(
178 SkGradientShader::CreateLinear(
179 grad_points
, grad_colors
, NULL
, 2, SkShader::kMirror_TileMode
));
180 paint
.setShader(gradient_shader
.get());
182 gfx::CanvasSkiaPaint
canvas_paint(expose
, false);
183 SkCanvas
& canvas
= *canvas_paint
.sk_canvas();
185 canvas
.drawPath(path
, paint
);
187 paint
.setShader(NULL
);
188 paint
.setColor(SkColorSetA(gfx::GdkColorToSkColor(source
->GetBorderColor()),
189 SkColorGetA(grad_colors
[0])));
190 paint
.setStyle(SkPaint::kStroke_Style
);
191 canvas
.drawPath(path
, paint
);