Merge lp:~aauzi/midori/fix-1179200 into lp:midori

Proposed by André Auzi
Status: Superseded
Proposed branch: lp:~aauzi/midori/fix-1179200
Merge into: lp:midori
Diff against target: 2005 lines (+1212/-305)
9 files modified
katze/katze-array.c (+40/-0)
katze/katze-array.h (+4/-0)
katze/katze-item.c (+8/-2)
midori/midori-array.c (+9/-7)
midori/midori-bookmarks.c (+632/-45)
midori/midori-bookmarks.h (+10/-12)
midori/midori-browser.c (+77/-38)
panels/midori-bookmarks.c (+432/-187)
panels/midori-bookmarks.h (+0/-14)
To merge this branch: bzr merge lp:~aauzi/midori/fix-1179200
Reviewer Review Type Date Requested Status
André Auzi Needs Resubmitting
Midori Devs Pending
Review via email: mp+164969@code.launchpad.net

This proposal has been superseded by a proposal from 2013-06-05.

To post a comment you must log in.
Revision history for this message
Cris Dywan (kalikiana) wrote :

- if (item->parent)
- katze_array_update ((KatzeArray*)item->parent);
+ if (item->parent && g_strcmp0(icon, picon))
+ katze_array_update_item ((KatzeArray*)item->parent, item);

katze_array_update_item replaces katze_array_update if I see correctly, so that should be mentioned by marking the old one as Deprecated for clarity.

The checks like g_strcmp0(icon, picon) seem to avoid updating if nothing changed - in that case, why not do this for all values in each function?

Revision history for this message
Cris Dywan (kalikiana) wrote :

A side note on the comment "panels/midori-bookmarks seems to be a view": yes, historically the code is badly mixed, in an ideal world both the panel and the toolbar should be independent views. Much like the Transfers extension these days.

Revision history for this message
André Auzi (aauzi) wrote :

> - if (item->parent)
> - katze_array_update ((KatzeArray*)item->parent);
> + if (item->parent && g_strcmp0(icon, picon))
> + katze_array_update_item ((KatzeArray*)item->parent, item);
>
> katze_array_update_item replaces katze_array_update if I see correctly, so
> that should be mentioned by marking the old one as Deprecated for clarity.

Not exactly... I kept the update for one purpose, the update of the whole data structure.

Basically here, the update in the bookmark panel, clears the whole tree and reloads it.

It thought this use case was still needed for features like bookmark imports, especially the way it is still designed.

Basically, the array_tree is a container. update is signal signaling an item and its containees, update_item only the item.

You probably have noticed that the update_item in the panel basically:
* finds the item in the tree
* unlinks it from where it is
* finds its new parent
* and relinks the item at its new position.

It doesn't do much if the item and its new parent is not visible.

So far, the bookmark bar does not implement such behaviour, it only refresh the whol bar on item change. This would probablye have to be changed for correct DND reordering of the bookmark bar.

> The checks like g_strcmp0(icon, picon) seem to avoid updating if nothing
> changed - in that case, why not do this for all values in each function?

Well, I didn't do it because it would most probably generate a lot of useless signal traffic.
The design I choose make midori/midori-bookmarks robust to poor design that would do multiple updates of the items but I wouldn't not encourage it. I like the speed of midori as it is.

I therefore just kept the automatic signal of the container on containee change where it was in place (I believe it's due to site title and favicon update after page load, isn't it?)

Revision history for this message
André Auzi (aauzi) wrote :

> A side note on the comment "panels/midori-bookmarks seems to be a view": yes,
> historically the code is badly mixed, in an ideal world both the panel and the
> toolbar should be independent views. Much like the Transfers extension these
> days.

Yes, I've identified the Model-View-Controller design pattern in the way the code is organised:
* the data base is the Model
* the midori/midori-bookmarks is the Controller
* and bookmarkbar and bookmarkpanel are Views of this model.

With this design pattern actions on the model should go through the controller and that's what I've implemented for delete and update.
I did not do it for insert for two reasons:
1. I didn't intend to change the whole design. I believe there are historical reasons to designs decision that may not be obvious on quick examination. The less I touch the better everybody is :)
2. I do not have a satisfying solution for the provision of the parentid on the insert of a child item I've seen in the file import. No solution => no change.

Revision history for this message
Cris Dywan (kalikiana) wrote :

> > The checks like g_strcmp0(icon, picon) seem to avoid updating if nothing
> > changed - in that case, why not do this for all values in each function?

> Well, I didn't do it because it would most probably generate a lot of useless signal traffic.
> The design I choose make midori/midori-bookmarks robust to poor design that would do multiple updates of the > items but I wouldn't not encourage it. I like the speed of midori as it is.

> I therefore just kept the automatic signal of the container on containee change where it was in place
> (I believe it's due to site title and favicon update after page load, isn't it?)

What I meant was to wrap the whole of the values that change, for example:

{
     gchar* pname;
     g_return_if_fail (KATZE_IS_ITEM (item));
     pname = item->name;
     if (item->parent && g_strcmp0(name, pname))
     {
         katze_assign (item->name, g_strdup (name));
         katze_array_update((KatzeArray*)item->parent);
         g_object_notify (G_OBJECT (item), "name");
     }
}

I also realize you're doing "pname = item->name" before g_return_if_fail - don't, it will crash if the assertion is hit. Do it as I do above to be on the safe side.

Revision history for this message
André Auzi (aauzi) wrote :

> What I meant was to wrap the whole of the values that change, for example:
>
> {
> gchar* pname;
> g_return_if_fail (KATZE_IS_ITEM (item));
> pname = item->name;
> if (item->parent && g_strcmp0(name, pname))
> {
> katze_assign (item->name, g_strdup (name));
> katze_array_update((KatzeArray*)item->parent);
> g_object_notify (G_OBJECT (item), "name");
> }
> }
>
> I also realize you're doing "pname = item->name" before g_return_if_fail -
> don't, it will crash if the assertion is hit. Do it as I do above to be on the
> safe side.

OK, understood and agreed.
I will change that.

lp:~aauzi/midori/fix-1179200 updated
6171. By André Auzi

1. fix of change value protection in katze_item_set_name and katze_item_set_icon
2. replace remaining katze_array_update by katze_array_update_item in katze_item_set_name
   note: global update katze_array_update mechanism still in use for:
         a- initial populate of bookmarks and extensions,
         b- bookmarks imports
3. fix patch application regression
--- midori/midori-browser.c 2013-05-21 19:20:44 +0000
+++ midori/midori-browser.c 2013-05-28 18:58:04 +0000
@@ -6127,6 +6127,8 @@
         "array", dummy_array /* updated, unique */,
         NULL);
     g_object_connect (action,
+ "signal::populate-folder",
+ _action_bookmarks_populate_folder, browser,
                       "signal::activate-item-alt",
                       midori_bookmarkbar_activate_item_alt, browser,
                       NULL);
@@ -6143,8 +6145,6 @@
         "array", dummy_array /* updated, unique */,
         NULL);
     g_object_connect (action,
- "signal::populate-folder",
- _action_bookmarks_populate_folder, browser,
                       "signal::populate-popup",
                       _action_tools_populate_popup, browser,
                       "signal::activate-item-alt",

Revision history for this message
André Auzi (aauzi) wrote :
Download full text (4.1 KiB)

> > A side note on the comment "panels/midori-bookmarks seems to be a view":
> yes,
> > historically the code is badly mixed, in an ideal world both the panel and
> the
> > toolbar should be independent views. Much like the Transfers extension these
> > days.
>
> Yes, I've identified the Model-View-Controller design pattern in the way the
> code is organised:
> * the data base is the Model
> * the midori/midori-bookmarks is the Controller
> * and bookmarkbar and bookmarkpanel are Views of this model.
>
> With this design pattern actions on the model should go through the controller
> and that's what I've implemented for delete and update.
> I did not do it for insert for two reasons:

Let's do a little follow up on this topic.

Thanks to your remark in Bug #1185595 I felt encouraged to investigate the way bookmarks are retrieved from the database.

My investigations led me to two observations:
 1- katze_array_query_recursive is specialized to "bookmarks" table query due to the fact that the bookmarks table name is hard encoded in it
 2- katze_array_query_recursize lacks the feature of identifying folders item and populate them as katze_array of type KATZE_TYPE_ITEM.

My conclusions are the following:
 1- katze_array_query_recursive should be replaced by something like midori_bookmarks_query_recursive
 2- midori_bookmarks_query_recursive should allways select the column 'uri' of bookmarks in order to identify a folder item
 3- it should then parse the result rows for 'uri' value in order to determine if it can populate a katze_item or a katze_array of type KATZE_TYPE_ITEM

The connection with this bug fix proposal is the following.

The new function midori_bookmarks_query_recursive should be implemented in what I identify as the data base controller: midory/midory-bookmarks.c.

This would allow us to implement one additional steps forward in the direction of convergence of the bookmarks data and fix a concern pfor and I share, the triplication of data between bookmarks menu, bookmarkbar item and bookmarks side panel.

The hash table I used to implement packing of updates, or something similar, can also be used for items retrieval, before populating new items, midori_bookmarks_query_recursive should search the bookmarks array for their existence and only populate a new one a last resort.

Of course, the rows data should be inserted in an existing item, different client view may need different metadata.

To integrate well with the existing fix proposal, midori_bookmarks_query_recursive should flush the pending updates.

pfor, again, tipped me on how this flush could be sped up further: updates should be enclosed into SQLite transactions.

Finally, I was mentioning I did not have a solution for the insert, I think it is not the case anymore.

The katze_array_add_item callback of midori/midori-bookmarks.c should actually process the item added in order to make them available in the bookmarks array hash table.

This callback should change a folder implemented in katze_item into an empty katze_array of type
 KATZE_ARRAY_ITEM before inserting it in the database

It should also go through a katze_array of type KATZE_ARRAY:
 1- insert all child bookm...

Read more...

Revision history for this message
André Auzi (aauzi) wrote :

> The consequences on the client views are the following:
> 1- they should ignore add_item of katze_array which are not type
> KATZE_TYPE_ITEM
> 2- they should ignore add_item of katze_item of a folder
>

Additional note:

The consequence on client side may be totally removed if midori_bookmarks derives from katze_array and implements the preprocessing of folders (convert in katze_array of type KATZE_ARRAY_ITEM) and the population of the content of a katze_array is done in a specialization of katze_array_add_item.

By doing so we could ensure that *valid* items would be signaled to the client views.

lp:~aauzi/midori/fix-1179200 updated
6172. By André Auzi

merge lp:midori

6173. By André Auzi

merge lp:midori

6174. By André Auzi

prototype of bookmarks DB with transactions
+ minor fix in xbel import

6175. By André Auzi

merge lp:midori + fix Bookmarks vs Tools menue issue

Revision history for this message
André Auzi (aauzi) wrote :

Ok, I've prototyped the ideas shared in the previous comment.

Now, all the database operations are implemented in midori/midori-bookmarks.c using transactions.

The result is impressive, my 883 bookmarks are now imported in #500ms where it was previously taking more than 6s.

The katze_array_query(_recursive) are now replaced by a fixed midori_bookmarks_query_recursive which ensures that the items are not duplicated between the different views.

Well, this is not clean code yet (far from it) but I was so excited by the impressive improvement that I wanted to share it for maybe some stress test feedback.

I therefore resubmit it as it is and will implement suggested changes in a clean update.

review: Needs Resubmitting
lp:~aauzi/midori/fix-1179200 updated
6176. By André Auzi

cleanups and fix of remove and add item issue

6177. By André Auzi

merge lp:midori

6178. By André Auzi

more cleanups

Revision history for this message
André Auzi (aauzi) wrote :

Well, this is now clean enough.

lp:~aauzi/midori/fix-1179200 updated
6179. By André Auzi

implement complete derivation from KatzeArray for proper database interaction.
midori/midori-bookmarks.[ch] -> midori/midori-bookmarks-db.[ch]
class MidoriBookmarksDb created.

6180. By André Auzi

merge lp:midori
move midori_array_count_recursive into midori_bookmarks_db_count_recursive

6181. By André Auzi

fix relay method selection

6182. By André Auzi

revert changes in katze-item and katze-array,
localize them in midori-bookmarks-db

6183. By André Auzi

merge lp:midori

6184. By André Auzi

insert change 6208 from lp:midori

6185. By André Auzi

cosmetics coding style

6186. By André Auzi

adapt bookmarks unit tests and fix add item issue

6187. By André Auzi

cleanup update_item implementation

6188. By André Auzi

merge lp:midori

6189. By André Auzi

merge lp:midori

6190. By André Auzi

fix potential memory leak

Unmerged revisions

6190. By André Auzi

fix potential memory leak

6189. By André Auzi

merge lp:midori

6188. By André Auzi

merge lp:midori

6187. By André Auzi

cleanup update_item implementation

6186. By André Auzi

adapt bookmarks unit tests and fix add item issue

6185. By André Auzi

cosmetics coding style

6184. By André Auzi

insert change 6208 from lp:midori

6183. By André Auzi

merge lp:midori

6182. By André Auzi

revert changes in katze-item and katze-array,
localize them in midori-bookmarks-db

6181. By André Auzi

fix relay method selection

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'katze/katze-array.c'
2--- katze/katze-array.c 2012-08-08 23:02:56 +0000
3+++ katze/katze-array.c 2013-06-05 17:59:27 +0000
4@@ -42,6 +42,9 @@
5 (*add_item) (KatzeArray* array,
6 gpointer item);
7 void
8+ (*update_item) (KatzeArray* array,
9+ gpointer item);
10+ void
11 (*remove_item) (KatzeArray* array,
12 gpointer item);
13 void
14@@ -59,6 +62,7 @@
15
16 enum {
17 ADD_ITEM,
18+ UPDATE_ITEM,
19 REMOVE_ITEM,
20 MOVE_ITEM,
21 CLEAR,
22@@ -95,6 +99,13 @@
23 }
24
25 static void
26+_katze_array_update_item (KatzeArray* array,
27+ gpointer item)
28+{
29+ _katze_array_update (array);
30+}
31+
32+static void
33 _katze_array_remove_item (KatzeArray* array,
34 gpointer item)
35 {
36@@ -147,6 +158,17 @@
37 G_TYPE_NONE, 1,
38 G_TYPE_POINTER);
39
40+ signals[UPDATE_ITEM] = g_signal_new (
41+ "update-item",
42+ G_TYPE_FROM_CLASS (class),
43+ (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
44+ G_STRUCT_OFFSET (KatzeArrayClass, add_item),
45+ 0,
46+ NULL,
47+ g_cclosure_marshal_VOID__POINTER,
48+ G_TYPE_NONE, 1,
49+ G_TYPE_POINTER);
50+
51 signals[REMOVE_ITEM] = g_signal_new (
52 "remove-item",
53 G_TYPE_FROM_CLASS (class),
54@@ -213,6 +235,7 @@
55 gobject_class->finalize = katze_array_finalize;
56
57 class->add_item = _katze_array_add_item;
58+ class->update_item = _katze_array_update_item;
59 class->remove_item = _katze_array_remove_item;
60 class->move_item = _katze_array_move_item;
61 class->clear = _katze_array_clear;
62@@ -301,6 +324,23 @@
63 }
64
65 /**
66+ * katze_array_update_item:
67+ * @array: a #KatzeArray
68+ * @item: an item
69+ *
70+ * Notify an update of the item of the array.
71+ *
72+ **/
73+void
74+katze_array_update_item (KatzeArray* array,
75+ gpointer item)
76+{
77+ g_return_if_fail (KATZE_IS_ARRAY (array));
78+
79+ g_signal_emit (array, signals[UPDATE_ITEM], 0, item);
80+}
81+
82+/**
83 * katze_array_remove_item:
84 * @array: a #KatzeArray
85 * @item: an item
86
87=== modified file 'katze/katze-array.h'
88--- katze/katze-array.h 2011-01-19 20:58:26 +0000
89+++ katze/katze-array.h 2013-06-05 17:59:27 +0000
90@@ -47,6 +47,10 @@
91 gpointer item);
92
93 void
94+katze_array_update_item (KatzeArray* array,
95+ gpointer item);
96+
97+void
98 katze_array_remove_item (KatzeArray* array,
99 gpointer item);
100
101
102=== modified file 'katze/katze-item.c'
103--- katze/katze-item.c 2013-02-21 21:36:30 +0000
104+++ katze/katze-item.c 2013-06-05 17:59:27 +0000
105@@ -316,9 +316,12 @@
106 {
107 g_return_if_fail (KATZE_IS_ITEM (item));
108
109+ if (!g_strcmp0 (item->name, name))
110+ return;
111+
112 katze_assign (item->name, g_strdup (name));
113 if (item->parent)
114- katze_array_update ((KatzeArray*)item->parent);
115+ katze_array_update_item ((KatzeArray*)item->parent, item);
116 g_object_notify (G_OBJECT (item), "name");
117 }
118
119@@ -420,9 +423,12 @@
120 {
121 g_return_if_fail (KATZE_IS_ITEM (item));
122
123+ if (!g_strcmp0 (katze_item_get_meta_string (item, "icon"), icon))
124+ return;
125+
126 katze_item_set_meta_string (item, "icon", icon);
127 if (item->parent)
128- katze_array_update ((KatzeArray*)item->parent);
129+ katze_array_update_item ((KatzeArray*)item->parent, item);
130 g_object_notify (G_OBJECT (item), "icon");
131 }
132
133
134=== modified file 'midori/midori-array.c'
135--- midori/midori-array.c 2013-04-16 23:16:24 +0000
136+++ midori/midori-array.c 2013-06-05 17:59:27 +0000
137@@ -97,15 +97,15 @@
138 if (katze_str_equal ((gchar*)cur->name, "title"))
139 {
140 gchar* value = g_strstrip ((gchar*)xmlNodeGetContent (cur));
141+ katze_item_set_name (KATZE_ITEM (array), value);
142+ xmlFree (value);
143+ }
144+ else if (katze_str_equal ((gchar*)cur->name, "desc"))
145+ {
146+ gchar* value = g_strstrip ((gchar*)xmlNodeGetContent (cur));
147 katze_item_set_text (KATZE_ITEM (array), value);
148 xmlFree (value);
149 }
150- else if (katze_str_equal ((gchar*)cur->name, "desc"))
151- {
152- gchar* value = g_strstrip ((gchar*)xmlNodeGetContent (cur));
153- katze_item_set_name (KATZE_ITEM (array), value);
154- xmlFree (value);
155- }
156 else if (katze_str_equal ((gchar*)cur->name, "info"))
157 katze_xbel_parse_info ((KatzeItem*)array, cur);
158 else if (katze_str_equal ((gchar*)cur->name, "folder"))
159@@ -986,7 +986,7 @@
160 return FALSE;
161 }
162
163-static void
164+/* static */ void
165 katze_item_set_value_from_column (sqlite3_stmt* stmt,
166 gint column,
167 KatzeItem* item)
168@@ -1116,6 +1116,8 @@
169 * Return value: a #KatzeArray on success, %NULL otherwise
170 *
171 * Since: 0.4.4
172+ *
173+ * Deprecated: 0.5.2: Use midori_bookmarks_query_recursive() instead.
174 **/
175 KatzeArray*
176 midori_array_query_recursive (KatzeArray* bookmarks,
177
178=== modified file 'midori/midori-bookmarks.c'
179--- midori/midori-bookmarks.c 2012-11-25 15:37:41 +0000
180+++ midori/midori-bookmarks.c 2013-06-05 17:59:27 +0000
181@@ -25,6 +25,30 @@
182 #include <unistd.h>
183 #endif
184
185+#define MIDORI_BOOKMARKS_DB_CONTROL(a) ((MidoriBookmarksDBControl*)(a))
186+typedef struct _MidoriBookmarksDBControl MidoriBookmarksDBControl;
187+struct _MidoriBookmarksDBControl
188+{
189+ sqlite3* db;
190+ GList* pending_inserts;
191+ GHashTable* pending_updates;
192+ GHashTable* pending_deletes;
193+ GHashTable* all_items;
194+};
195+
196+static gint64
197+midori_bookmarks_insert_item_db (sqlite3* db,
198+ KatzeItem* item,
199+ gint64 parentid);
200+
201+static gboolean
202+midori_bookmarks_update_item_db (sqlite3* db,
203+ KatzeItem* item);
204+
205+static gboolean
206+midori_bookmarks_remove_item_db (sqlite3* db,
207+ KatzeItem* item);
208+
209 void
210 midori_bookmarks_dbtracer (void* dummy,
211 const char* query)
212@@ -32,35 +56,415 @@
213 g_printerr ("%s\n", query);
214 }
215
216-void
217+static guint
218+item_hash (gconstpointer item)
219+{
220+ gint64 id = katze_item_get_meta_integer (KATZE_ITEM (item), "id");
221+ return g_int64_hash (&id);
222+}
223+
224+static gboolean
225+item_equal (gconstpointer item_a, gconstpointer item_b)
226+{
227+ gint64 id_a = katze_item_get_meta_integer (KATZE_ITEM (item_a), "id");
228+ gint64 id_b = katze_item_get_meta_integer (KATZE_ITEM (item_b), "id");
229+ return (id_a == id_b)? TRUE : FALSE;
230+}
231+
232+static gboolean
233+midori_bookmarks_begin_transaction (sqlite3* db)
234+{
235+ char* errmsg = NULL;
236+
237+ if (sqlite3_exec (db, "BEGIN TRANSACTION;", NULL, NULL, &errmsg) != SQLITE_OK)
238+ {
239+ g_printerr (_("Failed to begin transaction: %s\n"), errmsg);
240+ sqlite3_free (errmsg);
241+ return FALSE;
242+ }
243+
244+ return TRUE;
245+}
246+
247+static gboolean
248+midori_bookmarks_end_transaction (sqlite3* db, gboolean commit)
249+{
250+ char* errmsg = NULL;
251+ if (sqlite3_exec (db, (commit ? "COMMIT;" : "ROLLBACK;"), NULL, NULL, &errmsg) != SQLITE_OK)
252+ {
253+ if (commit)
254+ g_printerr (_("Failed to end transaction: %s\n"), errmsg);
255+ else
256+ g_printerr (_("Failed to cancel transaction: %s\n"), errmsg);
257+ sqlite3_free (errmsg);
258+ return FALSE;
259+ }
260+
261+ return TRUE;
262+}
263+
264+static gint
265+midori_bookmarks_insert_item (MidoriBookmarksDBControl* control,
266+ KatzeItem* item)
267+{
268+ GList* list;
269+ KatzeArray* array;
270+ gint64 id = 0;
271+ gint count = 0;
272+ gint64 parentid = katze_item_get_meta_integer (item, "parentid");
273+
274+ id = midori_bookmarks_insert_item_db (control->db, item, parentid);
275+ count++;
276+
277+ g_object_ref (item);
278+ g_hash_table_insert (control->all_items, item, item);
279+
280+ if (!KATZE_IS_ARRAY (item))
281+ return count;
282+
283+ array = KATZE_ARRAY (item);
284+
285+ KATZE_ARRAY_FOREACH_ITEM_L (item, array, list)
286+ {
287+ katze_item_set_meta_integer (item, "parentid", id);
288+ count += midori_bookmarks_insert_item (control, item);
289+ }
290+
291+ g_list_free (list);
292+ return count;
293+}
294+
295+static void
296+midori_bookmarks_remove_item (KatzeItem* item,
297+ MidoriBookmarksDBControl* control)
298+{
299+ gint64 id = katze_item_get_meta_integer (item, "id");
300+ GHashTableIter hash_iter;
301+ gpointer key, value;
302+ gpointer found;
303+ GList* childs = NULL;
304+ GList* list_iter;
305+
306+ if (found = g_list_find (control->pending_inserts, item))
307+ {
308+ g_object_unref (((GList*)found)->data);
309+ control->pending_inserts = g_list_delete_link (control->pending_inserts,
310+ ((GList*)found));
311+ }
312+
313+ if (found = g_hash_table_lookup (control->pending_updates, item))
314+ {
315+ g_hash_table_remove (control->pending_updates, found);
316+ g_object_unref (found);
317+ }
318+
319+ if (found = g_hash_table_lookup (control->all_items, item))
320+ {
321+ g_hash_table_remove (control->all_items, found);
322+ g_object_unref (found);
323+ }
324+
325+ if (!KATZE_ITEM_IS_FOLDER (item))
326+ return;
327+
328+ g_hash_table_iter_init (&hash_iter, control->all_items);
329+
330+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
331+ {
332+ KatzeItem *hash_item = KATZE_ITEM (value);
333+ gint64 parentid = katze_item_get_meta_integer (hash_item, "parentid");
334+
335+ if (parentid != id)
336+ continue;
337+
338+ childs = g_list_append (childs, hash_item);
339+ }
340+
341+ for (list_iter = childs; list_iter; list_iter = g_list_next (list_iter))
342+ {
343+ KatzeItem *list_item = KATZE_ITEM (list_iter->data);
344+
345+ midori_bookmarks_remove_item (list_item, control);
346+ }
347+
348+ g_list_free (childs);
349+}
350+
351+static gboolean
352+midori_bookmarks_idle_func (gpointer data)
353+{
354+ GTimer *timer = g_timer_new();
355+ gint count = 0;
356+ gulong microseconds;
357+ gboolean with_transaction;
358+ KatzeArray* bookmarks = KATZE_ARRAY (data);
359+ MidoriBookmarksDBControl* control = g_object_get_data (G_OBJECT (bookmarks), "control");
360+ GList* list_iter;
361+ GHashTableIter hash_iter;
362+ gpointer key, value;
363+
364+ g_timer_start (timer);
365+
366+ with_transaction = midori_bookmarks_begin_transaction (control->db);
367+
368+ for (list_iter = control->pending_inserts; list_iter; list_iter = g_list_next (list_iter))
369+ {
370+ KatzeItem *item = KATZE_ITEM (list_iter->data);
371+
372+ count += midori_bookmarks_insert_item (control, item);
373+
374+ g_object_unref (item);
375+ }
376+
377+ g_hash_table_iter_init (&hash_iter, control->pending_updates);
378+
379+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
380+ {
381+ KatzeItem *item = KATZE_ITEM (value);
382+
383+ midori_bookmarks_update_item_db (control->db, item);
384+ g_object_unref (item);
385+ count++;
386+ }
387+
388+ g_hash_table_iter_init (&hash_iter, control->pending_deletes);
389+
390+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
391+ {
392+ KatzeItem *item = KATZE_ITEM (value);
393+
394+ midori_bookmarks_remove_item_db (control->db, item);
395+ g_object_unref (item);
396+ count++;
397+ }
398+
399+ if (with_transaction)
400+ midori_bookmarks_end_transaction (control->db, TRUE);
401+
402+ g_timer_elapsed (timer, &microseconds);
403+ g_print ("midori_bookmarks_idle: %d DB operation(s) in %lu micro-seconds\n",
404+ count, microseconds);
405+
406+ g_timer_destroy (timer);
407+
408+ g_hash_table_remove_all (control->pending_deletes);
409+ g_hash_table_remove_all (control->pending_updates);
410+ g_list_free (control->pending_inserts);
411+ control->pending_inserts = NULL;
412+
413+ return FALSE;
414+}
415+
416+static void
417+midori_bookmarks_idle_start (KatzeArray* array,
418+ MidoriBookmarksDBControl* control)
419+{
420+ if (control->pending_inserts
421+ || g_hash_table_size (control->pending_updates)
422+ || g_hash_table_size (control->pending_deletes))
423+ return;
424+
425+ g_idle_add (midori_bookmarks_idle_func, array);
426+}
427+
428+static void
429 midori_bookmarks_add_item_cb (KatzeArray* array,
430 KatzeItem* item,
431- sqlite3* db)
432-{
433- midori_bookmarks_insert_item_db (db, item,
434- katze_item_get_meta_integer (item, "parentid"));
435-}
436-
437-void
438+ MidoriBookmarksDBControl* control)
439+{
440+ gpointer found = g_list_find (control->pending_inserts, item);
441+
442+ if (found)
443+ return;
444+
445+ midori_bookmarks_idle_start (array, control);
446+
447+ g_object_ref (item);
448+ control->pending_inserts = g_list_append (control->pending_inserts, item);
449+}
450+
451+static void
452+midori_bookmarks_update_item_cb (KatzeArray* array,
453+ KatzeItem* item,
454+ MidoriBookmarksDBControl* control)
455+{
456+ gpointer found = g_hash_table_lookup (control->pending_updates, item);
457+
458+ if (found)
459+ return;
460+
461+ midori_bookmarks_idle_start (array, control);
462+
463+ g_object_ref (item);
464+ g_hash_table_insert (control->pending_updates, item, item);
465+}
466+
467+static void
468 midori_bookmarks_remove_item_cb (KatzeArray* array,
469 KatzeItem* item,
470- sqlite3* db)
471-{
472- gchar* sqlcmd;
473- char* errmsg = NULL;
474-
475-
476- sqlcmd = sqlite3_mprintf (
477- "DELETE FROM bookmarks WHERE id = %" G_GINT64_FORMAT ";",
478+ MidoriBookmarksDBControl* control)
479+{
480+ gpointer found = g_hash_table_lookup (control->pending_deletes, item);
481+
482+ if (found)
483+ return;
484+
485+ midori_bookmarks_idle_start (array, control);
486+
487+ midori_bookmarks_remove_item (item, control);
488+
489+ g_object_ref (item);
490+ g_hash_table_insert (control->pending_deletes, item, item);
491+}
492+
493+static gint64
494+midori_bookmarks_insert_item_db (sqlite3* db,
495+ KatzeItem* item,
496+ gint64 parentid)
497+{
498+ gchar* sqlcmd;
499+ char* errmsg = NULL;
500+ KatzeItem* old_parent;
501+ gchar* new_parentid;
502+ gchar* id = NULL;
503+ const gchar* uri = NULL;
504+ const gchar* desc = NULL;
505+ gint64 seq = 0;
506+
507+ /* Bookmarks must have a name, import may produce invalid items */
508+ g_return_val_if_fail (katze_item_get_name (item), seq);
509+
510+ if (!db)
511+ return seq;
512+
513+ if (katze_item_get_meta_integer (item, "id") > 0)
514+ id = g_strdup_printf ("%" G_GINT64_FORMAT, katze_item_get_meta_integer(item, "id"));
515+ else
516+ id = g_strdup_printf ("NULL");
517+
518+ if (KATZE_ITEM_IS_BOOKMARK (item))
519+ uri = katze_item_get_uri (item);
520+
521+ if (katze_item_get_text (item))
522+ desc = katze_item_get_text (item);
523+
524+ /* Use folder, otherwise fallback to parent folder */
525+ old_parent = katze_item_get_parent (item);
526+ if (parentid > 0)
527+ new_parentid = g_strdup_printf ("%" G_GINT64_FORMAT, parentid);
528+ else if (old_parent && katze_item_get_meta_integer (old_parent, "id") > 0)
529+ new_parentid = g_strdup_printf ("%" G_GINT64_FORMAT, katze_item_get_meta_integer (old_parent, "id"));
530+ else
531+ new_parentid = g_strdup_printf ("NULL");
532+
533+ sqlcmd = sqlite3_mprintf (
534+ "INSERT INTO bookmarks (id, parentid, title, uri, desc, toolbar, app) "
535+ "VALUES (%q, %q, '%q', '%q', '%q', %d, %d)",
536+ id,
537+ new_parentid,
538+ katze_item_get_name (item),
539+ katze_str_non_null (uri),
540+ katze_str_non_null (desc),
541+ katze_item_get_meta_boolean (item, "toolbar"),
542+ katze_item_get_meta_boolean (item, "app"));
543+
544+ if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) == SQLITE_OK)
545+ {
546+ /* Get insert id */
547+ if (g_str_equal (id, "NULL"))
548+ {
549+ KatzeArray* seq_array;
550+
551+ sqlite3_free (sqlcmd);
552+ sqlcmd = sqlite3_mprintf (
553+ "SELECT seq FROM sqlite_sequence WHERE name = 'bookmarks'");
554+
555+ seq_array = katze_array_from_sqlite (db, sqlcmd);
556+ if (katze_array_get_nth_item (seq_array, 0))
557+ {
558+ KatzeItem* seq_item = katze_array_get_nth_item (seq_array, 0);
559+
560+ seq = katze_item_get_meta_integer (seq_item, "seq");
561+ katze_item_set_meta_integer (item, "id", seq);
562+ }
563+ g_object_unref (seq_array);
564+ }
565+ }
566+ else
567+ {
568+ g_printerr (_("Failed to add bookmark item: %s\n"), errmsg);
569+ sqlite3_free (errmsg);
570+ }
571+
572+ sqlite3_free (sqlcmd);
573+ g_free (new_parentid);
574+ g_free (id);
575+
576+ return seq;
577+}
578+
579+static gboolean
580+midori_bookmarks_update_item_db (sqlite3* db,
581+ KatzeItem* item)
582+{
583+ gchar* sqlcmd;
584+ char* errmsg = NULL;
585+ gchar* parentid;
586+ gboolean updated;
587+
588+ if (katze_item_get_meta_integer (item, "parentid") > 0)
589+ parentid = g_strdup_printf ("%" G_GINT64_FORMAT,
590+ katze_item_get_meta_integer (item, "parentid"));
591+ else
592+ parentid = g_strdup_printf ("NULL");
593+
594+ sqlcmd = sqlite3_mprintf (
595+ "UPDATE bookmarks SET "
596+ "parentid=%q, title='%q', uri='%q', desc='%q', toolbar=%d, app=%d "
597+ "WHERE id = %" G_GINT64_FORMAT ";",
598+ parentid,
599+ katze_item_get_name (item),
600+ katze_str_non_null (katze_item_get_uri (item)),
601+ katze_str_non_null (katze_item_get_meta_string (item, "desc")),
602+ katze_item_get_meta_boolean (item, "toolbar"),
603+ katze_item_get_meta_boolean (item, "app"),
604 katze_item_get_meta_integer (item, "id"));
605
606- if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) != SQLITE_OK)
607- {
608- g_printerr (_("Failed to remove history item: %s\n"), errmsg);
609- sqlite3_free (errmsg);
610- }
611-
612- sqlite3_free (sqlcmd);
613+ updated = TRUE;
614+ if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) != SQLITE_OK)
615+ {
616+ updated = FALSE;
617+ g_printerr (_("Failed to update bookmark : %s\n"), errmsg);
618+ sqlite3_free (errmsg);
619+ }
620+
621+ sqlite3_free (sqlcmd);
622+ g_free (parentid);
623+
624+ return updated;
625+}
626+
627+static gboolean
628+midori_bookmarks_remove_item_db (sqlite3* db,
629+ KatzeItem* item)
630+{
631+ char* errmsg = NULL;
632+ gchar* sqlcmd;
633+ gboolean removed = TRUE;
634+
635+ sqlcmd = sqlite3_mprintf (
636+ "DELETE FROM bookmarks WHERE id = %" G_GINT64_FORMAT ";",
637+ katze_item_get_meta_integer (item, "id"));
638+
639+ if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) != SQLITE_OK)
640+ {
641+ g_printerr (_("Failed to remove bookmark item: %s\n"), errmsg);
642+ sqlite3_free (errmsg);
643+ removed = FALSE;
644+ }
645+
646+ sqlite3_free (sqlcmd);
647+ return removed;
648 }
649
650 #define _APPEND_TO_SQL_ERRORMSG(custom_errmsg) \
651@@ -141,6 +545,7 @@
652 gchar* sql_errmsg = NULL;
653 gchar* import_errmsg = NULL;
654 KatzeArray* array;
655+ MidoriBookmarksDBControl* control;
656
657 g_return_val_if_fail (errmsg != NULL, NULL);
658
659@@ -279,12 +684,21 @@
660 init_success:
661 g_free (newfile);
662 g_free (oldfile);
663+ control = MIDORI_BOOKMARKS_DB_CONTROL (g_malloc (sizeof (*control)));
664+ control->db = db;
665+ control->pending_inserts = NULL;
666+ control->pending_updates = g_hash_table_new (item_hash, item_equal);
667+ control->pending_deletes = g_hash_table_new (item_hash, item_equal);
668+ control->all_items = g_hash_table_new (item_hash, item_equal);
669 array = katze_array_new (KATZE_TYPE_ARRAY);
670 g_signal_connect (array, "add-item",
671- G_CALLBACK (midori_bookmarks_add_item_cb), db);
672+ G_CALLBACK (midori_bookmarks_add_item_cb), control);
673+ g_signal_connect (array, "update-item",
674+ G_CALLBACK (midori_bookmarks_update_item_cb), control);
675 g_signal_connect (array, "remove-item",
676- G_CALLBACK (midori_bookmarks_remove_item_cb), db);
677+ G_CALLBACK (midori_bookmarks_remove_item_cb), control);
678 g_object_set_data (G_OBJECT (array), "db", db);
679+ g_object_set_data (G_OBJECT (array), "control", control);
680 return array;
681
682 init_failed:
683@@ -298,24 +712,6 @@
684 }
685
686 void
687-midori_bookmarks_import (const gchar* filename,
688- sqlite3* db)
689-{
690- KatzeArray* bookmarks;
691- GError* error = NULL;
692-
693- bookmarks = katze_array_new (KATZE_TYPE_ARRAY);
694-
695- if (!midori_array_from_file (bookmarks, filename, "xbel", &error))
696- {
697- g_warning (_("The bookmarks couldn't be saved. %s"), error->message);
698- g_error_free (error);
699- return;
700- }
701- midori_bookmarks_import_array_db (db, bookmarks, 0);
702-}
703-
704-void
705 midori_bookmarks_on_quit (KatzeArray* array)
706 {
707 g_return_if_fail (KATZE_IS_ARRAY (array));
708@@ -323,5 +719,196 @@
709 sqlite3* db = g_object_get_data (G_OBJECT (array), "db");
710 g_return_if_fail (db != NULL);
711 sqlite3_close (db);
712-}
713-
714+ MidoriBookmarksDBControl* control = g_object_get_data (G_OBJECT (array), "control");
715+ g_return_if_fail (control != NULL);
716+ g_idle_remove_by_data (array);
717+ g_list_free (control->pending_inserts);
718+ g_hash_table_unref (control->pending_updates);
719+ g_hash_table_unref (control->pending_deletes);
720+ g_hash_table_unref (control->all_items);
721+}
722+
723+/**
724+ * midori_bookmarks_import_array:
725+ * @array: the main bookmark array
726+ * @array: #KatzeArray containing the items to import
727+ * @parentid: the id of folder
728+ *
729+ * Imports the items of @array as childs of the folder
730+ * identfied by @parentid.
731+ *
732+ * Since: 0.5.2
733+ **/
734+void
735+midori_bookmarks_import_array (KatzeArray* bookmarks,
736+ KatzeArray* array,
737+ gint64 parentid)
738+{
739+ GList* list;
740+ KatzeItem* item;
741+
742+ KATZE_ARRAY_FOREACH_ITEM_L (item, array, list)
743+ {
744+ katze_item_set_meta_integer (item, "parentid", parentid);
745+ katze_array_add_item (bookmarks, item);
746+ }
747+
748+ g_list_free (list);
749+}
750+
751+static void
752+midori_bookmarks_force_idle (KatzeArray* array,
753+ MidoriBookmarksDBControl* control)
754+{
755+ if (g_idle_remove_by_data (array))
756+ midori_bookmarks_idle_func (array);
757+}
758+
759+/* static */ void
760+katze_item_set_value_from_column (sqlite3_stmt* stmt,
761+ gint column,
762+ KatzeItem* item);
763+
764+static KatzeArray*
765+midori_bookmarks_array_from_statement (sqlite3_stmt* stmt,
766+ MidoriBookmarksDBControl* control)
767+{
768+ KatzeArray *array;
769+ gint result;
770+ gint cols;
771+
772+ array = katze_array_new (KATZE_TYPE_ITEM);
773+ cols = sqlite3_column_count (stmt);
774+
775+ while ((result = sqlite3_step (stmt)) == SQLITE_ROW)
776+ {
777+ gint i;
778+ KatzeItem* item;
779+ KatzeItem* found;
780+
781+ item = katze_item_new ();
782+ for (i = 0; i < cols; i++)
783+ katze_item_set_value_from_column (stmt, i, item);
784+
785+ if (found = g_hash_table_lookup (control->all_items, item))
786+ {
787+ for (i = 0; i < cols; i++)
788+ katze_item_set_value_from_column (stmt, i, found);
789+
790+ g_object_unref (item);
791+
792+ item = found;
793+ }
794+ else if (KATZE_ITEM_IS_FOLDER (item))
795+ {
796+ item = KATZE_ITEM (katze_array_new (KATZE_TYPE_ITEM));
797+
798+ for (i = 0; i < cols; i++)
799+ katze_item_set_value_from_column (stmt, i, item);
800+
801+ g_object_ref (item);
802+ g_hash_table_insert (control->all_items, item, item);
803+ }
804+ else
805+ {
806+ g_object_ref (item);
807+ g_hash_table_insert (control->all_items, item, item);
808+ }
809+
810+ katze_array_add_item (array, item);
811+ }
812+
813+ sqlite3_clear_bindings (stmt);
814+ sqlite3_reset (stmt);
815+ return array;
816+}
817+
818+static KatzeArray*
819+midori_bookmarks_array_from_sqlite (KatzeArray* array,
820+ const gchar* sqlcmd)
821+{
822+ MidoriBookmarksDBControl* control;
823+ sqlite3_stmt* stmt;
824+ gint result;
825+
826+ control = g_object_get_data (G_OBJECT (array), "control");
827+ g_return_val_if_fail (control != NULL, NULL);
828+
829+ midori_bookmarks_force_idle (array, control);
830+
831+ result = sqlite3_prepare_v2 (control->db, sqlcmd, -1, &stmt, NULL);
832+ if (result != SQLITE_OK)
833+ return NULL;
834+
835+ return midori_bookmarks_array_from_statement (stmt, control);
836+}
837+
838+/**
839+ * midori_bookmarks_query_recursive:
840+ * @array: the main bookmark array
841+ * @fields: comma separated list of fields
842+ * @condition: condition, like "folder = '%q'"
843+ * @value: a value to be inserted if @condition contains %q
844+ * @recursive: if %TRUE include children
845+ *
846+ * Stores the result in a #KatzeArray.
847+ *
848+ * Return value: a #KatzeArray on success, %NULL otherwise
849+ *
850+ * Since: 0.5.2
851+ **/
852+KatzeArray*
853+midori_bookmarks_query_recursive (KatzeArray* bookmarks,
854+ const gchar* fields,
855+ const gchar* condition,
856+ const gchar* value,
857+ gboolean recursive)
858+{
859+ gchar* sqlcmd;
860+ char* sqlcmd_value;
861+ KatzeArray* array;
862+ KatzeItem* item;
863+ GList* list;
864+
865+ g_return_val_if_fail (KATZE_IS_ARRAY (bookmarks), NULL);
866+ g_return_val_if_fail (fields, NULL);
867+ g_return_val_if_fail (condition, NULL);
868+
869+ sqlcmd = g_strdup_printf ("SELECT %s FROM bookmarks WHERE %s "
870+ "ORDER BY (uri='') ASC, title DESC", fields, condition);
871+ if (strstr (condition, "%q"))
872+ {
873+ sqlcmd_value = sqlite3_mprintf (sqlcmd, value ? value : "");
874+ array = midori_bookmarks_array_from_sqlite (bookmarks, sqlcmd_value);
875+ sqlite3_free (sqlcmd_value);
876+ }
877+ else
878+ array = midori_bookmarks_array_from_sqlite (bookmarks, sqlcmd);
879+ g_free (sqlcmd);
880+
881+ if (!recursive)
882+ return array;
883+
884+ KATZE_ARRAY_FOREACH_ITEM_L (item, array, list)
885+ {
886+ if (KATZE_ITEM_IS_FOLDER (item))
887+ {
888+ gchar* parentid = g_strdup_printf ("%" G_GINT64_FORMAT,
889+ katze_item_get_meta_integer (item, "id"));
890+ KatzeArray* subarray = midori_bookmarks_query_recursive (bookmarks,
891+ fields, "parentid=%q", parentid, TRUE);
892+ KatzeItem* subitem;
893+ GList* sublist;
894+
895+ KATZE_ARRAY_FOREACH_ITEM_L (subitem, subarray, sublist)
896+ {
897+ katze_array_add_item (KATZE_ARRAY (item), subitem);
898+ }
899+
900+ g_object_unref (subarray);
901+ g_free (parentid);
902+ }
903+ }
904+ g_list_free (list);
905+ return array;
906+}
907
908=== modified file 'midori/midori-bookmarks.h'
909--- midori/midori-bookmarks.h 2012-11-25 15:37:41 +0000
910+++ midori/midori-bookmarks.h 2013-06-05 17:59:27 +0000
911@@ -16,16 +16,6 @@
912 #include <sqlite3.h>
913 #include <katze/katze.h>
914
915-void
916-midori_bookmarks_add_item_cb (KatzeArray* array,
917- KatzeItem* item,
918- sqlite3* db);
919-
920-void
921-midori_bookmarks_remove_item_cb (KatzeArray* array,
922- KatzeItem* item,
923- sqlite3* db);
924-
925 KatzeArray*
926 midori_bookmarks_new (char** errmsg);
927
928@@ -33,8 +23,16 @@
929 midori_bookmarks_on_quit (KatzeArray* array);
930
931 void
932-midori_bookmarks_import (const gchar* filename,
933- sqlite3* db);
934+midori_bookmarks_import_array (KatzeArray* bookmarks,
935+ KatzeArray* array,
936+ gint64 parentid);
937+
938+KatzeArray*
939+midori_bookmarks_query_recursive (KatzeArray* bookmarks,
940+ const gchar* fields,
941+ const gchar* condition,
942+ const gchar* value,
943+ gboolean recursive);
944
945 #endif /* !__MIDORI_BOOKMARKS_H__ */
946
947
948=== modified file 'midori/midori-browser.c'
949--- midori/midori-browser.c 2013-05-31 03:40:27 +0000
950+++ midori/midori-browser.c 2013-06-05 17:59:27 +0000
951@@ -96,6 +96,8 @@
952 gboolean show_statusbar;
953 guint maximum_history_age;
954 guint last_web_search;
955+
956+ gboolean bookmarkbar_populate;
957 };
958
959 G_DEFINE_TYPE (MidoriBrowser, midori_browser, GTK_TYPE_WINDOW)
960@@ -162,20 +164,13 @@
961 GParamSpec* pspec);
962
963 void
964-midori_bookmarks_import_array_db (sqlite3* db,
965- KatzeArray* array,
966- gint64 parentid);
967-
968-gboolean
969-midori_bookmarks_update_item_db (sqlite3* db,
970- KatzeItem* item);
971-
972-void
973 midori_browser_open_bookmark (MidoriBrowser* browser,
974 KatzeItem* item);
975
976 static void
977 midori_bookmarkbar_populate (MidoriBrowser* browser);
978+static void
979+midori_bookmarkbar_populate_idle (MidoriBrowser* browser);
980
981 static void
982 midori_bookmarkbar_clear (GtkWidget* toolbar);
983@@ -484,6 +479,9 @@
984 inter = ZEITGEIST_ZG_DELETE_EVENT;
985 else
986 g_assert_not_reached ();
987+ g_assert (KATZE_IS_ITEM (item));
988+ if (KATZE_ITEM_IS_FOLDER (item))
989+ return;
990 zeitgeist_log_insert_events_no_reply (zeitgeist_log_get_default (),
991 zeitgeist_event_new_full (inter, ZEITGEIST_ZG_USER_ACTIVITY,
992 "application://midori.desktop",
993@@ -1061,16 +1059,11 @@
994 if (new_bookmark)
995 katze_array_add_item (browser->bookmarks, bookmark);
996 else
997- midori_bookmarks_update_item_db (db, bookmark);
998- midori_browser_update_history (bookmark, "bookmark", new_bookmark ? "create" : "modify");
999+ katze_array_update_item (browser->bookmarks, bookmark);
1000
1001- if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_toolbar)))
1002- if (!gtk_widget_get_visible (browser->bookmarkbar))
1003- _action_set_active (browser, "Bookmarkbar", TRUE);
1004 return_status = TRUE;
1005 }
1006- if (gtk_widget_get_visible (browser->bookmarkbar))
1007- midori_bookmarkbar_populate (browser);
1008+
1009 gtk_widget_destroy (dialog);
1010 return return_status;
1011 }
1012@@ -3096,8 +3089,8 @@
1013 else
1014 condition = "parentid = %q";
1015
1016- bookmarks = midori_array_query (browser->bookmarks,
1017- "id, title, parentid, uri, app, pos_panel, pos_bar", condition, id);
1018+ bookmarks = midori_bookmarks_query_recursive (browser->bookmarks,
1019+ "id, title, parentid, uri, app, pos_panel, pos_bar", condition, id, FALSE);
1020 if (!bookmarks)
1021 return FALSE;
1022
1023@@ -4486,7 +4479,7 @@
1024 if (error)
1025 g_error_free (error);
1026 }
1027- midori_bookmarks_import_array_db (db, bookmarks, selected);
1028+ midori_bookmarks_import_array (browser->bookmarks, bookmarks, selected);
1029 katze_array_update (browser->bookmarks);
1030 g_object_unref (bookmarks);
1031 g_free (path);
1032@@ -4541,7 +4534,7 @@
1033 return;
1034
1035 error = NULL;
1036- bookmarks = midori_array_query_recursive (browser->bookmarks,
1037+ bookmarks = midori_bookmarks_query_recursive (browser->bookmarks,
1038 "*", "parentid IS NULL", NULL, TRUE);
1039 if (!midori_array_to_file (bookmarks, path, format, &error))
1040 {
1041@@ -5968,6 +5961,21 @@
1042 }
1043 }
1044
1045+static gboolean
1046+midori_browser_idle (gpointer data)
1047+{
1048+ MidoriBrowser* browser = MIDORI_BROWSER (data);
1049+
1050+ if (browser->bookmarkbar_populate)
1051+ {
1052+ midori_bookmarkbar_populate_idle (browser);
1053+
1054+ browser->bookmarkbar_populate = FALSE;
1055+ }
1056+
1057+ return FALSE;
1058+}
1059+
1060 static void
1061 midori_browser_init (MidoriBrowser* browser)
1062 {
1063@@ -6448,6 +6456,8 @@
1064 katze_object_assign (browser->history, NULL);
1065 katze_object_assign (browser->dial, NULL);
1066
1067+ g_idle_remove_by_data (browser);
1068+
1069 G_OBJECT_CLASS (midori_browser_parent_class)->finalize (object);
1070 }
1071
1072@@ -6962,7 +6972,7 @@
1073 KatzeItem* item)
1074 {
1075 MidoriBrowser* browser = midori_browser_get_for_widget (toolbar);
1076- GtkAction* action = _action_by_name (browser, "Tools");
1077+ GtkAction* action = _action_by_name (browser, "Bookmarks");
1078 GtkToolItem* toolitem = katze_array_action_create_tool_item_for (
1079 KATZE_ARRAY_ACTION (action), item);
1080 g_object_set_data (G_OBJECT (toolitem), "KatzeItem", item);
1081@@ -6983,6 +6993,28 @@
1082 }
1083
1084 static void
1085+midori_bookmarkbar_add_item_cb (KatzeArray* bookmarks,
1086+ KatzeItem* item,
1087+ MidoriBrowser* browser)
1088+{
1089+ if (gtk_widget_get_visible (browser->bookmarkbar))
1090+ midori_bookmarkbar_populate (browser);
1091+ else if (katze_item_get_meta_boolean (item, "toolbar"))
1092+ _action_set_active (browser, "Bookmarkbar", TRUE);
1093+ midori_browser_update_history (item, "bookmark", "created");
1094+}
1095+
1096+static void
1097+midori_bookmarkbar_update_item_cb (KatzeArray* bookmarks,
1098+ KatzeItem* item,
1099+ MidoriBrowser* browser)
1100+{
1101+ if (gtk_widget_get_visible (browser->bookmarkbar))
1102+ midori_bookmarkbar_populate (browser);
1103+ midori_browser_update_history (item, "bookmark", "modify");
1104+}
1105+
1106+static void
1107 midori_bookmarkbar_remove_item_cb (KatzeArray* bookmarks,
1108 KatzeItem* item,
1109 MidoriBrowser* browser)
1110@@ -6995,6 +7027,16 @@
1111 static void
1112 midori_bookmarkbar_populate (MidoriBrowser* browser)
1113 {
1114+ if (browser->bookmarkbar_populate)
1115+ return;
1116+
1117+ g_idle_add (midori_browser_idle, browser);
1118+ browser->bookmarkbar_populate = TRUE;
1119+}
1120+
1121+static void
1122+midori_bookmarkbar_populate_idle (MidoriBrowser* browser)
1123+{
1124 KatzeArray* array;
1125 KatzeItem* item;
1126
1127@@ -7004,8 +7046,8 @@
1128 gtk_toolbar_insert (GTK_TOOLBAR (browser->bookmarkbar),
1129 gtk_separator_tool_item_new (), -1);
1130
1131- array = midori_array_query (browser->bookmarks,
1132- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "toolbar = 1", NULL);
1133+ array = midori_bookmarks_query_recursive (browser->bookmarks,
1134+ "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "toolbar = 1", NULL, FALSE);
1135 if (!array)
1136 {
1137 _action_set_sensitive (browser, "BookmarkAdd", FALSE);
1138@@ -7015,21 +7057,7 @@
1139
1140 KATZE_ARRAY_FOREACH_ITEM (item, array)
1141 {
1142- if (KATZE_ITEM_IS_BOOKMARK (item))
1143- midori_bookmarkbar_insert_item (browser->bookmarkbar, item);
1144- else
1145- {
1146- gint64 id = katze_item_get_meta_integer (item, "id");
1147- gchar* parentid = g_strdup_printf ("%" G_GINT64_FORMAT, id);
1148- KatzeArray* subfolder = midori_array_query (browser->bookmarks,
1149- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid = %q AND uri != ''",
1150- parentid);
1151-
1152- katze_item_set_name (KATZE_ITEM (subfolder), katze_item_get_name (item));
1153- katze_item_set_meta_integer (KATZE_ITEM (subfolder), "id", id);
1154- midori_bookmarkbar_insert_item (browser->bookmarkbar, KATZE_ITEM (subfolder));
1155- g_free (parentid);
1156- }
1157+ midori_bookmarkbar_insert_item (browser->bookmarkbar, item);
1158 }
1159 _action_set_sensitive (browser, "BookmarkAdd", TRUE);
1160 _action_set_sensitive (browser, "BookmarkFolderAdd", TRUE);
1161@@ -7064,8 +7092,15 @@
1162 MidoriWebSettings* settings;
1163
1164 if (browser->bookmarks != NULL)
1165+ {
1166+ g_signal_handlers_disconnect_by_func (browser->bookmarks,
1167+ midori_bookmarkbar_add_item_cb, browser);
1168+ g_signal_handlers_disconnect_by_func (browser->bookmarks,
1169+ midori_bookmarkbar_update_item_cb, browser);
1170 g_signal_handlers_disconnect_by_func (browser->bookmarks,
1171 midori_bookmarkbar_remove_item_cb, browser);
1172+ }
1173+
1174 settings = midori_browser_get_settings (browser);
1175 g_signal_handlers_disconnect_by_func (settings,
1176 midori_browser_show_bookmarkbar_notify_value_cb, browser);
1177@@ -7094,6 +7129,10 @@
1178 g_signal_connect (settings, "notify::show-bookmarkbar",
1179 G_CALLBACK (midori_browser_show_bookmarkbar_notify_value_cb), browser);
1180 g_object_notify (G_OBJECT (settings), "show-bookmarkbar");
1181+ g_signal_connect_after (bookmarks, "add-item",
1182+ G_CALLBACK (midori_bookmarkbar_add_item_cb), browser);
1183+ g_signal_connect_after (bookmarks, "update-item",
1184+ G_CALLBACK (midori_bookmarkbar_update_item_cb), browser);
1185 g_signal_connect_after (bookmarks, "remove-item",
1186 G_CALLBACK (midori_bookmarkbar_remove_item_cb), browser);
1187 }
1188
1189=== modified file 'panels/midori-bookmarks.c'
1190--- panels/midori-bookmarks.c 2013-02-11 21:49:41 +0000
1191+++ panels/midori-bookmarks.c 2013-06-05 17:59:27 +0000
1192@@ -25,6 +25,111 @@
1193
1194 #define COMPLETION_DELAY 200
1195
1196+G_BEGIN_DECLS
1197+
1198+#define MIDORI_BOOKMARKS_TREE_STORE_TYPE \
1199+ (midori_bookmarks_tree_store_get_type ())
1200+#define MIDORI_BOOKMARKS_TREE_STORE(obj) \
1201+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), MIDORI_BOOKMARKS_TREE_STORE_TYPE, MidoriBookmarksTreeStore))
1202+#define MIDORI_BOOKMARKS_TREE_STORE_CLASS(klass) \
1203+ (G_TYPE_CHECK_CLASS_CAST ((klass), MIDORI_BOOKMARKS_TREE_STORE_TYPE, MidoriBookmarksTreeStoreClass))
1204+
1205+static gboolean
1206+midori_bookmarks_tree_store_row_drop_possible (GtkTreeDragDest *drag_dest,
1207+ GtkTreePath *dest_path,
1208+ GtkSelectionData *selection_data);
1209+
1210+typedef struct _MidoriBookmarksTreeStore MidoriBookmarksTreeStore;
1211+typedef struct _MidoriBookmarksTreeStoreClass MidoriBookmarksTreeStoreClass;
1212+
1213+struct _MidoriBookmarksTreeStore
1214+{
1215+ GtkTreeStore parent_instance;
1216+};
1217+
1218+struct _MidoriBookmarksTreeStoreClass
1219+{
1220+ GtkTreeStoreClass parent_class;
1221+};
1222+
1223+static GtkTreeDragDestIface *
1224+gtk_tree_store_gtk_tree_drag_dest_iface = NULL;
1225+
1226+static void
1227+midori_bookmarks_tree_store_drag_dest_init (GtkTreeDragDestIface *iface)
1228+{
1229+ gtk_tree_store_gtk_tree_drag_dest_iface = g_type_interface_peek_parent (iface);
1230+
1231+ iface->row_drop_possible = midori_bookmarks_tree_store_row_drop_possible;
1232+}
1233+
1234+static void
1235+midori_bookmarks_tree_store_init (MidoriBookmarksTreeStore* item)
1236+{
1237+}
1238+
1239+static void
1240+midori_bookmarks_tree_store_class_init (MidoriBookmarksTreeStoreClass *class)
1241+{
1242+}
1243+
1244+G_DEFINE_TYPE_WITH_CODE (MidoriBookmarksTreeStore,
1245+ midori_bookmarks_tree_store,
1246+ GTK_TYPE_TREE_STORE,
1247+ G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST,
1248+ midori_bookmarks_tree_store_drag_dest_init));
1249+
1250+
1251+GtkTreeStore*
1252+midori_bookmarks_tree_store_new (gint n_columns, ...)
1253+{
1254+ GtkTreeStore* tree_store = GTK_TREE_STORE (g_object_new (MIDORI_BOOKMARKS_TREE_STORE_TYPE, NULL));
1255+ va_list ap;
1256+ GType* types;
1257+ gint n;
1258+
1259+ if (!tree_store)
1260+ return NULL;
1261+
1262+ types = g_new (GType, n_columns);
1263+
1264+ if (!types)
1265+ {
1266+ g_object_unref(tree_store);
1267+ return NULL;
1268+ }
1269+
1270+ va_start(ap, n_columns);
1271+ for (n = 0; n < n_columns; n++)
1272+ {
1273+ types[n] = va_arg(ap, GType);
1274+ }
1275+ va_end(ap);
1276+
1277+ gtk_tree_store_set_column_types (tree_store,
1278+ n_columns,
1279+ types);
1280+
1281+ g_free (types);
1282+ return tree_store;
1283+}
1284+
1285+
1286+GtkTreeStore*
1287+midori_bookmarks_tree_store_newv (gint n_columns, GType *types)
1288+{
1289+ GtkTreeStore* tree_store = GTK_TREE_STORE (g_object_new (MIDORI_BOOKMARKS_TREE_STORE_TYPE, NULL));
1290+
1291+ if (!tree_store)
1292+ return NULL;
1293+
1294+ gtk_tree_store_set_column_types (tree_store,
1295+ n_columns,
1296+ types);
1297+
1298+ return tree_store;
1299+}
1300+
1301 gboolean
1302 midori_browser_edit_bookmark_dialog_new (MidoriBrowser* browser,
1303 KatzeItem* bookmark,
1304@@ -36,6 +141,12 @@
1305 midori_browser_open_bookmark (MidoriBrowser* browser,
1306 KatzeItem* item);
1307
1308+static void
1309+midori_bookmarks_row_changed_cb (GtkTreeModel* model,
1310+ GtkTreePath* path,
1311+ GtkTreeIter* iter,
1312+ MidoriBookmarks* bookmarks);
1313+
1314 struct _MidoriBookmarks
1315 {
1316 GtkVBox parent_instance;
1317@@ -49,6 +160,8 @@
1318
1319 gint filter_timeout;
1320 gchar* filter;
1321+
1322+ GList* pending_inserts;
1323 };
1324
1325 struct _MidoriBookmarksClass
1326@@ -86,6 +199,13 @@
1327 GParamSpec* pspec);
1328
1329 static void
1330+midori_bookmarks_add_item_cb (KatzeArray* array,
1331+ KatzeItem* item,
1332+ MidoriBookmarks* bookmarks);
1333+static void
1334+midori_bookmarks_update_cb (KatzeArray* array,
1335+ MidoriBookmarks* bookmarks);
1336+static void
1337 midori_bookmarks_class_init (MidoriBookmarksClass* class)
1338 {
1339 GObjectClass* gobject_class;
1340@@ -120,6 +240,7 @@
1341 return STOCK_BOOKMARKS;
1342 }
1343
1344+#if 0
1345 /* TODO: Function never used */
1346 void
1347 midori_bookmarks_export_array_db (sqlite3* db,
1348@@ -133,7 +254,7 @@
1349 gchar* parent_id;
1350
1351 parent_id = g_strdup_printf ("%" G_GINT64_FORMAT, parentid);
1352- if (!(root_array = midori_array_query (array, "*", "parentid = %q", parent_id)))
1353+ if (!(root_array = midori_bookmarks_query_recursive (array, "*", "parentid = %q", parent_id, FALSE)))
1354 {
1355 g_free (parent_id);
1356 return;
1357@@ -155,27 +276,7 @@
1358 g_free (parent_id);
1359 g_list_free (list);
1360 }
1361-
1362-void
1363-midori_bookmarks_import_array_db (sqlite3* db,
1364- KatzeArray* array,
1365- gint64 parentid)
1366-{
1367- GList* list;
1368- KatzeItem* item;
1369- gint64 id;
1370-
1371- if (!db)
1372- return;
1373-
1374- KATZE_ARRAY_FOREACH_ITEM_L (item, array, list)
1375- {
1376- id = midori_bookmarks_insert_item_db (db, item, parentid);
1377- if (KATZE_IS_ARRAY (item))
1378- midori_bookmarks_import_array_db (db, KATZE_ARRAY (item), id);
1379- }
1380- g_list_free (list);
1381-}
1382+#endif
1383
1384 static KatzeArray*
1385 midori_bookmarks_read_from_db (MidoriBookmarks* bookmarks,
1386@@ -185,21 +286,21 @@
1387 KatzeArray* array;
1388
1389 if (keyword && *keyword)
1390- array = midori_array_query (bookmarks->array,
1391- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "title LIKE '%%%q%%'", keyword);
1392+ array = midori_bookmarks_query_recursive (bookmarks->array,
1393+ "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "title LIKE '%%%q%%'", keyword, FALSE);
1394 else
1395 {
1396 if (parentid > 0)
1397 {
1398 gchar* parent_id = g_strdup_printf ("%" G_GINT64_FORMAT, parentid);
1399- array = midori_array_query (bookmarks->array,
1400- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid = %q", parent_id);
1401+ array = midori_bookmarks_query_recursive (bookmarks->array,
1402+ "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid = %q", parent_id, FALSE);
1403
1404 g_free (parent_id);
1405 }
1406 else
1407- array = midori_array_query (bookmarks->array,
1408- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid IS NULL", NULL);
1409+ array = midori_bookmarks_query_recursive (bookmarks->array,
1410+ "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid IS NULL", NULL, FALSE);
1411 }
1412 return array ? array : katze_array_new (KATZE_TYPE_ITEM);
1413 }
1414@@ -230,90 +331,133 @@
1415 g_object_unref (item);
1416 }
1417
1418-gint64
1419-midori_bookmarks_insert_item_db (sqlite3* db,
1420- KatzeItem* item,
1421- gint64 parentid)
1422+static gboolean
1423+midori_bookmarks_reach_item_recurse (GtkTreeModel* model,
1424+ GtkTreeIter* iter,
1425+ gint64 id)
1426 {
1427- gchar* sqlcmd;
1428- char* errmsg = NULL;
1429- KatzeItem* old_parent;
1430- gchar* new_parentid;
1431- gchar* id = NULL;
1432- const gchar* uri = NULL;
1433- const gchar* desc = NULL;
1434- gint64 seq = 0;
1435-
1436- /* Bookmarks must have a name, import may produce invalid items */
1437- g_return_val_if_fail (katze_item_get_name (item), seq);
1438-
1439- if (!db)
1440- return seq;
1441-
1442- if (katze_item_get_meta_integer (item, "id") > 0)
1443- id = g_strdup_printf ("%" G_GINT64_FORMAT, katze_item_get_meta_integer(item, "id"));
1444- else
1445- id = g_strdup_printf ("NULL");
1446-
1447- if (KATZE_ITEM_IS_BOOKMARK (item))
1448- uri = katze_item_get_uri (item);
1449-
1450- if (katze_item_get_text (item))
1451- desc = katze_item_get_text (item);
1452-
1453- /* Use folder, otherwise fallback to parent folder */
1454- old_parent = katze_item_get_parent (item);
1455- if (parentid > 0)
1456- new_parentid = g_strdup_printf ("%" G_GINT64_FORMAT, parentid);
1457- else if (old_parent && katze_item_get_meta_integer (old_parent, "id") > 0)
1458- new_parentid = g_strdup_printf ("%" G_GINT64_FORMAT, katze_item_get_meta_integer (old_parent, "id"));
1459- else
1460- new_parentid = g_strdup_printf ("NULL");
1461-
1462- sqlcmd = sqlite3_mprintf (
1463- "INSERT INTO bookmarks (id, parentid, title, uri, desc, toolbar, app) "
1464- "VALUES (%q, %q, '%q', '%q', '%q', %d, %d)",
1465- id,
1466- new_parentid,
1467- katze_item_get_name (item),
1468- katze_str_non_null (uri),
1469- katze_str_non_null (desc),
1470- katze_item_get_meta_boolean (item, "toolbar"),
1471- katze_item_get_meta_boolean (item, "app"));
1472-
1473- if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) == SQLITE_OK)
1474+ do
1475 {
1476- /* Get insert id */
1477- if (g_str_equal (id, "NULL"))
1478- {
1479- KatzeArray* seq_array;
1480-
1481- sqlite3_free (sqlcmd);
1482- sqlcmd = sqlite3_mprintf (
1483- "SELECT seq FROM sqlite_sequence WHERE name = 'bookmarks'");
1484-
1485- seq_array = katze_array_from_sqlite (db, sqlcmd);
1486- if (katze_array_get_nth_item (seq_array, 0))
1487+ GtkTreeIter child;
1488+ KatzeItem *item;
1489+ gint64 itemid = -1;
1490+
1491+ gtk_tree_model_get (model, iter, 0, &item, -1);
1492+
1493+ if (!KATZE_ITEM_IS_SEPARATOR(item))
1494+ {
1495+ itemid = katze_item_get_meta_integer (item, "id");
1496+ g_object_unref (item);
1497+ }
1498+
1499+ if (id == itemid)
1500+ return TRUE;
1501+
1502+ if (gtk_tree_model_iter_children (model, &child, iter))
1503+ {
1504+ if (midori_bookmarks_reach_item_recurse (model, &child, id))
1505 {
1506- KatzeItem* seq_item = katze_array_get_nth_item (seq_array, 0);
1507-
1508- seq = katze_item_get_meta_integer (seq_item, "seq");
1509- katze_item_set_meta_integer (item, "id", seq);
1510+ *iter = child;
1511+ return TRUE;
1512 }
1513- g_object_unref (seq_array);
1514 }
1515 }
1516- else
1517- {
1518- g_printerr (_("Failed to add bookmark item: %s\n"), errmsg);
1519- sqlite3_free (errmsg);
1520- }
1521-
1522- sqlite3_free (sqlcmd);
1523- g_free (new_parentid);
1524- g_free (id);
1525-
1526- return seq;
1527+ while (gtk_tree_model_iter_next(model, iter));
1528+
1529+ return FALSE;
1530+}
1531+
1532+static gboolean
1533+midori_bookmarks_reach_item (GtkTreeModel* model,
1534+ GtkTreeIter* iter,
1535+ gint64 id)
1536+{
1537+ if (!gtk_tree_model_get_iter_first(model, iter))
1538+ return FALSE;
1539+
1540+ return midori_bookmarks_reach_item_recurse (model, iter, id);
1541+}
1542+
1543+static void
1544+midori_bookmarks_add_item_to_model(GtkTreeStore* model,
1545+ GtkTreeIter* parent,
1546+ KatzeItem* item)
1547+{
1548+ if (KATZE_ITEM_IS_BOOKMARK (item))
1549+ {
1550+ gchar* tooltip = g_markup_escape_text (katze_item_get_uri (item), -1);
1551+
1552+ gtk_tree_store_insert_with_values (model, NULL, parent,
1553+ 0,
1554+ 0, item, 1, tooltip, -1);
1555+ g_free (tooltip);
1556+ }
1557+ else
1558+ {
1559+ GtkTreeIter root_iter;
1560+
1561+ gtk_tree_store_insert_with_values (model, &root_iter, parent,
1562+ 0, 0, item, -1);
1563+
1564+ /* That's an invisible dummy, so we always have an expander */
1565+ gtk_tree_store_insert_with_values (model, NULL, &root_iter,
1566+ 0,
1567+ 0, NULL, -1);
1568+ }
1569+}
1570+
1571+static void
1572+midori_bookmarks_update_item_in_model(MidoriBookmarks* bookmarks,
1573+ GtkTreeStore* model,
1574+ GtkTreeIter* iter,
1575+ KatzeItem* item)
1576+{
1577+ g_signal_handlers_block_by_func (model,
1578+ midori_bookmarks_row_changed_cb,
1579+ bookmarks);
1580+
1581+ if (KATZE_ITEM_IS_BOOKMARK (item))
1582+ {
1583+ gchar* tooltip = g_markup_escape_text (katze_item_get_uri (item), -1);
1584+
1585+ gtk_tree_store_set(model, iter,
1586+ 0, item, 1, tooltip, -1);
1587+
1588+ g_free (tooltip);
1589+ }
1590+ else
1591+ {
1592+ gtk_tree_store_set(model, iter,
1593+ 0, item, -1);
1594+ }
1595+
1596+ g_signal_handlers_unblock_by_func (model,
1597+ midori_bookmarks_row_changed_cb,
1598+ bookmarks);
1599+}
1600+
1601+static void
1602+midori_bookmarks_add_item (KatzeItem* item,
1603+ MidoriBookmarks* bookmarks);
1604+
1605+static gboolean
1606+midori_bookmarks_idle (MidoriBookmarks* bookmarks)
1607+{
1608+ GList* list_iter;
1609+
1610+ for (list_iter = bookmarks->pending_inserts; list_iter; list_iter = g_list_next (list_iter))
1611+ {
1612+ KatzeItem *item = KATZE_ITEM (list_iter->data);
1613+
1614+ midori_bookmarks_add_item (item, bookmarks);
1615+
1616+ g_object_unref (item);
1617+ }
1618+
1619+ g_list_free (bookmarks->pending_inserts);
1620+ bookmarks->pending_inserts = NULL;
1621+
1622+ return FALSE;
1623 }
1624
1625 static void
1626@@ -321,11 +465,98 @@
1627 KatzeItem* item,
1628 MidoriBookmarks* bookmarks)
1629 {
1630- GtkTreeModel* model;
1631- model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
1632- gtk_tree_store_clear (GTK_TREE_STORE (model));
1633- midori_bookmarks_read_from_db_to_model (bookmarks,
1634- GTK_TREE_STORE (model), NULL, 0, bookmarks->filter);
1635+ if (!bookmarks->pending_inserts)
1636+ g_idle_add ((GSourceFunc)midori_bookmarks_idle, bookmarks);
1637+
1638+ g_object_ref (item);
1639+ bookmarks->pending_inserts = g_list_append (bookmarks->pending_inserts, item);
1640+}
1641+
1642+static void
1643+midori_bookmarks_add_item (KatzeItem* item,
1644+ MidoriBookmarks* bookmarks)
1645+{
1646+ gint64 id = katze_item_get_meta_integer (item, "id");
1647+ gint64 parentid = katze_item_get_meta_integer (item, "parentid");
1648+ GtkTreeModel* model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
1649+ GtkTreeIter iter;
1650+
1651+ if (!parentid)
1652+ {
1653+ midori_bookmarks_add_item_to_model (GTK_TREE_STORE (model), NULL, item);
1654+ }
1655+ else if (midori_bookmarks_reach_item (model, &iter, parentid))
1656+ {
1657+ GtkTreePath* path = gtk_tree_model_get_path(model, &iter);
1658+
1659+ if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (bookmarks->treeview), path))
1660+ {
1661+ midori_bookmarks_add_item_to_model (GTK_TREE_STORE (model), &iter, item);
1662+ }
1663+
1664+ gtk_tree_path_free (path);
1665+ }
1666+}
1667+
1668+static void
1669+midori_bookmarks_update_item_cb (KatzeArray* array,
1670+ KatzeItem* item,
1671+ MidoriBookmarks* bookmarks)
1672+{
1673+ gint64 id = katze_item_get_meta_integer (item, "id");
1674+ gint64 parentid = katze_item_get_meta_integer (item, "parentid");
1675+ GtkTreeModel* model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
1676+ GtkTreeIter iter;
1677+
1678+ if (midori_bookmarks_reach_item (model, &iter, id))
1679+ {
1680+ gint64 old_parentid = 0;
1681+ GtkTreeIter parent;
1682+
1683+ if (gtk_tree_model_iter_parent (model, &parent, &iter))
1684+ {
1685+ KatzeItem* old_parent;
1686+
1687+ gtk_tree_model_get (model, &parent, 0, &old_parent, -1);
1688+
1689+ old_parentid = katze_item_get_meta_integer (old_parent, "id");
1690+
1691+ g_object_unref (old_parent);
1692+
1693+ if (parentid == old_parentid)
1694+ {
1695+ midori_bookmarks_update_item_in_model (bookmarks, GTK_TREE_STORE (model), &iter, item);
1696+ }
1697+ else
1698+ {
1699+ gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
1700+
1701+ if (!gtk_tree_model_iter_has_child (model, &parent))
1702+ {
1703+ GtkTreePath* path = gtk_tree_model_get_path(model, &parent);
1704+
1705+ if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (bookmarks->treeview), path))
1706+ gtk_tree_view_collapse_row (GTK_TREE_VIEW (bookmarks->treeview), path);
1707+
1708+ gtk_tree_path_free (path);
1709+ }
1710+
1711+ midori_bookmarks_add_item (item, bookmarks);
1712+ }
1713+ }
1714+ else if (parentid == 0)
1715+ {
1716+ midori_bookmarks_update_item_in_model (bookmarks, GTK_TREE_STORE (model), &iter, item);
1717+ }
1718+ else
1719+ {
1720+ gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
1721+
1722+ midori_bookmarks_add_item (item, bookmarks);
1723+ }
1724+ }
1725+ else
1726+ midori_bookmarks_add_item (item, bookmarks);
1727 }
1728
1729 static void
1730@@ -333,12 +564,36 @@
1731 KatzeItem* item,
1732 MidoriBookmarks* bookmarks)
1733 {
1734+ gint64 id = katze_item_get_meta_integer (item, "id");
1735 GtkTreeModel* model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
1736- gtk_tree_store_clear (GTK_TREE_STORE (model));
1737- midori_bookmarks_read_from_db_to_model (bookmarks,
1738- GTK_TREE_STORE (model), NULL, 0, bookmarks->filter);
1739+ GtkTreeIter iter;
1740+
1741+ if (midori_bookmarks_reach_item (model, &iter, id))
1742+ {
1743+ GtkTreeIter parent;
1744+
1745+ if (gtk_tree_model_iter_parent (model, &parent, &iter))
1746+ {
1747+ gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
1748+
1749+ if (!gtk_tree_model_iter_has_child (model, &parent))
1750+ {
1751+ GtkTreePath* path = gtk_tree_model_get_path(model, &parent);
1752+
1753+ if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (bookmarks->treeview), path))
1754+ gtk_tree_view_collapse_row (GTK_TREE_VIEW (bookmarks->treeview), path);
1755+
1756+ gtk_tree_path_free (path);
1757+ }
1758+ }
1759+ else
1760+ {
1761+ gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
1762+ }
1763+ }
1764 }
1765
1766+
1767 static void
1768 midori_bookmarks_update_cb (KatzeArray* array,
1769 MidoriBookmarks* bookmarks)
1770@@ -350,6 +605,42 @@
1771 }
1772
1773
1774+static gboolean
1775+midori_bookmarks_tree_store_row_drop_possible (GtkTreeDragDest* drag_dest,
1776+ GtkTreePath* dest_path,
1777+ GtkSelectionData* selection_data)
1778+{
1779+ gboolean row_drop_possible =
1780+ gtk_tree_store_gtk_tree_drag_dest_iface->row_drop_possible (drag_dest,
1781+ dest_path,
1782+ selection_data);
1783+
1784+ if (!row_drop_possible)
1785+ return FALSE;
1786+
1787+ if ((!gtk_tree_path_get_depth(dest_path) > 1)
1788+ && gtk_tree_path_up(dest_path))
1789+ {
1790+ GtkTreeModel* model = GTK_TREE_MODEL(drag_dest);
1791+ GtkTreeIter iter;
1792+
1793+ if (gtk_tree_model_get_iter (model, &iter, dest_path))
1794+ {
1795+ KatzeItem* item;
1796+
1797+ gtk_tree_model_get (model, &iter, 0, &item, -1);
1798+
1799+ if (!KATZE_ITEM_IS_FOLDER(item))
1800+ row_drop_possible = FALSE;
1801+
1802+ if (item)
1803+ g_object_unref (item);
1804+ }
1805+ }
1806+
1807+ return row_drop_possible;
1808+}
1809+
1810 static void
1811 midori_bookmarks_row_changed_cb (GtkTreeModel* model,
1812 GtkTreePath* path,
1813@@ -358,31 +649,37 @@
1814 {
1815 KatzeItem* item;
1816 GtkTreeIter parent;
1817- KatzeItem* new_parent = NULL;
1818- gint64 parentid;
1819-
1820- gtk_tree_model_get (model, iter, 0, &item, -1);
1821+ gint64 parentid = 0;
1822
1823 if (gtk_tree_model_iter_parent (model, &parent, iter))
1824 {
1825+ KatzeItem* new_parent;
1826+
1827 gtk_tree_model_get (model, &parent, 0, &new_parent, -1);
1828
1829- /* Bookmarks must not be moved into non-folder items */
1830- if (!KATZE_ITEM_IS_FOLDER (new_parent))
1831- parentid = 0;
1832- else
1833- parentid = katze_item_get_meta_integer (new_parent, "id");
1834+ /* Bookmarks cannot be moved into non-folder items */
1835+ g_assert (KATZE_ITEM_IS_FOLDER (new_parent));
1836+
1837+ parentid = katze_item_get_meta_integer (new_parent, "id");
1838+
1839+ g_object_unref (new_parent);
1840 }
1841- else
1842- parentid = 0;
1843-
1844- katze_array_remove_item (bookmarks->array, item);
1845+
1846+ gtk_tree_model_get (model, iter, 0, &item, -1);
1847+
1848 katze_item_set_meta_integer (item, "parentid", parentid);
1849- katze_array_add_item (bookmarks->array, item);
1850+
1851+ g_signal_handlers_block_by_func (bookmarks->array,
1852+ midori_bookmarks_update_item_cb,
1853+ bookmarks);
1854+
1855+ katze_array_update_item (bookmarks->array, item);
1856+
1857+ g_signal_handlers_unblock_by_func (bookmarks->array,
1858+ midori_bookmarks_update_item_cb,
1859+ bookmarks);
1860
1861 g_object_unref (item);
1862- if (new_parent)
1863- g_object_unref (new_parent);
1864 }
1865
1866 static void
1867@@ -408,24 +705,15 @@
1868 {
1869 KatzeItem* item;
1870 MidoriBrowser* browser;
1871- gint64 parentid;
1872
1873 gtk_tree_model_get (model, &iter, 0, &item, -1);
1874
1875 g_assert (!KATZE_ITEM_IS_SEPARATOR (item));
1876
1877 browser = midori_browser_get_for_widget (bookmarks->treeview);
1878- parentid = katze_item_get_meta_integer (item, "parentid");
1879 midori_browser_edit_bookmark_dialog_new (
1880 browser, item, FALSE, KATZE_ITEM_IS_FOLDER (item), NULL);
1881
1882- if (katze_item_get_meta_integer (item, "parentid") != parentid)
1883- {
1884- gtk_tree_store_clear (GTK_TREE_STORE (model));
1885- midori_bookmarks_read_from_db_to_model (bookmarks, GTK_TREE_STORE (model),
1886- NULL, 0, NULL);
1887- }
1888-
1889 g_object_unref (item);
1890 }
1891 }
1892@@ -441,47 +729,6 @@
1893 gtk_widget_set_sensitive (GTK_WIDGET (bookmarks->edit), selected);
1894 }
1895
1896-gboolean
1897-midori_bookmarks_update_item_db (sqlite3* db,
1898- KatzeItem* item)
1899-{
1900- gchar* sqlcmd;
1901- char* errmsg = NULL;
1902- gchar* parentid;
1903- gboolean updated;
1904-
1905- if (katze_item_get_meta_integer (item, "parentid") > 0)
1906- parentid = g_strdup_printf ("%" G_GINT64_FORMAT,
1907- katze_item_get_meta_integer (item, "parentid"));
1908- else
1909- parentid = g_strdup_printf ("NULL");
1910-
1911- sqlcmd = sqlite3_mprintf (
1912- "UPDATE bookmarks SET "
1913- "parentid=%q, title='%q', uri='%q', desc='%q', toolbar=%d, app=%d "
1914- "WHERE id = %" G_GINT64_FORMAT ";",
1915- parentid,
1916- katze_item_get_name (item),
1917- katze_str_non_null (katze_item_get_uri (item)),
1918- katze_str_non_null (katze_item_get_meta_string (item, "desc")),
1919- katze_item_get_meta_boolean (item, "toolbar"),
1920- katze_item_get_meta_boolean (item, "app"),
1921- katze_item_get_meta_integer (item, "id"));
1922-
1923- updated = TRUE;
1924- if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) != SQLITE_OK)
1925- {
1926- updated = FALSE;
1927- g_printerr (_("Failed to update bookmark : %s\n"), errmsg);
1928- sqlite3_free (errmsg);
1929- }
1930-
1931- sqlite3_free (sqlcmd);
1932- g_free (parentid);
1933-
1934- return updated;
1935-}
1936-
1937 static void
1938 midori_bookmarks_delete_clicked_cb (GtkWidget* toolitem,
1939 MidoriBookmarks* bookmarks)
1940@@ -496,13 +743,8 @@
1941
1942 gtk_tree_model_get (model, &iter, 0, &item, -1);
1943
1944- /* Manually remove the iter and block clearing the treeview */
1945- gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
1946- g_signal_handlers_block_by_func (bookmarks->array,
1947- midori_bookmarks_remove_item_cb, bookmarks);
1948 katze_array_remove_item (bookmarks->array, item);
1949- g_signal_handlers_unblock_by_func (bookmarks->array,
1950- midori_bookmarks_remove_item_cb, bookmarks);
1951+
1952 g_object_unref (item);
1953 }
1954 }
1955@@ -599,6 +841,8 @@
1956 midori_bookmarks_read_from_db_to_model (bookmarks, GTK_TREE_STORE (model), NULL, 0, NULL);
1957 g_signal_connect_after (bookmarks->array, "add-item",
1958 G_CALLBACK (midori_bookmarks_add_item_cb), bookmarks);
1959+ g_signal_connect_after (bookmarks->array, "update-item",
1960+ G_CALLBACK (midori_bookmarks_update_item_cb), bookmarks);
1961 g_signal_connect (bookmarks->array, "remove-item",
1962 G_CALLBACK (midori_bookmarks_remove_item_cb), bookmarks);
1963 g_signal_connect (bookmarks->array, "update",
1964@@ -1015,7 +1259,7 @@
1965 gtk_box_pack_start (GTK_BOX (bookmarks), box, FALSE, FALSE, 5);
1966
1967 /* Create the treeview */
1968- model = gtk_tree_store_new (2, KATZE_TYPE_ITEM, G_TYPE_STRING);
1969+ model = midori_bookmarks_tree_store_new (2, KATZE_TYPE_ITEM, G_TYPE_STRING);
1970 treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
1971 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
1972 gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (treeview), 1);
1973@@ -1055,6 +1299,7 @@
1974 gtk_widget_show (treeview);
1975 gtk_box_pack_start (GTK_BOX (bookmarks), treeview, TRUE, TRUE, 0);
1976 bookmarks->treeview = treeview;
1977+ bookmarks->pending_inserts = NULL;
1978 }
1979
1980 static void
1981
1982=== modified file 'panels/midori-bookmarks.h'
1983--- panels/midori-bookmarks.h 2012-11-25 11:26:03 +0000
1984+++ panels/midori-bookmarks.h 2013-06-05 17:59:27 +0000
1985@@ -37,20 +37,6 @@
1986 GType
1987 midori_bookmarks_get_type (void);
1988
1989-gint64
1990-midori_bookmarks_insert_item_db (sqlite3* db,
1991- KatzeItem* item,
1992- gint64 parentid);
1993-
1994-void
1995-midori_bookmarks_import_array_db (sqlite3* db,
1996- KatzeArray* array,
1997- gint64 parentid);
1998-
1999-gboolean
2000-midori_bookmarks_update_item_db (sqlite3* db,
2001- KatzeItem* item);
2002-
2003 G_END_DECLS
2004
2005 #endif /* __MIDORI_BOOKMARKS_PANEL_H__ */

Subscribers

People subscribed via source and target branches

to all changes: