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

Proposed by André Auzi
Status: Work in progress
Proposed branch: lp:~aauzi/midori/fix-894143
Merge into: lp:midori
Diff against target: 3396 lines (+2255/-309)
10 files modified
katze/katze-array.c (+53/-2)
katze/katze-arrayaction.c (+10/-0)
katze/katze-utils.c (+90/-3)
katze/katze-utils.h (+5/-0)
midori/midori-array.c (+7/-0)
midori/midori-bookmarks-db.c (+107/-15)
midori/midori-bookmarks-db.h (+11/-9)
midori/midori-browser.c (+11/-19)
panels/midori-bookmarks.c (+1960/-260)
tests/bookmarks.c (+1/-1)
To merge this branch: bzr merge lp:~aauzi/midori/fix-894143
Reviewer Review Type Date Requested Status
Midori Devs Pending
Review via email: mp+165249@code.launchpad.net

Description of the change

(For review: fix-1179200 is a prerequisite to fix-894143)

To post a comment you must log in.
Revision history for this message
André Auzi (aauzi) wrote :

Updated with merge in of lp:~aauzi/midori/fix-1179200-9 and lp:midori.

Bookmark item positions are now properly managed in both menus and panel.

Multiple bookmark selection and drag-n-drop in the bookmark panel allow bookmark ordering.

lp:~aauzi/midori/fix-894143 updated
6340. By André Auzi

merge lp:midori after merge of fix-1179200-8

Unmerged revisions

6340. By André Auzi

merge lp:midori after merge of fix-1179200-8

6339. By André Auzi

Fix race condition in timestamp comparison

6338. By André Auzi

merge lp:midori

6337. By André Auzi

Fix Test #9 regression error

6336. By André Auzi

merge lp:~aauzi/midori/fix-1179200-9

6335. By André Auzi

merge fix-xbel-import-regression

6334. By André Auzi

merge lp:midori

6333. By André Auzi

manage sort order in bookmarks test

6332. By André Auzi

merge lp:midori after insert of fix-1179200-4

6331. By André Auzi

Derivation of GtkTreeStore for DND drop destination control.

Inserts are delayed to handle move operations where move is actually an insert at destination, followed by a delete at the source and finally a row change callback.

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

Subscribers

People subscribed via source and target branches

to all changes: