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