pidgin/gtkwebview.c

changeset 35784
247f6f06531f
parent 35733
60a5d065ca81
child 35785
65ced7f8beaa
equal deleted inserted replaced
35783:dcff8ce68d1b 35784:247f6f06531f
106 } edit; 106 } edit;
107 107
108 /* WebKit inspector */ 108 /* WebKit inspector */
109 WebKitWebView *inspector_view; 109 WebKitWebView *inspector_view;
110 GtkWindow *inspector_win; 110 GtkWindow *inspector_win;
111
112 /* Resources cache */
113 GHashTable *loaded_images;
111 } PidginWebViewPriv; 114 } PidginWebViewPriv;
112 115
113 /****************************************************************************** 116 /******************************************************************************
114 * Globals 117 * Globals
115 *****************************************************************************/ 118 *****************************************************************************/
192 webkit_network_request_set_uri(request, tmp); 195 webkit_network_request_set_uri(request, tmp);
193 g_free(b64); 196 g_free(b64);
194 g_free(tmp); 197 g_free(tmp);
195 } 198 }
196 } 199 }
200 }
201
202 static void
203 webview_resource_loaded(WebKitWebView *web_view, WebKitWebFrame *web_frame,
204 WebKitWebResource *web_resource, gpointer user_data)
205 {
206 PidginWebViewPriv *priv = PIDGIN_WEBVIEW_GET_PRIVATE(web_view);
207 const gchar *uri;
208 GString *data;
209 PurpleStoredImage *image;
210
211 if (!purple_str_has_caseprefix(
212 webkit_web_resource_get_mime_type(web_resource), "image/"))
213 {
214 return;
215 }
216
217 uri = webkit_web_resource_get_uri(web_resource);
218 if (g_hash_table_lookup(priv->loaded_images, uri))
219 return;
220
221 data = webkit_web_resource_get_data(web_resource);
222 if (data->len == 0)
223 return;
224
225 /* TODO: we could avoid copying data, if uri is a
226 * PURPLE_STORED_IMAGE_PROTOCOL or PURPLE_STOCK_IMAGE_PROTOCOL */
227 image = purple_imgstore_new(g_memdup(data->str, data->len),
228 data->len, NULL);
229 g_return_if_fail(image != NULL);
230
231 g_hash_table_insert(priv->loaded_images, g_strdup(uri), image);
232 }
233
234 static PurpleStoredImage *
235 webview_resource_get_loaded(WebKitWebView *web_view, const gchar *uri)
236 {
237 PidginWebViewPriv *priv = PIDGIN_WEBVIEW_GET_PRIVATE(web_view);
238
239 g_return_val_if_fail(priv != NULL, NULL);
240
241 return g_hash_table_lookup(priv->loaded_images, uri);
197 } 242 }
198 243
199 static void 244 static void
200 process_load_queue_element(PidginWebView *webview) 245 process_load_queue_element(PidginWebView *webview)
201 { 246 {
514 559
515 return menuitem; 560 return menuitem;
516 } 561 }
517 562
518 static void 563 static void
564 webview_image_saved(GtkWidget *dialog, gint response, gpointer _unused)
565 {
566 PurpleStoredImage *image;
567 gchar *filename;
568 GError *error = NULL;
569
570 if (response != GTK_RESPONSE_ACCEPT) {
571 gtk_widget_destroy(dialog);
572 return;
573 }
574
575 image = g_object_get_data(G_OBJECT(dialog), "pidgin-gtkwebview-image");
576 g_return_if_fail(image != NULL);
577
578 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
579 g_return_if_fail(filename != NULL);
580 g_return_if_fail(filename[0] != '\0');
581
582 g_file_set_contents(filename, purple_imgstore_get_data(image),
583 purple_imgstore_get_size(image), &error);
584 if (error) {
585 purple_debug_error("gtkwebview", "Failed saving image: %s",
586 error->message);
587 /* TODO: we should display a notification here */
588 }
589
590 g_free(filename);
591 gtk_widget_destroy(dialog);
592 }
593
594 static void
595 webview_image_save(GtkWidget *item, WebKitDOMHTMLImageElement *image_node)
596 {
597 const gchar *src = webkit_dom_html_image_element_get_src(image_node);
598 WebKitWebView *webview;
599 PurpleStoredImage *image;
600 GtkFileChooserDialog *dialog;
601 gchar *filename;
602 GtkWidget *parent;
603
604 webview = g_object_get_data(G_OBJECT(image_node), "pidgin-gtkwebview");
605 g_return_if_fail(webview != NULL);
606
607 image = webview_resource_get_loaded(webview, src);
608 g_return_if_fail(image != NULL);
609
610 parent = gtk_widget_get_ancestor(item, GTK_TYPE_WINDOW);
611 dialog = GTK_FILE_CHOOSER_DIALOG(gtk_file_chooser_dialog_new(
612 _("Save Image"),
613 parent ? GTK_WINDOW(parent) : NULL,
614 GTK_FILE_CHOOSER_ACTION_SAVE,
615 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
616 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
617 NULL));
618 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
619 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
620
621 /* TODO: use image's file name, if there is one */
622 filename = g_strdup_printf(_("image.%s"),
623 purple_imgstore_get_extension(image));
624 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), filename);
625 g_free(filename);
626
627 g_signal_connect(G_OBJECT(dialog), "response",
628 G_CALLBACK(webview_image_saved), NULL);
629
630 purple_imgstore_ref(image);
631 g_object_set_data_full(G_OBJECT(dialog), "pidgin-gtkwebview-image",
632 image, (GDestroyNotify)purple_imgstore_unref);
633
634 gtk_widget_show(GTK_WIDGET(dialog));
635 }
636
637 static void
519 do_popup_menu(WebKitWebView *webview, int button, int time, int context, 638 do_popup_menu(WebKitWebView *webview, int button, int time, int context,
520 WebKitDOMNode *node, const char *uri) 639 WebKitDOMNode *node, const char *uri)
521 { 640 {
522 GtkWidget *menu; 641 GtkWidget *menu;
523 GtkWidget *cut, *copy, *paste, *delete, *select; 642 GtkWidget *cut, *copy, *paste, *delete, *select;
643 gboolean show_clipboard = TRUE;
644 WebKitDOMHTMLImageElement *image_node = NULL;
524 645
525 menu = gtk_menu_new(); 646 menu = gtk_menu_new();
526 g_signal_connect(menu, "selection-done", 647 g_signal_connect(menu, "selection-done",
527 G_CALLBACK(gtk_widget_destroy), NULL); 648 G_CALLBACK(gtk_widget_destroy), NULL);
528 649
529 if ((context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) 650 if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) {
530 && !(context & WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION)) {
531 PidginWebViewProtocol *proto = NULL; 651 PidginWebViewProtocol *proto = NULL;
532 GList *children; 652 GList *children;
533 653 WebKitDOMNode *link_node = node;
534 while (node && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT(node)) { 654
535 node = webkit_dom_node_get_parent_node(node); 655 while (link_node && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT(link_node)) {
656 link_node = webkit_dom_node_get_parent_node(node);
536 } 657 }
537 658
538 if (uri && node) 659 if (uri && link_node)
539 proto = webview_find_protocol(uri, FALSE); 660 proto = webview_find_protocol(uri, FALSE);
540 661
541 if (proto && proto->context_menu) { 662 if (proto && proto->context_menu) {
542 proto->context_menu(PIDGIN_WEBVIEW(webview), 663 proto->context_menu(PIDGIN_WEBVIEW(webview),
543 WEBKIT_DOM_HTML_ANCHOR_ELEMENT(node), menu); 664 WEBKIT_DOM_HTML_ANCHOR_ELEMENT(link_node), menu);
544 } 665 }
545 666
546 children = gtk_container_get_children(GTK_CONTAINER(menu)); 667 children = gtk_container_get_children(GTK_CONTAINER(menu));
547 if (!children) { 668 if (!children) {
548 GtkWidget *item = gtk_menu_item_new_with_label(_("No actions available")); 669 GtkWidget *item = gtk_menu_item_new_with_label(_("No actions available"));
552 } else { 673 } else {
553 g_list_free(children); 674 g_list_free(children);
554 } 675 }
555 gtk_widget_show_all(menu); 676 gtk_widget_show_all(menu);
556 677
557 } else { 678 show_clipboard = FALSE;
679 }
680
681 if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) {
682 WebKitDOMNode *_image_node = node;
683
684 while (_image_node && !WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT(_image_node)) {
685 _image_node = webkit_dom_node_get_parent_node(_image_node);
686 }
687 if (_image_node)
688 image_node = WEBKIT_DOM_HTML_IMAGE_ELEMENT(_image_node);
689 /* don't do it on our theme smileys */
690 }
691 if (image_node && webkit_dom_html_image_element_get_complete(image_node)) {
692 GtkWidget *menu_item;
693 int width, height;
694
695 width = webkit_dom_html_image_element_get_width(image_node);
696 height = webkit_dom_html_image_element_get_height(image_node);
697
698 /* XXX */
699 g_object_set_data(G_OBJECT(image_node), "pidgin-gtkwebview", webview);
700
701 menu_item = gtk_image_menu_item_new_with_mnemonic(
702 _("_Save Image..."));
703 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),
704 gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_MENU));
705 g_signal_connect_object(G_OBJECT(menu_item), "activate",
706 G_CALLBACK(webview_image_save), image_node, 0);
707 gtk_widget_show(menu_item);
708 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
709
710 #if 0
711 /* TODO */
712 if (width <= 96 && height <= 96) {
713 menu_item = gtk_image_menu_item_new_with_mnemonic(
714 _("_Add Custom Smiley..."));
715 gtk_image_menu_item_set_image(
716 GTK_IMAGE_MENU_ITEM(menu_item),
717 gtk_image_new_from_stock(GTK_STOCK_ADD,
718 GTK_ICON_SIZE_MENU));
719 g_signal_connect(G_OBJECT(item), "activate",
720 G_CALLBACK(), save);
721 gtk_widget_show(menu_item);
722 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
723 }
724 #endif
725
726 show_clipboard = FALSE;
727 }
728
729 if (show_clipboard) {
558 /* Using connect_swapped means we don't need any wrapper functions */ 730 /* Using connect_swapped means we don't need any wrapper functions */
559 cut = pidgin_new_item_from_stock(menu, _("Cu_t"), GTK_STOCK_CUT, 731 cut = pidgin_new_item_from_stock(menu, _("Cu_t"), GTK_STOCK_CUT,
560 NULL, NULL, 0, 0, NULL); 732 NULL, NULL, 0, 0, NULL);
561 g_signal_connect_swapped(G_OBJECT(cut), "activate", 733 g_signal_connect_swapped(G_OBJECT(cut), "activate",
562 G_CALLBACK(webkit_web_view_cut_clipboard), 734 G_CALLBACK(webkit_web_view_cut_clipboard),
925 while (!g_queue_is_empty(priv->load_queue)) { 1097 while (!g_queue_is_empty(priv->load_queue)) {
926 g_queue_pop_head(priv->load_queue); 1098 g_queue_pop_head(priv->load_queue);
927 g_free(g_queue_pop_head(priv->load_queue)); 1099 g_free(g_queue_pop_head(priv->load_queue));
928 } 1100 }
929 g_queue_free(priv->load_queue); 1101 g_queue_free(priv->load_queue);
1102
1103 g_hash_table_destroy(priv->loaded_images);
930 1104
931 G_OBJECT_CLASS(parent_class)->finalize(G_OBJECT(webview)); 1105 G_OBJECT_CLASS(parent_class)->finalize(G_OBJECT(webview));
932 } 1106 }
933 1107
934 enum { 1108 enum {
1088 G_CALLBACK(webview_load_finished), NULL); 1262 G_CALLBACK(webview_load_finished), NULL);
1089 1263
1090 g_signal_connect(G_OBJECT(webview), "resource-request-starting", 1264 g_signal_connect(G_OBJECT(webview), "resource-request-starting",
1091 G_CALLBACK(webview_resource_loading), NULL); 1265 G_CALLBACK(webview_resource_loading), NULL);
1092 1266
1267 g_signal_connect(G_OBJECT(webview), "resource-load-finished",
1268 G_CALLBACK(webview_resource_loaded), NULL);
1269
1093 inspector = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview)); 1270 inspector = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview));
1094 g_signal_connect(G_OBJECT(inspector), "inspect-web-view", 1271 g_signal_connect(G_OBJECT(inspector), "inspect-web-view",
1095 G_CALLBACK(webview_inspector_create), NULL); 1272 G_CALLBACK(webview_inspector_create), NULL);
1096 g_signal_connect(G_OBJECT(inspector), "show-window", 1273 g_signal_connect(G_OBJECT(inspector), "show-window",
1097 G_CALLBACK(webview_inspector_show), webview); 1274 G_CALLBACK(webview_inspector_show), webview);
1275
1276 priv->loaded_images = g_hash_table_new_full(g_str_hash, g_str_equal,
1277 g_free, (GDestroyNotify)purple_imgstore_unref);
1098 } 1278 }
1099 1279
1100 GType 1280 GType
1101 pidgin_webview_get_type(void) 1281 pidgin_webview_get_type(void)
1102 { 1282 {

mercurial