From 7039d48e480e84bcb272457101d30beee86b24e5 Mon Sep 17 00:00:00 2001 From: Christian Dywan Date: Fri, 2 May 2008 22:30:26 +0200 Subject: [PATCH] First attempt at an extension interface. --- src/midori-addons.c | 719 ++++++++++++++++++++++++++------------- src/midori-browser.c | 166 +++++++-- src/midori-browser.h | 9 +- src/midori-websettings.c | 2 +- 4 files changed, 647 insertions(+), 249 deletions(-) diff --git a/src/midori-addons.c b/src/midori-addons.c index f2f38d22..c5cbe6e1 100644 --- a/src/midori-addons.c +++ b/src/midori-addons.c @@ -54,15 +54,34 @@ midori_addons_class_init (MidoriAddonsClass* class) g_type_class_add_private (class, sizeof (MidoriAddonsPrivate)); } +static const +gchar* _folder_for_kind (MidoriAddonKind kind) +{ + switch (kind) + { + case MIDORI_ADDON_EXTENSIONS: + return "extensions"; + case MIDORI_ADDON_USER_SCRIPTS: + return "scripts"; + case MIDORI_ADDON_USER_STYLES: + return "styles"; + default: + return NULL; + } +} + static void midori_addons_button_add_clicked_cb (GtkToolItem* toolitem, MidoriAddons* addons) { + MidoriAddonsPrivate* priv = addons->priv; + GtkWidget* dialog = gtk_message_dialog_new ( GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (addons))), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, - "Put scripts in the folder ~/.local/share/midori/scripts"); + "Put scripts in the folder ~/.local/share/midori/%s", + _folder_for_kind (priv->kind)); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } @@ -119,6 +138,401 @@ midori_addons_treeview_row_activated_cb (GtkTreeView* treeview, }*/ } +static gchar* +_js_string_utf8 (JSStringRef js_string) +{ + size_t size_utf8 = JSStringGetMaximumUTF8CStringSize (js_string); + gchar* string_utf8 = (gchar*)g_malloc (size_utf8); + JSStringGetUTF8CString (js_string, string_utf8, size_utf8); + return string_utf8; +} + +static void +_js_class_get_property_names_cb (JSContextRef js_context, + JSObjectRef js_object, + JSPropertyNameAccumulatorRef js_properties) +{ + GObject* object = JSObjectGetPrivate (js_object); + if (object) + { + guint n_properties; + GParamSpec** pspecs = g_object_class_list_properties ( + G_OBJECT_GET_CLASS (object), &n_properties); + gint i; + for (i = 0; i < n_properties; i++) + { + const gchar* property = g_param_spec_get_name (pspecs[i]); + JSStringRef js_property = JSStringCreateWithUTF8CString (property); + JSPropertyNameAccumulatorAddName (js_properties, js_property); + JSStringRelease (js_property); + } + } +} + +static bool +_js_class_has_property_cb (JSContextRef js_context, + JSObjectRef js_object, + JSStringRef js_property) +{ + bool result = false; + gchar* property = _js_string_utf8 (js_property); + GObject* object = JSObjectGetPrivate (js_object); + if (object) + { + if (g_object_class_find_property (G_OBJECT_GET_CLASS (object), + property)) + result = true; + } + else if (js_object == JSContextGetGlobalObject (js_context)) + { + gchar* property = _js_string_utf8 (js_property); + GType type = g_type_from_name (property); + result = type ? type : false; + } + g_free (property); + return result; +} + +static JSObjectRef +_js_object_new (JSContextRef js_context, + gpointer object); + +static void +_js_object_set_property (JSContextRef js_context, + JSObjectRef js_object, + const gchar* name, + JSValueRef js_value) +{ + JSStringRef js_name = JSStringCreateWithUTF8CString (name); + JSObjectSetProperty(js_context, js_object, js_name, js_value, + kJSPropertyAttributeNone, NULL); + JSStringRelease (js_name); +} + +static JSObjectRef +_js_object_call_as_constructor_cb (JSContextRef js_context, + JSObjectRef js_object, + size_t n_arguments, + const JSValueRef js_arguments[], + JSValueRef* js_exception) +{ + gchar* type_name = JSObjectGetPrivate (js_object); + if (type_name) + { + GType type = g_type_from_name (type_name); + if (type) + { + GObject* object = g_object_new (type, NULL); + JSObjectRef js_object = _js_object_new (js_context, object); + return js_object; + } + } + return JSValueMakeNull (js_context); +} + +static JSValueRef +_js_class_get_property_cb (JSContextRef js_context, + JSObjectRef js_object, + JSStringRef js_property, + JSValueRef* js_exception) +{ + if (js_object == JSContextGetGlobalObject (js_context)) + { + gchar* property = _js_string_utf8 (js_property); + GType type = g_type_from_name (property); + if (type) + { + JSClassDefinition js_class_def = kJSClassDefinitionEmpty; + js_class_def.className = g_strdup (property); + js_class_def.callAsConstructor = _js_object_call_as_constructor_cb; + JSClassRef js_class = JSClassCreate (&js_class_def); + return JSObjectMake (js_context, js_class, property); + } + g_free (property); + return JSValueMakeNull (js_context); + } + GObject* object = JSObjectGetPrivate (js_object); + JSValueRef js_result = NULL; + if (object) + { + gchar* property = _js_string_utf8 (js_property); + GParamSpec* pspec = g_object_class_find_property ( + G_OBJECT_GET_CLASS (object), property); + if (!pspec) + { + gchar* message = g_strdup_printf (_("%s has no property '%s'"), + KATZE_OBJECT_NAME (object), property); + JSStringRef js_message = JSStringCreateWithUTF8CString (message); + *js_exception = JSValueMakeString (js_context, js_message); + JSStringRelease (js_message); + g_free (message); + g_free (property); + return JSValueMakeNull (js_context); + } + if (!(pspec->flags & G_PARAM_READABLE)) + { + g_free (property); + return JSValueMakeUndefined (js_context); + } + GType type = G_PARAM_SPEC_TYPE (pspec); + if (type == G_TYPE_PARAM_STRING) + { + gchar* value; + g_object_get (object, property, &value, NULL); + if (value) + { + JSStringRef js_string = JSStringCreateWithUTF8CString (value); + js_result = JSValueMakeString (js_context, js_string); + } + } + else if (type == G_TYPE_PARAM_INT + || type == G_TYPE_PARAM_UINT) + { + gint value; + g_object_get (object, property, &value, NULL); + js_result = JSValueMakeNumber (js_context, value); + } + else if (type == G_TYPE_PARAM_BOOLEAN) + { + gboolean value; + g_object_get (object, property, &value, NULL); + js_result = JSValueMakeBoolean (js_context, value ? true : false); + } + else if (type == G_TYPE_PARAM_OBJECT) + { + GObject* value; + g_object_get (object, property, &value, NULL); + if (value) + js_result = _js_object_new (js_context, value); + } + else if (type == G_TYPE_PARAM_ENUM) + { + gint value; + g_object_get (object, property, &value, NULL); + js_result = JSValueMakeNumber (js_context, value); + } + else + js_result = JSValueMakeUndefined (js_context); + g_free (property); + } + return js_result ? js_result : JSValueMakeNull (js_context); +} + +static bool +_js_class_set_property_cb (JSContextRef js_context, + JSObjectRef js_object, + JSStringRef js_property, + JSValueRef js_value, + JSValueRef* js_exception) +{ + GObject* object = JSObjectGetPrivate (js_object); + bool result = false; + if (object) + { + gchar* property = _js_string_utf8 (js_property); + GParamSpec* pspec = g_object_class_find_property ( + G_OBJECT_GET_CLASS (object), property); + if (!pspec) + { + gchar* message = g_strdup_printf (_("%s has no property '%s'"), + KATZE_OBJECT_NAME (object), property); + JSStringRef js_message = JSStringCreateWithUTF8CString (message); + *js_exception = JSValueMakeString (js_context, js_message); + JSStringRelease (js_message); + g_free (message); + g_free (property); + return false; + } + if (!(pspec->flags & G_PARAM_WRITABLE)) + { + g_free (property); + return false; + } + GType type = G_PARAM_SPEC_TYPE (pspec); + if (type == G_TYPE_PARAM_STRING) + { + JSStringRef js_string_value = JSValueToStringCopy (js_context, + js_value, js_exception); + if (js_string_value) + { + gchar* string_value = _js_string_utf8 (js_string_value); + g_object_set (object, property, string_value, NULL); + g_free (string_value); + } + } + else if (type == G_TYPE_PARAM_INT + || type == G_TYPE_PARAM_UINT) + { + int value = JSValueToNumber (js_context, js_value, + js_exception); + g_object_set (object, property, value, NULL); + } + else if (type == G_TYPE_PARAM_BOOLEAN) + { + bool value = JSValueToBoolean (js_context, js_value); + g_object_set (object, property, value ? TRUE : FALSE, NULL); + } + else if (type == G_TYPE_PARAM_OBJECT) + { + JSObjectRef js_object_value = JSValueToObject ( + js_context, js_value, NULL); + GObject* object_value = JSObjectGetPrivate (js_object_value); + if (object_value) + g_object_set (object, property, object_value, NULL); + else + { + gchar* message = g_strdup_printf (_("%s cannot be assigned to %s.%s"), + "[object]", KATZE_OBJECT_NAME (object), property); + JSStringRef js_message = JSStringCreateWithUTF8CString (message); + *js_exception = JSValueMakeString (js_context, js_message); + JSStringRelease (js_message); + g_free (message); + } + } + else + { + gchar* message = g_strdup_printf (_("%s.%s cannot be accessed"), + KATZE_OBJECT_NAME (object), property); + JSStringRef js_message = JSStringCreateWithUTF8CString (message); + *js_exception = JSValueMakeString (js_context, js_message); + JSStringRelease (js_message); + g_free (message); + } + g_free (property); + } + return result; +} + +static JSValueRef +_js_foo_call_as_function_cb (JSContextRef js_context, + JSObjectRef js_function, + JSObjectRef js_this, + size_t n_arguments, + const JSValueRef js_arguments[], + JSValueRef* js_exception) +{ + GObject* object = JSObjectGetPrivate (js_this); + if (object) + { + if (!n_arguments) + { + gtk_widget_show (GTK_WIDGET (object)); + } + else if (n_arguments == 1) + { + JSObjectRef js_arg1 = JSValueToObject ( + js_context, js_arguments[0], NULL); + GObject* arg1 = JSObjectGetPrivate (js_arg1); + if (arg1) + gtk_container_add (GTK_CONTAINER (object), GTK_WIDGET (arg1)); + } + } + + return JSValueMakeUndefined (js_context); +} + +static void +_js_foo_add_function (JSContextRef js_context, + JSObjectRef js_object, + const gchar* func) +{ + JSStringRef js_func = JSStringCreateWithUTF8CString (func); + JSObjectRef js_function = JSObjectMakeFunctionWithCallback ( + js_context, js_func, _js_foo_call_as_function_cb); + JSStringRelease (js_func); + _js_object_set_property (js_context, js_object, func, js_function); +} + +static JSObjectRef +_js_object_new (JSContextRef js_context, + gpointer object) +{ + JSClassDefinition js_class_def = kJSClassDefinitionEmpty; + js_class_def.className = g_strdup (KATZE_OBJECT_NAME (object)); + js_class_def.getPropertyNames = _js_class_get_property_names_cb; + js_class_def.hasProperty = _js_class_has_property_cb; + js_class_def.getProperty = _js_class_get_property_cb; + js_class_def.setProperty = _js_class_set_property_cb; + JSClassRef js_class = JSClassCreate (&js_class_def); + JSObjectRef js_object = JSObjectMake (js_context, js_class, object); + if (GTK_IS_WIDGET (object)) + { + _js_foo_add_function (js_context, js_object, "show"); + } + if (GTK_IS_CONTAINER (object)) + { + _js_foo_add_function (js_context, js_object, "add"); + } + return js_object; +} + +static JSValueRef +_js_eval (JSContextRef js_context, + const gchar* script, + gchar** exception) +{ + JSStringRef js_script = JSStringCreateWithUTF8CString (script); + JSValueRef js_exception = NULL; + JSValueRef js_value = JSEvaluateScript (js_context, js_script, + JSContextGetGlobalObject (js_context), NULL, 0, &js_exception); + if (!js_value && exception) + { + JSStringRef js_message = JSValueToStringCopy (js_context, + js_exception, NULL); + *exception = _js_string_utf8 (js_message); + JSStringRelease (js_message); + js_value = JSValueMakeNull (js_context); + } + JSStringRelease (js_script); + return js_value; +} + +static bool +_js_check_syntax (JSContextRef js_context, + const gchar* script, + const gchar* source_id, + int line, + gchar** exception) +{ + JSStringRef js_script = JSStringCreateWithUTF8CString (script); + JSStringRef js_source_id = JSStringCreateWithUTF8CString (source_id); + JSValueRef js_exception = NULL; + bool result = JSCheckScriptSyntax (js_context, js_script, js_source_id, + line, &js_exception); + if (!result && exception) + { + JSStringRef js_message = JSValueToStringCopy (js_context, + js_exception, NULL); + *exception = _js_string_utf8 (js_message); + JSStringRelease (js_message); + } + JSStringRelease (js_source_id); + JSStringRelease (js_script); + return result; +} + +static gboolean +_js_document_load_script_file (JSContextRef js_context, + const gchar* filename, + gchar** exception) +{ + gboolean result = FALSE; + gchar* script; + GError* error = NULL; + if (g_file_get_contents (filename, &script, NULL, &error)) + { + if (_js_eval (js_context, script, exception)) + result = TRUE; + g_free (script); + } + else + { + *exception = g_strdup (error->message); + g_error_free (error); + } + return result; +} + static void midori_addons_init (MidoriAddons* addons) { @@ -150,237 +564,91 @@ midori_addons_init (MidoriAddons* addons) gtk_box_pack_start (GTK_BOX (addons), priv->treeview, TRUE, TRUE, 0); } -static gchar* -_js_string_utf8 (JSStringRef js_string) +static JSValueRef +_js_info_call_as_function_cb (JSContextRef js_context, + JSObjectRef js_function, + JSObjectRef js_this, + size_t n_arguments, + const JSValueRef js_arguments[], + JSValueRef* js_exception) { - size_t size_utf8 = JSStringGetMaximumUTF8CStringSize (js_string); - gchar* string_utf8 = (gchar*)g_malloc (size_utf8); - JSStringGetUTF8CString (js_string, string_utf8, size_utf8); - return string_utf8; + if (n_arguments > 0) { + JSStringRef js_string = JSValueToStringCopy ( + js_context, js_arguments[0], NULL); + gchar* string = _js_string_utf8 (js_string); + // FIXME: Do we want to print this somewhere else? + printf ("console.info: %s\n", string); + g_free (string); + JSStringRelease (js_string); + } + + return JSValueMakeUndefined (js_context); } static void -_js_class_get_property_names_cb (JSContextRef js_context, - JSObjectRef js_object, - JSPropertyNameAccumulatorRef js_properties) +_midori_addons_extensions_main (MidoriAddons* addons, + GtkWidget* web_widget) { - GObject* object = JSObjectGetPrivate (js_object); - if (object) - { - guint n_properties; - GParamSpec** pspecs = g_object_class_list_properties ( - G_OBJECT_GET_CLASS (object), &n_properties); - gint i; - for (i = 0; i < n_properties; i++) - { - GType type = G_PARAM_SPEC_TYPE (pspecs[i]); - const gchar* property = g_param_spec_get_name (pspecs[i]); - JSStringRef js_property = JSStringCreateWithUTF8CString (property); - JSPropertyNameAccumulatorAddName (js_properties, js_property); - JSStringRelease (js_property); - } - } -} + MidoriAddonsPrivate* priv = addons->priv; -static bool -_js_class_has_property_cb (JSContextRef js_context, - JSObjectRef js_object, - JSStringRef js_property) -{ - GObject* object = JSObjectGetPrivate (js_object); - bool result = false; - if (object) - { - gchar* property = _js_string_utf8 (js_property); - if (g_object_class_find_property (G_OBJECT_GET_CLASS (object), - property)) - result = true; - g_free (property); - } - return result; -} - -static JSValueRef -_js_class_get_property_cb (JSContextRef js_context, - JSObjectRef js_object, - JSStringRef js_property, - JSValueRef* js_exception) -{ - GObject* object = JSObjectGetPrivate (js_object); - JSValueRef js_result = NULL; - if (object) - { - gchar* property = _js_string_utf8 (js_property); - GParamSpec* pspec = g_object_class_find_property ( - G_OBJECT_GET_CLASS (object), property); - if (!pspec) - { - gchar* message = g_strdup_printf (_("%s has no property '%s'"), - KATZE_OBJECT_NAME (object), property); - JSStringRef js_message = JSStringCreateWithUTF8CString (message); - *js_exception = JSValueMakeString (js_context, js_message); - JSStringRelease (js_message); - g_free (message); - } - GType type = G_PARAM_SPEC_TYPE (pspec); - if (type == G_TYPE_PARAM_STRING) - { - gchar* value; - g_object_get (object, property, &value, NULL); - JSStringRef js_string = JSStringCreateWithUTF8CString (value); - js_result = JSValueMakeString (js_context, js_string); - } - else - { - gchar* message = g_strdup_printf (_("%s.%s cannot be accessed"), - KATZE_OBJECT_NAME (object), property); - JSStringRef js_message = JSStringCreateWithUTF8CString (message); - *js_exception = JSValueMakeString (js_context, js_message); - JSStringRelease (js_message); - g_free (message); - } - g_free (property); - } - return js_result; -} - -static bool -_js_class_set_property_cb (JSContextRef js_context, - JSObjectRef js_object, - JSStringRef js_property, - JSValueRef js_value, - JSValueRef* js_exception) -{ - GObject* object = JSObjectGetPrivate (js_object); - bool result = false; - if (object) - { - gchar* property = _js_string_utf8 (js_property); - GParamSpec* pspec = g_object_class_find_property ( - G_OBJECT_GET_CLASS (object), property); - if (!pspec) - { - gchar* message = g_strdup_printf (_("%s has no property '%s'"), - KATZE_OBJECT_NAME (object), property); - JSStringRef js_message = JSStringCreateWithUTF8CString (message); - *js_exception = JSValueMakeString (js_context, js_message); - JSStringRelease (js_message); - g_free (message); - } - if (!(pspec->flags & G_PARAM_WRITABLE)) - { - g_free (property); - return false; - } - GType type = G_PARAM_SPEC_TYPE (pspec); - if (type == G_TYPE_PARAM_STRING) - { - JSStringRef js_string = JSValueToStringCopy (js_context, js_value, - js_exception); - if (js_string) - { - gchar* string = _js_string_utf8 (js_string); - g_object_set (object, property, string, NULL); - g_free (string); - } - } - else - { - gchar* message = g_strdup_printf (_("%s.%s cannot be accessed"), - KATZE_OBJECT_NAME (object), property); - JSStringRef js_message = JSStringCreateWithUTF8CString (message); - *js_exception = JSValueMakeString (js_context, js_message); - JSStringRelease (js_message); - g_free (message); - } - g_free (property); - } - return result; -} - -static JSObjectRef -_js_object_new (JSContextRef js_context, - GObject* object) -{ + JSClassDefinition js_global_def = kJSClassDefinitionEmpty; + js_global_def.getPropertyNames = _js_class_get_property_names_cb; + js_global_def.hasProperty = _js_class_has_property_cb; + js_global_def.getProperty = _js_class_get_property_cb; + JSClassRef js_global_class = JSClassCreate (&js_global_def); + JSGlobalContextRef js_context = JSGlobalContextCreate (js_global_class); JSClassDefinition js_class_def = kJSClassDefinitionEmpty; - js_class_def.className = g_strdup (KATZE_OBJECT_NAME (object)); - js_class_def.getPropertyNames = _js_class_get_property_names_cb; - js_class_def.hasProperty = _js_class_has_property_cb; - js_class_def.getProperty = _js_class_get_property_cb; - js_class_def.setProperty = _js_class_set_property_cb; - // js_class_def.staticFunctions = JSStaticFunction*; + js_class_def.className = g_strdup ("console"); JSClassRef js_class = JSClassCreate (&js_class_def); - return JSObjectMake (js_context, js_class, object); -} + JSObjectRef js_console = JSObjectMake (js_context, js_class, NULL); + JSStringRef js_info = JSStringCreateWithUTF8CString ("info"); + JSObjectRef js_info_function = JSObjectMakeFunctionWithCallback ( + js_context, js_info, _js_info_call_as_function_cb); + JSObjectSetProperty (js_context, js_console, js_info, js_info_function, + kJSPropertyAttributeNone, NULL); + JSStringRelease (js_info); + _js_object_set_property (js_context, + JSContextGetGlobalObject (js_context), + "console", js_console); -static void -_js_object_set_property (JSContextRef js_context, - JSObjectRef js_object, - const gchar* name, - JSValueRef js_value) -{ - JSStringRef js_name = JSStringCreateWithUTF8CString (name); - JSObjectSetProperty(js_context, js_object, js_name, js_value, - kJSPropertyAttributeNone, NULL); - JSStringRelease (js_name); -} + GtkWidget* browser = gtk_widget_get_toplevel (GTK_WIDGET (web_widget)); + if (GTK_WIDGET_TOPLEVEL (browser)) + { + // FIXME: Midori should be backed up by a real GObject + JSClassDefinition js_class_def = kJSClassDefinitionEmpty; + js_class_def.className = g_strdup ("Midori"); + JSClassRef js_class = JSClassCreate (&js_class_def); + JSObjectRef js_midori = JSObjectMake (js_context, js_class, NULL); + _js_object_set_property (js_context, + JSContextGetGlobalObject (js_context), + "midori", js_midori); + JSObjectRef js_browser = _js_object_new (js_context, browser); + _js_object_set_property (js_context, + js_midori, + "browser", js_browser); + } -static JSValueRef -_js_eval (JSContextRef js_context, - const gchar* script, - gchar** exception) -{ - JSStringRef js_script = JSStringCreateWithUTF8CString (script); - JSValueRef js_exception; - JSValueRef js_value = JSEvaluateScript (js_context, js_script, - JSContextGetGlobalObject (js_context), - NULL, 0, &js_exception); - if (!js_value && exception) + // FIXME: We want to honor system installed addons as well + gchar* addon_path = g_build_filename (g_get_user_data_dir (), PACKAGE_NAME, + _folder_for_kind (priv->kind), NULL); + GDir* addon_dir = g_dir_open (addon_path, 0, NULL); + if (addon_dir) { - JSStringRef js_message = JSValueToStringCopy (js_context, - js_exception, NULL); - *exception = _js_string_utf8 (js_message); - JSStringRelease (js_message); - } - JSStringRelease (js_script); - return js_value; -} - -static gboolean -_js_document_load_script_file (JSContextRef js_context, - const gchar* filename, - gchar** exception) -{ - gboolean result = FALSE; - gchar* script; - GError* error = NULL; - if (g_file_get_contents (filename, &script, NULL, &error)) - { - if (_js_eval (js_context, script, exception)) - result = TRUE; - g_free (script); - } - else - { - *exception = g_strdup (error->message); - g_error_free (error); - } - return result; -} - -static const gchar* _folder_for_kind (MidoriAddonKind kind) -{ - switch (kind) - { - case MIDORI_ADDON_EXTENSIONS: - return "extensions"; - case MIDORI_ADDON_USER_SCRIPTS: - return "scripts"; - case MIDORI_ADDON_USER_STYLES: - return "styles"; - default: - return NULL; + const gchar* filename; + while ((filename = g_dir_read_name (addon_dir))) + { + gchar* fullname = g_build_filename (addon_path, filename, NULL); + gchar* exception = NULL; + _js_document_load_script_file (js_context, fullname, &exception); + if (exception) + // FIXME: Do we want to print this somewhere else? + // FIXME Convert the filename to UTF8 + printf ("%s - Exception: %s\n", filename, exception); + g_free (fullname); + } + g_dir_close (addon_dir); } + JSGlobalContextRelease (js_context); } static void @@ -392,13 +660,6 @@ midori_web_widget_window_object_cleared_cb (GtkWidget* web_widget, { MidoriAddonsPrivate* priv = addons->priv; - GObject* settings; - g_object_get (web_widget, "settings", &settings, NULL); - JSObjectRef js_settings = _js_object_new (js_context, settings); - _js_object_set_property (js_context, - JSContextGetGlobalObject (js_context), - KATZE_OBJECT_NAME (settings), js_settings); - // FIXME: We want to honor system installed addons as well gchar* addon_path = g_build_filename (g_get_user_data_dir (), PACKAGE_NAME, _folder_for_kind (priv->kind), NULL); @@ -453,7 +714,9 @@ midori_addons_new (GtkWidget* web_widget, MidoriAddonsPrivate* priv = addons->priv; priv->kind = kind; - if (kind == MIDORI_ADDON_USER_SCRIPTS) + if (kind == MIDORI_ADDON_EXTENSIONS) + _midori_addons_extensions_main (addons, web_widget); + else if (kind == MIDORI_ADDON_USER_SCRIPTS) g_signal_connect (web_widget, "window-object-cleared", G_CALLBACK (midori_web_widget_window_object_cleared_cb), addons); diff --git a/src/midori-browser.c b/src/midori-browser.c index e1b52f49..a8889a93 100644 --- a/src/midori-browser.c +++ b/src/midori-browser.c @@ -81,6 +81,10 @@ enum { PROP_0, + PROP_MENUBAR, + PROP_NAVIGATIONBAR, + PROP_TAB, + PROP_STATUSBAR, PROP_SETTINGS, PROP_STATUSBAR_TEXT, PROP_TRASH @@ -595,6 +599,62 @@ midori_browser_class_init (MidoriBrowserClass* class) GParamFlags flags = G_PARAM_READWRITE | G_PARAM_CONSTRUCT; + /** + * MidoriBrowser::menubar + * + * The menubar. + */ + g_object_class_install_property (gobject_class, + PROP_MENUBAR, + g_param_spec_object ( + "menubar", + _("Menubar"), + _("The menubar"), + GTK_TYPE_MENU_BAR, + G_PARAM_READABLE)); + + /** + * MidoriBrowser::navigationbar + * + * The navigationbar. + */ + g_object_class_install_property (gobject_class, + PROP_NAVIGATIONBAR, + g_param_spec_object ( + "navigationbar", + _("Navigationbar"), + _("The navigationbar"), + GTK_TYPE_TOOLBAR, + G_PARAM_READABLE)); + + /** + * MidoriBrowser::tab + * + * The current tab. + */ + g_object_class_install_property (gobject_class, + PROP_TAB, + g_param_spec_object ( + "tab", + _("Tab"), + _("The current tab"), + GTK_TYPE_WIDGET, + G_PARAM_READWRITE)); + + /** + * MidoriBrowser::statusbar + * + * The statusbar. + */ + g_object_class_install_property (gobject_class, + PROP_STATUSBAR, + g_param_spec_object ( + "statusbar", + _("Statusbar"), + _("The statusbar"), + GTK_TYPE_STATUSBAR, + G_PARAM_READABLE)); + /** * MidoriBrowser::settings * @@ -607,7 +667,7 @@ midori_browser_class_init (MidoriBrowserClass* class) PROP_SETTINGS, g_param_spec_object ( "settings", - "Settings", + _("Settings"), _("The associated settings"), MIDORI_TYPE_WEB_SETTINGS, G_PARAM_READWRITE)); @@ -627,7 +687,7 @@ midori_browser_class_init (MidoriBrowserClass* class) PROP_STATUSBAR_TEXT, g_param_spec_string ( "statusbar-text", - "Statusbar Text", + _("Statusbar Text"), _("The text that is displayed in the statusbar"), "", flags)); @@ -646,7 +706,7 @@ midori_browser_class_init (MidoriBrowserClass* class) PROP_TRASH, g_param_spec_object ( "trash", - "Trash", + _("Trash"), _("The trash, collecting recently closed tabs and windows"), MIDORI_TYPE_TRASH, G_PARAM_READWRITE)); @@ -697,7 +757,7 @@ static void _action_tab_close_activate (GtkAction* action, MidoriBrowser* browser) { - GtkWidget* widget = midori_browser_get_current_page (browser); + GtkWidget* widget = midori_browser_get_current_tab (browser); GtkWidget* scrolled = _midori_browser_scrolled_for_child (browser, widget); gtk_widget_destroy (scrolled); } @@ -2651,14 +2711,7 @@ midori_browser_init (MidoriBrowser* browser) priv->panel_pageholder, NULL, GTK_STOCK_CONVERT, _("Pageholder")); - // Addons - /*panel = midori_addons_new (GTK_WIDGET (browser), MIDORI_ADDON_EXTENSIONS); - gtk_widget_show (panel); - toolbar = midori_addons_get_toolbar (MIDORI_ADDONS (panel)); - gtk_widget_show (toolbar); - midori_panel_append_page (MIDORI_PANEL (priv->panel), - panel, toolbar, - "", _("Extensions"));*/ + // Userscripts panel = midori_addons_new (GTK_WIDGET (browser), MIDORI_ADDON_USER_SCRIPTS); gtk_widget_show (panel); toolbar = midori_addons_get_toolbar (MIDORI_ADDONS (panel)); @@ -2666,6 +2719,7 @@ midori_browser_init (MidoriBrowser* browser) midori_panel_append_page (MIDORI_PANEL (priv->panel), panel, toolbar, "", _("Userscripts")); + // Userstyles /*panel = midori_addons_new (GTK_WIDGET (browser), MIDORI_ADDON_USER_STYLES); gtk_widget_show (panel); toolbar = midori_addons_get_toolbar (MIDORI_ADDONS (panel)); @@ -2750,6 +2804,15 @@ midori_browser_init (MidoriBrowser* browser) gtk_box_pack_start (GTK_BOX (priv->statusbar), priv->progressbar, FALSE, FALSE, 3); + // Extensions + panel = midori_addons_new (GTK_WIDGET (browser), MIDORI_ADDON_EXTENSIONS); + gtk_widget_show (panel); + toolbar = midori_addons_get_toolbar (MIDORI_ADDONS (panel)); + gtk_widget_show (toolbar); + midori_panel_append_page (MIDORI_PANEL (priv->panel), + panel, toolbar, + "", _("Extensions")); + g_object_unref (ui_manager); } @@ -2921,6 +2984,9 @@ midori_browser_set_property (GObject* object, switch (prop_id) { + case PROP_TAB: + midori_browser_set_current_tab (browser, g_value_get_object (value)); + break; case PROP_STATUSBAR_TEXT: _midori_browser_set_statusbar_text (browser, g_value_get_string (value)); break; @@ -2962,6 +3028,18 @@ midori_browser_get_property (GObject* object, switch (prop_id) { + case PROP_MENUBAR: + g_value_set_object (value, priv->menubar); + break; + case PROP_NAVIGATIONBAR: + g_value_set_object (value, priv->navigationbar); + break; + case PROP_TAB: + g_value_set_object (value, midori_browser_get_current_tab (browser)); + break; + case PROP_STATUSBAR: + g_value_set_object (value, priv->statusbar); + break; case PROP_STATUSBAR_TEXT: g_value_set_string (value, priv->statusbar_text); break; @@ -3217,7 +3295,6 @@ midori_browser_set_current_page (MidoriBrowser* browser, gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), n); GtkWidget* scrolled = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), n); GtkWidget* widget = _midori_browser_child_for_scrolled (browser, scrolled); - printf ("_nth_page: %s\n", G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (widget))); if (widget && MIDORI_IS_WEB_VIEW (widget) && !strcmp (midori_web_view_get_display_uri ( MIDORI_WEB_VIEW (widget)), "")) @@ -3234,19 +3311,70 @@ midori_browser_set_current_page (MidoriBrowser* browser, * * If there is no page present at all, %NULL is returned. * - * Return value: the selected page, or %NULL + * Return value: the selected page, or -1 + **/ +gint +midori_browser_get_current_page (MidoriBrowser* browser) +{ + g_return_val_if_fail (MIDORI_IS_BROWSER (browser), -1); + + MidoriBrowserPrivate* priv = browser->priv; + + return gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook)); +} + +/** + * midori_browser_set_current_tab: + * @browser: a #MidoriBrowser + * @widget: a #GtkWidget + * + * Switches to the page containing @widget. + * + * The widget will also grab the focus automatically. + **/ +void +midori_browser_set_current_tab (MidoriBrowser* browser, + GtkWidget* widget) +{ + MidoriBrowserPrivate* priv = browser->priv; + + GtkWidget* scrolled = _midori_browser_scrolled_for_child (browser, widget); + gint n = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook), scrolled); + gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), n); + if (widget && MIDORI_IS_WEB_VIEW (widget) + && !strcmp (midori_web_view_get_display_uri ( + MIDORI_WEB_VIEW (widget)), "")) + gtk_widget_grab_focus (priv->location); + else + gtk_widget_grab_focus (widget); +} + +/** + * midori_browser_get_current_tab: + * @browser: a #MidoriBrowser + * + * Retrieves the currently selected tab. + * + * If there is no tab present at all, %NULL is returned. + * + * Return value: the selected tab, or %NULL **/ GtkWidget* -midori_browser_get_current_page (MidoriBrowser* browser) +midori_browser_get_current_tab (MidoriBrowser* browser) { g_return_val_if_fail (MIDORI_IS_BROWSER (browser), NULL); MidoriBrowserPrivate* priv = browser->priv; gint n = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook)); - GtkWidget* widget = _midori_browser_child_for_scrolled (browser, - gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), n)); - return widget; + if (n >= 0) + { + GtkWidget* widget = _midori_browser_child_for_scrolled (browser, + gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), n)); + return widget; + } + else + return NULL; } /** @@ -3267,7 +3395,7 @@ midori_browser_get_current_web_view (MidoriBrowser* browser) { g_return_val_if_fail (MIDORI_IS_BROWSER (browser), NULL); - GtkWidget* web_view = midori_browser_get_current_page (browser); + GtkWidget* web_view = midori_browser_get_current_tab (browser); return MIDORI_IS_WEB_VIEW (web_view) ? web_view : NULL; } diff --git a/src/midori-browser.h b/src/midori-browser.h index e7194235..6dda0b43 100644 --- a/src/midori-browser.h +++ b/src/midori-browser.h @@ -95,9 +95,16 @@ void midori_browser_set_current_page (MidoriBrowser* browser, gint n); -GtkWidget* +gint midori_browser_get_current_page (MidoriBrowser* browser); +void +midori_browser_set_current_tab (MidoriBrowser* browser, + GtkWidget* widget); + +GtkWidget* +midori_browser_get_current_tab (MidoriBrowser* browser); + GtkWidget* midori_browser_get_current_web_view (MidoriBrowser* browser); diff --git a/src/midori-websettings.c b/src/midori-websettings.c index 6f6ba7b3..4b1fdf4d 100644 --- a/src/midori-websettings.c +++ b/src/midori-websettings.c @@ -463,7 +463,7 @@ midori_web_settings_class_init (MidoriWebSettingsClass* class) "close-buttons-on-tabs", _("Close Buttons on Tabs"), _("Whether tabs have close buttons"), - FALSE, + TRUE, flags)); g_object_class_install_property (gobject_class,