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: 3290 lines (+1933/-637)
12 files modified
katze/katze-array.c (+37/-30)
katze/katze-array.h (+37/-0)
katze/katze-item.c (+8/-2)
midori/midori-array.c (+17/-248)
midori/midori-array.h (+5/-20)
midori/midori-bookmarks-db.c (+1213/-69)
midori/midori-bookmarks-db.h (+60/-21)
midori/midori-browser.c (+87/-48)
midori/midori-frontend.c (+5/-5)
midori/midori.h (+1/-1)
panels/midori-bookmarks.c (+463/-179)
panels/midori-bookmarks.h (+0/-14)
To merge this branch: bzr merge lp:~aauzi/midori/fix-1179200
Reviewer Review Type Date Requested Status
Midori Devs Pending
André Auzi Pending
Review via email: mp+167609@code.launchpad.net

This proposal supersedes a proposal from 2013-05-21.

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

- 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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

> - 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 : Posted in a previous version of this proposal

> 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 : Posted in a previous version of this proposal

> > 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 : Posted in a previous version of this proposal

> 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.

Revision history for this message
André Auzi (aauzi) wrote : Posted in a previous version of this proposal
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 : Posted in a previous version of this proposal

> 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.

Revision history for this message
André Auzi (aauzi) wrote : Posted in a previous version of this proposal

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
Revision history for this message
André Auzi (aauzi) wrote : Posted in a previous version of this proposal

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-11 20:53:24 +0000
4@@ -25,40 +25,11 @@
5 * #KatzeArray is a type aware container for items.
6 */
7
8-struct _KatzeArray
9-{
10- KatzeItem parent_instance;
11-
12- GType type;
13- GList* items;
14-};
15-
16-struct _KatzeArrayClass
17-{
18- KatzeItemClass parent_class;
19-
20- /* Signals */
21- void
22- (*add_item) (KatzeArray* array,
23- gpointer item);
24- void
25- (*remove_item) (KatzeArray* array,
26- gpointer item);
27- void
28- (*move_item) (KatzeArray* array,
29- gpointer item,
30- gint index);
31- void
32- (*clear) (KatzeArray* array);
33-
34- void
35- (*update) (KatzeArray* array);
36-};
37-
38 G_DEFINE_TYPE (KatzeArray, katze_array, KATZE_TYPE_ITEM);
39
40 enum {
41 ADD_ITEM,
42+ UPDATE_ITEM,
43 REMOVE_ITEM,
44 MOVE_ITEM,
45 CLEAR,
46@@ -95,6 +66,13 @@
47 }
48
49 static void
50+_katze_array_update_item (KatzeArray* array,
51+ gpointer item)
52+{
53+ _katze_array_update (array);
54+}
55+
56+static void
57 _katze_array_remove_item (KatzeArray* array,
58 gpointer item)
59 {
60@@ -147,6 +125,17 @@
61 G_TYPE_NONE, 1,
62 G_TYPE_POINTER);
63
64+ signals[UPDATE_ITEM] = g_signal_new (
65+ "update-item",
66+ G_TYPE_FROM_CLASS (class),
67+ (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
68+ G_STRUCT_OFFSET (KatzeArrayClass, update_item),
69+ 0,
70+ NULL,
71+ g_cclosure_marshal_VOID__POINTER,
72+ G_TYPE_NONE, 1,
73+ G_TYPE_POINTER);
74+
75 signals[REMOVE_ITEM] = g_signal_new (
76 "remove-item",
77 G_TYPE_FROM_CLASS (class),
78@@ -213,6 +202,7 @@
79 gobject_class->finalize = katze_array_finalize;
80
81 class->add_item = _katze_array_add_item;
82+ class->update_item = _katze_array_update_item;
83 class->remove_item = _katze_array_remove_item;
84 class->move_item = _katze_array_move_item;
85 class->clear = _katze_array_clear;
86@@ -301,6 +291,23 @@
87 }
88
89 /**
90+ * katze_array_update_item:
91+ * @array: a #KatzeArray
92+ * @item: an item
93+ *
94+ * Notify an update of the item of the array.
95+ *
96+ **/
97+void
98+katze_array_update_item (KatzeArray* array,
99+ gpointer item)
100+{
101+ g_return_if_fail (KATZE_IS_ARRAY (array));
102+
103+ g_signal_emit (array, signals[UPDATE_ITEM], 0, item);
104+}
105+
106+/**
107 * katze_array_remove_item:
108 * @array: a #KatzeArray
109 * @item: an item
110
111=== modified file 'katze/katze-array.h'
112--- katze/katze-array.h 2011-01-19 20:58:26 +0000
113+++ katze/katze-array.h 2013-06-11 20:53:24 +0000
114@@ -32,6 +32,39 @@
115 typedef struct _KatzeArray KatzeArray;
116 typedef struct _KatzeArrayClass KatzeArrayClass;
117
118+struct _KatzeArray
119+{
120+ KatzeItem parent_instance;
121+
122+ GType type;
123+ GList* items;
124+};
125+
126+struct _KatzeArrayClass
127+{
128+ KatzeItemClass parent_class;
129+
130+ /* Signals */
131+ void
132+ (*add_item) (KatzeArray* array,
133+ gpointer item);
134+ void
135+ (*update_item) (KatzeArray* array,
136+ gpointer item);
137+ void
138+ (*remove_item) (KatzeArray* array,
139+ gpointer item);
140+ void
141+ (*move_item) (KatzeArray* array,
142+ gpointer item,
143+ gint index);
144+ void
145+ (*clear) (KatzeArray* array);
146+
147+ void
148+ (*update) (KatzeArray* array);
149+};
150+
151 GType
152 katze_array_get_type (void) G_GNUC_CONST;
153
154@@ -47,6 +80,10 @@
155 gpointer item);
156
157 void
158+katze_array_update_item (KatzeArray* array,
159+ gpointer item);
160+
161+void
162 katze_array_remove_item (KatzeArray* array,
163 gpointer item);
164
165
166=== modified file 'katze/katze-item.c'
167--- katze/katze-item.c 2013-05-31 22:00:38 +0000
168+++ katze/katze-item.c 2013-06-11 20:53:24 +0000
169@@ -316,9 +316,12 @@
170 {
171 g_return_if_fail (KATZE_IS_ITEM (item));
172
173+ if (!g_strcmp0 (item->name, name))
174+ return;
175+
176 katze_assign (item->name, g_strdup (name));
177 if (item->parent)
178- katze_array_update ((KatzeArray*)item->parent);
179+ katze_array_update_item ((KatzeArray*)item->parent, item);
180 g_object_notify (G_OBJECT (item), "name");
181 }
182
183@@ -420,9 +423,12 @@
184 {
185 g_return_if_fail (KATZE_IS_ITEM (item));
186
187+ if (!g_strcmp0 (katze_item_get_meta_string (item, "icon"), icon))
188+ return;
189+
190 katze_item_set_meta_string (item, "icon", icon);
191 if (item->parent)
192- katze_array_update ((KatzeArray*)item->parent);
193+ katze_array_update_item ((KatzeArray*)item->parent, item);
194 g_object_notify (G_OBJECT (item), "icon");
195 }
196
197
198=== modified file 'midori/midori-array.c'
199--- midori/midori-array.c 2013-05-30 21:57:04 +0000
200+++ midori/midori-array.c 2013-06-11 20:53:24 +0000
201@@ -97,15 +97,15 @@
202 if (katze_str_equal ((gchar*)cur->name, "title"))
203 {
204 gchar* value = g_strstrip ((gchar*)xmlNodeGetContent (cur));
205+ katze_item_set_name (KATZE_ITEM (array), value);
206+ xmlFree (value);
207+ }
208+ else if (katze_str_equal ((gchar*)cur->name, "desc"))
209+ {
210+ gchar* value = g_strstrip ((gchar*)xmlNodeGetContent (cur));
211 katze_item_set_text (KATZE_ITEM (array), value);
212 xmlFree (value);
213 }
214- else if (katze_str_equal ((gchar*)cur->name, "desc"))
215- {
216- gchar* value = g_strstrip ((gchar*)xmlNodeGetContent (cur));
217- katze_item_set_name (KATZE_ITEM (array), value);
218- xmlFree (value);
219- }
220 else if (katze_str_equal ((gchar*)cur->name, "info"))
221 katze_xbel_parse_info ((KatzeItem*)array, cur);
222 else if (katze_str_equal ((gchar*)cur->name, "folder"))
223@@ -986,7 +986,17 @@
224 return FALSE;
225 }
226
227-static void
228+/**
229+ * katze_item_set_value_from_columne:
230+ * @stmt: prepared statement
231+ * @column: column to read
232+ * @item: #KatzeItem to populate
233+ *
234+ * Stores the column in the given #KatzeItem.
235+ *
236+ * Since: 0.2.7
237+ **/
238+void
239 katze_item_set_value_from_column (sqlite3_stmt* stmt,
240 gint column,
241 KatzeItem* item)
242@@ -1103,244 +1113,3 @@
243 return katze_array_from_statement (stmt);
244 }
245
246-/**
247- * midori_array_query_recursive:
248- * @array: the main bookmark array
249- * @fields: comma separated list of fields
250- * @condition: condition, like "folder = '%q'"
251- * @value: a value to be inserted if @condition contains %q
252- * @recursive: if %TRUE include children
253- *
254- * Stores the result in a #KatzeArray.
255- *
256- * Return value: a #KatzeArray on success, %NULL otherwise
257- *
258- * Since: 0.4.4
259- **/
260-KatzeArray*
261-midori_array_query_recursive (KatzeArray* bookmarks,
262- const gchar* fields,
263- const gchar* condition,
264- const gchar* value,
265- gboolean recursive)
266-{
267- sqlite3* db;
268- gchar* sqlcmd;
269- char* sqlcmd_value;
270- KatzeArray* array;
271- KatzeItem* item;
272- GList* list;
273-
274- g_return_val_if_fail (KATZE_IS_ARRAY (bookmarks), NULL);
275- g_return_val_if_fail (fields, NULL);
276- g_return_val_if_fail (condition, NULL);
277- db = g_object_get_data (G_OBJECT (bookmarks), "db");
278- g_return_val_if_fail (db != NULL, NULL);
279-
280- sqlcmd = g_strdup_printf ("SELECT %s FROM bookmarks WHERE %s "
281- "ORDER BY (uri='') ASC, title DESC", fields, condition);
282- if (strstr (condition, "%q"))
283- {
284- sqlcmd_value = sqlite3_mprintf (sqlcmd, value ? value : "");
285- array = katze_array_from_sqlite (db, sqlcmd_value);
286- sqlite3_free (sqlcmd_value);
287- }
288- else
289- array = katze_array_from_sqlite (db, sqlcmd);
290- g_free (sqlcmd);
291-
292- if (!recursive)
293- return array;
294-
295- KATZE_ARRAY_FOREACH_ITEM_L (item, array, list)
296- {
297- if (KATZE_ITEM_IS_FOLDER (item))
298- {
299- gchar* parentid = g_strdup_printf ("%" G_GINT64_FORMAT,
300- katze_item_get_meta_integer (item, "id"));
301- KatzeArray* subarray = midori_array_query_recursive (bookmarks,
302- fields, "parentid=%q", parentid, TRUE);
303- katze_item_set_name (KATZE_ITEM (subarray), katze_item_get_name (item));
304- katze_array_add_item (array, subarray);
305-
306- g_free (parentid);
307- }
308- }
309- g_list_free (list);
310- return array;
311-}
312-
313-/**
314- * midori_array_query:
315- * @array: the main bookmark array
316- * @fields: comma separated list of fields
317- * @condition: condition, like "folder = '%q'"
318- * @value: a value to be inserted if @condition contains %q
319- *
320- * Stores the result in a #KatzeArray.
321- *
322- * Return value: a #KatzeArray on success, %NULL otherwise
323- *
324- * Since: 0.4.3
325- *
326- * Deprecated: 0.4.4: Use midori_array_query_recursive() instead.
327- **/
328-KatzeArray*
329-midori_array_query (KatzeArray* bookmarks,
330- const gchar* fields,
331- const gchar* condition,
332- const gchar* value)
333-{
334- return midori_array_query_recursive (bookmarks, fields, condition, value, FALSE);
335-}
336-
337-static gint64
338-count_from_sqlite (sqlite3* db,
339- const gchar* sqlcmd)
340-{
341- gint64 count = -1;
342- sqlite3_stmt* stmt;
343- gint result;
344-
345- result = sqlite3_prepare_v2 (db, sqlcmd, -1, &stmt, NULL);
346- if (result != SQLITE_OK)
347- return -1;
348-
349- g_assert (sqlite3_column_count (stmt) == 1);
350-
351- if ((result = sqlite3_step (stmt)) == SQLITE_ROW)
352- count = sqlite3_column_int64(stmt, 0);
353-
354- sqlite3_clear_bindings (stmt);
355- sqlite3_reset (stmt);
356-
357- return count;
358-}
359-
360-static gint64
361-midori_array_count_recursive_by_id (KatzeArray* bookmarks,
362- const gchar* condition,
363- const gchar* value,
364- gint64 id,
365- gboolean recursive)
366-{
367- gint64 count = -1;
368- sqlite3* db;
369- gchar* sqlcmd;
370- char* sqlcmd_value;
371- sqlite3_stmt* stmt;
372- gint result;
373- GList* ids;
374- GList* iter_ids;
375-
376- g_return_val_if_fail (condition, -1);
377- g_return_val_if_fail (KATZE_IS_ARRAY (bookmarks), -1);
378- db = g_object_get_data (G_OBJECT (bookmarks), "db");
379- g_return_val_if_fail (db != NULL, -1);
380-
381- g_assert(!strstr("parentid", condition));
382-
383- if (id > 0)
384- sqlcmd = g_strdup_printf ("SELECT COUNT(*) FROM bookmarks "
385- "WHERE parentid = %" G_GINT64_FORMAT " AND %s",
386- id,
387- condition);
388- else
389- sqlcmd = g_strdup_printf ("SELECT COUNT(*) FROM bookmarks "
390- "WHERE parentid IS NULL AND %s ",
391- condition);
392-
393- if (strstr (condition, "%q"))
394- {
395- sqlcmd_value = sqlite3_mprintf (sqlcmd, value ? value : "");
396- count = count_from_sqlite (db, sqlcmd_value);
397- sqlite3_free (sqlcmd_value);
398- }
399- else
400- count = count_from_sqlite (db, sqlcmd);
401-
402- g_free (sqlcmd);
403-
404- if (!recursive || (count < 0))
405- return count;
406-
407- ids = NULL;
408-
409- if (id > 0)
410- sqlcmd_value = sqlite3_mprintf (
411- "SELECT id FROM bookmarks "
412- "WHERE parentid = %" G_GINT64_FORMAT " AND uri = ''", id);
413- else
414- sqlcmd_value = sqlite3_mprintf (
415- "SELECT id FROM bookmarks "
416- "WHERE parentid IS NULL AND uri = ''");
417-
418- if (sqlite3_prepare_v2 (db, sqlcmd_value, -1, &stmt, NULL) == SQLITE_OK)
419- {
420- g_assert (sqlite3_column_count (stmt) == 1);
421-
422- if ((result = sqlite3_step (stmt)) == SQLITE_ROW)
423- {
424- gint64* pid = g_new (gint64, 1);
425-
426- *pid = sqlite3_column_int64(stmt, 0);
427- ids = g_list_append (ids, pid);
428- }
429-
430- sqlite3_clear_bindings (stmt);
431- sqlite3_reset (stmt);
432- }
433-
434- sqlite3_free (sqlcmd_value);
435-
436- iter_ids = ids;
437- while (iter_ids)
438- {
439- gint64 sub_count = midori_array_count_recursive_by_id (bookmarks,
440- condition,
441- value,
442- *(gint64*)(iter_ids->data),
443- recursive);
444-
445- if (sub_count < 0)
446- {
447- g_list_free_full (ids, g_free);
448- return -1;
449- }
450-
451- count += sub_count;
452- iter_ids = g_list_next (iter_ids);
453- }
454-
455- g_list_free_full (ids, g_free);
456- return count;
457-}
458-
459-/**
460- * midori_array_count_recursive:
461- * @array: the main bookmark array
462- * @condition: condition, like "folder = '%q'"
463- * @value: a value to be inserted if @condition contains %q
464- * @recursive: if %TRUE include children
465- *
466- * Return value: the number of elements on success, -1 otherwise
467- *
468- * Since: 0.5.2
469- **/
470-gint64
471-midori_array_count_recursive (KatzeArray* bookmarks,
472- const gchar* condition,
473- const gchar* value,
474- KatzeItem* folder,
475- gboolean recursive)
476-{
477- gint64 id = -1;
478-
479- g_return_val_if_fail (!folder || KATZE_ITEM_IS_FOLDER (folder), -1);
480-
481- id = folder ? katze_item_get_meta_integer (folder, "id") : 0;
482-
483- return midori_array_count_recursive_by_id (bookmarks, condition,
484- value, id,
485- recursive);
486-}
487
488=== modified file 'midori/midori-array.h'
489--- midori/midori-array.h 2013-05-21 21:46:26 +0000
490+++ midori/midori-array.h 2013-06-11 20:53:24 +0000
491@@ -27,31 +27,16 @@
492 const gchar* format,
493 GError** error);
494
495+void
496+katze_item_set_value_from_column (sqlite3_stmt* stmt,
497+ gint column,
498+ KatzeItem* item);
499+
500 KatzeArray*
501 katze_array_from_statement (sqlite3_stmt* stmt);
502
503 KatzeArray*
504-midori_array_query (KatzeArray* array,
505- const gchar* fields,
506- const gchar* condition,
507- const gchar* value);
508-
509-KatzeArray*
510-midori_array_query_recursive (KatzeArray* array,
511- const gchar* fields,
512- const gchar* condition,
513- const gchar* value,
514- gboolean recursive);
515-
516-KatzeArray*
517 katze_array_from_sqlite (sqlite3* db,
518 const gchar* sqlcmd);
519
520-gint64
521-midori_array_count_recursive (KatzeArray* bookmarks,
522- const gchar* condition,
523- const gchar* value,
524- KatzeItem* folder,
525- gboolean recursive);
526-
527 #endif /* !__MIDORI_ARRAY_H__ */
528
529=== renamed file 'midori/midori-bookmarks.c' => 'midori/midori-bookmarks-db.c'
530--- midori/midori-bookmarks.c 2012-11-25 15:37:41 +0000
531+++ midori/midori-bookmarks-db.c 2013-06-11 20:53:24 +0000
532@@ -10,8 +10,8 @@
533 See the file COPYING for the full license text.
534 */
535
536-#include "midori-bookmarks.h"
537-#include "panels/midori-bookmarks.h"
538+#include "midori-bookmarks-db.h"
539+
540 #include "midori-app.h"
541 #include "midori-array.h"
542 #include "sokoke.h"
543@@ -25,42 +25,818 @@
544 #include <unistd.h>
545 #endif
546
547-void
548-midori_bookmarks_dbtracer (void* dummy,
549- const char* query)
550-{
551- g_printerr ("%s\n", query);
552-}
553-
554-void
555-midori_bookmarks_add_item_cb (KatzeArray* array,
556- KatzeItem* item,
557- sqlite3* db)
558-{
559- midori_bookmarks_insert_item_db (db, item,
560- katze_item_get_meta_integer (item, "parentid"));
561-}
562-
563-void
564-midori_bookmarks_remove_item_cb (KatzeArray* array,
565- KatzeItem* item,
566- sqlite3* db)
567-{
568- gchar* sqlcmd;
569- char* errmsg = NULL;
570-
571-
572- sqlcmd = sqlite3_mprintf (
573- "DELETE FROM bookmarks WHERE id = %" G_GINT64_FORMAT ";",
574+/**
575+ * SECTION:midory-bookmarks-db
576+ * @short_description: A #KatzeArray connected to a database
577+ * @see_also: #KatzeArray
578+ *
579+ * #MidoriBookmarksDb is a #KatzeArray specialized for database
580+ * interraction.
581+ */
582+
583+struct _MidoriBookmarksDb
584+{
585+ KatzeArray parent_instance;
586+
587+ sqlite3* db;
588+ GList* pending_inserts;
589+ GHashTable* pending_updates;
590+ GHashTable* pending_deletes;
591+ GHashTable* all_items;
592+ gboolean in_idle_func;
593+};
594+
595+struct _MidoriBookmarksDbClass
596+{
597+ KatzeArrayClass parent_class;
598+};
599+
600+G_DEFINE_TYPE (MidoriBookmarksDb, midori_bookmarks_db, KATZE_TYPE_ARRAY);
601+
602+static void
603+_midori_bookmarks_db_add_item (KatzeArray* array,
604+ gpointer item);
605+
606+static void
607+_midori_bookmarks_db_update_item (KatzeArray* array,
608+ gpointer item);
609+
610+static void
611+_midori_bookmarks_db_remove_item (KatzeArray* array,
612+ gpointer item);
613+
614+static void
615+_midori_bookmarks_db_move_item (KatzeArray* array,
616+ gpointer item,
617+ gint position);
618+
619+static void
620+_midori_bookmarks_db_clear (KatzeArray* array);
621+
622+static void
623+midori_bookmarks_db_force_idle (MidoriBookmarksDb* bookmarks);
624+
625+static void
626+midori_bookmarks_db_finalize (GObject* object);
627+
628+static gint64
629+midori_bookmarks_db_insert_item_db (sqlite3* db,
630+ KatzeItem* item,
631+ gint64 parentid);
632+
633+static gboolean
634+midori_bookmarks_db_update_item_db (sqlite3* db,
635+ KatzeItem* item);
636+
637+static gboolean
638+midori_bookmarks_db_remove_item_db (sqlite3* db,
639+ KatzeItem* item);
640+
641+static guint
642+item_hash (gconstpointer item)
643+{
644+ gint64 id = katze_item_get_meta_integer (KATZE_ITEM (item), "id");
645+ return g_int64_hash (&id);
646+}
647+
648+static gboolean
649+item_equal (gconstpointer item_a, gconstpointer item_b)
650+{
651+ gint64 id_a = katze_item_get_meta_integer (KATZE_ITEM (item_a), "id");
652+ gint64 id_b = katze_item_get_meta_integer (KATZE_ITEM (item_b), "id");
653+ return (id_a == id_b)? TRUE : FALSE;
654+}
655+
656+static void
657+midori_bookmarks_db_class_init (MidoriBookmarksDbClass* class)
658+{
659+ GObjectClass* gobject_class;
660+ KatzeArrayClass* katze_array_class;
661+
662+ gobject_class = G_OBJECT_CLASS (class);
663+ gobject_class->finalize = midori_bookmarks_db_finalize;
664+
665+ katze_array_class = KATZE_ARRAY_CLASS (class);
666+
667+ katze_array_class->add_item = _midori_bookmarks_db_add_item;
668+ katze_array_class->update_item = _midori_bookmarks_db_update_item;
669+ katze_array_class->remove_item = _midori_bookmarks_db_remove_item;
670+ katze_array_class->move_item = _midori_bookmarks_db_move_item;
671+ katze_array_class->clear = _midori_bookmarks_db_clear;
672+}
673+
674+static void
675+midori_bookmarks_db_init (MidoriBookmarksDb* bookmarks)
676+{
677+ bookmarks->db = NULL;
678+ bookmarks->pending_inserts = NULL;
679+ bookmarks->pending_updates = g_hash_table_new (item_hash, item_equal);
680+ bookmarks->pending_deletes = g_hash_table_new (item_hash, item_equal);
681+ bookmarks->all_items = g_hash_table_new (item_hash, item_equal);
682+
683+ bookmarks->in_idle_func = FALSE;
684+
685+ katze_item_set_meta_integer (KATZE_ITEM (bookmarks), "id", 0);
686+ g_hash_table_insert (bookmarks->all_items, bookmarks, bookmarks);
687+ /* g_object_ref (bookmarks); */
688+}
689+
690+static void
691+midori_bookmarks_db_finalize (GObject* object)
692+{
693+ MidoriBookmarksDb* bookmarks = MIDORI_BOOKMARKS_DB (object);
694+
695+ if (bookmarks->db)
696+ {
697+ midori_bookmarks_db_force_idle (bookmarks);
698+ sqlite3_close (bookmarks->db);
699+ }
700+
701+ g_list_free (bookmarks->pending_inserts);
702+ g_hash_table_unref (bookmarks->pending_updates);
703+ g_hash_table_unref (bookmarks->pending_deletes);
704+ g_hash_table_unref (bookmarks->all_items);
705+
706+ G_OBJECT_CLASS (midori_bookmarks_db_parent_class)->finalize (object);
707+}
708+
709+/**
710+ * midori_bookmarks_db_get_item_parent:
711+ * @bookmarks: the main bookmarks array
712+ * @item: a #KatzeItem
713+ *
714+ * Internal function that find the parent of the @item thanks to its %parentid
715+ **/
716+static KatzeArray*
717+midori_bookmarks_db_get_item_parent (MidoriBookmarksDb* bookmarks,
718+ gpointer item)
719+{
720+ KatzeArray* parent;
721+ gint64 parentid;
722+
723+ parentid = katze_item_get_meta_integer (KATZE_ITEM (item), "parentid");
724+
725+ if (parentid == 0)
726+ {
727+ parent = KATZE_ARRAY (bookmarks);
728+ }
729+ else
730+ {
731+ KatzeItem *search = katze_item_new ();
732+
733+ katze_item_set_meta_integer(search, "id", parentid);
734+
735+ parent = KATZE_ARRAY (g_hash_table_lookup (bookmarks->all_items, search));
736+
737+ g_object_unref (search);
738+ }
739+
740+ return parent;
741+}
742+
743+/**
744+ * _midori_bookmarks_db_add_item:
745+ * @array: the main bookmarks array
746+ * @item: a #KatzeItem
747+ *
748+ * Internal function that overloads the #KatzeArray %katze_array_add_item().
749+ * It relays the add item to the appropriate #KatzeArray.
750+ **/
751+static void
752+_midori_bookmarks_db_add_item (KatzeArray* array,
753+ gpointer item)
754+{
755+ MidoriBookmarksDb *bookmarks;
756+ KatzeArray* parent;
757+
758+ g_return_if_fail (IS_MIDORI_BOOKMARKS_DB (array));
759+ g_return_if_fail (KATZE_IS_ITEM (item));
760+
761+ bookmarks = MIDORI_BOOKMARKS_DB (array);
762+ g_return_if_fail (bookmarks->in_idle_func);
763+
764+ parent = katze_item_get_parent (KATZE_ITEM (item));
765+
766+ g_return_if_fail (!parent);
767+
768+ parent = midori_bookmarks_db_get_item_parent (bookmarks, item);
769+
770+ g_return_if_fail (parent);
771+
772+ KATZE_ARRAY_CLASS (midori_bookmarks_db_parent_class)->add_item (parent, item);
773+}
774+
775+/**
776+ * _midori_bookmarks_db_update_item:
777+ * @array: the main bookmarks array
778+ * @item: a #KatzeItem
779+ *
780+ * Internal function that overloads the #KatzeArray %katze_array_update_item().
781+ * It relays the update item to the appropriate #KatzeArray.
782+ **/
783+static void
784+_midori_bookmarks_db_update_item (KatzeArray* array,
785+ gpointer item)
786+{
787+ MidoriBookmarksDb *bookmarks;
788+ KatzeArray* parent;
789+
790+ g_return_if_fail (IS_MIDORI_BOOKMARKS_DB (array));
791+ g_return_if_fail (KATZE_IS_ITEM (item));
792+
793+ bookmarks = MIDORI_BOOKMARKS_DB (array);
794+ g_return_if_fail (bookmarks->in_idle_func);
795+
796+ parent = katze_item_get_parent (KATZE_ITEM (item));
797+
798+ g_return_if_fail (parent);
799+
800+ KATZE_ARRAY_CLASS (midori_bookmarks_db_parent_class)->update_item (parent, item);
801+}
802+
803+/**
804+ * _midori_bookmarks_db_remove_item:
805+ * @array: the main bookmarks array
806+ * @item: a #KatzeItem
807+ *
808+ * Internal function that overloads the #KatzeArray %katze_array_remove_item().
809+ * It relays the remove item to the appropriate #KatzeArray.
810+ **/
811+static void
812+_midori_bookmarks_db_remove_item (KatzeArray* array,
813+ gpointer item)
814+{
815+ MidoriBookmarksDb *bookmarks;
816+ KatzeArray* parent;
817+
818+ g_return_if_fail (IS_MIDORI_BOOKMARKS_DB (array));
819+ g_return_if_fail (KATZE_IS_ITEM (item));
820+
821+ bookmarks = MIDORI_BOOKMARKS_DB (array);
822+ g_return_if_fail (bookmarks->in_idle_func);
823+
824+ parent = katze_item_get_parent (KATZE_ITEM (item));
825+
826+ g_return_if_fail (parent);
827+
828+ KATZE_ARRAY_CLASS (midori_bookmarks_db_parent_class)->remove_item (parent, item);
829+}
830+
831+/**
832+ * _midori_bookmarks_db_move_item:
833+ * @array: the main bookmarks array
834+ * @item: a #KatzeItem
835+ * @position: the new @item position
836+ *
837+ * Internal function that overloads the #KatzeArray %katze_array_move_item().
838+ * It relays the move @item to the appropriate #KatzeArray.
839+ **/
840+static void
841+_midori_bookmarks_db_move_item (KatzeArray* array,
842+ gpointer item,
843+ gint position)
844+{
845+ MidoriBookmarksDb *bookmarks;
846+ KatzeArray* parent;
847+
848+ g_return_if_fail (IS_MIDORI_BOOKMARKS_DB (array));
849+ g_return_if_fail (KATZE_IS_ITEM (item));
850+
851+ parent = katze_item_get_parent (KATZE_ITEM (item));
852+
853+ g_return_if_fail (parent);
854+
855+ KATZE_ARRAY_CLASS (midori_bookmarks_db_parent_class)->move_item (parent, item, position);
856+}
857+
858+/**
859+ * _midori_bookmarks_db_clear:
860+ * @array: the main bookmarks array
861+ *
862+ * Internal function that overloads the #KatzeArray %katze_array_clear().
863+ * It deletes the whole bookmarks data.
864+ **/
865+static void
866+_midori_bookmarks_db_clear (KatzeArray* array)
867+{
868+ MidoriBookmarksDb *bookmarks;
869+ g_return_if_fail (IS_MIDORI_BOOKMARKS_DB (array));
870+
871+ bookmarks = MIDORI_BOOKMARKS_DB (array);
872+
873+ g_critical ("_midori_bookmarks_db_clear: not implemented\n");
874+}
875+
876+/**
877+ * midori_bookmarks_db_begin_transaction:
878+ * @db: the removed #KatzeItem
879+ *
880+ * Internal function that starts an SQL transaction.
881+ **/
882+static gboolean
883+midori_bookmarks_db_begin_transaction (sqlite3* db)
884+{
885+ char* errmsg = NULL;
886+
887+ if (sqlite3_exec (db, "BEGIN TRANSACTION;", NULL, NULL, &errmsg) != SQLITE_OK)
888+ {
889+ g_printerr (_("Failed to begin transaction: %s\n"), errmsg);
890+ sqlite3_free (errmsg);
891+ return FALSE;
892+ }
893+
894+ return TRUE;
895+}
896+
897+/**
898+ * midori_bookmarks_db_end_transaction:
899+ * @db: the removed #KatzeItem
900+ * @commit : boolean
901+ *
902+ * Internal function that ends an SQL transaction.
903+ * If @commit is %TRUE, the transaction is ended by a COMMIT.
904+ * It is ended by a ROLLBACK otherwise.
905+ **/
906+static void
907+midori_bookmarks_db_end_transaction (sqlite3* db, gboolean commit)
908+{
909+ char* errmsg = NULL;
910+ if (sqlite3_exec (db, (commit ? "COMMIT;" : "ROLLBACK;"), NULL, NULL, &errmsg) != SQLITE_OK)
911+ {
912+ if (commit)
913+ g_printerr (_("Failed to end transaction: %s\n"), errmsg);
914+ else
915+ g_printerr (_("Failed to cancel transaction: %s\n"), errmsg);
916+ sqlite3_free (errmsg);
917+ }
918+}
919+
920+/**
921+ * midori_bookmarks_db_add_item_recursive:
922+ * @item: the removed #KatzeItem
923+ * @bookmarks : the main bookmarks array
924+ *
925+ * Internal function that creates memory records of the added @item.
926+ * If @item is a #KatzeArray, the function recursiveley adds records
927+ * of all its childs.
928+ **/
929+static gint
930+midori_bookmarks_db_add_item_recursive (MidoriBookmarksDb* bookmarks,
931+ KatzeItem* item)
932+{
933+ GList* list;
934+ KatzeArray* array;
935+ gint64 id = 0;
936+ gint count = 0;
937+ gint64 parentid = katze_item_get_meta_integer (item, "parentid");
938+
939+ id = midori_bookmarks_db_insert_item_db (bookmarks->db, item, parentid);
940+ count++;
941+
942+ g_object_ref (item);
943+ g_hash_table_insert (bookmarks->all_items, item, item);
944+
945+ if (!KATZE_IS_ARRAY (item))
946+ return count;
947+
948+ array = KATZE_ARRAY (item);
949+
950+ KATZE_ARRAY_FOREACH_ITEM_L (item, array, list)
951+ {
952+ katze_item_set_meta_integer (item, "parentid", id);
953+ count += midori_bookmarks_db_add_item_recursive (bookmarks, item);
954+ }
955+
956+ g_list_free (list);
957+ return count;
958+}
959+
960+/**
961+ * midori_bookmarks_db_remove_item_recursive:
962+ * @item: the removed #KatzeItem
963+ * @bookmarks : the main bookmarks array
964+ *
965+ * Internal function that removes memory records of the removed @item.
966+ * If @item is a #KatzeArray, the function recursiveley removes records
967+ * of all its childs.
968+ **/
969+static void
970+midori_bookmarks_db_remove_item_recursive (KatzeItem* item,
971+ MidoriBookmarksDb* bookmarks)
972+{
973+ gint64 id = katze_item_get_meta_integer (item, "id");
974+ GHashTableIter hash_iter;
975+ gpointer key, value;
976+ gpointer found;
977+ KatzeArray* array;
978+ KatzeItem* child;
979+ GList* list;
980+
981+ if (NULL != (found = g_list_find (bookmarks->pending_inserts, item)))
982+ {
983+ g_object_unref (((GList*)found)->data);
984+ bookmarks->pending_inserts = g_list_delete_link (bookmarks->pending_inserts,
985+ ((GList*)found));
986+ }
987+
988+ if (NULL != (found = g_hash_table_lookup (bookmarks->pending_updates, item)))
989+ {
990+ g_hash_table_remove (bookmarks->pending_updates, found);
991+ g_object_unref (found);
992+ }
993+
994+ if (NULL != (found = g_hash_table_lookup (bookmarks->all_items, item)))
995+ {
996+ g_hash_table_remove (bookmarks->all_items, found);
997+ g_object_unref (found);
998+ }
999+
1000+ if (!KATZE_IS_ARRAY (item))
1001+ return;
1002+
1003+ array = KATZE_ARRAY (item);
1004+
1005+ KATZE_ARRAY_FOREACH_ITEM_L (child, array, list)
1006+ {
1007+ midori_bookmarks_db_remove_item_recursive (child, bookmarks);
1008+ }
1009+
1010+ g_list_free (list);
1011+}
1012+
1013+/**
1014+ * midori_bookmarks_db_idle_func:
1015+ * @data: the main bookmark array
1016+ *
1017+ * Internal function executed during idle time that Packs pending database
1018+ * operations in one transaction.
1019+ *
1020+ * Pending operations are either:
1021+ * a. a list of pending add items,
1022+ * all child #KatzeItem of a #KatzeArray are recursively added.
1023+ * Each added #KatzeItem is memorized for future use.
1024+ * (See %midori_bookmarks_db_array_from_statement())
1025+ * b. a hash table of items to update,
1026+ * c. or a hash table of items to remove
1027+ * the database CASCADE on delete takes care of removal of the childs
1028+ * #KatzeItem of a #KatzeArray in the database
1029+ *
1030+ * When database operations are done, the #KatzeArray equivalent operations
1031+ * are called to:
1032+ * 1. update the #KatzeArray tree content
1033+ * 2. signal the client views of the #KatzeArray tree content change.
1034+ **/
1035+static gboolean
1036+midori_bookmarks_db_idle_func (gpointer data)
1037+{
1038+ GTimer *timer = g_timer_new();
1039+ gint count = 0;
1040+ gulong microseconds;
1041+ gboolean with_transaction;
1042+ MidoriBookmarksDb* bookmarks = MIDORI_BOOKMARKS_DB (data);
1043+ GList* list_iter;
1044+ GHashTableIter hash_iter;
1045+ gpointer key, value;
1046+
1047+ bookmarks->in_idle_func = TRUE;
1048+
1049+ g_timer_start (timer);
1050+
1051+ with_transaction = midori_bookmarks_db_begin_transaction (bookmarks->db);
1052+
1053+ for (list_iter = bookmarks->pending_inserts; list_iter; list_iter = g_list_next (list_iter))
1054+ {
1055+ KatzeItem *item = KATZE_ITEM (list_iter->data);
1056+
1057+ count += midori_bookmarks_db_add_item_recursive (bookmarks, item);
1058+ katze_array_add_item (KATZE_ARRAY (bookmarks), item);
1059+
1060+ g_object_unref (item);
1061+ }
1062+
1063+ g_hash_table_iter_init (&hash_iter, bookmarks->pending_updates);
1064+
1065+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
1066+ {
1067+ KatzeItem *item = KATZE_ITEM (value);
1068+
1069+ midori_bookmarks_db_update_item_db (bookmarks->db, item);
1070+ katze_array_update_item (KATZE_ARRAY (bookmarks), item);
1071+ g_object_unref (item);
1072+ count++;
1073+ }
1074+
1075+ g_hash_table_iter_init (&hash_iter, bookmarks->pending_deletes);
1076+
1077+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
1078+ {
1079+ KatzeItem *item = KATZE_ITEM (value);
1080+
1081+ midori_bookmarks_db_remove_item_db (bookmarks->db, item);
1082+ katze_array_remove_item (KATZE_ARRAY (bookmarks), item);
1083+ g_object_unref (item);
1084+ count++;
1085+ }
1086+
1087+ if (with_transaction)
1088+ midori_bookmarks_db_end_transaction (bookmarks->db, TRUE);
1089+
1090+ g_timer_elapsed (timer, &microseconds);
1091+ g_print ("midori_bookmarks_db_idle: %d DB operation(s) in %lu micro-seconds\n",
1092+ count, microseconds);
1093+
1094+ g_timer_destroy (timer);
1095+
1096+ g_hash_table_remove_all (bookmarks->pending_deletes);
1097+ g_hash_table_remove_all (bookmarks->pending_updates);
1098+ g_list_free (bookmarks->pending_inserts);
1099+ bookmarks->pending_inserts = NULL;
1100+
1101+ bookmarks->in_idle_func = FALSE;
1102+
1103+ return FALSE;
1104+}
1105+
1106+/**
1107+ * midori_bookmarks_db_idle_start:
1108+ * @bookmarks: the main bookmark array
1109+ *
1110+ * Internal function that checks whether idle processing is pending,
1111+ * if not, add a new one.
1112+ **/
1113+static void
1114+midori_bookmarks_db_idle_start (MidoriBookmarksDb* bookmarks)
1115+{
1116+ g_return_if_fail (bookmarks->db != NULL);
1117+
1118+ if (bookmarks->pending_inserts
1119+ || g_hash_table_size (bookmarks->pending_updates)
1120+ || g_hash_table_size (bookmarks->pending_deletes))
1121+ return;
1122+
1123+ g_idle_add (midori_bookmarks_db_idle_func, bookmarks);
1124+}
1125+
1126+/**
1127+ * midori_bookmarks_db_insert_item_db:
1128+ * @db: the #sqlite3
1129+ * @item: #KatzeItem the item to insert
1130+ *
1131+ * Internal function that does the actual SQL INSERT of the @item in @db.
1132+ *
1133+ * Since: 0.5.2
1134+ **/
1135+static gint64
1136+midori_bookmarks_db_insert_item_db (sqlite3* db,
1137+ KatzeItem* item,
1138+ gint64 parentid)
1139+{
1140+ gchar* sqlcmd;
1141+ char* errmsg = NULL;
1142+ KatzeItem* old_parent;
1143+ gchar* new_parentid;
1144+ gchar* id = NULL;
1145+ const gchar* uri = NULL;
1146+ const gchar* desc = NULL;
1147+ gint64 seq = 0;
1148+
1149+ /* Bookmarks must have a name, import may produce invalid items */
1150+ g_return_val_if_fail (katze_item_get_name (item), seq);
1151+
1152+ if (!db)
1153+ return seq;
1154+
1155+ if (katze_item_get_meta_integer (item, "id") > 0)
1156+ id = g_strdup_printf ("%" G_GINT64_FORMAT, katze_item_get_meta_integer(item, "id"));
1157+ else
1158+ id = g_strdup_printf ("NULL");
1159+
1160+ if (KATZE_ITEM_IS_BOOKMARK (item))
1161+ uri = katze_item_get_uri (item);
1162+
1163+ if (katze_item_get_text (item))
1164+ desc = katze_item_get_text (item);
1165+
1166+ /* Use folder, otherwise fallback to parent folder */
1167+ old_parent = katze_item_get_parent (item);
1168+ if (parentid > 0)
1169+ new_parentid = g_strdup_printf ("%" G_GINT64_FORMAT, parentid);
1170+ else if (old_parent && katze_item_get_meta_integer (old_parent, "id") > 0)
1171+ new_parentid = g_strdup_printf ("%" G_GINT64_FORMAT, katze_item_get_meta_integer (old_parent, "id"));
1172+ else
1173+ new_parentid = g_strdup_printf ("NULL");
1174+
1175+ sqlcmd = sqlite3_mprintf (
1176+ "INSERT INTO bookmarks (id, parentid, title, uri, desc, toolbar, app) "
1177+ "VALUES (%q, %q, '%q', '%q', '%q', %d, %d)",
1178+ id,
1179+ new_parentid,
1180+ katze_item_get_name (item),
1181+ katze_str_non_null (uri),
1182+ katze_str_non_null (desc),
1183+ katze_item_get_meta_boolean (item, "toolbar"),
1184+ katze_item_get_meta_boolean (item, "app"));
1185+
1186+ if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) == SQLITE_OK)
1187+ {
1188+ /* Get insert id */
1189+ if (g_str_equal (id, "NULL"))
1190+ {
1191+ KatzeArray* seq_array;
1192+
1193+ sqlite3_free (sqlcmd);
1194+ sqlcmd = sqlite3_mprintf (
1195+ "SELECT seq FROM sqlite_sequence WHERE name = 'bookmarks'");
1196+
1197+ seq_array = katze_array_from_sqlite (db, sqlcmd);
1198+ if (katze_array_get_nth_item (seq_array, 0))
1199+ {
1200+ KatzeItem* seq_item = katze_array_get_nth_item (seq_array, 0);
1201+
1202+ seq = katze_item_get_meta_integer (seq_item, "seq");
1203+ katze_item_set_meta_integer (item, "id", seq);
1204+ }
1205+ g_object_unref (seq_array);
1206+ }
1207+ }
1208+ else
1209+ {
1210+ g_printerr (_("Failed to add bookmark item: %s\n"), errmsg);
1211+ sqlite3_free (errmsg);
1212+ }
1213+
1214+ sqlite3_free (sqlcmd);
1215+ g_free (new_parentid);
1216+ g_free (id);
1217+
1218+ return seq;
1219+}
1220+
1221+/**
1222+ * midori_bookmarks_db_update_item_db:
1223+ * @db: the #sqlite3
1224+ * @item: #KatzeItem the item to update
1225+ *
1226+ * Internal function that does the actual SQL UPDATE of the @item in @db.
1227+ *
1228+ * Since: 0.5.2
1229+ **/
1230+static gboolean
1231+midori_bookmarks_db_update_item_db (sqlite3* db,
1232+ KatzeItem* item)
1233+{
1234+ gchar* sqlcmd;
1235+ char* errmsg = NULL;
1236+ gchar* parentid;
1237+ gboolean updated;
1238+
1239+ if (katze_item_get_meta_integer (item, "parentid") > 0)
1240+ parentid = g_strdup_printf ("%" G_GINT64_FORMAT,
1241+ katze_item_get_meta_integer (item, "parentid"));
1242+ else
1243+ parentid = g_strdup_printf ("NULL");
1244+
1245+ sqlcmd = sqlite3_mprintf (
1246+ "UPDATE bookmarks SET "
1247+ "parentid=%q, title='%q', uri='%q', desc='%q', toolbar=%d, app=%d "
1248+ "WHERE id = %" G_GINT64_FORMAT ";",
1249+ parentid,
1250+ katze_item_get_name (item),
1251+ katze_str_non_null (katze_item_get_uri (item)),
1252+ katze_str_non_null (katze_item_get_meta_string (item, "desc")),
1253+ katze_item_get_meta_boolean (item, "toolbar"),
1254+ katze_item_get_meta_boolean (item, "app"),
1255 katze_item_get_meta_integer (item, "id"));
1256
1257- if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) != SQLITE_OK)
1258- {
1259- g_printerr (_("Failed to remove history item: %s\n"), errmsg);
1260- sqlite3_free (errmsg);
1261- }
1262-
1263- sqlite3_free (sqlcmd);
1264+ updated = TRUE;
1265+ if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) != SQLITE_OK)
1266+ {
1267+ updated = FALSE;
1268+ g_printerr (_("Failed to update bookmark : %s\n"), errmsg);
1269+ sqlite3_free (errmsg);
1270+ }
1271+
1272+ sqlite3_free (sqlcmd);
1273+ g_free (parentid);
1274+
1275+ return updated;
1276+}
1277+
1278+/**
1279+ * midori_bookmarks_db_remove_item_db:
1280+ * @db: the #sqlite3
1281+ * @item: #KatzeItem the item to delete
1282+ *
1283+ * Internal function that does the actual SQL DELETE of the @item in @db.
1284+ *
1285+ * Since: 0.5.2
1286+ **/
1287+static gboolean
1288+midori_bookmarks_db_remove_item_db (sqlite3* db,
1289+ KatzeItem* item)
1290+{
1291+ char* errmsg = NULL;
1292+ gchar* sqlcmd;
1293+ gboolean removed = TRUE;
1294+
1295+ sqlcmd = sqlite3_mprintf (
1296+ "DELETE FROM bookmarks WHERE id = %" G_GINT64_FORMAT ";",
1297+ katze_item_get_meta_integer (item, "id"));
1298+
1299+ if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) != SQLITE_OK)
1300+ {
1301+ g_printerr (_("Failed to remove bookmark item: %s\n"), errmsg);
1302+ sqlite3_free (errmsg);
1303+ removed = FALSE;
1304+ }
1305+
1306+ sqlite3_free (sqlcmd);
1307+ return removed;
1308+}
1309+
1310+/**
1311+ * midori_bookmarks_db_add_item:
1312+ * @bookmarks: the main bookmark array
1313+ * @item: #KatzeItem the item to update
1314+ *
1315+ * Adds the @item in the bookmark data base.
1316+ *
1317+ * Since: 0.5.2
1318+ **/
1319+void
1320+midori_bookmarks_db_add_item (MidoriBookmarksDb* bookmarks, KatzeItem* item)
1321+{
1322+ g_return_if_fail (IS_MIDORI_BOOKMARKS_DB (bookmarks));
1323+ g_return_if_fail (KATZE_IS_ITEM (item));
1324+ g_return_if_fail (NULL == katze_item_get_meta_string (item, "id"));
1325+
1326+ gpointer found = g_list_find (bookmarks->pending_inserts, item);
1327+
1328+ if (found)
1329+ return;
1330+
1331+ midori_bookmarks_db_idle_start (bookmarks);
1332+
1333+ g_object_ref (item);
1334+ bookmarks->pending_inserts = g_list_append (bookmarks->pending_inserts, item);
1335+}
1336+
1337+/**
1338+ * midori_bookmarks_db_update_item:
1339+ * @bookmarks: the main bookmark array
1340+ * @item: #KatzeItem the item to update
1341+ *
1342+ * Updates the @item in the bookmark data base.
1343+ *
1344+ * Since: 0.5.2
1345+ **/
1346+void
1347+midori_bookmarks_db_update_item (MidoriBookmarksDb* bookmarks, KatzeItem* item)
1348+{
1349+ g_return_if_fail (IS_MIDORI_BOOKMARKS_DB (bookmarks));
1350+ g_return_if_fail (KATZE_IS_ITEM (item));
1351+ g_return_if_fail (katze_item_get_meta_string (item, "id"));
1352+ g_return_if_fail (0 != katze_item_get_meta_integer (item, "id"));
1353+
1354+ gpointer found = g_hash_table_lookup (bookmarks->pending_updates, item);
1355+
1356+ if (found)
1357+ return;
1358+
1359+ midori_bookmarks_db_idle_start (bookmarks);
1360+
1361+ g_object_ref (item);
1362+ g_hash_table_insert (bookmarks->pending_updates, item, item);
1363+}
1364+
1365+/**
1366+ * midori_bookmarks_db_remove_item:
1367+ * @bookmarks: the main bookmark array
1368+ * @item: #KatzeItem the item to remove
1369+ *
1370+ * Removes the @item from the bookmark data base.
1371+ *
1372+ * Since: 0.5.2
1373+ **/
1374+void
1375+midori_bookmarks_db_remove_item (MidoriBookmarksDb* bookmarks, KatzeItem* item)
1376+{
1377+ g_return_if_fail (IS_MIDORI_BOOKMARKS_DB (bookmarks));
1378+ g_return_if_fail (KATZE_IS_ITEM (item));
1379+ g_return_if_fail (katze_item_get_meta_string (item, "id"));
1380+ g_return_if_fail (0 != katze_item_get_meta_integer (item, "id"));
1381+
1382+ gpointer found = g_hash_table_lookup (bookmarks->pending_deletes, item);
1383+
1384+ if (found)
1385+ return;
1386+
1387+ midori_bookmarks_db_idle_start (bookmarks);
1388+
1389+ midori_bookmarks_db_remove_item_recursive (item, bookmarks);
1390+
1391+ g_object_ref (item);
1392+ g_hash_table_insert (bookmarks->pending_deletes, item, item);
1393 }
1394
1395 #define _APPEND_TO_SQL_ERRORMSG(custom_errmsg) \
1396@@ -74,8 +850,8 @@
1397 g_string_append (errmsg_str, custom_errmsg); \
1398 } while (0)
1399
1400-gboolean
1401-midori_bookmarks_import_from_old_db (sqlite3* db,
1402+static gboolean
1403+midori_bookmarks_db_import_from_old_db (sqlite3* db,
1404 const gchar* oldfile,
1405 gchar** errmsg)
1406 {
1407@@ -130,8 +906,24 @@
1408 }
1409 #undef _APPEND_TO_SQL_ERRORMSG
1410
1411-KatzeArray*
1412-midori_bookmarks_new (char** errmsg)
1413+static void
1414+midori_bookmarks_db_dbtracer (void* dummy,
1415+ const char* query)
1416+{
1417+ g_printerr ("%s\n", query);
1418+}
1419+
1420+/**
1421+ * midori_bookmarks_db_new:
1422+ *
1423+ * Initializes the bookmark data base.
1424+ *
1425+ * Returns: the main bookmarks array
1426+ *
1427+ * Since: 0.5.2
1428+ **/
1429+MidoriBookmarksDb*
1430+midori_bookmarks_db_new (char** errmsg)
1431 {
1432 sqlite3* db;
1433 gchar* oldfile;
1434@@ -141,6 +933,7 @@
1435 gchar* sql_errmsg = NULL;
1436 gchar* import_errmsg = NULL;
1437 KatzeArray* array;
1438+ MidoriBookmarksDb* bookmarks;
1439
1440 g_return_val_if_fail (errmsg != NULL, NULL);
1441
1442@@ -158,7 +951,7 @@
1443 }
1444
1445 if (midori_debug ("bookmarks"))
1446- sqlite3_trace (db, midori_bookmarks_dbtracer, NULL);
1447+ sqlite3_trace (db, midori_bookmarks_db_dbtracer, NULL);
1448
1449 create_stmt = /* Table structure */
1450 "CREATE TABLE IF NOT EXISTS bookmarks "
1451@@ -269,7 +1062,7 @@
1452
1453 if (oldfile_exists)
1454 /* import from old db */
1455- if (!midori_bookmarks_import_from_old_db (db, oldfile, &import_errmsg))
1456+ if (!midori_bookmarks_db_import_from_old_db (db, oldfile, &import_errmsg))
1457 {
1458 *errmsg = g_strdup_printf (_("Couldn't import from old database: %s\n"),
1459 import_errmsg ? import_errmsg : "(err = NULL)");
1460@@ -279,13 +1072,11 @@
1461 init_success:
1462 g_free (newfile);
1463 g_free (oldfile);
1464- array = katze_array_new (KATZE_TYPE_ARRAY);
1465- g_signal_connect (array, "add-item",
1466- G_CALLBACK (midori_bookmarks_add_item_cb), db);
1467- g_signal_connect (array, "remove-item",
1468- G_CALLBACK (midori_bookmarks_remove_item_cb), db);
1469- g_object_set_data (G_OBJECT (array), "db", db);
1470- return array;
1471+ bookmarks = MIDORI_BOOKMARKS_DB (g_object_new (TYPE_MIDORI_BOOKMARKS_DB, NULL));
1472+ bookmarks->db = db;
1473+
1474+ g_object_set_data (G_OBJECT (bookmarks), "db", db);
1475+ return bookmarks;
1476
1477 init_failed:
1478 g_free (newfile);
1479@@ -297,31 +1088,384 @@
1480 return NULL;
1481 }
1482
1483+/**
1484+ * midori_bookmarks_db_on_quit:
1485+ * @bookmarks: the main bookmark array
1486+ *
1487+ * Delete the main bookmark array.
1488+ *
1489+ * Since: 0.5.2
1490+ **/
1491 void
1492-midori_bookmarks_import (const gchar* filename,
1493- sqlite3* db)
1494+midori_bookmarks_db_on_quit (MidoriBookmarksDb* bookmarks)
1495 {
1496- KatzeArray* bookmarks;
1497- GError* error = NULL;
1498-
1499- bookmarks = katze_array_new (KATZE_TYPE_ARRAY);
1500-
1501- if (!midori_array_from_file (bookmarks, filename, "xbel", &error))
1502- {
1503- g_warning (_("The bookmarks couldn't be saved. %s"), error->message);
1504- g_error_free (error);
1505- return;
1506- }
1507- midori_bookmarks_import_array_db (db, bookmarks, 0);
1508+ g_return_if_fail (IS_MIDORI_BOOKMARKS_DB (bookmarks));
1509+
1510+ g_object_unref (bookmarks);
1511 }
1512
1513+/**
1514+ * midori_bookmarks_db_import_array:
1515+ * @array: the main bookmark array
1516+ * @array: #KatzeArray containing the items to import
1517+ * @parentid: the id of folder
1518+ *
1519+ * Imports the items of @array as childs of the folder
1520+ * identfied by @parentid.
1521+ *
1522+ * Since: 0.5.2
1523+ **/
1524 void
1525-midori_bookmarks_on_quit (KatzeArray* array)
1526+midori_bookmarks_db_import_array (MidoriBookmarksDb* bookmarks,
1527+ KatzeArray* array,
1528+ gint64 parentid)
1529 {
1530+ GList* list;
1531+ KatzeItem* item;
1532+
1533+ g_return_if_fail (IS_MIDORI_BOOKMARKS_DB (bookmarks));
1534 g_return_if_fail (KATZE_IS_ARRAY (array));
1535
1536- sqlite3* db = g_object_get_data (G_OBJECT (array), "db");
1537- g_return_if_fail (db != NULL);
1538- sqlite3_close (db);
1539-}
1540-
1541+ KATZE_ARRAY_FOREACH_ITEM_L (item, array, list)
1542+ {
1543+ katze_item_set_meta_integer (item, "parentid", parentid);
1544+ midori_bookmarks_db_add_item (bookmarks, item);
1545+ }
1546+
1547+ g_list_free (list);
1548+}
1549+
1550+/**
1551+ * midori_bookmarks_db_force_idle:
1552+ * @array: the main bookmark array
1553+ *
1554+ * Internal function that checks if idle processing is pending.
1555+ * If it is the case, removes it from idle time processing and
1556+ * executes it immediately.
1557+ **/
1558+static void
1559+midori_bookmarks_db_force_idle (MidoriBookmarksDb* bookmarks)
1560+{
1561+ if (g_idle_remove_by_data (bookmarks))
1562+ midori_bookmarks_db_idle_func (bookmarks);
1563+}
1564+
1565+/**
1566+ * midori_bookmarks_db_array_from_statement:
1567+ * @stmt: the sqlite returned statement
1568+ * @bookmarks: the database controller
1569+ *
1570+ * Internal function that populate a #KatzeArray by processing the @stmt
1571+ * rows identifying:
1572+ * a- if the item is already in memory
1573+ * in this case the item data is updated with retreived database content
1574+ * and the already existing item is populated in the returned #KatzeArray
1575+ * b- if the data is a folder
1576+ * a new #KatzeArray item is populated in the returned #KatzeArray and
1577+ * memorized for furure use.
1578+ * c- if the data is a bookmark
1579+ * a new #KatzeItem item is populated in the returned #KatzeArray and
1580+ * memorized for furure use.
1581+ *
1582+ * Return value: the populated #KatzeArray
1583+ **/
1584+static KatzeArray*
1585+midori_bookmarks_db_array_from_statement (sqlite3_stmt* stmt,
1586+ MidoriBookmarksDb* bookmarks)
1587+{
1588+ KatzeArray *array;
1589+ gint result;
1590+ gint cols;
1591+
1592+ array = katze_array_new (KATZE_TYPE_ITEM);
1593+ cols = sqlite3_column_count (stmt);
1594+
1595+ while ((result = sqlite3_step (stmt)) == SQLITE_ROW)
1596+ {
1597+ gint i;
1598+ KatzeItem* item;
1599+ KatzeItem* found;
1600+
1601+ item = katze_item_new ();
1602+ for (i = 0; i < cols; i++)
1603+ katze_item_set_value_from_column (stmt, i, item);
1604+
1605+ if (NULL != (found = g_hash_table_lookup (bookmarks->all_items, item)))
1606+ {
1607+ for (i = 0; i < cols; i++)
1608+ katze_item_set_value_from_column (stmt, i, found);
1609+
1610+ g_object_unref (item);
1611+
1612+ item = found;
1613+ }
1614+ else if (KATZE_ITEM_IS_FOLDER (item))
1615+ {
1616+ item = KATZE_ITEM (katze_array_new (KATZE_TYPE_ITEM));
1617+
1618+ for (i = 0; i < cols; i++)
1619+ katze_item_set_value_from_column (stmt, i, item);
1620+
1621+ g_object_ref (item);
1622+ g_hash_table_insert (bookmarks->all_items, item, item);
1623+ }
1624+ else
1625+ {
1626+ g_object_ref (item);
1627+ g_hash_table_insert (bookmarks->all_items, item, item);
1628+ }
1629+
1630+ katze_array_add_item (array, item);
1631+ }
1632+
1633+ sqlite3_clear_bindings (stmt);
1634+ sqlite3_reset (stmt);
1635+ return array;
1636+}
1637+
1638+/**
1639+ * midori_bookmarks_db_array_from_sqlite:
1640+ * @array: the main bookmark array
1641+ * @sqlcmd: the sqlcmd to execute
1642+ *
1643+ * Internal function that first forces pending idle processing to update the
1644+ * database then process the requested @sqlcmd.
1645+ *
1646+ * Return value: a #KatzeArray on success, %NULL otherwise
1647+ **/
1648+static KatzeArray*
1649+midori_bookmarks_db_array_from_sqlite (MidoriBookmarksDb* bookmarks,
1650+ const gchar* sqlcmd)
1651+{
1652+ sqlite3_stmt* stmt;
1653+ gint result;
1654+
1655+ g_return_val_if_fail (bookmarks->db != NULL, NULL);
1656+
1657+ midori_bookmarks_db_force_idle (bookmarks);
1658+
1659+ result = sqlite3_prepare_v2 (bookmarks->db, sqlcmd, -1, &stmt, NULL);
1660+ if (result != SQLITE_OK)
1661+ return NULL;
1662+
1663+ return midori_bookmarks_db_array_from_statement (stmt, bookmarks);
1664+}
1665+
1666+/**
1667+ * midori_bookmarks_db_query_recursive:
1668+ * @array: the main bookmark array
1669+ * @fields: comma separated list of fields
1670+ * @condition: condition, like "folder = '%q'"
1671+ * @value: a value to be inserted if @condition contains %q
1672+ * @recursive: if %TRUE include children
1673+ *
1674+ * Stores the result in a #KatzeArray.
1675+ *
1676+ * Return value: a #KatzeArray on success, %NULL otherwise
1677+ *
1678+ * Since: 0.5.2
1679+ **/
1680+KatzeArray*
1681+midori_bookmarks_db_query_recursive (MidoriBookmarksDb* bookmarks,
1682+ const gchar* fields,
1683+ const gchar* condition,
1684+ const gchar* value,
1685+ gboolean recursive)
1686+{
1687+ gchar* sqlcmd;
1688+ char* sqlcmd_value;
1689+ KatzeArray* array;
1690+ KatzeItem* item;
1691+ GList* list;
1692+
1693+ g_return_val_if_fail (IS_MIDORI_BOOKMARKS_DB (bookmarks), NULL);
1694+ g_return_val_if_fail (fields, NULL);
1695+ g_return_val_if_fail (condition, NULL);
1696+
1697+ sqlcmd = g_strdup_printf ("SELECT %s FROM bookmarks WHERE %s "
1698+ "ORDER BY (uri='') ASC, title DESC", fields, condition);
1699+ if (strstr (condition, "%q"))
1700+ {
1701+ sqlcmd_value = sqlite3_mprintf (sqlcmd, value ? value : "");
1702+ array = midori_bookmarks_db_array_from_sqlite (bookmarks, sqlcmd_value);
1703+ sqlite3_free (sqlcmd_value);
1704+ }
1705+ else
1706+ array = midori_bookmarks_db_array_from_sqlite (bookmarks, sqlcmd);
1707+ g_free (sqlcmd);
1708+
1709+ if (!recursive)
1710+ return array;
1711+
1712+ KATZE_ARRAY_FOREACH_ITEM_L (item, array, list)
1713+ {
1714+ if (KATZE_ITEM_IS_FOLDER (item))
1715+ {
1716+ gchar* parentid = g_strdup_printf ("%" G_GINT64_FORMAT,
1717+ katze_item_get_meta_integer (item, "id"));
1718+ KatzeArray* subarray = midori_bookmarks_db_query_recursive (bookmarks,
1719+ fields, "parentid=%q", parentid, TRUE);
1720+ KatzeItem* subitem;
1721+ GList* sublist;
1722+
1723+ KATZE_ARRAY_FOREACH_ITEM_L (subitem, subarray, sublist)
1724+ {
1725+ katze_array_add_item (KATZE_ARRAY (item), subitem);
1726+ }
1727+
1728+ g_object_unref (subarray);
1729+ g_free (parentid);
1730+ }
1731+ }
1732+ g_list_free (list);
1733+ return array;
1734+}
1735+
1736+static gint64
1737+midori_bookmarks_db_count_from_sqlite (sqlite3* db,
1738+ const gchar* sqlcmd)
1739+{
1740+ gint64 count = -1;
1741+ sqlite3_stmt* stmt;
1742+ gint result;
1743+
1744+ result = sqlite3_prepare_v2 (db, sqlcmd, -1, &stmt, NULL);
1745+ if (result != SQLITE_OK)
1746+ return -1;
1747+
1748+ g_assert (sqlite3_column_count (stmt) == 1);
1749+
1750+ if ((result = sqlite3_step (stmt)) == SQLITE_ROW)
1751+ count = sqlite3_column_int64(stmt, 0);
1752+
1753+ sqlite3_clear_bindings (stmt);
1754+ sqlite3_reset (stmt);
1755+
1756+ return count;
1757+}
1758+
1759+static gint64
1760+midori_bookmarks_db_count_recursive_by_id (MidoriBookmarksDb* bookmarks,
1761+ const gchar* condition,
1762+ const gchar* value,
1763+ gint64 id,
1764+ gboolean recursive)
1765+{
1766+ gint64 count = -1;
1767+ gchar* sqlcmd;
1768+ char* sqlcmd_value;
1769+ sqlite3_stmt* stmt;
1770+ gint result;
1771+ GList* ids;
1772+ GList* iter_ids;
1773+
1774+ g_return_val_if_fail (condition, -1);
1775+ g_return_val_if_fail (MIDORI_BOOKMARKS_DB (bookmarks), -1);
1776+ g_return_val_if_fail (bookmarks->db != NULL, -1);
1777+
1778+ g_assert(!strstr("parentid", condition));
1779+
1780+ if (id > 0)
1781+ sqlcmd = g_strdup_printf ("SELECT COUNT(*) FROM bookmarks "
1782+ "WHERE parentid = %" G_GINT64_FORMAT " AND %s",
1783+ id,
1784+ condition);
1785+ else
1786+ sqlcmd = g_strdup_printf ("SELECT COUNT(*) FROM bookmarks "
1787+ "WHERE parentid IS NULL AND %s ",
1788+ condition);
1789+
1790+ if (strstr (condition, "%q"))
1791+ {
1792+ sqlcmd_value = sqlite3_mprintf (sqlcmd, value ? value : "");
1793+ count = midori_bookmarks_db_count_from_sqlite (bookmarks->db, sqlcmd_value);
1794+ sqlite3_free (sqlcmd_value);
1795+ }
1796+ else
1797+ count = midori_bookmarks_db_count_from_sqlite (bookmarks->db, sqlcmd);
1798+
1799+ g_free (sqlcmd);
1800+
1801+ if (!recursive || (count < 0))
1802+ return count;
1803+
1804+ ids = NULL;
1805+
1806+ if (id > 0)
1807+ sqlcmd_value = sqlite3_mprintf (
1808+ "SELECT id FROM bookmarks "
1809+ "WHERE parentid = %" G_GINT64_FORMAT " AND uri = ''", id);
1810+ else
1811+ sqlcmd_value = sqlite3_mprintf (
1812+ "SELECT id FROM bookmarks "
1813+ "WHERE parentid IS NULL AND uri = ''");
1814+
1815+ if (sqlite3_prepare_v2 (bookmarks->db, sqlcmd_value, -1, &stmt, NULL) == SQLITE_OK)
1816+ {
1817+ g_assert (sqlite3_column_count (stmt) == 1);
1818+
1819+ if ((result = sqlite3_step (stmt)) == SQLITE_ROW)
1820+ {
1821+ gint64* pid = g_new (gint64, 1);
1822+
1823+ *pid = sqlite3_column_int64(stmt, 0);
1824+ ids = g_list_append (ids, pid);
1825+ }
1826+
1827+ sqlite3_clear_bindings (stmt);
1828+ sqlite3_reset (stmt);
1829+ }
1830+
1831+ sqlite3_free (sqlcmd_value);
1832+
1833+ iter_ids = ids;
1834+ while (iter_ids)
1835+ {
1836+ gint64 sub_count = midori_bookmarks_db_count_recursive_by_id (bookmarks,
1837+ condition,
1838+ value,
1839+ *(gint64*)(iter_ids->data),
1840+ recursive);
1841+
1842+ if (sub_count < 0)
1843+ {
1844+ g_list_free_full (ids, g_free);
1845+ return -1;
1846+ }
1847+
1848+ count += sub_count;
1849+ iter_ids = g_list_next (iter_ids);
1850+ }
1851+
1852+ g_list_free_full (ids, g_free);
1853+ return count;
1854+}
1855+
1856+/**
1857+ * midori_bookmarks_db_count_recursive:
1858+ * @bookmarks: the main bookmark array
1859+ * @condition: condition, like "folder = '%q'"
1860+ * @value: a value to be inserted if @condition contains %q
1861+ * @recursive: if %TRUE include children
1862+ *
1863+ * Return value: the number of elements on success, -1 otherwise
1864+ *
1865+ * Since: 0.5.2
1866+ **/
1867+gint64
1868+midori_bookmarks_db_count_recursive (MidoriBookmarksDb* bookmarks,
1869+ const gchar* condition,
1870+ const gchar* value,
1871+ KatzeItem* folder,
1872+ gboolean recursive)
1873+{
1874+ gint64 id = -1;
1875+
1876+ g_return_val_if_fail (!folder || KATZE_ITEM_IS_FOLDER (folder), -1);
1877+
1878+ id = folder ? katze_item_get_meta_integer (folder, "id") : 0;
1879+
1880+ return midori_bookmarks_db_count_recursive_by_id (bookmarks, condition,
1881+ value, id,
1882+ recursive);
1883+}
1884
1885=== renamed file 'midori/midori-bookmarks.h' => 'midori/midori-bookmarks-db.h'
1886--- midori/midori-bookmarks.h 2012-11-25 15:37:41 +0000
1887+++ midori/midori-bookmarks-db.h 2013-06-11 20:53:24 +0000
1888@@ -10,31 +10,70 @@
1889 See the file COPYING for the full license text.
1890 */
1891
1892-#ifndef __MIDORI_BOOKMARKS_H__
1893-#define __MIDORI_BOOKMARKS_H__ 1
1894+#ifndef __MIDORI_BOOKMARKS_DB_H__
1895+#define __MIDORI_BOOKMARKS_DB_H__ 1
1896
1897 #include <sqlite3.h>
1898 #include <katze/katze.h>
1899
1900-void
1901-midori_bookmarks_add_item_cb (KatzeArray* array,
1902- KatzeItem* item,
1903- sqlite3* db);
1904-
1905-void
1906-midori_bookmarks_remove_item_cb (KatzeArray* array,
1907- KatzeItem* item,
1908- sqlite3* db);
1909+G_BEGIN_DECLS
1910+
1911+#define TYPE_MIDORI_BOOKMARKS_DB \
1912+ (midori_bookmarks_db_get_type ())
1913+#define MIDORI_BOOKMARKS_DB(obj) \
1914+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_MIDORI_BOOKMARKS_DB, MidoriBookmarksDb))
1915+#define MIDORI_BOOKMARKS_DB_CLASS(klass) \
1916+ (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_MIDORI_BOOKMARKS_DB, MidoriBookmarksDbClass))
1917+#define IS_MIDORI_BOOKMARKS_DB(obj) \
1918+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_MIDORI_BOOKMARKS_DB))
1919+#define IS_MIDORI_BOOKMARKS_DB_CLASS(klass) \
1920+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_MIDORI_BOOKMARKS_DB))
1921+#define MIDORI_BOOKMARKS_DB_GET_CLASS(obj) \
1922+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_MIDORI_BOOKMARKS_DB, MidoriBookmarksDbClass))
1923+
1924+typedef struct _MidoriBookmarksDb MidoriBookmarksDb;
1925+typedef struct _MidoriBookmarksDbClass MidoriBookmarksDbClass;
1926+
1927+GType
1928+midori_bookmarks_db_get_type (void) G_GNUC_CONST;
1929+
1930+MidoriBookmarksDb*
1931+midori_bookmarks_db_new (char** errmsg);
1932+
1933+void
1934+midori_bookmarks_db_on_quit (MidoriBookmarksDb* array);
1935+
1936+void
1937+midori_bookmarks_db_add_item (MidoriBookmarksDb* bookmarks, KatzeItem* item);
1938+
1939+void
1940+midori_bookmarks_db_update_item (MidoriBookmarksDb* bookmarks, KatzeItem* item);
1941+
1942+void
1943+midori_bookmarks_db_remove_item (MidoriBookmarksDb* bookmarks, KatzeItem* item);
1944+
1945+void
1946+midori_bookmarks_db_import_array (MidoriBookmarksDb* bookmarks,
1947+ KatzeArray* array,
1948+ gint64 parentid);
1949
1950 KatzeArray*
1951-midori_bookmarks_new (char** errmsg);
1952-
1953-void
1954-midori_bookmarks_on_quit (KatzeArray* array);
1955-
1956-void
1957-midori_bookmarks_import (const gchar* filename,
1958- sqlite3* db);
1959-
1960-#endif /* !__MIDORI_BOOKMARKS_H__ */
1961+midori_bookmarks_db_query_recursive (MidoriBookmarksDb* bookmarks,
1962+ const gchar* fields,
1963+ const gchar* condition,
1964+ const gchar* value,
1965+ gboolean recursive);
1966+
1967+gint64
1968+midori_bookmarks_db_count_recursive (MidoriBookmarksDb* bookmarks,
1969+ const gchar* condition,
1970+ const gchar* value,
1971+ KatzeItem* folder,
1972+ gboolean recursive);
1973+
1974+void
1975+midori_bookmarks_db_on_quit (MidoriBookmarksDb* array);
1976+
1977+
1978+#endif /* !__MIDORI_BOOKMARKS_DB_H__ */
1979
1980
1981=== modified file 'midori/midori-browser.c'
1982--- midori/midori-browser.c 2013-06-02 13:30:16 +0000
1983+++ midori/midori-browser.c 2013-06-11 20:53:24 +0000
1984@@ -26,6 +26,7 @@
1985 #include "midori-privatedata.h"
1986 #include "midori-core.h"
1987 #include "midori-privatedata.h"
1988+#include "midori-bookmarks-db.h"
1989
1990 #include "marshal.h"
1991
1992@@ -85,7 +86,7 @@
1993
1994 MidoriWebSettings* settings;
1995 KatzeArray* proxy_array;
1996- KatzeArray* bookmarks;
1997+ MidoriBookmarksDb* bookmarks;
1998 KatzeArray* trash;
1999 KatzeArray* search_engines;
2000 KatzeArray* history;
2001@@ -96,6 +97,8 @@
2002 gboolean show_statusbar;
2003 guint maximum_history_age;
2004 guint last_web_search;
2005+
2006+ gboolean bookmarkbar_populate;
2007 };
2008
2009 G_DEFINE_TYPE (MidoriBrowser, midori_browser, GTK_TYPE_WINDOW)
2010@@ -162,20 +165,13 @@
2011 GParamSpec* pspec);
2012
2013 void
2014-midori_bookmarks_import_array_db (sqlite3* db,
2015- KatzeArray* array,
2016- gint64 parentid);
2017-
2018-gboolean
2019-midori_bookmarks_update_item_db (sqlite3* db,
2020- KatzeItem* item);
2021-
2022-void
2023 midori_browser_open_bookmark (MidoriBrowser* browser,
2024 KatzeItem* item);
2025
2026 static void
2027 midori_bookmarkbar_populate (MidoriBrowser* browser);
2028+static void
2029+midori_bookmarkbar_populate_idle (MidoriBrowser* browser);
2030
2031 static void
2032 midori_bookmarkbar_clear (GtkWidget* toolbar);
2033@@ -203,8 +199,8 @@
2034 GtkToolbarStyle style);
2035
2036 static void
2037-midori_browser_set_bookmarks (MidoriBrowser* browser,
2038- KatzeArray* bookmarks);
2039+midori_browser_set_bookmarks (MidoriBrowser* browser,
2040+ MidoriBookmarksDb* bookmarks);
2041
2042 static void
2043 midori_browser_add_speed_dial (MidoriBrowser* browser);
2044@@ -484,6 +480,9 @@
2045 inter = ZEITGEIST_ZG_DELETE_EVENT;
2046 else
2047 g_assert_not_reached ();
2048+ g_assert (KATZE_IS_ITEM (item));
2049+ if (KATZE_ITEM_IS_FOLDER (item))
2050+ return;
2051 zeitgeist_log_insert_events_no_reply (zeitgeist_log_get_default (),
2052 zeitgeist_event_new_full (inter, ZEITGEIST_ZG_USER_ACTIVITY,
2053 "application://midori.desktop",
2054@@ -817,7 +816,7 @@
2055 }
2056
2057 static GtkWidget*
2058-midori_bookmark_folder_button_new (KatzeArray* array,
2059+midori_bookmark_folder_button_new (MidoriBookmarksDb* array,
2060 gboolean new_bookmark,
2061 gint64 selected,
2062 gint64 parentid)
2063@@ -1059,18 +1058,13 @@
2064 katze_item_set_meta_integer (bookmark, "parentid", selected);
2065
2066 if (new_bookmark)
2067- katze_array_add_item (browser->bookmarks, bookmark);
2068+ midori_bookmarks_db_add_item (browser->bookmarks, bookmark);
2069 else
2070- midori_bookmarks_update_item_db (db, bookmark);
2071- midori_browser_update_history (bookmark, "bookmark", new_bookmark ? "create" : "modify");
2072+ midori_bookmarks_db_update_item (browser->bookmarks, bookmark);
2073
2074- if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_toolbar)))
2075- if (!gtk_widget_get_visible (browser->bookmarkbar))
2076- _action_set_active (browser, "Bookmarkbar", TRUE);
2077 return_status = TRUE;
2078 }
2079- if (gtk_widget_get_visible (browser->bookmarkbar))
2080- midori_bookmarkbar_populate (browser);
2081+
2082 gtk_widget_destroy (dialog);
2083 return return_status;
2084 }
2085@@ -2321,7 +2315,7 @@
2086 "bookmarks",
2087 "Bookmarks",
2088 "The bookmarks folder, containing all bookmarks",
2089- KATZE_TYPE_ARRAY,
2090+ TYPE_MIDORI_BOOKMARKS_DB,
2091 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2092
2093 /**
2094@@ -3096,8 +3090,8 @@
2095 else
2096 condition = "parentid = %q";
2097
2098- bookmarks = midori_array_query (browser->bookmarks,
2099- "id, title, parentid, uri, app, pos_panel, pos_bar", condition, id);
2100+ bookmarks = midori_bookmarks_db_query_recursive (browser->bookmarks,
2101+ "id, title, parentid, uri, app, pos_panel, pos_bar", condition, id, FALSE);
2102 if (!bookmarks)
2103 return FALSE;
2104
2105@@ -4227,7 +4221,7 @@
2106 KatzeItem* item;
2107
2108 item = (KatzeItem*)g_object_get_data (G_OBJECT (menuitem), "KatzeItem");
2109- katze_array_remove_item (browser->bookmarks, item);
2110+ midori_bookmarks_db_remove_item (browser->bookmarks, item);
2111 }
2112
2113 static void
2114@@ -4242,7 +4236,7 @@
2115 menu = gtk_menu_new ();
2116 if (KATZE_ITEM_IS_FOLDER (item))
2117 {
2118- gint child_bookmarks_count = midori_array_count_recursive (browser->bookmarks,
2119+ gint child_bookmarks_count = midori_bookmarks_db_count_recursive (browser->bookmarks,
2120 "uri <> ''", NULL, item, FALSE);
2121
2122 midori_browser_bookmark_popup_item (menu,
2123@@ -4497,8 +4491,7 @@
2124 if (error)
2125 g_error_free (error);
2126 }
2127- midori_bookmarks_import_array_db (db, bookmarks, selected);
2128- katze_array_update (browser->bookmarks);
2129+ midori_bookmarks_db_import_array (browser->bookmarks, bookmarks, selected);
2130 g_object_unref (bookmarks);
2131 g_free (path);
2132 }
2133@@ -4552,7 +4545,7 @@
2134 return;
2135
2136 error = NULL;
2137- bookmarks = midori_array_query_recursive (browser->bookmarks,
2138+ bookmarks = midori_bookmarks_db_query_recursive (browser->bookmarks,
2139 "*", "parentid IS NULL", NULL, TRUE);
2140 if (!midori_array_to_file (bookmarks, path, format, &error))
2141 {
2142@@ -5979,6 +5972,21 @@
2143 }
2144 }
2145
2146+static gboolean
2147+midori_browser_idle (gpointer data)
2148+{
2149+ MidoriBrowser* browser = MIDORI_BROWSER (data);
2150+
2151+ if (browser->bookmarkbar_populate)
2152+ {
2153+ midori_bookmarkbar_populate_idle (browser);
2154+
2155+ browser->bookmarkbar_populate = FALSE;
2156+ }
2157+
2158+ return FALSE;
2159+}
2160+
2161 static void
2162 midori_browser_init (MidoriBrowser* browser)
2163 {
2164@@ -6459,6 +6467,8 @@
2165 katze_object_assign (browser->history, NULL);
2166 katze_object_assign (browser->dial, NULL);
2167
2168+ g_idle_remove_by_data (browser);
2169+
2170 G_OBJECT_CLASS (midori_browser_parent_class)->finalize (object);
2171 }
2172
2173@@ -6973,7 +6983,7 @@
2174 KatzeItem* item)
2175 {
2176 MidoriBrowser* browser = midori_browser_get_for_widget (toolbar);
2177- GtkAction* action = _action_by_name (browser, "Tools");
2178+ GtkAction* action = _action_by_name (browser, "Bookmarks");
2179 GtkToolItem* toolitem = katze_array_action_create_tool_item_for (
2180 KATZE_ARRAY_ACTION (action), item);
2181 g_object_set_data (G_OBJECT (toolitem), "KatzeItem", item);
2182@@ -6994,6 +7004,28 @@
2183 }
2184
2185 static void
2186+midori_bookmarkbar_add_item_cb (KatzeArray* bookmarks,
2187+ KatzeItem* item,
2188+ MidoriBrowser* browser)
2189+{
2190+ if (gtk_widget_get_visible (browser->bookmarkbar))
2191+ midori_bookmarkbar_populate (browser);
2192+ else if (katze_item_get_meta_boolean (item, "toolbar"))
2193+ _action_set_active (browser, "Bookmarkbar", TRUE);
2194+ midori_browser_update_history (item, "bookmark", "created");
2195+}
2196+
2197+static void
2198+midori_bookmarkbar_update_item_cb (KatzeArray* bookmarks,
2199+ KatzeItem* item,
2200+ MidoriBrowser* browser)
2201+{
2202+ if (gtk_widget_get_visible (browser->bookmarkbar))
2203+ midori_bookmarkbar_populate (browser);
2204+ midori_browser_update_history (item, "bookmark", "modify");
2205+}
2206+
2207+static void
2208 midori_bookmarkbar_remove_item_cb (KatzeArray* bookmarks,
2209 KatzeItem* item,
2210 MidoriBrowser* browser)
2211@@ -7006,6 +7038,16 @@
2212 static void
2213 midori_bookmarkbar_populate (MidoriBrowser* browser)
2214 {
2215+ if (browser->bookmarkbar_populate)
2216+ return;
2217+
2218+ g_idle_add (midori_browser_idle, browser);
2219+ browser->bookmarkbar_populate = TRUE;
2220+}
2221+
2222+static void
2223+midori_bookmarkbar_populate_idle (MidoriBrowser* browser)
2224+{
2225 KatzeArray* array;
2226 KatzeItem* item;
2227
2228@@ -7015,8 +7057,8 @@
2229 gtk_toolbar_insert (GTK_TOOLBAR (browser->bookmarkbar),
2230 gtk_separator_tool_item_new (), -1);
2231
2232- array = midori_array_query (browser->bookmarks,
2233- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "toolbar = 1", NULL);
2234+ array = midori_bookmarks_db_query_recursive (browser->bookmarks,
2235+ "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "toolbar = 1", NULL, FALSE);
2236 if (!array)
2237 {
2238 _action_set_sensitive (browser, "BookmarkAdd", FALSE);
2239@@ -7026,21 +7068,7 @@
2240
2241 KATZE_ARRAY_FOREACH_ITEM (item, array)
2242 {
2243- if (KATZE_ITEM_IS_BOOKMARK (item))
2244- midori_bookmarkbar_insert_item (browser->bookmarkbar, item);
2245- else
2246- {
2247- gint64 id = katze_item_get_meta_integer (item, "id");
2248- gchar* parentid = g_strdup_printf ("%" G_GINT64_FORMAT, id);
2249- KatzeArray* subfolder = midori_array_query (browser->bookmarks,
2250- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid = %q AND uri != ''",
2251- parentid);
2252-
2253- katze_item_set_name (KATZE_ITEM (subfolder), katze_item_get_name (item));
2254- katze_item_set_meta_integer (KATZE_ITEM (subfolder), "id", id);
2255- midori_bookmarkbar_insert_item (browser->bookmarkbar, KATZE_ITEM (subfolder));
2256- g_free (parentid);
2257- }
2258+ midori_bookmarkbar_insert_item (browser->bookmarkbar, item);
2259 }
2260 _action_set_sensitive (browser, "BookmarkAdd", TRUE);
2261 _action_set_sensitive (browser, "BookmarkFolderAdd", TRUE);
2262@@ -7070,13 +7098,20 @@
2263
2264 static void
2265 midori_browser_set_bookmarks (MidoriBrowser* browser,
2266- KatzeArray* bookmarks)
2267+ MidoriBookmarksDb* bookmarks)
2268 {
2269 MidoriWebSettings* settings;
2270
2271 if (browser->bookmarks != NULL)
2272+ {
2273+ g_signal_handlers_disconnect_by_func (browser->bookmarks,
2274+ midori_bookmarkbar_add_item_cb, browser);
2275+ g_signal_handlers_disconnect_by_func (browser->bookmarks,
2276+ midori_bookmarkbar_update_item_cb, browser);
2277 g_signal_handlers_disconnect_by_func (browser->bookmarks,
2278 midori_bookmarkbar_remove_item_cb, browser);
2279+ }
2280+
2281 settings = midori_browser_get_settings (browser);
2282 g_signal_handlers_disconnect_by_func (settings,
2283 midori_browser_show_bookmarkbar_notify_value_cb, browser);
2284@@ -7105,6 +7140,10 @@
2285 g_signal_connect (settings, "notify::show-bookmarkbar",
2286 G_CALLBACK (midori_browser_show_bookmarkbar_notify_value_cb), browser);
2287 g_object_notify (G_OBJECT (settings), "show-bookmarkbar");
2288+ g_signal_connect_after (bookmarks, "add-item",
2289+ G_CALLBACK (midori_bookmarkbar_add_item_cb), browser);
2290+ g_signal_connect_after (bookmarks, "update-item",
2291+ G_CALLBACK (midori_bookmarkbar_update_item_cb), browser);
2292 g_signal_connect_after (bookmarks, "remove-item",
2293 G_CALLBACK (midori_bookmarkbar_remove_item_cb), browser);
2294 }
2295
2296=== modified file 'midori/midori-frontend.c'
2297--- midori/midori-frontend.c 2013-05-23 22:15:27 +0000
2298+++ midori/midori-frontend.c 2013-06-11 20:53:24 +0000
2299@@ -10,7 +10,7 @@
2300 */
2301
2302 #include "midori-array.h"
2303-#include "midori-bookmarks.h"
2304+#include "midori-bookmarks-db.h"
2305 #include "midori-history.h"
2306 #include "midori-preferences.h"
2307 #include "midori-privatedata.h"
2308@@ -487,9 +487,9 @@
2309 }
2310 g_free (uri);
2311
2312- KatzeArray* bookmarks;
2313+ MidoriBookmarksDb* bookmarks;
2314 gchar* errmsg = NULL;
2315- if (!(bookmarks = midori_bookmarks_new (&errmsg)))
2316+ if (!(bookmarks = midori_bookmarks_db_new (&errmsg)))
2317 {
2318 g_string_append_printf (error_messages,
2319 _("Bookmarks couldn't be loaded: %s\n"), errmsg);
2320@@ -594,11 +594,11 @@
2321 midori_normal_app_on_quit (MidoriApp* app)
2322 {
2323 MidoriWebSettings* settings = katze_object_get_object (app, "settings");
2324- KatzeArray* bookmarks = katze_object_get_object (app, "bookmarks");
2325+ MidoriBookmarksDb* bookmarks = katze_object_get_object (app, "bookmarks");
2326 KatzeArray* history = katze_object_get_object (app, "history");
2327
2328 g_object_notify (G_OBJECT (settings), "load-on-startup");
2329- midori_bookmarks_on_quit (bookmarks);
2330+ midori_bookmarks_db_on_quit (bookmarks);
2331 midori_history_on_quit (history, settings);
2332 midori_private_data_on_quit (settings);
2333 /* Removing KatzeHttpCookies makes it save outstanding changes */
2334
2335=== modified file 'midori/midori.h'
2336--- midori/midori.h 2012-12-02 15:32:20 +0000
2337+++ midori/midori.h 2013-06-11 20:53:24 +0000
2338@@ -14,7 +14,7 @@
2339
2340 #include "midori-app.h"
2341 #include "midori-array.h"
2342-#include "midori-bookmarks.h"
2343+#include "midori-bookmarks-db.h"
2344 #include "midori-browser.h"
2345 #include "midori-extension.h"
2346 #include "midori-frontend.h"
2347
2348=== modified file 'panels/midori-bookmarks.c'
2349--- panels/midori-bookmarks.c 2013-06-04 17:28:05 +0000
2350+++ panels/midori-bookmarks.c 2013-06-11 20:53:24 +0000
2351@@ -17,6 +17,7 @@
2352 #include "midori-platform.h"
2353 #include "midori-view.h"
2354 #include "midori-core.h"
2355+#include "midori-bookmarks-db.h"
2356
2357 #include <glib/gi18n.h>
2358 #include <string.h>
2359@@ -25,6 +26,111 @@
2360
2361 #define COMPLETION_DELAY 200
2362
2363+G_BEGIN_DECLS
2364+
2365+#define MIDORI_BOOKMARKS_TREE_STORE_TYPE \
2366+ (midori_bookmarks_tree_store_get_type ())
2367+#define MIDORI_BOOKMARKS_TREE_STORE(obj) \
2368+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), MIDORI_BOOKMARKS_TREE_STORE_TYPE, MidoriBookmarksTreeStore))
2369+#define MIDORI_BOOKMARKS_TREE_STORE_CLASS(klass) \
2370+ (G_TYPE_CHECK_CLASS_CAST ((klass), MIDORI_BOOKMARKS_TREE_STORE_TYPE, MidoriBookmarksTreeStoreClass))
2371+
2372+static gboolean
2373+midori_bookmarks_tree_store_row_drop_possible (GtkTreeDragDest *drag_dest,
2374+ GtkTreePath *dest_path,
2375+ GtkSelectionData *selection_data);
2376+
2377+typedef struct _MidoriBookmarksTreeStore MidoriBookmarksTreeStore;
2378+typedef struct _MidoriBookmarksTreeStoreClass MidoriBookmarksTreeStoreClass;
2379+
2380+struct _MidoriBookmarksTreeStore
2381+{
2382+ GtkTreeStore parent_instance;
2383+};
2384+
2385+struct _MidoriBookmarksTreeStoreClass
2386+{
2387+ GtkTreeStoreClass parent_class;
2388+};
2389+
2390+static GtkTreeDragDestIface *
2391+gtk_tree_store_gtk_tree_drag_dest_iface = NULL;
2392+
2393+static void
2394+midori_bookmarks_tree_store_drag_dest_init (GtkTreeDragDestIface *iface)
2395+{
2396+ gtk_tree_store_gtk_tree_drag_dest_iface = g_type_interface_peek_parent (iface);
2397+
2398+ iface->row_drop_possible = midori_bookmarks_tree_store_row_drop_possible;
2399+}
2400+
2401+static void
2402+midori_bookmarks_tree_store_init (MidoriBookmarksTreeStore* item)
2403+{
2404+}
2405+
2406+static void
2407+midori_bookmarks_tree_store_class_init (MidoriBookmarksTreeStoreClass *class)
2408+{
2409+}
2410+
2411+G_DEFINE_TYPE_WITH_CODE (MidoriBookmarksTreeStore,
2412+ midori_bookmarks_tree_store,
2413+ GTK_TYPE_TREE_STORE,
2414+ G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST,
2415+ midori_bookmarks_tree_store_drag_dest_init));
2416+
2417+
2418+GtkTreeStore*
2419+midori_bookmarks_tree_store_new (gint n_columns, ...)
2420+{
2421+ GtkTreeStore* tree_store = GTK_TREE_STORE (g_object_new (MIDORI_BOOKMARKS_TREE_STORE_TYPE, NULL));
2422+ va_list ap;
2423+ GType* types;
2424+ gint n;
2425+
2426+ if (!tree_store)
2427+ return NULL;
2428+
2429+ types = g_new (GType, n_columns);
2430+
2431+ if (!types)
2432+ {
2433+ g_object_unref(tree_store);
2434+ return NULL;
2435+ }
2436+
2437+ va_start(ap, n_columns);
2438+ for (n = 0; n < n_columns; n++)
2439+ {
2440+ types[n] = va_arg(ap, GType);
2441+ }
2442+ va_end(ap);
2443+
2444+ gtk_tree_store_set_column_types (tree_store,
2445+ n_columns,
2446+ types);
2447+
2448+ g_free (types);
2449+ return tree_store;
2450+}
2451+
2452+
2453+GtkTreeStore*
2454+midori_bookmarks_tree_store_newv (gint n_columns, GType *types)
2455+{
2456+ GtkTreeStore* tree_store = GTK_TREE_STORE (g_object_new (MIDORI_BOOKMARKS_TREE_STORE_TYPE, NULL));
2457+
2458+ if (!tree_store)
2459+ return NULL;
2460+
2461+ gtk_tree_store_set_column_types (tree_store,
2462+ n_columns,
2463+ types);
2464+
2465+ return tree_store;
2466+}
2467+
2468 gboolean
2469 midori_browser_edit_bookmark_dialog_new (MidoriBrowser* browser,
2470 KatzeItem* bookmark,
2471@@ -36,6 +142,12 @@
2472 midori_browser_open_bookmark (MidoriBrowser* browser,
2473 KatzeItem* item);
2474
2475+static void
2476+midori_bookmarks_row_changed_cb (GtkTreeModel* model,
2477+ GtkTreePath* path,
2478+ GtkTreeIter* iter,
2479+ MidoriBookmarks* bookmarks);
2480+
2481 struct _MidoriBookmarks
2482 {
2483 GtkVBox parent_instance;
2484@@ -44,11 +156,12 @@
2485 GtkWidget* delete;
2486 GtkWidget* treeview;
2487 MidoriApp* app;
2488- KatzeArray* array;
2489+ MidoriBookmarksDb* bookmarks_db;
2490
2491 gint filter_timeout;
2492 gchar* filter;
2493
2494+ GList* pending_inserts;
2495 KatzeItem* hovering_item;
2496 };
2497
2498@@ -87,7 +200,12 @@
2499 GParamSpec* pspec);
2500
2501 static void
2502-midori_bookmarks_statusbar_update (MidoriBookmarks *bookmarks);
2503+midori_bookmarks_add_item_cb (KatzeArray* array,
2504+ KatzeItem* item,
2505+ MidoriBookmarks* bookmarks);
2506+static void
2507+midori_bookmarks_update_cb (KatzeArray* array,
2508+ MidoriBookmarks* bookmarks);
2509
2510 static void
2511 midori_bookmarks_class_init (MidoriBookmarksClass* class)
2512@@ -124,6 +242,7 @@
2513 return STOCK_BOOKMARKS;
2514 }
2515
2516+#if 0
2517 /* TODO: Function never used */
2518 void
2519 midori_bookmarks_export_array_db (sqlite3* db,
2520@@ -137,7 +256,7 @@
2521 gchar* parent_id;
2522
2523 parent_id = g_strdup_printf ("%" G_GINT64_FORMAT, parentid);
2524- if (!(root_array = midori_array_query (array, "*", "parentid = %q", parent_id)))
2525+ if (!(root_array = midori_bookmarks_db_query_recursive (array, "*", "parentid = %q", parent_id, FALSE)))
2526 {
2527 g_free (parent_id);
2528 return;
2529@@ -159,27 +278,7 @@
2530 g_free (parent_id);
2531 g_list_free (list);
2532 }
2533-
2534-void
2535-midori_bookmarks_import_array_db (sqlite3* db,
2536- KatzeArray* array,
2537- gint64 parentid)
2538-{
2539- GList* list;
2540- KatzeItem* item;
2541- gint64 id;
2542-
2543- if (!db)
2544- return;
2545-
2546- KATZE_ARRAY_FOREACH_ITEM_L (item, array, list)
2547- {
2548- id = midori_bookmarks_insert_item_db (db, item, parentid);
2549- if (KATZE_IS_ARRAY (item))
2550- midori_bookmarks_import_array_db (db, KATZE_ARRAY (item), id);
2551- }
2552- g_list_free (list);
2553-}
2554+#endif
2555
2556 static KatzeArray*
2557 midori_bookmarks_read_from_db (MidoriBookmarks* bookmarks,
2558@@ -189,21 +288,21 @@
2559 KatzeArray* array;
2560
2561 if (keyword && *keyword)
2562- array = midori_array_query (bookmarks->array,
2563- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "title LIKE '%%%q%%'", keyword);
2564+ array = midori_bookmarks_db_query_recursive (bookmarks->bookmarks_db,
2565+ "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "title LIKE '%%%q%%'", keyword, FALSE);
2566 else
2567 {
2568 if (parentid > 0)
2569 {
2570 gchar* parent_id = g_strdup_printf ("%" G_GINT64_FORMAT, parentid);
2571- array = midori_array_query (bookmarks->array,
2572- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid = %q", parent_id);
2573+ array = midori_bookmarks_db_query_recursive (bookmarks->bookmarks_db,
2574+ "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid = %q", parent_id, FALSE);
2575
2576 g_free (parent_id);
2577 }
2578 else
2579- array = midori_array_query (bookmarks->array,
2580- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid IS NULL", NULL);
2581+ array = midori_bookmarks_db_query_recursive (bookmarks->bookmarks_db,
2582+ "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid IS NULL", NULL, FALSE);
2583 }
2584 return array ? array : katze_array_new (KATZE_TYPE_ITEM);
2585 }
2586@@ -234,90 +333,133 @@
2587 g_object_unref (item);
2588 }
2589
2590-gint64
2591-midori_bookmarks_insert_item_db (sqlite3* db,
2592- KatzeItem* item,
2593- gint64 parentid)
2594+static gboolean
2595+midori_bookmarks_reach_item_recurse (GtkTreeModel* model,
2596+ GtkTreeIter* iter,
2597+ gint64 id)
2598 {
2599- gchar* sqlcmd;
2600- char* errmsg = NULL;
2601- KatzeItem* old_parent;
2602- gchar* new_parentid;
2603- gchar* id = NULL;
2604- const gchar* uri = NULL;
2605- const gchar* desc = NULL;
2606- gint64 seq = 0;
2607-
2608- /* Bookmarks must have a name, import may produce invalid items */
2609- g_return_val_if_fail (katze_item_get_name (item), seq);
2610-
2611- if (!db)
2612- return seq;
2613-
2614- if (katze_item_get_meta_integer (item, "id") > 0)
2615- id = g_strdup_printf ("%" G_GINT64_FORMAT, katze_item_get_meta_integer(item, "id"));
2616- else
2617- id = g_strdup_printf ("NULL");
2618-
2619- if (KATZE_ITEM_IS_BOOKMARK (item))
2620- uri = katze_item_get_uri (item);
2621-
2622- if (katze_item_get_text (item))
2623- desc = katze_item_get_text (item);
2624-
2625- /* Use folder, otherwise fallback to parent folder */
2626- old_parent = katze_item_get_parent (item);
2627- if (parentid > 0)
2628- new_parentid = g_strdup_printf ("%" G_GINT64_FORMAT, parentid);
2629- else if (old_parent && katze_item_get_meta_integer (old_parent, "id") > 0)
2630- new_parentid = g_strdup_printf ("%" G_GINT64_FORMAT, katze_item_get_meta_integer (old_parent, "id"));
2631- else
2632- new_parentid = g_strdup_printf ("NULL");
2633-
2634- sqlcmd = sqlite3_mprintf (
2635- "INSERT INTO bookmarks (id, parentid, title, uri, desc, toolbar, app) "
2636- "VALUES (%q, %q, '%q', '%q', '%q', %d, %d)",
2637- id,
2638- new_parentid,
2639- katze_item_get_name (item),
2640- katze_str_non_null (uri),
2641- katze_str_non_null (desc),
2642- katze_item_get_meta_boolean (item, "toolbar"),
2643- katze_item_get_meta_boolean (item, "app"));
2644-
2645- if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) == SQLITE_OK)
2646+ do
2647 {
2648- /* Get insert id */
2649- if (g_str_equal (id, "NULL"))
2650- {
2651- KatzeArray* seq_array;
2652-
2653- sqlite3_free (sqlcmd);
2654- sqlcmd = sqlite3_mprintf (
2655- "SELECT seq FROM sqlite_sequence WHERE name = 'bookmarks'");
2656-
2657- seq_array = katze_array_from_sqlite (db, sqlcmd);
2658- if (katze_array_get_nth_item (seq_array, 0))
2659+ GtkTreeIter child;
2660+ KatzeItem *item;
2661+ gint64 itemid = -1;
2662+
2663+ gtk_tree_model_get (model, iter, 0, &item, -1);
2664+
2665+ if (!KATZE_ITEM_IS_SEPARATOR(item))
2666+ {
2667+ itemid = katze_item_get_meta_integer (item, "id");
2668+ g_object_unref (item);
2669+ }
2670+
2671+ if (id == itemid)
2672+ return TRUE;
2673+
2674+ if (gtk_tree_model_iter_children (model, &child, iter))
2675+ {
2676+ if (midori_bookmarks_reach_item_recurse (model, &child, id))
2677 {
2678- KatzeItem* seq_item = katze_array_get_nth_item (seq_array, 0);
2679-
2680- seq = katze_item_get_meta_integer (seq_item, "seq");
2681- katze_item_set_meta_integer (item, "id", seq);
2682+ *iter = child;
2683+ return TRUE;
2684 }
2685- g_object_unref (seq_array);
2686 }
2687 }
2688- else
2689- {
2690- g_printerr (_("Failed to add bookmark item: %s\n"), errmsg);
2691- sqlite3_free (errmsg);
2692- }
2693-
2694- sqlite3_free (sqlcmd);
2695- g_free (new_parentid);
2696- g_free (id);
2697-
2698- return seq;
2699+ while (gtk_tree_model_iter_next(model, iter));
2700+
2701+ return FALSE;
2702+}
2703+
2704+static gboolean
2705+midori_bookmarks_reach_item (GtkTreeModel* model,
2706+ GtkTreeIter* iter,
2707+ gint64 id)
2708+{
2709+ if (!gtk_tree_model_get_iter_first(model, iter))
2710+ return FALSE;
2711+
2712+ return midori_bookmarks_reach_item_recurse (model, iter, id);
2713+}
2714+
2715+static void
2716+midori_bookmarks_add_item_to_model(GtkTreeStore* model,
2717+ GtkTreeIter* parent,
2718+ KatzeItem* item)
2719+{
2720+ if (KATZE_ITEM_IS_BOOKMARK (item))
2721+ {
2722+ gchar* tooltip = g_markup_escape_text (katze_item_get_uri (item), -1);
2723+
2724+ gtk_tree_store_insert_with_values (model, NULL, parent,
2725+ 0,
2726+ 0, item, 1, tooltip, -1);
2727+ g_free (tooltip);
2728+ }
2729+ else
2730+ {
2731+ GtkTreeIter root_iter;
2732+
2733+ gtk_tree_store_insert_with_values (model, &root_iter, parent,
2734+ 0, 0, item, -1);
2735+
2736+ /* That's an invisible dummy, so we always have an expander */
2737+ gtk_tree_store_insert_with_values (model, NULL, &root_iter,
2738+ 0,
2739+ 0, NULL, -1);
2740+ }
2741+}
2742+
2743+static void
2744+midori_bookmarks_update_item_in_model(MidoriBookmarks* bookmarks,
2745+ GtkTreeStore* model,
2746+ GtkTreeIter* iter,
2747+ KatzeItem* item)
2748+{
2749+ g_signal_handlers_block_by_func (model,
2750+ midori_bookmarks_row_changed_cb,
2751+ bookmarks);
2752+
2753+ if (KATZE_ITEM_IS_BOOKMARK (item))
2754+ {
2755+ gchar* tooltip = g_markup_escape_text (katze_item_get_uri (item), -1);
2756+
2757+ gtk_tree_store_set(model, iter,
2758+ 0, item, 1, tooltip, -1);
2759+
2760+ g_free (tooltip);
2761+ }
2762+ else
2763+ {
2764+ gtk_tree_store_set(model, iter,
2765+ 0, item, -1);
2766+ }
2767+
2768+ g_signal_handlers_unblock_by_func (model,
2769+ midori_bookmarks_row_changed_cb,
2770+ bookmarks);
2771+}
2772+
2773+static void
2774+midori_bookmarks_add_item (KatzeItem* item,
2775+ MidoriBookmarks* bookmarks);
2776+
2777+static gboolean
2778+midori_bookmarks_idle (MidoriBookmarks* bookmarks)
2779+{
2780+ GList* list_iter;
2781+
2782+ for (list_iter = bookmarks->pending_inserts; list_iter; list_iter = g_list_next (list_iter))
2783+ {
2784+ KatzeItem *item = KATZE_ITEM (list_iter->data);
2785+
2786+ midori_bookmarks_add_item (item, bookmarks);
2787+
2788+ g_object_unref (item);
2789+ }
2790+
2791+ g_list_free (bookmarks->pending_inserts);
2792+ bookmarks->pending_inserts = NULL;
2793+
2794+ return FALSE;
2795 }
2796
2797 static void
2798@@ -325,11 +467,98 @@
2799 KatzeItem* item,
2800 MidoriBookmarks* bookmarks)
2801 {
2802- GtkTreeModel* model;
2803- model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
2804- gtk_tree_store_clear (GTK_TREE_STORE (model));
2805- midori_bookmarks_read_from_db_to_model (bookmarks,
2806- GTK_TREE_STORE (model), NULL, 0, bookmarks->filter);
2807+ if (!bookmarks->pending_inserts)
2808+ g_idle_add ((GSourceFunc)midori_bookmarks_idle, bookmarks);
2809+
2810+ g_object_ref (item);
2811+ bookmarks->pending_inserts = g_list_append (bookmarks->pending_inserts, item);
2812+}
2813+
2814+static void
2815+midori_bookmarks_add_item (KatzeItem* item,
2816+ MidoriBookmarks* bookmarks)
2817+{
2818+ gint64 id = katze_item_get_meta_integer (item, "id");
2819+ gint64 parentid = katze_item_get_meta_integer (item, "parentid");
2820+ GtkTreeModel* model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
2821+ GtkTreeIter iter;
2822+
2823+ if (!parentid)
2824+ {
2825+ midori_bookmarks_add_item_to_model (GTK_TREE_STORE (model), NULL, item);
2826+ }
2827+ else if (midori_bookmarks_reach_item (model, &iter, parentid))
2828+ {
2829+ GtkTreePath* path = gtk_tree_model_get_path(model, &iter);
2830+
2831+ if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (bookmarks->treeview), path))
2832+ {
2833+ midori_bookmarks_add_item_to_model (GTK_TREE_STORE (model), &iter, item);
2834+ }
2835+
2836+ gtk_tree_path_free (path);
2837+ }
2838+}
2839+
2840+static void
2841+midori_bookmarks_update_item_cb (KatzeArray* array,
2842+ KatzeItem* item,
2843+ MidoriBookmarks* bookmarks)
2844+{
2845+ gint64 id = katze_item_get_meta_integer (item, "id");
2846+ gint64 parentid = katze_item_get_meta_integer (item, "parentid");
2847+ GtkTreeModel* model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
2848+ GtkTreeIter iter;
2849+
2850+ if (midori_bookmarks_reach_item (model, &iter, id))
2851+ {
2852+ gint64 old_parentid = 0;
2853+ GtkTreeIter parent;
2854+
2855+ if (gtk_tree_model_iter_parent (model, &parent, &iter))
2856+ {
2857+ KatzeItem* old_parent;
2858+
2859+ gtk_tree_model_get (model, &parent, 0, &old_parent, -1);
2860+
2861+ old_parentid = katze_item_get_meta_integer (old_parent, "id");
2862+
2863+ g_object_unref (old_parent);
2864+
2865+ if (parentid == old_parentid)
2866+ {
2867+ midori_bookmarks_update_item_in_model (bookmarks, GTK_TREE_STORE (model), &iter, item);
2868+ }
2869+ else
2870+ {
2871+ gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
2872+
2873+ if (!gtk_tree_model_iter_has_child (model, &parent))
2874+ {
2875+ GtkTreePath* path = gtk_tree_model_get_path(model, &parent);
2876+
2877+ if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (bookmarks->treeview), path))
2878+ gtk_tree_view_collapse_row (GTK_TREE_VIEW (bookmarks->treeview), path);
2879+
2880+ gtk_tree_path_free (path);
2881+ }
2882+
2883+ midori_bookmarks_add_item (item, bookmarks);
2884+ }
2885+ }
2886+ else if (parentid == 0)
2887+ {
2888+ midori_bookmarks_update_item_in_model (bookmarks, GTK_TREE_STORE (model), &iter, item);
2889+ }
2890+ else
2891+ {
2892+ gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
2893+
2894+ midori_bookmarks_add_item (item, bookmarks);
2895+ }
2896+ }
2897+ else
2898+ midori_bookmarks_add_item (item, bookmarks);
2899 }
2900
2901 static void
2902@@ -337,12 +566,36 @@
2903 KatzeItem* item,
2904 MidoriBookmarks* bookmarks)
2905 {
2906+ gint64 id = katze_item_get_meta_integer (item, "id");
2907 GtkTreeModel* model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
2908- gtk_tree_store_clear (GTK_TREE_STORE (model));
2909- midori_bookmarks_read_from_db_to_model (bookmarks,
2910- GTK_TREE_STORE (model), NULL, 0, bookmarks->filter);
2911+ GtkTreeIter iter;
2912+
2913+ if (midori_bookmarks_reach_item (model, &iter, id))
2914+ {
2915+ GtkTreeIter parent;
2916+
2917+ if (gtk_tree_model_iter_parent (model, &parent, &iter))
2918+ {
2919+ gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
2920+
2921+ if (!gtk_tree_model_iter_has_child (model, &parent))
2922+ {
2923+ GtkTreePath* path = gtk_tree_model_get_path(model, &parent);
2924+
2925+ if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (bookmarks->treeview), path))
2926+ gtk_tree_view_collapse_row (GTK_TREE_VIEW (bookmarks->treeview), path);
2927+
2928+ gtk_tree_path_free (path);
2929+ }
2930+ }
2931+ else
2932+ {
2933+ gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
2934+ }
2935+ }
2936 }
2937
2938+
2939 static void
2940 midori_bookmarks_update_cb (KatzeArray* array,
2941 MidoriBookmarks* bookmarks)
2942@@ -354,6 +607,42 @@
2943 }
2944
2945
2946+static gboolean
2947+midori_bookmarks_tree_store_row_drop_possible (GtkTreeDragDest* drag_dest,
2948+ GtkTreePath* dest_path,
2949+ GtkSelectionData* selection_data)
2950+{
2951+ gboolean row_drop_possible =
2952+ gtk_tree_store_gtk_tree_drag_dest_iface->row_drop_possible (drag_dest,
2953+ dest_path,
2954+ selection_data);
2955+
2956+ if (!row_drop_possible)
2957+ return FALSE;
2958+
2959+ if ((!gtk_tree_path_get_depth(dest_path) > 1)
2960+ && gtk_tree_path_up(dest_path))
2961+ {
2962+ GtkTreeModel* model = GTK_TREE_MODEL(drag_dest);
2963+ GtkTreeIter iter;
2964+
2965+ if (gtk_tree_model_get_iter (model, &iter, dest_path))
2966+ {
2967+ KatzeItem* item;
2968+
2969+ gtk_tree_model_get (model, &iter, 0, &item, -1);
2970+
2971+ if (!KATZE_ITEM_IS_FOLDER(item))
2972+ row_drop_possible = FALSE;
2973+
2974+ if (item)
2975+ g_object_unref (item);
2976+ }
2977+ }
2978+
2979+ return row_drop_possible;
2980+}
2981+
2982 static void
2983 midori_bookmarks_row_changed_cb (GtkTreeModel* model,
2984 GtkTreePath* path,
2985@@ -362,31 +651,37 @@
2986 {
2987 KatzeItem* item;
2988 GtkTreeIter parent;
2989- KatzeItem* new_parent = NULL;
2990- gint64 parentid;
2991-
2992- gtk_tree_model_get (model, iter, 0, &item, -1);
2993+ gint64 parentid = 0;
2994
2995 if (gtk_tree_model_iter_parent (model, &parent, iter))
2996 {
2997+ KatzeItem* new_parent;
2998+
2999 gtk_tree_model_get (model, &parent, 0, &new_parent, -1);
3000
3001- /* Bookmarks must not be moved into non-folder items */
3002- if (!KATZE_ITEM_IS_FOLDER (new_parent))
3003- parentid = 0;
3004- else
3005- parentid = katze_item_get_meta_integer (new_parent, "id");
3006+ /* Bookmarks cannot be moved into non-folder items */
3007+ g_assert (KATZE_ITEM_IS_FOLDER (new_parent));
3008+
3009+ parentid = katze_item_get_meta_integer (new_parent, "id");
3010+
3011+ g_object_unref (new_parent);
3012 }
3013- else
3014- parentid = 0;
3015-
3016- katze_array_remove_item (bookmarks->array, item);
3017+
3018+ gtk_tree_model_get (model, iter, 0, &item, -1);
3019+
3020 katze_item_set_meta_integer (item, "parentid", parentid);
3021- katze_array_add_item (bookmarks->array, item);
3022+
3023+ g_signal_handlers_block_by_func (bookmarks->bookmarks_db,
3024+ midori_bookmarks_update_item_cb,
3025+ bookmarks);
3026+
3027+ midori_bookmarks_db_update_item (bookmarks->bookmarks_db, item);
3028+
3029+ g_signal_handlers_unblock_by_func (bookmarks->bookmarks_db,
3030+ midori_bookmarks_update_item_cb,
3031+ bookmarks);
3032
3033 g_object_unref (item);
3034- if (new_parent)
3035- g_object_unref (new_parent);
3036 }
3037
3038 static void
3039@@ -412,24 +707,15 @@
3040 {
3041 KatzeItem* item;
3042 MidoriBrowser* browser;
3043- gint64 parentid;
3044
3045 gtk_tree_model_get (model, &iter, 0, &item, -1);
3046
3047 g_assert (!KATZE_ITEM_IS_SEPARATOR (item));
3048
3049 browser = midori_browser_get_for_widget (bookmarks->treeview);
3050- parentid = katze_item_get_meta_integer (item, "parentid");
3051 midori_browser_edit_bookmark_dialog_new (
3052 browser, item, FALSE, KATZE_ITEM_IS_FOLDER (item), NULL);
3053
3054- if (katze_item_get_meta_integer (item, "parentid") != parentid)
3055- {
3056- gtk_tree_store_clear (GTK_TREE_STORE (model));
3057- midori_bookmarks_read_from_db_to_model (bookmarks, GTK_TREE_STORE (model),
3058- NULL, 0, NULL);
3059- }
3060-
3061 g_object_unref (item);
3062 }
3063 }
3064@@ -445,7 +731,7 @@
3065 gtk_widget_set_sensitive (GTK_WIDGET (bookmarks->edit), selected);
3066 }
3067
3068-static gchar*
3069+static gchar*
3070 midori_bookmarks_statusbar_bookmarks_str (gint count)
3071 {
3072 if (!count)
3073@@ -455,7 +741,7 @@
3074 return g_strdup_printf (ngettext ("%d bookmark", "%d bookmarks", count), count);
3075 }
3076
3077-static gchar*
3078+static gchar*
3079 midori_bookmarks_statusbar_subfolders_str (gint count)
3080 {
3081 if (!count)
3082@@ -481,9 +767,9 @@
3083
3084 if (KATZE_ITEM_IS_FOLDER (item))
3085 {
3086- gint child_folders_count = midori_array_count_recursive (bookmarks->array,
3087+ gint child_folders_count = midori_bookmarks_db_count_recursive (bookmarks->bookmarks_db,
3088 "uri = ''", NULL, item, FALSE);
3089- gint child_bookmarks_count = midori_array_count_recursive (bookmarks->array,
3090+ gint child_bookmarks_count = midori_bookmarks_db_count_recursive (bookmarks->bookmarks_db,
3091 "uri <> ''", NULL, item, FALSE);
3092 gchar* child_folders_str = midori_bookmarks_statusbar_subfolders_str (child_folders_count);
3093 gchar* child_bookmarks_str = midori_bookmarks_statusbar_bookmarks_str (child_bookmarks_count);
3094@@ -516,13 +802,13 @@
3095 }
3096 else
3097 {
3098- gint child_folders_count = midori_array_count_recursive (bookmarks->array,
3099+ gint child_folders_count = midori_bookmarks_db_count_recursive (bookmarks->bookmarks_db,
3100 "uri = ''", NULL, NULL, FALSE);
3101- gint child_bookmarks_count = midori_array_count_recursive (bookmarks->array,
3102+ gint child_bookmarks_count = midori_bookmarks_db_count_recursive (bookmarks->bookmarks_db,
3103 "uri <> ''", NULL, NULL, FALSE);
3104 gchar* child_folders_str = midori_bookmarks_statusbar_subfolders_str (child_folders_count);
3105 gchar* child_bookmarks_str = midori_bookmarks_statusbar_bookmarks_str (child_bookmarks_count);
3106-
3107+
3108 if (!child_bookmarks_count && (child_folders_count >= 1))
3109 /* i18n: [[n] folder(s)] and no bookmark */
3110 text = g_strdup_printf (_("%s and no bookmark"),
3111@@ -533,7 +819,7 @@
3112 /* i18n: [[n] bookmark(s)] and [[n] folder(s)] */
3113 text = g_strdup_printf (_("%s and %s"),
3114 child_bookmarks_str, child_folders_str);
3115-
3116+
3117 g_free (child_folders_str);
3118 g_free (child_bookmarks_str);
3119 }
3120@@ -541,9 +827,9 @@
3121 if (text)
3122 {
3123 MidoriBrowser* browser = midori_browser_get_for_widget (bookmarks->treeview);
3124-
3125+
3126 g_object_set (browser, "statusbar-text", text, NULL);
3127-
3128+
3129 g_free(text);
3130 }
3131 }
3132@@ -603,13 +889,8 @@
3133
3134 gtk_tree_model_get (model, &iter, 0, &item, -1);
3135
3136- /* Manually remove the iter and block clearing the treeview */
3137- gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
3138- g_signal_handlers_block_by_func (bookmarks->array,
3139- midori_bookmarks_remove_item_cb, bookmarks);
3140- katze_array_remove_item (bookmarks->array, item);
3141- g_signal_handlers_unblock_by_func (bookmarks->array,
3142- midori_bookmarks_remove_item_cb, bookmarks);
3143+ midori_bookmarks_db_remove_item (bookmarks->bookmarks_db, item);
3144+
3145 g_object_unref (item);
3146 }
3147 }
3148@@ -693,9 +974,9 @@
3149 GtkTreeModel* model;
3150
3151 model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
3152- if (bookmarks->array)
3153+ if (bookmarks->bookmarks_db)
3154 {
3155- g_object_unref (bookmarks->array);
3156+ g_object_unref (bookmarks->bookmarks_db);
3157 gtk_tree_store_clear (GTK_TREE_STORE (model));
3158 }
3159 katze_assign (bookmarks->app, app);
3160@@ -703,17 +984,19 @@
3161 return;
3162
3163 g_object_ref (app);
3164- bookmarks->array = katze_object_get_object (app, "bookmarks");
3165+ bookmarks->bookmarks_db = katze_object_get_object (app, "bookmarks");
3166 midori_bookmarks_read_from_db_to_model (bookmarks, GTK_TREE_STORE (model), NULL, 0, NULL);
3167- g_signal_connect_after (bookmarks->array, "add-item",
3168- G_CALLBACK (midori_bookmarks_add_item_cb), bookmarks);
3169- g_signal_connect (bookmarks->array, "remove-item",
3170- G_CALLBACK (midori_bookmarks_remove_item_cb), bookmarks);
3171- g_signal_connect (bookmarks->array, "update",
3172- G_CALLBACK (midori_bookmarks_update_cb), bookmarks);
3173+ g_signal_connect_after (bookmarks->bookmarks_db, "add-item",
3174+ G_CALLBACK (midori_bookmarks_add_item_cb), bookmarks);
3175+ g_signal_connect_after (bookmarks->bookmarks_db, "update-item",
3176+ G_CALLBACK (midori_bookmarks_update_item_cb), bookmarks);
3177+ g_signal_connect (bookmarks->bookmarks_db, "remove-item",
3178+ G_CALLBACK (midori_bookmarks_remove_item_cb), bookmarks);
3179+ g_signal_connect (bookmarks->bookmarks_db, "update",
3180+ G_CALLBACK (midori_bookmarks_update_cb), bookmarks);
3181 g_signal_connect_after (model, "row-changed",
3182- G_CALLBACK (midori_bookmarks_row_changed_cb),
3183- bookmarks);
3184+ G_CALLBACK (midori_bookmarks_row_changed_cb),
3185+ bookmarks);
3186 }
3187
3188 static void
3189@@ -943,12 +1226,12 @@
3190 menu = gtk_menu_new ();
3191 if (KATZE_ITEM_IS_FOLDER (item))
3192 {
3193- gint child_bookmarks_count = midori_array_count_recursive (bookmarks->array,
3194+ gint child_bookmarks_count = midori_bookmarks_db_count_recursive (bookmarks->bookmarks_db,
3195 "uri <> ''", NULL, item, FALSE);
3196
3197 midori_bookmarks_popup_item (menu,
3198- STOCK_TAB_NEW, _("Open all in _Tabs"), item,
3199- (!child_bookmarks_count ? NULL : midori_bookmarks_open_in_tab_activate_cb),
3200+ STOCK_TAB_NEW, _("Open all in _Tabs"), item,
3201+ (!child_bookmarks_count ? NULL : midori_bookmarks_open_in_tab_activate_cb),
3202 bookmarks);
3203 }
3204 else
3205@@ -1080,7 +1363,7 @@
3206 static KatzeItem*
3207 midori_bookmarks_get_item_at_pos (GtkTreeView *treeview,
3208 gint x, gint y)
3209-{
3210+{
3211 GtkTreeModel* model = gtk_tree_view_get_model (treeview);
3212 GtkTreePath* path;
3213 GtkTreeIter iter;
3214@@ -1088,13 +1371,13 @@
3215
3216 gtk_tree_view_get_path_at_pos (treeview, x, y,
3217 &path, NULL, NULL, NULL);
3218-
3219+
3220 if (!path)
3221 return NULL;
3222-
3223+
3224 if (gtk_tree_model_get_iter (model, &iter, path))
3225 gtk_tree_model_get (model, &iter, 0, &item, -1);
3226-
3227+
3228 gtk_tree_path_free (path);
3229
3230 return item;
3231@@ -1166,7 +1449,7 @@
3232
3233 if (bookmarks->hovering_item)
3234 g_object_unref (bookmarks->hovering_item);
3235-
3236+
3237 bookmarks->hovering_item = NULL;
3238
3239 g_object_set (browser, "statusbar-text", "", NULL);
3240@@ -1229,7 +1512,7 @@
3241 gtk_box_pack_start (GTK_BOX (bookmarks), box, FALSE, FALSE, 5);
3242
3243 /* Create the treeview */
3244- model = gtk_tree_store_new (2, KATZE_TYPE_ITEM, G_TYPE_STRING);
3245+ model = midori_bookmarks_tree_store_new (2, KATZE_TYPE_ITEM, G_TYPE_STRING);
3246 treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
3247 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
3248 gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (treeview), 1);
3249@@ -1268,7 +1551,7 @@
3250 "signal::leave-notify-event",
3251 midori_bookmarks_leave_notify_event_cb, bookmarks,
3252 NULL);
3253- gtk_widget_add_events (GTK_WIDGET (treeview),
3254+ gtk_widget_add_events (GTK_WIDGET (treeview),
3255 GDK_POINTER_MOTION_MASK
3256 | GDK_POINTER_MOTION_HINT_MASK);
3257
3258@@ -1279,6 +1562,7 @@
3259 gtk_widget_show (treeview);
3260 gtk_box_pack_start (GTK_BOX (bookmarks), treeview, TRUE, TRUE, 0);
3261 bookmarks->treeview = treeview;
3262+ bookmarks->pending_inserts = NULL;
3263 bookmarks->hovering_item = NULL;
3264 }
3265
3266
3267=== modified file 'panels/midori-bookmarks.h'
3268--- panels/midori-bookmarks.h 2012-11-25 11:26:03 +0000
3269+++ panels/midori-bookmarks.h 2013-06-11 20:53:24 +0000
3270@@ -37,20 +37,6 @@
3271 GType
3272 midori_bookmarks_get_type (void);
3273
3274-gint64
3275-midori_bookmarks_insert_item_db (sqlite3* db,
3276- KatzeItem* item,
3277- gint64 parentid);
3278-
3279-void
3280-midori_bookmarks_import_array_db (sqlite3* db,
3281- KatzeArray* array,
3282- gint64 parentid);
3283-
3284-gboolean
3285-midori_bookmarks_update_item_db (sqlite3* db,
3286- KatzeItem* item);
3287-
3288 G_END_DECLS
3289
3290 #endif /* __MIDORI_BOOKMARKS_PANEL_H__ */

Subscribers

People subscribed via source and target branches

to all changes: