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/confirm_bubble_gtk.h"
      6 
      7 #include <gtk/gtk.h>
      8 
      9 #include "base/logging.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "chrome/browser/ui/browser.h"
     12 #include "chrome/browser/ui/confirm_bubble.h"
     13 #include "chrome/browser/ui/confirm_bubble_model.h"
     14 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
     15 #include "chrome/browser/ui/gtk/custom_button.h"
     16 #include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"
     17 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
     18 #include "chrome/browser/ui/gtk/gtk_util.h"
     19 #include "grit/theme_resources.h"
     20 #include "ui/base/gtk/gtk_hig_constants.h"
     21 #include "ui/base/gtk/gtk_screen_util.h"
     22 #include "ui/base/resource/resource_bundle.h"
     23 #include "ui/gfx/image/image.h"
     24 
     25 namespace {
     26 
     27 // Padding between content and edge of bubble.
     28 const int kContentBorder = 7;
     29 
     30 // Horizontal spacing between the image view and the label.
     31 const int kImageViewSpacing = 5;
     32 
     33 // Vertical spacing between labels.
     34 const int kInterLineSpacing = 5;
     35 
     36 // Text size of the message label. 12.1px = 9pt @ 96dpi.
     37 const double kMessageTextSize = 12.1;
     38 
     39 // Maximum width for the message field. We will wrap the message text when its
     40 // width is wider than this.
     41 const int kMaxMessageWidth = 400;
     42 
     43 }  // namespace
     44 
     45 ConfirmBubbleGtk::ConfirmBubbleGtk(gfx::NativeView anchor,
     46                                    const gfx::Point& anchor_point,
     47                                    ConfirmBubbleModel* model)
     48     : bubble_(NULL),
     49       anchor_(anchor),
     50       anchor_point_(anchor_point),
     51       model_(model) {
     52   DCHECK(model);
     53 }
     54 
     55 ConfirmBubbleGtk::~ConfirmBubbleGtk() {
     56 }
     57 
     58 void ConfirmBubbleGtk::BubbleClosing(BubbleGtk* bubble,
     59                                      bool closed_by_escape) {
     60 }
     61 
     62 void ConfirmBubbleGtk::Show() {
     63   GtkWidget* toplevel = gtk_widget_get_toplevel(anchor_);
     64   BrowserWindowGtk* browser_window =
     65       BrowserWindowGtk::GetBrowserWindowForNativeWindow(GTK_WINDOW(toplevel));
     66   GtkThemeService* theme_service = GtkThemeService::GetFrom(
     67       browser_window->browser()->profile());
     68 
     69   GtkWidget* content = gtk_vbox_new(FALSE, kInterLineSpacing);
     70   gtk_container_set_border_width(GTK_CONTAINER(content), kContentBorder);
     71   g_signal_connect(content, "destroy", G_CALLBACK(OnDestroyThunk), this);
     72 
     73   // Add the icon, the title label and the close button to the first row.
     74   GtkWidget* row = gtk_hbox_new(FALSE, kImageViewSpacing);
     75   GtkWidget* icon_view =
     76       gtk_image_new_from_pixbuf(model_->GetIcon()->ToGdkPixbuf());
     77   gtk_box_pack_start(GTK_BOX(row), icon_view, FALSE, FALSE, 0);
     78 
     79   GtkWidget* title_label = theme_service->BuildLabel(
     80       UTF16ToUTF8(model_->GetTitle()), ui::kGdkBlack);
     81   gtk_box_pack_start(GTK_BOX(row), title_label, FALSE, FALSE, 0);
     82   gtk_box_pack_start(GTK_BOX(row), gtk_label_new(NULL), TRUE, TRUE, 0);
     83 
     84   close_button_.reset(CustomDrawButton::CloseButtonBubble(theme_service));
     85   g_signal_connect(close_button_->widget(), "clicked",
     86                    G_CALLBACK(OnCloseButtonThunk), this);
     87   gtk_box_pack_end(GTK_BOX(row), close_button_->widget(), FALSE, FALSE, 0);
     88   gtk_box_pack_start(GTK_BOX(content), row, FALSE, FALSE, 0);
     89 
     90   // Add the message label to the second row.
     91   GtkWidget* message_label = theme_service->BuildLabel(
     92       UTF16ToUTF8(model_->GetMessageText()), ui::kGdkBlack);
     93   gtk_util::ForceFontSizePixels(message_label, kMessageTextSize);
     94   gtk_util::SetLabelWidth(message_label, kMaxMessageWidth);
     95   gtk_box_pack_start(GTK_BOX(content), message_label, FALSE, FALSE, 0);
     96 
     97   // Add the the link label to the third row if it exists.
     98   const string16 link_text = model_->GetLinkText();
     99   if (!link_text.empty()) {
    100     GtkWidget* row = gtk_hbox_new(FALSE, kImageViewSpacing);
    101     GtkWidget* link_button = gtk_chrome_link_button_new(
    102         UTF16ToUTF8(link_text).c_str());
    103     g_signal_connect(link_button, "clicked", G_CALLBACK(OnLinkButtonThunk),
    104                      this);
    105     gtk_util::ForceFontSizePixels(link_button, kMessageTextSize);
    106     gtk_box_pack_start(GTK_BOX(row), link_button, FALSE, FALSE, 0);
    107     gtk_box_pack_end(GTK_BOX(row), gtk_label_new(NULL), TRUE, TRUE, 0);
    108     gtk_box_pack_start(GTK_BOX(content), row, FALSE, FALSE, 0);
    109   }
    110 
    111   bool has_ok_button = !!(model_->GetButtons() & ConfirmBubbleModel::BUTTON_OK);
    112   bool has_cancel_button =
    113       !!(model_->GetButtons() & ConfirmBubbleModel::BUTTON_CANCEL);
    114   if (has_ok_button || has_cancel_button) {
    115     GtkWidget* row = gtk_hbox_new(FALSE, kImageViewSpacing);
    116     gtk_box_pack_start(GTK_BOX(row), gtk_label_new(NULL), TRUE, TRUE, 0);
    117     if (has_cancel_button) {
    118       GtkWidget* cancel_button = gtk_button_new_with_label(UTF16ToUTF8(
    119           model_->GetButtonLabel(ConfirmBubbleModel::BUTTON_CANCEL)).c_str());
    120       g_signal_connect(cancel_button, "clicked",
    121                        G_CALLBACK(OnCancelButtonThunk), this);
    122       gtk_box_pack_start(GTK_BOX(row), cancel_button, FALSE, FALSE, 0);
    123     }
    124     if (has_ok_button) {
    125       GtkWidget* ok_button = gtk_button_new_with_label(UTF16ToUTF8(
    126           model_->GetButtonLabel(ConfirmBubbleModel::BUTTON_OK)).c_str());
    127       g_signal_connect(ok_button, "clicked", G_CALLBACK(OnOkButtonThunk), this);
    128       gtk_box_pack_start(GTK_BOX(row), ok_button, FALSE, FALSE, 0);
    129     }
    130     gtk_box_pack_start(GTK_BOX(content), row, FALSE, FALSE, 0);
    131   }
    132 
    133   // Show a bubble consisting of the above widgets under the anchor point.
    134   gfx::Rect rect = ui::GetWidgetScreenBounds(anchor_);
    135   rect.set_x(anchor_point_.x() - rect.x());
    136   rect.set_y(anchor_point_.y() - rect.y());
    137   rect.set_width(0);
    138   rect.set_height(0);
    139   bubble_ = BubbleGtk::Show(anchor_,
    140                             &rect,
    141                             content,
    142                             BubbleGtk::FLOAT_BELOW_RECT,
    143                             BubbleGtk::MATCH_SYSTEM_THEME |
    144                                 BubbleGtk::POPUP_WINDOW |
    145                                 BubbleGtk::GRAB_INPUT,
    146                             theme_service,
    147                             this);  // error
    148 }
    149 
    150 void ConfirmBubbleGtk::OnDestroy(GtkWidget* sender) {
    151   // TODO(hbono): this code prevents the model from updating this view when we
    152   // click buttons. We should ask the model if we can delete this view.
    153   delete this;
    154 }
    155 
    156 void ConfirmBubbleGtk::OnCloseButton(GtkWidget* sender) {
    157   bubble_->Close();
    158 }
    159 
    160 void ConfirmBubbleGtk::OnOkButton(GtkWidget* sender) {
    161   model_->Accept();
    162   // TODO(hbono): this code prevents the model from updating this view when we
    163   // click this button. We should ask the model if we can close this view.
    164   bubble_->Close();
    165 }
    166 
    167 void ConfirmBubbleGtk::OnCancelButton(GtkWidget* sender) {
    168   model_->Cancel();
    169   // TODO(hbono): this code prevents the model from updating this view when we
    170   // click this button. We should ask the model if we can close this view.
    171   bubble_->Close();
    172 }
    173 
    174 void ConfirmBubbleGtk::OnLinkButton(GtkWidget* sender) {
    175   model_->LinkClicked();
    176   // TODO(hbono): this code prevents the model from updating this view when we
    177   // click this link. We should ask the model if we can close this view.
    178   bubble_->Close();
    179 }
    180 
    181 namespace chrome {
    182 
    183 void ShowConfirmBubble(gfx::NativeView view,
    184                        const gfx::Point& origin,
    185                        ConfirmBubbleModel* model) {
    186   ConfirmBubbleGtk* bubble = new ConfirmBubbleGtk(view, origin, model);
    187   bubble->Show();
    188 }
    189 
    190 }  // namespace chrome
    191