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.
5 #include "chrome/browser/ui/gtk/confirm_bubble_gtk.h"
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"
27 // Padding between content and edge of bubble.
28 const int kContentBorder
= 7;
30 // Horizontal spacing between the image view and the label.
31 const int kImageViewSpacing
= 5;
33 // Vertical spacing between labels.
34 const int kInterLineSpacing
= 5;
36 // Text size of the message label. 12.1px = 9pt @ 96dpi.
37 const double kMessageTextSize
= 12.1;
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;
45 ConfirmBubbleGtk::ConfirmBubbleGtk(gfx::NativeView anchor
,
46 const gfx::Point
& anchor_point
,
47 ConfirmBubbleModel
* model
)
50 anchor_point_(anchor_point
),
55 ConfirmBubbleGtk::~ConfirmBubbleGtk() {
58 void ConfirmBubbleGtk::BubbleClosing(BubbleGtk
* bubble
,
59 bool closed_by_escape
) {
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.
70 GtkThemeService
* theme_service
= GtkThemeService::GetFrom(
71 browser_window
->browser()->profile());
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);
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);
83 GtkWidget
* title_label
= theme_service
->BuildLabel(
84 base::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);
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);
94 // Add the message label to the second row.
95 GtkWidget
* message_label
= theme_service
->BuildLabel(
96 base::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);
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 base::UTF16ToUTF8(link_text
).c_str());
107 g_signal_connect(link_button
, "clicked", G_CALLBACK(OnLinkButtonThunk
),
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);
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(base::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);
129 GtkWidget
* ok_button
= gtk_button_new_with_label(base::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);
134 gtk_box_pack_start(GTK_BOX(content
), row
, FALSE
, FALSE
, 0);
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());
143 bubble_
= BubbleGtk::Show(anchor_
,
146 BubbleGtk::FLOAT_BELOW_RECT
,
147 BubbleGtk::MATCH_SYSTEM_THEME
|
148 BubbleGtk::POPUP_WINDOW
|
149 BubbleGtk::GRAB_INPUT
,
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.
160 void ConfirmBubbleGtk::OnCloseButton(GtkWidget
* sender
) {
164 void ConfirmBubbleGtk::OnOkButton(GtkWidget
* sender
) {
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.
171 void ConfirmBubbleGtk::OnCancelButton(GtkWidget
* sender
) {
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.
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.
187 void ShowConfirmBubble(gfx::NativeView view
,
188 const gfx::Point
& origin
,
189 ConfirmBubbleModel
* model
) {
190 ConfirmBubbleGtk
* bubble
= new ConfirmBubbleGtk(view
, origin
, model
);
194 } // namespace chrome