Implement navigation history backed by sqlite

The implementation relies on KatzeArray for the
most part and only does the storage with sqlite
behind the scenes. The change includes a working
History panel. Changes to the database are
committed in realtime.
This commit is contained in:
Dale Whittaker 2008-10-07 02:19:33 +02:00 committed by Christian Dywan
parent a13f8b64b3
commit d445745787
10 changed files with 982 additions and 20 deletions

View file

@ -83,6 +83,12 @@ AC_SUBST(LIBXML_CFLAGS)
AC_SUBST(LIBXML_LIBS)
AC_DEFINE_UNQUOTED(HAVE_LIBXML,$have_libxml, [Whether LibXML is available])
# Check for sqlite 3
PKG_CHECK_MODULES([SQLITE3], [sqlite3 >= 3.0], have_sqlite3=1, have_sqlite3=0)
AC_SUBST([SQLITE3_CFLAGS])
AC_SUBST([SQLITE3_LIBS])
AC_DEFINE_UNQUOTED(HAVE_SQLITE3,$have_sqlite3, [Whether sqlite3 is available])
# i18n
GETTEXT_PACKAGE=midori
AC_SUBST(GETTEXT_PACKAGE)

View file

@ -34,7 +34,9 @@ enum
PROP_TEXT,
PROP_URI,
PROP_ICON,
PROP_TOKEN
PROP_TOKEN,
PROP_ADDED,
PROP_VISITS
};
static void
@ -109,6 +111,27 @@ katze_item_class_init (KatzeItemClass* class)
_("The token of the item"),
NULL,
flags));
g_object_class_install_property (gobject_class,
PROP_ADDED,
g_param_spec_string (
"added",
_("Added"),
_("When the item was added"),
NULL,
flags));
g_object_class_install_property (gobject_class,
PROP_VISITS,
g_param_spec_int (
"visits",
_("Visits"),
_("The number of visits of the item"),
G_MININT,
G_MAXINT,
0,
flags));
}
@ -129,6 +152,7 @@ katze_item_finalize (GObject* object)
g_free (item->uri);
g_free (item->icon);
g_free (item->token);
g_free (item->added);
G_OBJECT_CLASS (katze_item_parent_class)->finalize (object);
}
@ -158,6 +182,12 @@ katze_item_set_property (GObject* object,
case PROP_TOKEN:
item->token = g_value_dup_string (value);
break;
case PROP_ADDED:
item->added = g_value_dup_string (value);
break;
case PROP_VISITS:
item->visits = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -189,6 +219,12 @@ katze_item_get_property (GObject* object,
case PROP_TOKEN:
g_value_set_string (value, item->token);
break;
case PROP_ADDED:
g_value_set_string (value, item->added);
break;
case PROP_VISITS:
g_value_set_int (value, item->visits);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -375,6 +411,72 @@ katze_item_set_token (KatzeItem* item,
g_object_notify (G_OBJECT (item), "token");
}
/**
* katze_item_get_added:
* @item: a #KatzeItem
*
* Determines when @item was added.
*
* Return value: a timestamp
**/
const gchar*
katze_item_get_added (KatzeItem* item)
{
g_return_val_if_fail (KATZE_IS_ITEM (item), NULL);
return item->added;
}
/**
* katze_item_set_added:
* @item: a #KatzeItem
* @added: a timestamp
*
* Sets when @item was added.
**/
void
katze_item_set_added (KatzeItem* item,
const gchar* added)
{
g_return_if_fail (KATZE_IS_ITEM (item));
katze_assign (item->added, g_strdup (added));
g_object_notify (G_OBJECT (item), "added");
}
/**
* katze_item_get_visits:
* @item: a #KatzeItem
*
* Retrieves the number of visits of @item.
*
* Return value: the number of visits
**/
gint
katze_item_get_visits (KatzeItem* item)
{
g_return_val_if_fail (KATZE_IS_ITEM (item), -1);
return item->visits;
}
/**
* katze_item_set_visits:
* @item: a #KatzeItem
* @visits: an integer
*
* Sets the number of visits of @item.
**/
void
katze_item_set_visits (KatzeItem* item,
gint visits)
{
g_return_if_fail (KATZE_IS_ITEM (item));
item->visits = visits;
g_object_notify (G_OBJECT (item), "visits");
}
/**
* katze_item_get_parent:
* @item: a #KatzeItem
@ -412,3 +514,4 @@ katze_item_set_parent (KatzeItem* item,
katze_object_assign (item->parent, parent);
/* g_object_notify (G_OBJECT (item), "parent"); */
}

View file

@ -41,6 +41,8 @@ struct _KatzeItem
gchar* uri;
gchar* icon;
gchar* token;
gchar* added;
gint visits;
KatzeItem* parent;
};
@ -91,6 +93,20 @@ void
katze_item_set_token (KatzeItem* item,
const gchar* token);
const gchar*
katze_item_get_added (KatzeItem* item);
void
katze_item_set_added (KatzeItem* item,
const gchar* added);
gint
katze_item_get_visits (KatzeItem* item);
void
katze_item_set_visits (KatzeItem* item,
gint visits);
gpointer
katze_item_get_parent (KatzeItem* item);

View file

@ -29,6 +29,7 @@ G_DEFINE_TYPE (KatzeList, katze_list, KATZE_TYPE_ITEM)
enum {
ADD_ITEM,
REMOVE_ITEM,
CLEAR,
LAST_SIGNAL
};
@ -52,6 +53,23 @@ _katze_list_remove_item (KatzeList* list,
list->items = g_list_remove (list->items, item);
}
static void
_katze_list_clear (KatzeList* list)
{
guint n;
guint i;
GObject* item;
n = g_list_length (list->items);
for (i = 0; i < n; i++)
{
if ((item = g_list_nth_data (list->items, i)))
katze_list_remove_item (list, item);
}
g_list_free (list->items);
list->items = NULL;
}
static void
katze_list_class_init (KatzeListClass* class)
{
@ -79,11 +97,23 @@ katze_list_class_init (KatzeListClass* class)
G_TYPE_NONE, 1,
G_TYPE_POINTER);
signals[CLEAR] = g_signal_new (
"clear",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
G_STRUCT_OFFSET (KatzeListClass, clear),
0,
NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
gobject_class = G_OBJECT_CLASS (class);
gobject_class->finalize = katze_list_finalize;
class->add_item = _katze_list_add_item;
class->remove_item = _katze_list_remove_item;
class->clear = _katze_list_clear;
}
static void
@ -227,18 +257,7 @@ katze_list_get_length (KatzeList* list)
void
katze_list_clear (KatzeList* list)
{
guint n;
guint i;
GObject* item;
g_return_if_fail (KATZE_IS_LIST (list));
n = g_list_length (list->items);
for (i = 0; i < n; i++)
{
if ((item = g_list_nth_data (list->items, i)))
katze_list_remove_item (list, item);
}
g_list_free (list->items);
list->items = NULL;
g_signal_emit (list, signals[CLEAR], 0);
}

View file

@ -50,6 +50,9 @@ struct _KatzeListClass
void
(*remove_item) (KatzeList* list,
gpointer item);
void
(*clear) (KatzeList* list);
};
GType

View file

@ -1,5 +1,6 @@
/*
Copyright (C) 2007-2008 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2008 Dale Whittaker <dayul@users.sf.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@ -27,11 +28,21 @@
#include <gtk/gtk.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <sqlite3.h>
#if ENABLE_NLS
#include <libintl.h>
#endif
#define MIDORI_HISTORY_ERROR g_quark_from_string("MIDORI_HISTORY_ERROR")
typedef enum
{
MIDORI_HISTORY_ERROR_DB_OPEN, /* Error opening the database file */
MIDORI_HISTORY_ERROR_EXEC_SQL, /* Error executing SQL statement */
} MidoriHistoryError;
static void
stock_items_init (void)
{
@ -564,6 +575,283 @@ katze_array_from_file (KatzeArray* array,
return TRUE;
}
/* Open database 'dbname' */
static sqlite3*
db_open (const char* dbname,
GError** error)
{
sqlite3* db;
if (sqlite3_open (dbname, &db))
{
if (error)
{
*error = g_error_new (MIDORI_HISTORY_ERROR,
MIDORI_HISTORY_ERROR_DB_OPEN,
_("Error opening database: %s\n"),
sqlite3_errmsg (db));
}
sqlite3_close (db);
return NULL;
}
return (db);
}
/* Close database 'db' */
static void
db_close (sqlite3* db)
{
sqlite3_close (db);
}
/* Execute an SQL statement and run 'callback' on the result data */
static gboolean
db_exec_callback (sqlite3* db,
const char* sqlcmd,
int (*callback)(void*, int, char**, char**),
void* cbarg,
GError** error)
{
char* errmsg;
if (sqlite3_exec (db, sqlcmd, callback, cbarg, &errmsg) != SQLITE_OK)
{
if (error)
{
*error = g_error_new (MIDORI_HISTORY_ERROR,
MIDORI_HISTORY_ERROR_EXEC_SQL,
_("Error opening database: %s\n"),
errmsg);
}
sqlite3_free (errmsg);
return FALSE;
}
return TRUE;
}
/* Execute a SQL statement */
static gboolean
db_exec (sqlite3* db,
const char* sqlcmd,
GError** error)
{
return (db_exec_callback (db, sqlcmd, NULL, NULL, error));
}
/* sqlite method for retrieving the date/ time */
static int
gettimestr (void* data,
int argc,
char** argv,
char** colname)
{
KatzeItem* item = KATZE_ITEM (data);
(void) colname;
g_return_val_if_fail (argc == 1, 1);
katze_item_set_added (item, argv[0]);
return 0;
}
static void
midori_history_remove_item_cb (KatzeArray* history,
KatzeItem* item,
sqlite3* db)
{
gchar* sqlcmd;
gboolean success = TRUE;
GError* error = NULL;
g_return_if_fail (KATZE_IS_ITEM (item));
sqlcmd = g_strdup_printf ("DELETE FROM history WHERE uri = '%s' AND"
" title = '%s' AND date = '%s' AND visits = %d",
katze_item_get_uri (item),
katze_item_get_name (item),
katze_item_get_added (item),
katze_item_get_visits (item));
success = db_exec (db, sqlcmd, &error);
if (!success)
{
g_printerr (_("Failed to remove history item. %s\n"), error->message);
g_error_free (error);
return ;
}
g_free (sqlcmd);
}
static void
midori_history_clear_before_cb (KatzeArray* item,
sqlite3* db)
{
g_signal_handlers_block_by_func (item, midori_history_remove_item_cb, db);
}
static void
midori_history_clear_cb (KatzeArray* history,
sqlite3* db)
{
GError* error = NULL;
g_return_if_fail (KATZE_IS_ARRAY (history));
if (!db_exec (db, "DELETE FROM history", &error))
{
g_printerr (_("Failed to clear history. %s\n"), error->message);
g_error_free (error);
}
}
static void
midori_history_add_item_cb (KatzeArray* array,
KatzeItem* item,
sqlite3* db)
{
gchar* sqlcmd;
gboolean success = TRUE;
GError* error = NULL;
g_return_if_fail (KATZE_IS_ITEM (item));
if (KATZE_IS_ARRAY (item))
{
g_signal_connect_after (item, "add-item",
G_CALLBACK (midori_history_add_item_cb), db);
g_signal_connect (item, "remove-item",
G_CALLBACK (midori_history_remove_item_cb), db);
g_signal_connect (item, "clear",
G_CALLBACK (midori_history_clear_before_cb), db);
return;
}
/* New item, set added to the current date/ time */
if (!katze_item_get_added (item))
{
if (!db_exec_callback (db, "SELECT datetime('now')",
gettimestr, item, &error))
{
g_printerr (_("Failed to add history item. %s\n"), error->message);
g_error_free (error);
return;
}
}
sqlcmd = g_strdup_printf ("INSERT INTO history VALUES"
"('%s', '%s', '%s', %d)",
katze_item_get_uri (item),
katze_item_get_name (item),
katze_item_get_added (item),
katze_item_get_visits (item));
success = db_exec (db, sqlcmd, &error);
g_free (sqlcmd);
if (!success)
{
g_printerr (_("Failed to add history item. %s\n"), error->message);
g_error_free (error);
return ;
}
}
static int
midori_history_add_items (void* data,
int argc,
char** argv,
char** colname)
{
KatzeItem* item;
KatzeArray* parent = NULL;
KatzeArray* array = KATZE_ARRAY (data);
gchar* newdate;
gint i, j, n;
gint ncols = 4;
gsize len;
g_return_val_if_fail (KATZE_IS_ARRAY (array), 1);
/* Test whether have the right number of columns */
g_return_val_if_fail (argc % ncols == 0, 1);
for (i = 0; i <= (argc - ncols); i++)
{
if (argv[i])
{
if (colname[i] && g_ascii_strcasecmp (colname[i], "uri") == 0 &&
colname[i + 1] && g_ascii_strcasecmp (colname[i + 1], "title") == 0 &&
colname[i + 2] && g_ascii_strcasecmp (colname[i + 2], "date") == 0 &&
colname[i + 3] && g_ascii_strcasecmp (colname[i + 3], "visits") == 0)
{
item = katze_item_new ();
katze_item_set_uri (item, argv[i]);
katze_item_set_name (item, argv[i + 1]);
katze_item_set_added (item, argv[i + 2]);
katze_item_set_visits (item, atoi (argv[i + 3]));
len = (g_strrstr (argv[i + 2], " ") - argv[i + 2]);
newdate = g_strndup (argv[i + 2], len);
n = katze_array_get_length (array);
for (j = 0; j < n; j++)
{
parent = katze_array_get_nth_item (array, j);
if (newdate && g_ascii_strcasecmp
(katze_item_get_added (KATZE_ITEM (parent)), newdate) == 0)
break;
}
if (j == n)
{
parent = katze_array_new (KATZE_TYPE_ARRAY);
katze_item_set_added (KATZE_ITEM (parent), newdate);
katze_array_add_item (array, parent);
}
g_free (newdate);
katze_array_add_item (parent, item);
}
}
}
return 0;
}
static sqlite3*
midori_history_initialize (KatzeArray* array,
const gchar* filename,
GError** error)
{
sqlite3* db;
KatzeItem* item;
gint i, n;
if ((db = db_open (filename, error)) == NULL)
return db;
if (!db_exec (db,
"CREATE TABLE IF NOT EXISTS "
"history(uri text, title text, date text, visits integer)",
error))
return NULL;
if (!db_exec_callback (db,
"SELECT uri, title, date, visits FROM history "
"ORDER BY strftime('%s', date) ASC",
midori_history_add_items,
array,
error))
return NULL;
n = katze_array_get_length (array);
for (i = 0; i < n; i++)
{
item = katze_array_get_nth_item (array, i);
g_signal_connect_after (item, "add-item",
G_CALLBACK (midori_history_add_item_cb), db);
g_signal_connect (item, "remove-item",
G_CALLBACK (midori_history_remove_item_cb), db);
g_signal_connect (item, "clear",
G_CALLBACK (midori_history_clear_before_cb), db);
}
return db;
}
static gchar*
_simple_xml_element (const gchar* name,
const gchar* value)
@ -756,6 +1044,7 @@ main (int argc,
gchar* homepage;
KatzeArray* search_engines;
KatzeArray* bookmarks;
KatzeArray* history;
KatzeArray* _session;
KatzeArray* trash;
MidoriBrowser* browser;
@ -764,6 +1053,7 @@ main (int argc,
gchar* uri;
KatzeItem* item;
gchar* uri_ready;
sqlite3* db;
#if ENABLE_NLS
bindtextdomain (GETTEXT_PACKAGE, MIDORI_LOCALEDIR);
@ -910,6 +1200,16 @@ main (int argc,
g_error_free (error);
}
g_free (config_file);
config_file = g_build_filename (config_path, "history.db", NULL);
history = katze_array_new (KATZE_TYPE_ARRAY);
error = NULL;
if ((db = midori_history_initialize (history, config_file, &error)) == NULL)
{
g_string_append_printf (error_messages,
_("The history couldn't be loaded. %s\n"), error->message);
g_error_free (error);
}
g_free (config_file);
/* In case of errors */
if (error_messages->len)
@ -943,6 +1243,7 @@ main (int argc,
g_object_unref (bookmarks);
g_object_unref (_session);
g_object_unref (trash);
g_object_unref (history);
g_string_free (error_messages, TRUE);
return 0;
}
@ -987,11 +1288,16 @@ main (int argc,
g_signal_connect_after (trash, "add-item",
G_CALLBACK (midori_web_list_add_item_cb), NULL);
g_signal_connect_after (history, "add-item",
G_CALLBACK (midori_history_add_item_cb), db);
g_signal_connect_after (history, "clear",
G_CALLBACK (midori_history_clear_cb), db);
g_object_set (app, "settings", settings,
"bookmarks", bookmarks,
"trash", trash,
"search-engines", search_engines,
"history", history,
NULL);
browser = g_object_new (MIDORI_TYPE_BROWSER,
@ -999,6 +1305,7 @@ main (int argc,
"bookmarks", bookmarks,
"trash", trash,
"search-engines", search_engines,
"history", history,
NULL);
midori_app_add_browser (app, browser);
gtk_widget_show (GTK_WIDGET (browser));
@ -1059,6 +1366,8 @@ main (int argc,
config_path = g_build_filename (g_get_user_config_dir (), PACKAGE_NAME,
NULL);
g_mkdir_with_parents (config_path, 0755);
g_object_unref (history);
db_close (db);
config_file = g_build_filename (config_path, "search", NULL);
error = NULL;
if (!search_engines_save_to_file (search_engines, config_file, &error))

View file

@ -35,6 +35,7 @@ struct _MidoriApp
KatzeArray* bookmarks;
KatzeArray* trash;
KatzeArray* search_engines;
KatzeArray* history;
gpointer instance;
};
@ -52,6 +53,7 @@ enum
PROP_TRASH,
PROP_SEARCH_ENGINES,
PROP_BROWSER,
PROP_HISTORY,
PROP_BROWSER_COUNT
};
@ -172,6 +174,17 @@ midori_app_class_init (MidoriAppClass* class)
_("The current number of browsers"),
0, G_MAXUINT, 0,
G_PARAM_READABLE));
g_object_class_install_property (gobject_class,
PROP_HISTORY,
g_param_spec_object (
"history",
_("History"),
_("The list of history items"),
KATZE_TYPE_ARRAY,
G_PARAM_READWRITE));
}
static GObject*
@ -212,6 +225,7 @@ midori_browser_message_received_cb (UniqueApp* instance,
"bookmarks", app->bookmarks,
"trash", app->trash,
"search-engines", app->search_engines,
"history", app->history,
NULL);
/* FIXME: Should open the homepage according to settings */
midori_browser_add_uri (browser, "about:blank");
@ -267,6 +281,7 @@ midori_app_init (MidoriApp* app)
app->bookmarks = NULL;
app->trash = NULL;
app->search_engines = NULL;
app->history = NULL;
#if HAVE_UNIQUE
display_name = g_strdup (gdk_display_get_name (gdk_display_get_default ()));
@ -301,6 +316,8 @@ midori_app_finalize (GObject* object)
g_object_unref (app->trash);
if (app->search_engines)
g_object_unref (app->search_engines);
if (app->history)
g_object_unref (app->history);
if (app->instance)
g_object_unref (app->instance);
@ -338,6 +355,10 @@ midori_app_set_property (GObject* object,
g_object_ref (app->search_engines);
/* FIXME: Propagate search engines to all browsers */
break;
case PROP_HISTORY:
katze_object_assign (app->history, g_value_get_object (value));
/* FIXME: Propagate history to all browsers */
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -366,6 +387,9 @@ midori_app_get_property (GObject* object,
case PROP_SEARCH_ENGINES:
g_value_set_object (value, app->search_engines);
break;
case PROP_HISTORY:
g_value_set_object (value, app->history);
break;
case PROP_BROWSER:
g_value_set_object (value, app->browser);
break;
@ -398,6 +422,7 @@ midori_browser_new_window_cb (MidoriBrowser* browser,
"bookmarks", app->bookmarks,
"trash", app->trash,
"search-engines", app->search_engines,
"history", app->history,
NULL);
midori_browser_add_uri (new_browser, uri);
gtk_widget_show (GTK_WIDGET (new_browser));

View file

@ -1,5 +1,6 @@
/*
Copyright (C) 2007-2008 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2008 Dale Whittaker <dayul@users.sf.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@ -47,6 +48,7 @@ struct _MidoriBrowser
GtkWidget* menu_tools;
GtkWidget* menu_window;
GtkWidget* popup_bookmark;
GtkWidget* popup_history;
GtkWidget* throbber;
GtkWidget* navigationbar;
GtkWidget* button_tab_new;
@ -58,6 +60,7 @@ struct _MidoriBrowser
GtkWidget* panel;
GtkWidget* panel_bookmarks;
GtkWidget* panel_history;
GtkWidget* panel_console;
GtkWidget* panel_pageholder;
GtkWidget* notebook;
@ -77,6 +80,7 @@ struct _MidoriBrowser
KatzeArray* proxy_array;
KatzeArray* trash;
KatzeArray* search_engines;
KatzeArray* history;
};
G_DEFINE_TYPE (MidoriBrowser, midori_browser, GTK_TYPE_WINDOW)
@ -94,7 +98,8 @@ enum
PROP_SETTINGS,
PROP_BOOKMARKS,
PROP_TRASH,
PROP_SEARCH_ENGINES
PROP_SEARCH_ENGINES,
PROP_HISTORY
};
enum
@ -130,7 +135,9 @@ midori_browser_get_property (GObject* object,
GValue* value,
GParamSpec* pspec);
static void
midori_browser_new_history_item (MidoriBrowser* browser,
KatzeItem* item);
static GtkAction*
_action_by_name (MidoriBrowser* browser,
@ -435,12 +442,24 @@ midori_view_notify_title_cb (GtkWidget* view,
const gchar* title;
GtkAction* action;
gchar* window_title;
KatzeItem* item;
uri = midori_view_get_display_uri (MIDORI_VIEW (view));
title = midori_view_get_display_title (MIDORI_VIEW (view));
action = _action_by_name (browser, "Location");
midori_location_action_set_title_for_uri (
MIDORI_LOCATION_ACTION (action), title, uri);
if (midori_view_get_load_status (MIDORI_VIEW (view)) == MIDORI_LOAD_COMMITTED)
{
if (!browser->history)
return;
item = katze_item_new ();
katze_item_set_uri (item, uri);
katze_item_set_name (item, title);
katze_item_set_visits (item, -1);
midori_browser_new_history_item (browser, item);
}
if (view == midori_browser_get_current_tab (browser))
{
@ -1094,6 +1113,23 @@ midori_browser_class_init (MidoriBrowserClass* class)
_("The list of search engines to be used for web search"),
KATZE_TYPE_ARRAY,
G_PARAM_READWRITE));
/**
* MidoriBrowser:history:
*
* The list of history items.
*
* This is actually a reference to a history instance,
* so if history should be used it must be initially set.
*/
g_object_class_install_property (gobject_class,
PROP_HISTORY,
g_param_spec_object (
"history",
_("History"),
_("The list of history items"),
KATZE_TYPE_ARRAY,
G_PARAM_READWRITE));
}
static void
@ -1941,6 +1977,98 @@ midori_panel_bookmarks_popup_menu_cb (GtkWidget* widget,
}
}
static void
midori_panel_history_row_activated_cb (GtkTreeView* treeview,
GtkTreePath* path,
GtkTreeViewColumn* column,
MidoriBrowser* browser)
{
KatzeItem* item;
GtkTreeModel* model;
GtkTreeIter iter;
const gchar* uri;
model = gtk_tree_view_get_model (treeview);
if (gtk_tree_model_get_iter (model, &iter, path))
{
gtk_tree_model_get (model, &iter, 0, &item, -1);
if (KATZE_IS_ITEM (item))
{
uri = katze_item_get_uri (item);
_midori_browser_open_uri (browser, uri);
}
g_object_unref (item);
}
}
static void
_midori_panel_history_popup (GtkWidget* widget,
GdkEventButton* event,
KatzeItem* item,
MidoriBrowser* browser)
{
gboolean is_history_item = (KATZE_IS_ITEM (item) && !KATZE_IS_ARRAY (item));
_action_set_sensitive (browser, "HistoryOpen", is_history_item);
_action_set_sensitive (browser, "HistoryOpenTab", is_history_item);
sokoke_widget_popup (widget, GTK_MENU (browser->popup_history),
event, SOKOKE_MENU_POSITION_CURSOR);
}
static gboolean
midori_panel_history_button_release_event_cb (GtkWidget* widget,
GdkEventButton* event,
MidoriBrowser* browser)
{
GtkTreeModel* model;
GtkTreeIter iter;
KatzeItem* item;
const gchar* uri;
gint n;
if (event->button != 2 && event->button != 3)
return FALSE;
if (sokoke_tree_view_get_selected_iter (GTK_TREE_VIEW (widget),
&model, &iter))
{
gtk_tree_model_get (model, &iter, 0, &item, -1);
uri = katze_item_get_uri (item);
if (event->button == 2)
{
if (uri && *uri)
{
n = midori_browser_add_uri (browser, uri);
midori_browser_set_current_page (browser, n);
}
}
else
_midori_panel_history_popup (widget, event, item, browser);
g_object_unref (item);
return TRUE;
}
return FALSE;
}
static void
midori_panel_history_popup_menu_cb (GtkWidget* widget,
MidoriBrowser* browser)
{
GtkTreeModel* model;
GtkTreeIter iter;
KatzeItem* item;
if (sokoke_tree_view_get_selected_iter (GTK_TREE_VIEW (widget),
&model, &iter))
{
gtk_tree_model_get (model, &iter, 0, &item, -1);
_midori_panel_history_popup (widget, NULL, item, browser);
g_object_unref (item);
}
}
static void
_tree_store_insert_folder (GtkTreeStore* treestore,
GtkTreeIter* parent,
@ -2131,6 +2259,70 @@ _action_bookmark_add_activate (GtkAction* action,
midori_browser_edit_bookmark_dialog_new (browser, NULL);
}
static void
midori_browser_history_render_icon_cb (GtkTreeViewColumn* column,
GtkCellRenderer* renderer,
GtkTreeModel* model,
GtkTreeIter* iter,
GtkWidget* treeview)
{
KatzeItem* item;
GdkPixbuf* pixbuf = NULL;
gtk_tree_model_get (model, iter, 0, &item, -1);
if (G_UNLIKELY (!item))
return;
if (G_UNLIKELY (!katze_item_get_parent (item)))
{
gtk_tree_store_remove (GTK_TREE_STORE (model), iter);
g_object_unref (item);
return;
}
if (KATZE_IS_ARRAY (item))
pixbuf = gtk_widget_render_icon (treeview, GTK_STOCK_DIRECTORY,
GTK_ICON_SIZE_MENU, NULL);
else
pixbuf = gtk_widget_render_icon (treeview, GTK_STOCK_FILE,
GTK_ICON_SIZE_MENU, NULL);
g_object_set (renderer, "pixbuf", pixbuf, NULL);
if (pixbuf)
g_object_unref (pixbuf);
g_object_unref (item);
}
static void
midori_browser_history_render_text_cb (GtkTreeViewColumn* column,
GtkCellRenderer* renderer,
GtkTreeModel* model,
GtkTreeIter* iter,
GtkWidget* treeview)
{
KatzeItem* item;
gtk_tree_model_get (model, iter, 0, &item, -1);
if (G_UNLIKELY (!item))
return;
if (G_UNLIKELY (!katze_item_get_parent (item)))
{
gtk_tree_store_remove (GTK_TREE_STORE (model), iter);
g_object_unref (item);
return;
}
if (KATZE_IS_ARRAY (item))
g_object_set (renderer, "text", katze_item_get_added (item), NULL);
else
g_object_set (renderer, "text", katze_item_get_name (item), NULL);
g_object_unref (item);
}
static void
_action_manage_search_engines_activate (GtkAction* action,
MidoriBrowser* browser)
@ -2419,6 +2611,120 @@ _action_bookmark_edit_activate (GtkAction* action,
}
}
static void
_action_history_open_activate (GtkAction* action,
MidoriBrowser* browser)
{
GtkTreeView* tree_view;
GtkTreeModel* model;
GtkTreeIter iter;
KatzeItem* item;
const gchar* uri;
tree_view = GTK_TREE_VIEW (browser->panel_history);
if (sokoke_tree_view_get_selected_iter (tree_view, &model, &iter))
{
gtk_tree_model_get (model, &iter, 0, &item, -1);
uri = katze_item_get_uri (item);
if (uri && *uri)
_midori_browser_open_uri (browser, uri);
g_object_unref (item);
}
}
static void
_action_history_open_tab_activate (GtkAction* action,
MidoriBrowser* browser)
{
GtkTreeView* tree_view;
GtkTreeModel* model;
GtkTreeIter iter;
KatzeItem* item;
const gchar* uri;
gint n;
tree_view = GTK_TREE_VIEW (browser->panel_history);
if (sokoke_tree_view_get_selected_iter (tree_view, &model, &iter))
{
gtk_tree_model_get (model, &iter, 0, &item, -1);
uri = katze_item_get_uri (item);
if (uri && *uri)
{
n = midori_browser_add_item (browser, item);
_midori_browser_set_current_page_smartly (browser, n);
}
g_object_unref (item);
}
}
static void
_action_history_delete_activate (GtkAction* action,
MidoriBrowser* browser)
{
GtkTreeView* treeview;
GtkTreeModel* treemodel;
GtkTreeIter iter;
GtkTreeIter childiter;
KatzeItem* item;
KatzeItem* child;
KatzeArray* parent;
gint i, n;
treeview = GTK_TREE_VIEW (browser->panel_history);
if (sokoke_tree_view_get_selected_iter (treeview, &treemodel, &iter))
{
gtk_tree_model_get (treemodel, &iter, 0, &item, -1);
if (KATZE_IS_ARRAY (item))
{
n = katze_array_get_length (KATZE_ARRAY (item));
for (i = 0; i < n; i++)
{
child = katze_array_get_nth_item (KATZE_ARRAY (item), 0);
katze_array_remove_item (KATZE_ARRAY (item), child);
}
parent = katze_item_get_parent (item);
katze_array_remove_item (parent, item);
while (gtk_tree_model_iter_nth_child (treemodel, &childiter, &iter, 0))
gtk_tree_store_remove (GTK_TREE_STORE (treemodel), &childiter);
gtk_tree_store_remove (GTK_TREE_STORE (treemodel), &iter);
g_object_unref (item);
}
else
{
parent = katze_item_get_parent (item);
katze_array_remove_item (parent, item);
gtk_tree_store_remove (GTK_TREE_STORE (treemodel), &iter);
g_object_unref (item);
}
}
}
static void
_action_history_clear_activate (GtkAction* action,
MidoriBrowser* browser)
{
GtkTreeView* tree_view;
GtkTreeStore* store;
KatzeItem* item;
gint i, n;
if (!browser->history)
return;
tree_view = GTK_TREE_VIEW (browser->panel_history);
store = GTK_TREE_STORE (gtk_tree_view_get_model (tree_view));
gtk_tree_store_clear (store);
n = katze_array_get_length (browser->history);
for (i = 0; i < n; i++)
{
item = katze_array_get_nth_item (browser->history, i);
katze_array_clear (KATZE_ARRAY (item));
}
katze_array_clear (browser->history);
}
static void
_action_undo_tab_close_activate (GtkAction* action,
MidoriBrowser* browser)
@ -2601,7 +2907,18 @@ static const GtkActionEntry entries[] = {
{ "BookmarkDelete", GTK_STOCK_DELETE,
NULL, "",
N_("Delete the selected bookmark"), G_CALLBACK (_action_bookmark_delete_activate) },
{ "HistoryDelete", GTK_STOCK_DELETE,
NULL, "",
N_("Delete the selected history item"), G_CALLBACK (_action_history_delete_activate) },
{ "HistoryClear", GTK_STOCK_CLEAR,
NULL, "",
N_("Clear the enitre history"), G_CALLBACK (_action_history_clear_activate) },
{ "HistoryOpen", GTK_STOCK_OPEN,
NULL, "",
N_("Open the selected history item"), G_CALLBACK (_action_history_open_activate) },
{ "HistoryOpenTab", STOCK_TAB_NEW,
N_("Open in New _Tab"), "",
N_("Open the selected history item in a new tab"), G_CALLBACK (_action_history_open_tab_activate) },
{ "Tools", NULL, N_("_Tools") },
{ "ManageSearchEngines", GTK_STOCK_PROPERTIES,
N_("_Manage Search Engines"), "<Ctrl><Alt>s",
@ -2822,6 +3139,16 @@ static const gchar* ui_markup =
"<menuitem action='BookmarkEdit'/>"
"<menuitem action='BookmarkDelete'/>"
"</popup>"
"<toolbar name='toolbar_history'>"
"<toolitem action='HistoryDelete'/>"
"<toolitem action='HistoryClear' position='bottom' />"
"</toolbar>"
"<popup name='popup_history'>"
"<menuitem action='HistoryOpen'/>"
"<menuitem action='HistoryOpenTab'/>"
"<separator/>"
"<menuitem action='HistoryDelete'/>"
"</popup>"
"</ui>";
static void
@ -2862,6 +3189,7 @@ midori_browser_init (MidoriBrowser* browser)
browser->bookmarks = NULL;
browser->trash = NULL;
browser->search_engines = NULL;
browser->history = NULL;
/* Setup the window metrics */
g_signal_connect (browser, "realize",
@ -2987,6 +3315,9 @@ midori_browser_init (MidoriBrowser* browser)
browser->popup_bookmark = gtk_ui_manager_get_widget (
ui_manager, "/popup_bookmark");
g_object_ref (browser->popup_bookmark);
browser->popup_history = gtk_ui_manager_get_widget (
ui_manager, "/popup_history");
g_object_ref (browser->popup_history);
browser->menu_tools = gtk_menu_item_get_submenu (GTK_MENU_ITEM (
gtk_ui_manager_get_widget (ui_manager, "/menubar/Tools")));
menuitem = gtk_separator_menu_item_new ();
@ -3126,10 +3457,39 @@ midori_browser_init (MidoriBrowser* browser)
STOCK_CONSOLE, _("Console"));
/* History */
panel = midori_view_new ();
gtk_widget_show (panel);
box = gtk_vbox_new (FALSE, 0);
treestore = gtk_tree_store_new (1, KATZE_TYPE_ITEM);
treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (treestore));
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (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_browser_history_render_icon_cb,
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_browser_history_render_text_cb,
treeview, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
g_object_unref (treestore);
g_object_connect (treeview,
"signal::row-activated",
midori_panel_history_row_activated_cb, browser,
"signal::button-release-event",
midori_panel_history_button_release_event_cb, browser,
"signal::popup-menu",
midori_panel_history_popup_menu_cb, browser,
NULL);
gtk_box_pack_start (GTK_BOX (box), treeview, TRUE, TRUE, 0);
browser->panel_history = treeview;
gtk_widget_show_all (box);
toolbar = gtk_ui_manager_get_widget (ui_manager, "/toolbar_history");
gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar), GTK_ICON_SIZE_MENU);
gtk_widget_show (toolbar);
midori_panel_append_page (MIDORI_PANEL (browser->panel),
panel, NULL,
box, toolbar,
STOCK_HISTORY, _("History"));
/* Pageholder */
@ -3298,6 +3658,8 @@ midori_browser_finalize (GObject* object)
g_object_unref (browser->trash);
if (browser->search_engines)
g_object_unref (browser->search_engines);
if (browser->history)
g_object_unref (browser->history);
G_OBJECT_CLASS (midori_browser_parent_class)->finalize (object);
}
@ -3513,6 +3875,115 @@ midori_browser_load_bookmarks (MidoriBrowser* browser)
_action_set_sensitive (browser, "BookmarkAdd", TRUE);
}
static void
_tree_store_insert_history_item (GtkTreeStore* treestore,
GtkTreeIter* parent,
KatzeItem* item)
{
GtkTreeIter iter;
KatzeItem* child;
guint i, n;
GtkTreeIter* piter;
g_return_if_fail (KATZE_IS_ITEM (item));
if (KATZE_IS_ARRAY (item))
{
piter = parent;
if (katze_item_get_added (item))
{
gtk_tree_store_insert_with_values (treestore, &iter, parent, 0, 0, item, -1);
g_object_unref (item);
piter = &iter;
}
n = katze_array_get_length (KATZE_ARRAY (item));
for (i = 0; i < n; i++)
{
child = katze_array_get_nth_item (KATZE_ARRAY (item), i);
_tree_store_insert_history_item (treestore, piter, child);
}
}
else
{
gtk_tree_store_insert_with_values (treestore, &iter, parent, 0, 0, item, -1);
g_object_unref (item);
}
}
static void
midori_browser_new_history_item (MidoriBrowser* browser,
KatzeItem* item)
{
GtkTreeView* treeview;
GtkTreeModel* treemodel;
GtkTreeIter iter;
KatzeArray* parent;
gint i;
gboolean found;
time_t now;
gchar newdate [70];
gchar *today;
gsize len;
treeview = GTK_TREE_VIEW (browser->panel_history);
treemodel = gtk_tree_view_get_model (treeview);
now = time (NULL);
strftime (newdate, sizeof (newdate), "%Y-%m-%d %H:%M:%S", localtime (&now));
katze_item_set_added (item, newdate);
len = (g_strrstr (newdate, " ") - newdate);
today = g_strndup (newdate, len);
found = FALSE;
i = 0;
while (gtk_tree_model_iter_nth_child (treemodel, &iter, NULL, i++))
{
gtk_tree_model_get (treemodel, &iter, 0, &parent, -1);
if (g_ascii_strcasecmp (today,
katze_item_get_added (KATZE_ITEM (parent))) == 0)
{
found = TRUE;
break;
}
g_object_unref (parent);
}
if (!found)
{
parent = katze_array_new (KATZE_TYPE_ARRAY);
katze_item_set_added (KATZE_ITEM (parent), today);
katze_array_add_item (browser->history, parent);
katze_array_add_item (parent, item);
_tree_store_insert_history_item (GTK_TREE_STORE (treemodel), NULL,
KATZE_ITEM (parent));
}
else
{
_tree_store_insert_history_item (GTK_TREE_STORE (treemodel),
&iter, item);
katze_array_add_item (parent, item);
g_object_unref (parent);
}
g_free (today);
}
static void
midori_browser_load_history (MidoriBrowser* browser)
{
GtkTreeView* treeview;
GtkTreeModel* treemodel;
if (!browser->history)
return;
treeview = GTK_TREE_VIEW (browser->panel_history);
treemodel = gtk_tree_view_get_model (treeview);
_tree_store_insert_history_item (GTK_TREE_STORE (treemodel),
NULL, KATZE_ITEM (browser->history));
}
static void
midori_browser_set_property (GObject* object,
guint prop_id,
@ -3578,6 +4049,12 @@ midori_browser_set_property (GObject* object,
_action_by_name (browser, "Search")), item);
}
break;
case PROP_HISTORY:
; /* FIXME: Disconnect handlers */
katze_object_assign (browser->history, g_value_get_object (value));
midori_browser_load_history (browser);
/* FIXME: Connect to updates */
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -3624,6 +4101,9 @@ midori_browser_get_property (GObject* object,
case PROP_SEARCH_ENGINES:
g_value_set_object (value, browser->search_engines);
break;
case PROP_HISTORY:
g_value_set_object (value, browser->history);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;

View file

@ -5,5 +5,5 @@ obj = bld.create_obj ('cc', 'program')
obj.target = 'midori'
obj.includes = '.. ../katze'
obj.find_sources_in_dirs ('.')
obj.uselib = 'UNIQUE GIO GTK GTKSOURCEVIEW WEBKIT LIBXML'
obj.uselib = 'UNIQUE GIO GTK GTKSOURCEVIEW SQLITE WEBKIT LIBXML'
obj.uselib_local = 'katze'

View file

@ -110,6 +110,7 @@ def configure (conf):
conf.check_pkg ('gtk+-2.0', destvar='GTK', vnum='2.6.0', mandatory=True)
conf.check_pkg ('gtksourceview-2.0', destvar='GTKSOURCEVIEW', vnum='2.0', mandatory=False)
conf.check_pkg ('sqlite3', destvar='SQLITE', vnum='3.0', mandatory=False)
conf.check_pkg ('webkit-1.0', destvar='WEBKIT', vnum='0.1', mandatory=True)
conf.check_pkg ('libxml-2.0', destvar='LIBXML', vnum='2.6', mandatory=True)