]> spindle.queued.net Git - midori/commitdiff
Initial addon panel feature, only userscripts for now.
authorChristian Dywan <christian@twotoasts.de>
Mon, 28 Apr 2008 20:38:57 +0000 (22:38 +0200)
committerChristian Dywan <christian@twotoasts.de>
Mon, 28 Apr 2008 20:38:57 +0000 (22:38 +0200)
katze/katze-utils.h
src/Makefile.am
src/main.c
src/midori-addons.c [new file with mode: 0644]
src/midori-addons.h [new file with mode: 0644]
src/midori-browser.c
src/midori-browser.h
src/midori-console.c

index c42267d6e8e47cf081fa0679ae0032ce95569f66..6ff7b8634e40173dc43dbe835e851d5307de47a1 100644 (file)
 
 G_BEGIN_DECLS
 
+/**
+ * KATZE_OBJECT_NAME:
+ * @object: an object
+ *
+ * Return the name of an object's class structure's type.
+ **/
+#define KATZE_OBJECT_NAME(object) \
+    G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (object))
+
 /**
  * katze_assign:
  * @lvalue: a pointer
index 042ef9be731b6369487b7d0252c213c15261ee94..82208d265fba8750ffa43010078c6297e9388dfd 100644 (file)
@@ -17,9 +17,10 @@ bin_PROGRAMS = \
     midori
 
 midori_SOURCES = \
-    main.c      main.h      \
+    main.c               main.h               \
     midori-browser.c     midori-browser.h     \
     midori-panel.c       midori-panel.h       \
+    midori-addons.c      midori-addons.h      \
     midori-console.c     midori-console.h     \
     midori-trash.c       midori-trash.h       \
     midori-webview.c     midori-webview.h     \
index 1933c254f2c3bc18559f585dbb7d37f6c6b615e2..884e30abc2afb04e77504ce62bcb0241a849ada4 100644 (file)
@@ -253,20 +253,21 @@ settings_save_to_file (MidoriWebSettings* settings,
                                    enum_value->value_name);
         }
         else
-            g_warning ("Unhandled settings property '%s'", property);
+            g_warning (_("Unhandled settings property '%s'"), property);
     }
     gboolean saved = sokoke_key_file_save_to_file (key_file, filename, error);
     g_key_file_free (key_file);
     return saved;
 }
 
-int main(int argc, char** argv)
+int
+main (int argc, char** argv)
 {
     MidoriStartup load_on_startup;
     gchar* homepage;
 
-    locale_init();
-    g_set_application_name(_("midori"));
+    locale_init ();
+    g_set_application_name (_("midori"));
 
     // Parse cli options
     gboolean version = FALSE;
@@ -278,15 +279,16 @@ int main(int argc, char** argv)
     };
 
     GError* error = NULL;
-    if(!gtk_init_with_args(&argc, &argv, _("[URL]"), entries, GETTEXT_PACKAGE, &error))
+    if (!gtk_init_with_args (&argc, &argv, _("[URL]"), entries,
+                             GETTEXT_PACKAGE, &error))
     {
-        g_error_free(error);
+        g_error_free (error);
         return 1;
     }
 
-    if(version)
+    if (version)
     {
-        g_print(
+        g_print (
           "%s %s - Copyright (c) 2007-2008 Christian Dywan\n\n"
           "GTK+2:  \t\t%s\n"
           "WebKit: \t\t%s\n"
@@ -403,17 +405,17 @@ int main(int argc, char** argv)
 
     // TODO: Handle any number of separate uris from argv
     // Open as many tabs as we have uris, seperated by pipes
-    gchar* uri = argc > 1 ? strtok(g_strdup(argv[1]), "|") : NULL;
-    while(uri != NULL)
+    gchar* uri = argc > 1 ? strtok (g_strdup(argv[1]), "|") : NULL;
+    while (uri != NULL)
     {
-        KatzeXbelItem* item = katze_xbel_bookmark_new();
-        gchar* uriReady = sokoke_magic_uri (uri, NULL);
-        katze_xbel_bookmark_set_href(item, uriReady);
-        g_free(uriReady);
-        katze_xbel_folder_append_item(_session, item);
-        uri = strtok(NULL, "|");
+        KatzeXbelItem* item = katze_xbel_bookmark_new ();
+        gchar* uri_ready = sokoke_magic_uri (uri, NULL);
+        katze_xbel_bookmark_set_href (item, uri_ready);
+        g_free (uri_ready);
+        katze_xbel_folder_append_item (_session, item);
+        uri = strtok (NULL, "|");
     }
-    g_free(uri);
+    g_free (uri);
 
     if (katze_xbel_folder_is_empty (_session))
     {
@@ -484,8 +486,8 @@ int main(int argc, char** argv)
     error = NULL;
     if (!search_engines_to_file (searchEngines, config_file, &error))
     {
-        g_warning("The search engines couldn't be saved. %s", error->message);
-        g_error_free(error);
+        g_warning (_("The search engines couldn't be saved. %s"), error->message);
+        g_error_free (error);
     }
     search_engines_free(searchEngines);
     g_free (config_file);
@@ -493,8 +495,8 @@ int main(int argc, char** argv)
     error = NULL;
     if (!katze_xbel_folder_to_file (bookmarks, config_file, &error))
     {
-        g_warning("The bookmarks couldn't be saved. %s", error->message);
-        g_error_free(error);
+        g_warning (_("The bookmarks couldn't be saved. %s"), error->message);
+        g_error_free (error);
     }
     katze_xbel_item_unref(bookmarks);
     g_free (config_file);
@@ -502,7 +504,7 @@ int main(int argc, char** argv)
     error = NULL;
     if (!katze_xbel_folder_to_file (xbel_trash, config_file, &error))
     {
-        g_warning ("The trash couldn't be saved. %s", error->message);
+        g_warning (_("The trash couldn't be saved. %s"), error->message);
         g_error_free (error);
     }
     katze_xbel_item_unref (xbel_trash);
@@ -514,7 +516,7 @@ int main(int argc, char** argv)
         error = NULL;
         if (!katze_xbel_folder_to_file (session, config_file, &error))
         {
-            g_warning ("The session couldn't be saved. %s", error->message);
+            g_warning (_("The session couldn't be saved. %s"), error->message);
             g_error_free (error);
         }
     }
@@ -523,7 +525,7 @@ int main(int argc, char** argv)
     error = NULL;
     if (!settings_save_to_file (settings, config_file, &error))
     {
-        g_warning ("The configuration couldn't be saved. %s", error->message);
+        g_warning (_("The configuration couldn't be saved. %s"), error->message);
         g_error_free (error);
     }
     katze_assign (config_file, g_build_filename (config_path, "accels", NULL));
diff --git a/src/midori-addons.c b/src/midori-addons.c
new file mode 100644 (file)
index 0000000..0dd013f
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ Copyright (C) 2008 Christian Dywan <christian@twotoasts.de>
+
+ 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 "config.h"
+
+#include "midori-addons.h"
+
+#include "sokoke.h"
+#include <webkit/webkit.h>
+#include <JavaScriptCore/JavaScript.h>
+#include <glib/gi18n.h>
+
+G_DEFINE_TYPE (MidoriAddons, midori_addons, GTK_TYPE_VBOX)
+
+struct _MidoriAddonsPrivate
+{
+    MidoriAddonKind kind;
+    GtkWidget* toolbar;
+    GtkWidget* treeview;
+};
+
+#define MIDORI_ADDONS_GET_PRIVATE(obj) \
+    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
+     MIDORI_TYPE_ADDONS, MidoriAddonsPrivate))
+
+GType
+midori_addon_kind_get_type (void)
+{
+    static GType type = 0;
+    if (!type)
+    {
+        static const GEnumValue values[] = {
+         { MIDORI_ADDON_EXTENSIONS, "MIDORI_ADDON_EXTENSIONS", N_("Extensions") },
+         { MIDORI_ADDON_USER_SCRIPTS, "MIDORI_USER_SCRIPTS", N_("Userscripts") },
+         { MIDORI_ADDON_USER_STYLES, "MIDORI_USER_STYLES", N_("Userstyles") },
+         { 0, NULL, NULL }
+        };
+        type = g_enum_register_static ("MidoriAddonKind", values);
+    }
+    return type;
+}
+
+static void
+midori_addons_class_init (MidoriAddonsClass* class)
+{
+    g_type_class_add_private (class, sizeof (MidoriAddonsPrivate));
+}
+
+static void
+midori_addons_button_add_clicked_cb (GtkToolItem*  toolitem,
+                                     MidoriAddons* addons)
+{
+    GtkWidget* dialog = gtk_message_dialog_new (
+        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");
+    gtk_dialog_run (GTK_DIALOG (dialog));
+    gtk_widget_destroy (dialog);
+}
+
+static void
+midori_addons_treeview_render_icon_cb (GtkTreeViewColumn* column,
+                                       GtkCellRenderer*   renderer,
+                                       GtkTreeModel*      model,
+                                       GtkTreeIter*       iter,
+                                       GtkWidget*         treeview)
+{
+    // gchar* source_id;
+    // gtk_tree_model_get (model, iter, 2, &source_id, -1);
+
+    g_object_set (renderer, "stock-id", GTK_STOCK_FILE, NULL);
+
+    // g_free (source_id);
+}
+
+static void
+midori_addons_treeview_render_text_cb (GtkTreeViewColumn* column,
+                                       GtkCellRenderer*   renderer,
+                                       GtkTreeModel*      model,
+                                       GtkTreeIter*       iter,
+                                       GtkWidget*         treeview)
+{
+    gchar* filename;
+    gint   a;
+    gchar* b;
+    gtk_tree_model_get (model, iter, 0, &filename, 1, &a, 2, &b, -1);
+
+    // FIXME: Convert filename to UTF8
+    gchar* text = g_strdup_printf ("%s", filename);
+    g_object_set (renderer, "text", text, NULL);
+    g_free (text);
+
+    g_free (filename);
+    g_free (b);
+}
+
+static void
+midori_addons_treeview_row_activated_cb (GtkTreeView*       treeview,
+                                         GtkTreePath*       path,
+                                         GtkTreeViewColumn* column,
+                                         MidoriAddons*     addons)
+{
+    /*GtkTreeModel* model = gtk_tree_view_get_model (treeview);
+    GtkTreeIter iter;
+    if (gtk_tree_model_get_iter (model, &iter, path))
+    {
+        gchar* b;
+        gtk_tree_model_get (model, &iter, 2, &b, -1);
+        g_free (b);
+    }*/
+}
+
+static void
+midori_addons_init (MidoriAddons* addons)
+{
+    addons->priv = MIDORI_ADDONS_GET_PRIVATE (addons);
+
+    MidoriAddonsPrivate* priv = addons->priv;
+
+    GtkTreeViewColumn* column;
+    GtkCellRenderer* renderer_text;
+    GtkCellRenderer* renderer_pixbuf;
+    priv->treeview = gtk_tree_view_new ();
+    gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->treeview), FALSE);
+    column = gtk_tree_view_column_new ();
+    renderer_pixbuf = gtk_cell_renderer_pixbuf_new ();
+    gtk_tree_view_column_pack_start (column, renderer_pixbuf, FALSE);
+    gtk_tree_view_column_set_cell_data_func (column, renderer_pixbuf,
+        (GtkTreeCellDataFunc)midori_addons_treeview_render_icon_cb,
+        priv->treeview, NULL);
+    renderer_text = gtk_cell_renderer_text_new ();
+    gtk_tree_view_column_pack_start (column, renderer_text, FALSE);
+    gtk_tree_view_column_set_cell_data_func (column, renderer_text,
+        (GtkTreeCellDataFunc)midori_addons_treeview_render_text_cb,
+        priv->treeview, NULL);
+    gtk_tree_view_append_column (GTK_TREE_VIEW (priv->treeview), column);
+    g_signal_connect (priv->treeview, "row-activated",
+                      G_CALLBACK (midori_addons_treeview_row_activated_cb),
+                      addons);
+    gtk_widget_show (priv->treeview);
+    gtk_box_pack_start (GTK_BOX (addons), priv->treeview, TRUE, TRUE, 0);
+}
+
+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 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_class_def = kJSClassDefinitionEmpty;
+    js_class_def.className = g_strdup (KATZE_OBJECT_NAME (object));
+    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*;
+    JSClassRef js_class = JSClassCreate (&js_class_def);
+    return JSObjectMake (js_context, js_class, 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 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)
+    {
+        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;
+    }
+}
+
+static void
+midori_web_widget_window_object_cleared_cb (GtkWidget*         web_widget,
+                                            WebKitWebFrame*    web_frame,
+                                            JSGlobalContextRef js_context,
+                                            JSObjectRef        js_window,
+                                            MidoriAddons*      addons)
+{
+    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);
+    GDir* addon_dir = g_dir_open (addon_path, 0, NULL);
+    if (addon_dir)
+    {
+        const gchar* filename;
+        while ((filename = g_dir_read_name (addon_dir)))
+        {
+            gchar* fullname = g_build_filename (addon_path, filename, NULL);
+            gchar* exception;
+            if (!_js_document_load_script_file (js_context, fullname,
+                                                &exception))
+            {
+                gchar* message = g_strdup_printf ("console.error ('%s');",
+                                                  exception);
+                _js_eval (js_context, message, NULL);
+                g_free (message);
+                g_free (exception);
+            }
+            g_free (fullname);
+        }
+        g_dir_close (addon_dir);
+    }
+}
+
+/**
+ * midori_addons_new:
+ * @web_widget: a web widget
+ * @kind: the kind of addon
+ * @extension: a file extension mask
+ *
+ * Creates a new addons widget.
+ *
+ * @web_widget can be one of the following:
+ *     %MidoriBrowser, %MidoriWebView, %WebKitWebView
+ *
+ * Note: Currently @extension has no effect.
+ *
+ * Return value: a new #MidoriAddons
+ **/
+GtkWidget*
+midori_addons_new (GtkWidget*      web_widget,
+                   MidoriAddonKind kind)
+{
+    g_return_val_if_fail (GTK_IS_WIDGET (web_widget), NULL);
+
+    MidoriAddons* addons = g_object_new (MIDORI_TYPE_ADDONS,
+                                         // "kind", kind,
+                                         NULL);
+
+    MidoriAddonsPrivate* priv = addons->priv;
+    priv->kind = kind;
+
+    if (kind == MIDORI_ADDON_USER_SCRIPTS)
+        g_signal_connect (web_widget, "window-object-cleared",
+            G_CALLBACK (midori_web_widget_window_object_cleared_cb), addons);
+
+    GtkListStore* liststore = gtk_list_store_new (3, G_TYPE_STRING,
+                                                     G_TYPE_INT,
+                                                     G_TYPE_STRING);
+    // 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)
+    {
+        const gchar* filename;
+        while ((filename = g_dir_read_name (addon_dir)))
+        {
+            GtkTreeIter iter;
+            gtk_list_store_append (liststore, &iter);
+            gtk_list_store_set (liststore, &iter,
+                0, filename, 1, 0, 2, "", -1);
+        }
+        g_dir_close (addon_dir);
+    }
+    gtk_tree_view_set_model (GTK_TREE_VIEW (priv->treeview),
+                             GTK_TREE_MODEL (liststore));
+
+    return GTK_WIDGET (addons);
+}
+
+/**
+ * midori_addons_get_toolbar:
+ *
+ * Retrieves the toolbar of the addons. A new widget is created on
+ * the first call of this function.
+ *
+ * Return value: a new #MidoriAddons
+ **/
+GtkWidget*
+midori_addons_get_toolbar (MidoriAddons* addons)
+{
+    MidoriAddonsPrivate* priv = addons->priv;
+
+    g_return_val_if_fail (MIDORI_IS_ADDONS (addons), NULL);
+
+    if (!priv->toolbar)
+    {
+        GtkWidget* toolbar = gtk_toolbar_new ();
+        gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH_HORIZ);
+        gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar), GTK_ICON_SIZE_BUTTON);
+        GtkToolItem* toolitem = gtk_tool_item_new ();
+        gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
+        gtk_widget_show (GTK_WIDGET (toolitem));
+        toolitem = gtk_separator_tool_item_new ();
+        gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (toolitem),
+                                          FALSE);
+        gtk_tool_item_set_expand (toolitem, TRUE);
+        gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
+        gtk_widget_show (GTK_WIDGET (toolitem));
+        toolitem = gtk_tool_button_new_from_stock (GTK_STOCK_ADD);
+        gtk_tool_item_set_is_important (toolitem, TRUE);
+        g_signal_connect (toolitem, "clicked",
+            G_CALLBACK (midori_addons_button_add_clicked_cb), addons);
+        gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
+        gtk_widget_show (GTK_WIDGET (toolitem));
+        priv->toolbar = toolbar;
+    }
+
+    return priv->toolbar;
+}
diff --git a/src/midori-addons.h b/src/midori-addons.h
new file mode 100644 (file)
index 0000000..940ff73
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ Copyright (C) 2008 Christian Dywan <christian@twotoasts.de>
+
+ 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.
+*/
+
+#ifndef __MIDORI_ADDONS_H__
+#define __MIDORI_ADDONS_H__
+
+#include <gtk/gtk.h>
+
+#include <katze/katze.h>
+
+G_BEGIN_DECLS
+
+#define MIDORI_TYPE_ADDONS \
+    (midori_addons_get_type ())
+#define MIDORI_ADDONS(obj) \
+    (G_TYPE_CHECK_INSTANCE_CAST ((obj), MIDORI_TYPE_ADDONS, MidoriAddons))
+#define MIDORI_ADDONS_CLASS(klass) \
+    (G_TYPE_CHECK_CLASS_CAST ((klass), MIDORI_TYPE_ADDONS, MidoriAddonsClass))
+#define MIDORI_IS_ADDONS(obj) \
+    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MIDORI_TYPE_ADDONS))
+#define MIDORI_IS_ADDONS_CLASS(klass) \
+    (G_TYPE_CHECK_CLASS_TYPE ((klass), MIDORI_TYPE_ADDONS))
+#define MIDORI_ADDONS_GET_CLASS(obj) \
+    (G_TYPE_INSTANCE_GET_CLASS ((obj), MIDORI_TYPE_ADDONS, MidoriAddonsClass))
+
+typedef struct _MidoriAddons                MidoriAddons;
+typedef struct _MidoriAddonsPrivate         MidoriAddonsPrivate;
+typedef struct _MidoriAddonsClass           MidoriAddonsClass;
+
+struct _MidoriAddons
+{
+    GtkVBox parent_instance;
+
+    MidoriAddonsPrivate* priv;
+};
+
+struct _MidoriAddonsClass
+{
+    GtkVBoxClass parent_class;
+};
+
+typedef enum
+{
+    MIDORI_ADDON_EXTENSIONS,
+    MIDORI_ADDON_USER_SCRIPTS,
+    MIDORI_ADDON_USER_STYLES
+} MidoriAddonKind;
+
+GType
+midori_addon_kind_get_type (void) G_GNUC_CONST;
+
+#define MIDORI_TYPE_ADDON_KIND \
+    (midori_addon_kind_get_type ())
+
+GType
+midori_addons_get_type               (void);
+
+GtkWidget*
+midori_addons_new                    (GtkWidget*      web_widget,
+                                      MidoriAddonKind kind);
+
+GtkWidget*
+midori_addons_get_toolbar            (MidoriAddons*       console);
+
+G_END_DECLS
+
+#endif /* __MIDORI_ADDONS_H__ */
index f6e825cc85300d9a153f072fce2a38456ff6859f..e1b52f495e8167103f53063dec85ba6672a3d8da 100644 (file)
@@ -20,6 +20,7 @@
 #include "midori-webview.h"
 #include "midori-preferences.h"
 #include "midori-panel.h"
+#include "midori-addons.h"
 #include "midori-console.h"
 #include "midori-trash.h"
 
@@ -87,10 +88,11 @@ enum
 
 enum
 {
-    NEW_WINDOW,
+    WINDOW_OBJECT_CLEARED,
     STATUSBAR_TEXT_CHANGED,
     ELEMENT_MOTION,
     QUIT,
+    NEW_WINDOW,
 
     LAST_SIGNAL
 };
@@ -268,6 +270,17 @@ _midori_browser_update_progress (MidoriBrowser* browser,
     }
 }
 
+static void
+midori_web_view_window_object_cleared_cb (GtkWidget*         web_view,
+                                          WebKitWebFrame*    web_frame,
+                                          JSGlobalContextRef js_context,
+                                          JSObjectRef        js_window,
+                                          MidoriBrowser*     browser)
+{
+    g_signal_emit (browser, signals[WINDOW_OBJECT_CLEARED], 0,
+                   web_frame, js_context, js_window);
+}
+
 static void
 midori_web_view_load_started_cb (GtkWidget*      web_view,
                                  WebKitWebFrame* web_frame,
@@ -477,9 +490,82 @@ midori_web_view_destroy_cb (GtkWidget*     widget,
     return FALSE;
 }
 
+static void
+midori_cclosure_marshal_VOID__OBJECT_POINTER_POINTER (GClosure*     closure,
+                                                      GValue*       return_value,
+                                                      guint         n_param_values,
+                                                      const GValue* param_values,
+                                                      gpointer      invocation_hint,
+                                                      gpointer      marshal_data)
+{
+    typedef gboolean(*GMarshalFunc_VOID__OBJECT_POINTER_POINTER) (gpointer  data1,
+                                                                  gpointer  arg_1,
+                                                                  gpointer  arg_2,
+                                                                  gpointer  arg_3,
+                                                                  gpointer  data2);
+    register GMarshalFunc_VOID__OBJECT_POINTER_POINTER 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__OBJECT_POINTER_POINTER) (marshal_data
+        ? marshal_data : cc->callback);
+
+    callback (data1,
+              g_value_get_object (param_values + 1),
+              g_value_get_pointer (param_values + 2),
+              g_value_get_pointer (param_values + 3),
+              data2);
+}
+
 static void
 midori_browser_class_init (MidoriBrowserClass* class)
 {
+    signals[WINDOW_OBJECT_CLEARED] = g_signal_new (
+        "window-object-cleared",
+        G_TYPE_FROM_CLASS (class),
+        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
+        G_STRUCT_OFFSET (MidoriBrowserClass, window_object_cleared),
+        0,
+        NULL,
+        midori_cclosure_marshal_VOID__OBJECT_POINTER_POINTER,
+        G_TYPE_NONE, 3,
+        WEBKIT_TYPE_WEB_FRAME,
+        G_TYPE_POINTER,
+        G_TYPE_POINTER);
+
+    signals[STATUSBAR_TEXT_CHANGED] = g_signal_new (
+        "statusbar-text-changed",
+        G_TYPE_FROM_CLASS (class),
+        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
+        G_STRUCT_OFFSET (MidoriBrowserClass, statusbar_text_changed),
+        0,
+        NULL,
+        g_cclosure_marshal_VOID__STRING,
+        G_TYPE_NONE, 1,
+        G_TYPE_STRING);
+
+    signals[ELEMENT_MOTION] = g_signal_new (
+        "element-motion",
+        G_TYPE_FROM_CLASS (class),
+        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
+        G_STRUCT_OFFSET (MidoriBrowserClass, new_window),
+        0,
+        NULL,
+        g_cclosure_marshal_VOID__STRING,
+        G_TYPE_NONE, 1,
+        G_TYPE_STRING);
 
     signals[QUIT] = g_signal_new (
         "quit",
@@ -2534,12 +2620,10 @@ midori_browser_init (MidoriBrowser* browser)
                               "vcard", _("Bookmarks"));
 
     // Transfers
-    priv->panel_pageholder = g_object_new (MIDORI_TYPE_WEB_VIEW,
-                                           "uri", "",
-                                           NULL);
-    gtk_widget_show (priv->panel_pageholder);
+    GtkWidget* panel = midori_web_view_new ();
+    gtk_widget_show (panel);
     midori_panel_append_page (MIDORI_PANEL (priv->panel),
-                              priv->panel_pageholder, NULL,
+                              panel, NULL,
                               "package", _("Transfers"));
 
     // Console
@@ -2552,12 +2636,10 @@ midori_browser_init (MidoriBrowser* browser)
                               "terminal", _("Console"));
 
     // History
-    priv->panel_pageholder = g_object_new (MIDORI_TYPE_WEB_VIEW,
-                                           "uri", "",
-                                           NULL);
-    gtk_widget_show (priv->panel_pageholder);
+    panel = midori_web_view_new ();
+    gtk_widget_show (panel);
     midori_panel_append_page (MIDORI_PANEL (priv->panel),
-                              priv->panel_pageholder, NULL,
+                              panel, NULL,
                               "document-open-recent", _("History"));
 
     // Pageholder
@@ -2569,6 +2651,29 @@ 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"));*/
+    panel = midori_addons_new (GTK_WIDGET (browser), MIDORI_ADDON_USER_SCRIPTS);
+    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,
+                              "", _("Userscripts"));
+    /*panel = midori_addons_new (GTK_WIDGET (browser), MIDORI_ADDON_USER_STYLES);
+    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,
+                              "", _("Userstyles"));*/
+
     // Notebook, containing all web_views
     priv->notebook = gtk_notebook_new ();
     gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
@@ -2944,6 +3049,8 @@ midori_browser_append_tab (MidoriBrowser* browser,
         }
 
         g_object_connect (widget,
+                          "signal::window-object-cleared",
+                          midori_web_view_window_object_cleared_cb, browser,
                           "signal::load-started",
                           midori_web_view_load_started_cb, browser,
                           "signal::load-committed",
index a6a797d8c3c570f6bf2fbb338c6c9ca00665afa7..e7194235ec9645ffd1bf3f2bc21abed0252d9cf5 100644 (file)
@@ -48,6 +48,17 @@ struct _MidoriBrowserClass
 
     /* Signals */
     void
+    (*window_object_cleared)   (MidoriBrowser*       browser,
+                                WebKitWebFrame*      web_frame,
+                                JSContextRef*        context,
+                                JSObjectRef*         window_object);
+    void
+    (*statusbar_text_changed)  (MidoriBrowser*       browser,
+                                const gchar*         text);
+    void
+    (*element_motion)          (MidoriBrowser*       browser,
+                                const gchar*         link_uri);
+    void
     (*quit)                    (MidoriBrowser*       browser);
     void
     (*new_window)              (MidoriBrowser*       browser,
index 5eb191de07f8e5fee9d4fc40167d2672489b799e..693608dacf8e1239ad672f31d85abc1bb96264aa 100644 (file)
@@ -18,6 +18,7 @@ G_DEFINE_TYPE (MidoriConsole, midori_console, GTK_TYPE_VBOX)
 
 struct _MidoriConsolePrivate
 {
+    GtkWidget* toolbar;
     GtkWidget* treeview;
 };
 
@@ -156,36 +157,35 @@ midori_console_new (void)
 GtkWidget*
 midori_console_get_toolbar (MidoriConsole* console)
 {
-    g_return_if_fail (MIDORI_IS_CONSOLE (console));
+    g_return_val_if_fail (MIDORI_IS_CONSOLE (console), NULL);
 
     MidoriConsolePrivate* priv = console->priv;
 
-    static GtkWidget* toolbar = NULL;
-
-    if (!toolbar)
+    if (!priv->toolbar)
     {
-        toolbar = gtk_toolbar_new ();
+        GtkWidget* toolbar = gtk_toolbar_new ();
         gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH_HORIZ);
         gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar), GTK_ICON_SIZE_BUTTON);
         GtkToolItem* toolitem = gtk_tool_item_new ();
         // TODO: What about a find entry here that filters e.g. by url?
         gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
-        gtk_widget_show (toolitem);
+        gtk_widget_show (GTK_WIDGET (toolitem));
         toolitem = gtk_separator_tool_item_new ();
         gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (toolitem),
                                           FALSE);
         gtk_tool_item_set_expand (toolitem, TRUE);
         gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
-        gtk_widget_show (toolitem);
+        gtk_widget_show (GTK_WIDGET (toolitem));
         toolitem = gtk_tool_button_new_from_stock (GTK_STOCK_CLEAR);
         gtk_tool_item_set_is_important (toolitem, TRUE);
         g_signal_connect (toolitem, "clicked",
             G_CALLBACK (midori_console_button_clear_clicked_cb), console);
         gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
-        gtk_widget_show (toolitem);
+        gtk_widget_show (GTK_WIDGET (toolitem));
+        priv->toolbar = toolbar;
     }
 
-    return toolbar;
+    return priv->toolbar;
 }
 
 /**