Merge lp:~aauzi/midori/bookmarks-work into lp:midori

Proposed by André Auzi
Status: Rejected
Rejected by: Danielle Foré
Proposed branch: lp:~aauzi/midori/bookmarks-work
Merge into: lp:midori
Diff against target: 3717 lines (+2658/-233)
13 files modified
katze/katze-array.c (+40/-0)
katze/katze-array.h (+4/-0)
katze/katze-item.c (+15/-7)
katze/katze-utils.c (+90/-3)
katze/katze-utils.h (+5/-0)
midori/midori-array.c (+139/-5)
midori/midori-array.h (+10/-1)
midori/midori-bookmarks.c (+156/-9)
midori/midori-bookmarks.h (+0/-10)
midori/midori-browser.c (+188/-40)
midori/midori-view.c (+1/-1)
panels/midori-bookmarks.c (+2009/-156)
tests/bookmarks.c (+1/-1)
To merge this branch: bzr merge lp:~aauzi/midori/bookmarks-work
Reviewer Review Type Date Requested Status
Danielle Foré (community) Disapprove
Review via email: mp+164815@code.launchpad.net

Description of the change

All is in revision 6164 commit message.
    Fix proposal for:
      #894143 Unable to select multiple bookmarks for moving/deleting
      #1116908 Bookmarkbar folder is not updated when bookmark is added to folder
      #1177553 Bookmark folder tree not reflected in bookmark bar and edit dialog
      #1179200 Bookmark folder tree collapsed on bookmarks update
      #1179624 Lack of visual feedback in bookmarks panel

    Implements:
      * bookmarks panel multiple selection DND delete and moves
      * katze items signals between bookmarks panel and bookmarks bar
      * correct handling of panel position on database retrieval
      * bookmarkbar folder trees
      * bookmark folder path is bookmark edit combo box
      * use of current selection for bookmark/folder creation parent folder
      * status bar updates and popup menus cosmetics
      * idle time database updates (speed feeling)

    Does not implement (maybe future work):
      * DND exchanges between bookmark bar and bookmark panel
      * DND bookmark creation

Considering the amount of changes I thought merging the different bugs myself may help :)

To post a comment you must log in.
Revision history for this message
Danielle Foré (danrabbit) wrote :

Hey André, thanks for this huge work! I have a couple of problems to report however...

It seems for some reason there is a bunch of .po files scattered around your branch :p

Also, the build fails with:

../panels/midori-bookmarks.c: In function ‘midori_bookmarks_tree_store_drag_data_get’:
../panels/midori-bookmarks.c:903:23: error: dereferencing pointer to incomplete type
../panels/midori-bookmarks.c: In function ‘midori_bookmarks_tree_store_get_rows_drag_data’:
../panels/midori-bookmarks.c:1044:23: error: dereferencing pointer to incomplete type
../panels/midori-bookmarks.c:1047:23: error: dereferencing pointer to incomplete type
../panels/midori-bookmarks.c:1050:33: error: dereferencing pointer to incomplete type
../panels/midori-bookmarks.c: In function ‘midori_bookmarks_tree_store_row_drop_possible’:
../panels/midori-bookmarks.c:1112:23: error: dereferencing pointer to incomplete type
../panels/midori-bookmarks.c: In function ‘midori_bookmarks_tree_store_drag_data_received’:
../panels/midori-bookmarks.c:1229:23: error: dereferencing pointer to incomplete type

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

> Hey André, thanks for this huge work! I have a couple of problems to report
> however...
>
> It seems for some reason there is a bunch of .po files scattered around your
> branch :p
>
> Also, the build fails with:
>
> ../panels/midori-bookmarks.c: In function
> ‘midori_bookmarks_tree_store_drag_data_get’:
> ../panels/midori-bookmarks.c:903:23: error: dereferencing pointer to
> incomplete type
> ../panels/midori-bookmarks.c: In function
> ‘midori_bookmarks_tree_store_get_rows_drag_data’:
> ../panels/midori-bookmarks.c:1044:23: error: dereferencing pointer to
> incomplete type
> ../panels/midori-bookmarks.c:1047:23: error: dereferencing pointer to
> incomplete type
> ../panels/midori-bookmarks.c:1050:33: error: dereferencing pointer to
> incomplete type
> ../panels/midori-bookmarks.c: In function
> ‘midori_bookmarks_tree_store_row_drop_possible’:
> ../panels/midori-bookmarks.c:1112:23: error: dereferencing pointer to
> incomplete type
> ../panels/midori-bookmarks.c: In function
> ‘midori_bookmarks_tree_store_drag_data_received’:
> ../panels/midori-bookmarks.c:1229:23: error: dereferencing pointer to
> incomplete type

Hi Dan, no problem.

I'm glad there are so few problems on a first shoot :D

For the .po files I'm clueless so far but if your show me directions I'll do my best.

I'm concerned by the compilation errors you have.

Apparently, the GtkSelectionData structure is not exposed in your environment.

I'm building with gtk+-2.0 version : 2.24.16 on Fedora 18, let me know what is your environment, I'll see if I can figure out what happens.

lp:~aauzi/midori/bookmarks-work updated
6164. By André Auzi

Fix proposal for:
  #894143 Unable to select multiple bookmarks for moving/deleting
  #1116908 Bookmarkbar folder is not updated when bookmark is added to folder
  #1177553 Bookmark folder tree not reflected in bookmark bar and edit dialog
  #1179200 Bookmark folder tree collapsed on bookmarks update
  #1179624 Lack of visual feedback in bookmarks panel

Implements:
  * bookmarks panel multiple selection DND delete and moves
  * katze items signals between bookmarks panel and bookmarks bar
  * correct handling of panel position on database retrieval
  * bookmarkbar folder trees
  * bookmark folder path in bookmark edit combo box
  * use of current selection for bookmark/folder creation parent folder
  * status bar updates and popup menus cosmetics
  * idle time database updates (speed feeling)

Does not implement (maybe future work):
  * DND exchanges between bookmark bar and bookmark panel
  * DND bookmark creation

Notes:
  1-recommit with use of accessors for GtkSelectionData fields

6165. By André Auzi

merge lp:~aauzi/midori/bookmarks-work

Revision history for this message
Danielle Foré (danrabbit) wrote :

Hey André,

I think for the .po files, you can probably just delete any of them that aren't in the po directory and we'll know there's something really wrong if they regenerate :p

After your latest commit, the branch does build with Gtk 3.4 :) But I'm afraid there are issues >.<

I do have a problem that I can't access any bookmarks that are in a folder. Clicking the expander triangle does nothing. Double-clicking the folder expands, but as soon as I move the cursor it starts a drag operation.

Dragging any bookmarks in the sidepanel forces the Bookmarks Bar to open.

I think for the statusbar feature, it should probably only show on hover as is the behavior with links in the web view (but it is a cool feature!)

review: Needs Fixing
Revision history for this message
Danielle Foré (danrabbit) wrote :

Hey André,

it looks like the .po file thing is actually our fault. Something went weird with LP translations.

But also I want to say if possible it would be great to split this up into separate feature branches that we could merge individually. This is quite a large diff and is very difficult to review. It would probably be a lot safer to merge one feature at a time.

If one branch relies on another, that's okay! LP has a feature where you can mark one branch as being a pre-requisite of another so we know what order to merge them in.

Thanks again for your hard work!

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

Hi Dan,

> I think for the .po files, you can probably just delete any of them that
> aren't in the po directory and we'll know there's something really wrong if
> they regenerate :p

it's OK on my side, no more .po after deletion and clean build.

> After your latest commit, the branch does build with Gtk 3.4 :) But I'm afraid
> there are issues >.<

Great news! and issues were expected, it's all the point of testing and reviews, isn't it?

> I do have a problem that I can't access any bookmarks that are in a folder.
> Clicking the expander triangle does nothing. Double-clicking the folder
> expands, but as soon as I move the cursor it starts a drag operation.

OK, I believe the ugly trick used to keep the multi selection before dragging does not integrate well with Gtk 3. I'll work on that.

> Dragging any bookmarks in the sidepanel forces the Bookmarks Bar to open.

Oups! Now I have to see how we hide the bookmark bar. I'll work on that too.

> I think for the statusbar feature, it should probably only show on hover as is
> the behavior with links in the web view (but it is a cool feature!)

Sounds good, I will have a look on the web view and see how I can reproduce the behaviour.

Thanks for the valuable feedback.

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

Hi Dan,

> But also I want to say if possible it would be great to split this up into
> separate feature branches that we could merge individually. This is quite a
> large diff and is very difficult to review. It would probably be a lot safer
> to merge one feature at a time.

No problem, I have the increments ready, it's just a matter of configuration management ;)

> Thanks again for your hard work!

you're welcome. Thank you for midori, it's cool to hack in.

Now it's time to go to the office :D

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

OK, it's time for a follow up.

So far I've reworked the proposal in three independent branches (in increasing order of complexity):
1. lp:~aauzi/midori/fix-1177553: Bookmark folder tree not reflected in bookmark bar and edit dialog
2. lp:~aauzi/midori/fix-1179624: Lack of visual feedback in bookmarks panel
3. lp:~aauzi/midori/fix-1179200: Bookmark folder tree collapsed on bookmarks update

The reported issues/remarks taken into account are:
> Dragging any bookmarks in the sidepanel forces the Bookmarks Bar to open
=> fixed in lp:~aauzi/midori/fix-1179200

> I think for the statusbar feature, it should probably only show on hover as is
> the behavior with links in the web view (but it is a cool feature!)
=> reworked into lp:~aauzi/midori/fix-1179624

There's still an upcoming one:
> I do have a problem that I can't access any bookmarks that are in a folder.
> Clicking the expander triangle does nothing. Double-clicking the folder
> expands, but as soon as I move the cursor it starts a drag operation.
=> it's work in progress
   it'll show up in a branch related to bug
     #894143 Unable to select multiple bookmarks for moving/deleting, lp:~aauzi/midori/fix-894143
   the merge of this branch will have to come after the merge of branches:
     lp:~aauzi/midori/fix-1179200
     lp:~aauzi/midori/fix-1179624
   the remaining activities are:
   * rework hovering to take multi selection into account (~1day)
   * integrate with Gtk+3 to investigate the misbehaviour (hopefully no more than ~1day)
   I will propose it for merge when it's ready.

I therefore propose to close this review and switch to the reviews of the fore mentioned branches.

Revision history for this message
Danielle Foré (danrabbit) wrote :

Great! Thanks André! I'll go ahead and close this review then and we can get started on merging those smaller branches :)

review: Disapprove

Unmerged revisions

6165. By André Auzi

merge lp:~aauzi/midori/bookmarks-work

6164. By André Auzi

Fix proposal for:
  #894143 Unable to select multiple bookmarks for moving/deleting
  #1116908 Bookmarkbar folder is not updated when bookmark is added to folder
  #1177553 Bookmark folder tree not reflected in bookmark bar and edit dialog
  #1179200 Bookmark folder tree collapsed on bookmarks update
  #1179624 Lack of visual feedback in bookmarks panel

Implements:
  * bookmarks panel multiple selection DND delete and moves
  * katze items signals between bookmarks panel and bookmarks bar
  * correct handling of panel position on database retrieval
  * bookmarkbar folder trees
  * bookmark folder path in bookmark edit combo box
  * use of current selection for bookmark/folder creation parent folder
  * status bar updates and popup menus cosmetics
  * idle time database updates (speed feeling)

Does not implement (maybe future work):
  * DND exchanges between bookmark bar and bookmark panel
  * DND bookmark creation

Notes:
  1-recommit with use of accessors for GtkSelectionData fields

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-05-21 05:35:32 +0000
4@@ -42,6 +42,9 @@
5 (*add_item) (KatzeArray* array,
6 gpointer item);
7 void
8+ (*update_item) (KatzeArray* array,
9+ gpointer item);
10+ void
11 (*remove_item) (KatzeArray* array,
12 gpointer item);
13 void
14@@ -59,6 +62,7 @@
15
16 enum {
17 ADD_ITEM,
18+ UPDATE_ITEM,
19 REMOVE_ITEM,
20 MOVE_ITEM,
21 CLEAR,
22@@ -95,6 +99,13 @@
23 }
24
25 static void
26+_katze_array_update_item (KatzeArray* array,
27+ gpointer item)
28+{
29+ _katze_array_update (array);
30+}
31+
32+static void
33 _katze_array_remove_item (KatzeArray* array,
34 gpointer item)
35 {
36@@ -147,6 +158,17 @@
37 G_TYPE_NONE, 1,
38 G_TYPE_POINTER);
39
40+ signals[UPDATE_ITEM] = g_signal_new (
41+ "update-item",
42+ G_TYPE_FROM_CLASS (class),
43+ (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
44+ G_STRUCT_OFFSET (KatzeArrayClass, add_item),
45+ 0,
46+ NULL,
47+ g_cclosure_marshal_VOID__POINTER,
48+ G_TYPE_NONE, 1,
49+ G_TYPE_POINTER);
50+
51 signals[REMOVE_ITEM] = g_signal_new (
52 "remove-item",
53 G_TYPE_FROM_CLASS (class),
54@@ -213,6 +235,7 @@
55 gobject_class->finalize = katze_array_finalize;
56
57 class->add_item = _katze_array_add_item;
58+ class->update_item = _katze_array_update_item;
59 class->remove_item = _katze_array_remove_item;
60 class->move_item = _katze_array_move_item;
61 class->clear = _katze_array_clear;
62@@ -301,6 +324,23 @@
63 }
64
65 /**
66+ * katze_array_update_item:
67+ * @array: a #KatzeArray
68+ * @item: an item
69+ *
70+ * Notify an update of the item of the array.
71+ *
72+ **/
73+void
74+katze_array_update_item (KatzeArray* array,
75+ gpointer item)
76+{
77+ g_return_if_fail (KATZE_IS_ARRAY (array));
78+
79+ g_signal_emit (array, signals[UPDATE_ITEM], 0, item);
80+}
81+
82+/**
83 * katze_array_remove_item:
84 * @array: a #KatzeArray
85 * @item: an item
86
87=== modified file 'katze/katze-array.h'
88--- katze/katze-array.h 2011-01-19 20:58:26 +0000
89+++ katze/katze-array.h 2013-05-21 05:35:32 +0000
90@@ -47,6 +47,10 @@
91 gpointer item);
92
93 void
94+katze_array_update_item (KatzeArray* array,
95+ gpointer item);
96+
97+void
98 katze_array_remove_item (KatzeArray* array,
99 gpointer item);
100
101
102=== modified file 'katze/katze-item.c'
103--- katze/katze-item.c 2013-02-21 21:36:30 +0000
104+++ katze/katze-item.c 2013-05-21 05:35:32 +0000
105@@ -314,11 +314,12 @@
106 katze_item_set_name (KatzeItem* item,
107 const gchar* name)
108 {
109+ gchar* pname = item->name;
110 g_return_if_fail (KATZE_IS_ITEM (item));
111
112 katze_assign (item->name, g_strdup (name));
113- if (item->parent)
114- katze_array_update ((KatzeArray*)item->parent);
115+ if (item->parent && g_strcmp0(name, pname))
116+ katze_array_update((KatzeArray*)item->parent);
117 g_object_notify (G_OBJECT (item), "name");
118 }
119
120@@ -418,11 +419,12 @@
121 katze_item_set_icon (KatzeItem* item,
122 const gchar* icon)
123 {
124+ gchar *picon = katze_item_get_meta_string (item, "icon");
125 g_return_if_fail (KATZE_IS_ITEM (item));
126
127 katze_item_set_meta_string (item, "icon", icon);
128- if (item->parent)
129- katze_array_update ((KatzeArray*)item->parent);
130+ if (item->parent && g_strcmp0(icon, picon))
131+ katze_array_update_item ((KatzeArray*)item->parent, item);
132 g_object_notify (G_OBJECT (item), "icon");
133 }
134
135@@ -445,8 +447,13 @@
136
137 g_return_val_if_fail (KATZE_IS_ITEM (item), NULL);
138
139- if (widget && KATZE_ITEM_IS_FOLDER (item))
140- return gtk_widget_render_icon (widget, GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU, NULL);
141+ if (widget)
142+ {
143+ if (KATZE_ITEM_IS_FOLDER (item))
144+ return gtk_widget_render_icon (widget, GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU, NULL);
145+ if (KATZE_ITEM_IS_BOOKMARK (item) && katze_item_get_meta_boolean (item, "app"))
146+ return gtk_widget_render_icon (widget, GTK_STOCK_EXECUTE, GTK_ICON_SIZE_MENU, NULL);
147+ }
148 if ((pixbuf = midori_paths_get_icon (katze_item_get_icon (item), NULL)))
149 return pixbuf;
150 if ((pixbuf = midori_paths_get_icon (item->uri, widget)))
151@@ -526,7 +533,8 @@
152 gtk_widget_show (image);
153 if (pixbuf != NULL)
154 g_object_unref (pixbuf);
155- if (KATZE_ITEM_IS_FOLDER (item))
156+ if (KATZE_ITEM_IS_FOLDER (item)
157+ || (KATZE_ITEM_IS_BOOKMARK (item) && katze_item_get_meta_boolean (item, "app")))
158 return image;
159 g_object_set_data (G_OBJECT (image), "KatzeItem", g_object_ref (item));
160 g_signal_connect (image, "destroy",
161
162=== modified file 'katze/katze-utils.c'
163--- katze/katze-utils.c 2013-04-16 23:16:24 +0000
164+++ katze/katze-utils.c 2013-05-21 05:35:32 +0000
165@@ -984,12 +984,99 @@
166
167 g_return_val_if_fail (GTK_IS_TREE_VIEW (treeview), FALSE);
168
169- if ((selection = gtk_tree_view_get_selection (treeview)))
170- if (gtk_tree_selection_get_selected (selection, model, iter))
171- return TRUE;
172+ selection = gtk_tree_view_get_selection(treeview);
173+
174+ switch (gtk_tree_selection_get_mode (selection))
175+ {
176+ default:
177+ break;
178+
179+ case GTK_SELECTION_SINGLE:
180+ case GTK_SELECTION_BROWSE:
181+ if (gtk_tree_selection_get_selected (selection, model, iter))
182+ return TRUE;
183+ break;
184+
185+ case GTK_SELECTION_MULTIPLE:
186+ if (gtk_tree_selection_count_selected_rows (selection) == 1)
187+ {
188+ GtkTreeModel *stock_model;
189+ GList *list = gtk_tree_selection_get_selected_rows (selection, &stock_model);
190+
191+ if (model)
192+ *model = stock_model;
193+
194+ if (list)
195+ {
196+ GtkTreePath *path = (GtkTreePath *)g_list_nth_data (list, 0);
197+
198+ if (path && (!iter || gtk_tree_model_get_iter (stock_model, iter, path)))
199+ {
200+ g_list_free_full(list, (GDestroyNotify) gtk_tree_path_free);
201+ return TRUE;
202+ }
203+
204+ g_list_free_full(list, (GDestroyNotify) gtk_tree_path_free);
205+ }
206+ }
207+ }
208+
209 return FALSE;
210 }
211
212+/**
213+ * katze_tree_view_get_selected_rows:
214+ * @treeview: a #GtkTreeView
215+ * @model: a pointer to store the #GtkTreeModel, or %NULL
216+ * @rows: a pointer to store the #GList of #GtkTreePath, or %NULL
217+ *
218+ * Determines whether there is a selection in @treeview
219+ * and sets the @rows to the current selection.
220+ *
221+ * If there is a selection and @model is not %NULL, it is
222+ * set to the model. If @model is %NULL, the @rows will be
223+ * set to %NULL.
224+ *
225+ * Either @model or @rows or both can be %NULL in which case
226+ * no value will be assigned in any case.
227+ *
228+ * When @rows is not %NULL it must be freed using:
229+ * g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
230+ *
231+ * Return value: the count of selected rows
232+ *
233+ * Since: 0.4.7.aau.1
234+ **/
235+
236+gint
237+katze_tree_view_get_selected_rows (GtkTreeView* treeview,
238+ GtkTreeModel** model,
239+ GList** rows)
240+{
241+ gint count;
242+ GtkTreeSelection* selection;
243+
244+ if (model)
245+ *model = NULL;
246+ if (rows)
247+ *rows = NULL;
248+
249+ g_return_val_if_fail (GTK_IS_TREE_VIEW (treeview), 0);
250+
251+ selection = gtk_tree_view_get_selection(treeview);
252+
253+ count = gtk_tree_selection_count_selected_rows (selection);
254+ if (count > 0)
255+ {
256+ if (model && rows)
257+ {
258+ *rows = gtk_tree_selection_get_selected_rows (selection, model);
259+ }
260+ }
261+
262+ return count;
263+}
264+
265 void
266 katze_bookmark_populate_tree_view (KatzeArray* array,
267 GtkTreeStore* model,
268
269=== modified file 'katze/katze-utils.h'
270--- katze/katze-utils.h 2013-04-16 22:10:56 +0000
271+++ katze/katze-utils.h 2013-05-21 05:35:32 +0000
272@@ -88,6 +88,11 @@
273 GtkTreeModel** model,
274 GtkTreeIter* iter);
275
276+gint
277+katze_tree_view_get_selected_rows (GtkTreeView* treeview,
278+ GtkTreeModel** model,
279+ GList** rows);
280+
281 void
282 katze_bookmark_populate_tree_view (KatzeArray* array,
283 GtkTreeStore* model,
284
285=== modified file 'midori/midori-array.c'
286--- midori/midori-array.c 2013-04-16 23:16:24 +0000
287+++ midori/midori-array.c 2013-05-21 05:35:32 +0000
288@@ -1109,6 +1109,12 @@
289 * @fields: comma separated list of fields
290 * @condition: condition, like "folder = '%q'"
291 * @value: a value to be inserted if @condition contains %q
292+ * @order: a value to be inserted in "ORDER BY"
293+ * default order is :
294+ * (uri='') ASC, title DESC
295+ * @order is inserted before title.
296+ * order then becomes :
297+ * (uri='') ASC, @order title DESC
298 * @recursive: if %TRUE include children
299 *
300 * Stores the result in a #KatzeArray.
301@@ -1122,6 +1128,7 @@
302 const gchar* fields,
303 const gchar* condition,
304 const gchar* value,
305+ const gchar* order,
306 gboolean recursive)
307 {
308 sqlite3* db;
309@@ -1138,7 +1145,8 @@
310 g_return_val_if_fail (db != NULL, NULL);
311
312 sqlcmd = g_strdup_printf ("SELECT %s FROM bookmarks WHERE %s "
313- "ORDER BY (uri='') ASC, title DESC", fields, condition);
314+ "ORDER BY (uri='') ASC, %s title DESC",
315+ fields, condition, order ? order : "");
316 if (strstr (condition, "%q"))
317 {
318 sqlcmd_value = sqlite3_mprintf (sqlcmd, value ? value : "");
319@@ -1159,7 +1167,7 @@
320 gchar* parentid = g_strdup_printf ("%" G_GINT64_FORMAT,
321 katze_item_get_meta_integer (item, "id"));
322 KatzeArray* subarray = midori_array_query_recursive (bookmarks,
323- fields, "parentid=%q", parentid, TRUE);
324+ fields, "parentid=%q", parentid, order, TRUE);
325 katze_item_set_name (KATZE_ITEM (subarray), katze_item_get_name (item));
326 katze_array_add_item (array, subarray);
327
328@@ -1176,6 +1184,12 @@
329 * @fields: comma separated list of fields
330 * @condition: condition, like "folder = '%q'"
331 * @value: a value to be inserted if @condition contains %q
332+ * @order: a value to be inserted in "ORDER BY"
333+ * default order is :
334+ * (uri='') ASC, title DESC
335+ * @order is inserted before title.
336+ * order then becomes :
337+ * (uri='') ASC, @order title DESC
338 *
339 * Stores the result in a #KatzeArray.
340 *
341@@ -1189,8 +1203,128 @@
342 midori_array_query (KatzeArray* bookmarks,
343 const gchar* fields,
344 const gchar* condition,
345- const gchar* value)
346-{
347- return midori_array_query_recursive (bookmarks, fields, condition, value, FALSE);
348+ const gchar* value,
349+ const gchar* order)
350+{
351+ return midori_array_query_recursive (bookmarks, fields, condition, value, order, FALSE);
352+}
353+
354+static gint64
355+count_from_sqlite (sqlite3* db,
356+ const gchar* sqlcmd)
357+{
358+ gint64 count = -1;
359+ sqlite3_stmt* stmt;
360+ gint result;
361+
362+ result = sqlite3_prepare_v2 (db, sqlcmd, -1, &stmt, NULL);
363+ if (result != SQLITE_OK)
364+ return -1;
365+
366+ g_assert (sqlite3_column_count (stmt) == 1);
367+
368+ if ((result = sqlite3_step (stmt)) == SQLITE_ROW)
369+ count = sqlite3_column_int64(stmt, 0);
370+
371+ sqlite3_clear_bindings (stmt);
372+ sqlite3_reset (stmt);
373+
374+ return count;
375+}
376+
377+/**
378+ * midori_array_count_recursive:
379+ * @array: the main bookmark array
380+ * @condition: condition, like "folder = '%q'"
381+ * @value: a value to be inserted if @condition contains %q
382+ * @recursive: if %TRUE include children
383+ *
384+ * Return value: the number of elements on success, -1 otherwise
385+ *
386+ * Since: 0.5.1
387+ **/
388+gint64
389+midori_array_count_recursive (KatzeArray* bookmarks,
390+ const gchar* condition,
391+ const gchar* value,
392+ KatzeItem* folder,
393+ gboolean recursive)
394+{
395+ gint64 count = -1;
396+ gint64 id = -1;
397+ sqlite3* db;
398+ gchar* sqlcmd;
399+ char* sqlcmd_value;
400+ KatzeArray* array;
401+ KatzeItem* item;
402+ GList* list;
403+ gchar* id_str;
404+
405+ g_return_val_if_fail (KATZE_IS_ARRAY (bookmarks), -1);
406+ g_return_val_if_fail (!folder || KATZE_ITEM_IS_FOLDER (folder), -1);
407+ g_return_val_if_fail (condition, -1);
408+ db = g_object_get_data (G_OBJECT (bookmarks), "db");
409+ g_return_val_if_fail (db != NULL, -1);
410+
411+ g_assert(!strstr("parentid", condition));
412+
413+ id = folder ? katze_item_get_meta_integer (folder, "id") : 0;
414+
415+ if (id > 0)
416+ sqlcmd = g_strdup_printf ("SELECT COUNT(*) FROM bookmarks "
417+ "WHERE parentid = %" G_GINT64_FORMAT " AND %s",
418+ id,
419+ condition);
420+ else
421+ sqlcmd = g_strdup_printf ("SELECT COUNT(*) FROM bookmarks "
422+ "WHERE parentid IS NULL AND %s ",
423+ condition);
424+
425+ if (strstr (condition, "%q"))
426+ {
427+ sqlcmd_value = sqlite3_mprintf (sqlcmd, value ? value : "");
428+ count = count_from_sqlite (db, sqlcmd_value);
429+ sqlite3_free (sqlcmd_value);
430+ }
431+ else
432+ count = count_from_sqlite (db, sqlcmd);
433+
434+ g_free (sqlcmd);
435+
436+ if (!recursive || (count < 0))
437+ return count;
438+
439+ if (id > 0)
440+ sqlcmd = g_strdup_printf ("parentid = %" G_GINT64_FORMAT " AND uri = ''",
441+ id);
442+ else
443+ sqlcmd = g_strdup ("parentid IS NULL AND uri = '' ");
444+
445+ array = midori_array_query_recursive (bookmarks, "id, uri", sqlcmd, NULL, NULL, FALSE);
446+
447+ g_free (sqlcmd);
448+
449+ KATZE_ARRAY_FOREACH_ITEM_L (item, array, list)
450+ {
451+ if (KATZE_ITEM_IS_FOLDER(item))
452+ {
453+ gint64 sub_count = midori_array_count_recursive (bookmarks,
454+ condition,
455+ value,
456+ item,
457+ recursive);
458+
459+ if (sub_count < 0)
460+ {
461+ g_object_unref (array);
462+ return -1;
463+ }
464+
465+ count += sub_count;
466+ }
467+ }
468+
469+ g_object_unref (array);
470+ return count;
471 }
472
473
474=== modified file 'midori/midori-array.h'
475--- midori/midori-array.h 2012-01-03 20:15:19 +0000
476+++ midori/midori-array.h 2013-05-21 05:35:32 +0000
477@@ -34,17 +34,26 @@
478 midori_array_query (KatzeArray* array,
479 const gchar* fields,
480 const gchar* condition,
481- const gchar* value);
482+ const gchar* value,
483+ const gchar* order);
484
485 KatzeArray*
486 midori_array_query_recursive (KatzeArray* array,
487 const gchar* fields,
488 const gchar* condition,
489 const gchar* value,
490+ const gchar* order,
491 gboolean recursive);
492
493 KatzeArray*
494 katze_array_from_sqlite (sqlite3* db,
495 const gchar* sqlcmd);
496
497+gint64
498+midori_array_count_recursive (KatzeArray* bookmarks,
499+ const gchar* condition,
500+ const gchar* value,
501+ KatzeItem* folder,
502+ gboolean recursive);
503+
504 #endif /* !__MIDORI_ARRAY_H__ */
505
506=== modified file 'midori/midori-bookmarks.c'
507--- midori/midori-bookmarks.c 2012-11-25 15:37:41 +0000
508+++ midori/midori-bookmarks.c 2013-05-21 05:35:32 +0000
509@@ -25,6 +25,19 @@
510 #include <unistd.h>
511 #endif
512
513+#define MIDORI_BOOKMARKS_DB_CONTROL(a) ((MidoriBookmarksDBControl*)(a))
514+typedef struct _MidoriBookmarksDBControl MidoriBookmarksDBControl;
515+struct _MidoriBookmarksDBControl
516+{
517+ sqlite3* db;
518+ GHashTable* pending_updates;
519+};
520+
521+static void
522+midori_bookmarks_recursive_remove_childs_db (KatzeArray* array,
523+ KatzeItem* item,
524+ sqlite3* db);
525+
526 void
527 midori_bookmarks_dbtracer (void* dummy,
528 const char* query)
529@@ -32,31 +45,155 @@
530 g_printerr ("%s\n", query);
531 }
532
533-void
534+static guint
535+item_hash (gconstpointer item)
536+{
537+ gint64 id = katze_item_get_meta_integer (KATZE_ITEM (item), "id");
538+ return g_int64_hash (&id);
539+}
540+
541+static gboolean
542+item_equal (gconstpointer item_a, gconstpointer item_b)
543+{
544+ gint64 id_a = katze_item_get_meta_integer (KATZE_ITEM (item_a), "id");
545+ gint64 id_b = katze_item_get_meta_integer (KATZE_ITEM (item_b), "id");
546+ return (id_a == id_b)? TRUE : FALSE;
547+}
548+
549+static gboolean
550+midori_bookmarks_idle (gpointer data)
551+{
552+ MidoriBookmarksDBControl* control = MIDORI_BOOKMARKS_DB_CONTROL (data);
553+ GHashTableIter hash_iter;
554+ gpointer key, value;
555+
556+ g_hash_table_iter_init (&hash_iter, control->pending_updates);
557+
558+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
559+ {
560+ KatzeItem *item = KATZE_ITEM(key);
561+
562+ midori_bookmarks_update_item_db (control->db, item);
563+ g_object_unref (item);
564+ }
565+
566+ g_hash_table_remove_all (control->pending_updates);
567+
568+ return FALSE;
569+}
570+
571+static void
572 midori_bookmarks_add_item_cb (KatzeArray* array,
573 KatzeItem* item,
574- sqlite3* db)
575+ MidoriBookmarksDBControl* control)
576 {
577- midori_bookmarks_insert_item_db (db, item,
578+ midori_bookmarks_insert_item_db (control->db, item,
579 katze_item_get_meta_integer (item, "parentid"));
580 }
581
582-void
583+static void
584+midori_bookmarks_update_item_cb (KatzeArray* array,
585+ KatzeItem* item,
586+ MidoriBookmarksDBControl* control)
587+{
588+ if (!g_hash_table_size (control->pending_updates))
589+ g_idle_add (midori_bookmarks_idle, control);
590+ else if (g_hash_table_lookup (control->pending_updates, item))
591+ return;
592+
593+ g_object_ref (item);
594+ g_hash_table_insert (control->pending_updates, item, item);
595+}
596+
597+static void
598 midori_bookmarks_remove_item_cb (KatzeArray* array,
599 KatzeItem* item,
600- sqlite3* db)
601+ MidoriBookmarksDBControl* control)
602 {
603 gchar* sqlcmd;
604 char* errmsg = NULL;
605-
606+ gpointer found;
607+
608+ if (KATZE_ITEM_IS_FOLDER (item))
609+ {
610+ gint64 id = katze_item_get_meta_integer (item, "id");
611+ GHashTableIter iter;
612+ gpointer key, value;
613+
614+ g_hash_table_iter_init (&iter, control->pending_updates);
615+
616+ while (g_hash_table_iter_next (&iter, &key, &value))
617+ {
618+ KatzeItem *hash_item = KATZE_ITEM(key);
619+
620+ gint64 parentid = katze_item_get_meta_integer (hash_item, "parentid");
621+ if (parentid == id)
622+ {
623+ g_hash_table_iter_remove (&iter);
624+ g_object_unref (hash_item);
625+ }
626+ }
627+
628+ midori_bookmarks_recursive_remove_childs_db (array, item, control->db);
629+ }
630+
631+ if (found = g_hash_table_lookup (control->pending_updates, item))
632+ {
633+ g_hash_table_remove (control->pending_updates, found);
634+ g_object_unref (found);
635+ }
636
637 sqlcmd = sqlite3_mprintf (
638 "DELETE FROM bookmarks WHERE id = %" G_GINT64_FORMAT ";",
639 katze_item_get_meta_integer (item, "id"));
640
641+ if (sqlite3_exec (control->db, sqlcmd, NULL, NULL, &errmsg) != SQLITE_OK)
642+ {
643+ g_printerr (_("Failed to remove bookmark item: %s\n"), errmsg);
644+ sqlite3_free (errmsg);
645+ }
646+
647+ sqlite3_free (sqlcmd);
648+}
649+
650+static void
651+midori_bookmarks_recursive_remove_childs_db (KatzeArray* array,
652+ KatzeItem* item,
653+ sqlite3* db)
654+{
655+ gint64 parentid = katze_item_get_meta_integer (item, "id");
656+ char* errmsg = NULL;
657+ KatzeArray* childs_array;
658+ gchar* sqlcmd;
659+
660+ sqlcmd = g_strdup_printf ("SELECT id FROM bookmarks"
661+ "WHERE uri = '' AND parentid = %"G_GINT64_FORMAT ";",
662+ parentid);
663+
664+ childs_array = katze_array_from_sqlite (db, sqlcmd);
665+ g_free (sqlcmd);
666+
667+ if (KATZE_IS_ARRAY (childs_array))
668+ {
669+ KatzeItem* child;
670+ GList* list;
671+
672+ KATZE_ARRAY_FOREACH_ITEM_L (child, childs_array, list)
673+ {
674+ katze_array_remove_item (array, child);
675+ }
676+
677+ g_list_free (list);
678+ g_object_unref(array);
679+ }
680+
681+ sqlcmd = sqlite3_mprintf (
682+ "DELETE FROM bookmarks WHERE parentid = %" G_GINT64_FORMAT ";",
683+ parentid);
684+
685 if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) != SQLITE_OK)
686 {
687- g_printerr (_("Failed to remove history item: %s\n"), errmsg);
688+ g_printerr (_("Failed to remove bookmarks folder child items: %s\n"), errmsg);
689 sqlite3_free (errmsg);
690 }
691
692@@ -141,6 +278,7 @@
693 gchar* sql_errmsg = NULL;
694 gchar* import_errmsg = NULL;
695 KatzeArray* array;
696+ MidoriBookmarksDBControl* control;
697
698 g_return_val_if_fail (errmsg != NULL, NULL);
699
700@@ -279,12 +417,18 @@
701 init_success:
702 g_free (newfile);
703 g_free (oldfile);
704+ control = MIDORI_BOOKMARKS_DB_CONTROL (g_malloc (sizeof (*control)));
705+ control->db = db;
706+ control->pending_updates = g_hash_table_new (item_hash, item_equal);
707 array = katze_array_new (KATZE_TYPE_ARRAY);
708 g_signal_connect (array, "add-item",
709- G_CALLBACK (midori_bookmarks_add_item_cb), db);
710+ G_CALLBACK (midori_bookmarks_add_item_cb), control);
711+ g_signal_connect (array, "update-item",
712+ G_CALLBACK (midori_bookmarks_update_item_cb), control);
713 g_signal_connect (array, "remove-item",
714- G_CALLBACK (midori_bookmarks_remove_item_cb), db);
715+ G_CALLBACK (midori_bookmarks_remove_item_cb), control);
716 g_object_set_data (G_OBJECT (array), "db", db);
717+ g_object_set_data (G_OBJECT (array), "control", control);
718 return array;
719
720 init_failed:
721@@ -323,5 +467,8 @@
722 sqlite3* db = g_object_get_data (G_OBJECT (array), "db");
723 g_return_if_fail (db != NULL);
724 sqlite3_close (db);
725+ MidoriBookmarksDBControl* control = g_object_get_data (G_OBJECT (array), "control");
726+ g_return_if_fail (control != NULL);
727+ g_hash_table_unref (control->pending_updates);
728 }
729
730
731=== modified file 'midori/midori-bookmarks.h'
732--- midori/midori-bookmarks.h 2012-11-25 15:37:41 +0000
733+++ midori/midori-bookmarks.h 2013-05-21 05:35:32 +0000
734@@ -16,16 +16,6 @@
735 #include <sqlite3.h>
736 #include <katze/katze.h>
737
738-void
739-midori_bookmarks_add_item_cb (KatzeArray* array,
740- KatzeItem* item,
741- sqlite3* db);
742-
743-void
744-midori_bookmarks_remove_item_cb (KatzeArray* array,
745- KatzeItem* item,
746- sqlite3* db);
747-
748 KatzeArray*
749 midori_bookmarks_new (char** errmsg);
750
751
752=== modified file 'midori/midori-browser.c'
753--- midori/midori-browser.c 2013-05-19 09:33:02 +0000
754+++ midori/midori-browser.c 2013-05-21 05:35:32 +0000
755@@ -96,6 +96,8 @@
756 gboolean show_statusbar;
757 guint maximum_history_age;
758 guint last_web_search;
759+
760+ gboolean bookmarkbar_populate;
761 };
762
763 G_DEFINE_TYPE (MidoriBrowser, midori_browser, GTK_TYPE_WINDOW)
764@@ -166,16 +168,14 @@
765 KatzeArray* array,
766 gint64 parentid);
767
768-gboolean
769-midori_bookmarks_update_item_db (sqlite3* db,
770- KatzeItem* item);
771-
772 void
773 midori_browser_open_bookmark (MidoriBrowser* browser,
774 KatzeItem* item);
775
776 static void
777 midori_bookmarkbar_populate (MidoriBrowser* browser);
778+static void
779+midori_bookmarkbar_populate_idle (MidoriBrowser* browser);
780
781 static void
782 midori_bookmarkbar_clear (GtkWidget* toolbar);
783@@ -484,6 +484,9 @@
784 inter = ZEITGEIST_ZG_DELETE_EVENT;
785 else
786 g_assert_not_reached ();
787+ g_assert (KATZE_IS_ITEM (item));
788+ if (KATZE_ITEM_IS_FOLDER (item))
789+ return;
790 zeitgeist_log_insert_events_no_reply (zeitgeist_log_get_default (),
791 zeitgeist_event_new_full (inter, ZEITGEIST_ZG_USER_ACTIVITY,
792 "application://midori.desktop",
793@@ -818,9 +821,8 @@
794
795 static GtkWidget*
796 midori_bookmark_folder_button_new (KatzeArray* array,
797- gboolean new_bookmark,
798 gint64 selected,
799- gint64 parentid)
800+ gint64 new_parentid)
801 {
802 GtkListStore* model;
803 GtkWidget* combo;
804@@ -829,8 +831,16 @@
805 sqlite3* db;
806 sqlite3_stmt* statement;
807 gint result;
808- const gchar* sqlcmd = "SELECT title, id FROM bookmarks WHERE uri='' ORDER BY title ASC";
809+ const gchar* sqlcmd = "SELECT title, id, parentid FROM bookmarks WHERE uri='' ORDER BY parentid, title ASC";
810+ guint selection;
811+ gchar *parent_path;
812+ gint64 parent_id;
813
814+ /* folder combo box content:
815+ ** 0: path
816+ ** 1: path ellipsize mode
817+ ** 2: id
818+ */
819 model = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT64);
820 combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (model));
821 renderer = gtk_cell_renderer_text_new ();
822@@ -838,31 +848,102 @@
823 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), renderer, "text", 0);
824 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), renderer, "ellipsize", 1);
825 gtk_list_store_insert_with_values (model, NULL, G_MAXINT,
826- 0, _("Bookmarks"), 1, PANGO_ELLIPSIZE_END, 2, (gint64)0, -1);
827+ 0, _("Bookmarks"), 1, PANGO_ELLIPSIZE_MIDDLE, 2, (gint64)0, -1);
828 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
829
830 db = g_object_get_data (G_OBJECT (array), "db");
831 g_return_val_if_fail (db != NULL, NULL);
832+ selection = 0;
833+ parent_id = -1;
834+ parent_path = NULL;
835 n = 1;
836 if ((result = sqlite3_prepare_v2 (db, sqlcmd, -1, &statement, NULL)) == SQLITE_OK)
837 while ((result = sqlite3_step (statement)) == SQLITE_ROW)
838 {
839- const unsigned char* name = sqlite3_column_text (statement, 0);
840+ const unsigned char* title = sqlite3_column_text (statement, 0);
841 gint64 id = sqlite3_column_int64 (statement, 1);
842+ gint64 parentid = sqlite3_column_int64 (statement, 2);
843
844 /* do not show the folder itself */
845 if (id != selected)
846- {
847- gtk_list_store_insert_with_values (model, NULL, G_MAXINT,
848- 0, name, 1, PANGO_ELLIPSIZE_END, 2, id, -1);
849+ {
850+ if (parentid != parent_id)
851+ {
852+ if (parent_path)
853+ {
854+ g_free(parent_path);
855+ parent_path = NULL;
856+ parent_id = -1;
857+ }
858+
859+ if (!parentid)
860+ {
861+ parent_id = 0;
862+ parent_path = g_strdup(_("Bookmarks"));
863+ }
864+ else
865+ {
866+ GtkTreeIter iter;
867+ gboolean valid;
868+
869+ valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
870+
871+ while (valid)
872+ {
873+ gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
874+ 2, &parent_id, -1);
875+
876+ if (parentid == parent_id)
877+ {
878+ gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
879+ 0, &parent_path, -1);
880+ break;
881+ }
882+
883+ valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter);
884+ }
885+ }
886+ }
887+
888+ /* If something went wrong, fallback to title presentation */
889+ if (parent_path)
890+ {
891+ gchar *path = g_strdup_printf ("%s/%s", parent_path, title);
892+
893+ gtk_list_store_insert_with_values (model, NULL, G_MAXINT,
894+ 0, path ? path : (gchar*)title,
895+ 1, PANGO_ELLIPSIZE_MIDDLE,
896+ 2, id, -1);
897+
898+ if (path)
899+ g_free (path);
900+ }
901+ else
902+ {
903+ gtk_list_store_insert_with_values (model, NULL, G_MAXINT,
904+ 0, title,
905+ 1, PANGO_ELLIPSIZE_MIDDLE,
906+ 2, id, -1);
907+ parent_id = -1;
908+ }
909+
910+ if (id == new_parentid)
911+ selection = n;
912
913- if (!new_bookmark && id == parentid)
914- gtk_combo_box_set_active (GTK_COMBO_BOX (combo), n);
915 n++;
916 }
917 }
918+
919+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo), selection);
920+
921 if (n < 2)
922 gtk_widget_set_sensitive (combo, FALSE);
923+
924+ if (parent_path)
925+ g_free (parent_path);
926+
927+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE (model), 0, GTK_SORT_ASCENDING);
928+
929 return combo;
930 }
931
932@@ -876,14 +957,8 @@
933
934 if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
935 {
936- gchar* selected = NULL;
937 GtkTreeModel* model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
938- gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 0, &selected, 2, &id, -1);
939-
940- if (g_str_equal (selected, _("Bookmarks")))
941- id = 0;
942-
943- g_free (selected);
944+ gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 2, &id, -1);
945 }
946
947 return id;
948@@ -910,11 +985,12 @@
949 /* Private function, used by MidoriBookmarks and MidoriHistory */
950 /* static */ gboolean
951 midori_browser_edit_bookmark_dialog_new (MidoriBrowser* browser,
952- KatzeItem* bookmark,
953+ KatzeItem* bookmark_or_parent,
954 gboolean new_bookmark,
955 gboolean is_folder,
956 GtkWidget* proxy)
957 {
958+ KatzeItem* bookmark = bookmark_or_parent;
959 const gchar* title;
960 GtkWidget* dialog;
961 GtkWidget* content_area;
962@@ -980,7 +1056,7 @@
963 gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
964 gtk_container_set_border_width (GTK_CONTAINER (content_area), 5);
965
966- if (!bookmark)
967+ if (new_bookmark)
968 {
969 view = midori_browser_get_current_tab (browser);
970 if (is_folder)
971@@ -993,6 +1069,12 @@
972 bookmark = g_object_new (KATZE_TYPE_ITEM,
973 "uri", midori_view_get_display_uri (MIDORI_VIEW (view)),
974 "name", midori_view_get_display_title (MIDORI_VIEW (view)), NULL);
975+
976+ katze_item_set_meta_integer (
977+ bookmark, "parentid",
978+ (!bookmark_or_parent
979+ ? 0
980+ : katze_item_get_meta_integer (bookmark_or_parent, "id")));
981 }
982
983 entry_title = gtk_entry_new ();
984@@ -1020,7 +1102,7 @@
985 }
986
987 combo_folder = midori_bookmark_folder_button_new (browser->bookmarks,
988- new_bookmark, katze_item_get_meta_integer (bookmark, "id"),
989+ katze_item_get_meta_integer (bookmark, "id"),
990 katze_item_get_meta_integer (bookmark, "parentid"));
991 gtk_container_add (GTK_CONTAINER (content_area), combo_folder);
992
993@@ -1054,7 +1136,7 @@
994 {
995 gint64 selected;
996
997- katze_item_set_name (bookmark,
998+ katze_item_set_name (bookmark,
999 gtk_entry_get_text (GTK_ENTRY (entry_title)));
1000 katze_item_set_meta_integer (bookmark, "toolbar",
1001 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_toolbar)));
1002@@ -1072,16 +1154,11 @@
1003 if (new_bookmark)
1004 katze_array_add_item (browser->bookmarks, bookmark);
1005 else
1006- midori_bookmarks_update_item_db (db, bookmark);
1007- midori_browser_update_history (bookmark, "bookmark", new_bookmark ? "create" : "modify");
1008+ katze_array_update_item (browser->bookmarks, bookmark);
1009
1010- if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_toolbar)))
1011- if (!gtk_widget_get_visible (browser->bookmarkbar))
1012- _action_set_active (browser, "Bookmarkbar", TRUE);
1013 return_status = TRUE;
1014 }
1015- if (gtk_widget_get_visible (browser->bookmarkbar))
1016- midori_bookmarkbar_populate (browser);
1017+
1018 gtk_widget_destroy (dialog);
1019 return return_status;
1020 }
1021@@ -3108,7 +3185,8 @@
1022 condition = "parentid = %q";
1023
1024 bookmarks = midori_array_query (browser->bookmarks,
1025- "id, title, parentid, uri, app, pos_panel, pos_bar", condition, id);
1026+ "id, title, parentid, uri, app, pos_panel, pos_bar", condition, id,
1027+ "pos_panel ASC,");
1028 if (!bookmarks)
1029 return FALSE;
1030
1031@@ -4143,7 +4221,10 @@
1032 else if (!KATZE_IS_ARRAY (item) && strcmp (stock_id, GTK_STOCK_DELETE))
1033 gtk_widget_set_sensitive (menuitem, uri != NULL);
1034 g_object_set_data (G_OBJECT (menuitem), "KatzeItem", item);
1035- g_signal_connect (menuitem, "activate", G_CALLBACK (callback), userdata);
1036+ if (callback)
1037+ g_signal_connect (menuitem, "activate", G_CALLBACK (callback), userdata);
1038+ else
1039+ gtk_widget_set_sensitive (menuitem, FALSE);
1040 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
1041 gtk_widget_show (menuitem);
1042 }
1043@@ -4444,7 +4525,7 @@
1044 gtk_widget_show_all (hbox);
1045
1046 combobox_folder = midori_bookmark_folder_button_new (browser->bookmarks,
1047- FALSE, 0, 0);
1048+ 0, 0);
1049 gtk_container_add (GTK_CONTAINER (content_area), combobox_folder);
1050
1051 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
1052@@ -4538,7 +4619,7 @@
1053
1054 error = NULL;
1055 bookmarks = midori_array_query_recursive (browser->bookmarks,
1056- "*", "parentid IS NULL", NULL, TRUE);
1057+ "*", "parentid IS NULL", NULL, NULL, TRUE);
1058 if (!midori_array_to_file (bookmarks, path, format, &error))
1059 {
1060 sokoke_message_dialog (GTK_MESSAGE_ERROR,
1061@@ -5963,6 +6044,21 @@
1062 }
1063 }
1064
1065+static gboolean
1066+midori_browser_idle (gpointer data)
1067+{
1068+ MidoriBrowser* browser = MIDORI_BROWSER (data);
1069+
1070+ if (browser->bookmarkbar_populate)
1071+ {
1072+ midori_bookmarkbar_populate_idle (browser);
1073+
1074+ browser->bookmarkbar_populate = FALSE;
1075+ }
1076+
1077+ return FALSE;
1078+}
1079+
1080 static void
1081 midori_browser_init (MidoriBrowser* browser)
1082 {
1083@@ -6140,6 +6236,8 @@
1084 "array", dummy_array /* updated, unique */,
1085 NULL);
1086 g_object_connect (action,
1087+ "signal::populate-folder",
1088+ _action_bookmarks_populate_folder, browser,
1089 "signal::populate-popup",
1090 _action_tools_populate_popup, browser,
1091 "signal::activate-item-alt",
1092@@ -6443,6 +6541,8 @@
1093 katze_object_assign (browser->history, NULL);
1094 katze_object_assign (browser->dial, NULL);
1095
1096+ g_idle_remove_by_data (browser);
1097+
1098 G_OBJECT_CLASS (midori_browser_parent_class)->finalize (object);
1099 }
1100
1101@@ -6978,6 +7078,30 @@
1102 }
1103
1104 static void
1105+midori_bookmarkbar_add_item_cb (KatzeArray* bookmarks,
1106+ KatzeItem* item,
1107+ MidoriBrowser* browser)
1108+{
1109+ if (gtk_widget_get_visible (browser->bookmarkbar))
1110+ midori_bookmarkbar_populate (browser);
1111+ else if (katze_item_get_meta_boolean (item, "toolbar"))
1112+ _action_set_active (browser, "Bookmarkbar", TRUE);
1113+ midori_browser_update_history (item, "bookmark", "created");
1114+}
1115+
1116+static void
1117+midori_bookmarkbar_update_item_cb (KatzeArray* bookmarks,
1118+ KatzeItem* item,
1119+ MidoriBrowser* browser)
1120+{
1121+ if (gtk_widget_get_visible (browser->bookmarkbar))
1122+ midori_bookmarkbar_populate (browser);
1123+ else if (katze_item_get_meta_boolean (item, "toolbar"))
1124+ _action_set_active (browser, "Bookmarkbar", TRUE);
1125+ midori_browser_update_history (item, "bookmark", "modify");
1126+}
1127+
1128+static void
1129 midori_bookmarkbar_remove_item_cb (KatzeArray* bookmarks,
1130 KatzeItem* item,
1131 MidoriBrowser* browser)
1132@@ -6990,6 +7114,16 @@
1133 static void
1134 midori_bookmarkbar_populate (MidoriBrowser* browser)
1135 {
1136+ if (browser->bookmarkbar_populate)
1137+ return;
1138+
1139+ g_idle_add (midori_browser_idle, browser);
1140+ browser->bookmarkbar_populate = TRUE;
1141+}
1142+
1143+static void
1144+midori_bookmarkbar_populate_idle (MidoriBrowser* browser)
1145+{
1146 KatzeArray* array;
1147 KatzeItem* item;
1148
1149@@ -7000,7 +7134,7 @@
1150 gtk_separator_tool_item_new (), -1);
1151
1152 array = midori_array_query (browser->bookmarks,
1153- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "toolbar = 1", NULL);
1154+ "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "toolbar = 1", NULL, "pos_bar ASC,");
1155 if (!array)
1156 {
1157 _action_set_sensitive (browser, "BookmarkAdd", FALSE);
1158@@ -7016,9 +7150,12 @@
1159 {
1160 gint64 id = katze_item_get_meta_integer (item, "id");
1161 gchar* parentid = g_strdup_printf ("%" G_GINT64_FORMAT, id);
1162- KatzeArray* subfolder = midori_array_query (browser->bookmarks,
1163- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid = %q AND uri != ''",
1164- parentid);
1165+ KatzeArray* subfolder = midori_array_query (
1166+ browser->bookmarks,
1167+ "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar",
1168+ "parentid = %q",
1169+ parentid,
1170+ "pos_panel ASC,");
1171
1172 katze_item_set_name (KATZE_ITEM (subfolder), katze_item_get_name (item));
1173 katze_item_set_meta_integer (KATZE_ITEM (subfolder), "id", id);
1174@@ -7059,8 +7196,15 @@
1175 MidoriWebSettings* settings;
1176
1177 if (browser->bookmarks != NULL)
1178+ {
1179+ g_signal_handlers_disconnect_by_func (browser->bookmarks,
1180+ midori_bookmarkbar_add_item_cb, browser);
1181+ g_signal_handlers_disconnect_by_func (browser->bookmarks,
1182+ midori_bookmarkbar_update_item_cb, browser);
1183 g_signal_handlers_disconnect_by_func (browser->bookmarks,
1184 midori_bookmarkbar_remove_item_cb, browser);
1185+ }
1186+
1187 settings = midori_browser_get_settings (browser);
1188 g_signal_handlers_disconnect_by_func (settings,
1189 midori_browser_show_bookmarkbar_notify_value_cb, browser);
1190@@ -7089,6 +7233,10 @@
1191 g_signal_connect (settings, "notify::show-bookmarkbar",
1192 G_CALLBACK (midori_browser_show_bookmarkbar_notify_value_cb), browser);
1193 g_object_notify (G_OBJECT (settings), "show-bookmarkbar");
1194+ g_signal_connect_after (bookmarks, "add-item",
1195+ G_CALLBACK (midori_bookmarkbar_add_item_cb), browser);
1196+ g_signal_connect_after (bookmarks, "update-item",
1197+ G_CALLBACK (midori_bookmarkbar_update_item_cb), browser);
1198 g_signal_connect_after (bookmarks, "remove-item",
1199 G_CALLBACK (midori_bookmarkbar_remove_item_cb), browser);
1200 }
1201
1202=== modified file 'midori/midori-view.c'
1203--- midori/midori-view.c 2013-05-08 00:51:02 +0000
1204+++ midori/midori-view.c 2013-05-21 05:35:32 +0000
1205@@ -2759,7 +2759,7 @@
1206 _("Open Link in New _Window"), STOCK_WINDOW_NEW,
1207 G_CALLBACK (midori_web_view_menu_new_window_activate_cb), widget);
1208 midori_view_insert_menu_item (menu_shell, -1,
1209- _("Open Link as Web A_pplication"), NULL,
1210+ _("Open Link as Web A_pplication"), GTK_STOCK_EXECUTE,
1211 G_CALLBACK (midori_web_view_menu_web_app_activate_cb), widget);
1212 }
1213
1214
1215=== modified file 'panels/midori-bookmarks.c'
1216--- panels/midori-bookmarks.c 2013-02-11 21:49:41 +0000
1217+++ panels/midori-bookmarks.c 2013-05-21 05:35:32 +0000
1218@@ -25,6 +25,150 @@
1219
1220 #define COMPLETION_DELAY 200
1221
1222+G_BEGIN_DECLS
1223+
1224+#define MIDORI_BOOKMARKS_TREE_STORE_TYPE \
1225+ (midori_bookmarks_tree_store_get_type ())
1226+#define MIDORI_BOOKMARKS_TREE_STORE(obj) \
1227+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), MIDORI_BOOKMARKS_TREE_STORE_TYPE, MidoriBookmarksTreeStore))
1228+#define MIDORI_BOOKMARKS_TREE_STORE_CLASS(klass) \
1229+ (G_TYPE_CHECK_CLASS_CAST ((klass), MIDORI_BOOKMARKS_TREE_STORE_TYPE, MidoriBookmarksTreeStoreClass))
1230+
1231+static gboolean
1232+midori_bookmarks_tree_store_drag_data_get (GtkTreeDragSource* drag_source,
1233+ GtkTreePath* source_path,
1234+ GtkSelectionData* selection_data);
1235+static gboolean
1236+midori_bookmarks_tree_store_drag_data_delete (GtkTreeDragSource* drag_source,
1237+ GtkTreePath* source_path);
1238+static gboolean
1239+midori_bookmarks_tree_store_row_drop_possible (GtkTreeDragDest *drag_dest,
1240+ GtkTreePath *dest_path,
1241+ GtkSelectionData *selection_data);
1242+static gboolean
1243+midori_bookmarks_tree_store_drag_data_received (GtkTreeDragDest *drag_dest,
1244+ GtkTreePath *dest_path,
1245+ GtkSelectionData *selection_data);
1246+
1247+typedef struct _MidoriBookmarksTreeStore MidoriBookmarksTreeStore;
1248+typedef struct _MidoriBookmarksTreeStoreClass MidoriBookmarksTreeStoreClass;
1249+typedef struct _TreeRowData TreeRowData;
1250+
1251+struct _MidoriBookmarksTreeStore
1252+{
1253+ GtkTreeStore parent_instance;
1254+
1255+ GList* stock_got_rows;
1256+ GtkTreeView *source_view;
1257+};
1258+
1259+struct _MidoriBookmarksTreeStoreClass
1260+{
1261+ GtkTreeStoreClass parent_class;
1262+};
1263+
1264+struct _TreeRowData
1265+{
1266+ GtkTreeModel *model;
1267+ gchar path[4];
1268+};
1269+
1270+static GtkTreeDragSourceIface *
1271+gtk_tree_store_gtk_tree_drag_source_iface = NULL;
1272+static GtkTreeDragDestIface *
1273+gtk_tree_store_gtk_tree_drag_dest_iface = NULL;
1274+
1275+static void
1276+midori_bookmarks_tree_store_drag_source_init (GtkTreeDragSourceIface *iface)
1277+{
1278+ gtk_tree_store_gtk_tree_drag_source_iface = g_type_interface_peek_parent (iface);
1279+
1280+ iface->drag_data_get = midori_bookmarks_tree_store_drag_data_get;
1281+ iface->drag_data_delete = midori_bookmarks_tree_store_drag_data_delete;
1282+}
1283+
1284+static void
1285+midori_bookmarks_tree_store_drag_dest_init (GtkTreeDragDestIface *iface)
1286+{
1287+ gtk_tree_store_gtk_tree_drag_dest_iface = g_type_interface_peek_parent (iface);
1288+
1289+ iface->row_drop_possible = midori_bookmarks_tree_store_row_drop_possible;
1290+ iface->drag_data_received = midori_bookmarks_tree_store_drag_data_received;
1291+}
1292+
1293+static void
1294+midori_bookmarks_tree_store_init (MidoriBookmarksTreeStore* item)
1295+{
1296+ item->stock_got_rows = NULL;
1297+ item->source_view = NULL;
1298+}
1299+
1300+static void
1301+midori_bookmarks_tree_store_class_init (MidoriBookmarksTreeStoreClass *class)
1302+{
1303+}
1304+
1305+G_DEFINE_TYPE_WITH_CODE (MidoriBookmarksTreeStore,
1306+ midori_bookmarks_tree_store,
1307+ GTK_TYPE_TREE_STORE,
1308+ G_IMPLEMENT_INTERFACE (
1309+ GTK_TYPE_TREE_DRAG_SOURCE,
1310+ midori_bookmarks_tree_store_drag_source_init)
1311+ G_IMPLEMENT_INTERFACE (
1312+ GTK_TYPE_TREE_DRAG_DEST,
1313+ midori_bookmarks_tree_store_drag_dest_init));
1314+
1315+
1316+GtkTreeStore*
1317+midori_bookmarks_tree_store_new (gint n_columns, ...)
1318+{
1319+ GtkTreeStore* tree_store = GTK_TREE_STORE (g_object_new (MIDORI_BOOKMARKS_TREE_STORE_TYPE, NULL));
1320+ va_list ap;
1321+ GType* types;
1322+ gint n;
1323+
1324+ if (!tree_store)
1325+ return NULL;
1326+
1327+ types = g_new (GType, n_columns);
1328+
1329+ if (!types)
1330+ {
1331+ g_object_unref(tree_store);
1332+ return NULL;
1333+ }
1334+
1335+ va_start(ap, n_columns);
1336+ for (n = 0; n < n_columns; n++)
1337+ {
1338+ types[n] = va_arg(ap, GType);
1339+ }
1340+ va_end(ap);
1341+
1342+ gtk_tree_store_set_column_types (tree_store,
1343+ n_columns,
1344+ types);
1345+
1346+ g_free (types);
1347+ return tree_store;
1348+}
1349+
1350+
1351+GtkTreeStore*
1352+midori_bookmarks_tree_store_newv (gint n_columns, GType *types)
1353+{
1354+ GtkTreeStore* tree_store = GTK_TREE_STORE (g_object_new (MIDORI_BOOKMARKS_TREE_STORE_TYPE, NULL));
1355+
1356+ if (!tree_store)
1357+ return NULL;
1358+
1359+ gtk_tree_store_set_column_types (tree_store,
1360+ n_columns,
1361+ types);
1362+
1363+ return tree_store;
1364+}
1365+
1366 gboolean
1367 midori_browser_edit_bookmark_dialog_new (MidoriBrowser* browser,
1368 KatzeItem* bookmark,
1369@@ -36,6 +180,12 @@
1370 midori_browser_open_bookmark (MidoriBrowser* browser,
1371 KatzeItem* item);
1372
1373+static void
1374+midori_bookmarks_row_changed_cb (GtkTreeModel* model,
1375+ GtkTreePath* path,
1376+ GtkTreeIter* iter,
1377+ MidoriBookmarks* bookmarks);
1378+
1379 struct _MidoriBookmarks
1380 {
1381 GtkVBox parent_instance;
1382@@ -49,6 +199,16 @@
1383
1384 gint filter_timeout;
1385 gchar* filter;
1386+
1387+ struct _stock_pending_event
1388+ {
1389+ gint x;
1390+ gint y;
1391+ } *pending_event,
1392+ stock_pending_event;
1393+
1394+ GHashTable* updated_items;
1395+ GList* added_paths;
1396 };
1397
1398 struct _MidoriBookmarksClass
1399@@ -86,6 +246,49 @@
1400 GParamSpec* pspec);
1401
1402 static void
1403+midori_bookmarks_update_cb (KatzeArray* array,
1404+ MidoriBookmarks* bookmarks);
1405+static void
1406+midori_bookmarks_add_item_cb (KatzeArray* array,
1407+ KatzeItem* item,
1408+ MidoriBookmarks* bookmarks);
1409+static void
1410+midori_bookmarks_update_item_cb (KatzeArray* array,
1411+ KatzeItem* item,
1412+ MidoriBookmarks* bookmarks);
1413+static void
1414+midori_bookmarks_remove_item_cb (KatzeArray* array,
1415+ KatzeItem* item,
1416+ MidoriBookmarks* bookmarks);
1417+
1418+static void
1419+midori_bookmarks_row_added_cb (GtkTreeModel* model,
1420+ GtkTreePath* path,
1421+ GtkTreeIter* iter,
1422+ MidoriBookmarks* bookmarks);
1423+static void
1424+midori_bookmarks_row_changed_cb (GtkTreeModel* model,
1425+ GtkTreePath* path,
1426+ GtkTreeIter* iter,
1427+ MidoriBookmarks* bookmarks);
1428+static void
1429+midori_bookmarks_row_deleted_cb (GtkTreeModel* model,
1430+ GtkTreePath* path,
1431+ MidoriBookmarks* bookmarks);
1432+
1433+static void
1434+midori_bookmarks_update_item (MidoriBookmarks* bookmarks, KatzeItem *item);
1435+
1436+static void
1437+midori_bookmarks_statusbar_update (MidoriBookmarks *bookmarks);
1438+
1439+static void
1440+midori_bookmarks_idle_remove_item (MidoriBookmarks* bookmarks, KatzeItem *item);
1441+
1442+static gboolean
1443+midori_bookmarks_idle (gpointer data);
1444+
1445+static void
1446 midori_bookmarks_class_init (MidoriBookmarksClass* class)
1447 {
1448 GObjectClass* gobject_class;
1449@@ -133,7 +336,7 @@
1450 gchar* parent_id;
1451
1452 parent_id = g_strdup_printf ("%" G_GINT64_FORMAT, parentid);
1453- if (!(root_array = midori_array_query (array, "*", "parentid = %q", parent_id)))
1454+ if (!(root_array = midori_array_query (array, "*", "parentid = %q", parent_id, "pos_pnel DESC,")))
1455 {
1456 g_free (parent_id);
1457 return;
1458@@ -186,20 +389,20 @@
1459
1460 if (keyword && *keyword)
1461 array = midori_array_query (bookmarks->array,
1462- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "title LIKE '%%%q%%'", keyword);
1463+ "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "title LIKE '%%%q%%'", keyword, NULL);
1464 else
1465 {
1466 if (parentid > 0)
1467 {
1468 gchar* parent_id = g_strdup_printf ("%" G_GINT64_FORMAT, parentid);
1469 array = midori_array_query (bookmarks->array,
1470- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid = %q", parent_id);
1471+ "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid = %q", parent_id, "pos_panel DESC,");
1472
1473 g_free (parent_id);
1474 }
1475 else
1476 array = midori_array_query (bookmarks->array,
1477- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid IS NULL", NULL);
1478+ "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid IS NULL", NULL, "pos_panel DESC,");
1479 }
1480 return array ? array : katze_array_new (KATZE_TYPE_ITEM);
1481 }
1482@@ -216,19 +419,39 @@
1483 KatzeItem* item;
1484 GtkTreeIter child;
1485
1486+ g_signal_handlers_block_by_func (model,
1487+ midori_bookmarks_row_changed_cb,
1488+ bookmarks);
1489+
1490 array = midori_bookmarks_read_from_db (bookmarks, parentid, keyword);
1491 katze_bookmark_populate_tree_view (array, model, parent);
1492+
1493+ g_signal_handlers_unblock_by_func (model,
1494+ midori_bookmarks_row_changed_cb,
1495+ bookmarks);
1496+
1497+
1498 /* Remove invisible dummy row */
1499 last = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (model), parent);
1500 if (!last)
1501 return;
1502+
1503+ g_signal_handlers_block_by_func (model,
1504+ midori_bookmarks_row_deleted_cb,
1505+ bookmarks);
1506+
1507 gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (model), &child, parent, last - 1);
1508 gtk_tree_model_get (GTK_TREE_MODEL (model), &child, 0, &item, -1);
1509 if (KATZE_ITEM_IS_SEPARATOR (item))
1510 gtk_tree_store_remove (model, &child);
1511 else
1512 g_object_unref (item);
1513-}
1514+
1515+ g_signal_handlers_unblock_by_func (model,
1516+ midori_bookmarks_row_deleted_cb,
1517+ bookmarks);
1518+
1519+ }
1520
1521 gint64
1522 midori_bookmarks_insert_item_db (sqlite3* db,
1523@@ -316,16 +539,237 @@
1524 return seq;
1525 }
1526
1527+static gboolean
1528+midori_bookmarks_reach_item_recurse (GtkTreeModel* model,
1529+ GtkTreeIter* iter,
1530+ gint64 id)
1531+{
1532+ do
1533+ {
1534+ GtkTreeIter child;
1535+ KatzeItem *item;
1536+ gint64 itemid = -1;
1537+
1538+ gtk_tree_model_get (model, iter, 0, &item, -1);
1539+
1540+ if (!KATZE_ITEM_IS_SEPARATOR(item))
1541+ {
1542+ itemid = katze_item_get_meta_integer (item, "id");
1543+ g_object_unref (item);
1544+ }
1545+
1546+ if (id == itemid)
1547+ return TRUE;
1548+
1549+ if (gtk_tree_model_iter_children (model, &child, iter))
1550+ {
1551+ if (midori_bookmarks_reach_item_recurse (model, &child, id))
1552+ {
1553+ *iter = child;
1554+ return TRUE;
1555+ }
1556+ }
1557+ }
1558+ while (gtk_tree_model_iter_next(model, iter));
1559+
1560+ return FALSE;
1561+}
1562+
1563+static gboolean
1564+midori_bookmarks_reach_item (GtkTreeModel* model,
1565+ GtkTreeIter* iter,
1566+ gint64 id)
1567+{
1568+ if (!gtk_tree_model_get_iter_first(model, iter))
1569+ return FALSE;
1570+
1571+ return midori_bookmarks_reach_item_recurse (model, iter, id);
1572+}
1573+
1574+static void
1575+midori_bookmarks_add_item_to_model(MidoriBookmarks* bookmarks,
1576+ GtkTreeModel* model,
1577+ GtkTreeIter* parent,
1578+ KatzeItem* item)
1579+{
1580+ GtkTreeStore* tree_store = GTK_TREE_STORE (model);
1581+ gint last;
1582+ GtkTreeIter child;
1583+
1584+ if (KATZE_ITEM_IS_BOOKMARK (item))
1585+ {
1586+ gint position = 0;
1587+
1588+ /* skip the folders to be consistent with the db query order */
1589+ if (gtk_tree_model_iter_children (model, &child, parent))
1590+ while (gtk_tree_model_iter_has_child (model, &child))
1591+ {
1592+ position++;
1593+ if (!gtk_tree_model_iter_next (model, &child))
1594+ break;
1595+ }
1596+
1597+ gchar* tooltip = g_markup_escape_text (katze_item_get_uri (item), -1);
1598+
1599+ gtk_tree_store_insert_with_values (tree_store, NULL, parent,
1600+ position,
1601+ 0, item, 1, tooltip, -1);
1602+ g_free (tooltip);
1603+ }
1604+ else if (KATZE_ITEM_IS_FOLDER (item))
1605+ {
1606+ GtkTreeIter root_iter;
1607+
1608+ gtk_tree_store_insert_with_values (tree_store, &root_iter, parent,
1609+ 0, 0, item, -1);
1610+
1611+ /* That's an invisible dummy, so we always have an expander */
1612+ gtk_tree_store_insert_with_values (tree_store, NULL, &root_iter,
1613+ 0,
1614+ 0, NULL, -1);
1615+ }
1616+
1617+ /* Remove invisible dummy row */
1618+ last = gtk_tree_model_iter_n_children (model, parent);
1619+ if (!last)
1620+ return;
1621+
1622+ g_signal_handlers_block_by_func (model,
1623+ midori_bookmarks_row_deleted_cb,
1624+ bookmarks);
1625+
1626+ gtk_tree_model_iter_nth_child (model, &child, parent, last - 1);
1627+ gtk_tree_model_get (model, &child, 0, &item, -1);
1628+ if (KATZE_ITEM_IS_SEPARATOR (item))
1629+ gtk_tree_store_remove (tree_store, &child);
1630+ else
1631+ g_object_unref (item);
1632+
1633+ g_signal_handlers_unblock_by_func (model,
1634+ midori_bookmarks_row_deleted_cb,
1635+ bookmarks);
1636+}
1637+
1638+static void
1639+midori_bookmarks_update_item_in_model(MidoriBookmarks* bookmarks,
1640+ GtkTreeStore* model,
1641+ GtkTreeIter* iter,
1642+ KatzeItem* item)
1643+{
1644+ g_signal_handlers_block_by_func (model,
1645+ midori_bookmarks_row_changed_cb,
1646+ bookmarks);
1647+
1648+ if (KATZE_ITEM_IS_BOOKMARK (item))
1649+ {
1650+ gchar* tooltip = g_markup_escape_text (katze_item_get_uri (item), -1);
1651+
1652+ gtk_tree_store_set(model, iter,
1653+ 0, item, 1, tooltip, -1);
1654+
1655+ g_free (tooltip);
1656+ }
1657+ else
1658+ {
1659+ gtk_tree_store_set(model, iter,
1660+ 0, item, -1);
1661+ }
1662+
1663+ g_signal_handlers_unblock_by_func (model,
1664+ midori_bookmarks_row_changed_cb,
1665+ bookmarks);
1666+
1667+}
1668+
1669 static void
1670 midori_bookmarks_add_item_cb (KatzeArray* array,
1671 KatzeItem* item,
1672 MidoriBookmarks* bookmarks)
1673 {
1674- GtkTreeModel* model;
1675- model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
1676- gtk_tree_store_clear (GTK_TREE_STORE (model));
1677- midori_bookmarks_read_from_db_to_model (bookmarks,
1678- GTK_TREE_STORE (model), NULL, 0, bookmarks->filter);
1679+ GtkTreeModel* model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
1680+ GtkTreeIter iter;
1681+ gint64 parentid;
1682+
1683+ g_assert (KATZE_IS_ITEM (item));
1684+
1685+ parentid = katze_item_get_meta_integer (item, "parentid");
1686+
1687+ if (!parentid)
1688+ midori_bookmarks_add_item_to_model (bookmarks, model, NULL, item);
1689+ else if (midori_bookmarks_reach_item (model, &iter, parentid))
1690+ {
1691+ GtkTreePath* path = gtk_tree_model_get_path(model, &iter);
1692+ gint n_children = gtk_tree_model_iter_n_children (model, &iter);
1693+
1694+ if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (bookmarks->treeview), path)
1695+ || !n_children)
1696+ midori_bookmarks_add_item_to_model (bookmarks, model, &iter, item);
1697+
1698+ gtk_tree_path_free (path);
1699+ }
1700+}
1701+
1702+static void
1703+midori_bookmarks_update_item_cb (KatzeArray* array,
1704+ KatzeItem* item,
1705+ MidoriBookmarks* bookmarks)
1706+{
1707+ gint64 id = katze_item_get_meta_integer (item, "id");
1708+ gint64 parentid = katze_item_get_meta_integer (item, "parentid");
1709+ GtkTreeModel* model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
1710+ GtkTreeIter iter;
1711+
1712+ g_assert (KATZE_IS_ITEM (item));
1713+
1714+ if (midori_bookmarks_reach_item (model, &iter, id))
1715+ {
1716+ gint64 old_parentid = 0;
1717+ GtkTreeIter parent;
1718+
1719+ if (gtk_tree_model_iter_parent (model, &parent, &iter))
1720+ {
1721+ KatzeItem* old_parent;
1722+
1723+ gtk_tree_model_get (model, &parent, 0, &old_parent, -1);
1724+
1725+ old_parentid = katze_item_get_meta_integer (old_parent, "id");
1726+
1727+ g_object_unref (old_parent);
1728+
1729+ if (parentid == old_parentid)
1730+ {
1731+ midori_bookmarks_update_item_in_model (bookmarks, GTK_TREE_STORE (model), &iter, item);
1732+ }
1733+ else
1734+ {
1735+ gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
1736+
1737+ if (!gtk_tree_model_iter_has_child (model, &parent))
1738+ {
1739+ GtkTreePath* path = gtk_tree_model_get_path(model, &parent);
1740+
1741+ if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (bookmarks->treeview), path))
1742+ gtk_tree_view_collapse_row (GTK_TREE_VIEW (bookmarks->treeview), path);
1743+
1744+ gtk_tree_path_free (path);
1745+ }
1746+
1747+ midori_bookmarks_add_item_cb (array, item, bookmarks);
1748+ }
1749+ }
1750+ else if (parentid == 0)
1751+ {
1752+ midori_bookmarks_update_item_in_model (bookmarks, GTK_TREE_STORE (model), &iter, item);
1753+ }
1754+ else
1755+ {
1756+ gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
1757+
1758+ midori_bookmarks_add_item_cb (array, item, bookmarks);
1759+ }
1760+ }
1761+ else
1762+ midori_bookmarks_add_item_cb (array, item, bookmarks);
1763 }
1764
1765 static void
1766@@ -333,22 +777,665 @@
1767 KatzeItem* item,
1768 MidoriBookmarks* bookmarks)
1769 {
1770+ gint64 id = katze_item_get_meta_integer (item, "id");
1771 GtkTreeModel* model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
1772- gtk_tree_store_clear (GTK_TREE_STORE (model));
1773- midori_bookmarks_read_from_db_to_model (bookmarks,
1774- GTK_TREE_STORE (model), NULL, 0, bookmarks->filter);
1775+ GtkTreeIter iter;
1776+
1777+ g_assert (KATZE_IS_ITEM (item));
1778+
1779+ midori_bookmarks_idle_remove_item (bookmarks, item);
1780+
1781+ if (midori_bookmarks_reach_item (model, &iter, id))
1782+ {
1783+ GtkTreeIter parent;
1784+
1785+ if (gtk_tree_model_iter_parent (model, &parent, &iter))
1786+ {
1787+ gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
1788+
1789+ if (!gtk_tree_model_iter_has_child (model, &parent))
1790+ {
1791+ GtkTreePath* path = gtk_tree_model_get_path(model, &parent);
1792+
1793+ if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (bookmarks->treeview), path))
1794+ gtk_tree_view_collapse_row (GTK_TREE_VIEW (bookmarks->treeview), path);
1795+
1796+ gtk_tree_path_free (path);
1797+ }
1798+ }
1799+ else
1800+ {
1801+ gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
1802+ }
1803+
1804+ midori_bookmarks_statusbar_update (bookmarks);
1805+ }
1806 }
1807
1808+
1809 static void
1810 midori_bookmarks_update_cb (KatzeArray* array,
1811 MidoriBookmarks* bookmarks)
1812 {
1813 GtkTreeModel* model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
1814+
1815 gtk_tree_store_clear (GTK_TREE_STORE (model));
1816 midori_bookmarks_read_from_db_to_model (bookmarks,
1817 GTK_TREE_STORE (model), NULL, 0, bookmarks->filter);
1818-}
1819-
1820+
1821+ midori_bookmarks_statusbar_update (bookmarks);
1822+}
1823+
1824+gboolean
1825+midori_bookmarks_tree_set_row_drag_data (GtkSelectionData *selection_data,
1826+ GtkTreeModel *tree_model,
1827+ GList* rows)
1828+{
1829+ TreeRowData *trd;
1830+ gint len;
1831+ gint struct_size;
1832+ gint length;
1833+ gint i;
1834+ GString *data;
1835+
1836+ g_return_val_if_fail (selection_data != NULL, FALSE);
1837+ g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE);
1838+ g_return_val_if_fail (rows != NULL, FALSE);
1839+
1840+ data = g_string_new("");
1841+
1842+ length = g_list_length (rows);
1843+ for (i = 0; i < length; i++)
1844+ {
1845+ GtkTreePath *path = (GtkTreePath *)g_list_nth_data (rows, i);
1846+ gchar *path_str = gtk_tree_path_to_string (path);
1847+
1848+ g_string_append (data, path_str);
1849+ if (i < length-1)
1850+ g_string_append_c (data, '\n');
1851+ }
1852+
1853+ len = data->len;
1854+
1855+ /* the old allocate-end-of-struct-to-hold-string trick */
1856+ struct_size = sizeof (TreeRowData) + len + 1 -
1857+ (sizeof (TreeRowData) - G_STRUCT_OFFSET (TreeRowData, path));
1858+
1859+ trd = g_malloc (struct_size);
1860+
1861+ strcpy (trd->path, data->str);
1862+
1863+ g_string_free (data, TRUE);
1864+
1865+ trd->model = tree_model;
1866+
1867+ gtk_selection_data_set (selection_data,
1868+ gdk_atom_intern_static_string ("GTK_TREE_MODEL_MULTI_ROWS"),
1869+ 8, /* bytes */
1870+ (void*)trd,
1871+ struct_size);
1872+
1873+ g_free (trd);
1874+
1875+ return TRUE;
1876+}
1877+
1878+static gboolean
1879+midori_bookmarks_tree_store_drag_data_get (GtkTreeDragSource* drag_source,
1880+ GtkTreePath* source_path,
1881+ GtkSelectionData* selection_data)
1882+{
1883+ MidoriBookmarksTreeStore *tree_store;
1884+ gboolean status = FALSE;
1885+
1886+ g_return_val_if_fail (selection_data != NULL, FALSE);
1887+ g_return_val_if_fail (GTK_IS_TREE_MODEL (drag_source), FALSE);
1888+ g_return_val_if_fail (source_path != NULL, FALSE);
1889+
1890+ tree_store = MIDORI_BOOKMARKS_TREE_STORE(drag_source);
1891+
1892+ if (tree_store->stock_got_rows)
1893+ {
1894+ g_list_free_full (tree_store->stock_got_rows, (GDestroyNotify) gtk_tree_path_free);
1895+ tree_store->stock_got_rows = NULL;
1896+ }
1897+
1898+ if (gtk_selection_data_get_target (selection_data) ==
1899+ gdk_atom_intern_static_string ("GTK_TREE_MODEL_MULTI_ROWS"))
1900+ {
1901+ GtkTreeModel *model;
1902+ GList* rows;
1903+ if (katze_tree_view_get_selected_rows (
1904+ tree_store->source_view, &model, &rows))
1905+ {
1906+ status = midori_bookmarks_tree_set_row_drag_data (selection_data, model, rows);
1907+
1908+ tree_store->stock_got_rows = rows;
1909+ }
1910+ }
1911+
1912+ return status;
1913+}
1914+
1915+static void
1916+update_path_list_for_insert (GList * rows, GtkTreePath* path)
1917+{
1918+ gint length = g_list_length (rows);
1919+ gint i;
1920+
1921+ for (i = 0; i < length; i++ )
1922+ {
1923+ GtkTreePath *src_path_r = (GtkTreePath *)g_list_nth_data (rows, i);
1924+ gint la = gtk_tree_path_get_depth (path);
1925+ gint lb = gtk_tree_path_get_depth (src_path_r);
1926+ gint *ia = gtk_tree_path_get_indices (path);
1927+ gint *ib = gtk_tree_path_get_indices (src_path_r);
1928+ gint j;
1929+
1930+ if (la > lb) /* insert was donne in a deeper branch than source */
1931+ continue;
1932+
1933+ if (ia[la-1] > ib[la-1]) /* insert was donne after source */
1934+ continue;
1935+
1936+ for (j = 0; j < la; j++)
1937+ {
1938+ if (ia[j] != ib[j]) break;
1939+ }
1940+
1941+ if (j < la-1) /* insert and source are not in the same branch */
1942+ continue;
1943+
1944+ /* source at depth level of insert must be incremented due to the insert */
1945+ ib[la-1] += 1;
1946+ }
1947+}
1948+
1949+static void
1950+update_path_list_for_delete (GList * rows, GtkTreePath* path)
1951+{
1952+ gint length = g_list_length (rows);
1953+ gint i;
1954+
1955+ for (i = 0; i < length; i++ )
1956+ {
1957+ GtkTreePath *src_path_r = (GtkTreePath *)g_list_nth_data (rows, i);
1958+ gint la = gtk_tree_path_get_depth (path);
1959+ gint lb = gtk_tree_path_get_depth (src_path_r);
1960+ gint *ia = gtk_tree_path_get_indices (path);
1961+ gint *ib = gtk_tree_path_get_indices (src_path_r);
1962+ gint j;
1963+
1964+ if (la > lb) /* removal was donne in a deeper branch than source */
1965+ continue;
1966+
1967+ if (ia[la-1] > ib[la-1]) /* removal was donne after source */
1968+ continue;
1969+
1970+ for (j = 0; j < la; j++)
1971+ {
1972+ if (ia[j] != ib[j]) break;
1973+ }
1974+
1975+ if (j < la-1) /* removal and source are not in the same branch */
1976+ continue;
1977+
1978+ /* source at depth level of removal must be decremented due to the removal */
1979+ ib[la-1] -= 1;
1980+ }
1981+}
1982+
1983+static gboolean
1984+midori_bookmarks_tree_store_drag_data_delete (GtkTreeDragSource* drag_source,
1985+ GtkTreePath* source_path)
1986+{
1987+ gboolean status = TRUE;
1988+ guint length;
1989+ guint i;
1990+ MidoriBookmarksTreeStore *tree_store = MIDORI_BOOKMARKS_TREE_STORE(drag_source);
1991+ GtkTreeModel* model = GTK_TREE_MODEL(drag_source);
1992+
1993+ if (!tree_store->stock_got_rows)
1994+ return TRUE;
1995+
1996+ length = g_list_length (tree_store->stock_got_rows);
1997+ for (i = 0; i < length; i++)
1998+ {
1999+ GtkTreePath *prev = (GtkTreePath *)g_list_nth_data (tree_store->stock_got_rows, i);
2000+ GtkTreeIter iter;
2001+
2002+ if (gtk_tree_model_get_iter (model, &iter, prev))
2003+ {
2004+ gint j;
2005+ gchar* src_path_str = gtk_tree_path_to_string (prev);
2006+
2007+ /* remove item updating source paths */
2008+ g_free(src_path_str);
2009+
2010+ gtk_tree_store_remove (GTK_TREE_STORE (drag_source), &iter);
2011+
2012+ update_path_list_for_delete (tree_store->stock_got_rows, prev);
2013+ }
2014+ else
2015+ status = FALSE;
2016+ }
2017+
2018+ g_list_free_full (tree_store->stock_got_rows, (GDestroyNotify) gtk_tree_path_free);
2019+ tree_store->stock_got_rows = NULL;
2020+
2021+ return status;
2022+}
2023+
2024+static gboolean
2025+midori_bookmarks_tree_store_get_rows_drag_data (GtkSelectionData *selection_data,
2026+ GtkTreeModel **tree_model,
2027+ GList **rows)
2028+{
2029+ GList *list;
2030+ TreeRowData *trd;
2031+
2032+ g_return_val_if_fail (selection_data != NULL, FALSE);
2033+
2034+ if (tree_model)
2035+ *tree_model = NULL;
2036+
2037+ if (rows)
2038+ *rows = NULL;
2039+
2040+ if (gtk_selection_data_get_target (selection_data) !=
2041+ gdk_atom_intern_static_string ("GTK_TREE_MODEL_MULTI_ROWS"))
2042+ return FALSE;
2043+
2044+ if (gtk_selection_data_get_length (selection_data) < 0)
2045+ return FALSE;
2046+
2047+ trd = (void*) gtk_selection_data_get_data (selection_data);
2048+
2049+ if (tree_model)
2050+ *tree_model = trd->model;
2051+
2052+ if (rows)
2053+ {
2054+ GList *list = NULL;
2055+ gchar *trd_path = g_strdup (trd->path);
2056+ gchar *path_str;
2057+
2058+ path_str = strtok(trd_path, "\n");
2059+ while (path_str && *path_str)
2060+ {
2061+ list = g_list_append (list, gtk_tree_path_new_from_string (path_str));
2062+ path_str = strtok (NULL, "\n");
2063+ }
2064+
2065+ *rows = list;
2066+ g_free (trd_path);
2067+ }
2068+
2069+ return TRUE;
2070+}
2071+
2072+static gboolean
2073+midori_bookmarks_tree_store_row_drop_possible (GtkTreeDragDest* drag_dest,
2074+ GtkTreePath* dest_path,
2075+ GtkSelectionData* selection_data)
2076+{
2077+ GtkTreeModel* dest_model = GTK_TREE_MODEL(drag_dest);
2078+ GtkTreeIter dest_parent;
2079+ GtkTreePath *parent = gtk_tree_path_copy (dest_path);
2080+ GtkTreeIter *dest_parent_p = NULL;
2081+ gboolean row_drop_possible = TRUE;
2082+
2083+ if (gtk_tree_path_up (parent) &&
2084+ gtk_tree_path_get_depth (parent) > 0)
2085+ {
2086+ gtk_tree_model_get_iter (dest_model,
2087+ &dest_parent,
2088+ parent);
2089+ dest_parent_p = &dest_parent;
2090+ }
2091+ gtk_tree_path_free (parent);
2092+
2093+ if (dest_parent_p)
2094+ {
2095+ KatzeItem* item;
2096+
2097+ gtk_tree_model_get (dest_model, dest_parent_p, 0, &item, -1);
2098+
2099+ if (!KATZE_ITEM_IS_FOLDER(item))
2100+ row_drop_possible = FALSE;
2101+
2102+ if (item)
2103+ g_object_unref (item);
2104+ }
2105+
2106+ if (!row_drop_possible)
2107+ return FALSE;
2108+
2109+ if (gtk_selection_data_get_target (selection_data) ==
2110+ gdk_atom_intern_static_string ("GTK_TREE_MODEL_MULTI_ROWS"))
2111+ {
2112+ GtkTreeModel *model;
2113+ GList* rows;
2114+
2115+ if (midori_bookmarks_tree_store_get_rows_drag_data (selection_data,
2116+ &model,
2117+ &rows))
2118+ {
2119+ /* TODO: */
2120+ /* if (destination_is_in_folders (GTK_TREE_MODEL(drag_dest), dest_path) */
2121+ /* && selection_contains_bookmarks (mode, rows)) */
2122+ /* { */
2123+ /* row_drop_possible = FALSE; */
2124+ /* goto done; */
2125+ /* } */
2126+ /* if (destination_is_in_bookmarks (GTK_TREE_MODEL(drag_dest), dest_path) */
2127+ /* && selection_contains_folders (mode, rows)) */
2128+ /* { */
2129+ /* row_drop_possible = FALSE; */
2130+ /* goto done; */
2131+ /* } */
2132+ if (model == GTK_TREE_MODEL(drag_dest))
2133+ {
2134+ gint length = g_list_length (rows);
2135+ gint i;
2136+
2137+ for (i = 0; i < length; i++)
2138+ {
2139+ gint j;
2140+ GtkTreePath *src_path = (GtkTreePath *)g_list_nth_data (rows, i);
2141+
2142+ /* Can't drop into ourself. */
2143+ if (gtk_tree_path_is_ancestor (src_path,
2144+ dest_path))
2145+ {
2146+ row_drop_possible = FALSE;
2147+ goto done;
2148+ }
2149+ update_path_list_for_insert (rows, dest_path);
2150+ }
2151+ }
2152+ done:
2153+ g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
2154+ }
2155+ else
2156+ return FALSE;
2157+ }
2158+ else
2159+ return FALSE;
2160+
2161+ return row_drop_possible;
2162+}
2163+
2164+static void
2165+copy_node_data (GtkTreeStore *src_store,
2166+ GtkTreeIter *src_iter,
2167+ GtkTreeStore *dest_store,
2168+ GtkTreeIter *dest_iter)
2169+{
2170+ gint i;
2171+ gint n_columns;
2172+ GtkTreeModel* src_model = GTK_TREE_MODEL (src_store);
2173+
2174+ n_columns = gtk_tree_model_get_n_columns (src_model);
2175+
2176+ for (i = 0; i < n_columns; i++)
2177+ {
2178+ void *item;
2179+
2180+ gtk_tree_model_get (src_model, src_iter, i, &item, -1);
2181+ gtk_tree_store_set (dest_store, dest_iter, i, item, -1);
2182+ }
2183+}
2184+
2185+static void
2186+recursive_node_copy (GtkTreeStore *src_store,
2187+ GtkTreeIter *src_iter,
2188+ GtkTreeStore *dest_store,
2189+ GtkTreeIter *dest_iter)
2190+{
2191+ GtkTreeIter child;
2192+ GtkTreeModel *src_model = GTK_TREE_MODEL (src_store);
2193+
2194+ copy_node_data (src_store, src_iter, dest_store, dest_iter);
2195+
2196+ if (gtk_tree_model_iter_children (src_model, &child, src_iter))
2197+ {
2198+ /* Need to create children and recurse. Note our
2199+ * dependence on persistent iterators here.
2200+ */
2201+ do
2202+ {
2203+ GtkTreeIter copy;
2204+
2205+ /* Gee, a really slow algorithm... ;-) FIXME */
2206+ gtk_tree_store_append (dest_store,
2207+ &copy,
2208+ dest_iter);
2209+
2210+ recursive_node_copy (src_store, &child, dest_store, &copy);
2211+ }
2212+ while (gtk_tree_model_iter_next (src_model, &child));
2213+ }
2214+}
2215+
2216+static gboolean
2217+midori_bookmarks_tree_store_drag_data_received (GtkTreeDragDest *drag_dest,
2218+ GtkTreePath *dest_path,
2219+ GtkSelectionData *selection_data)
2220+{
2221+ gboolean status = TRUE;
2222+
2223+ g_return_val_if_fail (selection_data != NULL, FALSE);
2224+ g_return_val_if_fail (GTK_IS_TREE_MODEL (drag_dest), FALSE);
2225+ g_return_val_if_fail (dest_path != NULL, FALSE);
2226+
2227+ if (gtk_selection_data_get_target (selection_data) ==
2228+ gdk_atom_intern_static_string ("GTK_TREE_MODEL_MULTI_ROWS"))
2229+ {
2230+ GtkTreeStore *dest_store = GTK_TREE_STORE (drag_dest);
2231+ GtkTreeModel *dest_model = GTK_TREE_MODEL (drag_dest);
2232+ GtkTreeModel *src_model;
2233+ GList* rows;
2234+
2235+ if (midori_bookmarks_tree_store_get_rows_drag_data (selection_data,
2236+ &src_model,
2237+ &rows))
2238+ {
2239+ GtkTreeStore *src_store = GTK_TREE_STORE (src_model);
2240+ GtkTreePath *prev = gtk_tree_path_copy (dest_path);
2241+
2242+ gint count = 0;
2243+ gint length = g_list_length (rows);
2244+ gint i;
2245+
2246+ for (i = 0; i < length; i++)
2247+ {
2248+ gint j;
2249+ GtkTreeIter dest_iter;
2250+ GtkTreeIter src_iter;
2251+ GtkTreePath *src_path = (GtkTreePath *)g_list_nth_data (rows, i);
2252+
2253+ if (!gtk_tree_model_get_iter (src_model, &src_iter, src_path))
2254+ continue;
2255+
2256+ /* Get the path to insert _after_ (dest is the path to insert _before_) */
2257+ if (i == 0)
2258+ {
2259+ if (!gtk_tree_path_prev (prev))
2260+ { /* Get the parent, NULL if parent is the root */
2261+ GtkTreeIter dest_parent;
2262+ GtkTreePath *parent = gtk_tree_path_copy (dest_path);
2263+ GtkTreeIter *dest_parent_p = NULL;
2264+
2265+ if (gtk_tree_path_up (parent) &&
2266+ gtk_tree_path_get_depth (parent) > 0)
2267+ {
2268+ gtk_tree_model_get_iter (dest_model,
2269+ &dest_parent,
2270+ parent);
2271+ dest_parent_p = &dest_parent;
2272+ }
2273+ gtk_tree_path_free (parent);
2274+
2275+ gtk_tree_store_prepend (dest_store, &dest_iter, dest_parent_p);
2276+ }
2277+ else if (gtk_tree_model_get_iter (dest_model, &dest_iter, prev))
2278+ {
2279+ GtkTreeIter tmp_iter = dest_iter;
2280+
2281+ gtk_tree_store_insert_after (dest_store, &dest_iter, NULL,
2282+ &tmp_iter);
2283+ }
2284+ }
2285+ else if (gtk_tree_model_get_iter (dest_model, &dest_iter, prev))
2286+ {
2287+ GtkTreeIter tmp_iter = dest_iter;
2288+
2289+ gtk_tree_store_insert_after (dest_store, &dest_iter, NULL,
2290+ &tmp_iter);
2291+ }
2292+
2293+ gtk_tree_path_free (prev);
2294+
2295+ recursive_node_copy (src_store, &src_iter, dest_store, &dest_iter);
2296+ count++;
2297+
2298+ prev = gtk_tree_model_get_path (dest_model, &dest_iter);
2299+
2300+ if (src_store != dest_store)
2301+ continue;
2302+
2303+ update_path_list_for_insert (rows, prev);
2304+ }
2305+
2306+ gtk_tree_path_free (prev);
2307+
2308+ g_assert (count == length);
2309+
2310+ if (src_store == dest_store)
2311+ {
2312+ MidoriBookmarksTreeStore *tree_store = MIDORI_BOOKMARKS_TREE_STORE(src_store);
2313+
2314+ g_list_free_full (tree_store->stock_got_rows, (GDestroyNotify) gtk_tree_path_free);
2315+ tree_store->stock_got_rows = rows;
2316+ }
2317+ else
2318+ g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
2319+ }
2320+ }
2321+
2322+ return status;
2323+}
2324+
2325+static void
2326+midori_bookmarks_set_item_positon (GtkTreeModel* model,
2327+ GtkTreeIter* iter,
2328+ gint64 parentid,
2329+ MidoriBookmarks* bookmarks)
2330+{
2331+ KatzeItem* item;
2332+ gint position = 0;
2333+ GtkTreeIter next = *iter;
2334+
2335+ do {
2336+ gboolean update = FALSE;
2337+ gtk_tree_model_get (model, &next, 0, &item, -1);
2338+
2339+ if (!KATZE_IS_ITEM (item))
2340+ continue;
2341+
2342+ if (position != katze_item_get_meta_integer (item, "pos_panel"))
2343+ {
2344+ katze_item_set_meta_integer (item, "pos_panel", position);
2345+ update = TRUE;
2346+ }
2347+ if (parentid != katze_item_get_meta_integer (item, "parentid"))
2348+ {
2349+ katze_item_set_meta_integer (item, "parentid", parentid);
2350+ update = TRUE;
2351+ }
2352+
2353+ if (update)
2354+ midori_bookmarks_update_item (bookmarks, item);
2355+
2356+ position++;
2357+
2358+ g_object_unref (item);
2359+ }
2360+ while (gtk_tree_model_iter_next (model, &next));
2361+}
2362+
2363+static void
2364+midori_bookmarks_row_added_cb (GtkTreeModel* model,
2365+ GtkTreePath* path,
2366+ GtkTreeIter* iter,
2367+ MidoriBookmarks* bookmarks)
2368+{
2369+ if (!g_hash_table_size (bookmarks->updated_items)
2370+ || (!g_list_length (bookmarks->added_paths)))
2371+ g_idle_add (midori_bookmarks_idle, bookmarks);
2372+
2373+ update_path_list_for_insert (bookmarks->added_paths, path);
2374+
2375+ bookmarks->added_paths = g_list_append (bookmarks->added_paths, gtk_tree_path_copy (path));
2376+}
2377+
2378+static void
2379+update_items_for_parent (GtkTreeModel* model,
2380+ GtkTreePath* path,
2381+ MidoriBookmarks* bookmarks)
2382+{
2383+ if ((gtk_tree_path_get_depth (path) > 1)
2384+ && gtk_tree_path_up (path))
2385+ {
2386+ KatzeItem *item;
2387+ GtkTreeIter parent;
2388+ GtkTreeIter local_iter;
2389+ gint64 id;
2390+
2391+ if (!gtk_tree_model_get_iter (model, &parent, path))
2392+ return;
2393+
2394+ gtk_tree_model_get (model, &parent, 0, &item, -1);
2395+
2396+ g_assert (KATZE_ITEM_IS_FOLDER (item));
2397+
2398+ id = katze_item_get_meta_integer (item, "id");
2399+
2400+ g_object_unref (item);
2401+
2402+ if (gtk_tree_model_iter_children (model, &local_iter, &parent))
2403+ midori_bookmarks_set_item_positon(model, &local_iter, id, bookmarks);
2404+ }
2405+ else
2406+ {
2407+ GtkTreeIter local_iter;
2408+ if (gtk_tree_model_get_iter_first (model, &local_iter))
2409+ midori_bookmarks_set_item_positon(model, &local_iter, 0, bookmarks);
2410+ }
2411+}
2412+
2413+static void
2414+midori_bookmarks_row_deleted_cb (GtkTreeModel* model,
2415+ GtkTreePath* path,
2416+ MidoriBookmarks* bookmarks)
2417+{
2418+ GList* found;
2419+
2420+ if (!g_hash_table_size (bookmarks->updated_items)
2421+ || (!g_list_length (bookmarks->added_paths)))
2422+ g_idle_add (midori_bookmarks_idle, bookmarks);
2423+
2424+ while (found = g_list_find_custom (bookmarks->added_paths,
2425+ path, (GCompareFunc)gtk_tree_path_compare))
2426+ {
2427+ bookmarks->added_paths = g_list_remove(bookmarks->added_paths, found->data);
2428+ gtk_tree_path_free ((GtkTreePath*)found->data);
2429+ }
2430+
2431+ update_path_list_for_delete (bookmarks->added_paths, path);
2432+ update_items_for_parent (model, path, bookmarks);
2433+}
2434
2435 static void
2436 midori_bookmarks_row_changed_cb (GtkTreeModel* model,
2437@@ -356,44 +1443,59 @@
2438 GtkTreeIter* iter,
2439 MidoriBookmarks* bookmarks)
2440 {
2441- KatzeItem* item;
2442- GtkTreeIter parent;
2443- KatzeItem* new_parent = NULL;
2444- gint64 parentid;
2445-
2446- gtk_tree_model_get (model, iter, 0, &item, -1);
2447-
2448- if (gtk_tree_model_iter_parent (model, &parent, iter))
2449- {
2450- gtk_tree_model_get (model, &parent, 0, &new_parent, -1);
2451-
2452- /* Bookmarks must not be moved into non-folder items */
2453- if (!KATZE_ITEM_IS_FOLDER (new_parent))
2454- parentid = 0;
2455- else
2456- parentid = katze_item_get_meta_integer (new_parent, "id");
2457- }
2458- else
2459- parentid = 0;
2460-
2461- katze_array_remove_item (bookmarks->array, item);
2462- katze_item_set_meta_integer (item, "parentid", parentid);
2463- katze_array_add_item (bookmarks->array, item);
2464-
2465- g_object_unref (item);
2466- if (new_parent)
2467- g_object_unref (new_parent);
2468+ update_items_for_parent (model, path, bookmarks);
2469 }
2470
2471 static void
2472-midori_bookmarks_add_clicked_cb (GtkWidget* toolitem)
2473+midori_bookmarks_add_clicked_cb (GtkWidget* toolitem,
2474+ MidoriBookmarks* bookmarks)
2475 {
2476 MidoriBrowser* browser = midori_browser_get_for_widget (toolitem);
2477- /* FIXME: Take selected folder into account */
2478+ GtkTreeView* treeview = GTK_TREE_VIEW (bookmarks->treeview);
2479+ GtkTreeModel* model;
2480+ GtkTreeIter iter;
2481+ KatzeItem* parent = NULL;
2482+
2483+ if (katze_tree_view_get_selected_iter (treeview,
2484+ &model, &iter))
2485+ {
2486+ gboolean done = FALSE;
2487+ while (!done)
2488+ {
2489+ gtk_tree_model_get (model, &iter, 0, &parent, -1);
2490+
2491+ if (KATZE_ITEM_IS_FOLDER (parent))
2492+ {
2493+ GtkTreePath* path = gtk_tree_model_get_path(model, &iter);
2494+
2495+ if (!gtk_tree_view_row_expanded (treeview, path))
2496+ gtk_tree_view_expand_row (treeview, path, FALSE);
2497+
2498+ gtk_tree_path_free (path);
2499+ done = TRUE;
2500+ }
2501+ else
2502+ {
2503+ GtkTreeIter child = iter;
2504+
2505+ if (parent) g_object_unref (parent);
2506+ parent = NULL;
2507+
2508+ if (!gtk_tree_model_iter_parent (model, &iter, &child))
2509+ {
2510+ done = TRUE;
2511+ }
2512+ }
2513+ }
2514+ }
2515+
2516 if (g_str_equal (gtk_widget_get_name (toolitem), "BookmarkFolderAdd"))
2517- midori_browser_edit_bookmark_dialog_new (browser, NULL, TRUE, TRUE, toolitem);
2518+ midori_browser_edit_bookmark_dialog_new (browser, parent, TRUE, TRUE, toolitem);
2519 else
2520- midori_browser_edit_bookmark_dialog_new (browser, NULL, TRUE, FALSE, toolitem);
2521+ midori_browser_edit_bookmark_dialog_new (browser, parent, TRUE, FALSE, toolitem);
2522+
2523+ if (parent)
2524+ g_object_unref (parent);
2525 }
2526
2527 static void
2528@@ -408,24 +1510,15 @@
2529 {
2530 KatzeItem* item;
2531 MidoriBrowser* browser;
2532- gint64 parentid;
2533
2534 gtk_tree_model_get (model, &iter, 0, &item, -1);
2535
2536 g_assert (!KATZE_ITEM_IS_SEPARATOR (item));
2537
2538 browser = midori_browser_get_for_widget (bookmarks->treeview);
2539- parentid = katze_item_get_meta_integer (item, "parentid");
2540 midori_browser_edit_bookmark_dialog_new (
2541 browser, item, FALSE, KATZE_ITEM_IS_FOLDER (item), NULL);
2542
2543- if (katze_item_get_meta_integer (item, "parentid") != parentid)
2544- {
2545- gtk_tree_store_clear (GTK_TREE_STORE (model));
2546- midori_bookmarks_read_from_db_to_model (bookmarks, GTK_TREE_STORE (model),
2547- NULL, 0, NULL);
2548- }
2549-
2550 g_object_unref (item);
2551 }
2552 }
2553@@ -433,12 +1526,226 @@
2554 static void
2555 midori_bookmarks_toolbar_update (MidoriBookmarks *bookmarks)
2556 {
2557- gboolean selected;
2558+ gint selected;
2559
2560- selected = katze_tree_view_get_selected_iter (
2561+ selected = katze_tree_view_get_selected_rows (
2562 GTK_TREE_VIEW (bookmarks->treeview), NULL, NULL);
2563- gtk_widget_set_sensitive (GTK_WIDGET (bookmarks->delete), selected);
2564- gtk_widget_set_sensitive (GTK_WIDGET (bookmarks->edit), selected);
2565+ gtk_widget_set_sensitive (
2566+ GTK_WIDGET (bookmarks->delete), (selected > 0 ? TRUE : FALSE));
2567+ gtk_widget_set_sensitive (
2568+ GTK_WIDGET (bookmarks->edit), (selected == 1 ? TRUE : FALSE));
2569+}
2570+
2571+static void
2572+midori_bookmarks_statusbar_update (MidoriBookmarks *bookmarks)
2573+{
2574+ gchar* text = NULL;
2575+ GtkTreeModel* model;
2576+ GList *rows;
2577+ gint selected;
2578+
2579+ selected = katze_tree_view_get_selected_rows (
2580+ GTK_TREE_VIEW (bookmarks->treeview), &model, &rows);
2581+
2582+ if (selected == 1)
2583+ {
2584+ GtkTreePath* path = (GtkTreePath *)g_list_nth_data (rows, 0);
2585+ GtkTreeIter iter;
2586+ KatzeItem* item;
2587+ const gchar* name;
2588+
2589+ if (!gtk_tree_model_get_iter (model, &iter, path))
2590+ return;
2591+
2592+ gtk_tree_model_get (model, &iter, 0, &item, -1);
2593+
2594+ g_assert (!KATZE_ITEM_IS_SEPARATOR (item));
2595+
2596+ name = katze_item_get_name (item);
2597+
2598+ if (KATZE_ITEM_IS_FOLDER (item))
2599+ {
2600+ gint child_folders_count = midori_array_count_recursive(
2601+ bookmarks->array,
2602+ "uri = ''",
2603+ NULL,
2604+ item,
2605+ FALSE);
2606+ gint child_bookmarks_count = midori_array_count_recursive(
2607+ bookmarks->array,
2608+ "uri <> ''",
2609+ NULL,
2610+ item,
2611+ FALSE);
2612+
2613+ if (!child_bookmarks_count && !child_folders_count)
2614+ text = g_strdup_printf(
2615+ _("%s : empty folder"),
2616+ name);
2617+ else if (!child_bookmarks_count && (child_folders_count == 1))
2618+ text = g_strdup_printf(
2619+ _("%s : folder, one subfolder and no bookmark"),
2620+ name);
2621+ else if (!child_bookmarks_count && child_folders_count)
2622+ text = g_strdup_printf(
2623+ _("%s : folder, %d subfolders and no bookmark"),
2624+ name, child_folders_count);
2625+ else if ((child_bookmarks_count == 1) && !child_folders_count)
2626+ text = g_strdup_printf(
2627+ _("%s : folder, one bookmark"),
2628+ name);
2629+ else if ((child_bookmarks_count == 1) && (child_folders_count == 1))
2630+ text = g_strdup_printf(
2631+ _("%s : folder, one bookmark and one subfolder"),
2632+ name);
2633+ else if ((child_bookmarks_count == 1) && child_folders_count)
2634+ text = g_strdup_printf(
2635+ _("%s : folder, one bookmark and %d subfolders"),
2636+ name, child_folders_count);
2637+ else if (child_bookmarks_count && !child_folders_count)
2638+ text = g_strdup_printf(
2639+ _("%s : folder, %d bookmarks"),
2640+ name, child_bookmarks_count);
2641+ else if (child_bookmarks_count && (child_folders_count == 1))
2642+ text = g_strdup_printf(
2643+ _("%s : folder, %d bookmarks and one subfolder"),
2644+ name, child_bookmarks_count);
2645+ else if (child_bookmarks_count && child_folders_count)
2646+ text = g_strdup_printf(
2647+ _("%s : folder, %d bookmarks and %d subfolders"),
2648+ name, child_bookmarks_count, child_folders_count);
2649+ }
2650+ else if (KATZE_ITEM_IS_BOOKMARK (item))
2651+ {
2652+ const gchar* uri = katze_item_get_uri (item);
2653+
2654+ if (katze_item_get_meta_boolean (item, "app"))
2655+ text = g_strdup_printf (_("%s : web application bookmark to : %s"), name, uri);
2656+ else
2657+ text = g_strdup_printf (_("%s : bookmark to : %s"), name, uri);
2658+ }
2659+
2660+ g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
2661+ }
2662+ else if (selected > 1)
2663+ {
2664+ gint i;
2665+ gint selected_folders_count = 0;
2666+ gint selected_bookmarks_count = 0;
2667+
2668+ for (i = 0 ; i < selected ; i++)
2669+ {
2670+ GtkTreeIter iter;
2671+ KatzeItem* item;
2672+
2673+ g_return_if_fail (gtk_tree_model_get_iter (
2674+ model, &iter, (GtkTreePath *)g_list_nth_data (rows, i)));
2675+
2676+ gtk_tree_model_get (model, &iter, 0, &item, -1);
2677+
2678+ g_assert (!KATZE_ITEM_IS_SEPARATOR (item));
2679+
2680+ if (KATZE_ITEM_IS_FOLDER (item))
2681+ {
2682+ selected_folders_count++;
2683+ }
2684+ else
2685+ {
2686+ selected_bookmarks_count++;
2687+ }
2688+ }
2689+
2690+ if (!selected_bookmarks_count && !selected_folders_count)
2691+ g_assert_not_reached ();
2692+ else if (!selected_bookmarks_count && (selected_folders_count == 1))
2693+ text = g_strdup_printf(
2694+ _("one folder and no bookmark selected"));
2695+ else if (!selected_bookmarks_count && selected_folders_count)
2696+ text = g_strdup_printf(
2697+ _("%d folders and no bookmark selected"),
2698+ selected_folders_count);
2699+ else if ((selected_bookmarks_count == 1) && !selected_folders_count)
2700+ text = g_strdup_printf(
2701+ _("one bookmark selected"));
2702+ else if ((selected_bookmarks_count == 1) && (selected_folders_count == 1))
2703+ text = g_strdup_printf(
2704+ _("one bookmark and one folder selected"));
2705+ else if ((selected_bookmarks_count == 1) && selected_folders_count)
2706+ text = g_strdup_printf(
2707+ _("one bookmark and %d folders selected"),
2708+ selected_folders_count);
2709+ else if (selected_bookmarks_count && !selected_folders_count)
2710+ text = g_strdup_printf(
2711+ _("%d bookmarks selected"),
2712+ selected_bookmarks_count);
2713+ else if (selected_bookmarks_count && (selected_folders_count == 1))
2714+ text = g_strdup_printf(
2715+ _("%d bookmarks and one folder selected"),
2716+ selected_bookmarks_count);
2717+ else if (selected_bookmarks_count && selected_folders_count)
2718+ text = g_strdup_printf(
2719+ _("%d bookmarks and %d folders selected"),
2720+ selected_bookmarks_count, selected_folders_count);
2721+
2722+ g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
2723+ }
2724+ else
2725+ {
2726+ gint child_folders_count = midori_array_count_recursive(
2727+ bookmarks->array,
2728+ "uri = ''",
2729+ NULL,
2730+ NULL,
2731+ FALSE);
2732+ gint child_bookmarks_count = midori_array_count_recursive(
2733+ bookmarks->array,
2734+ "uri <> ''",
2735+ NULL,
2736+ NULL,
2737+ FALSE);
2738+
2739+ if (!child_bookmarks_count && !child_folders_count)
2740+ text = g_strdup_printf(
2741+ _("Bookmarks : empty"));
2742+ else if (!child_bookmarks_count && (child_folders_count == 1))
2743+ text = g_strdup_printf(
2744+ _("Bookmarks : one folder and no bookmark"));
2745+ else if (!child_bookmarks_count && child_folders_count)
2746+ text = g_strdup_printf(
2747+ _("Bookmarks : %d folders and no bookmark"),
2748+ child_folders_count);
2749+ else if ((child_bookmarks_count == 1) && !child_folders_count)
2750+ text = g_strdup_printf(
2751+ _("Bookmarks : one bookmark"));
2752+ else if ((child_bookmarks_count == 1) && (child_folders_count == 1))
2753+ text = g_strdup_printf(
2754+ _("Bookmarks : one bookmark and one folder"));
2755+ else if ((child_bookmarks_count == 1) && child_folders_count)
2756+ text = g_strdup_printf(
2757+ _("Bookmarks : one bookmark and %d folders"),
2758+ child_folders_count);
2759+ else if (child_bookmarks_count && !child_folders_count)
2760+ text = g_strdup_printf(
2761+ _("Bookmarks : %d bookmarks"),
2762+ child_bookmarks_count);
2763+ else if (child_bookmarks_count && (child_folders_count == 1))
2764+ text = g_strdup_printf(
2765+ _("Bookmarks : %d bookmarks and one folder"),
2766+ child_bookmarks_count);
2767+ else if (child_bookmarks_count && child_folders_count)
2768+ text = g_strdup_printf(
2769+ _("Bookmarks : %d bookmarks and %d folders"),
2770+ child_bookmarks_count, child_folders_count);
2771+ }
2772+
2773+ if (text)
2774+ {
2775+ MidoriBrowser* browser = midori_browser_get_for_widget (bookmarks->treeview);
2776+
2777+ g_object_set (browser, "statusbar-text", text, NULL);
2778+
2779+ g_free(text);
2780+ }
2781 }
2782
2783 gboolean
2784@@ -458,7 +1765,8 @@
2785
2786 sqlcmd = sqlite3_mprintf (
2787 "UPDATE bookmarks SET "
2788- "parentid=%q, title='%q', uri='%q', desc='%q', toolbar=%d, app=%d "
2789+ "parentid=%q, title='%q', uri='%q', desc='%q', toolbar=%d, app=%d, "
2790+ "pos_panel=%" G_GINT64_FORMAT ", pos_bar=%" G_GINT64_FORMAT " "
2791 "WHERE id = %" G_GINT64_FORMAT ";",
2792 parentid,
2793 katze_item_get_name (item),
2794@@ -466,8 +1774,14 @@
2795 katze_str_non_null (katze_item_get_meta_string (item, "desc")),
2796 katze_item_get_meta_boolean (item, "toolbar"),
2797 katze_item_get_meta_boolean (item, "app"),
2798+ katze_item_get_meta_integer (item, "pos_panel"),
2799+ katze_item_get_meta_integer (item, "pos_bar"),
2800 katze_item_get_meta_integer (item, "id"));
2801
2802+ /* g_printf("update: %d -> %d\n", */
2803+ /* (gint)katze_item_get_meta_integer (item, "id"), */
2804+ /* (gint)katze_item_get_meta_integer (item, "pos_panel")); */
2805+
2806 updated = TRUE;
2807 if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) != SQLITE_OK)
2808 {
2809@@ -487,23 +1801,38 @@
2810 MidoriBookmarks* bookmarks)
2811 {
2812 GtkTreeModel* model;
2813- GtkTreeIter iter;
2814+ GList* rows;
2815+ gint length;
2816
2817- if (katze_tree_view_get_selected_iter (GTK_TREE_VIEW (bookmarks->treeview),
2818- &model, &iter))
2819+ length = katze_tree_view_get_selected_rows(GTK_TREE_VIEW (bookmarks->treeview),
2820+ &model, &rows);
2821+ while (length)
2822 {
2823- KatzeItem* item;
2824-
2825- gtk_tree_model_get (model, &iter, 0, &item, -1);
2826-
2827- /* Manually remove the iter and block clearing the treeview */
2828- gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
2829- g_signal_handlers_block_by_func (bookmarks->array,
2830- midori_bookmarks_remove_item_cb, bookmarks);
2831- katze_array_remove_item (bookmarks->array, item);
2832- g_signal_handlers_unblock_by_func (bookmarks->array,
2833- midori_bookmarks_remove_item_cb, bookmarks);
2834- g_object_unref (item);
2835+ gint new_length;
2836+ GtkTreeIter iter;
2837+
2838+ if (gtk_tree_model_get_iter (
2839+ model, &iter, (GtkTreePath *)g_list_nth_data (rows, 0)))
2840+ {
2841+ KatzeItem* item;
2842+
2843+ gtk_tree_model_get (model, &iter, 0, &item, -1);
2844+
2845+ if (KATZE_IS_ITEM(item))
2846+ {
2847+ katze_array_remove_item (bookmarks->array, item);
2848+ }
2849+
2850+ g_object_unref (item);
2851+ }
2852+
2853+ g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
2854+
2855+ new_length = katze_tree_view_get_selected_rows(GTK_TREE_VIEW (bookmarks->treeview),
2856+ &model, &rows);
2857+
2858+ /* avoid infinite loop due to delete failure */
2859+ length = (new_length == length) ? 0 : new_length;
2860 }
2861 }
2862
2863@@ -547,6 +1876,7 @@
2864 gtk_widget_show (GTK_WIDGET (toolitem));
2865 bookmarks->delete = GTK_WIDGET (toolitem);
2866 midori_bookmarks_toolbar_update (bookmarks);
2867+ midori_bookmarks_statusbar_update (bookmarks);
2868 toolitem = gtk_separator_tool_item_new ();
2869 gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (toolitem), FALSE);
2870 gtk_tool_item_set_expand (toolitem, TRUE);
2871@@ -599,10 +1929,18 @@
2872 midori_bookmarks_read_from_db_to_model (bookmarks, GTK_TREE_STORE (model), NULL, 0, NULL);
2873 g_signal_connect_after (bookmarks->array, "add-item",
2874 G_CALLBACK (midori_bookmarks_add_item_cb), bookmarks);
2875+ g_signal_connect_after (bookmarks->array, "update-item",
2876+ G_CALLBACK (midori_bookmarks_update_item_cb), bookmarks);
2877 g_signal_connect (bookmarks->array, "remove-item",
2878 G_CALLBACK (midori_bookmarks_remove_item_cb), bookmarks);
2879 g_signal_connect (bookmarks->array, "update",
2880 G_CALLBACK (midori_bookmarks_update_cb), bookmarks);
2881+ g_signal_connect_after (model, "row-inserted",
2882+ G_CALLBACK (midori_bookmarks_row_added_cb),
2883+ bookmarks);
2884+ g_signal_connect_after (model, "row-deleted",
2885+ G_CALLBACK (midori_bookmarks_row_deleted_cb),
2886+ bookmarks);
2887 g_signal_connect_after (model, "row-changed",
2888 G_CALLBACK (midori_bookmarks_row_changed_cb),
2889 bookmarks);
2890@@ -745,7 +2083,10 @@
2891 else if (!KATZE_ITEM_IS_FOLDER (item) && strcmp (stock_id, GTK_STOCK_DELETE))
2892 gtk_widget_set_sensitive (menuitem, uri != NULL);
2893 g_object_set_data (G_OBJECT (menuitem), "KatzeItem", item);
2894- g_signal_connect (menuitem, "activate", G_CALLBACK (callback), bookmarks);
2895+ if (callback)
2896+ g_signal_connect (menuitem, "activate", G_CALLBACK (callback), bookmarks);
2897+ else
2898+ gtk_widget_set_sensitive (menuitem, FALSE);
2899 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
2900 gtk_widget_show (menuitem);
2901 }
2902@@ -766,39 +2107,125 @@
2903 }
2904 }
2905
2906+static GtkWidget*
2907+midori_bookmarks_open_bookmark_in_tab (KatzeItem *item,
2908+ MidoriBrowser* browser)
2909+{
2910+ const gchar* uri = katze_item_get_uri (item);
2911+
2912+ if (!uri || !*uri)
2913+ return NULL;
2914+
2915+ return midori_browser_add_item (browser, item);
2916+}
2917+
2918+static GtkWidget*
2919+midori_bookmarks_open_folder_in_tab (gint64 parentid,
2920+ MidoriBookmarks* bookmarks,
2921+ MidoriBrowser* browser)
2922+{
2923+ GtkWidget* last_view = NULL;
2924+ KatzeArray* array;
2925+
2926+ array = midori_bookmarks_read_from_db (bookmarks, parentid, NULL);
2927+
2928+ if (KATZE_IS_ARRAY (array))
2929+ {
2930+ KatzeItem* child;
2931+
2932+ KATZE_ARRAY_FOREACH_ITEM (child, array)
2933+ {
2934+ GtkWidget* view = midori_bookmarks_open_bookmark_in_tab (child, browser);
2935+ if (view)
2936+ last_view = view;
2937+ }
2938+ }
2939+
2940+ return last_view;
2941+}
2942+
2943+
2944+static void
2945+midori_bookmarks_run_web_application_cb (GtkWidget* menuitem,
2946+ MidoriBookmarks* bookmarks)
2947+{
2948+ KatzeItem* item;
2949+ const gchar* uri;
2950+
2951+ item = (KatzeItem*)g_object_get_data (G_OBJECT (menuitem), "KatzeItem");
2952+
2953+ if ((uri = katze_item_get_uri (item)) && *uri)
2954+ {
2955+ gchar* uri_fixed = sokoke_magic_uri (uri, TRUE, FALSE);
2956+ if (!uri_fixed)
2957+ uri_fixed = g_strdup (uri);
2958+
2959+ sokoke_spawn_app (uri_fixed, FALSE);
2960+
2961+ g_free (uri_fixed);
2962+ }
2963+}
2964+
2965 static void
2966 midori_bookmarks_open_in_tab_activate_cb (GtkWidget* menuitem,
2967 MidoriBookmarks* bookmarks)
2968 {
2969- KatzeItem* item;
2970- const gchar* uri;
2971-
2972- item = (KatzeItem*)g_object_get_data (G_OBJECT (menuitem), "KatzeItem");
2973- if (KATZE_ITEM_IS_FOLDER (item))
2974- {
2975- KatzeItem* child;
2976- KatzeArray* array;
2977-
2978- array = midori_bookmarks_read_from_db (bookmarks,
2979- katze_item_get_meta_integer (item, "parentid"), NULL);
2980-
2981- g_return_if_fail (KATZE_IS_ARRAY (array));
2982- KATZE_ARRAY_FOREACH_ITEM (child, array)
2983- {
2984- if ((uri = katze_item_get_uri (child)) && *uri)
2985- {
2986- MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (bookmarks));
2987- GtkWidget* view = midori_browser_add_item (browser, child);
2988- midori_browser_set_current_tab_smartly (browser, view);
2989- }
2990- }
2991- }
2992- else if ((uri = katze_item_get_uri (item)) && *uri)
2993- {
2994- MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (bookmarks));
2995- GtkWidget* view = midori_browser_add_item (browser, item);
2996- midori_browser_set_current_tab_smartly (browser, view);
2997- }
2998+ GtkWidget* last_view = NULL;
2999+ MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (bookmarks));
3000+ GtkTreeModel* model;
3001+ GList* rows;
3002+ gint length;
3003+ gint i;
3004+
3005+ length = katze_tree_view_get_selected_rows (GTK_TREE_VIEW (bookmarks->treeview),
3006+ &model, &rows);
3007+
3008+ if (!length)
3009+ {
3010+ KatzeItem* root = KATZE_ITEM (bookmarks->array);
3011+ KatzeItem* item = KATZE_ITEM (g_object_get_data (G_OBJECT (menuitem), "KatzeItem"));
3012+
3013+ if (item != root)
3014+ return;
3015+
3016+ last_view = midori_bookmarks_open_folder_in_tab (0, bookmarks, browser);
3017+ }
3018+ else
3019+ {
3020+ for (i = 0 ; i < length; i++)
3021+ {
3022+ GtkTreeIter iter;
3023+
3024+ if (gtk_tree_model_get_iter (
3025+ model, &iter, (GtkTreePath *)g_list_nth_data (rows, i)))
3026+ {
3027+ GtkWidget* view = NULL;
3028+ KatzeItem* item;
3029+ const gchar* uri;
3030+
3031+ gtk_tree_model_get (model, &iter, 0, &item, -1);
3032+
3033+ if (KATZE_ITEM_IS_SEPARATOR(item))
3034+ continue;
3035+
3036+ if (KATZE_ITEM_IS_FOLDER (item))
3037+ view = midori_bookmarks_open_folder_in_tab (
3038+ katze_item_get_meta_integer (item, "id"), bookmarks, browser);
3039+ else
3040+ view = midori_bookmarks_open_bookmark_in_tab (item, browser);
3041+
3042+ g_object_unref (item);
3043+
3044+ if (view)
3045+ last_view = view;
3046+ }
3047+ }
3048+
3049+ g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
3050+ }
3051+
3052+ if (last_view)
3053+ midori_browser_set_current_tab_smartly (browser, last_view);
3054 }
3055
3056 static void
3057@@ -826,32 +2253,113 @@
3058 KatzeItem* item,
3059 MidoriBookmarks* bookmarks)
3060 {
3061+ KatzeItem* root = KATZE_ITEM (bookmarks->array);
3062 GtkWidget* menu;
3063 GtkWidget* menuitem;
3064
3065 menu = gtk_menu_new ();
3066- if (KATZE_ITEM_IS_FOLDER (item))
3067- midori_bookmarks_popup_item (menu,
3068- STOCK_TAB_NEW, _("Open all in _Tabs"),
3069- item, midori_bookmarks_open_in_tab_activate_cb, bookmarks);
3070+ if ((item == root)
3071+ || KATZE_ITEM_IS_FOLDER (item))
3072+ {
3073+ gint child_bookmarks_count = midori_array_count_recursive(
3074+ bookmarks->array,
3075+ "uri <> ''",
3076+ NULL,
3077+ item,
3078+ FALSE);
3079+
3080+ if (!child_bookmarks_count)
3081+ midori_bookmarks_popup_item (menu,
3082+ STOCK_TAB_NEW, _("Open all in _Tabs"),
3083+ item, NULL, bookmarks);
3084+ else
3085+ midori_bookmarks_popup_item (menu,
3086+ STOCK_TAB_NEW, _("Open all in _Tabs"),
3087+ item, midori_bookmarks_open_in_tab_activate_cb, bookmarks);
3088+ }
3089 else
3090 {
3091- midori_bookmarks_popup_item (menu, GTK_STOCK_OPEN, NULL,
3092- item, midori_bookmarks_open_activate_cb, bookmarks);
3093+ if (katze_item_get_meta_boolean (item, "app"))
3094+ {
3095+ midori_bookmarks_popup_item (menu,
3096+ GTK_STOCK_EXECUTE, _("Open as Web A_pplication"),
3097+ item, midori_bookmarks_run_web_application_cb, bookmarks);
3098+ midori_bookmarks_popup_item (menu, GTK_STOCK_OPEN, NULL,
3099+ item, midori_bookmarks_open_activate_cb, bookmarks);
3100+ }
3101+ else
3102+ {
3103+ midori_bookmarks_popup_item (menu, GTK_STOCK_OPEN, NULL,
3104+ item, midori_bookmarks_open_activate_cb, bookmarks);
3105+ midori_bookmarks_popup_item (menu,
3106+ GTK_STOCK_EXECUTE, _("Open as Web A_pplication"),
3107+ item, midori_bookmarks_run_web_application_cb, bookmarks);
3108+ }
3109+
3110 midori_bookmarks_popup_item (menu, STOCK_TAB_NEW, _("Open in New _Tab"),
3111 item, midori_bookmarks_open_in_tab_activate_cb, bookmarks);
3112 midori_bookmarks_popup_item (menu, STOCK_WINDOW_NEW, _("Open in New _Window"),
3113 item, midori_bookmarks_open_in_window_activate_cb, bookmarks);
3114 }
3115- menuitem = gtk_separator_menu_item_new ();
3116- gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
3117- gtk_widget_show (menuitem);
3118- midori_bookmarks_popup_item (menu, GTK_STOCK_EDIT, NULL,
3119- item, midori_bookmarks_edit_clicked_cb, bookmarks);
3120- midori_bookmarks_popup_item (menu, GTK_STOCK_DELETE, NULL,
3121- item, midori_bookmarks_delete_clicked_cb, bookmarks);
3122-
3123- katze_widget_popup (widget, GTK_MENU (menu), event, KATZE_MENU_POSITION_CURSOR);
3124+
3125+ menuitem = gtk_separator_menu_item_new ();
3126+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
3127+ gtk_widget_show (menuitem);
3128+
3129+ midori_bookmarks_popup_item (menu, GTK_STOCK_EDIT, NULL,
3130+ item, (item == root) ? NULL : midori_bookmarks_edit_clicked_cb, bookmarks);
3131+ midori_bookmarks_popup_item (menu, GTK_STOCK_DELETE, NULL,
3132+ item, (item == root) ? NULL : midori_bookmarks_delete_clicked_cb, bookmarks);
3133+
3134+ katze_widget_popup (widget, GTK_MENU (menu), event, KATZE_MENU_POSITION_CURSOR);
3135+}
3136+
3137+static void
3138+midori_bookmarks_multi_popup (GtkWidget* widget,
3139+ GdkEventButton* event,
3140+ MidoriBookmarks* bookmarks,
3141+ GtkTreeModel* model,
3142+ gint count,
3143+ GList* rows)
3144+{
3145+ GtkWidget* menu;
3146+ GtkWidget* menuitem;
3147+
3148+ menu = gtk_menu_new ();
3149+
3150+ midori_bookmarks_popup_item (menu,
3151+ STOCK_TAB_NEW, _("Open all in _Tabs"),
3152+ KATZE_ITEM(bookmarks->array), midori_bookmarks_open_in_tab_activate_cb, bookmarks);
3153+ menuitem = gtk_separator_menu_item_new ();
3154+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
3155+ gtk_widget_show (menuitem);
3156+
3157+ midori_bookmarks_popup_item (menu, GTK_STOCK_EDIT, NULL,
3158+ NULL, NULL, bookmarks);
3159+ midori_bookmarks_popup_item (menu, GTK_STOCK_DELETE, NULL,
3160+ KATZE_ITEM(bookmarks->array), midori_bookmarks_delete_clicked_cb, bookmarks);
3161+
3162+ katze_widget_popup (widget, GTK_MENU (menu), event, KATZE_MENU_POSITION_CURSOR);
3163+}
3164+
3165+static gboolean
3166+midori_bookmarks_do_block_selection (GtkTreeSelection *selection,
3167+ GtkTreeModel *model,
3168+ GtkTreePath *path,
3169+ gboolean path_currently_selected,
3170+ gpointer data)
3171+{
3172+ return FALSE;
3173+}
3174+
3175+static gboolean
3176+midori_bookmarks_do_not_block_selection (GtkTreeSelection *selection,
3177+ GtkTreeModel *model,
3178+ GtkTreePath *path,
3179+ gboolean path_currently_selected,
3180+ gpointer data)
3181+{
3182+ return TRUE;
3183 }
3184
3185 static gboolean
3186@@ -862,32 +2370,198 @@
3187 GtkTreeModel* model;
3188 GtkTreeIter iter;
3189
3190- if (event->button != 2 && event->button != 3)
3191+ if (bookmarks->pending_event)
3192+ {
3193+ GtkTreeView* treeview = GTK_TREE_VIEW(widget);
3194+ GtkTreeSelection* selection = gtk_tree_view_get_selection (treeview);
3195+ gint x = bookmarks->stock_pending_event.x;
3196+ gint y = bookmarks->stock_pending_event.y;
3197+
3198+ bookmarks->pending_event = NULL;
3199+ gtk_tree_selection_set_select_function (
3200+ selection, midori_bookmarks_do_not_block_selection, NULL, NULL);
3201+
3202+ if (x != event->x || y != event->y)
3203+ return TRUE;
3204+ }
3205+
3206+ if (event->button == 3)
3207+ return TRUE;
3208+
3209+ if (event->button != 2)
3210 return FALSE;
3211
3212 if (katze_tree_view_get_selected_iter (GTK_TREE_VIEW (widget), &model, &iter))
3213 {
3214- KatzeItem* item;
3215-
3216- gtk_tree_model_get (model, &iter, 0, &item, -1);
3217-
3218- if (event->button == 2)
3219- {
3220- const gchar* uri;
3221- if (KATZE_ITEM_IS_BOOKMARK (item) && (uri = katze_item_get_uri (item)) && *uri)
3222- {
3223- MidoriBrowser* browser = midori_browser_get_for_widget (widget);
3224- GtkWidget* view = midori_browser_add_uri (browser, uri);
3225- midori_browser_set_current_tab (browser, view);
3226- }
3227- }
3228- else
3229- midori_bookmarks_popup (widget, event, item, bookmarks);
3230-
3231- if (item != NULL)
3232- g_object_unref (item);
3233- return TRUE;
3234- }
3235+ gboolean done = FALSE;
3236+ KatzeItem* item;
3237+
3238+ gtk_tree_model_get (model, &iter, 0, &item, -1);
3239+
3240+ if (KATZE_ITEM_IS_BOOKMARK (item))
3241+ {
3242+ MidoriBrowser* browser = midori_browser_get_for_widget (widget);
3243+ GtkWidget* view = midori_bookmarks_open_bookmark_in_tab (
3244+ item, browser);
3245+
3246+ if (widget)
3247+ {
3248+ midori_browser_set_current_tab_smartly (browser, view);
3249+ done = TRUE;
3250+ }
3251+ }
3252+
3253+ g_object_unref (item);
3254+
3255+ return done;
3256+ }
3257+
3258+ return FALSE;
3259+}
3260+
3261+static gboolean
3262+midori_bookmarks_block_selection(GtkWidget* widget,
3263+ GdkEventButton* event,
3264+ MidoriBookmarks* bookmarks)
3265+{
3266+ GtkTreeView* treeview = GTK_TREE_VIEW(widget);
3267+ GtkTreePath* path;
3268+ GtkTreeSelection* selection;
3269+ gint cell_x;
3270+ gint cell_y;
3271+
3272+ if (!gtk_tree_view_get_path_at_pos (
3273+ treeview, event->x, event->y,
3274+ &path, NULL, &cell_x, &cell_y))
3275+ return FALSE;
3276+
3277+ gtk_widget_grab_focus (widget);
3278+
3279+ selection = gtk_tree_view_get_selection (treeview);
3280+
3281+ if (gtk_tree_selection_path_is_selected (selection, path)
3282+ && !(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)))
3283+ {
3284+ bookmarks->pending_event = &bookmarks->stock_pending_event;
3285+ bookmarks->stock_pending_event.x = event->x;
3286+ bookmarks->stock_pending_event.y = event->y;
3287+ gtk_tree_selection_set_select_function (
3288+ selection, midori_bookmarks_do_block_selection, NULL, NULL);
3289+ }
3290+ else
3291+ {
3292+ bookmarks->pending_event = NULL;
3293+ gtk_tree_selection_set_select_function (
3294+ selection, midori_bookmarks_do_not_block_selection, NULL, NULL);
3295+ }
3296+
3297+ return FALSE;
3298+}
3299+
3300+static gboolean
3301+midori_bookmarks_button_press_event_cb (GtkWidget* widget,
3302+ GdkEventButton* event,
3303+ MidoriBookmarks* bookmarks)
3304+{
3305+ GtkTreeView* treeview = GTK_TREE_VIEW(widget);
3306+ GtkTreePath* path;
3307+ GtkTreeSelection* selection;
3308+ GtkTreeModel* model;
3309+ gint selected;
3310+ GList* rows;
3311+ gint cell_x;
3312+ gint cell_y;
3313+
3314+ if (event->button == 1)
3315+ return midori_bookmarks_block_selection (widget, event, bookmarks);
3316+
3317+ if (event->button != 3)
3318+ return FALSE;
3319+
3320+ selection = gtk_tree_view_get_selection (treeview);
3321+
3322+ if (!gtk_tree_view_get_path_at_pos (
3323+ treeview, event->x, event->y,
3324+ &path, NULL, &cell_x, &cell_y))
3325+ {
3326+ /* FIXME: popup opening below treeview
3327+ * Rationale: the user is actually in ROOT folder
3328+ * we may need to have a non editable, non deletable, ROOT folder popup
3329+ * Open all in Tabs
3330+ * Separator
3331+ * Edit [inactive]
3332+ * Delete [inactive]
3333+ * Here we just mimic the Files behaviour:
3334+ * 1- unselect all
3335+ * 2- let popup based on selection process
3336+ */
3337+
3338+ gtk_tree_selection_unselect_all (selection);
3339+ }
3340+ else if (!gtk_tree_selection_path_is_selected (selection, path))
3341+ {
3342+ /* Use case: popup opening on item not in selection
3343+ * Rationale: the user is addressing a single item not in selection
3344+ * we may need a single item popup with callbacks working on the item,
3345+ * not the selection.
3346+ * Here we just mimic the Files behaviour:
3347+ * 1- change the selection to the item the popup is opened on
3348+ * 2- let popup based on selection process
3349+ */
3350+
3351+ gtk_tree_selection_unselect_all (selection);
3352+ gtk_tree_selection_select_path (selection, path);
3353+ }
3354+
3355+ selected = katze_tree_view_get_selected_rows(GTK_TREE_VIEW (widget), &model, &rows);
3356+
3357+ if (!selected)
3358+ {
3359+ KatzeItem* root = KATZE_ITEM (bookmarks->array);
3360+
3361+ midori_bookmarks_popup (widget, event, root, bookmarks);
3362+
3363+ return TRUE;
3364+ }
3365+
3366+ if (selected == 1)
3367+ {
3368+ GtkTreeIter iter;
3369+ KatzeItem* item;
3370+
3371+ if (!gtk_tree_model_get_iter (
3372+ model, &iter, (GtkTreePath *)g_list_nth_data (rows, 0)))
3373+ {
3374+ g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
3375+
3376+ return FALSE;
3377+ }
3378+
3379+ gtk_tree_model_get (model, &iter, 0, &item, -1);
3380+
3381+ midori_bookmarks_popup (widget, event, item, bookmarks);
3382+
3383+ g_object_unref (item);
3384+
3385+ g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
3386+
3387+ return TRUE;
3388+ }
3389+
3390+ if (selected > 1)
3391+ {
3392+ midori_bookmarks_multi_popup (widget,
3393+ event,
3394+ bookmarks,
3395+ model,
3396+ selected,
3397+ rows);
3398+
3399+ g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
3400+
3401+ return TRUE;
3402+ }
3403+
3404 return FALSE;
3405 }
3406
3407@@ -918,46 +2592,82 @@
3408 }
3409 }
3410
3411-static void
3412-midori_bookmarks_row_expanded_cb (GtkTreeView* treeview,
3413- GtkTreeIter* iter,
3414- GtkTreePath* path,
3415- MidoriBookmarks* bookmarks)
3416+static gboolean
3417+midori_bookmarks_test_expand_row_cb (GtkTreeView* treeview,
3418+ GtkTreeIter* iter,
3419+ GtkTreePath* path,
3420+ MidoriBookmarks* bookmarks)
3421 {
3422 GtkTreeModel* model;
3423+ GtkTreeIter child;
3424 KatzeItem* item;
3425+ gint64 id;
3426
3427 model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
3428+
3429 gtk_tree_model_get (model, iter, 0, &item, -1);
3430+
3431+ g_return_val_if_fail (KATZE_IS_ITEM(item), TRUE);
3432+
3433+ g_signal_handlers_block_by_func (model,
3434+ midori_bookmarks_row_deleted_cb,
3435+ bookmarks);
3436+
3437+ id = katze_item_get_meta_integer (item, "id");
3438+
3439+ g_object_unref (item);
3440+
3441+ while (gtk_tree_model_iter_children (model, &child, iter))
3442+ gtk_tree_store_remove (GTK_TREE_STORE (model), &child);
3443+ /* That's an invisible dummy, so we always have an expander */
3444+ gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &child, iter,
3445+ 0, 0, NULL, -1);
3446+
3447+ g_signal_handlers_unblock_by_func (model,
3448+ midori_bookmarks_row_deleted_cb,
3449+ bookmarks);
3450+
3451 midori_bookmarks_read_from_db_to_model (bookmarks, GTK_TREE_STORE (model),
3452- iter, katze_item_get_meta_integer (item, "id"), NULL);
3453- g_object_unref (item);
3454+ iter, id, NULL);
3455+
3456+ return FALSE;
3457 }
3458
3459 static void
3460 midori_bookmarks_row_collapsed_cb (GtkTreeView *treeview,
3461 GtkTreeIter *parent,
3462 GtkTreePath *path,
3463- gpointer user_data)
3464+ MidoriBookmarks* bookmarks)
3465 {
3466 GtkTreeModel* model;
3467 GtkTreeStore* treestore;
3468 GtkTreeIter child;
3469
3470 model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
3471+
3472+ g_signal_handlers_block_by_func (model,
3473+ midori_bookmarks_row_deleted_cb,
3474+ bookmarks);
3475+
3476 treestore = GTK_TREE_STORE (model);
3477 while (gtk_tree_model_iter_nth_child (model, &child, parent, 0))
3478 gtk_tree_store_remove (treestore, &child);
3479 /* That's an invisible dummy, so we always have an expander */
3480 gtk_tree_store_insert_with_values (treestore, &child, parent,
3481 0, 0, NULL, -1);
3482-}
3483+
3484+ g_signal_handlers_block_by_func (model,
3485+ midori_bookmarks_row_deleted_cb,
3486+ bookmarks);
3487+
3488+ }
3489
3490 static void
3491 midori_bookmarks_selection_changed_cb (GtkTreeSelection *treeview,
3492 MidoriBookmarks *bookmarks)
3493 {
3494 midori_bookmarks_toolbar_update (bookmarks);
3495+ midori_bookmarks_statusbar_update (bookmarks);
3496 }
3497
3498 static gboolean
3499@@ -993,6 +2703,123 @@
3500 midori_bookmarks_filter_timeout_cb, bookmarks, NULL);
3501 }
3502
3503+static GtkTargetEntry midori_bookmarks_dnd_target_entries[]=
3504+{
3505+ {"GTK_TREE_MODEL_MULTI_ROWS", GTK_TARGET_SAME_WIDGET, 0},
3506+};
3507+
3508+#define MIDORI_BOOKMARKS_DND_NB_TARGET_ENTRIES \
3509+ G_N_ELEMENTS (midori_bookmarks_dnd_target_entries)
3510+
3511+static guint
3512+item_hash (gconstpointer item)
3513+{
3514+ gint64 id = katze_item_get_meta_integer (KATZE_ITEM (item), "id");
3515+ return g_int64_hash (&id);
3516+}
3517+
3518+static gboolean
3519+item_equal (gconstpointer item_a, gconstpointer item_b)
3520+{
3521+ gint64 id_a = katze_item_get_meta_integer (KATZE_ITEM (item_a), "id");
3522+ gint64 id_b = katze_item_get_meta_integer (KATZE_ITEM (item_b), "id");
3523+ return (id_a == id_b)? TRUE : FALSE;
3524+}
3525+
3526+static gboolean
3527+midori_bookmarks_idle (gpointer data)
3528+{
3529+ MidoriBookmarks* bookmarks = MIDORI_BOOKMARKS (data);
3530+ GtkTreeModel* model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
3531+ GHashTableIter hash_iter;
3532+ gpointer key, value;
3533+ GList* list_iter;
3534+
3535+ /* update remaining additions */
3536+
3537+ list_iter = bookmarks->added_paths;
3538+ while (list_iter)
3539+ {
3540+ GtkTreePath* path = (GtkTreePath*)list_iter->data;
3541+
3542+ update_items_for_parent (model, path, bookmarks);
3543+
3544+ list_iter = g_list_next (list_iter);
3545+ }
3546+
3547+ g_list_free_full (bookmarks->added_paths, (GDestroyNotify) gtk_tree_path_free);
3548+ bookmarks->added_paths = NULL;
3549+
3550+ /* then finalize updates */
3551+ g_signal_handlers_block_by_func (bookmarks->array,
3552+ midori_bookmarks_update_item_cb,
3553+ bookmarks);
3554+
3555+ g_hash_table_iter_init (&hash_iter, bookmarks->updated_items);
3556+
3557+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
3558+ {
3559+ KatzeItem *item = KATZE_ITEM(key);
3560+
3561+ katze_array_update_item (bookmarks->array, item);
3562+ g_object_unref (item);
3563+ }
3564+
3565+ g_signal_handlers_unblock_by_func (bookmarks->array,
3566+ midori_bookmarks_update_item_cb,
3567+ bookmarks);
3568+
3569+ g_hash_table_remove_all (bookmarks->updated_items);
3570+
3571+ return FALSE;
3572+}
3573+
3574+static void
3575+midori_bookmarks_update_item (MidoriBookmarks* bookmarks, KatzeItem *item)
3576+{
3577+ if (!g_hash_table_size (bookmarks->updated_items)
3578+ || (!g_list_length (bookmarks->added_paths)))
3579+ g_idle_add (midori_bookmarks_idle, bookmarks);
3580+ else if (g_hash_table_lookup (bookmarks->updated_items, item))
3581+ return;
3582+
3583+ g_object_ref (item);
3584+ g_hash_table_insert (bookmarks->updated_items, item, item);
3585+}
3586+
3587+static void
3588+midori_bookmarks_idle_remove_item (MidoriBookmarks* bookmarks, KatzeItem *item)
3589+{
3590+ gpointer found;
3591+
3592+ if (KATZE_ITEM_IS_FOLDER (item))
3593+ {
3594+ gint64 id = katze_item_get_meta_integer (item, "id");
3595+ GHashTableIter iter;
3596+ gpointer key, value;
3597+
3598+ g_hash_table_iter_init (&iter, bookmarks->updated_items);
3599+
3600+ while (g_hash_table_iter_next (&iter, &key, &value))
3601+ {
3602+ KatzeItem *hash_item = KATZE_ITEM(key);
3603+
3604+ gint64 parentid = katze_item_get_meta_integer (hash_item, "parentid");
3605+ if (parentid == id)
3606+ {
3607+ g_hash_table_iter_remove (&iter);
3608+ g_object_unref (hash_item);
3609+ }
3610+ }
3611+ }
3612+
3613+ if (found = g_hash_table_lookup (bookmarks->updated_items, item))
3614+ {
3615+ g_hash_table_remove (bookmarks->updated_items, found);
3616+ g_object_unref (found);
3617+ }
3618+}
3619+
3620 static void
3621 midori_bookmarks_init (MidoriBookmarks* bookmarks)
3622 {
3623@@ -1005,6 +2832,8 @@
3624 GtkCellRenderer* renderer_text;
3625 GtkTreeSelection* selection;
3626
3627+ bookmarks->pending_event = NULL;
3628+
3629 /* Create the filter entry */
3630 entry = sokoke_search_entry_new (_("Search Bookmarks"));
3631 g_signal_connect_after (entry, "changed",
3632@@ -1015,7 +2844,7 @@
3633 gtk_box_pack_start (GTK_BOX (bookmarks), box, FALSE, FALSE, 5);
3634
3635 /* Create the treeview */
3636- model = gtk_tree_store_new (2, KATZE_TYPE_ITEM, G_TYPE_STRING);
3637+ model = midori_bookmarks_tree_store_new (2, KATZE_TYPE_ITEM, G_TYPE_STRING);
3638 treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
3639 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
3640 gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (treeview), 1);
3641@@ -1032,29 +2861,49 @@
3642 (GtkTreeCellDataFunc)midori_bookmarks_treeview_render_text_cb,
3643 treeview, NULL);
3644 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
3645- gtk_tree_view_set_reorderable (GTK_TREE_VIEW (treeview), TRUE);
3646+ gtk_tree_view_set_reorderable (GTK_TREE_VIEW (treeview), FALSE);
3647+ gtk_tree_view_enable_model_drag_source (
3648+ GTK_TREE_VIEW (treeview),
3649+ GDK_BUTTON1_MASK,
3650+ midori_bookmarks_dnd_target_entries,
3651+ MIDORI_BOOKMARKS_DND_NB_TARGET_ENTRIES,
3652+ GDK_ACTION_MOVE|GDK_ACTION_LINK);
3653+ gtk_tree_view_enable_model_drag_dest (
3654+ GTK_TREE_VIEW (treeview),
3655+ midori_bookmarks_dnd_target_entries,
3656+ MIDORI_BOOKMARKS_DND_NB_TARGET_ENTRIES,
3657+ GDK_ACTION_MOVE|GDK_ACTION_LINK);
3658 g_object_unref (model);
3659 g_object_connect (treeview,
3660 "signal::row-activated",
3661 midori_bookmarks_row_activated_cb, bookmarks,
3662+ "signal::button-press-event",
3663+ midori_bookmarks_button_press_event_cb, bookmarks,
3664 "signal::button-release-event",
3665 midori_bookmarks_button_release_event_cb, bookmarks,
3666 "signal::key-release-event",
3667 midori_bookmarks_key_release_event_cb, bookmarks,
3668 "signal::popup-menu",
3669 midori_bookmarks_popup_menu_cb, bookmarks,
3670- "signal::row-expanded",
3671- midori_bookmarks_row_expanded_cb, bookmarks,
3672+ "signal::test-expand-row",
3673+ midori_bookmarks_test_expand_row_cb, bookmarks,
3674 "signal::row-collapsed",
3675 midori_bookmarks_row_collapsed_cb, bookmarks,
3676 NULL);
3677+
3678+ MIDORI_BOOKMARKS_TREE_STORE (model)->source_view = GTK_TREE_VIEW (treeview);
3679+
3680 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
3681+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
3682 g_signal_connect_after (selection, "changed",
3683 G_CALLBACK (midori_bookmarks_selection_changed_cb),
3684 bookmarks);
3685 gtk_widget_show (treeview);
3686 gtk_box_pack_start (GTK_BOX (bookmarks), treeview, TRUE, TRUE, 0);
3687 bookmarks->treeview = treeview;
3688+
3689+ bookmarks->updated_items = g_hash_table_new (item_hash, item_equal);
3690+ bookmarks->added_paths = NULL;
3691 }
3692
3693 static void
3694@@ -1064,5 +2913,9 @@
3695
3696 if (bookmarks->app)
3697 g_object_unref (bookmarks->app);
3698+
3699+ g_idle_remove_by_data (bookmarks);
3700+ g_hash_table_unref (bookmarks->updated_items);
3701+ g_list_free_full (bookmarks->added_paths, (GDestroyNotify) gtk_tree_path_free);
3702+ bookmarks->added_paths = NULL;
3703 }
3704-
3705
3706=== modified file 'tests/bookmarks.c'
3707--- tests/bookmarks.c 2012-11-25 15:37:41 +0000
3708+++ tests/bookmarks.c 2013-05-21 05:35:32 +0000
3709@@ -128,7 +128,7 @@
3710 }
3711
3712 db_items = midori_array_query_recursive (db_bookmarks,
3713- "*", "title='%q'", katze_item_get_name (test_item), FALSE);
3714+ "*", "title='%q'", katze_item_get_name (test_item), NULL, FALSE);
3715
3716 /* FIXME g_assert_cmpint (katze_array_get_length (db_items), ==, 1); */
3717 db_item = katze_array_get_nth_item (db_items, 0);

Subscribers

People subscribed via source and target branches

to all changes: