Home | History | Annotate | Download | only in gtk
      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, &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 
    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