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. 4 5#include "chrome/browser/ui/gtk/infobars/infobar_container_gtk.h" 6 7#include <gtk/gtk.h> 8 9#include <utility> 10 11#include "base/message_loop/message_loop.h" 12#include "chrome/browser/infobars/infobar_delegate.h" 13#include "chrome/browser/platform_util.h" 14#include "chrome/browser/ui/browser_window.h" 15#include "chrome/browser/ui/gtk/browser_window_gtk.h" 16#include "chrome/browser/ui/gtk/gtk_util.h" 17#include "chrome/browser/ui/gtk/infobars/infobar_gtk.h" 18#include "third_party/skia/include/effects/SkGradientShader.h" 19#include "ui/base/gtk/gtk_compat.h" 20#include "ui/gfx/canvas_skia_paint.h" 21#include "ui/gfx/color_utils.h" 22#include "ui/gfx/rect.h" 23#include "ui/gfx/skia_utils_gtk.h" 24 25InfoBarContainerGtk::InfoBarContainerGtk(InfoBarContainer::Delegate* delegate, 26 Profile* profile) 27 : InfoBarContainer(delegate), 28 profile_(profile), 29 container_(gtk_vbox_new(FALSE, 0)) { 30 gtk_widget_show(widget()); 31} 32 33InfoBarContainerGtk::~InfoBarContainerGtk() { 34 RemoveAllInfoBarsForDestruction(); 35 container_.Destroy(); 36} 37 38int InfoBarContainerGtk::TotalHeightOfAnimatingBars() const { 39 int sum = 0; 40 41 for (std::vector<InfoBarGtk*>::const_iterator it = infobars_gtk_.begin(); 42 it != infobars_gtk_.end(); ++it) { 43 sum += (*it)->AnimatingHeight(); 44 } 45 46 return sum; 47} 48 49bool InfoBarContainerGtk::ContainsInfobars() const { 50 return !infobars_gtk_.empty(); 51} 52 53void InfoBarContainerGtk::PlatformSpecificAddInfoBar(InfoBar* infobar, 54 size_t position) { 55 InfoBarGtk* infobar_gtk = static_cast<InfoBarGtk*>(infobar); 56 infobar_gtk->InitWidgets(); 57 infobars_gtk_.insert(infobars_gtk_.begin() + position, infobar_gtk); 58 59 if (infobars_gtk_.back() == infobar_gtk) { 60 gtk_box_pack_start(GTK_BOX(widget()), infobar_gtk->widget(), 61 FALSE, FALSE, 0); 62 } else { 63 // Clear out our container and then repack it to make sure everything is in 64 // the right order. 65 gtk_util::RemoveAllChildren(widget()); 66 67 // Repack our container. 68 for (std::vector<InfoBarGtk*>::const_iterator it = infobars_gtk_.begin(); 69 it != infobars_gtk_.end(); ++it) { 70 gtk_box_pack_start(GTK_BOX(widget()), (*it)->widget(), 71 FALSE, FALSE, 0); 72 } 73 } 74} 75 76void InfoBarContainerGtk::PlatformSpecificRemoveInfoBar(InfoBar* infobar) { 77 InfoBarGtk* infobar_gtk = static_cast<InfoBarGtk*>(infobar); 78 gtk_container_remove(GTK_CONTAINER(widget()), infobar_gtk->widget()); 79 80 std::vector<InfoBarGtk*>::iterator it = 81 std::find(infobars_gtk_.begin(), infobars_gtk_.end(), infobar); 82 if (it != infobars_gtk_.end()) 83 infobars_gtk_.erase(it); 84 85 base::MessageLoop::current()->DeleteSoon(FROM_HERE, infobar); 86} 87 88void InfoBarContainerGtk::PlatformSpecificInfoBarStateChanged( 89 bool is_animating) { 90 // Force a redraw of all infobars since something has a new height and we 91 // need to make sure we animate our arrows. 92 for (std::vector<InfoBarGtk*>::iterator it = infobars_gtk_.begin(); 93 it != infobars_gtk_.end(); ++it) { 94 gtk_widget_queue_draw((*it)->widget()); 95 } 96} 97 98void InfoBarContainerGtk::PaintInfobarBitsOn(GtkWidget* sender, 99 GdkEventExpose* expose, 100 InfoBarGtk* infobar) { 101 if (infobars_gtk_.empty()) 102 return; 103 104 // For each infobar after |infobar| (or starting from the beginning if NULL), 105 // we draw each every arrow and rely on clipping rects to ignore overdraw. 106 std::vector<InfoBarGtk*>::iterator it; 107 if (infobar) { 108 it = std::find(infobars_gtk_.begin(), infobars_gtk_.end(), infobar); 109 if (it == infobars_gtk_.end()) { 110 NOTREACHED(); 111 return; 112 } 113 114 it++; 115 if (it == infobars_gtk_.end()) { 116 // |infobar| is the last infobar in the list and thus doesn't need to 117 // paint the next infobar's arrow. 118 return; 119 } 120 } else { 121 it = infobars_gtk_.begin(); 122 } 123 124 // Figure out the x location so that that arrow is over the location item. 125 GtkWindow* parent = platform_util::GetTopLevel(sender); 126 BrowserWindowGtk* browser_window = 127 BrowserWindowGtk::GetBrowserWindowForNativeWindow(parent); 128 int x = browser_window ? 129 browser_window->GetXPositionOfLocationIcon(sender) : 0; 130 131 for (; it != infobars_gtk_.end(); ++it) { 132 // Find the location of the arrow in |sender|'s coordinate space relative 133 // to the infobar. 134 int y = 0; 135 gtk_widget_translate_coordinates((*it)->widget(), sender, 136 0, 0, 137 NULL, &y); 138 if (!gtk_widget_get_has_window(sender)) { 139 GtkAllocation allocation; 140 gtk_widget_get_allocation(sender, &allocation); 141 y += allocation.y; 142 } 143 144 // We rely on the +1 in the y calculation so we hide the bottom of the drawn 145 // triangle just right outside the view bounds. 146 gfx::Rect bounds(x - (*it)->arrow_half_width(), 147 y - (*it)->arrow_height() + 1, 148 2 * (*it)->arrow_half_width(), 149 (*it)->arrow_target_height()); 150 151 PaintArrowOn(sender, expose, bounds, *it); 152 } 153} 154 155void InfoBarContainerGtk::PaintArrowOn(GtkWidget* widget, 156 GdkEventExpose* expose, 157 const gfx::Rect& bounds, 158 InfoBarGtk* source) { 159 // TODO(erg): All of this could be rewritten in cairo. 160 SkPath path; 161 path.moveTo(bounds.x() + 0.5, bounds.bottom() + 0.5); 162 path.rLineTo(bounds.width() / 2.0, -bounds.height()); 163 path.lineTo(bounds.right() + 0.5, bounds.bottom() + 0.5); 164 path.close(); 165 166 SkPaint paint; 167 paint.setStrokeWidth(1); 168 paint.setStyle(SkPaint::kFill_Style); 169 paint.setAntiAlias(true); 170 171 SkPoint grad_points[2]; 172 grad_points[0].set(SkIntToScalar(0), SkIntToScalar(bounds.bottom())); 173 grad_points[1].set(SkIntToScalar(0), 174 SkIntToScalar(bounds.bottom() + 175 source->arrow_target_height())); 176 177 SkColor grad_colors[2]; 178 grad_colors[0] = source->ConvertGetColor(&InfoBarGtk::GetTopColor); 179 grad_colors[1] = source->ConvertGetColor(&InfoBarGtk::GetBottomColor); 180 181 skia::RefPtr<SkShader> gradient_shader = skia::AdoptRef( 182 SkGradientShader::CreateLinear( 183 grad_points, grad_colors, NULL, 2, SkShader::kMirror_TileMode)); 184 paint.setShader(gradient_shader.get()); 185 186 gfx::CanvasSkiaPaint canvas_paint(expose, false); 187 SkCanvas& canvas = *canvas_paint.sk_canvas(); 188 189 canvas.drawPath(path, paint); 190 191 paint.setShader(NULL); 192 paint.setColor(SkColorSetA(gfx::GdkColorToSkColor(source->GetBorderColor()), 193 SkColorGetA(grad_colors[0]))); 194 paint.setStyle(SkPaint::kStroke_Style); 195 canvas.drawPath(path, paint); 196} 197