Home | History | Annotate | Download | only in gtk
      1 // Copyright (c) 2012 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/global_error_bubble.h"
      6 
      7 #include <gtk/gtk.h>
      8 
      9 #include "base/i18n/rtl.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "chrome/browser/ui/browser.h"
     12 #include "chrome/browser/ui/global_error/global_error.h"
     13 #include "chrome/browser/ui/global_error/global_error_service.h"
     14 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
     15 #include "chrome/browser/ui/gtk/browser_toolbar_gtk.h"
     16 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
     17 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
     18 #include "chrome/browser/ui/gtk/gtk_util.h"
     19 #include "ui/base/gtk/gtk_hig_constants.h"
     20 #include "ui/gfx/gtk_util.h"
     21 #include "ui/gfx/image/image.h"
     22 
     23 namespace {
     24 
     25 // Text size of the message label. 12.1px = 9pt @ 96dpi.
     26 const double kMessageTextSize = 12.1;
     27 
     28 // Minimum width of the message label.
     29 const int kMinMessageLabelWidth = 250;
     30 
     31 }  // namespace
     32 
     33 GlobalErrorBubble::GlobalErrorBubble(Browser* browser,
     34                                      const base::WeakPtr<GlobalError>& error,
     35                                      GtkWidget* anchor)
     36     : browser_(browser),
     37       bubble_(NULL),
     38       error_(error),
     39       message_width_(kMinMessageLabelWidth) {
     40   DCHECK(browser_);
     41   DCHECK(error_.get());
     42   GtkWidget* content = gtk_vbox_new(FALSE, ui::kControlSpacing);
     43   gtk_container_set_border_width(GTK_CONTAINER(content),
     44                                  ui::kContentAreaBorder);
     45   g_signal_connect(content, "destroy", G_CALLBACK(OnDestroyThunk), this);
     46 
     47   GtkThemeService* theme_service =
     48       GtkThemeService::GetFrom(browser_->profile());
     49 
     50   gfx::Image image = error_->GetBubbleViewIcon();
     51   CHECK(!image.IsEmpty());
     52   GdkPixbuf* pixbuf = image.ToGdkPixbuf();
     53   GtkWidget* image_view = gtk_image_new_from_pixbuf(pixbuf);
     54 
     55   GtkWidget* title_label = theme_service->BuildLabel(
     56       UTF16ToUTF8(error_->GetBubbleViewTitle()),
     57       ui::kGdkBlack);
     58   std::vector<string16> messages = error_->GetBubbleViewMessages();
     59   for (size_t i = 0; i < messages.size(); ++i) {
     60     message_labels_.push_back(theme_service->BuildLabel(
     61         UTF16ToUTF8(messages[i]),
     62         ui::kGdkBlack));
     63     gtk_util::ForceFontSizePixels(message_labels_[i], kMessageTextSize);
     64   }
     65   // Message label will be sized later in "realize" callback because we need
     66   // to know the width of the title and the width of the buttons group.
     67   GtkWidget* accept_button = gtk_button_new_with_label(
     68       UTF16ToUTF8(error_->GetBubbleViewAcceptButtonLabel()).c_str());
     69   string16 cancel_string = error_->GetBubbleViewCancelButtonLabel();
     70   GtkWidget* cancel_button = NULL;
     71   if (!cancel_string.empty()) {
     72     cancel_button =
     73         gtk_button_new_with_label(UTF16ToUTF8(cancel_string).c_str());
     74   }
     75 
     76   // Top, icon and title.
     77   GtkWidget* top = gtk_hbox_new(FALSE, ui::kControlSpacing);
     78   gtk_box_pack_start(GTK_BOX(top), image_view, FALSE, FALSE, 0);
     79   gtk_box_pack_start(GTK_BOX(top), title_label, FALSE, FALSE, 0);
     80   gtk_box_pack_start(GTK_BOX(content), top, FALSE, FALSE, 0);
     81 
     82   // Middle, messages.
     83   for (size_t i = 0; i < message_labels_.size(); ++i)
     84     gtk_box_pack_start(GTK_BOX(content), message_labels_[i], FALSE, FALSE, 0);
     85   gtk_box_pack_start(GTK_BOX(content), gtk_hbox_new(FALSE, 0), FALSE, FALSE, 0);
     86 
     87   // Bottom, accept and cancel button.
     88   // We want the buttons on the right, so just use an expanding label to fill
     89   // all of the extra space on the left.
     90   GtkWidget* bottom = gtk_hbox_new(FALSE, ui::kControlSpacing);
     91   gtk_box_pack_start(GTK_BOX(bottom), gtk_label_new(NULL), TRUE, TRUE, 0);
     92   if (cancel_button)
     93     gtk_box_pack_start(GTK_BOX(bottom), cancel_button, FALSE, FALSE, 0);
     94   gtk_box_pack_start(GTK_BOX(bottom), accept_button, FALSE, FALSE, 0);
     95   gtk_box_pack_start(GTK_BOX(content), bottom, FALSE, FALSE, 0);
     96 
     97   gtk_widget_grab_focus(accept_button);
     98 
     99   g_signal_connect(accept_button, "clicked",
    100                    G_CALLBACK(OnAcceptButtonThunk), this);
    101   if (cancel_button) {
    102     g_signal_connect(cancel_button, "clicked",
    103                      G_CALLBACK(OnCancelButtonThunk), this);
    104   }
    105 
    106   g_signal_connect(top, "realize", G_CALLBACK(OnRealizeThunk), this);
    107   g_signal_connect(bottom, "realize", G_CALLBACK(OnRealizeThunk), this);
    108 
    109   bubble_ = BubbleGtk::Show(anchor,
    110                             NULL,
    111                             content,
    112                             BubbleGtk::ANCHOR_TOP_RIGHT,
    113                             BubbleGtk::MATCH_SYSTEM_THEME |
    114                                 BubbleGtk::POPUP_WINDOW |
    115                                 BubbleGtk::GRAB_INPUT,
    116                             theme_service,
    117                             this);  // error_
    118 }
    119 
    120 GlobalErrorBubble::~GlobalErrorBubble() {
    121 }
    122 
    123 void GlobalErrorBubble::BubbleClosing(BubbleGtk* bubble,
    124                                       bool closed_by_escape) {
    125   if (error_.get())
    126     error_->BubbleViewDidClose(browser_);
    127 }
    128 
    129 void GlobalErrorBubble::OnDestroy(GtkWidget* sender) {
    130   delete this;
    131 }
    132 
    133 void GlobalErrorBubble::OnAcceptButton(GtkWidget* sender) {
    134   if (error_.get())
    135     error_->BubbleViewAcceptButtonPressed(browser_);
    136   bubble_->Close();
    137 }
    138 
    139 void GlobalErrorBubble::OnCancelButton(GtkWidget* sender) {
    140   if (error_.get())
    141     error_->BubbleViewCancelButtonPressed(browser_);
    142   bubble_->Close();
    143 }
    144 
    145 void GlobalErrorBubble::OnRealize(GtkWidget* sender) {
    146   int width = gtk_util::GetWidgetSize(sender).width();
    147   message_width_ = std::max(message_width_, width);
    148   for (size_t i = 0; i < message_labels_.size(); ++i)
    149     gtk_util::SetLabelWidth(message_labels_[i], message_width_);
    150 }
    151 
    152 void GlobalErrorBubble::CloseBubbleView() {
    153   bubble_->Close();
    154 }
    155 
    156 GlobalErrorBubbleViewBase* GlobalErrorBubbleViewBase::ShowBubbleView(
    157     Browser* browser,
    158     const base::WeakPtr<GlobalError>& error) {
    159   BrowserWindowGtk* browser_window =
    160       BrowserWindowGtk::GetBrowserWindowForNativeWindow(
    161           browser->window()->GetNativeWindow());
    162   GtkWidget* anchor = browser_window->GetToolbar()->GetAppMenuButton();
    163 
    164   // The bubble will be automatically deleted when it's closed.
    165   return new GlobalErrorBubble(browser, error, anchor);
    166 }
    167