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

Side by Side Diff: chrome/browser/ui/gtk/bookmarks/bookmark_utils_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/bookmarks/bookmark_utils_gtk.h"
6
7 #include "base/pickle.h"
8 #include "base/strings/string16.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/bookmarks/bookmark_model.h"
12 #include "chrome/browser/bookmarks/bookmark_node_data.h"
13 #include "chrome/browser/bookmarks/bookmark_utils.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/themes/theme_properties.h"
16 #include "chrome/browser/ui/gtk/gtk_chrome_button.h"
17 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
18 #include "chrome/browser/ui/gtk/gtk_util.h"
19 #include "grit/generated_resources.h"
20 #include "grit/theme_resources.h"
21 #include "grit/ui_strings.h"
22 #include "net/base/filename_util.h"
23 #include "ui/base/dragdrop/gtk_dnd_util.h"
24 #include "ui/base/gtk/gtk_hig_constants.h"
25 #include "ui/base/gtk/gtk_screen_util.h"
26 #include "ui/base/l10n/l10n_util.h"
27 #include "ui/base/resource/resource_bundle.h"
28 #include "ui/gfx/canvas_skia_paint.h"
29 #include "ui/gfx/font_list.h"
30 #include "ui/gfx/geometry/rect.h"
31 #include "ui/gfx/image/image.h"
32 #include "ui/gfx/text_elider.h"
33
34 namespace {
35
36 // Spacing between the favicon and the text.
37 const int kBarButtonPadding = 4;
38
39 // Used in gtk_selection_data_set(). (I assume from this parameter that gtk has
40 // to some really exotic hardware...)
41 const int kBitsInAByte = 8;
42
43 // Maximum number of characters on a bookmark button.
44 const size_t kMaxCharsOnAButton = 15;
45
46 // Maximum number of characters on a menu label.
47 const int kMaxCharsOnAMenuLabel = 50;
48
49 // Padding between the chrome button highlight border and the contents (favicon,
50 // text).
51 const int kButtonPaddingTop = 0;
52 const int kButtonPaddingBottom = 0;
53 const int kButtonPaddingLeft = 5;
54 const int kButtonPaddingRight = 0;
55
56 void* AsVoid(const BookmarkNode* node) {
57 return const_cast<BookmarkNode*>(node);
58 }
59
60 // Creates the widget hierarchy for a bookmark button.
61 void PackButton(GdkPixbuf* pixbuf,
62 const base::string16& title,
63 bool ellipsize,
64 GtkThemeService* provider,
65 GtkWidget* button) {
66 GtkWidget* former_child = gtk_bin_get_child(GTK_BIN(button));
67 if (former_child)
68 gtk_container_remove(GTK_CONTAINER(button), former_child);
69
70 // We pack the button manually (rather than using gtk_button_set_*) so that
71 // we can have finer control over its label.
72 GtkWidget* image = gtk_image_new_from_pixbuf(pixbuf);
73
74 GtkWidget* box = gtk_hbox_new(FALSE, kBarButtonPadding);
75 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0);
76
77 std::string label_string = base::UTF16ToUTF8(title);
78 if (!label_string.empty()) {
79 GtkWidget* label = gtk_label_new(label_string.c_str());
80 // Until we switch to vector graphics, force the font size.
81 if (!provider->UsingNativeTheme())
82 gtk_util::ForceFontSizePixels(label, 13.4); // 13.4px == 10pt @ 96dpi
83
84 // Ellipsize long bookmark names.
85 if (ellipsize) {
86 gtk_label_set_max_width_chars(GTK_LABEL(label), kMaxCharsOnAButton);
87 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
88 }
89
90 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
91 SetButtonTextColors(label, provider);
92 }
93
94 GtkWidget* alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
95 // If we are not showing the label, don't set any padding, so that the icon
96 // will just be centered.
97 if (label_string.c_str()) {
98 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment),
99 kButtonPaddingTop, kButtonPaddingBottom,
100 kButtonPaddingLeft, kButtonPaddingRight);
101 }
102 gtk_container_add(GTK_CONTAINER(alignment), box);
103 gtk_container_add(GTK_CONTAINER(button), alignment);
104
105 gtk_widget_show_all(alignment);
106 }
107
108 const int kDragRepresentationWidth = 140;
109
110 struct DragRepresentationData {
111 public:
112 GdkPixbuf* favicon;
113 base::string16 text;
114 SkColor text_color;
115
116 DragRepresentationData(GdkPixbuf* favicon,
117 const base::string16& text,
118 SkColor text_color)
119 : favicon(favicon),
120 text(text),
121 text_color(text_color) {
122 g_object_ref(favicon);
123 }
124
125 ~DragRepresentationData() {
126 g_object_unref(favicon);
127 }
128
129 private:
130 DISALLOW_COPY_AND_ASSIGN(DragRepresentationData);
131 };
132
133 gboolean OnDragIconExpose(GtkWidget* sender,
134 GdkEventExpose* event,
135 DragRepresentationData* data) {
136 // Clear the background.
137 cairo_t* cr = gdk_cairo_create(event->window);
138 gdk_cairo_rectangle(cr, &event->area);
139 cairo_clip(cr);
140 cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
141 cairo_paint(cr);
142
143 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
144 gdk_cairo_set_source_pixbuf(cr, data->favicon, 0, 0);
145 cairo_paint(cr);
146 cairo_destroy(cr);
147
148 GtkAllocation allocation;
149 gtk_widget_get_allocation(sender, &allocation);
150
151 // Paint the title text.
152 gfx::CanvasSkiaPaint canvas(event, false);
153 int text_x = gdk_pixbuf_get_width(data->favicon) + kBarButtonPadding;
154 int text_width = allocation.width - text_x;
155 const gfx::Rect rect(text_x, 0, text_width, allocation.height);
156 canvas.DrawStringRectWithFlags(data->text, gfx::FontList(), data->text_color,
157 rect, gfx::Canvas::NO_SUBPIXEL_RENDERING);
158
159 return TRUE;
160 }
161
162 void OnDragIconDestroy(GtkWidget* drag_icon, DragRepresentationData* data) {
163 g_object_unref(drag_icon);
164 delete data;
165 }
166
167 } // namespace
168
169 const char kBookmarkNode[] = "bookmark-node";
170
171 GdkPixbuf* GetPixbufForNode(const BookmarkNode* node,
172 BookmarkModel* model,
173 bool native) {
174 GdkPixbuf* pixbuf;
175
176 if (node->is_url()) {
177 const gfx::Image& favicon = model->GetFavicon(node);
178 if (!favicon.IsEmpty()) {
179 pixbuf = favicon.CopyGdkPixbuf();
180 } else {
181 pixbuf = GtkThemeService::GetDefaultFavicon(native).ToGdkPixbuf();
182 g_object_ref(pixbuf);
183 }
184 } else {
185 pixbuf = GtkThemeService::GetFolderIcon(native).ToGdkPixbuf();
186 g_object_ref(pixbuf);
187 }
188
189 return pixbuf;
190 }
191
192 GtkWidget* GetDragRepresentation(GdkPixbuf* pixbuf,
193 const base::string16& title,
194 GtkThemeService* provider) {
195 GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP);
196
197 if (ui::IsScreenComposited() &&
198 gtk_util::AddWindowAlphaChannel(window)) {
199 DragRepresentationData* data = new DragRepresentationData(
200 pixbuf, title,
201 provider->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT));
202 g_signal_connect(window, "expose-event", G_CALLBACK(OnDragIconExpose),
203 data);
204 g_object_ref(window);
205 g_signal_connect(window, "destroy", G_CALLBACK(OnDragIconDestroy), data);
206
207 gtk_widget_set_size_request(window, kDragRepresentationWidth,
208 gfx::FontList().GetHeight());
209 } else {
210 if (!provider->UsingNativeTheme()) {
211 GdkColor color = provider->GetGdkColor(
212 ThemeProperties::COLOR_TOOLBAR);
213 gtk_widget_modify_bg(window, GTK_STATE_NORMAL, &color);
214 }
215 gtk_widget_realize(window);
216
217 GtkWidget* frame = gtk_frame_new(NULL);
218 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
219 gtk_container_add(GTK_CONTAINER(window), frame);
220
221 GtkWidget* floating_button = provider->BuildChromeButton();
222 PackButton(pixbuf, title, true, provider, floating_button);
223 gtk_container_add(GTK_CONTAINER(frame), floating_button);
224 gtk_widget_show_all(frame);
225 }
226
227 return window;
228 }
229
230 GtkWidget* GetDragRepresentationForNode(const BookmarkNode* node,
231 BookmarkModel* model,
232 GtkThemeService* provider) {
233 GdkPixbuf* pixbuf = GetPixbufForNode(
234 node, model, provider->UsingNativeTheme());
235 GtkWidget* widget = GetDragRepresentation(pixbuf, node->GetTitle(), provider);
236 g_object_unref(pixbuf);
237 return widget;
238 }
239
240 void ConfigureButtonForNode(const BookmarkNode* node,
241 BookmarkModel* model,
242 GtkWidget* button,
243 GtkThemeService* provider) {
244 GdkPixbuf* pixbuf =
245 GetPixbufForNode(node, model, provider->UsingNativeTheme());
246 PackButton(pixbuf, node->GetTitle(), node != model->other_node(), provider,
247 button);
248 g_object_unref(pixbuf);
249
250 std::string tooltip = BuildTooltipFor(node);
251 if (!tooltip.empty())
252 gtk_widget_set_tooltip_markup(button, tooltip.c_str());
253
254 g_object_set_data(G_OBJECT(button), kBookmarkNode, AsVoid(node));
255 }
256
257 void ConfigureAppsShortcutButton(GtkWidget* button, GtkThemeService* provider) {
258 GdkPixbuf* pixbuf = ui::ResourceBundle::GetSharedInstance().
259 GetNativeImageNamed(IDR_BOOKMARK_BAR_APPS_SHORTCUT,
260 ui::ResourceBundle::RTL_ENABLED).ToGdkPixbuf();
261 const base::string16& label = l10n_util::GetStringUTF16(
262 IDS_BOOKMARK_BAR_APPS_SHORTCUT_NAME);
263 PackButton(pixbuf, label, false, provider, button);
264 }
265
266 std::string BuildTooltipFor(const BookmarkNode* node) {
267 if (node->is_folder())
268 return std::string();
269
270 return gtk_util::BuildTooltipTitleFor(node->GetTitle(), node->url());
271 }
272
273 std::string BuildMenuLabelFor(const BookmarkNode* node) {
274 // This breaks on word boundaries. Ideally we would break on character
275 // boundaries.
276 std::string elided_name = base::UTF16ToUTF8(
277 gfx::TruncateString(node->GetTitle(), kMaxCharsOnAMenuLabel));
278
279 if (elided_name.empty()) {
280 elided_name = base::UTF16ToUTF8(gfx::TruncateString(
281 base::UTF8ToUTF16(node->url().possibly_invalid_spec()),
282 kMaxCharsOnAMenuLabel));
283 }
284
285 return elided_name;
286 }
287
288 const BookmarkNode* BookmarkNodeForWidget(GtkWidget* widget) {
289 return reinterpret_cast<const BookmarkNode*>(
290 g_object_get_data(G_OBJECT(widget), kBookmarkNode));
291 }
292
293 void SetButtonTextColors(GtkWidget* label, GtkThemeService* provider) {
294 if (provider->UsingNativeTheme()) {
295 gtk_util::SetLabelColor(label, NULL);
296 } else {
297 GdkColor color = provider->GetGdkColor(
298 ThemeProperties::COLOR_BOOKMARK_TEXT);
299 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &color);
300 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &color);
301
302 // Because the prelight state is a white image that doesn't change by the
303 // theme, force the text color to black when it would be used.
304 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &ui::kGdkBlack);
305 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &ui::kGdkBlack);
306 }
307 }
308
309 // DnD-related -----------------------------------------------------------------
310
311 int GetCodeMask(bool folder) {
312 int rv = ui::CHROME_BOOKMARK_ITEM;
313 if (!folder) {
314 rv |= ui::TEXT_URI_LIST |
315 ui::TEXT_HTML |
316 ui::TEXT_PLAIN |
317 ui::NETSCAPE_URL;
318 }
319 return rv;
320 }
321
322 void WriteBookmarkToSelection(const BookmarkNode* node,
323 GtkSelectionData* selection_data,
324 guint target_type,
325 Profile* profile) {
326 DCHECK(node);
327 std::vector<const BookmarkNode*> nodes;
328 nodes.push_back(node);
329 WriteBookmarksToSelection(nodes, selection_data, target_type, profile);
330 }
331
332 void WriteBookmarksToSelection(const std::vector<const BookmarkNode*>& nodes,
333 GtkSelectionData* selection_data,
334 guint target_type,
335 Profile* profile) {
336 switch (target_type) {
337 case ui::CHROME_BOOKMARK_ITEM: {
338 BookmarkNodeData data(nodes);
339 Pickle pickle;
340 data.WriteToPickle(profile, &pickle);
341
342 gtk_selection_data_set(selection_data,
343 gtk_selection_data_get_target(selection_data),
344 kBitsInAByte,
345 static_cast<const guchar*>(pickle.data()),
346 pickle.size());
347 break;
348 }
349 case ui::NETSCAPE_URL: {
350 // _NETSCAPE_URL format is URL + \n + title.
351 std::string utf8_text = nodes[0]->url().spec() + "\n" +
352 base::UTF16ToUTF8(nodes[0]->GetTitle());
353 gtk_selection_data_set(selection_data,
354 gtk_selection_data_get_target(selection_data),
355 kBitsInAByte,
356 reinterpret_cast<const guchar*>(utf8_text.c_str()),
357 utf8_text.length());
358 break;
359 }
360 case ui::TEXT_URI_LIST: {
361 gchar** uris = reinterpret_cast<gchar**>(malloc(sizeof(gchar*) *
362 (nodes.size() + 1)));
363 for (size_t i = 0; i < nodes.size(); ++i) {
364 // If the node is a folder, this will be empty. TODO(estade): figure out
365 // if there are any ramifications to passing an empty URI. After a
366 // little testing, it seems fine.
367 const GURL& url = nodes[i]->url();
368 // This const cast should be safe as gtk_selection_data_set_uris()
369 // makes copies.
370 uris[i] = const_cast<gchar*>(url.spec().c_str());
371 }
372 uris[nodes.size()] = NULL;
373
374 gtk_selection_data_set_uris(selection_data, uris);
375 free(uris);
376 break;
377 }
378 case ui::TEXT_HTML: {
379 std::string utf8_title = base::UTF16ToUTF8(nodes[0]->GetTitle());
380 std::string utf8_html = base::StringPrintf("<a href=\"%s\">%s</a>",
381 nodes[0]->url().spec().c_str(),
382 utf8_title.c_str());
383 gtk_selection_data_set(selection_data,
384 GetAtomForTarget(ui::TEXT_HTML),
385 kBitsInAByte,
386 reinterpret_cast<const guchar*>(utf8_html.data()),
387 utf8_html.size());
388 break;
389 }
390 case ui::TEXT_PLAIN: {
391 gtk_selection_data_set_text(selection_data,
392 nodes[0]->url().spec().c_str(), -1);
393 break;
394 }
395 default: {
396 DLOG(ERROR) << "Unsupported drag get type!";
397 }
398 }
399 }
400
401 std::vector<const BookmarkNode*> GetNodesFromSelection(
402 GdkDragContext* context,
403 GtkSelectionData* selection_data,
404 guint target_type,
405 Profile* profile,
406 gboolean* delete_selection_data,
407 gboolean* dnd_success) {
408 if (delete_selection_data)
409 *delete_selection_data = FALSE;
410 if (dnd_success)
411 *dnd_success = FALSE;
412
413 if (selection_data) {
414 gint length = gtk_selection_data_get_length(selection_data);
415 if (length > 0) {
416 if (context && delete_selection_data &&
417 context->action == GDK_ACTION_MOVE)
418 *delete_selection_data = TRUE;
419
420 switch (target_type) {
421 case ui::CHROME_BOOKMARK_ITEM: {
422 if (dnd_success)
423 *dnd_success = TRUE;
424 Pickle pickle(reinterpret_cast<const char*>(
425 gtk_selection_data_get_data(selection_data)), length);
426 BookmarkNodeData drag_data;
427 drag_data.ReadFromPickle(&pickle);
428 return drag_data.GetNodes(profile);
429 }
430 default: {
431 DLOG(ERROR) << "Unsupported drag received type: " << target_type;
432 }
433 }
434 }
435 }
436
437 return std::vector<const BookmarkNode*>();
438 }
439
440 bool CreateNewBookmarkFromNamedUrl(GtkSelectionData* selection_data,
441 BookmarkModel* model,
442 const BookmarkNode* parent,
443 int idx) {
444 GURL url;
445 base::string16 title;
446 if (!ui::ExtractNamedURL(selection_data, &url, &title))
447 return false;
448
449 model->AddURL(parent, idx, title, url);
450 return true;
451 }
452
453 bool CreateNewBookmarksFromURIList(GtkSelectionData* selection_data,
454 BookmarkModel* model,
455 const BookmarkNode* parent,
456 int idx) {
457 std::vector<GURL> urls;
458 ui::ExtractURIList(selection_data, &urls);
459 for (size_t i = 0; i < urls.size(); ++i) {
460 base::string16 title = GetNameForURL(urls[i]);
461 model->AddURL(parent, idx++, title, urls[i]);
462 }
463 return true;
464 }
465
466 bool CreateNewBookmarkFromNetscapeURL(GtkSelectionData* selection_data,
467 BookmarkModel* model,
468 const BookmarkNode* parent,
469 int idx) {
470 GURL url;
471 base::string16 title;
472 if (!ui::ExtractNetscapeURL(selection_data, &url, &title))
473 return false;
474
475 model->AddURL(parent, idx, title, url);
476 return true;
477 }
478
479 base::string16 GetNameForURL(const GURL& url) {
480 if (url.is_valid()) {
481 return net::GetSuggestedFilename(url,
482 std::string(),
483 std::string(),
484 std::string(),
485 std::string(),
486 std::string());
487 } else {
488 return l10n_util::GetStringUTF16(IDS_APP_UNTITLED_SHORTCUT_FILE_NAME);
489 }
490 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698