• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &gtk_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, &gtk_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