/* * Tab body * * Copyright (C) 2017 Patrick McDermott * * This file is part of Marquee. * * Marquee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Marquee is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Marquee. If not, see . */ #include #include #include #include "tab-body.h" #include "tab.h" #define WKCMA(ACTION) \ WEBKIT_CONTEXT_MENU_ACTION_##ACTION static void menu_open_link_activate_cb(GtkAction __attribute__((unused)) *action, MqTabBody *body) { webkit_web_view_load_uri(body->web_view, webkit_hit_test_result_get_link_uri(body->hit_test_result)); } static void menu_open_link_tab_activate_cb(GtkAction __attribute__((unused)) *action, MqTabBody *body) { g_print("Open Link in New Tab\n"); } static void menu_open_link_win_activate_cb(GtkAction __attribute__((unused)) *action, MqTabBody *body) { g_print("Open Link in New Window\n"); } static void menu_open_image_activate_cb(GtkAction __attribute__((unused)) *action, MqTabBody *body) { webkit_web_view_load_uri(body->web_view, webkit_hit_test_result_get_image_uri(body->hit_test_result)); } static void menu_open_image_tab_activate_cb(GtkAction __attribute__((unused)) *action, MqTabBody *body) { g_print("Open Image in New Tab\n"); } static void menu_open_image_win_activate_cb(GtkAction __attribute__((unused)) *action, MqTabBody *body) { g_print("Open Image in New Window\n"); } #define ITEM_DECLS \ GtkAction *action; \ WebKitContextMenuItem *menu_item; #define ITEM_DECLS_NO_CUSTOM \ WebKitContextMenuItem *menu_item; #define NEW_CUSTOM_ITEM(NAME, LABEL) \ do { \ /* Don't blame me; blame WebKitGTK+ for using GtkAction. */ \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")\ action = gtk_action_new(#NAME, (LABEL), NULL, NULL); \ _Pragma("GCC diagnostic pop") \ g_signal_connect(action, "activate", \ G_CALLBACK(menu_##NAME##_activate_cb), body); \ menu_item = webkit_context_menu_item_new(action); \ webkit_context_menu_append(context_menu, menu_item); \ } while (0) #define NEW_STOCK_ITEM(STOCK) \ do { \ menu_item = webkit_context_menu_item_new_from_stock_action( \ WEBKIT_CONTEXT_MENU_ACTION_##STOCK); \ webkit_context_menu_append(context_menu, menu_item); \ } while (0) #define NEW_SEPARATOR_ITEM() \ do { \ webkit_context_menu_append(context_menu, \ webkit_context_menu_item_new_separator()); \ } while (0) #define RESTORE_ITEMS(ITEMS) \ do { \ for (; ITEMS; ITEMS = ITEMS->next) { \ webkit_context_menu_append(context_menu, ITEMS->data); \ g_object_unref(ITEMS->data); \ } \ } while (0) static void context_menu_link_cb(WebKitContextMenu *context_menu, MqTabBody *body) { ITEM_DECLS NEW_CUSTOM_ITEM(open_link, "Open Link"); NEW_CUSTOM_ITEM(open_link_tab, "Open Link in New Tab"); NEW_CUSTOM_ITEM(open_link_win, "Open Link in New Window"); NEW_STOCK_ITEM(DOWNLOAD_LINK_TO_DISK); /* Download Linked File */ NEW_STOCK_ITEM(COPY_LINK_TO_CLIPBOARD); /* Copy Link Location */ } static void context_menu_image_cb(WebKitContextMenu *context_menu, MqTabBody *body) { ITEM_DECLS NEW_CUSTOM_ITEM(open_image, "Open Image"); NEW_CUSTOM_ITEM(open_image_tab, "Open Image in New Tab"); NEW_CUSTOM_ITEM(open_image_win, "Open Image in New Window"); NEW_STOCK_ITEM(DOWNLOAD_IMAGE_TO_DISK); /* Save Image As */ NEW_STOCK_ITEM(COPY_IMAGE_TO_CLIPBOARD); /* Copy Image */ NEW_STOCK_ITEM(COPY_IMAGE_URL_TO_CLIPBOARD); /* Copy Image Address */ } static void context_menu_document_cb(WebKitContextMenu *context_menu, GList *nav_items, MqTabBody *body) { ITEM_DECLS_NO_CUSTOM RESTORE_ITEMS(nav_items); /* Back, Forward, Stop, Reload */ NEW_SEPARATOR_ITEM(); /* --- */ NEW_STOCK_ITEM(SELECT_ALL); /* Select All */ NEW_SEPARATOR_ITEM(); /* --- */ /* View Page Source */ } #define PRESERVE_ITEM(ITEMS) \ do { \ g_object_ref(items->data); \ ITEMS = g_list_prepend(ITEMS, items->data); \ } while (0) static gboolean context_menu_cb(WebKitWebView __attribute__((unused)) *web_view, WebKitContextMenu *context_menu, GdkEvent __attribute__((unused)) *event, WebKitHitTestResult *hit_test_result, MqTabBody *body) { GList *items; GList *nav_items; GList *input_items; GList *spell_items; gboolean is_selection; gboolean is_video; WebKitContextMenuAction stock_action; WebKitHitTestResultContext context; gboolean context_handled; WebKitContextMenuItem *menu_item; /* Get more hints about the context, since WebKit doesn't describe * context very well in hit test results. Also, preserve menu items * that aren't easy to reproduce (e.g. the Unicode menu and spelling * guesses). */ items = webkit_context_menu_get_items(context_menu); nav_items = NULL; input_items = NULL; spell_items = NULL; is_selection = FALSE; is_video = FALSE; for (; items; items = items->next) { stock_action = webkit_context_menu_item_get_stock_action( items->data); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch" switch (stock_action) { case WKCMA(GO_BACK): case WKCMA(GO_FORWARD): case WKCMA(STOP): case WKCMA(RELOAD): PRESERVE_ITEM(nav_items); break; case WKCMA(COPY): is_selection = TRUE; break; case WKCMA(INPUT_METHODS): case WKCMA(UNICODE): PRESERVE_ITEM(input_items); break; case WKCMA(SPELLING_GUESS): case WKCMA(NO_GUESSES_FOUND): case WKCMA(IGNORE_SPELLING): case WKCMA(LEARN_SPELLING): case WKCMA(IGNORE_GRAMMAR): PRESERVE_ITEM(spell_items); break; case WKCMA(OPEN_VIDEO_IN_NEW_WINDOW): case WKCMA(COPY_VIDEO_LINK_TO_CLIPBOARD): case WKCMA(DOWNLOAD_VIDEO_TO_DISK): is_video = TRUE; break; case WKCMA(OPEN_AUDIO_IN_NEW_WINDOW): case WKCMA(COPY_AUDIO_LINK_TO_CLIPBOARD): case WKCMA(DOWNLOAD_AUDIO_TO_DISK): is_video = FALSE; break; case WKCMA(MEDIA_PLAY): case WKCMA(MEDIA_PAUSE): break; } #pragma GCC diagnostic pop } nav_items = g_list_reverse(nav_items); input_items = g_list_reverse(input_items); spell_items = g_list_reverse(spell_items); webkit_context_menu_remove_all(context_menu); context = webkit_hit_test_result_get_context(hit_test_result); if (body->hit_test_result) { g_object_unref(body->hit_test_result); } body->hit_test_result = hit_test_result; g_object_ref(body->hit_test_result); context_handled = FALSE; g_print("Context menu, hit test:"); if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) { context_menu_link_cb(context_menu, body); context_handled = TRUE; } if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) { if (context_handled) { NEW_SEPARATOR_ITEM(); } context_menu_image_cb(context_menu, body); context_handled = TRUE; } if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA) { if (is_video) { /* Play / Pause (preserved) * Mute (stock) * Toggle Media Controls (stock?) * Toggle Media Loop Playback (stock?) * Switch Video to Fullscreen (stock) * --- * Copy Video Link Location (stock) * Open Video * Open Video in New Tab * Open Video in New Window * Download Video (stock) */ g_print(" video"); context_handled = TRUE; } else { /* Play / Pause (preserved) * Mute (stock) * Toggle Media Controls (stock?) * Toggle Media Loop Playback (stock?) * --- * Copy Audio Link Location (stock) * Open Audio * Open Audio in New Tab * Open Audio in New Window * Download Audio (stock) */ g_print(" audio"); context_handled = TRUE; } } if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE) { /* Cut (stock) * Copy (stock) * Paste (stock) * Delete (stock) * --- * Select All (stock) * Insert Unicode Character (preserved) */ g_print(" editable"); context_handled = TRUE; } if (!context_handled && context & WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT) { if (is_selection) { g_print(" selection"); context_handled = TRUE; } else { context_menu_document_cb(context_menu, nav_items, body); context_handled = TRUE; } } g_print("\n"); if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT) { if (context_handled) { NEW_SEPARATOR_ITEM(); } NEW_STOCK_ITEM(INSPECT_ELEMENT); /* Inspect Element */ } return FALSE; } MqTabBody * mq_tab_body_new(MqTab *tab, gchar *uri) { MqTabBody *body; body = malloc(sizeof(*body)); body->tab = tab; body->web_view = WEBKIT_WEB_VIEW(webkit_web_view_new()); if (uri) { webkit_web_view_load_uri(body->web_view, uri); } body->container = GTK_WIDGET(body->web_view); gtk_widget_set_vexpand(body->container, TRUE); /* FIXME: This doesn't seem to be working. */ gtk_widget_grab_focus(body->container); body->hit_test_result = NULL; g_signal_connect(body->web_view, "context-menu", G_CALLBACK(context_menu_cb), body); return body; } GtkWidget * mq_tab_body_get_container(MqTabBody *body) { return body->container; } WebKitWebView * mq_tab_body_get_web_view(MqTabBody *body) { return body->web_view; }