/* * Xiphos Bible Study Tool * wk-html.c - webkit-specific html support * * Copyright (C) 2010-2020 Xiphos Developer Team * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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 Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #ifndef USE_WEBKIT2 #include #include #else #include #include #include #include #endif #include "main/url.hh" #include "main/module_dialogs.h" #include "wk-html.h" #include "marshal.h" #include "gui/dictlex.h" #include "main/sword.h" extern gboolean shift_key_pressed; extern gboolean in_url; static gchar *x_uri = NULL; static gboolean db_click; enum { URI_SELECTED, FRAME_SELECTED, TITLE_CHANGED, POPUPMENU_REQUESTED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; static GObjectClass *parent_class = NULL; static void wk_html_class_init(WkHtmlClass *klass); static void wk_html_init(WkHtml *html); G_DEFINE_TYPE_WITH_PRIVATE (WkHtml, wk_html, WEBKIT_TYPE_WEB_VIEW) static gboolean button_release_handler(GtkWidget *widget, GdkEventButton *event) { if (event->type == GDK_BUTTON_RELEASE && db_click) { XI_message((" button 1 = %s", "double click!\n")); #ifdef USE_WEBKIT2 webkit_web_view_execute_editing_command(WEBKIT_WEB_VIEW(widget), WEBKIT_EDITING_COMMAND_COPY); #else if (webkit_web_view_has_selection(WEBKIT_WEB_VIEW(widget))) { webkit_web_view_copy_clipboard(WEBKIT_WEB_VIEW(widget)); } #endif GtkClipboard *clipboard = gtk_widget_get_clipboard(widget, GDK_SELECTION_CLIPBOARD); gtk_clipboard_request_text(clipboard, gui_get_clipboard_text_for_lookup, NULL); } return FALSE; } static gboolean button_press_handler(GtkWidget *widget, GdkEventButton *event) { WkHtmlPrivate *priv; priv = wk_html_get_instance_private(WK_HTML(widget)); if (event->type == GDK_2BUTTON_PRESS && !x_uri) { db_click = TRUE; return FALSE; } db_click = FALSE; /* this is a left click */ if (event->button == 1) { /* go to the correct url otherwise webkit will do its own thing */ if (x_uri) { if (priv->is_dialog) { main_dialogs_url_handler(priv->dialog, x_uri, TRUE); } else { main_url_handler(x_uri, TRUE); } return TRUE; } return FALSE; } /* this is a right click */ if (event->button == 3) { /* display our own popup menu instead of webkit's one */ g_signal_emit(widget, signals[POPUPMENU_REQUESTED], 0, priv->dialog, FALSE); return TRUE; } if (event->button == 2) { /* ignore middle button */ return TRUE; } return FALSE; } /* this is the link handler for when a link is clicked */ static void link_handler(GtkWidget *widget, #ifdef USE_WEBKIT2 WebKitHitTestResult *hit_test_result, guint modifiers, #else gchar *title, gchar *uri, #endif gpointer user_data) { #ifdef USE_WEBKIT2 const char *uri; uri = webkit_hit_test_result_get_link_uri(hit_test_result); #endif WkHtmlPrivate *priv; priv = wk_html_get_instance_private(WK_HTML(widget)); XI_message(("html_link_message: uri = %s", (uri ? uri : "-none-"))); if (x_uri) { g_free(x_uri); x_uri = NULL; } if (uri) { x_uri = g_strdup(uri); in_url = 1; } else in_url = 0; g_signal_emit(widget, signals[URI_SELECTED], 0, uri, FALSE); if (uri) { if (priv->is_dialog) main_dialogs_url_handler(priv->dialog, uri, FALSE); else main_url_handler(uri, FALSE); } } static void html_realize(GtkWidget *widget) { GTK_WIDGET_CLASS(parent_class)->realize(widget); g_signal_connect(G_OBJECT(widget), "button-press-event", G_CALLBACK(button_press_handler), NULL); g_signal_connect(G_OBJECT(widget), "button-release-event", G_CALLBACK(button_release_handler), NULL); g_signal_connect(G_OBJECT(widget), #ifdef USE_WEBKIT2 "mouse-target-changed", #else "hovering-over-link", #endif G_CALLBACK(link_handler), NULL); } static void wk_html_init(WkHtml *html) { WkHtmlPrivate *priv; html->priv = priv = wk_html_get_instance_private(html); priv->base_uri = NULL; priv->anchor = NULL; priv->timeout = 0; priv->content = NULL; priv->mime = NULL; priv->initialised = FALSE; } static void html_dispose(GObject *object) { parent_class->dispose(object); } static void html_finalize(GObject *object) { WkHtml *html = WK_HTML(object); WkHtmlPrivate *priv = html->priv; if (priv->timeout) g_source_remove(priv->timeout); g_free(priv->base_uri); g_free(priv->anchor); parent_class->finalize(object); } static void wk_html_class_init(WkHtmlClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); parent_class = (GObjectClass *)g_type_class_peek_parent(klass); object_class->finalize = html_finalize; object_class->dispose = html_dispose; widget_class->realize = html_realize; signals[URI_SELECTED] = g_signal_new("uri_selected", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(WkHtmlClass, uri_selected), NULL, NULL, wk_marshal_VOID__POINTER_BOOLEAN, G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN); signals[FRAME_SELECTED] = g_signal_new("frame_selected", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(WkHtmlClass, frame_selected), g_signal_accumulator_true_handled, NULL, wk_marshal_BOOLEAN__POINTER_BOOLEAN, G_TYPE_BOOLEAN, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN); signals[POPUPMENU_REQUESTED] = g_signal_new("popupmenu_requested", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(WkHtmlClass, popupmenu_requested), NULL, NULL, wk_marshal_VOID__POINTER_BOOLEAN, G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN); } void wk_html_set_base_uri(WkHtml *html, const gchar *uri) { WkHtmlPrivate *priv; g_return_if_fail(WK_HTML_IS_HTML(html)); priv = html->priv; if (priv->base_uri) { g_free(priv->base_uri); } priv->base_uri = g_strdup("file://"); } void wk_html_open_stream(WkHtml *html, const gchar *mime) { wk_html_set_base_uri(html, NULL); #ifndef USE_WEBKIT2 webkit_web_view_set_transparent(WEBKIT_WEB_VIEW(html), TRUE); #endif html->priv->frames_enabled = FALSE; g_free(html->priv->content); html->priv->content = NULL; g_free(html->priv->mime); html->priv->mime = g_strdup(mime); } void wk_html_write(WkHtml *html, const gchar *data, gint len) { gchar *tmp = NULL; if (len == -1) { len = strlen(data); } if (html->priv->content) { tmp = g_strjoin(NULL, html->priv->content, data, NULL); g_free(html->priv->content); html->priv->content = tmp; } else { html->priv->content = g_strdup(data); } } void wk_html_frames(WkHtml *html, gboolean enable) { html->priv->frames_enabled = enable; } void wk_html_printf(WkHtml *html, char *format, ...) { va_list args; gchar *string; g_return_if_fail(format != NULL); va_start(args, format); string = g_strdup_vprintf(format, args); va_end(args); wk_html_write(html, string, -1); g_free(string); } void wk_html_close(WkHtml *html) { if (!html->priv->initialised) { html->priv->initialised = TRUE; #ifndef USE_WEBKIT2 webkit_web_view_set_maintains_back_forward_list(WEBKIT_WEB_VIEW(html), FALSE); #endif } #ifdef USE_WEBKIT2 GBytes *html_bytes; html_bytes = g_bytes_new(html->priv->content, strlen(html->priv->content)); webkit_web_view_load_bytes(WEBKIT_WEB_VIEW(html), html_bytes, html->priv->mime, NULL, html->priv->base_uri); g_bytes_unref(html_bytes); #else webkit_web_view_load_string(WEBKIT_WEB_VIEW(html), html->priv->content, html->priv->mime, NULL, html->priv->base_uri); #endif g_free(html->priv->content); html->priv->content = NULL; g_free(html->priv->mime); html->priv->mime = NULL; } void wk_html_render_data(WkHtml *html, const char *data, guint32 len) { printf("in render_data, data is %s", data); wk_html_open_stream(html, "application/xhtml+xml"); wk_html_write(html, data, len); wk_html_close(html); } gboolean wk_html_find(WkHtml *html, const gchar *find_string) { if (html->priv->find_string) g_free(html->priv->find_string); html->priv->find_string = g_strdup(find_string); #ifdef USE_WEBKIT2 WebKitFindController *find_controller; find_controller = webkit_web_view_get_find_controller(WEBKIT_WEB_VIEW(html)); webkit_find_controller_search(find_controller, find_string, WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE, G_MAXUINT); return TRUE; #else return webkit_web_view_search_text(WEBKIT_WEB_VIEW(html), find_string, FALSE, TRUE, TRUE); #endif } gboolean wk_html_find_again(WkHtml *html, gboolean forward) { #ifdef USE_WEBKIT2 WebKitFindController *find_controller; find_controller = webkit_web_view_get_find_controller(WEBKIT_WEB_VIEW(html)); webkit_find_controller_search(find_controller, html->priv->find_string, WEBKIT_FIND_OPTIONS_NONE, G_MAXUINT); return TRUE; #else return webkit_web_view_search_text(WEBKIT_WEB_VIEW(html), html->priv->find_string, FALSE, forward, TRUE); #endif } void wk_html_jump_to_anchor(WkHtml *html, gchar *anchor) { WkHtmlPrivate *priv; g_return_if_fail(html != NULL); priv = html->priv; g_free(priv->anchor); priv->anchor = g_strdup(anchor); } void wk_html_copy_selection(WkHtml *html) { #ifdef USE_WEBKIT2 webkit_web_view_execute_editing_command(WEBKIT_WEB_VIEW(html), WEBKIT_EDITING_COMMAND_COPY); #else if (webkit_web_view_has_selection(WEBKIT_WEB_VIEW(html))) webkit_web_view_copy_clipboard(WEBKIT_WEB_VIEW(html)); #endif } void wk_html_select_all(WkHtml *html) { #ifdef USE_WEBKIT2 webkit_web_view_execute_editing_command(WEBKIT_WEB_VIEW(html), WEBKIT_EDITING_COMMAND_SELECT_ALL); #else webkit_web_view_select_all(WEBKIT_WEB_VIEW(html)); #endif } void wk_html_print(WkHtml *html) { #ifdef USE_WEBKIT2 WebKitPrintOperation *print_operation; print_operation = webkit_print_operation_new(WEBKIT_WEB_VIEW(html)); webkit_print_operation_run_dialog(print_operation, NULL); #else webkit_web_frame_print(webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(html))); webkit_web_view_execute_script(WEBKIT_WEB_VIEW(html), "print();"); #endif } gboolean wk_html_initialize(void) { return TRUE; } void wk_html_shutdown(void) { /* Empty */ }