/* Copyright (C) 2007-2008 Christian Dywan This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. See the file COPYING for the full license text. */ #include "midori-webview.h" #include "main.h" #include "gjs.h" #include "sokoke.h" #include "compat.h" #if GLIB_CHECK_VERSION (2, 16, 0) #include #endif #include #include /* This is unstable API, so we need to declare it */ gchar* webkit_web_view_get_selected_text (WebKitWebView* web_view); struct _MidoriWebView { WebKitWebView parent_instance; gboolean window_object_cleared; GdkPixbuf* icon; gchar* uri; gchar* title; gdouble progress; MidoriLoadStatus load_status; gchar* statusbar_text; gchar* link_uri; MidoriWebList* news_feeds; MidoriWebSettings* settings; GtkWidget* menu_item; GtkWidget* tab_icon; GtkWidget* tab_title; KatzeXbelItem* xbel_item; }; G_DEFINE_TYPE (MidoriWebView, midori_web_view, WEBKIT_TYPE_WEB_VIEW) GType midori_load_status_get_type (void) { static GType type = 0; if (!type) { static const GEnumValue values[] = { { MIDORI_LOAD_PROVISIONAL, "MIDORI_LOAD_PROVISIONAL", N_("Load Provisional") }, { MIDORI_LOAD_COMMITTED, "MIDORI_LOAD_COMMITTED", N_("Load Committed") }, { MIDORI_LOAD_FINISHED, "MIDORI_LOAD_FINISHED", N_("Load Finished") }, { 0, NULL, NULL } }; type = g_enum_register_static ("MidoriLoadStatus", values); } return type; } enum { PROP_0, PROP_URI, PROP_TITLE, PROP_PROGRESS, PROP_MLOAD_STATUS, PROP_STATUSBAR_TEXT, PROP_SETTINGS }; enum { ICON_READY, NEWS_FEED_READY, ELEMENT_MOTION, NEW_TAB, NEW_WINDOW, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; static void midori_web_view_finalize (GObject* object); static void midori_web_view_set_property (GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec); static void midori_web_view_get_property (GObject* object, guint prop_id, GValue* value, GParamSpec* pspec); static void midori_cclosure_marshal_VOID__STRING_STRING_STRING (GClosure* closure, GValue* return_value, guint n_param_values, const GValue* param_values, gpointer invocation_hint, gpointer marshal_data) { typedef void(*GMarshalFunc_VOID__STRING_STRING_STRING) (gpointer data1, const gchar* arg_1, const gchar* arg_2, const gchar* arg_3, gpointer data2); register GMarshalFunc_VOID__STRING_STRING_STRING callback; register GCClosure* cc = (GCClosure*) closure; register gpointer data1, data2; g_return_if_fail (n_param_values == 4); if (G_CCLOSURE_SWAP_DATA (closure)) { data1 = closure->data; data2 = g_value_peek_pointer (param_values + 0); } else { data1 = g_value_peek_pointer (param_values + 0); data2 = closure->data; } callback = (GMarshalFunc_VOID__STRING_STRING_STRING) (marshal_data ? marshal_data : cc->callback); callback (data1, g_value_get_string (param_values + 1), g_value_get_string (param_values + 2), g_value_get_string (param_values + 3), data2); } static void midori_web_view_class_init (MidoriWebViewClass* class) { signals[ICON_READY] = g_signal_new ( "icon-ready", G_TYPE_FROM_CLASS (class), (GSignalFlags)(G_SIGNAL_RUN_LAST), G_STRUCT_OFFSET (MidoriWebViewClass, icon_ready), 0, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GDK_TYPE_PIXBUF); signals[NEWS_FEED_READY] = g_signal_new ( "news-feed-ready", G_TYPE_FROM_CLASS (class), (GSignalFlags)(G_SIGNAL_RUN_LAST), G_STRUCT_OFFSET (MidoriWebViewClass, news_feed_ready), 0, NULL, midori_cclosure_marshal_VOID__STRING_STRING_STRING, G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); signals[ELEMENT_MOTION] = g_signal_new ( "element-motion", G_TYPE_FROM_CLASS (class), (GSignalFlags)(G_SIGNAL_RUN_LAST), G_STRUCT_OFFSET (MidoriWebViewClass, element_motion), 0, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); signals[NEW_TAB] = g_signal_new ( "new-tab", G_TYPE_FROM_CLASS (class), (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION), G_STRUCT_OFFSET (MidoriWebViewClass, new_tab), 0, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); signals[NEW_WINDOW] = g_signal_new ( "new-window", G_TYPE_FROM_CLASS (class), (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION), G_STRUCT_OFFSET (MidoriWebViewClass, new_window), 0, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); GObjectClass* gobject_class = G_OBJECT_CLASS (class); gobject_class->finalize = midori_web_view_finalize; gobject_class->set_property = midori_web_view_set_property; gobject_class->get_property = midori_web_view_get_property; GParamFlags flags = G_PARAM_READWRITE | G_PARAM_CONSTRUCT; if (!g_object_class_find_property (gobject_class, "uri")) g_object_class_install_property (gobject_class, PROP_URI, g_param_spec_string ( "uri", "Uri", _("The URI of the currently loaded page"), "", flags)); if (!g_object_class_find_property (gobject_class, "title")) g_object_class_install_property (gobject_class, PROP_TITLE, g_param_spec_string ( "title", "Title", _("The title of the currently loaded page"), NULL, flags)); if (!g_object_class_find_property (gobject_class, "progress")) g_object_class_install_property (gobject_class, PROP_PROGRESS, g_param_spec_double ( "progress", "Progress", _("The current loading progress"), 0.0, 1.0, 0.0, G_PARAM_READABLE)); g_object_class_install_property (gobject_class, PROP_MLOAD_STATUS, g_param_spec_enum ( "mload-status", "Load Status", _("The current loading status"), MIDORI_TYPE_LOAD_STATUS, MIDORI_LOAD_FINISHED, G_PARAM_READABLE)); if (!g_object_class_find_property (gobject_class, "statusbar-text")) g_object_class_install_property (gobject_class, PROP_STATUSBAR_TEXT, g_param_spec_string ( "statusbar-text", "Statusbar Text", _("The text that is displayed in the statusbar"), "", G_PARAM_READABLE)); g_object_class_override_property (gobject_class, PROP_SETTINGS, "settings"); } static void webkit_web_view_load_started (MidoriWebView* web_view, WebKitWebFrame* web_frame) { web_view->window_object_cleared = FALSE; web_view->load_status = MIDORI_LOAD_PROVISIONAL; g_object_notify (G_OBJECT (web_view), "mload-status"); if (web_view->tab_icon) katze_throbber_set_animated (KATZE_THROBBER (web_view->tab_icon), TRUE); web_view->progress = 0.0; g_object_notify (G_OBJECT (web_view), "progress"); } static void webkit_web_view_window_object_cleared_cb (MidoriWebView* web_view, WebKitWebFrame* web_frame, JSGlobalContextRef js_context, JSObjectRef js_window) { web_view->window_object_cleared = TRUE; } #if GLIB_CHECK_VERSION (2, 16, 0) void loadable_icon_finish_cb (GdkPixbuf* icon, GAsyncResult* res, MidoriWebView* web_view) { GInputStream* stream; GdkPixbuf* pixbuf; GdkPixbuf* pixbuf_scaled; gint icon_width, icon_height; pixbuf = NULL; stream = g_loadable_icon_load_finish (G_LOADABLE_ICON (icon), res, NULL, NULL); if (stream) { pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, NULL); g_object_unref (stream); } if (!pixbuf) pixbuf = gtk_widget_render_icon (GTK_WIDGET (web_view), GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL); gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, &icon_height); pixbuf_scaled = gdk_pixbuf_scale_simple (pixbuf, icon_width, icon_height, GDK_INTERP_BILINEAR); g_object_unref (pixbuf); web_view->icon = pixbuf_scaled; g_signal_emit (web_view, signals[ICON_READY], 0, web_view->icon); } void file_info_finish_cb (GFile* icon_file, GAsyncResult* res, MidoriWebView* web_view) { GFileInfo* info; const gchar* content_type; GIcon* icon; GFile* parent; GFile* file; GdkPixbuf* pixbuf; gint icon_width, icon_height; GdkPixbuf* pixbuf_scaled; info = g_file_query_info_finish (G_FILE (icon_file), res, NULL); if (info) { content_type = g_file_info_get_content_type (info); if (g_str_has_prefix (content_type, "image/")) { icon = g_file_icon_new (icon_file); g_loadable_icon_load_async (G_LOADABLE_ICON (icon), 0, NULL, (GAsyncReadyCallback)loadable_icon_finish_cb, web_view); return; } } file = g_file_get_parent (icon_file); parent = g_file_get_parent (file); /* We need to check if file equals the parent due to a GIO bug */ if (parent && !g_file_equal (file, parent)) { icon_file = g_file_get_child (parent, "favicon.ico"); g_file_query_info_async (icon_file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, G_FILE_QUERY_INFO_NONE, 0, NULL, (GAsyncReadyCallback)file_info_finish_cb, web_view); return; } pixbuf = gtk_widget_render_icon (GTK_WIDGET (web_view), GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL); gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, &icon_height); pixbuf_scaled = gdk_pixbuf_scale_simple (pixbuf, icon_width, icon_height, GDK_INTERP_BILINEAR); g_object_unref (pixbuf); web_view->icon = pixbuf_scaled; g_signal_emit (web_view, signals[ICON_READY], 0, web_view->icon); } #endif static void _midori_web_view_load_icon (MidoriWebView* web_view) { #if GLIB_CHECK_VERSION (2, 16, 0) GFile* file; GFile* icon_file; #endif GdkPixbuf* pixbuf; gint icon_width, icon_height; GdkPixbuf* pixbuf_scaled; #if GLIB_CHECK_VERSION (2, 16, 0) if (web_view->uri) { file = g_file_new_for_uri (web_view->uri); icon_file = g_file_get_child (file, "favicon.ico"); g_file_query_info_async (icon_file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, G_FILE_QUERY_INFO_NONE, 0, NULL, (GAsyncReadyCallback)file_info_finish_cb, web_view); return; } #endif pixbuf = gtk_widget_render_icon (GTK_WIDGET (web_view), GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL); gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, &icon_height); pixbuf_scaled = gdk_pixbuf_scale_simple (pixbuf, icon_width, icon_height, GDK_INTERP_BILINEAR); g_object_unref (pixbuf); web_view->icon = pixbuf_scaled; g_signal_emit (web_view, signals[ICON_READY], 0, web_view->icon); } static void webkit_web_view_load_committed (MidoriWebView* web_view, WebKitWebFrame* web_frame) { const gchar* uri; GdkPixbuf* icon; uri = webkit_web_frame_get_uri (web_frame); katze_assign (web_view->uri, g_strdup (uri)); if (web_view->xbel_item) { uri = midori_web_view_get_display_uri (web_view); katze_xbel_bookmark_set_href (web_view->xbel_item, uri); } g_object_notify (G_OBJECT (web_view), "uri"); g_object_set (web_view, "title", NULL, NULL); icon = gtk_widget_render_icon (GTK_WIDGET (web_view), GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL); katze_object_assign (web_view->icon, icon); _midori_web_view_load_icon (web_view); if (web_view->tab_icon) katze_throbber_set_static_pixbuf (KATZE_THROBBER (web_view->tab_icon), icon); if (web_view->menu_item) gtk_image_menu_item_set_image ( GTK_IMAGE_MENU_ITEM (web_view->menu_item), gtk_image_new_from_pixbuf (icon)); web_view->load_status = MIDORI_LOAD_COMMITTED; g_object_notify (G_OBJECT (web_view), "mload-status"); } static void webkit_web_view_icon_ready (MidoriWebView* web_view, GdkPixbuf* icon) { if (web_view->tab_icon) katze_throbber_set_static_pixbuf (KATZE_THROBBER (web_view->tab_icon), icon); if (web_view->menu_item) gtk_image_menu_item_set_image ( GTK_IMAGE_MENU_ITEM (web_view->menu_item), gtk_image_new_from_pixbuf (icon)); } static void webkit_web_view_progress_changed (MidoriWebView* web_view, gint progress) { web_view->progress = progress ? progress / 100.0 : 0.0; g_object_notify (G_OBJECT (web_view), "progress"); } static void gjs_value_links_foreach_cb (GjsValue* link, MidoriWebView* web_view) { const gchar* type; const gchar* rel; #if GLIB_CHECK_VERSION (2, 16, 0) GFile* icon_file; GIcon* icon; #endif if (gjs_value_is_object (link) && gjs_value_has_attribute (link, "href")) { if (gjs_value_has_attribute (link, "type")) { type = gjs_value_get_attribute_string (link, "type"); if (!strcmp (type, "application/rss+xml") || !strcmp (type, "application/x.atom+xml") || !strcmp (type, "application/atom+xml")) { midori_web_list_add_item (web_view->news_feeds, link); g_signal_emit (web_view, signals[NEWS_FEED_READY], 0, gjs_value_get_attribute_string (link, "href"), type, gjs_value_has_attribute (link, "title") ? gjs_value_get_attribute_string (link, "title") : NULL); } } #if GLIB_CHECK_VERSION (2, 16, 0) if (gjs_value_has_attribute (link, "rel")) { rel = gjs_value_get_attribute_string (link, "rel"); if (!strcmp (rel, "icon") || !strcmp (rel, "shortcut icon")) { icon_file = g_file_new_for_uri ( gjs_value_get_attribute_string (link, "href")); icon = g_file_icon_new (icon_file); g_loadable_icon_load_async (G_LOADABLE_ICON (icon), 0, NULL, (GAsyncReadyCallback)loadable_icon_finish_cb, web_view); } } #endif } } static void webkit_web_frame_load_done (WebKitWebFrame* web_frame, gboolean success, MidoriWebView* web_view) { JSContextRef js_context; JSValueRef js_window; GjsValue* value; GjsValue* document; GjsValue* links; /* If WebKit didn't emit the signal due to a bug, we will */ if (!web_view->window_object_cleared) { js_context = webkit_web_frame_get_global_context (web_frame); js_window = JSContextGetGlobalObject (js_context); g_signal_emit_by_name (web_view, "window-object-cleared", web_frame, js_context, js_window); } value = gjs_value_new (webkit_web_frame_get_global_context (web_frame), NULL); document = gjs_value_get_by_name (value, "document"); links = gjs_value_get_elements_by_tag_name (document, "link"); midori_web_list_clear (web_view->news_feeds); gjs_value_foreach (links, (GjsCallback)gjs_value_links_foreach_cb, web_view); g_object_unref (links); g_object_unref (document); g_object_unref (value); if (web_view->tab_icon) katze_throbber_set_animated (KATZE_THROBBER (web_view->tab_icon), FALSE); web_view->load_status = MIDORI_LOAD_FINISHED; g_object_notify (G_OBJECT (web_view), "mload-status"); } static void webkit_web_view_load_finished (MidoriWebView* web_view) { web_view->progress = 1.0; g_object_notify (G_OBJECT (web_view), "progress"); } static void webkit_web_view_title_changed (MidoriWebView* web_view, WebKitWebFrame* web_frame, const gchar* title) { g_object_set (web_view, "title", title, NULL); } static void webkit_web_view_statusbar_text_changed (MidoriWebView* web_view, const gchar* text) { katze_assign (web_view->statusbar_text, g_strdup (text)); g_object_notify (G_OBJECT (web_view), "statusbar-text"); } static void webkit_web_view_hovering_over_link (MidoriWebView* web_view, const gchar* tooltip, const gchar* link_uri) { katze_assign (web_view->link_uri, g_strdup (link_uri)); g_signal_emit (web_view, signals[ELEMENT_MOTION], 0, link_uri); } static gboolean gtk_widget_button_press_event_after (MidoriWebView* web_view, GdkEventButton* event) { GdkModifierType state; GtkClipboard* clipboard; gchar* uri; gchar* new_uri; if (event->button == 2 && sokoke_object_get_boolean (web_view->settings, "middle-click-opens-selection")) { state = (GdkModifierType) event->state; clipboard = gtk_clipboard_get_for_display ( gtk_widget_get_display (GTK_WIDGET (web_view)), GDK_SELECTION_PRIMARY); uri = gtk_clipboard_wait_for_text (clipboard); if (uri && strchr (uri, '.') && !strchr (uri, ' ')) { new_uri = sokoke_magic_uri (uri, NULL); if (state & GDK_CONTROL_MASK) g_signal_emit (web_view, signals[NEW_TAB], 0, new_uri); else g_object_set (web_view, "uri", new_uri, NULL); g_free (new_uri); g_free (uri); return TRUE; } } return FALSE; } static gboolean gtk_widget_button_release_event (MidoriWebView* web_view, GdkEventButton* event) { GtkClipboard* clipboard; gchar* text; /* Emulate the primary clipboard, which WebKit doesn't support */ text = webkit_web_view_get_selected_text (WEBKIT_WEB_VIEW (web_view)); clipboard = gtk_clipboard_get_for_display ( gtk_widget_get_display (GTK_WIDGET (web_view)), GDK_SELECTION_PRIMARY); gtk_clipboard_set_text (clipboard, text, -1); g_free (text); return FALSE; } static gboolean gtk_widget_scroll_event (MidoriWebView* web_view, GdkEventScroll* event) { GdkModifierType state = (GdkModifierType)0; gint x, y; gdk_window_get_pointer (NULL, &x, &y, &state); if (state & GDK_CONTROL_MASK) { if (event->direction == GDK_SCROLL_DOWN) webkit_web_view_zoom_out (WEBKIT_WEB_VIEW (web_view)); else if(event->direction == GDK_SCROLL_UP) webkit_web_view_zoom_in (WEBKIT_WEB_VIEW (web_view)); return TRUE; } else return FALSE; } static void midori_web_view_menu_new_tab_activate_cb (GtkWidget* widget, MidoriWebView* web_view) { const gchar* uri = g_object_get_data (G_OBJECT (widget), "uri"); g_signal_emit (web_view, signals[NEW_TAB], 0, uri); } static void midori_web_view_menu_new_window_activate_cb (GtkWidget* widget, MidoriWebView* web_view) { const gchar* uri = g_object_get_data (G_OBJECT (widget), "uri"); g_signal_emit (web_view, signals[NEW_WINDOW], 0, uri); } static void midori_web_view_menu_download_activate_cb (GtkWidget* widget, MidoriWebView* web_view) { gchar* program; const gchar* uri; g_object_get (web_view->settings, "download-manager", &program, NULL); uri = g_object_get_data (G_OBJECT (widget), "uri"); sokoke_spawn_program (program, uri); g_free (program); } static void webkit_web_view_populate_popup_cb (GtkWidget* web_view, GtkWidget* menu) { const gchar* uri; GtkWidget* menuitem; GdkScreen* screen; GtkIconTheme* icon_theme; GtkWidget* icon; gchar* text; GList* items; gchar* program; uri = midori_web_view_get_link_uri (MIDORI_WEB_VIEW (web_view)); if (uri) { menuitem = gtk_image_menu_item_new_with_mnemonic ( _("Open Link in New _Tab")); screen = gtk_widget_get_screen (web_view); icon_theme = gtk_icon_theme_get_for_screen (screen); if (gtk_icon_theme_has_icon (icon_theme, STOCK_TAB_NEW)) { icon = gtk_image_new_from_stock (STOCK_TAB_NEW, GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), icon); } gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menuitem, 1); g_object_set_data (G_OBJECT (menuitem), "uri", (gchar*)uri); g_signal_connect (menuitem, "activate", G_CALLBACK (midori_web_view_menu_new_tab_activate_cb), web_view); gtk_widget_show (menuitem); /* hack to implement New Window */ items = gtk_container_get_children (GTK_CONTAINER (menu)); menuitem = (GtkWidget*)g_list_nth_data (items, 2); g_object_set_data (G_OBJECT (menuitem), "uri", (gchar*)uri); g_signal_connect (menuitem, "activate", G_CALLBACK (midori_web_view_menu_new_window_activate_cb), web_view); menuitem = (GtkWidget*)g_list_nth_data (items, 3); /* hack to disable non-functional Download File */ gtk_widget_set_sensitive (menuitem, FALSE); g_list_free (items); g_object_get (MIDORI_WEB_VIEW (web_view)->settings, "download-manager", &program, NULL); if (program && *program) { menuitem = gtk_image_menu_item_new_with_mnemonic ( _("Download Link with Download _Manager")); icon = gtk_image_new_from_stock (GTK_STOCK_SAVE_AS, GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), icon); gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menuitem, 4); g_object_set_data (G_OBJECT (menuitem), "uri", (gchar*)uri); g_signal_connect (menuitem, "activate", G_CALLBACK (midori_web_view_menu_download_activate_cb), web_view); gtk_widget_show (menuitem); } } if (!uri && midori_web_view_has_selection (MIDORI_WEB_VIEW (web_view))) { text = webkit_web_view_get_selected_text (WEBKIT_WEB_VIEW (web_view)); if (text && strchr (text, '.') && !strchr (text, ' ')) { menuitem = gtk_image_menu_item_new_with_mnemonic ( _("Open URL in New _Tab")); icon = gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), icon); gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menuitem, -1); g_object_set_data (G_OBJECT (menuitem), "uri", text); g_signal_connect (menuitem, "activate", G_CALLBACK (midori_web_view_menu_new_tab_activate_cb), web_view); gtk_widget_show (menuitem); } /* text should be const, but it is allocated, so we must free it */ g_free (text); } } static void midori_web_view_init (MidoriWebView* web_view) { web_view->icon = gtk_widget_render_icon (GTK_WIDGET (web_view), GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL); web_view->progress = 0.0; web_view->load_status = MIDORI_LOAD_FINISHED; web_view->news_feeds = midori_web_list_new (); web_view->settings = midori_web_settings_new (); g_object_set (web_view, "WebKitWebView::settings", web_view->settings, NULL); WebKitWebFrame* web_frame; web_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)); g_object_connect (web_view, "signal::load-started", webkit_web_view_load_started, NULL, "signal::window-object-cleared", webkit_web_view_window_object_cleared_cb, NULL, "signal::load-committed", webkit_web_view_load_committed, NULL, "signal::icon-ready", webkit_web_view_icon_ready, NULL, "signal::load-progress-changed", webkit_web_view_progress_changed, NULL, "signal::load-finished", webkit_web_view_load_finished, NULL, "signal::title-changed", webkit_web_view_title_changed, NULL, "signal::status-bar-text-changed", webkit_web_view_statusbar_text_changed, NULL, "signal::hovering-over-link", webkit_web_view_hovering_over_link, NULL, "signal::button-press-event", gtk_widget_button_press_event_after, NULL, "signal::button-release-event", gtk_widget_button_release_event, NULL, "signal::scroll-event", gtk_widget_scroll_event, NULL, "signal::populate-popup", webkit_web_view_populate_popup_cb, NULL, NULL); g_object_connect (web_frame, "signal::load-done", webkit_web_frame_load_done, web_view, NULL); } static void midori_web_view_finalize (GObject* object) { MidoriWebView* web_view; WebKitWebFrame* web_frame; web_view = MIDORI_WEB_VIEW (object); if (web_view->icon) g_object_unref (web_view->icon); g_free (web_view->uri); g_free (web_view->title); g_free (web_view->statusbar_text); g_free (web_view->link_uri); g_object_unref (web_view->news_feeds); if (web_view->settings) g_object_unref (web_view->settings); if (web_view->xbel_item) katze_xbel_item_unref (web_view->xbel_item); web_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)); g_signal_handlers_disconnect_by_func (web_frame, webkit_web_frame_load_done, web_view); G_OBJECT_CLASS (midori_web_view_parent_class)->finalize (object); } static void midori_web_view_set_property (GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec) { MidoriWebView* web_view = MIDORI_WEB_VIEW (object); switch (prop_id) { case PROP_URI: { const gchar* uri = g_value_get_string (value); if (uri && *uri) webkit_web_view_open (WEBKIT_WEB_VIEW (web_view), uri); break; } case PROP_TITLE: katze_assign (web_view->title, g_value_dup_string (value)); const gchar* title = midori_web_view_get_display_title (web_view); if (web_view->tab_title) { gtk_label_set_text (GTK_LABEL (web_view->tab_title), title); gtk_widget_set_tooltip_text (web_view->tab_title, title); } if (web_view->menu_item) gtk_label_set_text (GTK_LABEL (gtk_bin_get_child (GTK_BIN ( web_view->menu_item))), title); if (web_view->xbel_item) katze_xbel_item_set_title (web_view->xbel_item, title); break; case PROP_SETTINGS: katze_object_assign (web_view->settings, g_value_get_object (value)); g_object_ref (web_view->settings); g_object_set (object, "WebKitWebView::settings", web_view->settings, NULL); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void midori_web_view_get_property (GObject* object, guint prop_id, GValue* value, GParamSpec* pspec) { MidoriWebView* web_view = MIDORI_WEB_VIEW (object); switch (prop_id) { case PROP_URI: g_value_set_string (value, web_view->uri); break; case PROP_TITLE: g_value_set_string (value, web_view->title); break; case PROP_PROGRESS: g_value_set_double (value, web_view->progress); break; case PROP_MLOAD_STATUS: g_value_set_enum (value, web_view->load_status); break; case PROP_STATUSBAR_TEXT: g_value_set_string (value, web_view->statusbar_text); break; case PROP_SETTINGS: g_value_set_object (value, web_view->settings); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } /** * midori_web_view_new: * * Creates a new web view widget. * * Return value: a new #MidoriWebView **/ GtkWidget* midori_web_view_new (void) { MidoriWebView* web_view = g_object_new (MIDORI_TYPE_WEB_VIEW, NULL); return GTK_WIDGET (web_view); } /** * midori_web_view_set_settings: * @web_view: a #MidoriWebView * @web_settings: a #MidoriWebSettings * * Assigns a settings instance to the web view. **/ void midori_web_view_set_settings (MidoriWebView* web_view, MidoriWebSettings* web_settings) { g_object_set (web_view, "settings", web_settings, NULL); } /** * midori_web_view_get_proxy_menu_item: * @web_view: a #MidoriWebView * * Retrieves a proxy menu item that is typically added to a Window menu * and which on activation switches to the right window/ tab. * * The item is created on the first call and will be updated to reflect * changes to the icon and title automatically. * * Note: The item is only valid as the web view is embedded in a #GtkNotebook. * * Return value: the proxy #GtkMenuItem or %NULL **/ GtkWidget* midori_web_view_get_proxy_menu_item (MidoriWebView* web_view) { const gchar* title; g_return_val_if_fail (MIDORI_IS_WEB_VIEW (web_view), FALSE); if (!web_view->menu_item) { title = midori_web_view_get_display_title (web_view); web_view->menu_item = gtk_image_menu_item_new_with_label (title); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (web_view->menu_item), gtk_image_new_from_pixbuf (web_view->icon)); g_signal_connect (web_view->menu_item, "destroy", G_CALLBACK (gtk_widget_destroyed), &web_view->menu_item); } return web_view->menu_item; } /** * midori_web_view_get_proxy_tab_icon: * @web_view: a #MidoriWebView * * Retrieves a proxy tab icon that is typically used in a tab label. * * The icon is created on the first call and will be updated to reflect * loading progress and changes of the actual icon. * * Note: If a proxy tab label has been created before, this represents * the existing icon used in the label. * * Return value: the proxy #GtkImage **/ GtkWidget* midori_web_view_get_proxy_tab_icon (MidoriWebView* web_view) { g_return_val_if_fail (MIDORI_IS_WEB_VIEW (web_view), NULL); if (!web_view->tab_icon) { web_view->tab_icon = katze_throbber_new (); katze_throbber_set_static_pixbuf (KATZE_THROBBER (web_view->tab_icon), web_view->icon); g_signal_connect (web_view->tab_icon, "destroy", G_CALLBACK (gtk_widget_destroyed), &web_view->tab_icon); } return web_view->tab_icon; } /** * midori_web_view_get_proxy_tab_title: * @web_view: a #MidoriWebView * * Retrieves a proxy tab title that is typically used as the label * of a #GtkNotebook page. * * The title is created on the first call and will be updated to * reflect changes automatically. * * Return value: the proxy #GtkLabel **/ GtkWidget* midori_web_view_get_proxy_tab_title (MidoriWebView* web_view) { const gchar* title; g_return_val_if_fail (MIDORI_IS_WEB_VIEW (web_view), NULL); if (!web_view->tab_title) { title = midori_web_view_get_display_title (web_view); web_view->tab_title = gtk_label_new (title); g_signal_connect (web_view->tab_title, "destroy", G_CALLBACK (gtk_widget_destroyed), &web_view->tab_title); } return web_view->tab_title; } /** * midori_web_view_get_proxy_xbel_item: * @web_view: a #MidoriWebView * * Retrieves a proxy xbel item that can be used for bookmark storage as * well as session management. * * The item is created on the first call and will be updated to reflect * changes to the title and href automatically. * * Note: Currently the item is always a bookmark, but this might change * in the future. * * Return value: the proxy #KatzeXbelItem **/ KatzeXbelItem* midori_web_view_get_proxy_xbel_item (MidoriWebView* web_view) { const gchar* uri; const gchar* title; g_return_val_if_fail (MIDORI_IS_WEB_VIEW (web_view), NULL); if (!web_view->xbel_item) { web_view->xbel_item = katze_xbel_bookmark_new (); uri = midori_web_view_get_display_uri (web_view); katze_xbel_bookmark_set_href (web_view->xbel_item, uri); title = midori_web_view_get_display_title (web_view); katze_xbel_item_set_title (web_view->xbel_item, title); } return web_view->xbel_item; } /** * midori_web_view_load_status: * @web_view: a #MidoriWebView * * Determines the current loading status of a page. * * Return value: the current #MidoriLoadStatus **/ MidoriLoadStatus midori_web_view_get_load_status (MidoriWebView* web_view) { g_return_val_if_fail (MIDORI_IS_WEB_VIEW (web_view), MIDORI_LOAD_FINISHED); return web_view->load_status; } /** * midori_web_view_get_progress: * @web_view: a #MidoriWebView * * Retrieves the current loading progress as * a fraction between 0.0 and 1.0. * * Return value: the current loading progress **/ gdouble midori_web_view_get_progress (MidoriWebView* web_view) { g_return_val_if_fail (MIDORI_IS_WEB_VIEW (web_view), 0.0); return web_view->progress; } /** * midori_web_view_get_display_uri: * @web_view: a #MidoriWebView * * Retrieves a string that is suitable for displaying, particularly an * empty URI is represented as "". * * You can assume that the string is not %NULL. * * Return value: an URI string **/ const gchar* midori_web_view_get_display_uri (MidoriWebView* web_view) { g_return_val_if_fail (MIDORI_IS_WEB_VIEW (web_view), ""); return web_view->uri ? web_view->uri : ""; } /** * midori_web_view_get_display_title: * @web_view: a #MidoriWebView * * Retrieves a string that is suitable for displaying as a title. Most of the * time this will be the title or the current URI. * * You can assume that the string is not %NULL. * * Return value: a title string **/ const gchar* midori_web_view_get_display_title (MidoriWebView* web_view) { g_return_val_if_fail (MIDORI_IS_WEB_VIEW (web_view), "about:blank"); if (web_view->title) return web_view->title; if (web_view->uri) return web_view->uri; return "about:blank"; } /** * midori_web_view_get_link_uri: * @web_view: a #MidoriWebView * * Retrieves the uri of the currently focused link, particularly while the * mouse hovers a link or a context menu is being opened. * * Return value: an URI string, or %NULL if there is no link focussed **/ const gchar* midori_web_view_get_link_uri (MidoriWebView* web_view) { g_return_val_if_fail (MIDORI_IS_WEB_VIEW (web_view), NULL); return web_view->link_uri; } /** * midori_web_view_get_news_feeds: * @web_view: a #MidoriWebView * * Retrieves a list of news feeds for the current page * or %NULL if there are no feeds at all. * * Return value: a #MidoriWebList, or %NULL **/ MidoriWebList* midori_web_view_get_news_feeds (MidoriWebView* web_view) { g_return_val_if_fail (MIDORI_IS_WEB_VIEW (web_view), NULL); if (!midori_web_list_is_empty (web_view->news_feeds)) return web_view->news_feeds; return NULL; } /** * midori_web_view_has_selection: * @web_view: a #MidoriWebView * * Determines whether something on the page is selected. * * By contrast to webkit_web_view_has_selection() this * returns %FALSE if there is a selection that * effectively only consists of whitespace. * * Return value: %TRUE if effectively there is a selection **/ gboolean midori_web_view_has_selection (MidoriWebView* web_view) { gchar* text; g_return_val_if_fail (MIDORI_IS_WEB_VIEW (web_view), FALSE); text = webkit_web_view_get_selected_text (WEBKIT_WEB_VIEW (web_view)); if (text && *text) { g_free (text); return TRUE; } g_free (text); return FALSE; }