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
Show(GtkWindow * parent)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
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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
ThemeInstallBubbleViewGtk(GtkWidget * parent)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
~ThemeInstallBubbleViewGtk()79 ThemeInstallBubbleViewGtk::~ThemeInstallBubbleViewGtk() {
80 gtk_widget_destroy(widget_);
81 instance_ = NULL;
82 }
83
InitWidgets()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
MoveWindow()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
OnUnmapEvent(GtkWidget * widget)150 gboolean ThemeInstallBubbleViewGtk::OnUnmapEvent(GtkWidget* widget) {
151 delete this;
152 return FALSE;
153 }
154
OnExpose(GtkWidget * widget,GdkEventExpose * event)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