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   // If the top level window is not a browser window but instead a pop-up
     67   // dialog, |browser_window| will be null.
     68   if (!browser_window)
     69     return;
     70   GtkThemeService* theme_service = GtkThemeService::GetFrom(
     71       browser_window->browser()->profile());
     72 
     73   GtkWidget* content = gtk_vbox_new(FALSE, kInterLineSpacing);
     74   gtk_container_set_border_width(GTK_CONTAINER(content), kContentBorder);
     75   g_signal_connect(content, "destroy", G_CALLBACK(OnDestroyThunk), this);
     76 
     77   // Add the icon, the title label and the close button to the first row.
     78   GtkWidget* row = gtk_hbox_new(FALSE, kImageViewSpacing);
     79   GtkWidget* icon_view =
     80       gtk_image_new_from_pixbuf(model_->GetIcon()->ToGdkPixbuf());
     81   gtk_box_pack_start(GTK_BOX(row), icon_view, FALSE, FALSE, 0);
     82 
     83   GtkWidget* title_label = theme_service->BuildLabel(
     84       UTF16ToUTF8(model_->GetTitle()), ui::kGdkBlack);
     85   gtk_box_pack_start(GTK_BOX(row), title_label, FALSE, FALSE, 0);
     86   gtk_box_pack_start(GTK_BOX(row), gtk_label_new(NULL), TRUE, TRUE, 0);
     87 
     88   close_button_.reset(CustomDrawButton::CloseButtonBubble(theme_service));
     89   g_signal_connect(close_button_->widget(), "clicked",
     90                    G_CALLBACK(OnCloseButtonThunk), this);
     91   gtk_box_pack_end(GTK_BOX(row), close_button_->widget(), FALSE, FALSE, 0);
     92   gtk_box_pack_start(GTK_BOX(content), row, FALSE, FALSE, 0);
     93 
     94   // Add the message label to the second row.
     95   GtkWidget* message_label = theme_service->BuildLabel(
     96       UTF16ToUTF8(model_->GetMessageText()), ui::kGdkBlack);
     97   gtk_util::ForceFontSizePixels(message_label, kMessageTextSize);
     98   gtk_util::SetLabelWidth(message_label, kMaxMessageWidth);
     99   gtk_box_pack_start(GTK_BOX(content), message_label, FALSE, FALSE, 0);
    100 
    101   // Add the the link label to the third row if it exists.
    102   const base::string16 link_text = model_->GetLinkText();
    103   if (!link_text.empty()) {
    104     GtkWidget* row = gtk_hbox_new(FALSE, kImageViewSpacing);
    105     GtkWidget* link_button = gtk_chrome_link_button_new(
    106         UTF16ToUTF8(link_text).c_str());
    107     g_signal_connect(link_button, "clicked", G_CALLBACK(OnLinkButtonThunk),
    108                      this);
    109     gtk_util::ForceFontSizePixels(link_button, kMessageTextSize);
    110     gtk_box_pack_start(GTK_BOX(row), link_button, FALSE, FALSE, 0);
    111     gtk_box_pack_end(GTK_BOX(row), gtk_label_new(NULL), TRUE, TRUE, 0);
    112     gtk_box_pack_start(GTK_BOX(content), row, FALSE, FALSE, 0);
    113   }
    114 
    115   bool has_ok_button = !!(model_->GetButtons() & ConfirmBubbleModel::BUTTON_OK);
    116   bool has_cancel_button =
    117       !!(model_->GetButtons() & ConfirmBubbleModel::BUTTON_CANCEL);
    118   if (has_ok_button || has_cancel_button) {
    119     GtkWidget* row = gtk_hbox_new(FALSE, kImageViewSpacing);
    120     gtk_box_pack_start(GTK_BOX(row), gtk_label_new(NULL), TRUE, TRUE, 0);
    121     if (has_cancel_button) {
    122       GtkWidget* cancel_button = gtk_button_new_with_label(UTF16ToUTF8(
    123           model_->GetButtonLabel(ConfirmBubbleModel::BUTTON_CANCEL)).c_str());
    124       g_signal_connect(cancel_button, "clicked",
    125                        G_CALLBACK(OnCancelButtonThunk), this);
    126       gtk_box_pack_start(GTK_BOX(row), cancel_button, FALSE, FALSE, 0);
    127     }
    128     if (has_ok_button) {
    129       GtkWidget* ok_button = gtk_button_new_with_label(UTF16ToUTF8(
    130           model_->GetButtonLabel(ConfirmBubbleModel::BUTTON_OK)).c_str());
    131       g_signal_connect(ok_button, "clicked", G_CALLBACK(OnOkButtonThunk), this);
    132       gtk_box_pack_start(GTK_BOX(row), ok_button, FALSE, FALSE, 0);
    133     }
    134     gtk_box_pack_start(GTK_BOX(content), row, FALSE, FALSE, 0);
    135   }
    136 
    137   // Show a bubble consisting of the above widgets under the anchor point.
    138   gfx::Rect rect = ui::GetWidgetScreenBounds(anchor_);
    139   rect.set_x(anchor_point_.x() - rect.x());
    140   rect.set_y(anchor_point_.y() - rect.y());
    141   rect.set_width(0);
    142   rect.set_height(0);
    143   bubble_ = BubbleGtk::Show(anchor_,
    144                             &rect,
    145                             content,
    146                             BubbleGtk::FLOAT_BELOW_RECT,
    147                             BubbleGtk::MATCH_SYSTEM_THEME |
    148                                 BubbleGtk::POPUP_WINDOW |
    149                                 BubbleGtk::GRAB_INPUT,
    150                             theme_service,
    151                             this);  // error
    152 }
    153 
    154 void ConfirmBubbleGtk::OnDestroy(GtkWidget* sender) {
    155   // TODO(hbono): this code prevents the model from updating this view when we
    156   // click buttons. We should ask the model if we can delete this view.
    157   delete this;
    158 }
    159 
    160 void ConfirmBubbleGtk::OnCloseButton(GtkWidget* sender) {
    161   bubble_->Close();
    162 }
    163 
    164 void ConfirmBubbleGtk::OnOkButton(GtkWidget* sender) {
    165   model_->Accept();
    166   // TODO(hbono): this code prevents the model from updating this view when we
    167   // click this button. We should ask the model if we can close this view.
    168   bubble_->Close();
    169 }
    170 
    171 void ConfirmBubbleGtk::OnCancelButton(GtkWidget* sender) {
    172   model_->Cancel();
    173   // TODO(hbono): this code prevents the model from updating this view when we
    174   // click this button. We should ask the model if we can close this view.
    175   bubble_->Close();
    176 }
    177 
    178 void ConfirmBubbleGtk::OnLinkButton(GtkWidget* sender) {
    179   model_->LinkClicked();
    180   // TODO(hbono): this code prevents the model from updating this view when we
    181   // click this link. We should ask the model if we can close this view.
    182   bubble_->Close();
    183 }
    184 
    185 namespace chrome {
    186 
    187 void ShowConfirmBubble(gfx::NativeView view,
    188                        const gfx::Point& origin,
    189                        ConfirmBubbleModel* model) {
    190   ConfirmBubbleGtk* bubble = new ConfirmBubbleGtk(view, origin, model);
    191   bubble->Show();
    192 }
    193 
    194 }  // namespace chrome
    195