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

Proposed by André Auzi
Status: Work in progress
Proposed branch: lp:~aauzi/midori/fix-1006629
Merge into: lp:midori
Diff against target: 4720 lines (+3164/-334)
13 files modified
katze/katze-array.c (+53/-2)
katze/katze-arrayaction.c (+270/-2)
katze/katze-utils.c (+156/-3)
katze/katze-utils.h (+11/-0)
midori/midori-array.c (+7/-0)
midori/midori-bookmarks-db.c (+106/-15)
midori/midori-bookmarks-db.h (+11/-9)
midori/midori-browser.c (+390/-31)
midori/midori-locationaction.c (+78/-10)
midori/midori-locationaction.h (+8/-0)
midori/midori-view.c (+110/-1)
panels/midori-bookmarks.c (+1963/-260)
tests/bookmarks.c (+1/-1)
To merge this branch: bzr merge lp:~aauzi/midori/fix-1006629
Reviewer Review Type Date Requested Status
Midori Devs Pending
Review via email: mp+203255@code.launchpad.net

Description of the change

Coming after lp:~aauzi/midori/fix-894143, this proposes bookmark creation by Dnd from Location bar into the bookmarkbar and ordering of the bookmarkbar with Dnd.

Preliminary code for Dnd creation from tabs is deactivated. It needs rework to adapt to recent tabby evolutions.

To post a comment you must log in.
lp:~aauzi/midori/fix-1006629 updated
6212. By André Auzi

merge lp:midori

Unmerged revisions

6212. By André Auzi

merge lp:midori

6211. By André Auzi

Merge lp:~aauzi/midori/fix-894143 with pos_bar order

6210. By André Auzi

Fix bookmarkbar item position updates

6209. By André Auzi

Put traces in midori_debug ("bookmarks")

6208. By André Auzi

merge lp:midori, remove tab drag-n-drop due to tabby evolutions

6207. By André Auzi

merge lp:midori

6206. By André Auzi

connect menu button press event

6205. By André Auzi

merge lp:midori

6204. By André Auzi

fix hilight item width

6203. By André Auzi

merge lp:midori

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-26 19:44:25 +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-26 19:44:25 +0000
105@@ -29,6 +29,10 @@
106
107 KatzeArray* array;
108 gboolean reversed;
109+
110+ GtkTargetList* dnd_targets;
111+ GdkEventButton *press_event;
112+ GdkEventButton stock_press_event;
113 };
114
115 struct _KatzeArrayActionClass
116@@ -43,7 +47,8 @@
117 PROP_0,
118
119 PROP_ARRAY,
120- PROP_REVERSED
121+ PROP_REVERSED,
122+ PROP_DND_TARGETS
123 };
124
125 enum
126@@ -215,6 +220,20 @@
127 "Whether the array should be walked backwards when building menus",
128 FALSE,
129 G_PARAM_READWRITE));
130+ /**
131+ * KatzeArrayAction:dnd-targets:
132+ *
133+ * GtkTargetList* the array supports.
134+ *
135+ * Since: 0.5.2
136+ **/
137+ g_object_class_install_property (gobject_class,
138+ PROP_DND_TARGETS,
139+ g_param_spec_pointer (
140+ "dnd-targets",
141+ "DNDTargets",
142+ "GtkTargetList* the array supports",
143+ G_PARAM_READABLE|G_PARAM_WRITABLE));
144 }
145
146 static void
147@@ -222,6 +241,8 @@
148 {
149 array_action->array = NULL;
150 array_action->reversed = FALSE;
151+ array_action->dnd_targets = NULL;
152+ array_action->press_event = NULL;
153 }
154
155 static void
156@@ -229,6 +250,9 @@
157 {
158 KatzeArrayAction* array_action = KATZE_ARRAY_ACTION (object);
159
160+ if (array_action->dnd_targets)
161+ gtk_target_list_unref (array_action->dnd_targets);
162+
163 katze_object_assign (array_action->array, NULL);
164
165 G_OBJECT_CLASS (katze_array_action_parent_class)->finalize (object);
166@@ -250,6 +274,9 @@
167 case PROP_REVERSED:
168 array_action->reversed = g_value_get_boolean (value);
169 break;
170+ case PROP_DND_TARGETS:
171+ array_action->dnd_targets = (GtkTargetList*)g_value_get_pointer (value);
172+ break;
173 default:
174 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
175 break;
176@@ -272,6 +299,9 @@
177 case PROP_REVERSED:
178 g_value_set_boolean (value, array_action->reversed);
179 break;
180+ case PROP_DND_TARGETS:
181+ g_value_set_pointer (value, array_action->dnd_targets);
182+ break;
183 default:
184 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
185 break;
186@@ -310,8 +340,29 @@
187 GdkEventButton* event,
188 KatzeArrayAction* array_action)
189 {
190+ array_action->stock_press_event = *event;
191+ array_action->press_event = &array_action->stock_press_event;
192+
193+ return FALSE;
194+}
195+
196+static gboolean
197+katze_array_action_menu_button_release_cb (GtkWidget* proxy,
198+ GdkEventButton* event,
199+ KatzeArrayAction* array_action)
200+{
201 KatzeItem* item = g_object_get_data (G_OBJECT (proxy), "KatzeItem");
202
203+ if (array_action->press_event)
204+ {
205+ gint x = array_action->press_event->x;
206+ gint y = array_action->press_event->y;
207+ array_action->press_event = NULL;
208+
209+ if (x != event->x || y != event->y)
210+ return TRUE;
211+ }
212+
213 katze_array_action_activate_item (array_action, item, event->button);
214
215 /* we need to block the 'activate' handler which would be called
216@@ -323,6 +374,120 @@
217 }
218
219 static void
220+katze_array_action_item_drag_source_drag_data_get (
221+ KatzeItem *item,
222+ GdkDragContext *drag_context,
223+ GtkSelectionData *data,
224+ guint info,
225+ guint time,
226+ KatzeArrayAction* action)
227+{
228+ g_return_if_fail (KATZE_IS_ITEM (item));
229+
230+ switch (gdk_drag_context_get_selected_action (drag_context))
231+ {
232+ default:
233+ break;
234+ case GDK_ACTION_MOVE:
235+ case GDK_ACTION_COPY:
236+ if (selection_data_from_katze_item (data, item))
237+ break;
238+ case GDK_ACTION_LINK:
239+ if (KATZE_ITEM_IS_BOOKMARK (item))
240+ {
241+ const gchar* uri = katze_item_get_uri (item);
242+ if (uri && *uri)
243+ {
244+ gchar* src_uris[2];
245+ gchar** uris;
246+
247+ src_uris[0] = (gchar*)uri;
248+ src_uris[1] = NULL;
249+
250+ uris = g_strdupv (src_uris);
251+
252+ gtk_selection_data_set_uris (data, uris);
253+ gtk_selection_data_set_text (data, uri, -1);
254+
255+ g_strfreev (uris);
256+ }
257+ }
258+ break;
259+ }
260+}
261+
262+static void
263+katze_array_action_menu_item_drag_source_drag_data_get_cb (
264+ GtkWidget *widget,
265+ GdkDragContext *drag_context,
266+ GtkSelectionData *data,
267+ guint info,
268+ guint time,
269+ KatzeArrayAction* action)
270+{
271+ KatzeItem* item = (KatzeItem*)g_object_get_data (G_OBJECT (widget), "KatzeItem");
272+
273+ katze_array_action_item_drag_source_drag_data_get (item, drag_context, data, info, time, action);
274+}
275+
276+static void
277+katze_array_action_tool_item_child_drag_source_drag_data_get_cb (
278+ GtkWidget *widget,
279+ GdkDragContext *drag_context,
280+ GtkSelectionData *data,
281+ guint info,
282+ guint time,
283+ KatzeArrayAction *action)
284+{
285+ GtkWidget* proxy = gtk_widget_get_parent (widget);
286+ KatzeItem* item = (KatzeItem*)g_object_get_data (G_OBJECT (proxy), "KatzeItem");
287+ GtkToolbar* toolbar = GTK_TOOLBAR (gtk_widget_get_parent (proxy));
288+ gint pos;
289+
290+ g_return_if_fail (KATZE_IS_ITEM (item));
291+
292+ pos = gtk_toolbar_get_item_index (toolbar, GTK_TOOL_ITEM (proxy));
293+ if (pos != katze_item_get_meta_integer (item, "pos_bar"))
294+ katze_item_set_meta_integer (item, "pos_bar", pos);
295+
296+ katze_array_action_item_drag_source_drag_data_get (item, drag_context, data, info, time, action);
297+}
298+
299+extern void midori_bookmarkbar_position_items (GtkWidget *widget);
300+
301+
302+static void
303+katze_array_action_tool_item_child_drag_source_drag_data_delete_cb (
304+ GtkWidget *widget,
305+ GdkDragContext *drag_context,
306+ KatzeArrayAction* action)
307+{
308+ GtkWidget* proxy = gtk_widget_get_parent (widget);
309+ KatzeItem* item = (KatzeItem*)g_object_get_data (G_OBJECT (proxy), "KatzeItem");
310+ GtkToolbar* toolbar = GTK_TOOLBAR (gtk_widget_get_parent (proxy));
311+
312+ g_return_if_fail (KATZE_IS_ITEM (item));
313+
314+ gtk_container_remove (GTK_CONTAINER (toolbar), proxy);
315+
316+ midori_bookmarkbar_position_items (GTK_WIDGET (toolbar));
317+}
318+
319+static void
320+katze_array_action_menu_item_drag_source_drag_data_delete_cb (
321+ GtkWidget *proxy,
322+ GdkDragContext *drag_context,
323+ KatzeArrayAction* action)
324+{
325+ KatzeItem* item = (KatzeItem*)g_object_get_data (G_OBJECT (proxy), "KatzeItem");
326+ GtkMenuShell* menu = GTK_MENU_SHELL (gtk_widget_get_parent (proxy));
327+
328+ g_return_if_fail (KATZE_IS_ITEM (item));
329+
330+ gtk_container_remove (GTK_CONTAINER (menu), proxy);
331+}
332+
333+static void
334 katze_array_action_menu_item_select_cb (GtkWidget* proxy,
335 KatzeArrayAction* array_action);
336
337@@ -407,6 +572,24 @@
338 G_CALLBACK (katze_array_action_menu_item_select_cb), array_action);
339 g_signal_connect (menuitem, "activate",
340 G_CALLBACK (katze_array_action_menu_item_select_cb), array_action);
341+#if 0
342+ /* TODO: insert menu DND dest code here */
343+ if (array_action->dnd_targets)
344+ {
345+ gtk_drag_dest_set (submenu, GTK_DEST_DEFAULT_ALL, NULL, 0,
346+ GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_LINK);
347+ gtk_drag_dest_set_target_list (submenu, array_action->dnd_targets);
348+ g_signal_connect (submenu, "drag-motion",
349+ G_CALLBACK (katze_array_action_menu_drag_dest_drag_motion_cb),
350+ array_action);
351+ g_signal_connect (submenu, "drag-leave",
352+ G_CALLBACK (katze_array_action_menu_drag_dest_drag_leave_cb),
353+ array_action);
354+ g_signal_connect (submenu, "drag-data-received",
355+ G_CALLBACK (katze_array_action_menu_drag_dest_drag_data_received_cb),
356+ array_action);
357+ }
358+#endif
359 }
360 else
361 {
362@@ -416,6 +599,23 @@
363 }
364 g_signal_connect (menuitem, "button-press-event",
365 G_CALLBACK (katze_array_action_menu_button_press_cb), array_action);
366+ g_signal_connect (menuitem, "button-release-event",
367+ G_CALLBACK (katze_array_action_menu_button_release_cb), array_action);
368+
369+
370+ if (array_action->dnd_targets)
371+ {
372+ gtk_drag_source_set (menuitem, GDK_BUTTON1_MASK, NULL, 0,
373+ GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_LINK);
374+ gtk_drag_source_set_target_list (menuitem, array_action->dnd_targets);
375+ g_signal_connect (menuitem, "drag-data-get",
376+ G_CALLBACK (katze_array_action_menu_item_drag_source_drag_data_get_cb),
377+ array_action);
378+ g_signal_connect (menuitem, "drag-data-delete",
379+ G_CALLBACK (katze_array_action_menu_item_drag_source_drag_data_delete_cb),
380+ array_action);
381+ }
382+
383 gtk_widget_show (menuitem);
384 }
385 }
386@@ -622,15 +822,49 @@
387 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
388 g_signal_connect (menuitem, "select",
389 G_CALLBACK (katze_array_action_menu_item_select_cb), array_action);
390+#if 0
391+ /* TODO: insert menu DND menu code here */
392+ if (array_action->dnd_targets)
393+ {
394+ gtk_drag_dest_set (submenu, GTK_DEST_DEFAULT_ALL, NULL, 0,
395+ GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_LINK);
396+ gtk_drag_dest_set_target_list (submenu, array_action->dnd_targets);
397+ g_signal_connect (submenu, "drag-motion",
398+ G_CALLBACK (katze_array_action_menu_drag_dest_drag_motion_cb),
399+ array_action);
400+ g_signal_connect (submenu, "drag-leave",
401+ G_CALLBACK (katze_array_action_menu_drag_dest_drag_leave_cb),
402+ array_action);
403+ g_signal_connect (submenu, "drag-data-received",
404+ G_CALLBACK (katze_array_action_menu_drag_dest_drag_data_received_cb),
405+ array_action);
406+ }
407+#endif
408 }
409 else
410 {
411 g_signal_connect (menuitem, "button-press-event",
412 G_CALLBACK (katze_array_action_menu_button_press_cb), array_action);
413+ g_signal_connect (menuitem, "button-release-event",
414+ G_CALLBACK (katze_array_action_menu_button_release_cb), array_action);
415 /* we need the 'activate' signal as well for keyboard events */
416 g_signal_connect (menuitem, "activate",
417 G_CALLBACK (katze_array_action_menu_activate_cb), array_action);
418 }
419+
420+ if (array_action->dnd_targets)
421+ {
422+ gtk_drag_source_set (menuitem, GDK_BUTTON1_MASK, NULL, 0,
423+ GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_LINK);
424+ gtk_drag_source_set_target_list (menuitem, array_action->dnd_targets);
425+ g_signal_connect (menuitem, "drag-data-get",
426+ G_CALLBACK (katze_array_action_menu_item_drag_source_drag_data_get_cb),
427+ array_action);
428+ g_signal_connect (menuitem, "drag-data-delete",
429+ G_CALLBACK (katze_array_action_menu_item_drag_source_drag_data_delete_cb),
430+ array_action);
431+ }
432+
433 gtk_tool_item_set_proxy_menu_item (GTK_TOOL_ITEM (proxy),
434 "katze-tool-item-menu", menuitem);
435 return TRUE;
436@@ -706,9 +940,33 @@
437 else
438 gtk_tool_item_set_tooltip_text (toolitem, uri);
439
440- g_object_set_data (G_OBJECT (toolitem), "KatzeArray", item);
441+ g_object_set_data (G_OBJECT (toolitem), "KatzeItem", item);
442 g_signal_connect (toolitem, "clicked",
443 G_CALLBACK (katze_array_action_proxy_clicked_cb), array_action);
444+ if (KATZE_IS_ITEM (item))
445+ {
446+ GtkWidget* child = gtk_bin_get_child (GTK_BIN (toolitem));
447+
448+ if (KATZE_ITEM_IS_FOLDER (item) && array_action->dnd_targets)
449+ {
450+#if 0
451+ /* TODO: add toggle button DND dest code here */
452+#endif
453+ }
454+
455+ if (array_action->dnd_targets)
456+ {
457+ gtk_drag_source_set (child, GDK_BUTTON1_MASK, NULL, 0,
458+ GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_LINK);
459+ gtk_drag_source_set_target_list (child, array_action->dnd_targets);
460+ g_signal_connect (child, "drag-data-get",
461+ G_CALLBACK (katze_array_action_tool_item_child_drag_source_drag_data_get_cb),
462+ array_action);
463+ g_signal_connect (child, "drag-data-delete",
464+ G_CALLBACK (katze_array_action_tool_item_child_drag_source_drag_data_delete_cb),
465+ array_action);
466+ }
467+ }
468
469 g_object_set_data (G_OBJECT (toolitem), "KatzeArrayAction", array_action);
470 g_signal_connect (item, "notify",
471@@ -767,12 +1025,14 @@
472 KatzeArray* array)
473 {
474 GSList* proxies;
475+ KatzeArray *old_array = NULL;
476
477 g_return_if_fail (KATZE_IS_ARRAY_ACTION (array_action));
478 g_return_if_fail (!array || katze_array_is_a (array, KATZE_TYPE_ITEM));
479
480 /* FIXME: Disconnect old array */
481
482+ old_array = array_action->array;
483 if (array)
484 g_object_ref (array);
485 katze_object_assign (array_action->array, array);
486@@ -793,7 +1053,15 @@
487
488 do
489 {
490+ KatzeArray* item = g_object_get_data (G_OBJECT (proxies->data), "KatzeItem");
491+
492+ if (item && (item == old_array))
493+ g_object_set_data (G_OBJECT (proxies->data), "KatzeItem", array);
494+
495 gtk_widget_set_sensitive (proxies->data, array != NULL);
496 }
497 while ((proxies = g_slist_next (proxies)));
498+
499+ if (array)
500+ katze_array_update (KATZE_ARRAY (array));
501 }
502
503=== modified file 'katze/katze-utils.c'
504--- katze/katze-utils.c 2013-11-05 21:51:18 +0000
505+++ katze/katze-utils.c 2014-01-26 19:44:25 +0000
506@@ -29,6 +29,13 @@
507 #include <unistd.h>
508 #endif
509
510+typedef struct _KatzeArrayData KatzeArrayData;
511+
512+struct _KatzeArrayData
513+{
514+ KatzeItem* items[1];
515+};
516+
517 #define I_ g_intern_static_string
518
519 static void
520@@ -1002,12 +1009,99 @@
521
522 g_return_val_if_fail (GTK_IS_TREE_VIEW (treeview), FALSE);
523
524- if ((selection = gtk_tree_view_get_selection (treeview)))
525- if (gtk_tree_selection_get_selected (selection, model, iter))
526- return TRUE;
527+ selection = gtk_tree_view_get_selection(treeview);
528+
529+ switch (gtk_tree_selection_get_mode (selection))
530+ {
531+ default:
532+ break;
533+
534+ case GTK_SELECTION_SINGLE:
535+ case GTK_SELECTION_BROWSE:
536+ if (gtk_tree_selection_get_selected (selection, model, iter))
537+ return TRUE;
538+ break;
539+
540+ case GTK_SELECTION_MULTIPLE:
541+ if (gtk_tree_selection_count_selected_rows (selection) == 1)
542+ {
543+ GtkTreeModel *stock_model;
544+ GList *list = gtk_tree_selection_get_selected_rows (selection, &stock_model);
545+
546+ if (model)
547+ *model = stock_model;
548+
549+ if (list)
550+ {
551+ GtkTreePath *path = (GtkTreePath *)g_list_nth_data (list, 0);
552+
553+ if (path && (!iter || gtk_tree_model_get_iter (stock_model, iter, path)))
554+ {
555+ g_list_free_full(list, (GDestroyNotify) gtk_tree_path_free);
556+ return TRUE;
557+ }
558+
559+ g_list_free_full(list, (GDestroyNotify) gtk_tree_path_free);
560+ }
561+ }
562+ }
563+
564 return FALSE;
565 }
566
567+/**
568+ * katze_tree_view_get_selected_rows:
569+ * @treeview: a #GtkTreeView
570+ * @model: a pointer to store the #GtkTreeModel, or %NULL
571+ * @rows: a pointer to store the #GList of #GtkTreePath, or %NULL
572+ *
573+ * Determines whether there is a selection in @treeview
574+ * and sets the @rows to the current selection.
575+ *
576+ * If there is a selection and @model is not %NULL, it is
577+ * set to the model. If @model is %NULL, the @rows will be
578+ * set to %NULL.
579+ *
580+ * Either @model or @rows or both can be %NULL in which case
581+ * no value will be assigned in any case.
582+ *
583+ * When @rows is not %NULL it must be freed using:
584+ * g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
585+ *
586+ * Return value: the count of selected rows
587+ *
588+ * Since: 0.4.7.aau.1
589+ **/
590+
591+gint
592+katze_tree_view_get_selected_rows (GtkTreeView* treeview,
593+ GtkTreeModel** model,
594+ GList** rows)
595+{
596+ gint count;
597+ GtkTreeSelection* selection;
598+
599+ if (model)
600+ *model = NULL;
601+ if (rows)
602+ *rows = NULL;
603+
604+ g_return_val_if_fail (GTK_IS_TREE_VIEW (treeview), 0);
605+
606+ selection = gtk_tree_view_get_selection(treeview);
607+
608+ count = gtk_tree_selection_count_selected_rows (selection);
609+ if (count > 0)
610+ {
611+ if (model && rows)
612+ {
613+ *rows = gtk_tree_selection_get_selected_rows (selection, model);
614+ }
615+ }
616+
617+ return count;
618+}
619+
620 void
621 katze_bookmark_populate_tree_view (KatzeArray* array,
622 GtkTreeStore* model,
623@@ -1377,3 +1471,62 @@
624 gtk_widget_set_size_request (GTK_WIDGET (window), 700, 100);
625 }
626
627+gboolean
628+selection_data_from_katze_item (GtkSelectionData* selection_data, KatzeItem* item)
629+{
630+ GdkAtom katze_array_atom = gdk_atom_intern_static_string ("KATZE_ARRAY");
631+ KatzeArrayData *kad;
632+
633+ g_return_val_if_fail (selection_data != NULL, FALSE);
634+ g_return_val_if_fail (KATZE_IS_ITEM (item), FALSE);
635+
636+ if (gtk_selection_data_get_target (selection_data) != katze_array_atom)
637+ return FALSE;
638+
639+ kad = g_malloc (sizeof (KatzeArrayData));
640+
641+ kad->items[0] = item;
642+
643+ gtk_selection_data_set (selection_data,
644+ katze_array_atom,
645+ 8, /* bytes */
646+ (void*)kad,
647+ sizeof (KatzeArrayData));
648+
649+ g_free (kad);
650+ return TRUE;
651+}
652+
653+KatzeArray*
654+katze_array_from_selection_data (GtkSelectionData* selection_data)
655+{
656+ GdkAtom katze_array_atom = gdk_atom_intern_static_string ("KATZE_ARRAY");
657+ KatzeArrayData *kad;
658+ KatzeArray *array;
659+ gint size;
660+ gint count;
661+ gint i;
662+
663+ g_return_val_if_fail (selection_data != NULL, FALSE);
664+
665+ if (gtk_selection_data_get_target (selection_data) != katze_array_atom)
666+ return NULL;
667+
668+ if (gtk_selection_data_get_format (selection_data) != 8)
669+ return NULL;
670+
671+ size = gtk_selection_data_get_length (selection_data);
672+ if (size <= 0)
673+ return NULL;
674+
675+ kad = (void*) gtk_selection_data_get_data (selection_data);
676+
677+ count = (size - G_STRUCT_OFFSET (KatzeArrayData, items))/sizeof(KatzeItem*);
678+
679+ array = katze_array_new (KATZE_TYPE_ARRAY);
680+
681+ for ( i = 0; i < count; i++ )
682+ katze_array_add_item (array, kad->items[i]);
683+
684+ return array;
685+}
686
687=== modified file 'katze/katze-utils.h'
688--- katze/katze-utils.h 2013-04-16 22:10:56 +0000
689+++ katze/katze-utils.h 2014-01-26 19:44:25 +0000
690@@ -88,6 +88,11 @@
691 GtkTreeModel** model,
692 GtkTreeIter* iter);
693
694+gint
695+katze_tree_view_get_selected_rows (GtkTreeView* treeview,
696+ GtkTreeModel** model,
697+ GList** rows);
698+
699 void
700 katze_bookmark_populate_tree_view (KatzeArray* array,
701 GtkTreeStore* model,
702@@ -138,6 +143,12 @@
703 void
704 katze_window_set_sensible_default_size (GtkWindow* window);
705
706+gboolean
707+selection_data_from_katze_item (GtkSelectionData* selection_data, KatzeItem* item);
708+
709+KatzeArray*
710+katze_array_from_selection_data (GtkSelectionData* selection_data);
711+
712 G_END_DECLS
713
714 #endif /* __KATZE_UTILS_H__ */
715
716=== modified file 'midori/midori-array.c'
717--- midori/midori-array.c 2013-08-05 19:52:52 +0000
718+++ midori/midori-array.c 2014-01-26 19:44:25 +0000
719@@ -1031,9 +1031,16 @@
720 || g_str_equal (name, "last_visit") || g_str_equal (name, "visit_count")
721 || g_str_equal (name, "pos_panel") || g_str_equal (name, "pos_bar"))
722 {
723+#if 0
724 gint value;
725 value = sqlite3_column_int64 (stmt, column);
726 katze_item_set_meta_integer (item, name, value);
727+#else
728+ /* use text to properly handle NULL values */
729+ const unsigned char* text;
730+ text = sqlite3_column_text (stmt, column);
731+ katze_item_set_meta_string (item, name, (gchar*)text);
732+#endif
733 }
734 else if (g_str_equal (name, "desc"))
735 {
736
737=== modified file 'midori/midori-bookmarks-db.c'
738--- midori/midori-bookmarks-db.c 2014-01-24 23:04:05 +0000
739+++ midori/midori-bookmarks-db.c 2014-01-26 19:44:25 +0000
740@@ -181,14 +181,29 @@
741 midori_bookmarks_db_get_item_parent (MidoriBookmarksDb* bookmarks,
742 gpointer item)
743 {
744+ gint64 parentid = katze_item_get_meta_integer (KATZE_ITEM (item), "parentid");
745+ KatzeItem *search = katze_item_new ();
746 KatzeArray* parent;
747- gint64 parentid;
748-
749- parentid = katze_item_get_meta_integer (KATZE_ITEM (item), "parentid");
750-
751- if (parentid == 0)
752- {
753+
754+ if (!parentid)
755+ {
756+ parentid = katze_item_get_meta_integer (KATZE_ITEM (bookmarks), "id");
757+ katze_item_set_meta_integer (KATZE_ITEM (item), "parentid", parentid);
758+ }
759+
760+ katze_item_set_meta_integer(search, "id", parentid);
761+
762+ parent = KATZE_ARRAY (g_hash_table_lookup (bookmarks->all_items, search));
763+
764+ g_object_unref (search);
765+
766+ if (!parent)
767+ {
768+ g_warning ("item parent not found\n");
769+
770 parent = KATZE_ARRAY (bookmarks);
771+ katze_item_set_meta_integer (KATZE_ITEM (item), "parentid",
772+ katze_item_get_meta_integer (KATZE_ITEM (bookmarks), "id"));
773 }
774 else
775 {
776@@ -267,7 +282,10 @@
777
778 g_return_if_fail (parent);
779
780- katze_array_update (parent);
781+ if (IS_MIDORI_BOOKMARKS_DB (parent))
782+ KATZE_ARRAY_CLASS (midori_bookmarks_db_parent_class)->update (parent);
783+ else
784+ katze_array_update (parent);
785 }
786
787 /**
788@@ -479,7 +497,7 @@
789 else if (old_parent && katze_item_get_meta_integer (old_parent, "id") > 0)
790 new_parentid = g_strdup_printf ("%" G_GINT64_FORMAT, katze_item_get_meta_integer (old_parent, "id"));
791 else
792- new_parentid = g_strdup_printf ("NULL");
793+ new_parentid = g_strdup ("NULL");
794
795 sqlcmd = sqlite3_mprintf (
796 "INSERT INTO bookmarks (id, parentid, title, uri, desc, toolbar, app) "
797@@ -557,7 +575,8 @@
798
799 sqlcmd = sqlite3_mprintf (
800 "UPDATE bookmarks SET "
801- "parentid=%q, title='%q', uri='%q', desc='%q', toolbar=%d, app=%d "
802+ "parentid=%q, title='%q', uri='%q', desc='%q', toolbar=%d, app=%d, "
803+ "pos_bar=%d, pos_panel=%d "
804 "WHERE id = %q ;",
805 parentid,
806 katze_item_get_name (item),
807@@ -565,6 +584,8 @@
808 katze_str_non_null (katze_item_get_meta_string (item, "desc")),
809 katze_item_get_meta_boolean (item, "toolbar"),
810 katze_item_get_meta_boolean (item, "app"),
811+ (gint)katze_item_get_meta_integer (item, "pos_bar"),
812+ (gint)katze_item_get_meta_integer (item, "pos_panel"),
813 id);
814
815 updated = TRUE;
816@@ -579,6 +600,14 @@
817 g_free (parentid);
818 g_free (id);
819
820+#ifdef DEBUG_DB_UPDATE
821+ g_print ("update:%s - parentid: %s, pos_bar:%d, pos_panel:%d\n",
822+ katze_item_get_name (item),
823+ parentid,
824+ (gint)katze_item_get_meta_integer (item, "pos_bar"),
825+ (gint)katze_item_get_meta_integer (item, "pos_panel"));
826+#endif
827+
828 return updated;
829 }
830
831@@ -710,10 +739,10 @@
832 g_return_val_if_fail (errmsg != NULL, NULL);
833
834 database = midori_bookmarks_database_new (&error);
835-
836+
837 if (error != NULL)
838 {
839- *errmsg = g_strdup (error->message);
840+ *errmsg = g_strdup (error->message);
841 g_error_free (error);
842 return NULL;
843 }
844@@ -724,7 +753,10 @@
845 if (midori_debug ("bookmarks"))
846 sqlite3_trace (db, midori_bookmarks_db_dbtracer, NULL);
847
848- bookmarks = MIDORI_BOOKMARKS_DB (g_object_new (TYPE_MIDORI_BOOKMARKS_DB, NULL));
849+ bookmarks = MIDORI_BOOKMARKS_DB (g_object_new (TYPE_MIDORI_BOOKMARKS_DB,
850+ "type", KATZE_TYPE_ITEM,
851+ NULL));
852+
853 bookmarks->db = db;
854
855 g_object_set_data (G_OBJECT (bookmarks), "db", db);
856@@ -776,6 +808,7 @@
857 katze_item_set_meta_integer (item, "parentid", parentid);
858 midori_bookmarks_db_add_item (bookmarks, item);
859 }
860+
861 g_list_free (list);
862 }
863
864@@ -859,7 +892,7 @@
865 * @array: the main bookmark array
866 * @sqlcmd: the sqlcmd to execute
867 *
868- * Internal function that process the requested @sqlcmd.
869+ * Internal function that processes the requested @sqlcmd.
870 *
871 * Return value: a #KatzeArray on success, %NULL otherwise
872 **/
873@@ -885,6 +918,10 @@
874 * @fields: comma separated list of fields
875 * @condition: condition, like "folder = '%q'"
876 * @value: a value to be inserted if @condition contains %q
877+ * @order: a value to be inserted in "ORDER BY"
878+ * default order is :
879+ * (uri='') ASC, title DESC
880+ * given @order, when not %NULL, replaces the term: (uri='') ASC
881 * @recursive: if %TRUE include children
882 *
883 * Stores the result in a #KatzeArray.
884@@ -898,6 +935,7 @@
885 const gchar* fields,
886 const gchar* condition,
887 const gchar* value,
888+ const gchar* order,
889 gboolean recursive)
890 {
891 gchar* sqlcmd;
892@@ -911,7 +949,8 @@
893 g_return_val_if_fail (condition, NULL);
894
895 sqlcmd = g_strdup_printf ("SELECT %s FROM bookmarks WHERE %s "
896- "ORDER BY (uri='') ASC, title DESC", fields, condition);
897+ "ORDER BY %s, title DESC", fields, condition, order ? order : "(uri='') ASC");
898+
899 if (strstr (condition, "%q"))
900 {
901 sqlcmd_value = sqlite3_mprintf (sqlcmd, value ? value : "");
902@@ -932,7 +971,7 @@
903 gchar* parentid = g_strdup_printf ("%" G_GINT64_FORMAT,
904 katze_item_get_meta_integer (item, "id"));
905 KatzeArray* subarray = midori_bookmarks_db_query_recursive (bookmarks,
906- fields, "parentid=%q", parentid, TRUE);
907+ fields, "parentid=%q", parentid, order, TRUE);
908 KatzeItem* subitem;
909 GList* sublist;
910
911@@ -1097,3 +1136,55 @@
912 value, id,
913 recursive);
914 }
915+
916+/**
917+ * midori_bookmarks_db_populate_folder:
918+ **/
919+
920+void
921+midori_bookmarks_db_populate_folder (MidoriBookmarksDb* bookmarks,
922+ KatzeArray *folder)
923+{
924+ const gchar* id = katze_item_get_meta_string (KATZE_ITEM (folder), "id");
925+ const gchar *condition;
926+ KatzeArray* array;
927+ KatzeItem* item;
928+ GList* list;
929+
930+ if (id == NULL)
931+ {
932+ condition = "parentid is NULL";
933+ }
934+ else
935+ {
936+ condition = "parentid = %q";
937+ }
938+
939+ array = midori_bookmarks_db_query_recursive (bookmarks,
940+ "id, title, parentid, uri, app, pos_panel, pos_bar", condition, id, "(uri='') ASC, pos_panel ASC", FALSE);
941+
942+ if (IS_MIDORI_BOOKMARKS_DB (folder))
943+ {
944+ KATZE_ARRAY_FOREACH_ITEM_L (item, folder, list)
945+ {
946+ KATZE_ARRAY_CLASS (midori_bookmarks_db_parent_class)->remove_item (folder, item);
947+ }
948+
949+ KATZE_ARRAY_FOREACH_ITEM_L (item, array, list)
950+ {
951+ KATZE_ARRAY_CLASS (midori_bookmarks_db_parent_class)->add_item (folder, item);
952+ }
953+ }
954+ else
955+ {
956+ katze_array_clear(folder);
957+
958+ KATZE_ARRAY_FOREACH_ITEM_L (item, array, list)
959+ {
960+ katze_array_add_item (folder, item);
961+ }
962+ }
963+
964+ g_object_unref (array);
965+}
966+
967
968=== modified file 'midori/midori-bookmarks-db.h'
969--- midori/midori-bookmarks-db.h 2013-09-17 19:34:23 +0000
970+++ midori/midori-bookmarks-db.h 2014-01-26 19:44:25 +0000
971@@ -62,19 +62,21 @@
972 const gchar* fields,
973 const gchar* condition,
974 const gchar* value,
975+ const gchar* order,
976 gboolean recursive);
977
978 gint64
979 midori_bookmarks_db_count_recursive (MidoriBookmarksDb* bookmarks,
980- const gchar* condition,
981+ const gchar* condition,
982 const gchar* value,
983- KatzeItem* folder,
984- gboolean recursive);
985-
986-gint64
987-midori_bookmarks_insert_item_db (sqlite3* db,
988- KatzeItem* item,
989- gint64 parentid);
990+ KatzeItem* folder,
991+ gboolean recursive);
992+
993+void
994+midori_bookmarks_db_on_quit (MidoriBookmarksDb* array);
995+
996+void
997+midori_bookmarks_db_populate_folder (MidoriBookmarksDb* bookmarks,
998+ KatzeArray *folder);
999
1000 #endif /* !__MIDORI_BOOKMARKS_DB_H__ */
1001-
1002
1003=== modified file 'midori/midori-browser.c'
1004--- midori/midori-browser.c 2014-01-06 23:05:10 +0000
1005+++ midori/midori-browser.c 2014-01-26 19:44:25 +0000
1006@@ -99,9 +99,24 @@
1007 guint maximum_history_age;
1008 guint last_web_search;
1009
1010+ struct _BookmarkbarDNDControl
1011+ {
1012+ GtkTargetList* source_targets;
1013+ GtkTargetList* dest_targets;
1014+
1015+ struct _BookmarkbarDragDestControl
1016+ {
1017+ GtkToolItem* highligth_item;
1018+ gboolean hovering;
1019+ int n;
1020+ } drag_dest_control;
1021+ } bookmarkbar_dnd_control;
1022 gboolean bookmarkbar_populate;
1023 };
1024
1025+typedef struct _BookmarkbarDNDControl BookmarkbarDNDControl;
1026+typedef struct _BookmarkbarDragDestControl BookmarkbarDragDestControl;
1027+
1028 G_DEFINE_TYPE (MidoriBrowser, midori_browser, GTK_TYPE_WINDOW)
1029
1030 enum
1031@@ -173,11 +188,22 @@
1032 midori_bookmarkbar_populate (MidoriBrowser* browser);
1033 static void
1034 midori_bookmarkbar_populate_idle (MidoriBrowser* browser);
1035+void
1036+midori_bookmarkbar_position_items (GtkWidget* widget);
1037
1038 static void
1039 midori_bookmarkbar_clear (GtkWidget* toolbar);
1040
1041 static void
1042+bookmarkbar_dnd_control_init (BookmarkbarDNDControl* control, MidoriBrowser* browser);
1043+
1044+static void
1045+bookmarkbar_dnd_control_finalize (BookmarkbarDNDControl* control);
1046+
1047+static void
1048+bookmarkbar_dnd_control_install (BookmarkbarDNDControl* control, GtkToolbar* bookmarkbar);
1049+
1050+static void
1051 _midori_browser_set_toolbar_style (MidoriBrowser* browser,
1052 MidoriToolbarStyle toolbar_style);
1053
1054@@ -716,6 +742,8 @@
1055 const gchar* title)
1056 {
1057 const gchar* custom_title = midori_settings_get_custom_title (MIDORI_SETTINGS (browser->settings));
1058+ GtkAction* action = _action_by_name (browser, "Location");
1059+ midori_location_action_set_title (MIDORI_LOCATION_ACTION (action), title);
1060 if (custom_title && *custom_title)
1061 gtk_window_set_title (GTK_WINDOW (browser), custom_title);
1062 else if (katze_object_get_boolean (browser->settings, "enable-private-browsing"))
1063@@ -885,14 +913,14 @@
1064 gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo));
1065
1066 renderer = gtk_cell_renderer_pixbuf_new ();
1067- g_object_set (G_OBJECT (renderer),
1068+ g_object_set (G_OBJECT (renderer),
1069 "stock-id", GTK_STOCK_DIRECTORY,
1070 "stock-size", GTK_ICON_SIZE_MENU,
1071 NULL);
1072 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE);
1073
1074 renderer = katze_cell_renderer_combobox_text_new ();
1075- g_object_set (G_OBJECT (renderer),
1076+ g_object_set (G_OBJECT (renderer),
1077 "width-chars", 40, /* FIXME: figure out a way to define an acceptable string length */
1078 "ellipsize", PANGO_ELLIPSIZE_END,
1079 "unfolded-text", _("Select [text]"),
1080@@ -916,7 +944,7 @@
1081 folders = g_list_append (folders, folder);
1082 }
1083
1084- sqlite3_clear_bindings (statement);
1085+ sqlite3_clear_bindings (statement);
1086 sqlite3_reset (statement);
1087 }
1088
1089@@ -1025,7 +1053,7 @@
1090 static gint64
1091 midori_bookmark_folder_button_get_active (GtkWidget* combo)
1092 {
1093- gint64 id = 0;
1094+ gint64 id = -1;
1095 GtkTreeIter iter;
1096
1097 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo), 0);
1098@@ -1902,6 +1930,8 @@
1099 KatzeItem* item = midori_view_get_proxy_item (MIDORI_VIEW (view));
1100 guint n;
1101
1102+ g_object_set (view, "dnd-targets", browser->bookmarkbar_dnd_control.source_targets, NULL);
1103+
1104 midori_browser_connect_tab (browser, view);
1105
1106 if (!katze_item_get_meta_boolean (item, "append") &&
1107@@ -3107,29 +3137,17 @@
1108 KatzeArray* folder,
1109 MidoriBrowser* browser)
1110 {
1111- KatzeArray* bookmarks;
1112- const gchar* id = katze_item_get_meta_string (KATZE_ITEM (folder), "id");
1113- gchar* condition;
1114-
1115 if (browser->bookmarks == NULL)
1116 return FALSE;
1117
1118- if (id == NULL)
1119- condition = "parentid is NULL";
1120- else
1121- condition = "parentid = %q";
1122-
1123- bookmarks = midori_bookmarks_db_query_recursive (browser->bookmarks,
1124- "id, title, parentid, uri, app, pos_panel, pos_bar", condition, id, FALSE);
1125- if (!bookmarks)
1126- return FALSE;
1127+ midori_bookmarks_db_populate_folder (browser->bookmarks, folder);
1128
1129 /* Clear items from dummy array here */
1130 gtk_container_foreach (GTK_CONTAINER (menu),
1131 (GtkCallback)(gtk_widget_destroy), NULL);
1132
1133 /* "Import Bookmarks" and "Export Bookmarks" at the top */
1134- if (id == NULL)
1135+ if (folder == KATZE_ARRAY (browser->bookmarks))
1136 {
1137 GtkWidget* menuitem;
1138 menuitem = gtk_action_create_menu_item (_action_by_name (browser, "BookmarksImport"));
1139@@ -3143,7 +3161,7 @@
1140 gtk_widget_show (menuitem);
1141 }
1142
1143- if (katze_array_is_empty (bookmarks))
1144+ if (katze_array_is_empty (folder))
1145 {
1146 GtkWidget* menuitem = gtk_image_menu_item_new_with_label (_("Empty"));
1147 gtk_widget_set_sensitive (menuitem, FALSE);
1148@@ -3152,7 +3170,7 @@
1149 return TRUE;
1150 }
1151
1152- katze_array_action_generate_menu (KATZE_ARRAY_ACTION (action), bookmarks,
1153+ katze_array_action_generate_menu (KATZE_ARRAY_ACTION (action), folder,
1154 menu, GTK_WIDGET (browser));
1155 return TRUE;
1156 }
1157@@ -4502,7 +4520,7 @@
1158
1159 error = NULL;
1160 bookmarks = midori_bookmarks_db_query_recursive (browser->bookmarks,
1161- "*", "parentid IS NULL", NULL, TRUE);
1162+ "*", "parentid IS NULL", NULL, NULL, TRUE);
1163 if (!midori_array_to_file (bookmarks, path, format, &error))
1164 {
1165 sokoke_message_dialog (GTK_MESSAGE_ERROR,
1166@@ -4903,6 +4921,8 @@
1167 uri = midori_view_get_display_uri (new_view);
1168 midori_browser_set_title (browser, midori_view_get_display_title (new_view));
1169 action = _action_by_name (browser, "Location");
1170+ midori_location_action_set_icon (MIDORI_LOCATION_ACTION (action), midori_view_get_icon_uri (new_view));
1171+ midori_location_action_set_title (MIDORI_LOCATION_ACTION (action), midori_view_get_display_title (new_view));
1172 midori_location_action_set_text (MIDORI_LOCATION_ACTION (action), uri);
1173 if (midori_paths_get_runtime_mode () == MIDORI_RUNTIME_MODE_APP)
1174 gtk_window_set_icon (GTK_WINDOW (browser), midori_view_get_icon (new_view));
1175@@ -5876,8 +5896,6 @@
1176 g_object_connect (action,
1177 "signal::populate-popup",
1178 _action_tools_populate_popup, browser,
1179- "signal::activate-item-alt",
1180- midori_bookmarkbar_activate_item_alt, browser,
1181 NULL);
1182 gtk_action_group_add_action (browser->action_group, action);
1183 g_object_unref (action);
1184@@ -6003,7 +6021,11 @@
1185 g_signal_connect (browser->bookmarkbar, "popup-context-menu",
1186 G_CALLBACK (midori_browser_toolbar_popup_context_menu_cb), browser);
1187
1188- /* Create the panel */
1189+ bookmarkbar_dnd_control_init (&browser->bookmarkbar_dnd_control, browser);
1190+ bookmarkbar_dnd_control_install (&browser->bookmarkbar_dnd_control,
1191+ GTK_TOOLBAR (browser->bookmarkbar));
1192+
1193+ /* Create the panel */
1194 hpaned = gtk_hpaned_new ();
1195 g_signal_connect (hpaned, "notify::position",
1196 G_CALLBACK (midori_panel_notify_position_cb),
1197@@ -6101,6 +6123,8 @@
1198 {
1199 MidoriBrowser* browser = MIDORI_BROWSER (object);
1200
1201+ bookmarkbar_dnd_control_finalize (&browser->bookmarkbar_dnd_control);
1202+
1203 katze_assign (browser->statusbar_text, NULL);
1204
1205 katze_object_assign (browser->settings, NULL);
1206@@ -6624,7 +6648,8 @@
1207
1208 static void
1209 midori_bookmarkbar_insert_item (GtkWidget* toolbar,
1210- KatzeItem* item)
1211+ KatzeItem* item,
1212+ gint pos)
1213 {
1214 MidoriBrowser* browser = midori_browser_get_for_widget (toolbar);
1215 GtkAction* action = _action_by_name (browser, "Bookmarks");
1216@@ -6643,19 +6668,19 @@
1217 else /* Separator */
1218 gtk_tool_item_set_use_drag_window (toolitem, TRUE);
1219
1220+ gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, pos);
1221 gtk_widget_show (GTK_WIDGET (toolitem));
1222- gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
1223 }
1224
1225 static void
1226 midori_bookmarkbar_add_item_cb (KatzeArray* bookmarks,
1227- KatzeItem* item,
1228- MidoriBrowser* browser)
1229+ KatzeItem* item,
1230+ MidoriBrowser* browser)
1231 {
1232 if (gtk_widget_get_visible (browser->bookmarkbar))
1233 midori_bookmarkbar_populate (browser);
1234 else if (katze_item_get_meta_boolean (item, "toolbar"))
1235- _action_set_active (browser, "Bookmarkbar", TRUE);
1236+ _action_set_active (browser, "Bookmarkbar", TRUE);
1237 midori_browser_update_history (item, "bookmark", "created");
1238 }
1239
1240@@ -6702,7 +6727,7 @@
1241 gtk_separator_tool_item_new (), -1);
1242
1243 array = midori_bookmarks_db_query_recursive (browser->bookmarks,
1244- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "toolbar = 1", NULL, FALSE);
1245+ "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "toolbar = 1", NULL, "pos_bar ASC", FALSE);
1246 if (!array)
1247 {
1248 _action_set_sensitive (browser, "BookmarkAdd", FALSE);
1249@@ -6712,10 +6737,12 @@
1250
1251 KATZE_ARRAY_FOREACH_ITEM (item, array)
1252 {
1253- midori_bookmarkbar_insert_item (browser->bookmarkbar, item);
1254+ midori_bookmarkbar_insert_item (browser->bookmarkbar, item, -1);
1255 }
1256 _action_set_sensitive (browser, "BookmarkAdd", TRUE);
1257 _action_set_sensitive (browser, "BookmarkFolderAdd", TRUE);
1258+
1259+ midori_bookmarkbar_position_items (browser->bookmarkbar);
1260 }
1261
1262 static void
1263@@ -6756,6 +6783,10 @@
1264 midori_bookmarkbar_remove_item_cb, browser);
1265 }
1266
1267+ g_object_set (G_OBJECT (_action_by_name (browser, "Bookmarks")),
1268+ "array", KATZE_ARRAY (bookmarks),
1269+ NULL);
1270+
1271 settings = midori_browser_get_settings (browser);
1272 g_signal_handlers_disconnect_by_func (settings,
1273 midori_browser_show_bookmarkbar_notify_value_cb, browser);
1274@@ -7481,3 +7512,331 @@
1275
1276 g_signal_emit (browser, signals[QUIT], 0);
1277 }
1278+
1279+static void
1280+bookmarkbar_drag_dest_control_init (BookmarkbarDragDestControl* control)
1281+{
1282+ control->highligth_item = NULL;
1283+ control->hovering = FALSE;
1284+}
1285+
1286+static GtkTargetEntry bookmarkbar_dnd_target_entries[]=
1287+{
1288+ {"KATZE_ARRAY", GTK_TARGET_SAME_APP, 0},
1289+};
1290+
1291+#define BOOKMARKBAR_DND_NB_TARGET_ENTRIES \
1292+ G_N_ELEMENTS (bookmarkbar_dnd_target_entries)
1293+
1294+static void
1295+bookmarkbar_dnd_control_init (BookmarkbarDNDControl* control, MidoriBrowser* browser)
1296+{
1297+ bookmarkbar_drag_dest_control_init (&control->drag_dest_control);
1298+
1299+ control->source_targets = gtk_target_list_new (
1300+ bookmarkbar_dnd_target_entries, BOOKMARKBAR_DND_NB_TARGET_ENTRIES);
1301+ gtk_target_list_add_uri_targets (control->source_targets, 0);
1302+ gtk_target_list_add_text_targets (control->source_targets, 0);
1303+
1304+ control->dest_targets = gtk_target_list_new (
1305+ bookmarkbar_dnd_target_entries, BOOKMARKBAR_DND_NB_TARGET_ENTRIES);
1306+
1307+ g_object_set (_action_by_name (browser, "Bookmarks"), "dnd-targets", control->source_targets, NULL);
1308+ g_object_set (_action_by_name (browser, "Location"), "dnd-targets", control->source_targets, NULL);
1309+}
1310+
1311+static void
1312+bookmarkbar_dnd_control_finalize (BookmarkbarDNDControl* control)
1313+{
1314+ gtk_target_list_unref (control->source_targets);
1315+ gtk_target_list_unref (control->dest_targets);
1316+ control->source_targets = NULL;
1317+ control->dest_targets = NULL;
1318+}
1319+
1320+
1321+static GtkToolItem*
1322+bookmarkbar_drag_highlight_item (GdkDragContext *context)
1323+{
1324+
1325+ GtkToolItem* highlight_item = NULL;
1326+ GtkWidget* source = gtk_drag_get_source_widget (context);
1327+
1328+ if (source)
1329+ {
1330+ KatzeItem* item = (KatzeItem*)g_object_get_data (G_OBJECT (source), "KatzeItem");
1331+
1332+ if (!item && GTK_IS_TOOL_ITEM (gtk_widget_get_parent (source)))
1333+ item = (KatzeItem*)g_object_get_data (G_OBJECT (gtk_widget_get_parent (source)), "KatzeItem");
1334+
1335+ if (KATZE_ITEM_IS_BOOKMARK (item))
1336+ highlight_item = gtk_tool_button_new (NULL, "");
1337+ else if (KATZE_ITEM_IS_FOLDER (item))
1338+ highlight_item = gtk_toggle_tool_button_new ();
1339+
1340+ if (highlight_item)
1341+ {
1342+ GtkToolButton* toolbutton = GTK_TOOL_BUTTON (highlight_item);
1343+ GtkWidget* image = katze_item_get_image (item, GTK_WIDGET (highlight_item));
1344+ const gchar* title = katze_item_get_name (item);
1345+ GtkWidget* label = gtk_label_new (NULL);
1346+
1347+ gtk_widget_show (image);
1348+
1349+ if (!title || !*title)
1350+ title = katze_item_get_uri (item);
1351+
1352+ /* FIXME: Should text direction be respected here? */
1353+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1354+ gtk_label_set_max_width_chars (GTK_LABEL (label), 25);
1355+ gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_MIDDLE);
1356+ gtk_label_set_text (GTK_LABEL (label), title);
1357+ gtk_widget_show (label);
1358+
1359+ gtk_tool_button_set_icon_widget (toolbutton, image);
1360+ gtk_tool_button_set_label_widget (toolbutton, label);
1361+
1362+ gtk_tool_item_set_visible_horizontal (highlight_item, TRUE);
1363+ gtk_tool_item_set_visible_vertical (highlight_item, TRUE);
1364+ gtk_tool_item_set_is_important (highlight_item, TRUE);
1365+ }
1366+ }
1367+
1368+ if (!highlight_item)
1369+ highlight_item = gtk_separator_tool_item_new ();
1370+
1371+ gtk_widget_show (GTK_WIDGET (highlight_item));
1372+ return highlight_item;
1373+}
1374+
1375+static gboolean
1376+bookmarkbar_drag_dest_drag_motion_cb (GtkWidget *widget,
1377+ GdkDragContext *context,
1378+ gint x, gint y, guint t,
1379+ BookmarkbarDragDestControl* control)
1380+{
1381+ GtkToolbar* toolbar = GTK_TOOLBAR (widget);
1382+ gint n = gtk_toolbar_get_drop_index (toolbar, x, y);
1383+
1384+ if (!control->highligth_item)
1385+ {
1386+ GtkToolItem* highlight_item = bookmarkbar_drag_highlight_item (context);
1387+ g_object_ref (highlight_item);
1388+
1389+ gtk_toolbar_set_drop_highlight_item (toolbar,
1390+ highlight_item,
1391+ n);
1392+ control->highligth_item = highlight_item;
1393+ control->n = n;
1394+ }
1395+ else if (n != control->n)
1396+ {
1397+ gtk_toolbar_set_drop_highlight_item (toolbar,
1398+ control->highligth_item,
1399+ n);
1400+ control->n = n;
1401+ }
1402+ return FALSE;
1403+}
1404+
1405+static void
1406+bookmarkbar_drag_dest_drag_leave_cb (GtkWidget *widget,
1407+ GdkDragContext *context,
1408+ guint time,
1409+ BookmarkbarDragDestControl* control)
1410+{
1411+ GtkToolbar* toolbar = GTK_TOOLBAR (widget);
1412+
1413+ if (control->highligth_item)
1414+ {
1415+ gtk_toolbar_set_drop_highlight_item (toolbar, NULL, -1);
1416+ g_object_unref (control->highligth_item);
1417+ control->highligth_item = NULL;
1418+ }
1419+}
1420+
1421+void
1422+midori_bookmarkbar_position_items (GtkWidget *widget)
1423+{
1424+ GtkToolbar* toolbar = GTK_TOOLBAR (widget);
1425+ MidoriBrowser* browser = midori_browser_get_for_widget (widget);
1426+ gint n = gtk_toolbar_get_n_items (toolbar);
1427+ gint i;
1428+ gint pos;
1429+
1430+ pos = 0;
1431+ for (i = 0; i < n ; i++ )
1432+ {
1433+ GtkWidget* toolitem = GTK_WIDGET (gtk_toolbar_get_nth_item (toolbar, i));
1434+ KatzeItem* local_item = (KatzeItem*)g_object_get_data (G_OBJECT (toolitem), "KatzeItem");
1435+
1436+ if (!KATZE_IS_ITEM (local_item))
1437+ continue;
1438+
1439+ if (pos != katze_item_get_meta_integer (local_item, "pos_bar"))
1440+ katze_item_set_meta_integer (local_item, "pos_bar", pos);
1441+
1442+ midori_bookmarks_db_update_item (browser->bookmarks, local_item);
1443+
1444+ pos++;
1445+ }
1446+}
1447+
1448+static void
1449+bookmarkbar_drag_dest_drag_data_received_cb (GtkWidget *widget,
1450+ GdkDragContext *drag_context,
1451+ gint x,
1452+ gint y,
1453+ GtkSelectionData *data,
1454+ guint info,
1455+ guint time,
1456+ BookmarkbarDragDestControl* control)
1457+{
1458+ GtkToolbar* toolbar = GTK_TOOLBAR (widget);
1459+ MidoriBrowser* browser = midori_browser_get_for_widget (widget);
1460+ gboolean success = FALSE;
1461+ gboolean delete = FALSE;
1462+
1463+ g_assert (browser);
1464+
1465+ g_signal_handlers_block_by_func (browser->bookmarks,
1466+ midori_bookmarkbar_add_item_cb, browser);
1467+ g_signal_handlers_block_by_func (browser->bookmarks,
1468+ midori_bookmarkbar_update_item_cb, browser);
1469+ g_signal_handlers_block_by_func (browser->bookmarks,
1470+ midori_bookmarkbar_remove_item_cb, browser);
1471+
1472+ if (gtk_selection_data_get_length (data) >= 0)
1473+ {
1474+ switch (gdk_drag_context_get_selected_action (drag_context))
1475+ {
1476+ case GDK_ACTION_MOVE:
1477+ delete = TRUE;
1478+ case GDK_ACTION_COPY:
1479+ if (gtk_selection_data_get_format (data) == 8)
1480+ {
1481+ KatzeArray *array = katze_array_from_selection_data (data);
1482+
1483+ if (array)
1484+ {
1485+ KatzeItem *item;
1486+ KATZE_ARRAY_FOREACH_ITEM (item, array)
1487+ {
1488+ gint old_pos = gtk_toolbar_get_n_items(toolbar);
1489+ gint dest_pos = gtk_toolbar_get_drop_index (toolbar, x, y);
1490+ gboolean is_bookmark = katze_item_get_meta_string (item, "id") ? TRUE : FALSE ;
1491+ gboolean from_toolbar = is_bookmark && katze_item_get_meta_boolean (item, "toolbar");
1492+
1493+ if (midori_debug("bookmarks"))
1494+ {
1495+ g_print ("bookmarkbar received KATZE_ITEM(bookmark:%d, toolbar:%d, title:%s) \n",
1496+ is_bookmark, from_toolbar, katze_item_get_name (item));
1497+ }
1498+
1499+ if (from_toolbar)
1500+ {
1501+ GtkToolItem* toolitem = NULL;
1502+ KatzeItem* local_item = NULL;
1503+
1504+ old_pos = katze_item_get_meta_integer (item, "pos_bar");
1505+
1506+ if ((dest_pos != old_pos)
1507+ && (toolitem = gtk_toolbar_get_nth_item (toolbar,
1508+ old_pos))
1509+ && (local_item = (KatzeItem*)g_object_get_data (
1510+ G_OBJECT (toolitem), "KatzeItem")))
1511+ {
1512+ midori_bookmarkbar_insert_item (widget,
1513+ local_item, dest_pos);
1514+
1515+ if (old_pos < dest_pos)
1516+ old_pos++;
1517+
1518+ /* FIXME: force a move */
1519+ delete = TRUE;
1520+ }
1521+ else
1522+ delete = FALSE;
1523+ }
1524+ else if (!is_bookmark)
1525+ {
1526+ item = katze_item_copy (item);
1527+
1528+ katze_item_set_meta_integer (item, "toolbar", TRUE);
1529+
1530+ midori_bookmarkbar_insert_item (widget,
1531+ item, dest_pos);
1532+
1533+ midori_bookmarks_db_add_item (browser->bookmarks, item);
1534+
1535+ if (old_pos > dest_pos)
1536+ old_pos++;
1537+
1538+ delete = FALSE;
1539+ }
1540+ else
1541+ {
1542+ katze_item_set_meta_integer (item, "toolbar", TRUE);
1543+
1544+ midori_bookmarkbar_insert_item (widget,
1545+ item, dest_pos);
1546+
1547+ if (old_pos > dest_pos)
1548+ old_pos++;
1549+
1550+ delete = FALSE;
1551+ }
1552+ }
1553+
1554+ g_object_unref (array);
1555+ success = TRUE;
1556+ break;
1557+ }
1558+ }
1559+ default:
1560+ break;
1561+ }
1562+ }
1563+
1564+ if (!success)
1565+ delete = FALSE;
1566+
1567+ gtk_drag_finish (drag_context, success, delete, time);
1568+
1569+ if (success && !delete)
1570+ {
1571+ midori_bookmarkbar_position_items (GTK_WIDGET (toolbar));
1572+ }
1573+
1574+ g_signal_handlers_unblock_by_func (browser->bookmarks,
1575+ midori_bookmarkbar_add_item_cb, browser);
1576+ g_signal_handlers_unblock_by_func (browser->bookmarks,
1577+ midori_bookmarkbar_update_item_cb, browser);
1578+ g_signal_handlers_unblock_by_func (browser->bookmarks,
1579+ midori_bookmarkbar_remove_item_cb, browser);
1580+}
1581+
1582+static void
1583+bookmarkbar_drag_dest_control_install (BookmarkbarDNDControl* control,
1584+ GtkToolbar* bookmarkbar)
1585+{
1586+ GtkWidget* widget = GTK_WIDGET (bookmarkbar);
1587+ gtk_drag_dest_set (widget, GTK_DEST_DEFAULT_ALL, NULL, 0,
1588+ GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_LINK);
1589+ gtk_drag_dest_set_target_list (widget, control->dest_targets);
1590+ g_signal_connect (bookmarkbar, "drag-motion",
1591+ G_CALLBACK (bookmarkbar_drag_dest_drag_motion_cb),
1592+ &control->drag_dest_control);
1593+ g_signal_connect (bookmarkbar, "drag-leave",
1594+ G_CALLBACK (bookmarkbar_drag_dest_drag_leave_cb),
1595+ &control->drag_dest_control);
1596+ g_signal_connect (bookmarkbar, "drag-data-received",
1597+ G_CALLBACK (bookmarkbar_drag_dest_drag_data_received_cb),
1598+ &control->drag_dest_control);
1599+}
1600+
1601+static void
1602+bookmarkbar_dnd_control_install (BookmarkbarDNDControl* control, GtkToolbar* bookmarkbar)
1603+{
1604+ bookmarkbar_drag_dest_control_install (control, bookmarkbar);
1605+}
1606
1607=== modified file 'midori/midori-locationaction.c'
1608--- midori/midori-locationaction.c 2013-11-05 14:51:49 +0000
1609+++ midori/midori-locationaction.c 2014-01-26 19:44:25 +0000
1610@@ -43,6 +43,8 @@
1611 gint completion_index;
1612 GtkWidget* entry;
1613 KatzeArray* history;
1614+ KatzeItem* item;
1615+ GtkTargetList* dnd_targets;
1616 };
1617
1618 struct _MidoriLocationActionClass
1619@@ -58,7 +60,8 @@
1620
1621 PROP_PROGRESS,
1622 PROP_SECONDARY_ICON,
1623- PROP_HISTORY
1624+ PROP_HISTORY,
1625+ PROP_DND_TARGETS
1626 };
1627
1628 enum
1629@@ -222,6 +225,22 @@
1630 "The list of history items",
1631 KATZE_TYPE_ARRAY,
1632 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1633+
1634+ /**
1635+ * MidoriLocationAction:dnd-targets:
1636+ *
1637+ * GtkTargetList* the location action supports.
1638+ *
1639+ * Since: 0.5.2
1640+ **/
1641+ g_object_class_install_property (gobject_class,
1642+ PROP_DND_TARGETS,
1643+ g_param_spec_pointer (
1644+ "dnd-targets",
1645+ "DNDTargets",
1646+ "GtkTargetList* the location action supports",
1647+ G_PARAM_READABLE|G_PARAM_WRITABLE));
1648+
1649 }
1650
1651 gchar*
1652@@ -813,6 +832,8 @@
1653 {
1654 location_action->progress = 0.0;
1655 location_action->completion_index = -1;
1656+ location_action->item = katze_item_new ();
1657+ location_action->dnd_targets = NULL;
1658 }
1659
1660 static void
1661@@ -832,6 +853,8 @@
1662 }
1663 katze_object_assign (location_action->history, NULL);
1664
1665+ g_object_unref (location_action->item);
1666+
1667 G_OBJECT_CLASS (midori_location_action_parent_class)->finalize (object);
1668 }
1669
1670@@ -858,6 +881,9 @@
1671 katze_assign (location_action->history, g_value_dup_object (value));
1672 break;
1673 }
1674+ case PROP_DND_TARGETS:
1675+ location_action->dnd_targets = (GtkTargetList*)g_value_get_pointer (value);
1676+ break;
1677 default:
1678 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1679 break;
1680@@ -883,6 +909,9 @@
1681 case PROP_HISTORY:
1682 g_value_set_object (value, location_action->history);
1683 break;
1684+ case PROP_DND_TARGETS:
1685+ g_value_set_pointer (value, location_action->dnd_targets);
1686+ break;
1687 default:
1688 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1689 break;
1690@@ -917,20 +946,19 @@
1691 GtkSelectionData* data,
1692 guint info,
1693 guint32 time,
1694- GtkAction* action)
1695+ MidoriLocationAction* action)
1696 {
1697 if (gtk_entry_get_current_icon_drag_source (GTK_ENTRY (entry)) == GTK_ENTRY_ICON_PRIMARY)
1698 {
1699- const gchar* uri = gtk_entry_get_text (GTK_ENTRY (entry));
1700- gchar** uris = g_strsplit (uri, uri, 1);
1701- gtk_selection_data_set_uris (data, uris);
1702- g_strfreev (uris);
1703+ katze_item_set_uri (action->item, gtk_entry_get_text (GTK_ENTRY (entry)));
1704+ selection_data_from_katze_item (data, action->item);
1705 }
1706 }
1707
1708 static GtkWidget*
1709 midori_location_action_create_tool_item (GtkAction* action)
1710 {
1711+ MidoriLocationAction* location_action = MIDORI_LOCATION_ACTION (action);
1712 GtkWidget* toolitem;
1713 GtkWidget* alignment;
1714 GtkWidget* entry;
1715@@ -953,10 +981,18 @@
1716 gtk_entry_set_icon_activatable (GTK_ENTRY (entry),
1717 GTK_ENTRY_ICON_SECONDARY, TRUE);
1718
1719- targetlist = gtk_target_list_new (NULL, 0);
1720- gtk_target_list_add_uri_targets (targetlist, 0);
1721- gtk_entry_set_icon_drag_source (GTK_ENTRY (entry), GTK_ENTRY_ICON_PRIMARY, targetlist, GDK_ACTION_ASK | GDK_ACTION_COPY | GDK_ACTION_LINK);
1722- gtk_target_list_unref (targetlist);
1723+ g_object_set_data (G_OBJECT (entry), "KatzeItem", MIDORI_LOCATION_ACTION (action)->item);
1724+
1725+ if (location_action->dnd_targets)
1726+ gtk_entry_set_icon_drag_source (GTK_ENTRY (entry), GTK_ENTRY_ICON_PRIMARY,
1727+ location_action->dnd_targets, GDK_ACTION_ASK | GDK_ACTION_COPY | GDK_ACTION_LINK);
1728+ else
1729+ {
1730+ targetlist = gtk_target_list_new (NULL, 0);
1731+ gtk_target_list_add_uri_targets (targetlist, 0);
1732+ gtk_entry_set_icon_drag_source (GTK_ENTRY (entry), GTK_ENTRY_ICON_PRIMARY, targetlist, GDK_ACTION_ASK | GDK_ACTION_COPY | GDK_ACTION_LINK);
1733+ gtk_target_list_unref (targetlist);
1734+ }
1735 g_signal_connect (entry, "drag-data-get",
1736 G_CALLBACK (midori_location_action_entry_drag_data_get_cb), action);
1737 gtk_widget_show (entry);
1738@@ -1630,6 +1666,38 @@
1739 }
1740
1741 /**
1742+ * midori_location_action_set_icon:
1743+ * @location_action: a #MidoriLocationAction
1744+ * @icon: an icon
1745+ *
1746+ * Sets the entry icon to @icon.
1747+ *
1748+ * Since: 0.5.2
1749+ **/
1750+void
1751+midori_location_action_set_icon (MidoriLocationAction* location_action,
1752+ const gchar* icon)
1753+{
1754+ katze_item_set_icon (location_action->item, icon);
1755+}
1756+
1757+/**
1758+ * midori_location_action_set_title:
1759+ * @location_action: a #MidoriLocationAction
1760+ * @text: a string
1761+ *
1762+ * Sets the entry name to @text.
1763+ *
1764+ * Since: 0.5.2
1765+ **/
1766+void
1767+midori_location_action_set_title (MidoriLocationAction* location_action,
1768+ const gchar* title)
1769+{
1770+ katze_item_set_name (location_action->item, title);
1771+}
1772+
1773+/**
1774 * midori_location_action_set_text:
1775 * @location_action: a #MidoriLocationAction
1776 * @text: a string
1777
1778=== modified file 'midori/midori-locationaction.h'
1779--- midori/midori-locationaction.h 2013-04-16 23:10:10 +0000
1780+++ midori/midori-locationaction.h 2014-01-26 19:44:25 +0000
1781@@ -40,6 +40,14 @@
1782 midori_location_action_get_text (MidoriLocationAction* location_action);
1783
1784 void
1785+midori_location_action_set_icon (MidoriLocationAction* location_action,
1786+ const gchar* icon);
1787+
1788+void
1789+midori_location_action_set_title (MidoriLocationAction* location_action,
1790+ const gchar* title);
1791+
1792+void
1793 midori_location_action_set_text (MidoriLocationAction* location_action,
1794 const gchar* text);
1795
1796
1797=== modified file 'midori/midori-view.c'
1798--- midori/midori-view.c 2013-12-09 20:23:03 +0000
1799+++ midori/midori-view.c 2014-01-26 19:44:25 +0000
1800@@ -121,6 +121,8 @@
1801 GtkWidget* overlay_label;
1802 GtkWidget* overlay_find;
1803 #endif
1804+
1805+ GtkTargetList* dnd_targets;
1806 };
1807
1808 struct _MidoriViewClass
1809@@ -139,7 +141,8 @@
1810 PROP_MINIMIZED,
1811 PROP_ZOOM_LEVEL,
1812 PROP_NEWS_FEEDS,
1813- PROP_SETTINGS
1814+ PROP_SETTINGS,
1815+ PROP_DND_TARGETS
1816 };
1817
1818 enum {
1819@@ -379,6 +382,21 @@
1820 MIDORI_TYPE_WEB_SETTINGS,
1821 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1822
1823+ /**
1824+ * MidoriView:dnd-targets:
1825+ *
1826+ * GtkTargetList* the view supports.
1827+ *
1828+ * Since: 0.5.2
1829+ **/
1830+ g_object_class_install_property (gobject_class,
1831+ PROP_DND_TARGETS,
1832+ g_param_spec_pointer (
1833+ "dnd-targets",
1834+ "DNDTargets",
1835+ "GtkTargetList* the view supports",
1836+ G_PARAM_READABLE|G_PARAM_WRITABLE));
1837+
1838 #ifdef HAVE_WEBKIT2
1839 WebKitWebContext* context = webkit_web_context_get_default ();
1840 webkit_web_context_register_uri_scheme (context,
1841@@ -2983,6 +3001,8 @@
1842 view->news_feeds = NULL;
1843 view->find_links = -1;
1844 view->alerts = 0;
1845+ view->dnd_targets = NULL;
1846+
1847
1848 view->item = katze_item_new ();
1849
1850@@ -3056,6 +3076,9 @@
1851 case PROP_SETTINGS:
1852 _midori_view_set_settings (view, g_value_get_object (value));
1853 break;
1854+ case PROP_DND_TARGETS:
1855+ view->dnd_targets = (GtkTargetList*)g_value_get_pointer (value);
1856+ break;
1857 default:
1858 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1859 break;
1860@@ -3087,6 +3110,9 @@
1861 case PROP_SETTINGS:
1862 g_value_set_object (value, view->settings);
1863 break;
1864+ case PROP_DND_TARGETS:
1865+ g_value_set_pointer (value, view->dnd_targets);
1866+ break;
1867 default:
1868 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1869 break;
1870@@ -4296,6 +4322,89 @@
1871 return new_view;
1872 }
1873
1874+#if 0
1875+static void
1876+midori_view_tab_label_data_received (GtkWidget* widget,
1877+ GdkDragContext* context,
1878+ gint x,
1879+ gint y,
1880+ GtkSelectionData* data,
1881+ guint ttype,
1882+ guint timestamp,
1883+ MidoriView* view)
1884+{
1885+ KatzeArray *array;
1886+ gchar** uri;
1887+
1888+ array = katze_array_from_selection_data (data);
1889+ if (array)
1890+ {
1891+ if (katze_array_get_length (array))
1892+ {
1893+ KatzeItem* item = katze_array_get_nth_item (array, 0);
1894+ if (KATZE_ITEM_IS_BOOKMARK (item))
1895+ {
1896+ midori_view_set_title (view, katze_item_get_name (item));
1897+ midori_view_set_uri (view, katze_item_get_uri (item));
1898+ }
1899+ }
1900+
1901+ g_object_unref (array);
1902+ return;
1903+ }
1904+
1905+ uri = gtk_selection_data_get_uris (data);
1906+ if (uri != NULL)
1907+ {
1908+ midori_view_set_uri (view, uri[0]);
1909+ g_strfreev (uri);
1910+ }
1911+ else
1912+ {
1913+ gchar* text = (gchar*) gtk_selection_data_get_text (data);
1914+ midori_view_set_uri (view, text);
1915+ g_free (text);
1916+ }
1917+}
1918+
1919+static void
1920+midori_view_tab_label_data_get (GtkWidget *widget,
1921+ GdkDragContext *drag_context,
1922+ GtkSelectionData *data,
1923+ guint info,
1924+ guint time,
1925+ MidoriView* view)
1926+{
1927+ selection_data_from_katze_item (data, view->item);
1928+}
1929+
1930+static void
1931+midori_view_install_tab_dnd (MidoriView* view, GtkWidget* widget)
1932+{
1933+ g_object_set_data (G_OBJECT (widget), "KatzeItem", view->item)
1934+;
1935+ gtk_drag_dest_set (widget, GTK_DEST_DEFAULT_ALL, NULL,
1936+ 0, GDK_ACTION_COPY);
1937+ if (view->dnd_targets)
1938+ gtk_drag_dest_set_target_list (widget, view->dnd_targets);
1939+ gtk_drag_dest_add_text_targets (widget);
1940+ gtk_drag_dest_add_uri_targets (widget);
1941+ g_signal_connect (widget, "drag-data-received",
1942+ G_CALLBACK (midori_view_tab_label_data_received),
1943+ view);
1944+ if (view->dnd_targets)
1945+ {
1946+ g_print ("midori_view_get_tab: install source widget\n");
1947+ gtk_drag_source_set (widget, GDK_BUTTON1_MASK, NULL, 0,
1948+ GDK_ACTION_COPY|GDK_ACTION_LINK);
1949+ gtk_drag_source_set_target_list (widget, view->dnd_targets);
1950+ g_signal_connect (widget, "drag-data-get",
1951+ G_CALLBACK (midori_view_tab_label_data_get),
1952+ view);
1953+ }
1954+}
1955+#endif
1956+
1957 /**
1958 * midori_view_get_tab_menu:
1959 * @view: a #MidoriView
1960
1961=== modified file 'panels/midori-bookmarks.c'
1962--- panels/midori-bookmarks.c 2014-01-24 23:04:05 +0000
1963+++ panels/midori-bookmarks.c 2014-01-26 19:44:25 +0000
1964@@ -26,6 +26,151 @@
1965
1966 #define COMPLETION_DELAY 200
1967
1968+#define MIDORI_BOOKMARKS_TREE_MODEL_TARGET "GTK_TREE_MODEL_ROW"
1969+
1970+G_BEGIN_DECLS
1971+
1972+#define MIDORI_BOOKMARKS_TREE_STORE_TYPE \
1973+ (midori_bookmarks_tree_store_get_type ())
1974+#define MIDORI_BOOKMARKS_TREE_STORE(obj) \
1975+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), MIDORI_BOOKMARKS_TREE_STORE_TYPE, MidoriBookmarksTreeStore))
1976+#define MIDORI_BOOKMARKS_TREE_STORE_CLASS(klass) \
1977+ (G_TYPE_CHECK_CLASS_CAST ((klass), MIDORI_BOOKMARKS_TREE_STORE_TYPE, MidoriBookmarksTreeStoreClass))
1978+
1979+static gboolean
1980+midori_bookmarks_tree_store_drag_data_get (GtkTreeDragSource* drag_source,
1981+ GtkTreePath* source_path,
1982+ GtkSelectionData* selection_data);
1983+static gboolean
1984+midori_bookmarks_tree_store_drag_data_delete (GtkTreeDragSource* drag_source,
1985+ GtkTreePath* source_path);
1986+static gboolean
1987+midori_bookmarks_tree_store_row_drop_possible (GtkTreeDragDest *drag_dest,
1988+ GtkTreePath *dest_path,
1989+ GtkSelectionData *selection_data);
1990+static gboolean
1991+midori_bookmarks_tree_store_drag_data_received (GtkTreeDragDest *drag_dest,
1992+ GtkTreePath *dest_path,
1993+ GtkSelectionData *selection_data);
1994+
1995+typedef struct _MidoriBookmarksTreeStore MidoriBookmarksTreeStore;
1996+typedef struct _MidoriBookmarksTreeStoreClass MidoriBookmarksTreeStoreClass;
1997+typedef struct _TreeRowData TreeRowData;
1998+
1999+struct _MidoriBookmarksTreeStore
2000+{
2001+ GtkTreeStore parent_instance;
2002+
2003+ GList* stock_got_rows;
2004+ GtkTreeView *_view;
2005+};
2006+
2007+struct _MidoriBookmarksTreeStoreClass
2008+{
2009+ GtkTreeStoreClass parent_class;
2010+};
2011+
2012+struct _TreeRowData
2013+{
2014+ GtkTreeModel *model;
2015+ gchar path[4];
2016+};
2017+
2018+static GtkTreeDragSourceIface *
2019+gtk_tree_store_gtk_tree_drag_source_iface = NULL;
2020+static GtkTreeDragDestIface *
2021+gtk_tree_store_gtk_tree_drag_dest_iface = NULL;
2022+
2023+static void
2024+midori_bookmarks_tree_store_drag_source_init (GtkTreeDragSourceIface *iface)
2025+{
2026+ gtk_tree_store_gtk_tree_drag_source_iface = g_type_interface_peek_parent (iface);
2027+
2028+ iface->drag_data_get = midori_bookmarks_tree_store_drag_data_get;
2029+ iface->drag_data_delete = midori_bookmarks_tree_store_drag_data_delete;
2030+}
2031+
2032+static void
2033+midori_bookmarks_tree_store_drag_dest_init (GtkTreeDragDestIface *iface)
2034+{
2035+ gtk_tree_store_gtk_tree_drag_dest_iface = g_type_interface_peek_parent (iface);
2036+
2037+ iface->row_drop_possible = midori_bookmarks_tree_store_row_drop_possible;
2038+ iface->drag_data_received = midori_bookmarks_tree_store_drag_data_received;
2039+}
2040+
2041+static void
2042+midori_bookmarks_tree_store_init (MidoriBookmarksTreeStore* item)
2043+{
2044+ item->stock_got_rows = NULL;
2045+ item->_view = NULL;
2046+}
2047+
2048+static void
2049+midori_bookmarks_tree_store_class_init (MidoriBookmarksTreeStoreClass *class)
2050+{
2051+}
2052+
2053+G_DEFINE_TYPE_WITH_CODE (MidoriBookmarksTreeStore,
2054+ midori_bookmarks_tree_store,
2055+ GTK_TYPE_TREE_STORE,
2056+ G_IMPLEMENT_INTERFACE (
2057+ GTK_TYPE_TREE_DRAG_SOURCE,
2058+ midori_bookmarks_tree_store_drag_source_init)
2059+ G_IMPLEMENT_INTERFACE (
2060+ GTK_TYPE_TREE_DRAG_DEST,
2061+ midori_bookmarks_tree_store_drag_dest_init));
2062+
2063+
2064+GtkTreeStore*
2065+midori_bookmarks_tree_store_new (gint n_columns, ...)
2066+{
2067+ GtkTreeStore* tree_store = GTK_TREE_STORE (g_object_new (MIDORI_BOOKMARKS_TREE_STORE_TYPE, NULL));
2068+ va_list ap;
2069+ GType* types;
2070+ gint n;
2071+
2072+ if (!tree_store)
2073+ return NULL;
2074+
2075+ types = g_new (GType, n_columns);
2076+
2077+ if (!types)
2078+ {
2079+ g_object_unref(tree_store);
2080+ return NULL;
2081+ }
2082+
2083+ va_start(ap, n_columns);
2084+ for (n = 0; n < n_columns; n++)
2085+ {
2086+ types[n] = va_arg(ap, GType);
2087+ }
2088+ va_end(ap);
2089+
2090+ gtk_tree_store_set_column_types (tree_store,
2091+ n_columns,
2092+ types);
2093+
2094+ g_free (types);
2095+ return tree_store;
2096+}
2097+
2098+GtkTreeStore*
2099+midori_bookmarks_tree_store_newv (gint n_columns, GType *types)
2100+{
2101+ GtkTreeStore* tree_store = GTK_TREE_STORE (g_object_new (MIDORI_BOOKMARKS_TREE_STORE_TYPE, NULL));
2102+
2103+ if (!tree_store)
2104+ return NULL;
2105+
2106+ gtk_tree_store_set_column_types (tree_store,
2107+ n_columns,
2108+ types);
2109+
2110+ return tree_store;
2111+}
2112+
2113 gboolean
2114 midori_browser_edit_bookmark_dialog_new (MidoriBrowser* browser,
2115 KatzeItem* bookmark_or_parent,
2116@@ -37,6 +182,12 @@
2117 midori_browser_open_bookmark (MidoriBrowser* browser,
2118 KatzeItem* item);
2119
2120+static void
2121+midori_bookmarks_row_changed_cb (GtkTreeModel* model,
2122+ GtkTreePath* path,
2123+ GtkTreeIter* iter,
2124+ MidoriBookmarks* bookmarks);
2125+
2126 struct _MidoriBookmarks
2127 {
2128 GtkVBox parent_instance;
2129@@ -50,7 +201,19 @@
2130 gint filter_timeout;
2131 gchar* filter;
2132
2133+ GList* pending_inserts;
2134 KatzeItem* hovering_item;
2135+
2136+ struct _stock_pending_event
2137+ {
2138+ gint x;
2139+ gint y;
2140+ } *pending_event,
2141+ stock_pending_event;
2142+
2143+ GHashTable* updated_items;
2144+ GList* added_paths;
2145+ GList* reordered_paths;
2146 };
2147
2148 struct _MidoriBookmarksClass
2149@@ -88,36 +251,47 @@
2150 GParamSpec* pspec);
2151
2152 static void
2153-midori_bookmarks_row_changed_cb (GtkTreeModel* model,
2154- GtkTreePath* path,
2155- GtkTreeIter* iter,
2156- MidoriBookmarks* bookmarks);
2157-
2158+midori_bookmarks_update_cb (KatzeArray* array,
2159+ MidoriBookmarks* bookmarks);
2160 static void
2161 midori_bookmarks_add_item_cb (KatzeArray* array,
2162 KatzeItem* item,
2163 MidoriBookmarks* bookmarks);
2164-
2165 static void
2166 midori_bookmarks_update_item_cb (KatzeArray* array,
2167- KatzeItem* item,
2168- MidoriBookmarks* bookmarks);
2169-
2170+ KatzeItem* item,
2171+ MidoriBookmarks* bookmarks);
2172 static void
2173 midori_bookmarks_remove_item_cb (KatzeArray* array,
2174 KatzeItem* item,
2175 MidoriBookmarks* bookmarks);
2176
2177 static void
2178-midori_bookmarks_update_cb (KatzeArray* array,
2179- MidoriBookmarks* bookmarks);
2180+midori_bookmarks_row_inserted_cb (GtkTreeModel* model,
2181+ GtkTreePath* path,
2182+ GtkTreeIter* iter,
2183+ MidoriBookmarks* bookmarks);
2184+static void
2185+midori_bookmarks_row_changed_cb (GtkTreeModel* model,
2186+ GtkTreePath* path,
2187+ GtkTreeIter* iter,
2188+ MidoriBookmarks* bookmarks);
2189+static void
2190+midori_bookmarks_row_deleted_cb (GtkTreeModel* model,
2191+ GtkTreePath* path,
2192+ MidoriBookmarks* bookmarks);
2193+
2194+static void
2195+midori_bookmarks_update_item (MidoriBookmarks* bookmarks, KatzeItem *item);
2196
2197 static void
2198 midori_bookmarks_statusbar_update (MidoriBookmarks *bookmarks);
2199
2200 static void
2201-midori_bookmarks_add_item (KatzeItem* item,
2202- MidoriBookmarks* bookmarks);
2203+midori_bookmarks_idle_remove_item (MidoriBookmarks* bookmarks, KatzeItem *item);
2204+
2205+static gboolean
2206+midori_bookmarks_idle_func (gpointer data);
2207
2208 static void
2209 midori_bookmarks_class_init (MidoriBookmarksClass* class)
2210@@ -133,13 +307,13 @@
2211 flags = G_PARAM_READWRITE | G_PARAM_CONSTRUCT;
2212
2213 g_object_class_install_property (gobject_class,
2214- PROP_APP,
2215- g_param_spec_object (
2216- "app",
2217- "App",
2218- "The app",
2219- MIDORI_TYPE_APP,
2220- flags));
2221+ PROP_APP,
2222+ g_param_spec_object (
2223+ "app",
2224+ "App",
2225+ "The app",
2226+ MIDORI_TYPE_APP,
2227+ flags));
2228 }
2229
2230 static const gchar*
2231@@ -168,7 +342,7 @@
2232 gchar* parent_id;
2233
2234 parent_id = g_strdup_printf ("%" G_GINT64_FORMAT, parentid);
2235- if (!(root_array = midori_bookmarks_db_query_recursive (array, "*", "parentid = %q", parent_id, FALSE)))
2236+ if (!(root_array = midori_bookmarks_db_query_recursive (array, "*", "parentid = %q", parent_id, "(uri='') ASC, pos_panel DESC", FALSE)))
2237 {
2238 g_free (parent_id);
2239 return;
2240@@ -180,7 +354,7 @@
2241 subarray = katze_array_new (KATZE_TYPE_ARRAY);
2242 katze_item_set_name (KATZE_ITEM (subarray), katze_item_get_name (item));
2243 midori_bookmarks_export_array_db (db, subarray,
2244- katze_item_get_meta_integer (item, "parentid"));
2245+ katze_item_get_meta_integer (item, "parentid"));
2246 katze_array_add_item (array, subarray);
2247 }
2248 else
2249@@ -201,20 +375,20 @@
2250
2251 if (keyword && *keyword)
2252 array = midori_bookmarks_db_query_recursive (bookmarks->bookmarks_db,
2253- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "title LIKE '%%%q%%'", keyword, FALSE);
2254+ "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "title LIKE '%%%q%%'", keyword, NULL, FALSE);
2255 else
2256 {
2257 if (parentid > 0)
2258 {
2259 gchar* parent_id = g_strdup_printf ("%" G_GINT64_FORMAT, parentid);
2260 array = midori_bookmarks_db_query_recursive (bookmarks->bookmarks_db,
2261- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid = %q", parent_id, FALSE);
2262+ "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid = %q", parent_id, "(uri='') ASC, pos_panel DESC", FALSE);
2263
2264 g_free (parent_id);
2265 }
2266 else
2267 array = midori_bookmarks_db_query_recursive (bookmarks->bookmarks_db,
2268- "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid IS NULL", NULL, FALSE);
2269+ "id, parentid, title, uri, desc, app, toolbar, pos_panel, pos_bar", "parentid IS NULL", NULL, "(uri='') ASC, pos_panel DESC", FALSE);
2270 }
2271 return array ? array : katze_array_new (KATZE_TYPE_ITEM);
2272 }
2273@@ -231,24 +405,52 @@
2274 KatzeItem* item;
2275 GtkTreeIter child;
2276
2277+ g_signal_handlers_block_by_func (model,
2278+ midori_bookmarks_row_changed_cb,
2279+ bookmarks);
2280+
2281 array = midori_bookmarks_read_from_db (bookmarks, parentid, keyword);
2282 katze_bookmark_populate_tree_view (array, model, parent);
2283+
2284+ g_signal_handlers_unblock_by_func (model,
2285+ midori_bookmarks_row_changed_cb,
2286+ bookmarks);
2287+
2288 /* Remove invisible dummy row */
2289 last = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (model), parent);
2290 if (!last)
2291 return;
2292+
2293+ g_signal_handlers_block_by_func (model,
2294+ midori_bookmarks_row_deleted_cb,
2295+ bookmarks);
2296+
2297 gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (model), &child, parent, last - 1);
2298 gtk_tree_model_get (GTK_TREE_MODEL (model), &child, 0, &item, -1);
2299 if (KATZE_ITEM_IS_SEPARATOR (item))
2300 gtk_tree_store_remove (model, &child);
2301 else
2302 g_object_unref (item);
2303+
2304+ g_signal_handlers_unblock_by_func (model,
2305+ midori_bookmarks_row_deleted_cb,
2306+ bookmarks);
2307+
2308 }
2309
2310+static void
2311+midori_bookmarks_add_item (KatzeItem* item,
2312+ MidoriBookmarks* bookmarks);
2313+
2314+static void
2315+add_parent_to_reorder (GtkTreeModel* model,
2316+ GtkTreePath* path,
2317+ MidoriBookmarks* bookmarks);
2318+
2319 static gboolean
2320 midori_bookmarks_reach_item_recurse (GtkTreeModel* model,
2321- GtkTreeIter* iter,
2322- gint64 id)
2323+ GtkTreeIter* iter,
2324+ gint64 id)
2325 {
2326 do
2327 {
2328@@ -258,7 +460,7 @@
2329
2330 gtk_tree_model_get (model, iter, 0, &item, -1);
2331
2332- if (!KATZE_ITEM_IS_SEPARATOR(item))
2333+ if (item)
2334 {
2335 itemid = katze_item_get_meta_integer (item, "id");
2336 g_object_unref (item);
2337@@ -267,13 +469,11 @@
2338 if (id == itemid)
2339 return TRUE;
2340
2341- if (gtk_tree_model_iter_children (model, &child, iter))
2342+ if (gtk_tree_model_iter_children (model, &child, iter)
2343+ && midori_bookmarks_reach_item_recurse (model, &child, id))
2344 {
2345- if (midori_bookmarks_reach_item_recurse (model, &child, id))
2346- {
2347- *iter = child;
2348- return TRUE;
2349- }
2350+ *iter = child;
2351+ return TRUE;
2352 }
2353 }
2354 while (gtk_tree_model_iter_next(model, iter));
2355@@ -283,8 +483,8 @@
2356
2357 static gboolean
2358 midori_bookmarks_reach_item (GtkTreeModel* model,
2359- GtkTreeIter* iter,
2360- gint64 id)
2361+ GtkTreeIter* iter,
2362+ gint64 id)
2363 {
2364 if (!gtk_tree_model_get_iter_first(model, iter))
2365 return FALSE;
2366@@ -293,38 +493,73 @@
2367 }
2368
2369 static void
2370-midori_bookmarks_add_item_to_model(GtkTreeStore* model,
2371- GtkTreeIter* parent,
2372- KatzeItem* item)
2373+midori_bookmarks_add_item_to_model(MidoriBookmarks* bookmarks,
2374+ GtkTreeModel* model,
2375+ GtkTreeIter* parent,
2376+ KatzeItem* item)
2377 {
2378+ GtkTreeStore* tree_store = GTK_TREE_STORE (model);
2379+ gint last;
2380+ GtkTreeIter child;
2381+
2382 if (KATZE_ITEM_IS_BOOKMARK (item))
2383 {
2384+ gint position = 0;
2385+
2386+ /* skip the folders to be consistent with the db query order */
2387+ if (gtk_tree_model_iter_children (model, &child, parent))
2388+ while (gtk_tree_model_iter_has_child (model, &child))
2389+ {
2390+ position++;
2391+ if (!gtk_tree_model_iter_next (model, &child))
2392+ break;
2393+ }
2394+
2395 gchar* tooltip = g_markup_escape_text (katze_item_get_uri (item), -1);
2396
2397- gtk_tree_store_insert_with_values (model, NULL, parent,
2398- 0,
2399+ gtk_tree_store_insert_with_values (tree_store, NULL, parent,
2400+ position,
2401 0, item, 1, tooltip, -1);
2402 g_free (tooltip);
2403 }
2404- else
2405+ else if (KATZE_ITEM_IS_FOLDER (item))
2406 {
2407 GtkTreeIter root_iter;
2408
2409- gtk_tree_store_insert_with_values (model, &root_iter, parent,
2410+ gtk_tree_store_insert_with_values (tree_store, &root_iter, parent,
2411 0, 0, item, -1);
2412
2413 /* That's an invisible dummy, so we always have an expander */
2414- gtk_tree_store_insert_with_values (model, NULL, &root_iter,
2415- 0,
2416- 0, NULL, -1);
2417+ gtk_tree_store_insert_with_values (tree_store, NULL, &root_iter,
2418+ 0, 0, NULL, -1);
2419 }
2420+
2421+ /* Remove invisible dummy row */
2422+ last = gtk_tree_model_iter_n_children (model, parent);
2423+ if (!last)
2424+ return;
2425+
2426+ g_signal_handlers_block_by_func (model,
2427+ midori_bookmarks_row_deleted_cb,
2428+ bookmarks);
2429+
2430+ gtk_tree_model_iter_nth_child (model, &child, parent, last - 1);
2431+ gtk_tree_model_get (model, &child, 0, &item, -1);
2432+ if (KATZE_ITEM_IS_SEPARATOR (item))
2433+ gtk_tree_store_remove (tree_store, &child);
2434+ else
2435+ g_object_unref (item);
2436+
2437+ g_signal_handlers_unblock_by_func (model,
2438+ midori_bookmarks_row_deleted_cb,
2439+ bookmarks);
2440 }
2441
2442 static void
2443 midori_bookmarks_update_item_in_model(MidoriBookmarks* bookmarks,
2444- GtkTreeStore* model,
2445- GtkTreeIter* iter,
2446- KatzeItem* item)
2447+ GtkTreeStore* model,
2448+ GtkTreeIter* iter,
2449+ KatzeItem* item)
2450 {
2451 g_signal_handlers_block_by_func (model,
2452 midori_bookmarks_row_changed_cb,
2453@@ -350,18 +585,48 @@
2454 bookmarks);
2455 }
2456
2457+static gboolean
2458+midori_bookmarks_idle_pending (MidoriBookmarks* bookmarks)
2459+{
2460+ if (bookmarks->pending_inserts
2461+ || bookmarks->added_paths
2462+ || bookmarks->reordered_paths
2463+ || g_hash_table_size (bookmarks->updated_items))
2464+ return TRUE;
2465+ return FALSE;
2466+}
2467+
2468+/**
2469+ * midori_bookmarks_idle_start:
2470+ * @bookmarks: the bookmarks panel
2471+ *
2472+ * Internal function that checks whether idle processing is pending,
2473+ * if not, add a new one.
2474+ **/
2475+static void
2476+midori_bookmarks_idle_start (MidoriBookmarks* bookmarks)
2477+{
2478+ if (midori_bookmarks_idle_pending (bookmarks))
2479+ return;
2480+
2481+ g_idle_add (midori_bookmarks_idle_func, bookmarks);
2482+}
2483+
2484 static void
2485 midori_bookmarks_add_item (KatzeItem* item,
2486 MidoriBookmarks* bookmarks);
2487+
2488 static void
2489 midori_bookmarks_add_item_cb (KatzeArray* array,
2490 KatzeItem* item,
2491 MidoriBookmarks* bookmarks)
2492 {
2493- midori_bookmarks_add_item (item, bookmarks);
2494+ midori_bookmarks_idle_start (bookmarks);
2495+
2496+ g_object_ref (item);
2497+ bookmarks->pending_inserts = g_list_append (bookmarks->pending_inserts, item);
2498 }
2499
2500-
2501 static void
2502 midori_bookmarks_add_item (KatzeItem* item,
2503 MidoriBookmarks* bookmarks)
2504@@ -370,18 +635,16 @@
2505 GtkTreeModel* model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
2506 GtkTreeIter iter;
2507
2508- if (!parentid)
2509- {
2510- midori_bookmarks_add_item_to_model (GTK_TREE_STORE (model), NULL, item);
2511- }
2512+ if (parentid == katze_item_get_meta_integer (KATZE_ITEM (bookmarks->bookmarks_db), "id"))
2513+ midori_bookmarks_add_item_to_model (bookmarks, model, NULL, item);
2514 else if (midori_bookmarks_reach_item (model, &iter, parentid))
2515 {
2516 GtkTreePath* path = gtk_tree_model_get_path(model, &iter);
2517+ gint n_children = gtk_tree_model_iter_n_children (model, &iter);
2518
2519- if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (bookmarks->treeview), path))
2520- {
2521- midori_bookmarks_add_item_to_model (GTK_TREE_STORE (model), &iter, item);
2522- }
2523+ if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (bookmarks->treeview), path)
2524+ || !n_children)
2525+ midori_bookmarks_add_item_to_model (bookmarks, model, &iter, item);
2526
2527 gtk_tree_path_free (path);
2528 }
2529@@ -389,8 +652,8 @@
2530
2531 static void
2532 midori_bookmarks_update_item_cb (KatzeArray* array,
2533- KatzeItem* item,
2534- MidoriBookmarks* bookmarks)
2535+ KatzeItem* item,
2536+ MidoriBookmarks* bookmarks)
2537 {
2538 gint64 id = katze_item_get_meta_integer (item, "id");
2539 gint64 parentid = katze_item_get_meta_integer (item, "parentid");
2540@@ -433,7 +696,7 @@
2541 midori_bookmarks_add_item (item, bookmarks);
2542 }
2543 }
2544- else if (parentid == 0)
2545+ else if (parentid == katze_item_get_meta_integer (KATZE_ITEM (bookmarks->bookmarks_db), "id"))
2546 {
2547 midori_bookmarks_update_item_in_model (bookmarks, GTK_TREE_STORE (model), &iter, item);
2548 }
2549@@ -457,6 +720,10 @@
2550 GtkTreeModel* model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
2551 GtkTreeIter iter;
2552
2553+ g_assert (KATZE_IS_ITEM (item));
2554+
2555+ midori_bookmarks_idle_remove_item (bookmarks, item);
2556+
2557 if (midori_bookmarks_reach_item (model, &iter, id))
2558 {
2559 GtkTreeIter parent;
2560@@ -486,12 +753,869 @@
2561 midori_bookmarks_update_cb (KatzeArray* array,
2562 MidoriBookmarks* bookmarks)
2563 {
2564+#if 1
2565+ g_print ("midori_bookmarks_update_cb: ignored ********\n");
2566+#else
2567 GtkTreeModel* model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
2568+
2569 gtk_tree_store_clear (GTK_TREE_STORE (model));
2570 midori_bookmarks_read_from_db_to_model (bookmarks,
2571 GTK_TREE_STORE (model), NULL, 0, bookmarks->filter);
2572-}
2573-
2574+#endif
2575+}
2576+
2577+gboolean
2578+midori_bookmarks_tree_set_row_drag_data (GtkSelectionData *selection_data,
2579+ GtkTreeModel *tree_model,
2580+ GList* rows)
2581+{
2582+ TreeRowData *trd;
2583+ gint len;
2584+ gint struct_size;
2585+ gint length;
2586+ gint i;
2587+ GString *data;
2588+
2589+ g_return_val_if_fail (selection_data != NULL, FALSE);
2590+ g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE);
2591+ g_return_val_if_fail (rows != NULL, FALSE);
2592+
2593+ data = g_string_new("");
2594+
2595+ length = g_list_length (rows);
2596+ for (i = 0; i < length; i++)
2597+ {
2598+ GtkTreePath *path = (GtkTreePath *)g_list_nth_data (rows, i);
2599+ gchar *path_str = gtk_tree_path_to_string (path);
2600+
2601+ g_string_append (data, path_str);
2602+ if (i < length-1)
2603+ g_string_append_c (data, '\n');
2604+ }
2605+
2606+ len = data->len;
2607+
2608+ /* the old allocate-end-of-struct-to-hold-string trick */
2609+ struct_size = sizeof (TreeRowData) + len + 1 -
2610+ (sizeof (TreeRowData) - G_STRUCT_OFFSET (TreeRowData, path));
2611+
2612+ trd = g_malloc (struct_size);
2613+
2614+ strcpy (trd->path, data->str);
2615+
2616+ g_string_free (data, TRUE);
2617+
2618+ trd->model = tree_model;
2619+
2620+ gtk_selection_data_set (selection_data,
2621+ gdk_atom_intern_static_string (MIDORI_BOOKMARKS_TREE_MODEL_TARGET),
2622+ 8, /* bytes */
2623+ (void*)trd,
2624+ struct_size);
2625+
2626+ g_free (trd);
2627+
2628+ return TRUE;
2629+}
2630+
2631+static gboolean
2632+midori_bookmarks_tree_store_drag_data_get (GtkTreeDragSource* drag_source,
2633+ GtkTreePath* source_path,
2634+ GtkSelectionData* selection_data)
2635+{
2636+ MidoriBookmarksTreeStore *tree_store;
2637+ gboolean status = FALSE;
2638+
2639+ g_return_val_if_fail (selection_data != NULL, FALSE);
2640+ g_return_val_if_fail (GTK_IS_TREE_MODEL (drag_source), FALSE);
2641+ g_return_val_if_fail (source_path != NULL, FALSE);
2642+
2643+ tree_store = MIDORI_BOOKMARKS_TREE_STORE(drag_source);
2644+
2645+ if (tree_store->stock_got_rows)
2646+ {
2647+ g_list_free_full (tree_store->stock_got_rows, (GDestroyNotify) gtk_tree_path_free);
2648+ tree_store->stock_got_rows = NULL;
2649+ }
2650+
2651+ if (gtk_selection_data_get_target (selection_data) ==
2652+ gdk_atom_intern_static_string (MIDORI_BOOKMARKS_TREE_MODEL_TARGET))
2653+ {
2654+ GtkTreeModel *model;
2655+ GList* rows;
2656+ if (katze_tree_view_get_selected_rows (
2657+ tree_store->_view, &model, &rows))
2658+ {
2659+ status = midori_bookmarks_tree_set_row_drag_data (selection_data, model, rows);
2660+
2661+ tree_store->stock_got_rows = rows;
2662+ }
2663+ }
2664+
2665+ return status;
2666+}
2667+
2668+static void
2669+update_path_list_for_insert (GList * rows, GtkTreePath* path)
2670+{
2671+ gint length = g_list_length (rows);
2672+ gint i;
2673+
2674+ for (i = 0; i < length; i++ )
2675+ {
2676+ GtkTreePath *src_path_r = (GtkTreePath *)g_list_nth_data (rows, i);
2677+ gint la = gtk_tree_path_get_depth (path);
2678+ gint lb = gtk_tree_path_get_depth (src_path_r);
2679+ gint *ia = gtk_tree_path_get_indices (path);
2680+ gint *ib = gtk_tree_path_get_indices (src_path_r);
2681+ gint j;
2682+
2683+ if (la > lb) /* insert was donne in a deeper branch than source */
2684+ continue;
2685+
2686+ if (ia[la-1] > ib[la-1]) /* insert was donne after source */
2687+ continue;
2688+
2689+ for (j = 0; j < la; j++)
2690+ {
2691+ if (ia[j] != ib[j]) break;
2692+ }
2693+
2694+ if (j < la-1) /* insert and source are not in the same branch */
2695+ continue;
2696+
2697+ /* source at depth level of insert must be incremented due to the insert */
2698+ ib[la-1] += 1;
2699+ }
2700+}
2701+
2702+static gint
2703+midori_tree_path_compare (const GtkTreePath *a,
2704+ const GtkTreePath *b)
2705+{
2706+ if (!gtk_tree_path_get_depth ((GtkTreePath *)a))
2707+ {
2708+ if (!gtk_tree_path_get_depth ((GtkTreePath *)b))
2709+ return 0;
2710+
2711+ return -1;
2712+ }
2713+
2714+ if (!gtk_tree_path_get_depth ((GtkTreePath *)b))
2715+ return 1;
2716+
2717+ return gtk_tree_path_compare (a, b);
2718+}
2719+
2720+static GList*
2721+update_path_list_for_delete (GList* rows, GtkTreePath* removed_path)
2722+{
2723+ GList* new_rows = rows;
2724+
2725+ while (rows)
2726+ {
2727+ GtkTreePath *source_path = (GtkTreePath *)rows->data;
2728+ gint la = gtk_tree_path_get_depth (removed_path);
2729+ gint lb = gtk_tree_path_get_depth (source_path);
2730+ gint *ia = gtk_tree_path_get_indices (removed_path);
2731+ gint *ib = gtk_tree_path_get_indices (source_path);
2732+ gint cmp = midori_tree_path_compare (removed_path, source_path);
2733+ gint j;
2734+
2735+ if (cmp == 1) /* removal was done after source => kip source as it is */
2736+ goto keep_source;
2737+
2738+ if (cmp == 0) /* source is removed => remove source */
2739+ goto remove_source;
2740+
2741+ /* if removal is an ancestor of the source => remove source */
2742+ if (gtk_tree_path_is_ancestor (removed_path, source_path))
2743+ goto remove_source;
2744+
2745+ if (la > lb) /* removal was donne in a deeper branch than source */
2746+ goto keep_source;
2747+
2748+ for (j = 0; j < la; j++)
2749+ {
2750+ if (ia[j] != ib[j]) break;
2751+ }
2752+
2753+ if (j < la-1) /* removal and source are not in the same branch */
2754+ goto keep_source;
2755+
2756+ /* source at depth level of removal must be decremented due to the removal */
2757+ ib[la-1] -= 1;
2758+
2759+ if (ib[la-1] >= 0)
2760+ goto keep_source;
2761+
2762+ remove_source:
2763+ /* remove source entry */
2764+ gtk_tree_path_free (source_path);
2765+ {
2766+ GList *next_rows = g_list_next (rows);
2767+ new_rows = g_list_delete_link (new_rows, rows);
2768+ rows = next_rows;
2769+ }
2770+ continue;
2771+
2772+ keep_source:
2773+ rows = g_list_next (rows);
2774+ }
2775+
2776+ return new_rows;
2777+}
2778+
2779+static gboolean
2780+midori_bookmarks_tree_store_drag_data_delete (GtkTreeDragSource* drag_source,
2781+ GtkTreePath* source_path)
2782+{
2783+ gboolean status = TRUE;
2784+ MidoriBookmarksTreeStore *tree_store = MIDORI_BOOKMARKS_TREE_STORE(drag_source);
2785+ GtkTreeModel* model = GTK_TREE_MODEL(drag_source);
2786+
2787+ if (!tree_store->stock_got_rows)
2788+ return TRUE;
2789+
2790+ while (tree_store->stock_got_rows)
2791+ {
2792+ GtkTreePath *prev = (GtkTreePath *)tree_store->stock_got_rows->data;
2793+ GtkTreeIter iter;
2794+
2795+ tree_store->stock_got_rows = g_list_delete_link (tree_store->stock_got_rows,
2796+ tree_store->stock_got_rows);
2797+
2798+ if (gtk_tree_model_get_iter (model, &iter, prev))
2799+ {
2800+ /* remove item updating source paths */
2801+ gtk_tree_store_remove (GTK_TREE_STORE (drag_source), &iter);
2802+
2803+ tree_store->stock_got_rows = update_path_list_for_delete (tree_store->stock_got_rows, prev);
2804+ }
2805+ else
2806+ status = FALSE;
2807+
2808+ gtk_tree_path_free (prev);
2809+ }
2810+
2811+ return status;
2812+}
2813+
2814+static gboolean
2815+midori_bookmarks_tree_store_get_rows_drag_data (GtkSelectionData *selection_data,
2816+ GtkTreeModel **tree_model,
2817+ GList **rows)
2818+{
2819+ TreeRowData *trd;
2820+
2821+ g_return_val_if_fail (selection_data != NULL, FALSE);
2822+
2823+ if (tree_model)
2824+ *tree_model = NULL;
2825+
2826+ if (rows)
2827+ *rows = NULL;
2828+
2829+ if (gtk_selection_data_get_target (selection_data) !=
2830+ gdk_atom_intern_static_string (MIDORI_BOOKMARKS_TREE_MODEL_TARGET))
2831+ return FALSE;
2832+
2833+ if (gtk_selection_data_get_length (selection_data) < 0)
2834+ return FALSE;
2835+
2836+ trd = (void*) gtk_selection_data_get_data (selection_data);
2837+
2838+ if (tree_model)
2839+ *tree_model = trd->model;
2840+
2841+ if (rows)
2842+ {
2843+ GList *list = NULL;
2844+ gchar *trd_path = g_strdup (trd->path);
2845+ gchar *path_str;
2846+
2847+ path_str = strtok(trd_path, "\n");
2848+ while (path_str && *path_str)
2849+ {
2850+ list = g_list_append (list, gtk_tree_path_new_from_string (path_str));
2851+ path_str = strtok (NULL, "\n");
2852+ }
2853+
2854+ *rows = list;
2855+ g_free (trd_path);
2856+ }
2857+
2858+ return TRUE;
2859+}
2860+
2861+#if !GTK_CHECK_VERSION (3,0,0)
2862+gboolean
2863+gtk_tree_model_iter_previous (GtkTreeModel *tree_model,
2864+ GtkTreeIter *iter)
2865+{
2866+ GtkTreePath* path = gtk_tree_model_get_path (tree_model, iter);
2867+ gboolean result = gtk_tree_path_prev (path);
2868+
2869+ if (result)
2870+ result = gtk_tree_model_get_iter (tree_model, iter, path);
2871+ else
2872+ {
2873+ GtkTreeIter invalid = {0};
2874+ *iter = invalid;
2875+ }
2876+
2877+ gtk_tree_path_free (path);
2878+ return result;
2879+}
2880+#endif
2881+
2882+static gboolean
2883+midori_bookmarks_tree_store_row_drop_possible (GtkTreeDragDest* drag_dest,
2884+ GtkTreePath* dest_path,
2885+ GtkSelectionData* selection_data)
2886+{
2887+ GtkTreeModel* dest_model = GTK_TREE_MODEL(drag_dest);
2888+ GtkTreePath *parent;
2889+ GtkTreeIter dest_parent;
2890+ GtkTreeIter *dest_parent_p = NULL;
2891+ gboolean row_drop_possible = TRUE;
2892+ GtkTreeViewDropPosition drop_position;
2893+
2894+ gtk_tree_view_get_drag_dest_row (MIDORI_BOOKMARKS_TREE_STORE (dest_model)->_view,
2895+ NULL, &drop_position);
2896+
2897+ parent = gtk_tree_path_copy (dest_path);
2898+ if ((gtk_tree_path_get_depth (parent) > 1)
2899+ && gtk_tree_path_up (parent)
2900+ && gtk_tree_model_get_iter (dest_model, &dest_parent, parent))
2901+ dest_parent_p = &dest_parent;
2902+
2903+ gtk_tree_path_free (parent);
2904+
2905+ if (dest_parent_p)
2906+ {
2907+ KatzeItem* item;
2908+
2909+ gtk_tree_model_get (dest_model, dest_parent_p, 0, &item, -1);
2910+
2911+ if (!KATZE_ITEM_IS_FOLDER (item))
2912+ {
2913+#ifdef DEBUG_DROP
2914+ gchar *dest_path_str = gtk_tree_path_to_string (dest_path);
2915+
2916+ g_print ("%s: can only drop into folders\n", dest_path_str);
2917+ g_free (dest_path_str);
2918+#endif /* DEBUG_DROP */
2919+ row_drop_possible = FALSE;
2920+ }
2921+
2922+ if (item)
2923+ g_object_unref (item);
2924+ }
2925+
2926+ if (row_drop_possible
2927+ && (gtk_selection_data_get_target (selection_data) ==
2928+ gdk_atom_intern_static_string (MIDORI_BOOKMARKS_TREE_MODEL_TARGET)))
2929+ {
2930+ GtkTreeModel *src_model;
2931+ GList* rows;
2932+
2933+ if (midori_bookmarks_tree_store_get_rows_drag_data (selection_data,
2934+ &src_model, &rows))
2935+ {
2936+ GtkTreeIter dest_iter;
2937+ GList* src_row;
2938+ gboolean dest_is_folder = FALSE;
2939+ /* gboolean dest_is_bookmark = FALSE; */
2940+ gboolean src_has_folders = FALSE;
2941+ gboolean src_has_bookmarks = FALSE;
2942+
2943+ if (gtk_tree_model_get_iter (dest_model, &dest_iter, dest_path))
2944+ {
2945+ if (gtk_tree_model_iter_has_child (dest_model, &dest_iter))
2946+ dest_is_folder = TRUE;
2947+/*
2948+ else
2949+ {
2950+ KatzeItem* item;
2951+ gtk_tree_model_get (dest_model, &dest_iter, 0, &item, -1);
2952+ if (item)
2953+ {
2954+ dest_is_bookmark = TRUE;
2955+ g_object_unref (item);
2956+ }
2957+ }
2958+*/
2959+ }
2960+
2961+ for (src_row = rows ; src_row ; src_row = g_list_next (src_row))
2962+ {
2963+ GtkTreePath* src_path = (GtkTreePath*)src_row->data;
2964+ GtkTreeIter src_iter;
2965+ KatzeItem* item;
2966+
2967+ if (!gtk_tree_model_get_iter (src_model, &src_iter, src_path))
2968+ continue;
2969+
2970+ gtk_tree_model_get (src_model, &src_iter, 0, &item, -1);
2971+ if (item)
2972+ {
2973+ if (!src_has_folders && KATZE_ITEM_IS_FOLDER (item))
2974+ src_has_folders = TRUE;
2975+ else if (!src_has_bookmarks && KATZE_ITEM_IS_BOOKMARK (item))
2976+ src_has_bookmarks = TRUE;
2977+
2978+ g_object_unref (item);
2979+ }
2980+ if (src_has_bookmarks && src_has_folders)
2981+ break;
2982+ }
2983+
2984+ if (src_has_bookmarks)
2985+ {
2986+ switch (drop_position)
2987+ {
2988+ case GTK_TREE_VIEW_DROP_BEFORE:
2989+ case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
2990+ if (dest_is_folder)
2991+ {
2992+#ifdef DEBUG_DROP
2993+ gchar *dest_path_str = gtk_tree_path_to_string (dest_path);
2994+
2995+ g_print ("%s: cannot drop bookmarks in folders group\n", dest_path_str);
2996+ g_free (dest_path_str);
2997+#endif /* DEBUG_DROP */
2998+ row_drop_possible = FALSE;
2999+ goto done;
3000+ }
3001+ break;
3002+ case GTK_TREE_VIEW_DROP_AFTER:
3003+ case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
3004+ if (dest_is_folder)
3005+ {
3006+ gboolean next_dest_is_folder = FALSE;
3007+
3008+ if (gtk_tree_model_iter_next (dest_model, &dest_iter))
3009+ if (gtk_tree_model_iter_has_child (dest_model, &dest_iter))
3010+ next_dest_is_folder = TRUE;
3011+
3012+ if (next_dest_is_folder)
3013+ {
3014+#ifdef DEBUG_DROP
3015+ gchar *dest_path_str = gtk_tree_path_to_string (dest_path);
3016+
3017+ g_print ("%s: cannot drop bookmarks in folders group\n", dest_path_str);
3018+ g_free (dest_path_str);
3019+#endif /* DEBUG_DROP */
3020+ row_drop_possible = FALSE;
3021+ goto done;
3022+ }
3023+ }
3024+ break;
3025+ default:
3026+ break;
3027+ }
3028+ }
3029+ else if (src_has_folders)
3030+ {
3031+ gboolean prev_dest_is_folder = TRUE;
3032+
3033+ if (gtk_tree_model_iter_previous (dest_model, &dest_iter))
3034+ if (!gtk_tree_model_iter_has_child (dest_model, &dest_iter))
3035+ prev_dest_is_folder = FALSE;
3036+
3037+ if (!prev_dest_is_folder)
3038+ {
3039+#ifdef DEBUG_DROP
3040+ gchar *dest_path_str = gtk_tree_path_to_string (dest_path);
3041+
3042+ g_print ("%s: cannot drop folders in bookmarks group\n", dest_path_str);
3043+ g_free (dest_path_str);
3044+#endif /* DEBUG_DROP */
3045+ row_drop_possible = FALSE;
3046+ goto done;
3047+ }
3048+ }
3049+
3050+ if (src_model == dest_model)
3051+ {
3052+ for (src_row = rows ; src_row ; src_row = g_list_next (src_row))
3053+ {
3054+ GtkTreePath* src_path = (GtkTreePath*)src_row->data;
3055+
3056+ /* Can't drop into ourself. */
3057+ if (gtk_tree_path_is_ancestor (src_path, dest_path))
3058+ {
3059+#ifdef DEBUG_DROP
3060+ g_print ("cannot drop into source folders\n");
3061+#endif /* DEBUG_DROP */
3062+ row_drop_possible = FALSE;
3063+ goto done;
3064+ }
3065+ update_path_list_for_insert (rows, dest_path);
3066+ }
3067+ }
3068+ done:
3069+ g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
3070+ }
3071+ else
3072+ row_drop_possible = FALSE;
3073+ }
3074+ else
3075+ row_drop_possible = FALSE;
3076+
3077+#ifdef DEBUG_DROP
3078+ if (row_drop_possible)
3079+ {
3080+ gchar *dest_path_str = gtk_tree_path_to_string (dest_path);
3081+ gchar *drop_position_str = "unknown";
3082+ switch (drop_position)
3083+ {
3084+ case GTK_TREE_VIEW_DROP_BEFORE:
3085+ drop_position_str = "before";
3086+ break;
3087+ case GTK_TREE_VIEW_DROP_AFTER:
3088+ drop_position_str = "after";
3089+ break;
3090+ case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
3091+ drop_position_str = "into or before";
3092+ break;
3093+ case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
3094+ drop_position_str = "into or after";
3095+ break;
3096+ }
3097+
3098+ g_print ("%s: row drop possible (%s)\n", dest_path_str, drop_position_str);
3099+ g_free (dest_path_str);
3100+ }
3101+#endif /* DEBUG_DROP */
3102+
3103+ return row_drop_possible;
3104+}
3105+
3106+static void
3107+copy_node_data (GtkTreeStore *src_store,
3108+ GtkTreeIter *src_iter,
3109+ GtkTreeStore *dest_store,
3110+ GtkTreeIter *dest_iter)
3111+{
3112+ gint i;
3113+ gint n_columns;
3114+ GtkTreeModel* src_model = GTK_TREE_MODEL (src_store);
3115+
3116+ n_columns = gtk_tree_model_get_n_columns (src_model);
3117+
3118+ for (i = 0; i < n_columns; i++)
3119+ {
3120+ void *item;
3121+
3122+ gtk_tree_model_get (src_model, src_iter, i, &item, -1);
3123+ gtk_tree_store_set (dest_store, dest_iter, i, item, -1);
3124+ }
3125+}
3126+
3127+static void
3128+recursive_node_copy (GtkTreeStore *src_store,
3129+ GtkTreeIter *src_iter,
3130+ GtkTreeStore *dest_store,
3131+ GtkTreeIter *dest_iter)
3132+{
3133+ GtkTreeIter child;
3134+ GtkTreeModel *src_model = GTK_TREE_MODEL (src_store);
3135+
3136+ copy_node_data (src_store, src_iter, dest_store, dest_iter);
3137+
3138+ if (gtk_tree_model_iter_children (src_model, &child, src_iter))
3139+ {
3140+ /* Need to create children and recurse. Note our
3141+ * dependence on persistent iterators here.
3142+ */
3143+ do
3144+ {
3145+ GtkTreeIter copy;
3146+
3147+ /* Gee, a really slow algorithm... ;-) FIXME */
3148+ gtk_tree_store_append (dest_store,
3149+ &copy,
3150+ dest_iter);
3151+
3152+ recursive_node_copy (src_store, &child, dest_store, &copy);
3153+ }
3154+ while (gtk_tree_model_iter_next (src_model, &child));
3155+ }
3156+}
3157+
3158+static gboolean
3159+midori_bookmarks_tree_store_drag_data_received (GtkTreeDragDest *drag_dest,
3160+ GtkTreePath *dest_path,
3161+ GtkSelectionData *selection_data)
3162+{
3163+ gboolean status = TRUE;
3164+
3165+ g_return_val_if_fail (selection_data != NULL, FALSE);
3166+ g_return_val_if_fail (GTK_IS_TREE_MODEL (drag_dest), FALSE);
3167+ g_return_val_if_fail (dest_path != NULL, FALSE);
3168+
3169+ if (gtk_selection_data_get_target (selection_data) ==
3170+ gdk_atom_intern_static_string (MIDORI_BOOKMARKS_TREE_MODEL_TARGET))
3171+ {
3172+ GtkTreeStore *dest_store = GTK_TREE_STORE (drag_dest);
3173+ GtkTreeModel *dest_model = GTK_TREE_MODEL (drag_dest);
3174+ GtkTreeModel *src_model;
3175+ GList* rows;
3176+
3177+ if (midori_bookmarks_tree_store_get_rows_drag_data (selection_data,
3178+ &src_model, &rows))
3179+ {
3180+ GtkTreeStore *src_store = GTK_TREE_STORE (src_model);
3181+ GtkTreePath *prev = gtk_tree_path_copy (dest_path);
3182+
3183+ gint count = 0;
3184+ gint length = g_list_length (rows);
3185+ gint i;
3186+
3187+ for (i = 0; i < length; i++)
3188+ {
3189+ GtkTreeIter dest_iter;
3190+ GtkTreeIter src_iter;
3191+ GtkTreePath *src_path = (GtkTreePath *)g_list_nth_data (rows, i);
3192+
3193+ if (!gtk_tree_model_get_iter (src_model, &src_iter, src_path))
3194+ continue;
3195+
3196+ /* Get the path to insert _after_ (dest is the path to insert _before_) */
3197+ if (i == 0)
3198+ {
3199+ if (!gtk_tree_path_prev (prev))
3200+ { /* Get the parent, NULL if parent is the root */
3201+ GtkTreeIter dest_parent;
3202+ GtkTreePath *parent = gtk_tree_path_copy (dest_path);
3203+ GtkTreeIter *dest_parent_p = NULL;
3204+
3205+ if (gtk_tree_path_up (parent) &&
3206+ gtk_tree_path_get_depth (parent) > 0)
3207+ {
3208+ gtk_tree_model_get_iter (dest_model,
3209+ &dest_parent, parent);
3210+ dest_parent_p = &dest_parent;
3211+ }
3212+ gtk_tree_path_free (parent);
3213+
3214+ gtk_tree_store_prepend (dest_store, &dest_iter, dest_parent_p);
3215+ }
3216+ else if (gtk_tree_model_get_iter (dest_model, &dest_iter, prev))
3217+ {
3218+ GtkTreeIter tmp_iter = dest_iter;
3219+
3220+ gtk_tree_store_insert_after (dest_store, &dest_iter, NULL,
3221+ &tmp_iter);
3222+ }
3223+ }
3224+ else if (gtk_tree_model_get_iter (dest_model, &dest_iter, prev))
3225+ {
3226+ GtkTreeIter tmp_iter = dest_iter;
3227+
3228+ gtk_tree_store_insert_after (dest_store, &dest_iter, NULL,
3229+ &tmp_iter);
3230+ }
3231+
3232+ gtk_tree_path_free (prev);
3233+
3234+ recursive_node_copy (src_store, &src_iter, dest_store, &dest_iter);
3235+ count++;
3236+
3237+ prev = gtk_tree_model_get_path (dest_model, &dest_iter);
3238+
3239+ if (src_store != dest_store)
3240+ continue;
3241+
3242+ update_path_list_for_insert (rows, prev);
3243+ }
3244+
3245+ gtk_tree_path_free (prev);
3246+
3247+ g_assert (count == length);
3248+
3249+ if (src_store == dest_store)
3250+ {
3251+ MidoriBookmarksTreeStore *tree_store = MIDORI_BOOKMARKS_TREE_STORE(src_store);
3252+
3253+ g_list_free_full (tree_store->stock_got_rows, (GDestroyNotify) gtk_tree_path_free);
3254+ tree_store->stock_got_rows = rows;
3255+ }
3256+ else
3257+ g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
3258+ }
3259+ }
3260+
3261+ return status;
3262+}
3263+
3264+static void
3265+midori_bookmarks_set_item_positon (GtkTreeModel* model,
3266+ GtkTreeIter* iter,
3267+ gint64 parentid,
3268+ MidoriBookmarks* bookmarks)
3269+{
3270+ KatzeItem* item;
3271+ gint position = 0;
3272+ GtkTreeIter next = *iter;
3273+
3274+ do {
3275+ gboolean update = FALSE;
3276+ gtk_tree_model_get (model, &next, 0, &item, -1);
3277+
3278+ if (!KATZE_IS_ITEM (item))
3279+ continue;
3280+
3281+ if (position != katze_item_get_meta_integer (item, "pos_panel"))
3282+ {
3283+ katze_item_set_meta_integer (item, "pos_panel", position);
3284+ update = TRUE;
3285+ }
3286+ if (parentid != katze_item_get_meta_integer (item, "parentid"))
3287+ {
3288+ katze_item_set_meta_integer (item, "parentid", parentid);
3289+ update = TRUE;
3290+ }
3291+
3292+ if (update)
3293+ midori_bookmarks_update_item (bookmarks, item);
3294+
3295+ position++;
3296+
3297+ g_object_unref (item);
3298+ }
3299+ while (gtk_tree_model_iter_next (model, &next));
3300+}
3301+
3302+static void
3303+assert_reorder_are_folders (GtkTreeModel* model,
3304+ MidoriBookmarks* bookmarks)
3305+{
3306+ GList* iter;
3307+ for (iter = bookmarks->reordered_paths; iter ; iter = g_list_next (iter))
3308+ {
3309+ GtkTreePath* path = (GtkTreePath*)iter->data;
3310+ GtkTreeIter tree_iter;
3311+
3312+ if (!gtk_tree_path_get_depth (path))
3313+ continue;
3314+
3315+ if (gtk_tree_model_get_iter (model, &tree_iter, path))
3316+ {
3317+ KatzeItem *item;
3318+
3319+ gtk_tree_model_get (model, &tree_iter, 0, &item, -1);
3320+
3321+ g_assert (KATZE_ITEM_IS_FOLDER (item));
3322+ }
3323+ }
3324+}
3325+
3326+static void
3327+add_parent_to_reorder (GtkTreeModel* model,
3328+ GtkTreePath* path,
3329+ MidoriBookmarks* bookmarks)
3330+{
3331+ GtkTreePath* path_copy = gtk_tree_path_copy (path);
3332+ GList* found;
3333+
3334+ midori_bookmarks_idle_start (bookmarks);
3335+
3336+ if (gtk_tree_path_get_depth (path_copy) > 1
3337+ && gtk_tree_path_up (path_copy))
3338+ {
3339+ GtkTreeIter iter;
3340+ if (gtk_tree_model_get_iter (model, &iter, path_copy))
3341+ {
3342+ KatzeItem* item;
3343+ gtk_tree_model_get (model, &iter, 0, &item, -1);
3344+ if (item)
3345+ {
3346+ g_assert (KATZE_ITEM_IS_FOLDER (item));
3347+ g_object_unref (item);
3348+ }
3349+ else
3350+ g_assert_not_reached ();
3351+ }
3352+ else
3353+ g_assert_not_reached ();
3354+ }
3355+ else
3356+ {
3357+ gtk_tree_path_free (path_copy);
3358+ path_copy = gtk_tree_path_new ();
3359+ }
3360+
3361+ if ((found = g_list_find_custom (bookmarks->reordered_paths,
3362+ path_copy, (GCompareFunc)midori_tree_path_compare)) != NULL)
3363+ {
3364+ gtk_tree_path_free (path_copy);
3365+ return;
3366+ }
3367+
3368+ bookmarks->reordered_paths = g_list_append (bookmarks->reordered_paths, path_copy);
3369+}
3370+
3371+static void
3372+midori_bookmarks_row_inserted_cb (GtkTreeModel* model,
3373+ GtkTreePath* path,
3374+ GtkTreeIter* iter,
3375+ MidoriBookmarks* bookmarks)
3376+{
3377+ midori_bookmarks_idle_start (bookmarks);
3378+
3379+ update_path_list_for_insert (bookmarks->added_paths, path);
3380+ update_path_list_for_insert (bookmarks->reordered_paths, path);
3381+ assert_reorder_are_folders (model, bookmarks);
3382+
3383+ if (g_list_find_custom (bookmarks->added_paths,
3384+ path, (GCompareFunc)midori_tree_path_compare))
3385+ return;
3386+
3387+ bookmarks->added_paths = g_list_append (bookmarks->added_paths, gtk_tree_path_copy (path));
3388+}
3389+
3390+#ifdef DEBUG_LIST
3391+static void
3392+print_path_list (GList* iter)
3393+{
3394+ for ( ; iter ; iter = g_list_next (iter))
3395+ {
3396+ gchar* str = gtk_tree_path_to_string ((GtkTreePath*)iter->data);
3397+ g_print ("%s ", str);
3398+ g_free (str);
3399+ }
3400+ g_print ("\n");
3401+}
3402+#endif /* DEBUG_LIST */
3403+
3404+static void
3405+midori_bookmarks_row_deleted_cb (GtkTreeModel* model,
3406+ GtkTreePath* path,
3407+ MidoriBookmarks* bookmarks)
3408+{
3409+#ifdef DEBUG_LIST
3410+ gchar* str = gtk_tree_path_to_string (path);
3411+ g_print ("midori_bookmarks_row_deleted_cb: path: %s\n", str);
3412+ g_free (str);
3413+#endif /* DEBUG_LIST */
3414+
3415+ midori_bookmarks_idle_start (bookmarks);
3416+
3417+ bookmarks->added_paths = update_path_list_for_delete (bookmarks->added_paths, path);
3418+#ifdef DEBUG_LIST
3419+ print_path_list (bookmarks->reordered_paths);
3420+#endif /* DEBUG_LIST */
3421+ bookmarks->reordered_paths = update_path_list_for_delete (bookmarks->reordered_paths, path);
3422+#ifdef DEBUG_LIST
3423+ print_path_list (bookmarks->reordered_paths);
3424+#endif /* DEBUG_LIST */
3425+ assert_reorder_are_folders (model, bookmarks);
3426+ add_parent_to_reorder (model, path, bookmarks);
3427+ assert_reorder_are_folders (model, bookmarks);
3428+}
3429
3430 static void
3431 midori_bookmarks_row_changed_cb (GtkTreeModel* model,
3432@@ -499,41 +1623,8 @@
3433 GtkTreeIter* iter,
3434 MidoriBookmarks* bookmarks)
3435 {
3436- KatzeItem* item;
3437- GtkTreeIter parent;
3438- KatzeItem* new_parent = NULL;
3439- gint64 parentid;
3440-
3441- gtk_tree_model_get (model, iter, 0, &item, -1);
3442-
3443- if (gtk_tree_model_iter_parent (model, &parent, iter))
3444- {
3445- gtk_tree_model_get (model, &parent, 0, &new_parent, -1);
3446-
3447- /* Bookmarks must not be moved into non-folder items */
3448- if (!KATZE_ITEM_IS_FOLDER (new_parent))
3449- parentid = 0;
3450- else
3451- parentid = katze_item_get_meta_integer (new_parent, "id");
3452- }
3453- else
3454- parentid = 0;
3455-
3456- katze_item_set_meta_integer (item, "parentid", parentid);
3457-
3458- g_signal_handlers_block_by_func (bookmarks->bookmarks_db,
3459- midori_bookmarks_update_item_cb,
3460- bookmarks);
3461-
3462- midori_bookmarks_db_update_item (bookmarks->bookmarks_db, item);
3463-
3464- g_signal_handlers_unblock_by_func (bookmarks->bookmarks_db,
3465- midori_bookmarks_update_item_cb,
3466- bookmarks);
3467-
3468- g_object_unref (item);
3469- if (new_parent)
3470- g_object_unref (new_parent);
3471+ add_parent_to_reorder (model, path, bookmarks);
3472+ assert_reorder_are_folders (model, bookmarks);
3473 }
3474
3475 static void
3476@@ -596,7 +1687,7 @@
3477 GtkTreeIter iter;
3478
3479 if (katze_tree_view_get_selected_iter (GTK_TREE_VIEW (bookmarks->treeview),
3480- &model, &iter))
3481+ &model, &iter))
3482 {
3483 KatzeItem* item;
3484 MidoriBrowser* browser;
3485@@ -616,15 +1707,17 @@
3486 static void
3487 midori_bookmarks_toolbar_update (MidoriBookmarks *bookmarks)
3488 {
3489- gboolean selected;
3490+ gint selected;
3491
3492- selected = katze_tree_view_get_selected_iter (
3493+ selected = katze_tree_view_get_selected_rows (
3494 GTK_TREE_VIEW (bookmarks->treeview), NULL, NULL);
3495- gtk_widget_set_sensitive (GTK_WIDGET (bookmarks->delete), selected);
3496- gtk_widget_set_sensitive (GTK_WIDGET (bookmarks->edit), selected);
3497+ gtk_widget_set_sensitive (
3498+ GTK_WIDGET (bookmarks->delete), (selected > 0 ? TRUE : FALSE));
3499+ gtk_widget_set_sensitive (
3500+ GTK_WIDGET (bookmarks->edit), (selected == 1 ? TRUE : FALSE));
3501 }
3502
3503-static gchar*
3504+static gchar*
3505 midori_bookmarks_statusbar_bookmarks_str (gint count)
3506 {
3507 if (!count)
3508@@ -634,7 +1727,7 @@
3509 return g_strdup_printf (ngettext ("%d bookmark", "%d bookmarks", count), count);
3510 }
3511
3512-static gchar*
3513+static gchar*
3514 midori_bookmarks_statusbar_subfolders_str (gint count)
3515 {
3516 if (!count)
3517@@ -648,78 +1741,141 @@
3518 midori_bookmarks_statusbar_update (MidoriBookmarks *bookmarks)
3519 {
3520 gchar* text = NULL;
3521-
3522- if (bookmarks->hovering_item)
3523- {
3524- KatzeItem* item = bookmarks->hovering_item;
3525-
3526- g_assert (!KATZE_ITEM_IS_SEPARATOR (item));
3527-
3528- if (KATZE_ITEM_IS_FOLDER (item))
3529+ GtkTreeModel* model;
3530+ GList *rows;
3531+ gint selected;
3532+
3533+ selected = katze_tree_view_get_selected_rows (
3534+ GTK_TREE_VIEW (bookmarks->treeview), &model, &rows);
3535+
3536+ if (selected > 1)
3537+ {
3538+ gint i;
3539+ gint selected_folders_count = 0;
3540+ gint selected_bookmarks_count = 0;
3541+ gchar* selected_folders_str = midori_bookmarks_statusbar_subfolders_str (selected_folders_count);
3542+ gchar* selected_bookmarks_str = midori_bookmarks_statusbar_bookmarks_str (selected_bookmarks_count);
3543+
3544+ for (i = 0 ; i < selected ; i++)
3545+ {
3546+ GtkTreeIter iter;
3547+ KatzeItem* item;
3548+
3549+ if (!gtk_tree_model_get_iter (
3550+ model, &iter, (GtkTreePath *)g_list_nth_data (rows, i)))
3551+ continue;
3552+
3553+ gtk_tree_model_get (model, &iter, 0, &item, -1);
3554+
3555+ g_assert (!KATZE_ITEM_IS_SEPARATOR (item));
3556+
3557+ if (KATZE_ITEM_IS_FOLDER (item))
3558+ {
3559+ selected_folders_count++;
3560+ }
3561+ else
3562+ {
3563+ selected_bookmarks_count++;
3564+ }
3565+ }
3566+
3567+ selected_folders_str = midori_bookmarks_statusbar_subfolders_str (selected_folders_count);
3568+ selected_bookmarks_str = midori_bookmarks_statusbar_bookmarks_str (selected_bookmarks_count);
3569+
3570+ if (!selected_bookmarks_count && !selected_folders_count)
3571+ g_assert_not_reached ();
3572+ else if (!selected_bookmarks_count && (selected_folders_count >= 1))
3573+ /* i18n: Selection containing [[n] folder(s)] and no bookmark */
3574+ text = g_strdup_printf (_("Selection containing %s and no bookmark"),
3575+ selected_folders_str);
3576+ else if ((selected_bookmarks_count >= 1) && !selected_folders_count)
3577+ /* i18n: Selection containing [[n] bookmark(s)] */
3578+ text = g_strdup_printf (_("Selection containing %s"), selected_bookmarks_str);
3579+ else if ((selected_bookmarks_count >= 1) && (selected_folders_count >= 1))
3580+ /* i18n: Selection containing [[n] bookmark(s)] and [[n] folder(s)] */
3581+ text = g_strdup_printf (_("Selection containing %s and %s"),
3582+ selected_bookmarks_str, selected_folders_str);
3583+
3584+ g_free (selected_folders_str);
3585+ g_free (selected_bookmarks_str);
3586+
3587+ g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
3588+ }
3589+ else
3590+ {
3591+ if (selected)
3592+ g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
3593+
3594+ if (bookmarks->hovering_item)
3595+ {
3596+ KatzeItem* item = bookmarks->hovering_item;
3597+
3598+ if (KATZE_ITEM_IS_FOLDER (item))
3599+ {
3600+ gint child_folders_count = midori_bookmarks_db_count_recursive (bookmarks->bookmarks_db,
3601+ "uri = ''", NULL, item, FALSE);
3602+ gint child_bookmarks_count = midori_bookmarks_db_count_recursive (bookmarks->bookmarks_db,
3603+ "uri <> ''", NULL, item, FALSE);
3604+ gchar* child_folders_str = midori_bookmarks_statusbar_subfolders_str (child_folders_count);
3605+ gchar* child_bookmarks_str = midori_bookmarks_statusbar_bookmarks_str (child_bookmarks_count);
3606+
3607+ if (!child_bookmarks_count && !child_folders_count)
3608+ /* i18n: Empty folder */
3609+ text = g_strdup_printf (_("Empty folder"));
3610+ else if (!child_bookmarks_count && (child_folders_count >= 1))
3611+ /* i18n: Folder containing [[n] folder(s)] and no bookmark */
3612+ text = g_strdup_printf (_("Folder containing %s and no bookmark"),
3613+ child_folders_str);
3614+ else if ((child_bookmarks_count >= 1) && !child_folders_count)
3615+ /* i18n: Folder containing [[n] bookmark(s)] */
3616+ text = g_strdup_printf (_("Folder containing %s"), child_bookmarks_str);
3617+ else if ((child_bookmarks_count >= 1) && (child_folders_count >= 1))
3618+ /* i18n: Folder containing [[n] bookmark(s)] and [[n] folder(s)] */
3619+ text = g_strdup_printf (_("Folder containing %s and %s"),
3620+ child_bookmarks_str, child_folders_str);
3621+
3622+ g_free (child_folders_str);
3623+ g_free (child_bookmarks_str);
3624+ }
3625+ else if (KATZE_ITEM_IS_BOOKMARK (item))
3626+ {
3627+ const gchar* uri = katze_item_get_uri (item);
3628+
3629+ /* i18n: Bookmark leading to: [bookmark uri] */
3630+ text = g_strdup_printf (_("Bookmark leading to: %s"), uri);
3631+ }
3632+ }
3633+ else
3634 {
3635 gint child_folders_count = midori_bookmarks_db_count_recursive (bookmarks->bookmarks_db,
3636- "uri = ''", NULL, item, FALSE);
3637+ "uri = ''", NULL, NULL, FALSE);
3638 gint child_bookmarks_count = midori_bookmarks_db_count_recursive (bookmarks->bookmarks_db,
3639- "uri <> ''", NULL, item, FALSE);
3640+ "uri <> ''", NULL, NULL, FALSE);
3641 gchar* child_folders_str = midori_bookmarks_statusbar_subfolders_str (child_folders_count);
3642 gchar* child_bookmarks_str = midori_bookmarks_statusbar_bookmarks_str (child_bookmarks_count);
3643
3644- if (!child_bookmarks_count && !child_folders_count)
3645- /* i18n: Empty folder */
3646- text = g_strdup_printf (_("Empty folder"));
3647- else if (!child_bookmarks_count && (child_folders_count >= 1))
3648- /* i18n: Folder containing [[n] folder(s)] and no bookmark */
3649- text = g_strdup_printf (_("Folder containing %s and no bookmark"),
3650+ if (!child_bookmarks_count && (child_folders_count >= 1))
3651+ /* i18n: [[n] folder(s)] and no bookmark */
3652+ text = g_strdup_printf (_("%s and no bookmark"),
3653 child_folders_str);
3654 else if ((child_bookmarks_count >= 1) && !child_folders_count)
3655- /* i18n: Folder containing [[n] bookmark(s)] */
3656- text = g_strdup_printf (_("Folder containing %s"), child_bookmarks_str);
3657+ text = g_strdup (child_bookmarks_str);
3658 else if ((child_bookmarks_count >= 1) && (child_folders_count >= 1))
3659- /* i18n: Folder containing [[n] bookmark(s)] and [[n] folder(s)] */
3660- text = g_strdup_printf (_("Folder containing %s and %s"),
3661+ /* i18n: [[n] bookmark(s)] and [[n] folder(s)] */
3662+ text = g_strdup_printf (_("%s and %s"),
3663 child_bookmarks_str, child_folders_str);
3664
3665 g_free (child_folders_str);
3666 g_free (child_bookmarks_str);
3667 }
3668- else if (KATZE_ITEM_IS_BOOKMARK (item))
3669- {
3670- const gchar* uri = katze_item_get_uri (item);
3671-
3672- /* i18n: Bookmark leading to: [bookmark uri] */
3673- text = g_strdup_printf (_("Bookmark leading to: %s"), uri);
3674- }
3675- }
3676- else
3677- {
3678- gint child_folders_count = midori_bookmarks_db_count_recursive (bookmarks->bookmarks_db,
3679- "uri = ''", NULL, NULL, FALSE);
3680- gint child_bookmarks_count = midori_bookmarks_db_count_recursive (bookmarks->bookmarks_db,
3681- "uri <> ''", NULL, NULL, FALSE);
3682- gchar* child_folders_str = midori_bookmarks_statusbar_subfolders_str (child_folders_count);
3683- gchar* child_bookmarks_str = midori_bookmarks_statusbar_bookmarks_str (child_bookmarks_count);
3684-
3685- if (!child_bookmarks_count && (child_folders_count >= 1))
3686- /* i18n: [[n] folder(s)] and no bookmark */
3687- text = g_strdup_printf (_("%s and no bookmark"),
3688- child_folders_str);
3689- else if ((child_bookmarks_count >= 1) && !child_folders_count)
3690- text = g_strdup (child_bookmarks_str);
3691- else if ((child_bookmarks_count >= 1) && (child_folders_count >= 1))
3692- /* i18n: [[n] bookmark(s)] and [[n] folder(s)] */
3693- text = g_strdup_printf (_("%s and %s"),
3694- child_bookmarks_str, child_folders_str);
3695-
3696- g_free (child_folders_str);
3697- g_free (child_bookmarks_str);
3698 }
3699
3700 if (text)
3701 {
3702 MidoriBrowser* browser = midori_browser_get_for_widget (bookmarks->treeview);
3703-
3704+
3705 g_object_set (browser, "statusbar-text", text, NULL);
3706-
3707+
3708 g_free(text);
3709 }
3710 }
3711@@ -729,19 +1885,33 @@
3712 MidoriBookmarks* bookmarks)
3713 {
3714 GtkTreeModel* model;
3715- GtkTreeIter iter;
3716-
3717- if (katze_tree_view_get_selected_iter (GTK_TREE_VIEW (bookmarks->treeview),
3718- &model, &iter))
3719+ GList* rows;
3720+ GList* iter_row;
3721+
3722+ if (!katze_tree_view_get_selected_rows(GTK_TREE_VIEW (bookmarks->treeview),
3723+ &model, &rows))
3724+ return;
3725+
3726+ for (iter_row = rows ; iter_row ; iter_row = g_list_next (iter_row))
3727 {
3728- KatzeItem* item;
3729-
3730- gtk_tree_model_get (model, &iter, 0, &item, -1);
3731-
3732- midori_bookmarks_db_remove_item (bookmarks->bookmarks_db, item);
3733-
3734- g_object_unref (item);
3735+ GtkTreeIter iter;
3736+
3737+ if (gtk_tree_model_get_iter (model, &iter, (GtkTreePath *)iter_row->data))
3738+ {
3739+ KatzeItem* item;
3740+
3741+ gtk_tree_model_get (model, &iter, 0, &item, -1);
3742+
3743+ if (item)
3744+ {
3745+ midori_bookmarks_db_remove_item (bookmarks->bookmarks_db, item);
3746+
3747+ g_object_unref (item);
3748+ }
3749+ }
3750 }
3751+
3752+ g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
3753 }
3754
3755 static GtkWidget*
3756@@ -759,7 +1929,7 @@
3757 toolitem = gtk_tool_button_new_from_stock (STOCK_BOOKMARK_ADD);
3758 gtk_widget_set_name (GTK_WIDGET (toolitem), "BookmarkAdd");
3759 gtk_widget_set_tooltip_text (GTK_WIDGET (toolitem),
3760- _("Add a new bookmark"));
3761+ _("Add a new bookmark"));
3762 gtk_tool_item_set_is_important (toolitem, TRUE);
3763 g_signal_connect (toolitem, "clicked",
3764 G_CALLBACK (midori_bookmarks_add_clicked_cb), bookmarks);
3765@@ -767,7 +1937,7 @@
3766 gtk_widget_show (GTK_WIDGET (toolitem));
3767 toolitem = gtk_tool_button_new_from_stock (GTK_STOCK_EDIT);
3768 gtk_widget_set_tooltip_text (GTK_WIDGET (toolitem),
3769- _("Edit the selected bookmark"));
3770+ _("Edit the selected bookmark"));
3771 g_signal_connect (toolitem, "clicked",
3772 G_CALLBACK (midori_bookmarks_edit_clicked_cb), bookmarks);
3773 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
3774@@ -775,7 +1945,7 @@
3775 bookmarks->edit = GTK_WIDGET (toolitem);
3776 toolitem = gtk_tool_button_new_from_stock (GTK_STOCK_DELETE);
3777 gtk_widget_set_tooltip_text (GTK_WIDGET (toolitem),
3778- _("Delete the selected bookmark"));
3779+ _("Delete the selected bookmark"));
3780 g_signal_connect (toolitem, "clicked",
3781 G_CALLBACK (midori_bookmarks_delete_clicked_cb), bookmarks);
3782 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
3783@@ -791,7 +1961,7 @@
3784 toolitem = gtk_tool_button_new_from_stock (STOCK_FOLDER_NEW);
3785 gtk_widget_set_name (GTK_WIDGET (toolitem), "BookmarkFolderAdd");
3786 gtk_widget_set_tooltip_text (GTK_WIDGET (toolitem),
3787- _("Add a new folder"));
3788+ _("Add a new folder"));
3789 g_signal_connect (toolitem, "clicked",
3790 G_CALLBACK (midori_bookmarks_add_clicked_cb), bookmarks);
3791 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
3792@@ -841,6 +2011,12 @@
3793 G_CALLBACK (midori_bookmarks_remove_item_cb), bookmarks);
3794 g_signal_connect (bookmarks->bookmarks_db, "update",
3795 G_CALLBACK (midori_bookmarks_update_cb), bookmarks);
3796+ g_signal_connect_after (model, "row-inserted",
3797+ G_CALLBACK (midori_bookmarks_row_inserted_cb),
3798+ bookmarks);
3799+ g_signal_connect_after (model, "row-deleted",
3800+ G_CALLBACK (midori_bookmarks_row_deleted_cb),
3801+ bookmarks);
3802 g_signal_connect_after (model, "row-changed",
3803 G_CALLBACK (midori_bookmarks_row_changed_cb),
3804 bookmarks);
3805@@ -856,12 +2032,12 @@
3806
3807 switch (prop_id)
3808 {
3809- case PROP_APP:
3810- midori_bookmarks_set_app (bookmarks, g_value_get_object (value));
3811- break;
3812- default:
3813- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3814- break;
3815+ case PROP_APP:
3816+ midori_bookmarks_set_app (bookmarks, g_value_get_object (value));
3817+ break;
3818+ default:
3819+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3820+ break;
3821 }
3822 }
3823
3824@@ -875,12 +2051,12 @@
3825
3826 switch (prop_id)
3827 {
3828- case PROP_APP:
3829- g_value_set_object (value, bookmarks->app);
3830- break;
3831- default:
3832- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3833- break;
3834+ case PROP_APP:
3835+ g_value_set_object (value, bookmarks->app);
3836+ break;
3837+ default:
3838+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3839+ break;
3840 }
3841 }
3842
3843@@ -918,8 +2094,8 @@
3844 if (item && katze_item_get_name (item))
3845 {
3846 g_object_set (renderer, "markup", NULL,
3847- "ellipsize", PANGO_ELLIPSIZE_END,
3848- "text", katze_item_get_name (item), NULL);
3849+ "ellipsize", PANGO_ELLIPSIZE_END,
3850+ "text", katze_item_get_name (item), NULL);
3851 }
3852 else
3853 g_object_set (renderer, "markup", _("<i>Separator</i>"), NULL);
3854@@ -976,7 +2152,7 @@
3855 menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
3856 if (label)
3857 gtk_label_set_text_with_mnemonic (GTK_LABEL (gtk_bin_get_child (
3858- GTK_BIN (menuitem))), label);
3859+ GTK_BIN (menuitem))), label);
3860 if (!strcmp (stock_id, GTK_STOCK_EDIT))
3861 gtk_widget_set_sensitive (menuitem,
3862 !KATZE_ITEM_IS_SEPARATOR (item));
3863@@ -1007,39 +2183,103 @@
3864 }
3865 }
3866
3867-static void
3868-midori_bookmarks_open_in_tab_activate_cb (GtkWidget* menuitem,
3869- MidoriBookmarks* bookmarks)
3870-{
3871- KatzeItem* item;
3872- const gchar* uri;
3873-
3874- item = (KatzeItem*)g_object_get_data (G_OBJECT (menuitem), "KatzeItem");
3875- if (KATZE_ITEM_IS_FOLDER (item))
3876+static GtkWidget*
3877+midori_bookmarks_open_bookmark_in_tab (KatzeItem *item,
3878+ MidoriBrowser* browser)
3879+{
3880+ const gchar* uri = katze_item_get_uri (item);
3881+
3882+ if (!uri || !*uri)
3883+ return NULL;
3884+
3885+ return midori_browser_add_item (browser, item);
3886+}
3887+
3888+static GtkWidget*
3889+midori_bookmarks_open_folder_in_tab (gint64 parentid,
3890+ MidoriBookmarks* bookmarks,
3891+ MidoriBrowser* browser)
3892+{
3893+ GtkWidget* last_view = NULL;
3894+ KatzeArray* array;
3895+
3896+ array = midori_bookmarks_read_from_db (bookmarks, parentid, NULL);
3897+
3898+ if (KATZE_IS_ARRAY (array))
3899 {
3900 KatzeItem* child;
3901- KatzeArray* array;
3902-
3903- array = midori_bookmarks_read_from_db (bookmarks,
3904- katze_item_get_meta_integer (item, "parentid"), NULL);
3905-
3906- g_return_if_fail (KATZE_IS_ARRAY (array));
3907+
3908 KATZE_ARRAY_FOREACH_ITEM (child, array)
3909 {
3910- if ((uri = katze_item_get_uri (child)) && *uri)
3911+ GtkWidget* view = midori_bookmarks_open_bookmark_in_tab (child, browser);
3912+ if (view)
3913+ last_view = view;
3914+ }
3915+ }
3916+
3917+ return last_view;
3918+}
3919+
3920+static void
3921+midori_bookmarks_open_in_tab_activate_cb (GtkWidget* menuitem,
3922+ MidoriBookmarks* bookmarks)
3923+{
3924+ GtkWidget* last_view = NULL;
3925+ MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (bookmarks));
3926+ GtkTreeModel* model;
3927+ GList* rows;
3928+ gint length;
3929+
3930+ length = katze_tree_view_get_selected_rows (GTK_TREE_VIEW (bookmarks->treeview),
3931+ &model, &rows);
3932+
3933+ if (!length)
3934+ {
3935+ KatzeItem* root = KATZE_ITEM (bookmarks->bookmarks_db);
3936+ KatzeItem* item = KATZE_ITEM (g_object_get_data (G_OBJECT (menuitem), "KatzeItem"));
3937+
3938+ if (item != root)
3939+ return;
3940+
3941+ last_view = midori_bookmarks_open_folder_in_tab (0, bookmarks, browser);
3942+ }
3943+ else
3944+ {
3945+ gint i;
3946+
3947+ for (i = 0 ; i < length; i++)
3948+ {
3949+ GtkTreeIter iter;
3950+
3951+ if (gtk_tree_model_get_iter (
3952+ model, &iter, (GtkTreePath *)g_list_nth_data (rows, i)))
3953 {
3954- MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (bookmarks));
3955- GtkWidget* view = midori_browser_add_item (browser, child);
3956- midori_browser_set_current_tab_smartly (browser, view);
3957+ GtkWidget* view = NULL;
3958+ KatzeItem* item;
3959+
3960+ gtk_tree_model_get (model, &iter, 0, &item, -1);
3961+
3962+ if (KATZE_ITEM_IS_SEPARATOR(item))
3963+ continue;
3964+
3965+ if (KATZE_ITEM_IS_FOLDER (item))
3966+ view = midori_bookmarks_open_folder_in_tab (
3967+ katze_item_get_meta_integer (item, "id"), bookmarks, browser);
3968+ else
3969+ view = midori_bookmarks_open_bookmark_in_tab (item, browser);
3970+
3971+ g_object_unref (item);
3972+
3973+ if (view)
3974+ last_view = view;
3975 }
3976 }
3977- }
3978- else if ((uri = katze_item_get_uri (item)) && *uri)
3979- {
3980- MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (bookmarks));
3981- GtkWidget* view = midori_browser_add_item (browser, item);
3982- midori_browser_set_current_tab_smartly (browser, view);
3983- }
3984+
3985+ g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
3986+ }
3987+
3988+ if (last_view)
3989+ midori_browser_set_current_tab_smartly (browser, last_view);
3990 }
3991
3992 static void
3993@@ -1067,18 +2307,20 @@
3994 KatzeItem* item,
3995 MidoriBookmarks* bookmarks)
3996 {
3997+ KatzeItem* root = KATZE_ITEM (bookmarks->bookmarks_db);
3998 GtkWidget* menu;
3999 GtkWidget* menuitem;
4000
4001 menu = gtk_menu_new ();
4002- if (KATZE_ITEM_IS_FOLDER (item))
4003+ if ((item == root)
4004+ || KATZE_ITEM_IS_FOLDER (item))
4005 {
4006 gint child_bookmarks_count = midori_bookmarks_db_count_recursive (bookmarks->bookmarks_db,
4007 "uri <> ''", NULL, item, FALSE);
4008
4009 midori_bookmarks_popup_item (menu,
4010- STOCK_TAB_NEW, _("Open all in _Tabs"), item,
4011- (!child_bookmarks_count ? NULL : midori_bookmarks_open_in_tab_activate_cb),
4012+ STOCK_TAB_NEW, _("Open all in _Tabs"), item,
4013+ (!child_bookmarks_count ? NULL : midori_bookmarks_open_in_tab_activate_cb),
4014 bookmarks);
4015 }
4016 else
4017@@ -1094,11 +2336,59 @@
4018 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
4019 gtk_widget_show (menuitem);
4020 midori_bookmarks_popup_item (menu, GTK_STOCK_EDIT, NULL,
4021- item, midori_bookmarks_edit_clicked_cb, bookmarks);
4022- midori_bookmarks_popup_item (menu, GTK_STOCK_DELETE, NULL,
4023- item, midori_bookmarks_delete_clicked_cb, bookmarks);
4024-
4025- katze_widget_popup (widget, GTK_MENU (menu), event, KATZE_MENU_POSITION_CURSOR);
4026+ item, (item == root) ? NULL : midori_bookmarks_edit_clicked_cb, bookmarks);
4027+ midori_bookmarks_popup_item (menu, GTK_STOCK_DELETE, NULL,
4028+ item, (item == root) ? NULL : midori_bookmarks_delete_clicked_cb, bookmarks);
4029+
4030+ katze_widget_popup (widget, GTK_MENU (menu), event, KATZE_MENU_POSITION_CURSOR);
4031+}
4032+
4033+static void
4034+midori_bookmarks_multi_popup (GtkWidget* widget,
4035+ GdkEventButton* event,
4036+ MidoriBookmarks* bookmarks,
4037+ GtkTreeModel* model,
4038+ gint count,
4039+ GList* rows)
4040+{
4041+ GtkWidget* menu;
4042+ GtkWidget* menuitem;
4043+
4044+ menu = gtk_menu_new ();
4045+
4046+ midori_bookmarks_popup_item (menu,
4047+ STOCK_TAB_NEW, _("Open all in _Tabs"),
4048+ KATZE_ITEM(bookmarks->bookmarks_db), midori_bookmarks_open_in_tab_activate_cb, bookmarks);
4049+ menuitem = gtk_separator_menu_item_new ();
4050+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
4051+ gtk_widget_show (menuitem);
4052+
4053+ midori_bookmarks_popup_item (menu, GTK_STOCK_EDIT, NULL,
4054+ NULL, NULL, bookmarks);
4055+ midori_bookmarks_popup_item (menu, GTK_STOCK_DELETE, NULL,
4056+ KATZE_ITEM(bookmarks->bookmarks_db), midori_bookmarks_delete_clicked_cb, bookmarks);
4057+
4058+ katze_widget_popup (widget, GTK_MENU (menu), event, KATZE_MENU_POSITION_CURSOR);
4059+}
4060+
4061+static gboolean
4062+midori_bookmarks_do_block_selection (GtkTreeSelection *selection,
4063+ GtkTreeModel *model,
4064+ GtkTreePath *path,
4065+ gboolean path_currently_selected,
4066+ gpointer data)
4067+{
4068+ return FALSE;
4069+}
4070+
4071+static gboolean
4072+midori_bookmarks_do_not_block_selection (GtkTreeSelection *selection,
4073+ GtkTreeModel *model,
4074+ GtkTreePath *path,
4075+ gboolean path_currently_selected,
4076+ gpointer data)
4077+{
4078+ return TRUE;
4079 }
4080
4081 static gboolean
4082@@ -1109,32 +2399,194 @@
4083 GtkTreeModel* model;
4084 GtkTreeIter iter;
4085
4086- if (event->button != 2 && event->button != 3)
4087+ if (bookmarks->pending_event)
4088+ {
4089+ GtkTreeView* treeview = GTK_TREE_VIEW(widget);
4090+ GtkTreeSelection* selection = gtk_tree_view_get_selection (treeview);
4091+ gint x = bookmarks->stock_pending_event.x;
4092+ gint y = bookmarks->stock_pending_event.y;
4093+
4094+ bookmarks->pending_event = NULL;
4095+ gtk_tree_selection_set_select_function (
4096+ selection, midori_bookmarks_do_not_block_selection, NULL, NULL);
4097+
4098+ if (x != event->x || y != event->y)
4099+ return FALSE;
4100+ }
4101+
4102+ if (event->button == 3)
4103+ return TRUE;
4104+
4105+ if (event->button != 2)
4106 return FALSE;
4107
4108 if (katze_tree_view_get_selected_iter (GTK_TREE_VIEW (widget), &model, &iter))
4109 {
4110+ gboolean done = FALSE;
4111 KatzeItem* item;
4112
4113 gtk_tree_model_get (model, &iter, 0, &item, -1);
4114
4115- if (event->button == 2)
4116+ if (KATZE_ITEM_IS_BOOKMARK (item))
4117 {
4118- const gchar* uri;
4119- if (KATZE_ITEM_IS_BOOKMARK (item) && (uri = katze_item_get_uri (item)) && *uri)
4120+ MidoriBrowser* browser = midori_browser_get_for_widget (widget);
4121+ GtkWidget* view = midori_bookmarks_open_bookmark_in_tab (
4122+ item, browser);
4123+
4124+ if (widget)
4125 {
4126- MidoriBrowser* browser = midori_browser_get_for_widget (widget);
4127- GtkWidget* view = midori_browser_add_uri (browser, uri);
4128- midori_browser_set_current_tab (browser, view);
4129+ midori_browser_set_current_tab_smartly (browser, view);
4130+ done = TRUE;
4131 }
4132 }
4133- else
4134- midori_bookmarks_popup (widget, event, item, bookmarks);
4135-
4136- if (item != NULL)
4137- g_object_unref (item);
4138- return TRUE;
4139- }
4140+
4141+ g_object_unref (item);
4142+
4143+ return done;
4144+ }
4145+
4146+ return FALSE;
4147+}
4148+
4149+static gboolean
4150+midori_bookmarks_block_selection(GtkWidget* widget,
4151+ GdkEventButton* event,
4152+ MidoriBookmarks* bookmarks)
4153+{
4154+ GtkTreeView* treeview = GTK_TREE_VIEW(widget);
4155+ GtkTreePath* path;
4156+ GtkTreeSelection* selection;
4157+ gint cell_x;
4158+ gint cell_y;
4159+
4160+ if (!gtk_tree_view_get_path_at_pos (
4161+ treeview, event->x, event->y,
4162+ &path, NULL, &cell_x, &cell_y))
4163+ return FALSE;
4164+
4165+ gtk_widget_grab_focus (widget);
4166+
4167+ selection = gtk_tree_view_get_selection (treeview);
4168+
4169+ if (gtk_tree_selection_path_is_selected (selection, path)
4170+ && !(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)))
4171+ {
4172+ bookmarks->pending_event = &bookmarks->stock_pending_event;
4173+ bookmarks->stock_pending_event.x = event->x;
4174+ bookmarks->stock_pending_event.y = event->y;
4175+ gtk_tree_selection_set_select_function (
4176+ selection, midori_bookmarks_do_block_selection, NULL, NULL);
4177+ }
4178+ else
4179+ {
4180+ bookmarks->pending_event = NULL;
4181+ gtk_tree_selection_set_select_function (
4182+ selection, midori_bookmarks_do_not_block_selection, NULL, NULL);
4183+ }
4184+
4185+ return FALSE;
4186+}
4187+
4188+static gboolean
4189+midori_bookmarks_button_press_event_cb (GtkWidget* widget,
4190+ GdkEventButton* event,
4191+ MidoriBookmarks* bookmarks)
4192+{
4193+ GtkTreeView* treeview = GTK_TREE_VIEW(widget);
4194+ GtkTreePath* path;
4195+ GtkTreeSelection* selection;
4196+ GtkTreeModel* model;
4197+ gint selected;
4198+ GList* rows;
4199+ gint cell_x;
4200+ gint cell_y;
4201+
4202+ if (event->button == 1)
4203+ return midori_bookmarks_block_selection (widget, event, bookmarks);
4204+
4205+ if (event->button != 3)
4206+ return FALSE;
4207+
4208+ selection = gtk_tree_view_get_selection (treeview);
4209+
4210+ if (!gtk_tree_view_get_path_at_pos (
4211+ treeview, event->x, event->y,
4212+ &path, NULL, &cell_x, &cell_y))
4213+ {
4214+ /* FIXME: popup opening below treeview
4215+ * Rationale: the user is actually in ROOT folder
4216+ * we may need to have a non editable, non deletable, ROOT folder popup
4217+ * Open all in Tabs
4218+ * Separator
4219+ * Edit [inactive]
4220+ * Delete [inactive]
4221+ * Here we just mimic the Files behaviour:
4222+ * 1- unselect all
4223+ * 2- let popup based on selection process
4224+ */
4225+
4226+ gtk_tree_selection_unselect_all (selection);
4227+ }
4228+ else if (!gtk_tree_selection_path_is_selected (selection, path))
4229+ {
4230+ /* Use case: popup opening on item not in selection
4231+ * Rationale: the user is addressing a single item not in selection
4232+ * we may need a single item popup with callbacks working on the item,
4233+ * not the selection.
4234+ * Here we just mimic the Files behaviour:
4235+ * 1- change the selection to the item the popup is opened on
4236+ * 2- let popup based on selection process
4237+ */
4238+
4239+ gtk_tree_selection_unselect_all (selection);
4240+ gtk_tree_selection_select_path (selection, path);
4241+ }
4242+
4243+ selected = katze_tree_view_get_selected_rows(GTK_TREE_VIEW (widget), &model, &rows);
4244+
4245+ if (!selected)
4246+ {
4247+ KatzeItem* root = KATZE_ITEM (bookmarks->bookmarks_db);
4248+
4249+ midori_bookmarks_popup (widget, event, root, bookmarks);
4250+
4251+ return TRUE;
4252+ }
4253+
4254+ if (selected == 1)
4255+ {
4256+ GtkTreeIter iter;
4257+ KatzeItem* item;
4258+
4259+ if (!gtk_tree_model_get_iter (
4260+ model, &iter, (GtkTreePath *)g_list_nth_data (rows, 0)))
4261+ {
4262+ g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
4263+
4264+ return FALSE;
4265+ }
4266+
4267+ gtk_tree_model_get (model, &iter, 0, &item, -1);
4268+
4269+ midori_bookmarks_popup (widget, event, item, bookmarks);
4270+
4271+ g_object_unref (item);
4272+
4273+ g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
4274+
4275+ return TRUE;
4276+ }
4277+
4278+ if (selected > 1)
4279+ {
4280+ midori_bookmarks_multi_popup (widget, event, bookmarks,
4281+ model, selected, rows);
4282+
4283+ g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
4284+
4285+ return TRUE;
4286+ }
4287+
4288 return FALSE;
4289 }
4290
4291@@ -1165,39 +2617,74 @@
4292 }
4293 }
4294
4295-static void
4296-midori_bookmarks_row_expanded_cb (GtkTreeView* treeview,
4297- GtkTreeIter* iter,
4298- GtkTreePath* path,
4299- MidoriBookmarks* bookmarks)
4300+static gboolean
4301+midori_bookmarks_test_expand_row_cb (GtkTreeView* treeview,
4302+ GtkTreeIter* iter,
4303+ GtkTreePath* path,
4304+ MidoriBookmarks* bookmarks)
4305 {
4306 GtkTreeModel* model;
4307+ GtkTreeIter child;
4308 KatzeItem* item;
4309+ gint64 id;
4310
4311 model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
4312+
4313 gtk_tree_model_get (model, iter, 0, &item, -1);
4314+
4315+ g_return_val_if_fail (KATZE_IS_ITEM(item), TRUE);
4316+
4317+ g_signal_handlers_block_by_func (model,
4318+ midori_bookmarks_row_deleted_cb,
4319+ bookmarks);
4320+
4321+ id = katze_item_get_meta_integer (item, "id");
4322+
4323+ g_object_unref (item);
4324+
4325+ while (gtk_tree_model_iter_children (model, &child, iter))
4326+ gtk_tree_store_remove (GTK_TREE_STORE (model), &child);
4327+ /* That's an invisible dummy, so we always have an expander */
4328+ gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &child, iter,
4329+ 0, 0, NULL, -1);
4330+
4331+ g_signal_handlers_unblock_by_func (model,
4332+ midori_bookmarks_row_deleted_cb,
4333+ bookmarks);
4334+
4335 midori_bookmarks_read_from_db_to_model (bookmarks, GTK_TREE_STORE (model),
4336- iter, katze_item_get_meta_integer (item, "id"), NULL);
4337- g_object_unref (item);
4338+ iter, id, NULL);
4339+
4340+ return FALSE;
4341 }
4342
4343 static void
4344 midori_bookmarks_row_collapsed_cb (GtkTreeView *treeview,
4345 GtkTreeIter *parent,
4346 GtkTreePath *path,
4347- gpointer user_data)
4348+ MidoriBookmarks* bookmarks)
4349 {
4350 GtkTreeModel* model;
4351 GtkTreeStore* treestore;
4352 GtkTreeIter child;
4353
4354 model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
4355+
4356+ g_signal_handlers_block_by_func (model,
4357+ midori_bookmarks_row_deleted_cb,
4358+ bookmarks);
4359+
4360 treestore = GTK_TREE_STORE (model);
4361 while (gtk_tree_model_iter_nth_child (model, &child, parent, 0))
4362 gtk_tree_store_remove (treestore, &child);
4363 /* That's an invisible dummy, so we always have an expander */
4364 gtk_tree_store_insert_with_values (treestore, &child, parent,
4365 0, 0, NULL, -1);
4366+
4367+ g_signal_handlers_block_by_func (model,
4368+ midori_bookmarks_row_deleted_cb,
4369+ bookmarks);
4370+
4371 }
4372
4373 static void
4374@@ -1205,26 +2692,27 @@
4375 MidoriBookmarks *bookmarks)
4376 {
4377 midori_bookmarks_toolbar_update (bookmarks);
4378+ midori_bookmarks_statusbar_update (bookmarks);
4379 }
4380
4381 static KatzeItem*
4382 midori_bookmarks_get_item_at_pos (GtkTreeView *treeview,
4383 gint x, gint y)
4384-{
4385+{
4386 GtkTreeModel* model = gtk_tree_view_get_model (treeview);
4387 GtkTreePath* path;
4388 GtkTreeIter iter;
4389 KatzeItem* item = NULL;
4390
4391 gtk_tree_view_get_path_at_pos (treeview, x, y,
4392- &path, NULL, NULL, NULL);
4393-
4394+ &path, NULL, NULL, NULL);
4395+
4396 if (!path)
4397 return NULL;
4398-
4399+
4400 if (gtk_tree_model_get_iter (model, &iter, path))
4401 gtk_tree_model_get (model, &iter, 0, &item, -1);
4402-
4403+
4404 gtk_tree_path_free (path);
4405
4406 return item;
4407@@ -1296,7 +2784,7 @@
4408
4409 if (bookmarks->hovering_item)
4410 g_object_unref (bookmarks->hovering_item);
4411-
4412+
4413 bookmarks->hovering_item = NULL;
4414
4415 g_object_set (browser, "statusbar-text", "", NULL);
4416@@ -1337,6 +2825,186 @@
4417 midori_bookmarks_filter_timeout_cb, bookmarks, NULL);
4418 }
4419
4420+static GtkTargetEntry midori_bookmarks_dnd_target_entries[]=
4421+{
4422+ {MIDORI_BOOKMARKS_TREE_MODEL_TARGET, GTK_TARGET_SAME_WIDGET, 0},
4423+};
4424+
4425+#define MIDORI_BOOKMARKS_DND_NB_TARGET_ENTRIES \
4426+ G_N_ELEMENTS (midori_bookmarks_dnd_target_entries)
4427+
4428+static guint
4429+item_hash (gconstpointer item)
4430+{
4431+ gint64 id = katze_item_get_meta_integer (KATZE_ITEM (item), "id");
4432+ return g_int64_hash (&id);
4433+}
4434+
4435+static gboolean
4436+item_equal (gconstpointer item_a, gconstpointer item_b)
4437+{
4438+ gint64 id_a = katze_item_get_meta_integer (KATZE_ITEM (item_a), "id");
4439+ gint64 id_b = katze_item_get_meta_integer (KATZE_ITEM (item_b), "id");
4440+ return (id_a == id_b)? TRUE : FALSE;
4441+}
4442+
4443+static gboolean
4444+midori_bookmarks_idle_func (gpointer data)
4445+{
4446+ MidoriBookmarks* bookmarks = MIDORI_BOOKMARKS (data);
4447+ GtkTreeModel* model = gtk_tree_view_get_model (GTK_TREE_VIEW (bookmarks->treeview));
4448+ GHashTableIter hash_iter;
4449+ gpointer key, value;
4450+ GList* list_iter;
4451+
4452+ /* update remaining additions */
4453+ assert_reorder_are_folders (model, bookmarks);
4454+ for (list_iter = bookmarks->added_paths; list_iter ; list_iter = g_list_next (list_iter))
4455+ {
4456+ GtkTreePath* path = (GtkTreePath*)list_iter->data;
4457+
4458+ add_parent_to_reorder (model, path, bookmarks);
4459+ assert_reorder_are_folders (model, bookmarks);
4460+ }
4461+
4462+ g_list_free_full (bookmarks->added_paths, (GDestroyNotify) gtk_tree_path_free);
4463+ bookmarks->added_paths = NULL;
4464+
4465+ /* do actual reordering */
4466+ for (list_iter = bookmarks->reordered_paths; list_iter ; list_iter = g_list_next (list_iter))
4467+ {
4468+ GtkTreeIter local_iter;
4469+ GtkTreePath* path = (GtkTreePath*)list_iter->data;
4470+
4471+ if (gtk_tree_path_get_depth (path))
4472+ {
4473+ GtkTreeIter parent;
4474+
4475+ if (gtk_tree_model_get_iter (model, &parent, path))
4476+ {
4477+ KatzeItem *item;
4478+ gint64 id;
4479+
4480+ gtk_tree_model_get (model, &parent, 0, &item, -1);
4481+
4482+ g_assert (KATZE_ITEM_IS_FOLDER (item));
4483+
4484+ id = katze_item_get_meta_integer (item, "id");
4485+
4486+ if (gtk_tree_model_iter_children (model, &local_iter, &parent))
4487+ midori_bookmarks_set_item_positon(model, &local_iter, id, bookmarks);
4488+
4489+ /* update folder array for menu update */
4490+ katze_array_update (KATZE_ARRAY (item));
4491+
4492+ g_object_unref (item);
4493+ }
4494+ }
4495+ else
4496+ {
4497+ if (gtk_tree_model_get_iter_first (model, &local_iter))
4498+ midori_bookmarks_set_item_positon(model, &local_iter,
4499+ katze_item_get_meta_integer (KATZE_ITEM (bookmarks->bookmarks_db), "id"),
4500+ bookmarks);
4501+
4502+ g_signal_handlers_block_by_func (bookmarks->bookmarks_db,
4503+ midori_bookmarks_update_cb,
4504+ bookmarks);
4505+
4506+ /* update folder array for menu update */
4507+ katze_array_update (KATZE_ARRAY (bookmarks->bookmarks_db));
4508+
4509+ g_signal_handlers_unblock_by_func (bookmarks->bookmarks_db,
4510+ midori_bookmarks_update_cb,
4511+ bookmarks);
4512+ }
4513+ }
4514+
4515+ g_list_free_full (bookmarks->reordered_paths, (GDestroyNotify) gtk_tree_path_free);
4516+ bookmarks->reordered_paths = NULL;
4517+
4518+ /* then finalize updates */
4519+ g_signal_handlers_block_by_func (bookmarks->bookmarks_db,
4520+ midori_bookmarks_update_item_cb,
4521+ bookmarks);
4522+
4523+ g_hash_table_iter_init (&hash_iter, bookmarks->updated_items);
4524+
4525+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
4526+ {
4527+ midori_bookmarks_db_update_item (bookmarks->bookmarks_db, KATZE_ITEM (value));
4528+ g_object_unref (value);
4529+ }
4530+
4531+ g_signal_handlers_unblock_by_func (bookmarks->bookmarks_db,
4532+ midori_bookmarks_update_item_cb,
4533+ bookmarks);
4534+
4535+ g_hash_table_remove_all (bookmarks->updated_items);
4536+
4537+ /* process pending additions of inserted bookmarks */
4538+ for (list_iter = bookmarks->pending_inserts; list_iter; list_iter = g_list_next (list_iter))
4539+ {
4540+ KatzeItem *item = KATZE_ITEM (list_iter->data);
4541+ gint64 id = katze_item_get_meta_integer (item, "id");
4542+ GtkTreeIter iter;
4543+
4544+ if (!midori_bookmarks_reach_item (model, &iter, id))
4545+ midori_bookmarks_add_item (item, bookmarks);
4546+
4547+ g_object_unref (item);
4548+ }
4549+
4550+ g_list_free (bookmarks->pending_inserts);
4551+ bookmarks->pending_inserts = NULL;
4552+ return midori_bookmarks_idle_pending (bookmarks);
4553+}
4554+
4555+static void
4556+midori_bookmarks_update_item (MidoriBookmarks* bookmarks, KatzeItem *item)
4557+{
4558+ midori_bookmarks_idle_start (bookmarks);
4559+
4560+ if (g_hash_table_lookup (bookmarks->updated_items, item))
4561+ return;
4562+
4563+ g_object_ref (item);
4564+ g_hash_table_insert (bookmarks->updated_items, item, item);
4565+}
4566+
4567+static void
4568+midori_bookmarks_idle_remove_item (MidoriBookmarks* bookmarks, KatzeItem *item)
4569+{
4570+ gpointer found;
4571+
4572+ if (KATZE_ITEM_IS_FOLDER (item))
4573+ {
4574+ gint64 id = katze_item_get_meta_integer (item, "id");
4575+ GHashTableIter iter;
4576+ gpointer key, value;
4577+
4578+ g_hash_table_iter_init (&iter, bookmarks->updated_items);
4579+
4580+ while (g_hash_table_iter_next (&iter, &key, &value))
4581+ {
4582+ KatzeItem *hash_item = KATZE_ITEM(key);
4583+
4584+ gint64 parentid = katze_item_get_meta_integer (hash_item, "parentid");
4585+ if (parentid == id)
4586+ {
4587+ g_hash_table_iter_remove (&iter);
4588+ g_object_unref (hash_item);
4589+ }
4590+ }
4591+ }
4592+
4593+ if ((found = g_hash_table_lookup (bookmarks->updated_items, item)) != NULL)
4594+ {
4595+ g_hash_table_remove (bookmarks->updated_items, found);
4596+ g_object_unref (found);
4597+ }
4598+}
4599+
4600 static void
4601 midori_bookmarks_init (MidoriBookmarks* bookmarks)
4602 {
4603@@ -1349,6 +3017,8 @@
4604 GtkCellRenderer* renderer_text;
4605 GtkTreeSelection* selection;
4606
4607+ bookmarks->pending_event = NULL;
4608+
4609 /* Create the filter entry */
4610 entry = sokoke_search_entry_new (_("Search Bookmarks"));
4611 g_signal_connect_after (entry, "changed",
4612@@ -1359,7 +3029,7 @@
4613 gtk_box_pack_start (GTK_BOX (bookmarks), box, FALSE, FALSE, 5);
4614
4615 /* Create the treeview */
4616- model = gtk_tree_store_new (2, KATZE_TYPE_ITEM, G_TYPE_STRING);
4617+ model = midori_bookmarks_tree_store_new (2, KATZE_TYPE_ITEM, G_TYPE_STRING);
4618 treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
4619 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4620 gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (treeview), 1);
4621@@ -1376,19 +3046,32 @@
4622 (GtkTreeCellDataFunc)midori_bookmarks_treeview_render_text_cb,
4623 treeview, NULL);
4624 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4625- gtk_tree_view_set_reorderable (GTK_TREE_VIEW (treeview), TRUE);
4626+ gtk_tree_view_set_reorderable (GTK_TREE_VIEW (treeview), FALSE);
4627+ gtk_tree_view_enable_model_drag_source (
4628+ GTK_TREE_VIEW (treeview),
4629+ GDK_BUTTON1_MASK,
4630+ midori_bookmarks_dnd_target_entries,
4631+ MIDORI_BOOKMARKS_DND_NB_TARGET_ENTRIES,
4632+ GDK_ACTION_MOVE|GDK_ACTION_LINK);
4633+ gtk_tree_view_enable_model_drag_dest (
4634+ GTK_TREE_VIEW (treeview),
4635+ midori_bookmarks_dnd_target_entries,
4636+ MIDORI_BOOKMARKS_DND_NB_TARGET_ENTRIES,
4637+ GDK_ACTION_MOVE|GDK_ACTION_LINK);
4638 g_object_unref (model);
4639 g_object_connect (treeview,
4640 "signal::row-activated",
4641 midori_bookmarks_row_activated_cb, bookmarks,
4642+ "signal::button-press-event",
4643+ midori_bookmarks_button_press_event_cb, bookmarks,
4644 "signal::button-release-event",
4645 midori_bookmarks_button_release_event_cb, bookmarks,
4646 "signal::key-release-event",
4647 midori_bookmarks_key_release_event_cb, bookmarks,
4648 "signal::popup-menu",
4649 midori_bookmarks_popup_menu_cb, bookmarks,
4650- "signal::row-expanded",
4651- midori_bookmarks_row_expanded_cb, bookmarks,
4652+ "signal::test-expand-row",
4653+ midori_bookmarks_test_expand_row_cb, bookmarks,
4654 "signal::row-collapsed",
4655 midori_bookmarks_row_collapsed_cb, bookmarks,
4656 "signal::enter-notify-event",
4657@@ -1398,18 +3081,27 @@
4658 "signal::leave-notify-event",
4659 midori_bookmarks_leave_notify_event_cb, bookmarks,
4660 NULL);
4661- gtk_widget_add_events (GTK_WIDGET (treeview),
4662- GDK_POINTER_MOTION_MASK
4663- | GDK_POINTER_MOTION_HINT_MASK);
4664+
4665+ MIDORI_BOOKMARKS_TREE_STORE (model)->_view = GTK_TREE_VIEW (treeview);
4666+
4667+ gtk_widget_add_events (GTK_WIDGET (treeview),
4668+ GDK_POINTER_MOTION_MASK
4669+ | GDK_POINTER_MOTION_HINT_MASK);
4670
4671 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4672+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
4673 g_signal_connect_after (selection, "changed",
4674- G_CALLBACK (midori_bookmarks_selection_changed_cb),
4675- bookmarks);
4676+ G_CALLBACK (midori_bookmarks_selection_changed_cb),
4677+ bookmarks);
4678 gtk_widget_show (treeview);
4679 gtk_box_pack_start (GTK_BOX (bookmarks), treeview, TRUE, TRUE, 0);
4680 bookmarks->treeview = treeview;
4681+ bookmarks->pending_inserts = NULL;
4682 bookmarks->hovering_item = NULL;
4683+ bookmarks->pending_inserts = NULL;
4684+ bookmarks->updated_items = g_hash_table_new (item_hash, item_equal);
4685+ bookmarks->added_paths = NULL;
4686+ bookmarks->reordered_paths = NULL;
4687 }
4688
4689 static void
4690@@ -1420,5 +3112,16 @@
4691 if (bookmarks->app)
4692 g_object_unref (bookmarks->app);
4693 if (bookmarks->hovering_item)
4694- g_object_unref (bookmarks->hovering_item);
4695+ g_object_unref (bookmarks->hovering_item);
4696+
4697+ if (g_idle_remove_by_data (bookmarks))
4698+ {
4699+ g_list_free_full (bookmarks->pending_inserts, (GDestroyNotify) g_object_unref);
4700+ bookmarks->pending_inserts = NULL;
4701+ g_hash_table_unref (bookmarks->updated_items);
4702+ g_list_free_full (bookmarks->added_paths, (GDestroyNotify) gtk_tree_path_free);
4703+ bookmarks->added_paths = NULL;
4704+ g_list_free_full (bookmarks->reordered_paths, (GDestroyNotify) gtk_tree_path_free);
4705+ bookmarks->reordered_paths = NULL;
4706+ }
4707 }
4708
4709=== modified file 'tests/bookmarks.c'
4710--- tests/bookmarks.c 2013-08-05 19:52:52 +0000
4711+++ tests/bookmarks.c 2014-01-26 19:44:25 +0000
4712@@ -128,7 +128,7 @@
4713 }
4714
4715 db_items = midori_bookmarks_db_query_recursive (db_bookmarks,
4716- "*", "title='%q'", katze_item_get_name (test_item), FALSE);
4717+ "*", "title='%q'", katze_item_get_name (test_item), NULL, FALSE);
4718
4719 /* FIXME g_assert_cmpint (katze_array_get_length (db_items), ==, 1); */
4720 db_item = katze_array_get_nth_item (db_items, 0);

Subscribers

People subscribed via source and target branches

to all changes: