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