Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(298)

Side by Side Diff: chrome/browser/ui/gtk/content_setting_bubble_gtk.cc

Issue 231733005: Delete the GTK+ port of Chrome. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remerge to ToT Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/content_setting_bubble_gtk.h"
6
7 #include <set>
8 #include <string>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/i18n/rtl.h"
13 #include "base/stl_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/content_settings/host_content_settings_map.h"
16 #include "chrome/browser/plugins/plugin_finder.h"
17 #include "chrome/browser/plugins/plugin_metadata.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/content_settings/content_setting_bubble_model.h"
20 #include "chrome/browser/ui/content_settings/content_setting_media_menu_model.h"
21 #include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"
22 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
23 #include "chrome/browser/ui/gtk/gtk_util.h"
24 #include "chrome/common/content_settings.h"
25 #include "content/public/browser/plugin_service.h"
26 #include "content/public/browser/web_contents.h"
27 #include "grit/generated_resources.h"
28 #include "grit/ui_resources.h"
29 #include "ui/base/gtk/gtk_hig_constants.h"
30 #include "ui/base/l10n/l10n_util.h"
31 #include "ui/base/models/simple_menu_model.h"
32 #include "ui/gfx/font_list.h"
33 #include "ui/gfx/gtk_util.h"
34 #include "ui/gfx/text_elider.h"
35
36 using content::PluginService;
37 using content::WebContents;
38
39 namespace {
40
41 // The maximum width of a title entry in the content box. We elide anything
42 // longer than this.
43 const int kMaxLinkPixelSize = 500;
44
45 // The minimum and maximum width of the media menu buttons.
46 const int kMinMediaMenuButtonWidth = 100;
47 const int kMaxMediaMenuButtonWidth = 600;
48
49 std::string BuildElidedText(const std::string& input) {
50 return base::UTF16ToUTF8(gfx::ElideText(
51 base::UTF8ToUTF16(input),
52 gfx::FontList(),
53 kMaxLinkPixelSize,
54 gfx::ELIDE_AT_END));
55 }
56
57 } // namespace
58
59 ContentSettingBubbleGtk::ContentSettingBubbleGtk(
60 GtkWidget* anchor,
61 BubbleDelegateGtk* delegate,
62 ContentSettingBubbleModel* content_setting_bubble_model,
63 Profile* profile)
64 : anchor_(anchor),
65 profile_(profile),
66 delegate_(delegate),
67 content_setting_bubble_model_(content_setting_bubble_model),
68 bubble_(NULL) {
69 BuildBubble();
70 }
71
72 ContentSettingBubbleGtk::~ContentSettingBubbleGtk() {
73 }
74
75 void ContentSettingBubbleGtk::Close() {
76 STLDeleteValues(&media_menus_);
77
78 if (bubble_)
79 bubble_->Close();
80 }
81
82 void ContentSettingBubbleGtk::UpdateMenuLabel(content::MediaStreamType type,
83 const std::string& label) {
84 GtkMediaMenuMap::const_iterator it = media_menus_.begin();
85 for (; it != media_menus_.end(); ++it) {
86 if (it->second->type == type) {
87 gtk_label_set_text(GTK_LABEL(it->second->label.get()), label.c_str());
88 return;
89 }
90 }
91 NOTREACHED();
92 }
93
94 void ContentSettingBubbleGtk::BubbleClosing(BubbleGtk* bubble,
95 bool closed_by_escape) {
96 delegate_->BubbleClosing(bubble, closed_by_escape);
97 delete this;
98 }
99
100 void ContentSettingBubbleGtk::BuildBubble() {
101 GtkThemeService* theme_provider = GtkThemeService::GetFrom(profile_);
102
103 GtkWidget* bubble_content = gtk_vbox_new(FALSE, ui::kControlSpacing);
104 gtk_container_set_border_width(GTK_CONTAINER(bubble_content),
105 ui::kContentAreaBorder);
106
107 const ContentSettingBubbleModel::BubbleContent& content =
108 content_setting_bubble_model_->bubble_content();
109 if (!content.title.empty()) {
110 // Add the content label.
111 GtkWidget* label = theme_provider->BuildLabel(content.title.c_str(),
112 ui::kGdkBlack);
113 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
114 gtk_box_pack_start(GTK_BOX(bubble_content), label, FALSE, FALSE, 0);
115 }
116
117 if (content_setting_bubble_model_->content_type() ==
118 CONTENT_SETTINGS_TYPE_POPUPS) {
119 const std::vector<ContentSettingBubbleModel::PopupItem>& popup_items =
120 content.popup_items;
121 GtkWidget* table = gtk_table_new(popup_items.size(), 2, FALSE);
122 int row = 0;
123 for (std::vector<ContentSettingBubbleModel::PopupItem>::const_iterator
124 i(popup_items.begin()); i != popup_items.end(); ++i, ++row) {
125 GtkWidget* image = gtk_image_new();
126 if (!i->image.IsEmpty()) {
127 GdkPixbuf* icon_pixbuf = i->image.ToGdkPixbuf();
128 gtk_image_set_from_pixbuf(GTK_IMAGE(image), icon_pixbuf);
129
130 // We stuff the image in an event box so we can trap mouse clicks on the
131 // image (and launch the popup).
132 GtkWidget* event_box = gtk_event_box_new();
133 gtk_container_add(GTK_CONTAINER(event_box), image);
134
135 popup_icons_[event_box] = i -popup_items.begin();
136 g_signal_connect(event_box, "button_press_event",
137 G_CALLBACK(OnPopupIconButtonPressThunk), this);
138 gtk_table_attach(GTK_TABLE(table), event_box, 0, 1, row, row + 1,
139 GTK_FILL, GTK_FILL, ui::kControlSpacing / 2,
140 ui::kControlSpacing / 2);
141 }
142
143 GtkWidget* button = gtk_chrome_link_button_new(
144 BuildElidedText(i->title).c_str());
145 popup_links_[button] = i -popup_items.begin();
146 g_signal_connect(button, "clicked", G_CALLBACK(OnPopupLinkClickedThunk),
147 this);
148 gtk_table_attach(GTK_TABLE(table), button, 1, 2, row, row + 1,
149 GTK_FILL, GTK_FILL, ui::kControlSpacing / 2,
150 ui::kControlSpacing / 2);
151 }
152
153 gtk_box_pack_start(GTK_BOX(bubble_content), table, FALSE, FALSE, 0);
154 }
155
156 const ContentSettingBubbleModel::RadioGroup& radio_group =
157 content.radio_group;
158 for (ContentSettingBubbleModel::RadioItems::const_iterator i =
159 radio_group.radio_items.begin();
160 i != radio_group.radio_items.end(); ++i) {
161 std::string elided = BuildElidedText(*i);
162 GtkWidget* radio = radio_group_gtk_.empty() ?
163 gtk_radio_button_new(NULL) :
164 gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(radio_group_gtk_[0]));
165 GtkWidget* label =
166 theme_provider->BuildLabel(elided.c_str(), ui::kGdkBlack);
167 gtk_container_add(GTK_CONTAINER(radio), label);
168 gtk_box_pack_start(GTK_BOX(bubble_content), radio, FALSE, FALSE, 0);
169 if (i - radio_group.radio_items.begin() == radio_group.default_item) {
170 // We must set the default value before we attach the signal handlers
171 // or pain occurs.
172 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
173 }
174 if (!content.radio_group_enabled)
175 gtk_widget_set_sensitive(radio, FALSE);
176 radio_group_gtk_.push_back(radio);
177 }
178 for (std::vector<GtkWidget*>::const_iterator i = radio_group_gtk_.begin();
179 i != radio_group_gtk_.end(); ++i) {
180 // We can attach signal handlers now that all defaults are set.
181 g_signal_connect(*i, "toggled", G_CALLBACK(OnRadioToggledThunk), this);
182 }
183
184 // Layout code for the media device menus.
185 if (content_setting_bubble_model_->content_type() ==
186 CONTENT_SETTINGS_TYPE_MEDIASTREAM) {
187 GtkWidget* table = gtk_table_new(content.media_menus.size(), 2, FALSE);
188 int menu_width = 0;
189 int row = 0;
190 for (ContentSettingBubbleModel::MediaMenuMap::const_iterator i(
191 content.media_menus.begin());
192 i != content.media_menus.end();
193 ++i, ++row) {
194 GtkWidget* label = theme_provider->BuildLabel(
195 i->second.label.c_str(), ui::kGdkBlack);
196 gtk_table_attach(GTK_TABLE(table), gtk_util::LeftAlignMisc(label), 0, 1,
197 row, row + 1, GTK_FILL, GTK_FILL,
198 ui::kControlSpacing / 2, ui::kControlSpacing / 2);
199
200 // Build up the gtk menu button.
201 MediaMenuGtk* gtk_menu = new MediaMenuGtk(i->first);
202 gtk_menu->label.Own(
203 gtk_label_new(i->second.selected_device.name.c_str()));
204 GtkWidget* button = gtk_button_new();
205 GtkWidget* button_content = gtk_hbox_new(FALSE, 0);
206 gtk_box_pack_start(GTK_BOX(button_content),
207 gtk_util::LeftAlignMisc(gtk_menu->label.get()),
208 FALSE, FALSE, 0);
209 GtkWidget* arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
210 gtk_box_pack_end(GTK_BOX(button_content), arrow, FALSE, FALSE, 0);
211
212 gtk_container_add(GTK_CONTAINER(button), button_content);
213
214 // Store the gtk menu to the map.
215 gtk_menu->menu_model.reset(new ContentSettingMediaMenuModel(
216 gtk_menu->type,
217 content_setting_bubble_model_.get(),
218 base::Bind(&ContentSettingBubbleGtk::UpdateMenuLabel,
219 base::Unretained(this))));
220 gtk_menu->menu.reset(new MenuGtk(NULL, gtk_menu->menu_model.get()));
221 media_menus_[button] = gtk_menu;
222
223 if (!gtk_menu->menu_model->GetItemCount()) {
224 // Show a "None available" title and grey out the menu when there is
225 // no available device.
226 UpdateMenuLabel(
227 gtk_menu->type,
228 l10n_util::GetStringUTF8(IDS_MEDIA_MENU_NO_DEVICE_TITLE));
229 gtk_widget_set_sensitive(button, FALSE);
230 }
231
232 // Disable the device selection when the website is managing the devices
233 // itself.
234 if (i->second.disabled)
235 gtk_widget_set_sensitive(button, FALSE);
236
237 // Use the longest width of the menus as the width of the menu buttons.
238 GtkRequisition menu_req;
239 gtk_widget_size_request(gtk_menu->menu->widget(), &menu_req);
240 menu_width = std::max(menu_width, menu_req.width);
241
242 g_signal_connect(button, "clicked",
243 G_CALLBACK(OnMenuButtonClickedThunk), this);
244 gtk_table_attach(GTK_TABLE(table), button, 1, 2, row, row + 1,
245 GTK_FILL, GTK_FILL, ui::kControlSpacing * 2,
246 ui::kControlSpacing / 2);
247 }
248
249 // Make sure the width is within [kMinMediaMenuButtonWidth,
250 // kMaxMediaMenuButtonWidth].
251 menu_width = std::max(kMinMediaMenuButtonWidth, menu_width);
252 menu_width = std::min(kMaxMediaMenuButtonWidth, menu_width);
253
254 // Set all the menu buttons to the width we calculated above.
255 for (GtkMediaMenuMap::const_iterator i = media_menus_.begin();
256 i != media_menus_.end(); ++i)
257 gtk_widget_set_size_request(i->first, menu_width, -1);
258
259 gtk_box_pack_start(GTK_BOX(bubble_content), table, FALSE, FALSE, 0);
260 }
261
262 for (std::vector<ContentSettingBubbleModel::DomainList>::const_iterator i =
263 content.domain_lists.begin();
264 i != content.domain_lists.end(); ++i) {
265 // Put each list into its own vbox to allow spacing between lists.
266 GtkWidget* list_content = gtk_vbox_new(FALSE, ui::kControlSpacing);
267
268 GtkWidget* label = theme_provider->BuildLabel(
269 BuildElidedText(i->title).c_str(), ui::kGdkBlack);
270 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
271 GtkWidget* label_box = gtk_hbox_new(FALSE, 0);
272 gtk_box_pack_start(GTK_BOX(label_box), label, FALSE, FALSE, 0);
273 gtk_box_pack_start(GTK_BOX(list_content), label_box, FALSE, FALSE, 0);
274 for (std::set<std::string>::const_iterator j = i->hosts.begin();
275 j != i->hosts.end(); ++j) {
276 gtk_box_pack_start(GTK_BOX(list_content),
277 gtk_util::IndentWidget(gtk_util::CreateBoldLabel(*j)),
278 FALSE, FALSE, 0);
279 }
280 gtk_box_pack_start(GTK_BOX(bubble_content), list_content, FALSE, FALSE,
281 ui::kControlSpacing);
282 }
283
284 if (!content.custom_link.empty()) {
285 GtkWidget* custom_link_box = gtk_hbox_new(FALSE, 0);
286 GtkWidget* custom_link = NULL;
287 if (content.custom_link_enabled) {
288 custom_link =
289 theme_provider->BuildChromeLinkButton(content.custom_link.c_str());
290 g_signal_connect(custom_link, "clicked",
291 G_CALLBACK(OnCustomLinkClickedThunk), this);
292 } else {
293 custom_link = theme_provider->BuildLabel(content.custom_link.c_str(),
294 ui::kGdkBlack);
295 gtk_misc_set_alignment(GTK_MISC(custom_link), 0, 0.5);
296 }
297 DCHECK(custom_link);
298 gtk_box_pack_start(GTK_BOX(custom_link_box), custom_link, FALSE, FALSE, 0);
299 gtk_box_pack_start(GTK_BOX(bubble_content), custom_link_box,
300 FALSE, FALSE, 0);
301 }
302
303 gtk_box_pack_start(GTK_BOX(bubble_content), gtk_hseparator_new(),
304 FALSE, FALSE, 0);
305
306 GtkWidget* bottom_box = gtk_hbox_new(FALSE, 0);
307
308 GtkWidget* manage_link =
309 theme_provider->BuildChromeLinkButton(content.manage_link.c_str());
310 g_signal_connect(manage_link, "clicked", G_CALLBACK(OnManageLinkClickedThunk),
311 this);
312 gtk_box_pack_start(GTK_BOX(bottom_box), manage_link, FALSE, FALSE, 0);
313
314 GtkWidget* button =
315 gtk_button_new_with_label(l10n_util::GetStringUTF8(IDS_DONE).c_str());
316 g_signal_connect(button, "clicked", G_CALLBACK(OnCloseButtonClickedThunk),
317 this);
318 gtk_box_pack_end(GTK_BOX(bottom_box), button, FALSE, FALSE, 0);
319
320 gtk_box_pack_start(GTK_BOX(bubble_content), bottom_box, FALSE, FALSE, 0);
321 gtk_widget_grab_focus(button);
322
323 bubble_ = BubbleGtk::Show(anchor_,
324 NULL,
325 bubble_content,
326 BubbleGtk::ANCHOR_TOP_RIGHT,
327 BubbleGtk::MATCH_SYSTEM_THEME |
328 BubbleGtk::POPUP_WINDOW |
329 BubbleGtk::GRAB_INPUT,
330 theme_provider,
331 this);
332 }
333
334 void ContentSettingBubbleGtk::OnPopupIconButtonPress(
335 GtkWidget* icon_event_box,
336 GdkEventButton* event) {
337 PopupMap::iterator i(popup_icons_.find(icon_event_box));
338 DCHECK(i != popup_icons_.end());
339 content_setting_bubble_model_->OnPopupClicked(i->second);
340 // The views interface implicitly closes because of the launching of a new
341 // window; we need to do that explicitly.
342 Close();
343 }
344
345 void ContentSettingBubbleGtk::OnPopupLinkClicked(GtkWidget* button) {
346 PopupMap::iterator i(popup_links_.find(button));
347 DCHECK(i != popup_links_.end());
348 content_setting_bubble_model_->OnPopupClicked(i->second);
349 // The views interface implicitly closes because of the launching of a new
350 // window; we need to do that explicitly.
351 Close();
352 }
353
354 void ContentSettingBubbleGtk::OnRadioToggled(GtkWidget* widget) {
355 for (ContentSettingBubbleGtk::RadioGroupGtk::const_iterator i =
356 radio_group_gtk_.begin();
357 i != radio_group_gtk_.end(); ++i) {
358 if (widget == *i) {
359 content_setting_bubble_model_->OnRadioClicked(
360 i - radio_group_gtk_.begin());
361 return;
362 }
363 }
364 NOTREACHED() << "unknown radio toggled";
365 }
366
367 void ContentSettingBubbleGtk::OnCloseButtonClicked(GtkWidget* button) {
368 content_setting_bubble_model_->OnDoneClicked();
369 Close();
370 }
371
372 void ContentSettingBubbleGtk::OnCustomLinkClicked(GtkWidget* button) {
373 content_setting_bubble_model_->OnCustomLinkClicked();
374 Close();
375 }
376
377 void ContentSettingBubbleGtk::OnManageLinkClicked(GtkWidget* button) {
378 content_setting_bubble_model_->OnManageLinkClicked();
379 Close();
380 }
381
382 void ContentSettingBubbleGtk::OnMenuButtonClicked(GtkWidget* button) {
383 GtkMediaMenuMap::iterator i(media_menus_.find(button));
384 DCHECK(i != media_menus_.end());
385 i->second->menu->PopupForWidget(button, 1, gtk_get_current_event_time());
386 }
387
388 ContentSettingBubbleGtk::MediaMenuGtk::MediaMenuGtk(
389 content::MediaStreamType type)
390 : type(type) {}
391
392 ContentSettingBubbleGtk::MediaMenuGtk::~MediaMenuGtk() {}
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698