midori/panels/midori-addons.c
Christian Dywan 7f0563f5f4 Scratch the '.user' part in '.user.css' and '.user.js'.
For all practical purposes it caused confusion among users
and the regular file extension should be just as fine.
2008-12-06 04:47:51 +01:00

1159 lines
33 KiB
C

/*
Copyright (C) 2008 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2008 Arnaud Renevier <arenevier@fdn.fr>
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.
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include "midori-addons.h"
#include "midori-stock.h"
#include "sokoke.h"
#include "gjs.h"
#include <webkit/webkit.h>
#include <JavaScriptCore/JavaScript.h>
#include <glib/gi18n.h>
#include <string.h>
#if GLIB_CHECK_VERSION (2, 16, 0)
#include <gio/gio.h>
#endif
struct _MidoriAddons
{
GtkVBox parent_instance;
MidoriAddonKind kind;
GtkWidget* web_widget;
GtkWidget* toolbar;
GtkWidget* treeview;
GSList* elements;
};
struct _MidoriAddonsClass
{
GtkVBoxClass parent_class;
};
struct AddonElement
{
gchar *fullpath;
gchar *name;
gchar *description;
gboolean enabled;
gboolean broken;
GSList* includes;
GSList* excludes;
};
static void
midori_addons_viewable_iface_init (MidoriViewableIface* iface);
G_DEFINE_TYPE_WITH_CODE (MidoriAddons, midori_addons, GTK_TYPE_VBOX,
G_IMPLEMENT_INTERFACE (MIDORI_TYPE_VIEWABLE,
midori_addons_viewable_iface_init));
enum
{
PROP_0,
PROP_KIND,
PROP_WEB_WIDGET
};
static void
midori_addons_finalize (GObject* object);
static void
midori_addons_set_property (GObject* object,
guint prop_id,
const GValue* value,
GParamSpec* pspec);
static void
midori_addons_get_property (GObject* object,
guint prop_id,
GValue* value,
GParamSpec* pspec);
GType
midori_addon_kind_get_type (void)
{
static GType type = 0;
if (!type)
{
static const GEnumValue values[] = {
{ MIDORI_ADDON_NONE, "MIDORI_ADDON_NONE", N_("None") },
{ 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)
{
GObjectClass* gobject_class;
GParamFlags flags;
gobject_class = G_OBJECT_CLASS (class);
gobject_class->finalize = midori_addons_finalize;
gobject_class->set_property = midori_addons_set_property;
gobject_class->get_property = midori_addons_get_property;
flags = G_PARAM_READWRITE | G_PARAM_CONSTRUCT;
g_object_class_install_property (gobject_class,
PROP_KIND,
g_param_spec_enum (
"kind",
"Kind",
"The kind of addons",
MIDORI_TYPE_ADDON_KIND,
MIDORI_ADDON_NONE,
flags));
g_object_class_install_property (gobject_class,
PROP_WEB_WIDGET,
g_param_spec_object (
"web-widget",
"Web Widget",
"The assigned web widget",
GTK_TYPE_WIDGET,
G_PARAM_READWRITE));
}
static const gchar*
midori_addons_get_label (MidoriViewable* viewable)
{
if (MIDORI_ADDONS (viewable)->kind == MIDORI_ADDON_USER_SCRIPTS)
return _("Userscripts");
else if (MIDORI_ADDONS (viewable)->kind == MIDORI_ADDON_USER_SCRIPTS)
return _("Userstyles");
else
return NULL;
}
static const gchar*
midori_addons_get_stock_id (MidoriViewable* viewable)
{
if (MIDORI_ADDONS (viewable)->kind == MIDORI_ADDON_USER_SCRIPTS)
return STOCK_SCRIPTS;
else if (MIDORI_ADDONS (viewable)->kind == MIDORI_ADDON_USER_STYLES)
return STOCK_STYLES;
else
return NULL;
}
static void
midori_addons_viewable_iface_init (MidoriViewableIface* iface)
{
iface->get_stock_id = midori_addons_get_stock_id;
iface->get_label = midori_addons_get_label;
iface->get_toolbar = midori_addons_get_toolbar;
}
static void
midori_addons_set_property (GObject* object,
guint prop_id,
const GValue* value,
GParamSpec* pspec)
{
MidoriAddons* addons = MIDORI_ADDONS (object);
switch (prop_id)
{
case PROP_KIND:
addons->kind = g_value_get_enum (value);
break;
case PROP_WEB_WIDGET:
katze_object_assign (addons->web_widget, g_value_dup_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
midori_addons_get_property (GObject* object,
guint prop_id,
GValue* value,
GParamSpec* pspec)
{
MidoriAddons* addons = MIDORI_ADDONS (object);
switch (prop_id)
{
case PROP_KIND:
g_value_set_enum (value, addons->kind);
break;
case PROP_WEB_WIDGET:
g_value_set_object (value, addons->web_widget);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static const gchar*
_addons_get_folder (MidoriAddons* addons)
{
switch (addons->kind)
{
case MIDORI_ADDON_USER_SCRIPTS:
return "scripts";
case MIDORI_ADDON_USER_STYLES:
return "styles";
default:
return NULL;
}
}
static const gchar*
_addons_get_extension (MidoriAddons* addons)
{
switch (addons->kind)
{
case MIDORI_ADDON_USER_SCRIPTS:
return ".js";
case MIDORI_ADDON_USER_STYLES:
return ".css";
default:
return NULL;
}
}
static GSList*
_addons_get_directories (MidoriAddons* addons)
{
GSList *directories;
const char* const* datadirs;
const gchar* folder;
gchar* path;
folder = _addons_get_folder (addons);
/* user data dir */
path = g_build_path (G_DIR_SEPARATOR_S, g_get_user_data_dir (),
PACKAGE_NAME, folder, NULL);
directories = g_slist_prepend (NULL, path);
/* system data dirs */
datadirs = g_get_system_data_dirs ();
while (*datadirs)
{
path = g_build_path (G_DIR_SEPARATOR_S, *datadirs,
PACKAGE_NAME, folder, NULL);
directories = g_slist_prepend (directories, path);
datadirs++;
}
return directories;
}
static GSList*
_addons_get_files (MidoriAddons* addons)
{
GSList* files;
GDir* addon_dir;
const gchar* folder;
const gchar* extension;
GSList* list;
GSList* directories;
const gchar* filename;
gchar* dirname;
gchar* fullname;
files = NULL;
folder = _addons_get_folder (addons);
extension = _addons_get_extension (addons);
directories = _addons_get_directories (addons);
list = directories;
while (directories)
{
dirname = directories->data;
if ((addon_dir = g_dir_open (dirname, 0, NULL)))
{
while ((filename = g_dir_read_name (addon_dir)))
{
if (g_str_has_suffix (filename, extension))
{
fullname = g_build_filename (dirname, filename, NULL);
files = g_slist_prepend (files, fullname);
}
}
g_dir_close (addon_dir);
}
g_free (dirname);
directories = g_slist_next (directories);
}
g_slist_free (list);
return files;
}
GtkTreePath*
_treeview_first_selected_path (GtkTreeView *treeview)
{
GtkTreeSelection* selection;
GList* tree_paths;
selection = gtk_tree_view_get_selection (treeview);
if (!selection)
return NULL;
if (gtk_tree_selection_get_selected (selection, NULL, NULL))
{
tree_paths = gtk_tree_selection_get_selected_rows (selection, NULL);
return g_list_nth_data (tree_paths, 0);
}
else
return NULL;
}
static void
_addons_toggle_enable_button (MidoriAddons* addons,
gboolean sensitive)
{
GtkToolItem* button;
button = gtk_toolbar_get_nth_item (GTK_TOOLBAR (addons->toolbar), 1);
gtk_widget_set_sensitive (GTK_WIDGET (button), sensitive);
}
static void
_addons_toggle_disable_button (MidoriAddons* addons,
gboolean sensitive)
{
GtkToolItem* button;
button = gtk_toolbar_get_nth_item (GTK_TOOLBAR (addons->toolbar), 2);
gtk_widget_set_sensitive (GTK_WIDGET (button), sensitive);
}
#if GLIB_CHECK_VERSION (2, 16, 0)
static void
midori_addons_directory_monitor_changed (GFileMonitor* monitor,
GFile* child,
GFile* other_file,
GFileMonitorEvent flags,
MidoriAddons* addons)
{
midori_addons_update_elements (addons);
}
#endif
static void
midori_addons_treeview_cursor_changed (GtkTreeView* treeview,
MidoriAddons* addons)
{
struct AddonElement* element;
GtkTreeModel* model;
GtkTreeIter iter;
GtkTreePath *path;
path = _treeview_first_selected_path (treeview);
model = gtk_tree_view_get_model (treeview);
if (gtk_tree_model_get_iter (model, &iter, path))
{
gtk_tree_model_get (model, &iter, 0, &element, -1);
if (element->broken)
{
_addons_toggle_enable_button (addons, FALSE);
_addons_toggle_disable_button (addons, FALSE);
} else
{
_addons_toggle_enable_button (addons, !element->enabled);
_addons_toggle_disable_button (addons, element->enabled);
}
}
}
static void
midori_addons_button_status_clicked_cb (GtkToolItem* toolitem,
MidoriAddons* addons)
{
GtkTreeView* treeview;
struct AddonElement* element;
GtkTreeModel* model;
GtkTreeIter iter;
GtkTreePath* path;
treeview = GTK_TREE_VIEW (addons->treeview);
path = _treeview_first_selected_path (treeview);
model = gtk_tree_view_get_model (treeview);
if (gtk_tree_model_get_iter (model, &iter, path))
{
gtk_tree_model_get (model, &iter, 0, &element, -1);
if (toolitem == gtk_toolbar_get_nth_item (
GTK_TOOLBAR (addons->toolbar), 2)) /* disable button */
element->enabled = FALSE;
else if (toolitem == gtk_toolbar_get_nth_item (
GTK_TOOLBAR (addons->toolbar), 1)) /* enable button */
element->enabled = TRUE;
_addons_toggle_enable_button (addons, !element->enabled);
_addons_toggle_disable_button (addons, element->enabled);
/* After enabling or disabling an element, the tree view
is not updated automatically; we need to notify tree model
in order to take the modification into account */
gtk_tree_model_row_changed (model, path, &iter);
}
}
static void
midori_addons_button_add_clicked_cb (GtkToolItem* toolitem,
MidoriAddons* addons)
{
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/%s",
_addons_get_folder (addons));
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
#if GLIB_CHECK_VERSION (2, 16, 0)
/* FIXME: Without GIO clicking this button is the only
way to update the list */
midori_addons_update_elements (addons);
#endif
}
static void
midori_addons_treeview_render_icon_cb (GtkTreeViewColumn* column,
GtkCellRenderer* renderer,
GtkTreeModel* model,
GtkTreeIter* iter,
GtkWidget* treeview)
{
struct AddonElement *element;
gtk_tree_model_get (model, iter, 0, &element, -1);
if (element->broken)
g_object_set (renderer, "stock-id", GTK_STOCK_STOP, NULL);
else
g_object_set (renderer, "stock-id", GTK_STOCK_FILE, NULL);
}
static void
midori_addons_treeview_render_text_cb (GtkTreeViewColumn* column,
GtkCellRenderer* renderer,
GtkTreeModel* model,
GtkTreeIter* iter,
GtkWidget* treeview)
{
struct AddonElement *element;
gtk_tree_model_get (model, iter, 0, &element, -1);
g_object_set (renderer, "text", element->name, NULL);
if (!element->enabled)
g_object_set (renderer, "sensitive", false, NULL);
else
g_object_set (renderer, "sensitive", true, NULL);
}
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)
{
GtkTreeViewColumn* column;
GtkCellRenderer* renderer_text;
GtkCellRenderer* renderer_pixbuf;
addons->web_widget = NULL;
addons->elements = NULL;
addons->treeview = gtk_tree_view_new ();
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (addons->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,
addons->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,
addons->treeview, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (addons->treeview), column);
g_signal_connect (addons->treeview, "row-activated",
G_CALLBACK (midori_addons_treeview_row_activated_cb),
addons);
gtk_widget_show (addons->treeview);
gtk_box_pack_start (GTK_BOX (addons), addons->treeview, TRUE, TRUE, 0);
}
static void
midori_addons_finalize (GObject* object)
{
MidoriAddons* addons = MIDORI_ADDONS (object);
katze_object_assign (addons->web_widget, NULL);
g_slist_free (addons->elements);
}
static gboolean
_metadata_from_file (const gchar* filename,
GSList** includes,
GSList** excludes,
gchar** name,
gchar** description)
{
GIOChannel* channel;
gboolean found_meta;
gchar* line;
gchar* rest_of_line;
if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_SYMLINK))
return FALSE;
channel = g_io_channel_new_file (filename, "r", 0);
if (!channel)
return FALSE;
found_meta = FALSE;
while (g_io_channel_read_line (channel, &line, NULL, NULL, NULL)
== G_IO_STATUS_NORMAL)
{
if (g_str_has_prefix (line, "// ==UserScript=="))
found_meta = TRUE;
else if (found_meta)
{
if (g_str_has_prefix (line, "// ==/UserScript=="))
found_meta = FALSE;
else if (g_str_has_prefix (line, "// @require ") ||
g_str_has_prefix (line, "// @resource "))
{
/* We don't support these, so abort here */
g_free (line);
g_io_channel_shutdown (channel, false, 0);
g_slist_free (*includes);
g_slist_free (*excludes);
*includes = NULL;
*excludes = NULL;
return FALSE;
}
else if (includes && g_str_has_prefix (line, "// @include "))
{
rest_of_line = g_strdup (line + strlen ("// @include "));
rest_of_line = g_strstrip (rest_of_line);
*includes = g_slist_prepend (*includes, rest_of_line);
}
else if (excludes && g_str_has_prefix (line, "// @exclude "))
{
rest_of_line = g_strdup (line + strlen ("// @exclude "));
rest_of_line = g_strstrip (rest_of_line);
*excludes = g_slist_prepend (*excludes, rest_of_line);
}
else if (name && g_str_has_prefix (line, "// @name "))
{
rest_of_line = g_strdup (line + strlen ("// @name "));
rest_of_line = g_strstrip (rest_of_line);
*name = rest_of_line;
}
else if (description && g_str_has_prefix (line, "// @description "))
{
rest_of_line = g_strdup (line + strlen ("// @description "));
rest_of_line = g_strstrip (rest_of_line);
*description = rest_of_line;
}
}
g_free (line);
}
g_io_channel_shutdown (channel, false, 0);
g_io_channel_unref (channel);
return TRUE;
}
#define HAVE_GREGEX GLIB_CHECK_VERSION (2, 14, 0)
#if HAVE_GREGEX
static gchar*
_convert_to_simple_regexp (const gchar* pattern)
{
guint len;
gchar* dest;
guint pos;
guint i;
gchar c;
len = strlen (pattern);
dest = g_malloc0 (len * 2 + 1);
dest[0] = '^';
pos = 1;
for (i = 0; i < len; i++)
{
c = pattern[i];
switch (c)
{
case '*':
dest[pos] = '.';
dest[pos + 1] = c;
pos++;
pos++;
break;
case '.' :
case '?' :
case '^' :
case '$' :
case '+' :
case '{' :
case '[' :
case '|' :
case '(' :
case ')' :
case ']' :
case '\\' :
dest[pos] = '\\';
dest[pos + 1] = c;
pos++;
pos++;
break;
case ' ' :
break;
default:
dest[pos] = pattern[i];
pos ++;
}
}
return dest;
}
#else
static bool
_match_with_wildcard (const gchar* str,
const gchar* pattern)
{
gchar** parts;
gchar** parts_ref;
const gchar* subpart;
gchar* newsubpart;
parts = g_strsplit (pattern, "*", 0);
parts_ref = parts;
subpart = str;
do
{
newsubpart = g_strstr_len (subpart, strlen (subpart), *parts);
if (!newsubpart)
{
g_strfreev (parts_ref);
return FALSE;
}
subpart = newsubpart + strlen (*parts);
parts++;
}
while (*parts);
g_strfreev (parts_ref);
return TRUE;
}
#endif
static gboolean
_may_load_script (const gchar* uri,
GSList** includes,
GSList** excludes)
{
gboolean match;
GSList* list;
#if HAVE_GREGEX
gchar* re;
#else
guint uri_len;
guint pattern_len;
#endif
if (*includes)
match = FALSE;
else
match = TRUE;
list = *includes;
#if !HAVE_GREGEX
uri_len = strlen (uri);
#endif
while (list)
{
#if HAVE_GREGEX
re = _convert_to_simple_regexp (list->data);
if (g_regex_match_simple (re, uri, 0, 0))
{
match = TRUE;
break;
}
g_free (re);
#else
pattern_len = strlen (list->data);
if (!g_ascii_strncasecmp (uri, list->data, MAX (uri_len, pattern_len)))
{
match = TRUE;
break;
}
else if (_match_with_wildcard (uri, list->data))
{
match = TRUE;
break;
}
#endif
list = g_slist_next (list);
}
if (!match)
{
return FALSE;
}
list = *excludes;
while (list)
{
#if HAVE_GREGEX
re = _convert_to_simple_regexp (list->data);
if (g_regex_match_simple (re, uri, 0, 0))
{
match = FALSE;
break;
}
g_free (re);
#else
pattern_len = strlen (list->data);
if (!g_ascii_strncasecmp (uri, list->data, MAX (uri_len, pattern_len)))
{
match = FALSE;
break;
}
else if (_match_with_wildcard (uri, list->data))
{
match = FALSE;
break;
}
#endif
list = g_slist_next (list);
}
return match;
}
static gboolean
_js_script_from_file (JSContextRef js_context,
const gchar* filename,
gchar** exception)
{
gboolean result = FALSE;
gchar* script;
GError* error = NULL;
gchar* wrapped_script;
if (g_file_get_contents (filename, &script, NULL, &error))
{
/* Wrap the script to prevent global variables */
wrapped_script = g_strdup_printf (
"window.addEventListener ('DOMContentLoaded',"
"function () { %s }, true);", script);
if (gjs_script_eval (js_context, wrapped_script, exception))
result = TRUE;
g_free (wrapped_script);
g_free (script);
}
else
{
*exception = g_strdup (error->message);
g_error_free (error);
}
return result;
}
static gboolean
_js_style_from_file (JSContextRef js_context,
const gchar* filename,
gchar** exception)
{
gboolean result;
gchar* style;
GError* error;
guint i, n;
gchar* style_script;
result = FALSE;
error = NULL;
if (g_file_get_contents (filename, &style, NULL, &error))
{
n = strlen (style);
for (i = 0; i < n; i++)
{
/* Replace line breaks with spaces */
if (style[i] == '\n' || style[i] == '\r')
style[i] = ' ';
/* Change all single quotes to double quotes */
if (style[i] == '\'')
style[i] = '\"';
}
style_script = g_strdup_printf (
"window.addEventListener ('DOMContentLoaded',"
"function () {"
"var mystyle = document.createElement(\"style\");"
"mystyle.setAttribute(\"type\", \"text/css\");"
"mystyle.appendChild(document.createTextNode('%s'));"
"var head = document.getElementsByTagName(\"head\")[0];"
"if (head) head.appendChild(mystyle);"
"else document.documentElement.insertBefore(mystyle, document.documentElement.firstChild);"
"}, true);",
style);
if (gjs_script_eval (js_context, style_script, exception))
result = TRUE;
g_free (style_script);
g_free (style);
}
else
{
*exception = g_strdup (error->message);
g_error_free (error);
}
return result;
}
static void
midori_web_widget_context_ready_cb (GtkWidget* web_widget,
JSGlobalContextRef js_context,
MidoriAddons* addons)
{
const gchar* uri;
GSList* elements;
struct AddonElement* element;
gchar* fullname;
gchar* exception;
gchar* message;
uri = katze_object_get_string (web_widget, "uri");
if (!uri)
return;
elements = addons->elements;
while (elements)
{
element = elements->data;
if (!element->enabled || element->broken)
{
elements = g_slist_next (elements);
continue;
}
fullname = element->fullpath;
if (element->includes || element->excludes)
if (!_may_load_script (uri, &element->includes, &element->excludes))
{
elements = g_slist_next (elements);
continue;
}
exception = NULL;
if (addons->kind == MIDORI_ADDON_USER_SCRIPTS &&
!_js_script_from_file (js_context, fullname, &exception))
{
message = g_strdup_printf ("console.error ('%s');", exception);
gjs_script_eval (js_context, message, NULL);
g_free (message);
g_free (exception);
}
else if (addons->kind == MIDORI_ADDON_USER_STYLES &&
!_js_style_from_file (js_context, fullname, &exception))
{
message = g_strdup_printf ("console.error ('%s');", exception);
gjs_script_eval (js_context, message, NULL);
g_free (message);
g_free (exception);
}
elements = g_slist_next (elements);
}
}
/**
* midori_addons_new:
* @kind: the kind of addon
* @web_widget: a web widget
*
* Creates a new addons widget.
*
* @web_widget can be one of the following:
* %MidoriBrowser, %MidoriWebView, %WebKitWebView
*
* Return value: a new #MidoriAddons
**/
GtkWidget*
midori_addons_new (MidoriAddonKind kind,
GtkWidget* web_widget)
{
MidoriAddons* addons;
#if GLIB_CHECK_VERSION (2, 16, 0)
GSList* directories;
GSList* list;
GFile* directory;
GError* error;
GFileMonitor* monitor;
#endif
g_return_val_if_fail (GTK_IS_WIDGET (web_widget), NULL);
addons = g_object_new (MIDORI_TYPE_ADDONS,
"kind", kind,
"web-widget", web_widget,
NULL);
if (kind == MIDORI_ADDON_USER_SCRIPTS || kind == MIDORI_ADDON_USER_STYLES)
g_signal_connect (addons->web_widget, "context-ready",
G_CALLBACK (midori_web_widget_context_ready_cb), addons);
midori_addons_update_elements (addons);
#if GLIB_CHECK_VERSION (2, 16, 0)
directories = _addons_get_directories (addons);
list = directories;
while (directories)
{
directory = g_file_new_for_path (directories->data);
directories = g_slist_next (directories);
error = NULL;
monitor = g_file_monitor_directory (directory,
G_FILE_MONITOR_NONE,
NULL, &error);
if (!monitor)
{
g_warning ("could not monitor %s: %s", g_file_get_parse_name (directory),
error->message);
g_error_free (error);
}
g_signal_connect (monitor, "changed",
G_CALLBACK (midori_addons_directory_monitor_changed), addons);
}
g_slist_free (list);
#endif
return GTK_WIDGET (addons);
}
/**
* midori_addons_get_toolbar:
* @addons: a #MidoriAddons
*
* Retrieves the toolbar of the addons. A new widget
* is created on the first call of this function.
*
* Return value: a toolbar widget
*
* Deprecated: 0.1.2: Use midori_viewable_get_toolbar() instead.
**/
GtkWidget*
midori_addons_get_toolbar (MidoriViewable* addons)
{
GtkWidget* toolbar;
GtkToolItem* toolitem;
g_return_val_if_fail (MIDORI_IS_ADDONS (addons), NULL);
if (!MIDORI_ADDONS (addons)->toolbar)
{
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);
toolitem = gtk_tool_item_new ();
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
gtk_widget_show (GTK_WIDGET (toolitem));
/* enable button */
toolitem = gtk_tool_button_new_from_stock (GTK_STOCK_YES);
gtk_tool_button_set_label (GTK_TOOL_BUTTON (toolitem), _("_Enable"));
g_signal_connect (toolitem, "clicked",
G_CALLBACK (midori_addons_button_status_clicked_cb), addons);
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
gtk_widget_set_sensitive (GTK_WIDGET (toolitem), FALSE);
gtk_widget_show (GTK_WIDGET (toolitem));
/* disable button */
toolitem = gtk_tool_button_new_from_stock (GTK_STOCK_NO);
gtk_tool_button_set_label (GTK_TOOL_BUTTON (toolitem), _("_Disable"));
g_signal_connect (toolitem, "clicked",
G_CALLBACK (midori_addons_button_status_clicked_cb), addons);
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
gtk_widget_set_sensitive (GTK_WIDGET (toolitem), FALSE);
gtk_widget_show (GTK_WIDGET (toolitem));
/* separator */
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));
/* add button */
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));
MIDORI_ADDONS (addons)->toolbar = toolbar;
g_signal_connect (MIDORI_ADDONS (addons)->treeview, "cursor-changed",
G_CALLBACK (midori_addons_treeview_cursor_changed),
addons);
g_signal_connect (toolbar, "destroy",
G_CALLBACK (gtk_widget_destroyed),
&MIDORI_ADDONS (addons)->toolbar);
}
return MIDORI_ADDONS (addons)->toolbar;
}
/**
* midori_addons_update_elements:
* @addons: a #MidoriAddons
*
* Updates all addons elements (file paths and metadata).
*
**/
void
midori_addons_update_elements (MidoriAddons* addons)
{
GTree* disabled;
GSList* elements;
gboolean broken;
gchar* fullname;
gchar* displayname;
gchar* name;
gchar* description;
GSList* includes;
GSList* excludes;
GtkListStore* liststore;
GtkTreeIter iter;
GSList* addon_files;
GSList* list;
struct AddonElement* element;
g_return_if_fail (MIDORI_IS_ADDONS (addons));
g_return_if_fail (addons->kind != MIDORI_ADDON_NONE);
/* FIXME: would GHashTable be better? */
disabled = g_tree_new ((GCompareFunc)strcmp);
elements = addons->elements;
while (elements)
{
element = elements->data;
if (!element->enabled)
g_tree_insert (disabled, element->fullpath, NULL);
elements = g_slist_next (elements);
}
g_slist_free (addons->elements);
addons->elements = NULL;
liststore = gtk_list_store_new (3, G_TYPE_POINTER,
G_TYPE_INT,
G_TYPE_STRING);
addon_files = _addons_get_files (addons);
list = addon_files;
while (addon_files)
{
fullname = addon_files->data;
displayname = g_filename_display_basename (fullname);
description = NULL;
includes = NULL;
excludes = NULL;
broken = FALSE;
if (addons->kind == MIDORI_ADDON_USER_SCRIPTS)
{
name = NULL;
if (!_metadata_from_file (fullname, &includes, &excludes,
&name, &description))
broken = TRUE;
if (name)
{
g_free (displayname);
displayname = name;
}
}
element = g_new (struct AddonElement, 1);
element->name = displayname;
element->description = description;
element->fullpath = fullname;
if (g_tree_lookup_extended (disabled, fullname, NULL, NULL))
element->enabled = FALSE;
else
element->enabled = TRUE;
element->broken = broken;
element->includes = includes;
element->excludes = excludes;
addons->elements = g_slist_prepend (addons->elements, element);
gtk_list_store_append (liststore, &iter);
gtk_list_store_set (liststore, &iter,
0, element, 1, 0, 2, "", -1);
addon_files = g_slist_next (addon_files);
}
addons->elements = g_slist_reverse (addons->elements);
g_tree_destroy (disabled);
g_slist_free (list);
gtk_tree_view_set_model (GTK_TREE_VIEW (addons->treeview),
GTK_TREE_MODEL (liststore));
/* In case a row was selected, that selection will be cancelled
when calling gtk_tree_view_set_model. So, we need to make sure
that the buttons are insensitive. */
if (addons->toolbar)
{
_addons_toggle_enable_button (addons, FALSE);
_addons_toggle_disable_button (addons, FALSE);
}
}