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/theme_install_bubble_view_gtk.h" 6 7 #include <math.h> 8 9 #include "chrome/browser/ui/gtk/gtk_util.h" 10 #include "chrome/browser/ui/gtk/rounded_window.h" 11 #include "content/common/notification_service.h" 12 #include "content/common/notification_type.h" 13 #include "grit/generated_resources.h" 14 #include "ui/base/l10n/l10n_util.h" 15 16 // Roundedness of bubble. 17 static const int kBubbleCornerRadius = 4; 18 19 // Padding between border of bubble and text. 20 static const int kTextPadding = 8; 21 22 // The bubble is partially transparent. 23 static const double kBubbleOpacity = static_cast<double>(0xcc) / 0xff; 24 25 ThemeInstallBubbleViewGtk* ThemeInstallBubbleViewGtk::instance_ = NULL; 26 27 // ThemeInstallBubbleViewGtk, public ------------------------------------------- 28 29 // static 30 void ThemeInstallBubbleViewGtk::Show(GtkWindow* parent) { 31 if (instance_) 32 instance_->increment_num_loading(); 33 else 34 instance_ = new ThemeInstallBubbleViewGtk(GTK_WIDGET(parent)); 35 } 36 37 void ThemeInstallBubbleViewGtk::Observe(NotificationType type, 38 const NotificationSource& source, 39 const NotificationDetails& details) { 40 if (--num_loads_extant_ == 0) 41 delete this; 42 } 43 44 // ThemeInstallBubbleViewGtk, private ------------------------------------------ 45 46 ThemeInstallBubbleViewGtk::ThemeInstallBubbleViewGtk(GtkWidget* parent) 47 : widget_(NULL), 48 parent_(parent), 49 num_loads_extant_(1) { 50 InitWidgets(); 51 52 // Close when theme has been installed. 53 registrar_.Add( 54 this, 55 NotificationType::BROWSER_THEME_CHANGED, 56 NotificationService::AllSources()); 57 58 // Close when we are installing an extension, not a theme. 59 registrar_.Add( 60 this, 61 NotificationType::NO_THEME_DETECTED, 62 NotificationService::AllSources()); 63 registrar_.Add( 64 this, 65 NotificationType::EXTENSION_INSTALLED, 66 NotificationService::AllSources()); 67 registrar_.Add( 68 this, 69 NotificationType::EXTENSION_INSTALL_ERROR, 70 NotificationService::AllSources()); 71 72 // Don't let the bubble overlap the confirm dialog. 73 registrar_.Add( 74 this, 75 NotificationType::EXTENSION_WILL_SHOW_CONFIRM_DIALOG, 76 NotificationService::AllSources()); 77 } 78 79 ThemeInstallBubbleViewGtk::~ThemeInstallBubbleViewGtk() { 80 gtk_widget_destroy(widget_); 81 instance_ = NULL; 82 } 83 84 void ThemeInstallBubbleViewGtk::InitWidgets() { 85 // Widgematically, the bubble is just a label in a popup window. 86 widget_ = gtk_window_new(GTK_WINDOW_POPUP); 87 gtk_container_set_border_width(GTK_CONTAINER(widget_), kTextPadding); 88 GtkWidget* label = gtk_label_new(NULL); 89 90 gchar* markup = g_markup_printf_escaped( 91 "<span size='xx-large'>%s</span>", 92 l10n_util::GetStringUTF8(IDS_THEME_LOADING_TITLE).c_str()); 93 gtk_label_set_markup(GTK_LABEL(label), markup); 94 g_free(markup); 95 96 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, >k_util::kGdkWhite); 97 gtk_container_add(GTK_CONTAINER(widget_), label); 98 99 // We need to show the label so we'll know the widget's actual size when we 100 // call MoveWindow(). 101 gtk_widget_show_all(label); 102 103 bool composited = false; 104 if (gtk_util::IsScreenComposited()) { 105 composited = true; 106 GdkScreen* screen = gtk_widget_get_screen(widget_); 107 GdkColormap* colormap = gdk_screen_get_rgba_colormap(screen); 108 109 if (colormap) 110 gtk_widget_set_colormap(widget_, colormap); 111 else 112 composited = false; 113 } 114 115 if (composited) { 116 gtk_widget_set_app_paintable(widget_, TRUE); 117 g_signal_connect(widget_, "expose-event", 118 G_CALLBACK(OnExposeThunk), this); 119 gtk_widget_realize(widget_); 120 } else { 121 gtk_widget_modify_bg(widget_, GTK_STATE_NORMAL, >k_util::kGdkBlack); 122 GdkColor color; 123 gtk_util::ActAsRoundedWindow(widget_, color, kBubbleCornerRadius, 124 gtk_util::ROUNDED_ALL, gtk_util::BORDER_NONE); 125 } 126 127 MoveWindow(); 128 129 g_signal_connect(widget_, "unmap-event", 130 G_CALLBACK(OnUnmapEventThunk), this); 131 132 gtk_widget_show_all(widget_); 133 } 134 135 void ThemeInstallBubbleViewGtk::MoveWindow() { 136 GtkRequisition req; 137 gtk_widget_size_request(widget_, &req); 138 139 gint parent_x = 0, parent_y = 0; 140 gdk_window_get_position(parent_->window, &parent_x, &parent_y); 141 gint parent_width = parent_->allocation.width; 142 gint parent_height = parent_->allocation.height; 143 144 gint x = parent_x + parent_width / 2 - req.width / 2; 145 gint y = parent_y + parent_height / 2 - req.height / 2; 146 147 gtk_window_move(GTK_WINDOW(widget_), x, y); 148 } 149 150 gboolean ThemeInstallBubbleViewGtk::OnUnmapEvent(GtkWidget* widget) { 151 delete this; 152 return FALSE; 153 } 154 155 gboolean ThemeInstallBubbleViewGtk::OnExpose(GtkWidget* widget, 156 GdkEventExpose* event) { 157 cairo_t* cr = gdk_cairo_create(event->window); 158 gdk_cairo_rectangle(cr, &event->area); 159 cairo_clip(cr); 160 161 cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); 162 cairo_paint(cr); 163 cairo_set_operator(cr, CAIRO_OPERATOR_OVER); 164 165 // |inner_rect| has its corners at the centerpoints of the corner arcs. 166 gfx::Rect inner_rect(widget_->allocation); 167 int inset = kBubbleCornerRadius; 168 inner_rect.Inset(inset, inset); 169 170 // The positive y axis is down, so M_PI_2 is down. 171 cairo_arc(cr, inner_rect.x(), inner_rect.y(), inset, 172 M_PI, 3 * M_PI_2); 173 cairo_arc(cr, inner_rect.right(), inner_rect.y(), inset, 174 3 * M_PI_2, 0); 175 cairo_arc(cr, inner_rect.right(), inner_rect.bottom(), inset, 176 0, M_PI_2); 177 cairo_arc(cr, inner_rect.x(), inner_rect.bottom(), inset, 178 M_PI_2, M_PI); 179 180 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, kBubbleOpacity); 181 cairo_fill(cr); 182 cairo_destroy(cr); 183 184 return FALSE; 185 } 186