From f840f0508cc9d7bf5f2cb4dd2205dc5234d7e138 Mon Sep 17 00:00:00 2001 From: Oliver Hanraths Date: Sat, 16 Jun 2012 01:06:58 +0200 Subject: [PATCH] Bookmarks: Database structure improved The new format uses bookmarks_v2.db. We require sqlite 3.6.19 now due to foreign keys. Fixes: https://bugs.launchpad.net/midori/+bug/836707 --- midori/main.c | 15 +-- midori/midori-array.c | 24 ++-- midori/midori-bookmarks.c | 275 +++++++++++++++++++++++++++++++++----- midori/midori-bookmarks.h | 1 - midori/midori-browser.c | 128 ++++++++++++------ panels/midori-bookmarks.c | 212 +++++++++++++++++++++-------- panels/midori-bookmarks.h | 18 ++- wscript | 2 +- 8 files changed, 524 insertions(+), 151 deletions(-) diff --git a/midori/main.c b/midori/main.c index 53dfa035..b53ad980 100644 --- a/midori/main.c +++ b/midori/main.c @@ -434,7 +434,6 @@ midori_history_clear_cb (KatzeArray* array, static gboolean midori_history_initialize (KatzeArray* array, const gchar* filename, - const gchar* bookmarks_filename, char** errmsg) { sqlite3* db; @@ -442,6 +441,9 @@ midori_history_initialize (KatzeArray* array, sqlite3_stmt* stmt; gint result; gchar* sql; + gchar* bookmarks_filename; + + g_return_val_if_fail (errmsg != NULL, FALSE); if (sqlite3_open (filename, &db) != SQLITE_OK) { @@ -493,7 +495,9 @@ midori_history_initialize (KatzeArray* array, "COMMIT;", NULL, NULL, errmsg); + bookmarks_filename = build_config_filename ("bookmarks_v2.db"); sql = g_strdup_printf ("ATTACH DATABASE '%s' AS bookmarks", bookmarks_filename); + g_free (bookmarks_filename); sqlite3_exec (db, sql, NULL, NULL, errmsg); g_free (sql); g_object_set_data (G_OBJECT (array), "db", db); @@ -1900,9 +1904,7 @@ main (int argc, gchar** extensions; MidoriWebSettings* settings; gchar* config_file; - gchar* bookmarks_file; GKeyFile* speeddial; - gboolean bookmarks_exist; MidoriStartup load_on_startup; KatzeArray* search_engines; KatzeArray* bookmarks; @@ -2347,10 +2349,8 @@ main (int argc, midori_startup_timer ("Search read: \t%f"); bookmarks = katze_array_new (KATZE_TYPE_ARRAY); - bookmarks_file = g_build_filename (config, "bookmarks.db", NULL); - bookmarks_exist = g_access (bookmarks_file, F_OK) == 0; errmsg = NULL; - if ((db = midori_bookmarks_initialize (bookmarks, bookmarks_file, &errmsg)) == NULL) + if ((db = midori_bookmarks_initialize (bookmarks, &errmsg)) == NULL) { g_string_append_printf (error_messages, _("Bookmarks couldn't be loaded: %s\n"), errmsg); @@ -2396,13 +2396,12 @@ main (int argc, katze_assign (config_file, g_build_filename (config, "history.db", NULL)); errmsg = NULL; - if (!midori_history_initialize (history, config_file, bookmarks_file, &errmsg)) + if (!midori_history_initialize (history, config_file, &errmsg)) { g_string_append_printf (error_messages, _("The history couldn't be loaded: %s\n"), errmsg); errmsg = NULL; } - g_free (bookmarks_file); midori_startup_timer ("History read: \t%f"); error = NULL; diff --git a/midori/midori-array.c b/midori/midori-array.c index 7ba6c624..aa10715e 100644 --- a/midori/midori-array.c +++ b/midori/midori-array.c @@ -982,24 +982,26 @@ katze_item_set_value_from_column (sqlite3_stmt* stmt, item->added = date; } else if (g_str_equal (name, "day") || g_str_equal (name, "app") - || g_str_equal (name, "toolbar")) + || g_str_equal (name, "toolbar") || g_str_equal (name, "id") + || g_str_equal (name, "parentid") || g_str_equal (name, "seq") + || g_str_equal (name, "pos_panel") || g_str_equal (name, "pos_bar")) { gint value; value = sqlite3_column_int64 (stmt, column); katze_item_set_meta_integer (item, name, value); } - else if (g_str_equal (name, "folder")) - { - const unsigned char* folder; - folder = sqlite3_column_text (stmt, column); - katze_item_set_meta_string (item, name, (gchar*)folder); - } else if (g_str_equal (name, "desc")) { const unsigned char* text; text = sqlite3_column_text (stmt, column); item->text = g_strdup ((gchar*)text); } + else if (g_str_equal (name, "sql")) + { + const unsigned char* sql; + sql = sqlite3_column_text (stmt, column); + katze_item_set_meta_string (item, name, (gchar*)sql); + } else g_warn_if_reached (); } @@ -1102,7 +1104,7 @@ midori_array_query_recursive (KatzeArray* bookmarks, return NULL; sqlcmd = g_strdup_printf ("SELECT %s FROM bookmarks WHERE %s " - "ORDER BY title DESC", fields, condition); + "ORDER BY (uri='') ASC, title DESC", fields, condition); if (strstr (condition, "%q")) { sqlcmd_value = sqlite3_mprintf (sqlcmd, value ? value : ""); @@ -1120,10 +1122,14 @@ midori_array_query_recursive (KatzeArray* bookmarks, { if (KATZE_ITEM_IS_FOLDER (item)) { + gchar* parentid = g_strdup_printf ("%" G_GINT64_FORMAT, + katze_item_get_meta_integer (item, "id")); KatzeArray* subarray = midori_array_query_recursive (bookmarks, - fields, "folder='%q'", item->name, TRUE); + fields, "parentid=%q", parentid, TRUE); katze_item_set_name (KATZE_ITEM (subarray), item->name); katze_array_add_item (array, subarray); + + g_free (parentid); } } g_list_free (list); diff --git a/midori/midori-bookmarks.c b/midori/midori-bookmarks.c index 59865fb9..db9c5c73 100644 --- a/midori/midori-bookmarks.c +++ b/midori/midori-bookmarks.c @@ -13,7 +13,9 @@ #include "midori-bookmarks.h" #include "panels/midori-bookmarks.h" #include "midori-array.h" +#include "sokoke.h" +#include #include #ifdef G_ENABLE_DEBUG @@ -29,7 +31,7 @@ midori_bookmarks_add_item_cb (KatzeArray* array, sqlite3* db) { midori_bookmarks_insert_item_db (db, item, - katze_item_get_meta_string (item, "folder")); + katze_item_get_meta_integer (item, "parentid")); } void @@ -40,19 +42,10 @@ midori_bookmarks_remove_item_cb (KatzeArray* array, gchar* sqlcmd; char* errmsg = NULL; - if (KATZE_ITEM_IS_BOOKMARK (item)) - sqlcmd = sqlite3_mprintf ( - "DELETE FROM bookmarks WHERE uri = '%q' " - " AND folder = '%q'", - katze_item_get_uri (item), - katze_str_non_null (katze_item_get_meta_string (item, "folder"))); - else - sqlcmd = sqlite3_mprintf ( - "DELETE FROM bookmarks WHERE title = '%q'" - " AND folder = '%q'", - katze_item_get_name (item), - katze_str_non_null (katze_item_get_meta_string (item, "folder"))); + sqlcmd = sqlite3_mprintf ( + "DELETE FROM bookmarks WHERE id = %" G_GINT64_FORMAT ";", + katze_item_get_meta_integer (item, "id")); if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) != SQLITE_OK) { @@ -63,20 +56,102 @@ midori_bookmarks_remove_item_cb (KatzeArray* array, sqlite3_free (sqlcmd); } +#define _APPEND_TO_SQL_ERRORMSG(custom_errmsg) \ + do { \ + if (sql_errmsg) \ + { \ + g_string_append_printf (errmsg_str, "%s : %s\n", custom_errmsg, sql_errmsg); \ + sqlite3_free (sql_errmsg); \ + } \ + else \ + g_string_append (errmsg_str, custom_errmsg); \ + } while (0) + +gboolean +midori_bookmarks_import_from_old_db (sqlite3* db, + const gchar* oldfile, + gchar** errmsg) +{ + gint sql_errcode; + gboolean failure = FALSE; + gchar* sql_errmsg = NULL; + GString* errmsg_str = g_string_new (NULL); + gchar* attach_stmt = sqlite3_mprintf ("ATTACH DATABASE %Q AS old_db;", oldfile); + const gchar* convert_stmts = + "BEGIN TRANSACTION;" + "INSERT INTO main.bookmarks (parentid, title, uri, desc, app, toolbar) " + "SELECT NULL AS parentid, title, uri, desc, app, toolbar " + "FROM old_db.bookmarks;" + "UPDATE main.bookmarks SET parentid = (" + "SELECT id FROM main.bookmarks AS b1 WHERE b1.title = (" + "SELECT folder FROM old_db.bookmarks WHERE title = main.bookmarks.title));" + "COMMIT;"; + const gchar* detach_stmt = "DETACH DATABASE old_db;"; + + *errmsg = NULL; + sql_errcode = sqlite3_exec (db, attach_stmt, NULL, NULL, &sql_errmsg); + sqlite3_free (attach_stmt); + + if (sql_errcode != SQLITE_OK) + { + _APPEND_TO_SQL_ERRORMSG (_("failed to ATTACH old db")); + goto convert_failed; + } + + if (sqlite3_exec (db, convert_stmts, NULL, NULL, &sql_errmsg) != SQLITE_OK) + { + failure = TRUE; + _APPEND_TO_SQL_ERRORMSG (_("failed to import from old db")); + + /* try to get back to previous state */ + if (sqlite3_exec (db, "ROLLBACK TRANSACTION;", NULL, NULL, &sql_errmsg) != SQLITE_OK) + _APPEND_TO_SQL_ERRORMSG (_("failed to rollback the transaction")); + } + + if (sqlite3_exec (db, detach_stmt, NULL, NULL, &sql_errmsg) != SQLITE_OK) + _APPEND_TO_SQL_ERRORMSG (_("failed to DETACH ")); + + if (failure) + { + convert_failed: + *errmsg = g_string_free (errmsg_str, FALSE); + g_print ("ERRORR: %s\n", errmsg_str->str); + return FALSE; + } + + return TRUE; +} +#undef _APPEND_TO_SQL_ERRORMSG + sqlite3* midori_bookmarks_initialize (KatzeArray* array, - const gchar* filename, char** errmsg) { sqlite3* db; + gchar* oldfile; + gchar* newfile; + gboolean newfile_did_exist, oldfile_exists; + const gchar* create_stmt; + gchar* sql_errmsg = NULL; + gchar* import_errmsg = NULL; - if (sqlite3_open (filename, &db) != SQLITE_OK) + g_return_val_if_fail (errmsg != NULL, NULL); + + oldfile = g_build_filename (sokoke_set_config_dir (NULL), "bookmarks.db", NULL); + oldfile_exists = g_access (oldfile, F_OK) == 0; + newfile = g_build_filename (sokoke_set_config_dir (NULL), "bookmarks_v2.db", NULL); + newfile_did_exist = g_access (newfile, F_OK) == 0; + + /* sqlite3_open will create the file if it did not exists already */ + if (sqlite3_open (newfile, &db) != SQLITE_OK) { - if (errmsg) - *errmsg = g_strdup_printf (_("Failed to open database: %s\n"), - sqlite3_errmsg (db)); - sqlite3_close (db); - return NULL; + if (db) + *errmsg = g_strdup_printf (_("failed to open database: %s\n"), + sqlite3_errmsg (db)); + else + *errmsg = g_strdup (_("failed to open database\n")); + + goto init_failed; } #ifdef G_ENABLE_DEBUG @@ -84,17 +159,155 @@ midori_bookmarks_initialize (KatzeArray* array, sqlite3_trace (db, midori_bookmarks_dbtracer, NULL); #endif - if (sqlite3_exec (db, - "CREATE TABLE IF NOT EXISTS " - "bookmarks (uri text, title text, folder text, " - "desc text, app integer, toolbar integer);", - NULL, NULL, errmsg) != SQLITE_OK) + create_stmt = /* Table structure */ + "CREATE TABLE IF NOT EXISTS bookmarks " + "(id INTEGER PRIMARY KEY AUTOINCREMENT, " + "parentid INTEGER DEFAULT NULL, " + "title TEXT, uri TEXT, desc TEXT, app INTEGER, toolbar INTEGER, " + "pos_panel INTEGER, pos_bar INTEGER, " + "created DATE DEFAULT CURRENT_TIMESTAMP, " + "last_visit DATE, visit_count INTEGER DEFAULT 0, " + "nick TEXT, " + "FOREIGN KEY(parentid) REFERENCES bookmarks(id) " + "ON DELETE CASCADE); PRAGMA foreign_keys = ON;" + + /* trigger: insert panel position */ + "CREATE TRIGGER IF NOT EXISTS bookmarkInsertPosPanel " + "AFTER INSERT ON bookmarks FOR EACH ROW " + "BEGIN UPDATE bookmarks SET pos_panel = (" + "SELECT ifnull(MAX(pos_panel),0)+1 FROM bookmarks WHERE " + "(NEW.parentid IS NOT NULL AND parentid = NEW.parentid) " + "OR (NEW.parentid IS NULL AND parentid IS NULL)) " + "WHERE id = NEW.id; END;" + + /* trigger: insert Bookmarkbar position */ + "CREATE TRIGGER IF NOT EXISTS bookmarkInsertPosBar " + "AFTER INSERT ON bookmarks FOR EACH ROW WHEN NEW.toolbar=1 " + "BEGIN UPDATE bookmarks SET pos_bar = (" + "SELECT ifnull(MAX(pos_bar),0)+1 FROM bookmarks WHERE " + "((NEW.parentid IS NOT NULL AND parentid = NEW.parentid) " + "OR (NEW.parentid IS NULL AND parentid IS NULL)) AND toolbar=1) " + "WHERE id = NEW.id; END;" + + /* trigger: update panel position */ + "CREATE TRIGGER IF NOT EXISTS bookmarkUpdatePosPanel " + "BEFORE UPDATE OF parentid ON bookmarks FOR EACH ROW " + "WHEN ((NEW.parentid IS NULL OR OLD.parentid IS NULL) " + "AND NEW.parentid IS NOT OLD.parentid) OR " + "((NEW.parentid IS NOT NULL AND OLD.parentid IS NOT NULL) " + "AND NEW.parentid!=OLD.parentid) " + "BEGIN UPDATE bookmarks SET pos_panel = pos_panel-1 " + "WHERE ((OLD.parentid IS NOT NULL AND parentid = OLD.parentid) " + "OR (OLD.parentid IS NULL AND parentid IS NULL)) AND pos_panel > OLD.pos_panel; " + "UPDATE bookmarks SET pos_panel = (" + "SELECT ifnull(MAX(pos_panel),0)+1 FROM bookmarks " + "WHERE (NEW.parentid IS NOT NULL AND parentid = NEW.parentid) " + "OR (NEW.parentid IS NULL AND parentid IS NULL)) " + "WHERE id = OLD.id; END;" + + /* trigger: update Bookmarkbar position */ + "CREATE TRIGGER IF NOT EXISTS bookmarkUpdatePosBar0 " + "AFTER UPDATE OF parentid, toolbar ON bookmarks FOR EACH ROW " + "WHEN ((NEW.parentid IS NULL OR OLD.parentid IS NULL) " + "AND NEW.parentid IS NOT OLD.parentid) " + "OR ((NEW.parentid IS NOT NULL AND OLD.parentid IS NOT NULL) " + "AND NEW.parentid!=OLD.parentid) OR (OLD.toolbar=1 AND NEW.toolbar=0) " + "BEGIN UPDATE bookmarks SET pos_bar = NULL WHERE id = NEW.id; " + "UPDATE bookmarks SET pos_bar = pos_bar-1 " + "WHERE ((OLD.parentid IS NOT NULL AND parentid = OLD.parentid) " + "OR (OLD.parentid IS NULL AND parentid IS NULL)) AND pos_bar > OLD.pos_bar; END;" + + /* trigger: update Bookmarkbar position */ + "CREATE TRIGGER IF NOT EXISTS bookmarkUpdatePosBar1 " + "BEFORE UPDATE OF parentid, toolbar ON bookmarks FOR EACH ROW " + "WHEN ((NEW.parentid IS NULL OR OLD.parentid IS NULL) " + "AND NEW.parentid IS NOT OLD.parentid) OR " + "((NEW.parentid IS NOT NULL AND OLD.parentid IS NOT NULL) " + "AND NEW.parentid!=OLD.parentid) OR (OLD.toolbar=0 AND NEW.toolbar=1) " + "BEGIN UPDATE bookmarks SET pos_bar = (" + "SELECT ifnull(MAX(pos_bar),0)+1 FROM bookmarks WHERE " + "(NEW.parentid IS NOT NULL AND parentid = NEW.parentid) " + "OR (NEW.parentid IS NULL AND parentid IS NULL)) " + "WHERE id = OLD.id; END;" + + /* trigger: delete panel position */ + "CREATE TRIGGER IF NOT EXISTS bookmarkDeletePosPanel " + "AFTER DELETE ON bookmarks FOR EACH ROW " + "BEGIN UPDATE bookmarks SET pos_panel = pos_panel-1 " + "WHERE ((OLD.parentid IS NOT NULL AND parentid = OLD.parentid) " + "OR (OLD.parentid IS NULL AND parentid IS NULL)) AND pos_panel > OLD.pos_panel; END;" + + /* trigger: delete Bookmarkbar position */ + "CREATE TRIGGER IF NOT EXISTS bookmarkDeletePosBar " + "AFTER DELETE ON bookmarks FOR EACH ROW WHEN OLD.toolbar=1 " + "BEGIN UPDATE bookmarks SET pos_bar = pos_bar-1 " + "WHERE ((OLD.parentid IS NOT NULL AND parentid = OLD.parentid) " + "OR (OLD.parentid IS NULL AND parentid IS NULL)) AND pos_bar > OLD.pos_bar; END;"; + + + if (newfile_did_exist) + { + /* we are done */ + goto init_success; + } + else + { + /* initial creation */ + if (sqlite3_exec (db, create_stmt, NULL, NULL, &sql_errmsg) != SQLITE_OK) + { + + if (errmsg) + { + if (sql_errmsg) + { + *errmsg = g_strdup_printf (_("could not create bookmarks table: %s\n"), sql_errmsg); + sqlite3_free (sql_errmsg); + } + else + *errmsg = g_strdup (_("could not create bookmarks table")); + } + + /* we can as well remove the new file */ + g_unlink (newfile); + goto init_failed; + } + + } + + if (oldfile_exists) + /* import from old db */ + if (!midori_bookmarks_import_from_old_db (db, oldfile, &import_errmsg)) + { + if (errmsg) + { + if (import_errmsg) + { + *errmsg = g_strdup_printf (_("could not import from old database: %s\n"), import_errmsg); + g_free (import_errmsg); + } + else + *errmsg = g_strdup_printf (_("could not import from old database")); + } + } + + init_success: + g_free (newfile); + g_free (oldfile); + g_signal_connect (array, "add-item", + G_CALLBACK (midori_bookmarks_add_item_cb), db); + g_signal_connect (array, "remove-item", + G_CALLBACK (midori_bookmarks_remove_item_cb), db); + + return db; + + init_failed: + g_free (newfile); + g_free (oldfile); + + if (db) + sqlite3_close (db); + return NULL; - g_signal_connect (array, "add-item", - G_CALLBACK (midori_bookmarks_add_item_cb), db); - g_signal_connect (array, "remove-item", - G_CALLBACK (midori_bookmarks_remove_item_cb), db); - return db; } void @@ -112,5 +325,5 @@ midori_bookmarks_import (const gchar* filename, g_error_free (error); return; } - midori_bookmarks_import_array_db (db, bookmarks, ""); + midori_bookmarks_import_array_db (db, bookmarks, 0); } diff --git a/midori/midori-bookmarks.h b/midori/midori-bookmarks.h index a5f38ce1..e4a3f8d6 100644 --- a/midori/midori-bookmarks.h +++ b/midori/midori-bookmarks.h @@ -25,7 +25,6 @@ midori_bookmarks_remove_item_cb (KatzeArray* array, sqlite3* midori_bookmarks_initialize (KatzeArray* array, - const gchar* filename, char** errmsg); void diff --git a/midori/midori-browser.c b/midori/midori-browser.c index dba7b757..da3a04d5 100644 --- a/midori/midori-browser.c +++ b/midori/midori-browser.c @@ -173,7 +173,11 @@ midori_browser_get_property (GObject* object, void midori_bookmarks_import_array_db (sqlite3* db, KatzeArray* array, - gchar* folder); + gint64 parentid); + +gboolean +midori_bookmarks_update_item_db (sqlite3* db, + KatzeItem* item); void midori_browser_open_bookmark (MidoriBrowser* browser, @@ -659,9 +663,10 @@ midori_view_notify_statusbar_text_cb (GtkWidget* view, } static GtkWidget* -midori_bookmark_folder_button_new (KatzeArray* array, - gboolean new_bookmark, - const gchar* selected) +midori_bookmark_folder_button_new (KatzeArray* array, + gboolean new_bookmark, + gint64 selected, + gint64 parentid) { GtkListStore* model; GtkWidget* combo; @@ -670,16 +675,16 @@ midori_bookmark_folder_button_new (KatzeArray* array, sqlite3* db; sqlite3_stmt* statement; gint result; - const gchar* sqlcmd = "SELECT title from bookmarks where uri=''"; + const gchar* sqlcmd = "SELECT title, id FROM bookmarks WHERE uri='' ORDER BY title ASC"; - model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT); + model = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT64); combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (model)); renderer = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE); gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), renderer, "text", 0); gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), renderer, "ellipsize", 1); gtk_list_store_insert_with_values (model, NULL, G_MAXINT, - 0, _("Toplevel folder"), 1, PANGO_ELLIPSIZE_END, -1); + 0, _("Toplevel folder"), 1, PANGO_ELLIPSIZE_END, 2, (gint64)0, -1); gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0); db = g_object_get_data (G_OBJECT (array), "db"); @@ -689,34 +694,45 @@ midori_bookmark_folder_button_new (KatzeArray* array, while ((result = sqlite3_step (statement)) == SQLITE_ROW) { const unsigned char* name = sqlite3_column_text (statement, 0); - gtk_list_store_insert_with_values (model, NULL, G_MAXINT, - 0, name, 1, PANGO_ELLIPSIZE_END, -1); - if (!new_bookmark && !g_strcmp0 (selected, (gchar*)name)) - gtk_combo_box_set_active (GTK_COMBO_BOX (combo), n); - n++; + gint64 id = sqlite3_column_int64 (statement, 1); + + /* do not show the folder itself */ + if (id != selected) + { + gtk_list_store_insert_with_values (model, NULL, G_MAXINT, + 0, name, 1, PANGO_ELLIPSIZE_END, 2, id, -1); + + if (!new_bookmark && id == parentid) + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), n); + n++; + } } if (n < 2) gtk_widget_set_sensitive (combo, FALSE); return combo; } -static gchar* +static gint64 midori_bookmark_folder_button_get_active (GtkWidget* combo) { - gchar* selected = NULL; + gint64 id; GtkTreeIter iter; - g_return_val_if_fail (GTK_IS_COMBO_BOX (combo), NULL); + g_return_val_if_fail (GTK_IS_COMBO_BOX (combo), 0); if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter)) { + gchar* selected = NULL; GtkTreeModel* model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo)); - gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 0, &selected, -1); + gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 0, &selected, 2, &id, -1); + if (g_str_equal (selected, _("Toplevel folder"))) - katze_assign (selected, g_strdup ("")); + id = 0; + + g_free (selected); } - return selected; + return id; } static void @@ -861,7 +877,8 @@ midori_browser_edit_bookmark_dialog_new (MidoriBrowser* browser, gtk_size_group_add_widget (sizegroup, label); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); combo_folder = midori_bookmark_folder_button_new (browser->bookmarks, - new_bookmark, katze_item_get_meta_string (bookmark, "folder")); + new_bookmark, katze_item_get_meta_integer (bookmark, "id"), + katze_item_get_meta_integer (bookmark, "parentid")); gtk_box_pack_start (GTK_BOX (hbox), combo_folder, TRUE, TRUE, 0); gtk_container_add (GTK_CONTAINER (content_area), hbox); gtk_widget_show_all (hbox); @@ -912,10 +929,7 @@ midori_browser_edit_bookmark_dialog_new (MidoriBrowser* browser, gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { - gchar* selected; - - if (!new_bookmark) - katze_array_remove_item (browser->bookmarks, bookmark); + gint64 selected; katze_item_set_name (bookmark, gtk_entry_get_text (GTK_ENTRY (entry_title))); @@ -930,13 +944,16 @@ midori_browser_edit_bookmark_dialog_new (MidoriBrowser* browser, } selected = midori_bookmark_folder_button_get_active (combo_folder); - katze_item_set_meta_string (bookmark, "folder", selected); - katze_array_add_item (browser->bookmarks, bookmark); + katze_item_set_meta_integer (bookmark, "parentid", selected); + + if (new_bookmark) + katze_array_add_item (browser->bookmarks, bookmark); + else + midori_bookmarks_update_item_db (db, bookmark); if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_toolbar))) if (!gtk_widget_get_visible (browser->bookmarkbar)) _action_set_active (browser, "Bookmarkbar", TRUE); - g_free (selected); return_status = TRUE; } if (gtk_widget_get_visible (browser->bookmarkbar)) @@ -3041,14 +3058,35 @@ _action_bookmarks_populate_folder (GtkAction* action, KatzeArray* folder, MidoriBrowser* browser) { - const gchar* folder_name; + gint64 id; KatzeArray* bookmarks; GtkWidget* menuitem; - folder_name = katze_item_get_name (KATZE_ITEM (folder)); - if (!(bookmarks = midori_array_query (browser->bookmarks, - "uri, title, app, folder", "folder = '%q'", folder_name))) - return FALSE; + id = katze_item_get_meta_integer (KATZE_ITEM (folder), "id"); + + if (id == -1) + { + if (!(bookmarks = midori_array_query (browser->bookmarks, + "id, title, parentid, uri, app, pos_panel, pos_bar", "parentid is NULL", NULL))) + { + g_warning ("midori_array_query returned NULL)"); + return FALSE; + } + } + else + { + gchar *parentid = g_strdup_printf ("%" G_GINT64_FORMAT, id); + + if (!(bookmarks = midori_array_query (browser->bookmarks, + "id, title, parentid, uri, app, pos_panel, pos_bar", "parentid = %q", parentid))) + { + g_warning ("midori_array_query returned NULL (id='%s')", parentid); + g_free (parentid); + return FALSE; + } + + g_free (parentid); + } /* Clear items from dummy array here */ gtk_container_foreach (GTK_CONTAINER (menu), @@ -4365,7 +4403,7 @@ _action_bookmarks_import_activate (GtkAction* action, gtk_size_group_add_widget (sizegroup, label); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); combobox_folder = midori_bookmark_folder_button_new (browser->bookmarks, - FALSE, NULL); + FALSE, 0, 0); gtk_box_pack_start (GTK_BOX (hbox), combobox_folder, TRUE, TRUE, 0); gtk_container_add (GTK_CONTAINER (content_area), hbox); gtk_widget_show_all (hbox); @@ -4375,7 +4413,7 @@ _action_bookmarks_import_activate (GtkAction* action, { GtkTreeIter iter; gchar* path = NULL; - gchar* selected = NULL; + gint64 selected; GError* error; sqlite3* db = g_object_get_data (G_OBJECT (browser->bookmarks), "db"); @@ -4408,7 +4446,6 @@ _action_bookmarks_import_activate (GtkAction* action, midori_bookmarks_import_array_db (db, bookmarks, selected); katze_array_update (browser->bookmarks); g_object_unref (bookmarks); - g_free (selected); g_free (path); } else @@ -4465,7 +4502,7 @@ wrong_format: error = NULL; bookmarks = midori_array_query_recursive (browser->bookmarks, - "*", "folder='%q'", "", TRUE); + "*", "parentid IS NULL", NULL, TRUE); if (!midori_array_to_file (bookmarks, path, format, &error)) { sokoke_message_dialog (GTK_MESSAGE_ERROR, @@ -7044,9 +7081,10 @@ midori_bookmarkbar_populate (MidoriBrowser* browser) gtk_separator_tool_item_new (), -1); array = midori_array_query (browser->bookmarks, - "uri, title, desc, app, folder, toolbar", "toolbar = 1", NULL); + "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "toolbar = 1", NULL); if (!array) { + g_warning ("midori_array_query returned NULL"); _action_set_sensitive (browser, "BookmarkAdd", FALSE); _action_set_sensitive (browser, "BookmarkFolderAdd", FALSE); return; @@ -7058,10 +7096,22 @@ midori_bookmarkbar_populate (MidoriBrowser* browser) midori_bookmarkbar_insert_item (browser->bookmarkbar, item); else { + gint64 id = katze_item_get_meta_integer (item, "id"); + gchar* parentid = g_strdup_printf ("%" G_GINT64_FORMAT, id); KatzeArray* subfolder = midori_array_query (browser->bookmarks, - "uri, title, desc, app", "folder = '%q' AND uri != ''", katze_item_get_name (item)); - katze_item_set_name (KATZE_ITEM (subfolder), katze_item_get_name (item)); - midori_bookmarkbar_insert_item (browser->bookmarkbar, KATZE_ITEM (subfolder)); + "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid = %q AND uri != ''", + parentid); + + if (subfolder) + { + katze_item_set_name (KATZE_ITEM (subfolder), katze_item_get_name (item)); + katze_item_set_meta_integer (KATZE_ITEM (subfolder), "id", id); + midori_bookmarkbar_insert_item (browser->bookmarkbar, KATZE_ITEM (subfolder)); + } + else + g_warning ("midori_array_query returned NULL (id='%s')", parentid); + + g_free (parentid); } } _action_set_sensitive (browser, "BookmarkAdd", TRUE); diff --git a/panels/midori-bookmarks.c b/panels/midori-bookmarks.c index 390ebae4..2f9ef4cd 100644 --- a/panels/midori-bookmarks.c +++ b/panels/midori-bookmarks.c @@ -120,66 +120,87 @@ midori_bookmarks_get_stock_id (MidoriViewable* viewable) return STOCK_BOOKMARKS; } +/* TODO: Function never used */ void -midori_bookmarks_export_array_db (sqlite3* db, - KatzeArray* array, - const gchar* folder) +midori_bookmarks_export_array_db (sqlite3* db, + KatzeArray* array, + gint64 parentid) { KatzeArray* root_array; KatzeArray* subarray; KatzeItem* item; GList* list; + gchar* parent_id; - if (!(root_array = midori_array_query (array, "*", "folder='%q'", folder))) + parent_id = g_strdup_printf ("%" G_GINT64_FORMAT, parentid); + if (!(root_array = midori_array_query (array, "*", "parentid = %q", parent_id))) + { + g_free (parent_id); return; + } KATZE_ARRAY_FOREACH_ITEM_L (item, root_array, list) { if (KATZE_ITEM_IS_FOLDER (item)) { subarray = katze_array_new (KATZE_TYPE_ARRAY); katze_item_set_name (KATZE_ITEM (subarray), katze_item_get_name (item)); - midori_bookmarks_export_array_db (db, subarray, katze_item_get_name (item)); + midori_bookmarks_export_array_db (db, subarray, + katze_item_get_meta_integer (item, "parentid")); katze_array_add_item (array, subarray); } else katze_array_add_item (array, item); } + + g_free (parent_id); g_list_free (list); } void -midori_bookmarks_import_array_db (sqlite3* db, - KatzeArray* array, - const gchar* folder) +midori_bookmarks_import_array_db (sqlite3* db, + KatzeArray* array, + gint64 parentid) { GList* list; KatzeItem* item; + gint64 id; if (!db) return; KATZE_ARRAY_FOREACH_ITEM_L (item, array, list) { + id = midori_bookmarks_insert_item_db (db, item, parentid); if (KATZE_IS_ARRAY (item)) - midori_bookmarks_import_array_db (db, KATZE_ARRAY (item), folder); - midori_bookmarks_insert_item_db (db, item, folder); + midori_bookmarks_import_array_db (db, KATZE_ARRAY (item), id); } g_list_free (list); } static KatzeArray* midori_bookmarks_read_from_db (MidoriBookmarks* bookmarks, - const gchar* folder, + gint64 parentid, const gchar* keyword) { KatzeArray* array; if (keyword && *keyword) array = midori_array_query (bookmarks->array, - "uri, title, desc, app, toolbar, folder", "title LIKE '%%%q%%'", keyword); + "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "title LIKE '%%%q%%'", keyword); else - array = midori_array_query (bookmarks->array, - "uri, title, desc, app, toolbar, folder", "folder = '%q'", folder); + { + if (parentid > 0) + { + gchar* parent_id = g_strdup_printf ("%" G_GINT64_FORMAT, parentid); + array = midori_array_query (bookmarks->array, + "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid = %q", parent_id); + + g_free (parent_id); + } + else + array = midori_array_query (bookmarks->array, + "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid IS NULL", NULL); + } return array ? array : katze_array_new (KATZE_TYPE_ITEM); } @@ -187,7 +208,7 @@ static void midori_bookmarks_read_from_db_to_model (MidoriBookmarks* bookmarks, GtkTreeStore* model, GtkTreeIter* parent, - const gchar* folder, + gint64 parentid, const gchar* keyword) { KatzeArray* array; @@ -195,7 +216,7 @@ midori_bookmarks_read_from_db_to_model (MidoriBookmarks* bookmarks, KatzeItem* item; GtkTreeIter child; - array = midori_bookmarks_read_from_db (bookmarks, folder, keyword); + array = midori_bookmarks_read_from_db (bookmarks, parentid, keyword); katze_bookmark_populate_tree_view (array, model, parent); /* Remove invisible dummy row */ last = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (model), parent); @@ -209,23 +230,30 @@ midori_bookmarks_read_from_db_to_model (MidoriBookmarks* bookmarks, g_object_unref (item); } -void -midori_bookmarks_insert_item_db (sqlite3* db, - KatzeItem* item, - const gchar* folder) +gint64 +midori_bookmarks_insert_item_db (sqlite3* db, + KatzeItem* item, + gint64 parentid) { gchar* sqlcmd; char* errmsg = NULL; KatzeItem* old_parent; - const gchar* parent; + gchar* new_parentid; + gchar* id = NULL; const gchar* uri = NULL; const gchar* desc = NULL; + gint64 seq = 0; /* Bookmarks must have a name, import may produce invalid items */ - g_return_if_fail (katze_item_get_name (item)); + g_return_val_if_fail (katze_item_get_name (item), seq); if (!db) - return; + return seq; + + if (katze_item_get_meta_integer (item, "id") > 0) + id = g_strdup_printf ("%" G_GINT64_FORMAT, katze_item_get_meta_integer(item, "id")); + else + id = g_strdup_printf ("NULL"); if (KATZE_ITEM_IS_BOOKMARK (item)) uri = katze_item_get_uri (item); @@ -235,30 +263,57 @@ midori_bookmarks_insert_item_db (sqlite3* db, /* Use folder, otherwise fallback to parent folder */ old_parent = katze_item_get_parent (item); - if (folder && *folder) - parent = folder; - else if (old_parent && katze_item_get_name (old_parent)) - parent = katze_item_get_name (old_parent); + if (parentid > 0) + new_parentid = g_strdup_printf ("%" G_GINT64_FORMAT, parentid); + else if (old_parent && katze_item_get_meta_integer (old_parent, "id") > 0) + new_parentid = g_strdup_printf ("%" G_GINT64_FORMAT, katze_item_get_meta_integer (old_parent, "id")); else - parent = ""; + new_parentid = g_strdup_printf ("NULL"); sqlcmd = sqlite3_mprintf ( - "INSERT into bookmarks (uri, title, desc, folder, toolbar, app) values" - " ('%q', '%q', '%q', '%q', %d, %d)", - uri ? uri : "", + "INSERT INTO bookmarks (id, parentid, title, uri, desc, toolbar, app) " + "VALUES (%q, %q, '%q', '%q', '%q', %d, %d)", + id, + new_parentid, katze_item_get_name (item), - desc ? desc : "", - parent, + katze_str_non_null (uri), + katze_str_non_null (desc), katze_item_get_meta_boolean (item, "toolbar"), katze_item_get_meta_boolean (item, "app")); - if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) != SQLITE_OK) + if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) == SQLITE_OK) + { + /* Get insert id */ + if (g_str_equal (id, "NULL")) + { + KatzeArray* seq_array; + + sqlite3_free (sqlcmd); + sqlcmd = sqlite3_mprintf ( + "SELECT seq FROM sqlite_sequence WHERE name = 'bookmarks'"); + + seq_array = katze_array_from_sqlite (db, sqlcmd); + if (katze_array_get_nth_item (seq_array, 0)) + { + KatzeItem* seq_item = katze_array_get_nth_item (seq_array, 0); + + seq = katze_item_get_meta_integer (seq_item, "seq"); + katze_item_set_meta_integer (item, "id", seq); + } + g_object_unref (seq_array); + } + } + else { g_printerr (_("Failed to add bookmark item: %s\n"), errmsg); sqlite3_free (errmsg); } sqlite3_free (sqlcmd); + g_free (new_parentid); + g_free (id); + + return seq; } static void @@ -268,15 +323,9 @@ midori_bookmarks_add_item_cb (KatzeArray* array, { GtkTreeModel* model; model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview)); - if (!g_strcmp0 (katze_item_get_meta_string (item, "folder"), "")) - gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), - NULL, NULL, G_MAXINT, 0, item, -1); - else - { - gtk_tree_store_clear (GTK_TREE_STORE (model)); - midori_bookmarks_read_from_db_to_model (bookmarks, - GTK_TREE_STORE (model), NULL, NULL, bookmarks->filter); - } + gtk_tree_store_clear (GTK_TREE_STORE (model)); + midori_bookmarks_read_from_db_to_model (bookmarks, + GTK_TREE_STORE (model), NULL, 0, bookmarks->filter); } static void @@ -287,7 +336,7 @@ midori_bookmarks_remove_item_cb (KatzeArray* array, GtkTreeModel* model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview)); gtk_tree_store_clear (GTK_TREE_STORE (model)); midori_bookmarks_read_from_db_to_model (bookmarks, - GTK_TREE_STORE (model), NULL, NULL, bookmarks->filter); + GTK_TREE_STORE (model), NULL, 0, bookmarks->filter); } static void @@ -297,7 +346,7 @@ midori_bookmarks_update_cb (KatzeArray* array, GtkTreeModel* model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview)); gtk_tree_store_clear (GTK_TREE_STORE (model)); midori_bookmarks_read_from_db_to_model (bookmarks, - GTK_TREE_STORE (model), NULL, NULL, bookmarks->filter); + GTK_TREE_STORE (model), NULL, 0, bookmarks->filter); } @@ -310,7 +359,7 @@ midori_bookmarks_row_changed_cb (GtkTreeModel* model, KatzeItem* item; GtkTreeIter parent; KatzeItem* new_parent = NULL; - const gchar* parent_name; + gint64 parentid; gtk_tree_model_get (model, iter, 0, &item, -1); @@ -320,15 +369,15 @@ midori_bookmarks_row_changed_cb (GtkTreeModel* model, /* Bookmarks must not be moved into non-folder items */ if (!KATZE_ITEM_IS_FOLDER (new_parent)) - parent_name = ""; + parentid = 0; else - parent_name = katze_item_get_name (new_parent); + parentid = katze_item_get_meta_integer (new_parent, "id"); } else - parent_name = ""; + parentid = 0; katze_array_remove_item (bookmarks->array, item); - katze_item_set_meta_string (item, "folder", parent_name); + katze_item_set_meta_integer (item, "parentid", parentid); katze_array_add_item (bookmarks->array, item); g_object_unref (item); @@ -359,14 +408,24 @@ midori_bookmarks_edit_clicked_cb (GtkWidget* toolitem, { KatzeItem* item; MidoriBrowser* browser; + gint64 parentid; gtk_tree_model_get (model, &iter, 0, &item, -1); g_assert (!KATZE_ITEM_IS_SEPARATOR (item)); browser = midori_browser_get_for_widget (bookmarks->treeview); + parentid = katze_item_get_meta_integer (item, "parentid"); midori_browser_edit_bookmark_dialog_new ( browser, item, FALSE, KATZE_ITEM_IS_FOLDER (item), NULL); + + if (katze_item_get_meta_integer (item, "parentid") != parentid) + { + gtk_tree_store_clear (GTK_TREE_STORE (model)); + midori_bookmarks_read_from_db_to_model (bookmarks, GTK_TREE_STORE (model), + NULL, 0, NULL); + } + g_object_unref (item); } } @@ -382,6 +441,47 @@ midori_bookmarks_toolbar_update (MidoriBookmarks *bookmarks) gtk_widget_set_sensitive (GTK_WIDGET (bookmarks->edit), selected); } +gboolean +midori_bookmarks_update_item_db (sqlite3* db, + KatzeItem* item) +{ + gchar* sqlcmd; + char* errmsg = NULL; + gchar* parentid; + gboolean updated; + + if (katze_item_get_meta_integer (item, "parentid") > 0) + parentid = g_strdup_printf ("%" G_GINT64_FORMAT, + katze_item_get_meta_integer (item, "parentid")); + else + parentid = g_strdup_printf ("NULL"); + + sqlcmd = sqlite3_mprintf ( + "UPDATE bookmarks SET " + "parentid=%q, title='%q', uri='%q', desc='%q', toolbar=%d, app=%d " + "WHERE id = %" G_GINT64_FORMAT ";", + parentid, + katze_item_get_name (item), + katze_str_non_null (katze_item_get_uri (item)), + katze_str_non_null (katze_item_get_meta_string (item, "desc")), + katze_item_get_meta_boolean (item, "toolbar"), + katze_item_get_meta_boolean (item, "app"), + katze_item_get_meta_integer (item, "id")); + + updated = TRUE; + if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) != SQLITE_OK) + { + updated = FALSE; + g_printerr (_("Failed to update bookmark : %s\n"), errmsg); + sqlite3_free (errmsg); + } + + sqlite3_free (sqlcmd); + g_free (parentid); + + return updated; +} + static void midori_bookmarks_delete_clicked_cb (GtkWidget* toolitem, MidoriBookmarks* bookmarks) @@ -496,8 +596,8 @@ midori_bookmarks_set_app (MidoriBookmarks* bookmarks, g_object_ref (app); bookmarks->array = katze_object_get_object (app, "bookmarks"); - midori_bookmarks_read_from_db_to_model (bookmarks, GTK_TREE_STORE (model), NULL, "", NULL); - g_signal_connect (bookmarks->array, "add-item", + midori_bookmarks_read_from_db_to_model (bookmarks, GTK_TREE_STORE (model), NULL, 0, NULL); + g_signal_connect_after (bookmarks->array, "add-item", G_CALLBACK (midori_bookmarks_add_item_cb), bookmarks); g_signal_connect (bookmarks->array, "remove-item", G_CALLBACK (midori_bookmarks_remove_item_cb), bookmarks); @@ -687,7 +787,9 @@ midori_bookmarks_open_in_tab_activate_cb (GtkWidget* menuitem, KatzeItem* child; KatzeArray* array; - array = midori_bookmarks_read_from_db (bookmarks, katze_item_get_name (item), NULL); + array = midori_bookmarks_read_from_db (bookmarks, + katze_item_get_meta_integer (item, "parentid"), NULL); + g_return_if_fail (KATZE_IS_ARRAY (array)); KATZE_ARRAY_FOREACH_ITEM (child, array) { @@ -843,7 +945,7 @@ midori_bookmarks_row_expanded_cb (GtkTreeView* treeview, model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)); gtk_tree_model_get (model, iter, 0, &item, -1); midori_bookmarks_read_from_db_to_model (bookmarks, GTK_TREE_STORE (model), - iter, katze_item_get_name (item), NULL); + iter, katze_item_get_meta_integer (item, "id"), NULL); g_object_unref (item); } @@ -885,7 +987,7 @@ midori_bookmarks_filter_timeout_cb (gpointer data) gtk_tree_store_clear (treestore); midori_bookmarks_read_from_db_to_model (bookmarks, - treestore, NULL, NULL, bookmarks->filter); + treestore, NULL, 0, bookmarks->filter); return FALSE; } diff --git a/panels/midori-bookmarks.h b/panels/midori-bookmarks.h index 1be04121..cf56dea9 100644 --- a/panels/midori-bookmarks.h +++ b/panels/midori-bookmarks.h @@ -40,15 +40,19 @@ midori_bookmarks_get_type (void); GtkWidget* midori_bookmarks_new (void); -void -midori_bookmarks_insert_item_db (sqlite3* db, - KatzeItem* item, - const gchar* folder); +gint64 +midori_bookmarks_insert_item_db (sqlite3* db, + KatzeItem* item, + gint64 parentid); void -midori_bookmarks_import_array_db (sqlite3* db, - KatzeArray* array, - const gchar* folder); +midori_bookmarks_import_array_db (sqlite3* db, + KatzeArray* array, + gint64 parentid); + +gboolean +midori_bookmarks_update_item_db (sqlite3* db, + KatzeItem* item); G_END_DECLS diff --git a/wscript b/wscript index a3513f5c..dfe53ab3 100644 --- a/wscript +++ b/wscript @@ -268,7 +268,7 @@ def configure (conf): if check_version (conf.env['LIBSOUP_VERSION'], 2, 37, 1): conf.define ('HAVE_LIBSOUP_2_37_1', 1) check_pkg ('libxml-2.0', '2.6') - check_pkg ('sqlite3', '3.0', True, var='SQLITE') + check_pkg ('sqlite3', '3.6.19', True, var='SQLITE') if option_enabled ('hildon'): if check_pkg ('hildon-1', mandatory=False, var='HILDON'):