2006-08-04 James Livingston <doclivingston@gmail.com>
[rhythmbox.git] / sources / rb-sourcelist-model.c
blob24a04dddab75d9daf0d237f9084443457a0f9e1e
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * arch-tag: Implementation of GtkTreeModel iface containing RBSource objects
5 * Copyright (C) 2003 Colin Walters <walters@verbum.org>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public
18 * License along with this program; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301 USA.
24 #include "config.h"
26 #include <string.h>
28 #include <glib/gi18n.h>
29 #include <gdk/gdk.h>
30 #include <gtk/gtk.h>
32 #include "rb-sourcelist-model.h"
33 #include "rb-tree-dnd.h"
34 #include "rb-debug.h"
35 #include "rb-marshal.h"
36 #include "rb-playlist-source.h"
38 struct RBSourceListModelPrivate
40 GtkTreeRowReference *groups[RB_SOURCELIST_GROUP_LAST];
43 #define RB_SOURCELIST_MODEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_SOURCELIST_MODEL, RBSourceListModelPrivate))
45 enum
47 DROP_RECEIVED,
48 LAST_SIGNAL
51 static void rb_sourcelist_model_class_init (RBSourceListModelClass *klass);
52 static void rb_sourcelist_model_init (RBSourceListModel *model);
53 static void rb_sourcelist_model_drag_dest_init (RbTreeDragDestIface *iface);
54 static void rb_sourcelist_model_drag_source_init (RbTreeDragSourceIface *iface);
55 static void rb_sourcelist_model_finalize (GObject *object);
56 static gboolean rb_sourcelist_model_is_row_visible (GtkTreeModel *model,
57 GtkTreeIter *iter,
58 RBSourceListModel *sourcelist);
59 static gboolean rb_sourcelist_model_drag_data_received (RbTreeDragDest *drag_dest,
60 GtkTreePath *dest,
61 GtkTreeViewDropPosition pos,
62 GtkSelectionData *selection_data);
63 static gboolean rb_sourcelist_model_row_drop_possible (RbTreeDragDest *drag_dest,
64 GtkTreePath *dest,
65 GtkTreeViewDropPosition pos,
66 GtkSelectionData *selection_data);
67 static gboolean rb_sourcelist_model_row_drop_position (RbTreeDragDest *drag_dest,
68 GtkTreePath *dest_path,
69 GList *targets,
70 GtkTreeViewDropPosition *pos);
71 static GdkAtom rb_sourcelist_model_get_drag_target (RbTreeDragDest *drag_dest,
72 GtkWidget *widget,
73 GdkDragContext *context,
74 GtkTreePath *path,
75 GtkTargetList *target_list);
76 static gboolean rb_sourcelist_model_drag_data_delete (RbTreeDragSource *drag_source,
77 GList *path_list);
78 static gboolean rb_sourcelist_model_drag_data_get (RbTreeDragSource *drag_source,
79 GList *path_list,
80 GtkSelectionData *selection_data);
81 static gboolean rb_sourcelist_model_row_draggable (RbTreeDragSource *drag_source,
82 GList *path_list);
83 static void rb_sourcelist_model_row_inserted_cb (GtkTreeModel *model,
84 GtkTreePath *path,
85 GtkTreeIter *iter,
86 RBSourceListModel *sourcelist);
87 static void rb_sourcelist_model_row_deleted_cb (GtkTreeModel *model,
88 GtkTreePath *path,
89 RBSourceListModel *sourcelist);
91 static guint rb_sourcelist_model_signals[LAST_SIGNAL] = { 0 };
93 enum {
94 TARGET_PROPERTY,
95 TARGET_SOURCE,
96 TARGET_URIS,
97 TARGET_DELETE
100 static const GtkTargetEntry sourcelist_targets[] = { { "text/x-rhythmbox-album", 0, TARGET_PROPERTY },
101 { "text/x-rhythmbox-artist", 0, TARGET_PROPERTY },
102 { "text/x-rhythmbox-genre", 0, TARGET_PROPERTY },
103 { "application/x-rhythmbox-source", 0, TARGET_SOURCE },
104 { "text/uri-list", 0, TARGET_URIS },
105 { "application/x-delete-me", 0, TARGET_DELETE }};
107 static GtkTargetList *sourcelist_drag_target_list = NULL;
109 G_DEFINE_TYPE_EXTENDED (RBSourceListModel,
110 rb_sourcelist_model,
111 GTK_TYPE_TREE_MODEL_FILTER,
113 G_IMPLEMENT_INTERFACE (RB_TYPE_TREE_DRAG_SOURCE,
114 rb_sourcelist_model_drag_source_init)
115 G_IMPLEMENT_INTERFACE (RB_TYPE_TREE_DRAG_DEST,
116 rb_sourcelist_model_drag_dest_init));
118 static void
119 rb_sourcelist_model_class_init (RBSourceListModelClass *class)
121 GObjectClass *o_class;
122 GtkObjectClass *object_class;
124 o_class = (GObjectClass *) class;
125 object_class = (GtkObjectClass *) class;
127 o_class->finalize = rb_sourcelist_model_finalize;
129 rb_sourcelist_model_signals[DROP_RECEIVED] =
130 g_signal_new ("drop_received",
131 G_OBJECT_CLASS_TYPE (object_class),
132 G_SIGNAL_RUN_LAST,
133 G_STRUCT_OFFSET (RBSourceListModelClass, drop_received),
134 NULL, NULL,
135 rb_marshal_VOID__OBJECT_INT_POINTER,
136 G_TYPE_NONE,
138 RB_TYPE_SOURCE, G_TYPE_INT, G_TYPE_POINTER);
140 if (!sourcelist_drag_target_list)
141 sourcelist_drag_target_list =
142 gtk_target_list_new (sourcelist_targets,
143 G_N_ELEMENTS (sourcelist_targets));
145 g_type_class_add_private (class, sizeof (RBSourceListModelPrivate));
148 static void
149 rb_sourcelist_model_drag_dest_init (RbTreeDragDestIface *iface)
151 iface->drag_data_received = rb_sourcelist_model_drag_data_received;
152 iface->row_drop_possible = rb_sourcelist_model_row_drop_possible;
153 iface->row_drop_position = rb_sourcelist_model_row_drop_position;
154 iface->get_drag_target = rb_sourcelist_model_get_drag_target;
157 static void
158 rb_sourcelist_model_drag_source_init (RbTreeDragSourceIface *iface)
160 iface->row_draggable = rb_sourcelist_model_row_draggable;
161 iface->drag_data_get = rb_sourcelist_model_drag_data_get;
162 iface->drag_data_delete = rb_sourcelist_model_drag_data_delete;
165 void
166 rb_sourcelist_model_set_dnd_targets (RBSourceListModel *sourcelist,
167 GtkTreeView *treeview)
169 int n_targets = G_N_ELEMENTS (sourcelist_targets);
170 g_return_if_fail (RB_IS_SOURCELIST_MODEL (sourcelist));
172 rb_tree_dnd_add_drag_dest_support (treeview,
173 (RB_TREE_DEST_EMPTY_VIEW_DROP | RB_TREE_DEST_SELECT_ON_DRAG_TIMEOUT),
174 sourcelist_targets, n_targets,
175 GDK_ACTION_LINK);
177 rb_tree_dnd_add_drag_source_support (treeview,
178 GDK_BUTTON1_MASK,
179 sourcelist_targets, n_targets,
180 GDK_ACTION_COPY);
183 static void
184 rb_sourcelist_model_init (RBSourceListModel *model)
186 model->priv = RB_SOURCELIST_MODEL_GET_PRIVATE (model);
189 static void
190 rb_sourcelist_model_finalize (GObject *object)
192 RBSourceListModel *model;
193 int i;
195 g_return_if_fail (RB_IS_SOURCELIST_MODEL (object));
196 model = RB_SOURCELIST_MODEL (object);
198 for (i = 0; i < RB_SOURCELIST_GROUP_LAST; i++) {
199 gtk_tree_row_reference_free (model->priv->groups[i]);
202 G_OBJECT_CLASS (rb_sourcelist_model_parent_class)->finalize (object);
205 GtkTreeModel *
206 rb_sourcelist_model_new (void)
208 RBSourceListModel *model;
209 GtkTreeStore *store;
210 int i;
211 GType *column_types = g_new (GType, RB_SOURCELIST_MODEL_N_COLUMNS);
213 column_types[RB_SOURCELIST_MODEL_COLUMN_PLAYING] = G_TYPE_BOOLEAN;
214 column_types[RB_SOURCELIST_MODEL_COLUMN_PIXBUF] = GDK_TYPE_PIXBUF;
215 column_types[RB_SOURCELIST_MODEL_COLUMN_NAME] = G_TYPE_STRING;
216 column_types[RB_SOURCELIST_MODEL_COLUMN_SOURCE] = G_TYPE_OBJECT;
217 column_types[RB_SOURCELIST_MODEL_COLUMN_ATTRIBUTES] = PANGO_TYPE_ATTR_LIST;
218 column_types[RB_SOURCELIST_MODEL_COLUMN_VISIBILITY] = G_TYPE_BOOLEAN;
219 store = gtk_tree_store_newv (RB_SOURCELIST_MODEL_N_COLUMNS,
220 column_types);
222 model = RB_SOURCELIST_MODEL (g_object_new (RB_TYPE_SOURCELIST_MODEL,
223 "child-model", store,
224 "virtual-root", NULL,
225 NULL));
226 g_object_unref (store);
228 /* create marker rows used to separate source groups */
229 for (i = 0; i < RB_SOURCELIST_GROUP_LAST; i++) {
230 GtkTreeIter iter;
231 GtkTreePath *path;
233 gtk_tree_store_append (store, &iter, NULL);
234 gtk_tree_store_set (store, &iter,
235 RB_SOURCELIST_MODEL_COLUMN_NAME, "",
236 RB_SOURCELIST_MODEL_COLUMN_SOURCE, NULL,
237 RB_SOURCELIST_MODEL_COLUMN_VISIBILITY, FALSE,
238 -1);
240 path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
241 model->priv->groups[i] = gtk_tree_row_reference_new (GTK_TREE_MODEL (store), path);
242 gtk_tree_path_free (path);
245 /* ensure the group markers get updated as sources are added and removed */
246 g_signal_connect_object (G_OBJECT (store), "row-inserted",
247 G_CALLBACK (rb_sourcelist_model_row_inserted_cb),
248 model, 0);
249 g_signal_connect_object (G_OBJECT (store), "row-deleted",
250 G_CALLBACK (rb_sourcelist_model_row_deleted_cb),
251 model, 0);
253 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (model),
254 (GtkTreeModelFilterVisibleFunc) rb_sourcelist_model_is_row_visible,
255 model, NULL);
257 g_free (column_types);
259 return GTK_TREE_MODEL (model);
262 static gboolean
263 real_row_is_separator (RBSourceListModel *model, GtkTreeIter *iter)
265 int i;
266 GtkTreePath *path;
267 GtkTreeModel *real_model;
268 GtkTreePath *group_path;
270 real_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
271 path = gtk_tree_model_get_path (real_model, iter);
273 /* -1 here because the last group marker is always the last row in
274 * the model, and therefore can never be shown.
276 for (i = 0; i < RB_SOURCELIST_GROUP_LAST-1; i++) {
277 group_path = rb_sourcelist_model_get_group_path (model, i);
279 if (gtk_tree_path_compare (path, group_path) == 0) {
280 /* okay, we know this is a group marker.
281 * if the next row is not a group marker,
282 * then we should show this row as a separator.
284 gboolean separator = FALSE;
286 gtk_tree_path_next (path);
288 gtk_tree_path_free (group_path);
289 group_path = rb_sourcelist_model_get_group_path (model, i+1);
290 separator = (gtk_tree_path_compare (path, group_path) != 0);
292 gtk_tree_path_free (group_path);
293 gtk_tree_path_free (path);
294 return separator;
296 gtk_tree_path_free (group_path);
299 gtk_tree_path_free (path);
301 return FALSE;
304 static gboolean
305 rb_sourcelist_model_is_row_visible (GtkTreeModel *model,
306 GtkTreeIter *iter,
307 RBSourceListModel *sourcelist)
309 RBSource *source;
311 gtk_tree_model_get (GTK_TREE_MODEL (model), iter,
312 RB_SOURCELIST_MODEL_COLUMN_SOURCE, &source, -1);
314 if (source != NULL) {
315 gboolean visible;
316 g_object_get (source, "visibility", &visible, NULL);
318 g_object_unref (source);
320 return visible;
321 } else {
322 return real_row_is_separator (sourcelist, iter);
326 gboolean
327 rb_sourcelist_model_row_is_separator (GtkTreeModel *model,
328 GtkTreeIter *iter,
329 RBSourceListModel *sourcelist)
331 GtkTreeIter real_iter;
332 RBSource *source;
334 /* rows with actual sources are never separators */
335 gtk_tree_model_get (GTK_TREE_MODEL (model), iter,
336 RB_SOURCELIST_MODEL_COLUMN_SOURCE, &source, -1);
337 if (source != NULL) {
338 g_object_unref (source);
339 return FALSE;
342 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (sourcelist),
343 &real_iter,
344 iter);
345 return real_row_is_separator (sourcelist, &real_iter);
348 static int
349 get_group_for_path (RBSourceListModel *model, GtkTreePath *path)
351 GtkTreePath *group_path;
352 gboolean found = FALSE;
353 int i;
355 for (i=0; i < RB_SOURCELIST_GROUP_LAST; i++) {
356 group_path = rb_sourcelist_model_get_group_path (model, i);
357 g_assert (group_path);
359 found = (gtk_tree_path_compare (group_path, path) == 1);
360 gtk_tree_path_free (group_path);
362 if (found)
363 return i;
366 g_assert_not_reached ();
369 static gboolean
370 rb_sourcelist_model_drag_data_received (RbTreeDragDest *drag_dest,
371 GtkTreePath *dest,
372 GtkTreeViewDropPosition pos,
373 GtkSelectionData *selection_data)
375 RBSourceListModel *model;
377 g_return_val_if_fail (RB_IS_SOURCELIST_MODEL (drag_dest), FALSE);
378 model = RB_SOURCELIST_MODEL (drag_dest);
380 if (selection_data->type == gdk_atom_intern ("text/uri-list", TRUE)) {
381 GtkTreeIter iter;
382 RBSource *target = NULL;
384 rb_debug ("text/uri-list drag data received");
386 if (dest != NULL && gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, dest)) {
387 gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
388 RB_SOURCELIST_MODEL_COLUMN_SOURCE, &target, -1);
391 if (target != NULL) {
392 g_signal_emit (G_OBJECT (model), rb_sourcelist_model_signals[DROP_RECEIVED],
393 0, target, pos, selection_data);
394 g_object_unref (target);
397 return TRUE;
400 /* if artist, album or genre, only allow new playlists */
401 if (selection_data->type == gdk_atom_intern ("text/x-rhythmbox-album", TRUE) ||
402 selection_data->type == gdk_atom_intern ("text/x-rhythmbox-artist", TRUE) ||
403 selection_data->type == gdk_atom_intern ("text/x-rhythmbox-genre", TRUE)) {
404 rb_debug ("text/x-rhythmbox-(album|artist|genre) drag data received");
405 g_signal_emit (G_OBJECT (model), rb_sourcelist_model_signals[DROP_RECEIVED],
406 0, NULL, pos, selection_data);
407 return TRUE;
410 if (selection_data->type == gdk_atom_intern ("application/x-rhythmbox-source", TRUE)) {
411 GtkTreePath *path;
412 GtkTreePath *real_dest;
413 char *path_str;
414 GtkTreeIter iter, real_iter;
415 GtkTreeIter real_dest_iter;
416 GtkTreeModel *real_model;
417 RBSource *source;
418 RBSourceListGroup group;
419 int dest_group;
421 if (!dest)
422 return FALSE;
424 real_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
426 path_str = g_strndup ((char *) selection_data->data, selection_data->length);
428 path = gtk_tree_path_new_from_string (path_str);
429 gtk_tree_model_get_iter (GTK_TREE_MODEL (model),
430 &iter, path);
431 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
432 &real_iter, &iter);
434 real_dest =
435 gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (model),
436 dest);
438 gtk_tree_model_get (GTK_TREE_MODEL (real_model), &real_iter,
439 RB_SOURCELIST_MODEL_COLUMN_SOURCE, &source,
440 -1);
441 g_object_get (source, "sourcelist-group", &group, NULL);
442 g_object_unref (source);
444 /* restrict sources to within their group */
445 dest_group = get_group_for_path (model, real_dest);
446 if (dest_group < group) {
447 gtk_tree_path_free (real_dest);
448 real_dest = rb_sourcelist_model_get_group_path (model, group-1);
449 pos = GTK_TREE_VIEW_DROP_AFTER;
450 } else if (dest_group > group) {
451 gtk_tree_path_free (real_dest);
452 real_dest = rb_sourcelist_model_get_group_path (model, group);
453 pos = GTK_TREE_VIEW_DROP_BEFORE;
456 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (real_model),
457 &real_dest_iter, real_dest)) {
459 if (pos == GTK_TREE_VIEW_DROP_AFTER)
460 gtk_tree_store_move_after (GTK_TREE_STORE (real_model),
461 &real_iter, &real_dest_iter);
462 else
463 gtk_tree_store_move_before (GTK_TREE_STORE (real_model),
464 &real_iter, &real_dest_iter);
466 } else {
467 gtk_tree_store_move_before (GTK_TREE_STORE (real_model),
468 &real_iter, NULL);
471 gtk_tree_path_free (real_dest);
472 gtk_tree_path_free (path);
473 g_free (path_str);
476 return FALSE;
479 static gboolean
480 rb_sourcelist_model_row_drop_possible (RbTreeDragDest *drag_dest,
481 GtkTreePath *dest,
482 GtkTreeViewDropPosition pos,
483 GtkSelectionData *selection_data)
485 RBSourceListModel *model;
487 rb_debug ("row drop possible");
488 g_return_val_if_fail (RB_IS_SOURCELIST_MODEL (drag_dest), FALSE);
489 model = RB_SOURCELIST_MODEL (drag_dest);
491 if (selection_data->type == gdk_atom_intern ("text/uri-list", TRUE)
492 && !dest)
493 return FALSE;
495 if (!dest)
496 return TRUE;
498 /* Call the superclass method */
499 return gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (GTK_TREE_STORE (model)),
500 dest, selection_data);
503 static gboolean
504 path_is_droppable (RBSourceListModel *model,
505 GtkTreePath *dest)
507 GtkTreeIter iter;
508 gboolean res;
510 res = FALSE;
512 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, dest)) {
513 RBSource *source;
515 gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
516 RB_SOURCELIST_MODEL_COLUMN_SOURCE, &source, -1);
518 if (source != NULL) {
519 res = rb_source_can_paste (source);
520 g_object_unref (source);
524 return res;
527 static gboolean
528 path_is_reorderable (RBSourceListModel *model,
529 GtkTreePath *dest)
531 GtkTreeIter iter;
533 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, dest)) {
534 RBSource *source;
535 RBSourceListGroup group;
537 gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
538 RB_SOURCELIST_MODEL_COLUMN_SOURCE, &source, -1);
540 if (source == NULL)
541 return FALSE;
543 g_object_get (source, "sourcelist-group", &group, NULL);
544 g_object_unref (source);
546 /* fixed and transient sources are not reorderable, everything else is */
547 return (group != RB_SOURCELIST_GROUP_FIXED &&
548 group != RB_SOURCELIST_GROUP_TRANSIENT);
551 return FALSE;
554 static gboolean
555 rb_sourcelist_model_row_drop_position (RbTreeDragDest *drag_dest,
556 GtkTreePath *dest_path,
557 GList *targets,
558 GtkTreeViewDropPosition *pos)
560 GtkTreeModel *model = GTK_TREE_MODEL (drag_dest);
562 if (g_list_find (targets, gdk_atom_intern ("application/x-rhythmbox-source", TRUE)) && dest_path) {
563 rb_debug ("application/x-rhythmbox-source type");
564 if (!path_is_reorderable (RB_SOURCELIST_MODEL (model), dest_path)) {
565 GtkTreePath *test_path = gtk_tree_path_copy (dest_path);
566 gtk_tree_path_next (test_path);
567 gboolean ret = FALSE;
568 if (path_is_reorderable (RB_SOURCELIST_MODEL (model), test_path)) {
569 *pos = GTK_TREE_VIEW_DROP_AFTER;
570 ret = TRUE;
573 gtk_tree_path_free (test_path);
574 return ret;
577 if (*pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
578 *pos = GTK_TREE_VIEW_DROP_BEFORE;
579 else if (*pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
580 *pos = GTK_TREE_VIEW_DROP_AFTER;
582 return TRUE;
585 if (g_list_find (targets, gdk_atom_intern ("text/uri-list", TRUE))) {
586 rb_debug ("text/uri-list type");
587 if (dest_path && !path_is_droppable (RB_SOURCELIST_MODEL (model), dest_path))
588 return FALSE;
590 *pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE;
591 return TRUE;
594 if ((g_list_find (targets, gdk_atom_intern ("text/x-rhythmbox-artist", TRUE))
595 || g_list_find (targets, gdk_atom_intern ("text/x-rhythmbox-album", TRUE))
596 || g_list_find (targets, gdk_atom_intern ("text/x-rhythmbox-genre", TRUE)))
597 && !g_list_find (targets, gdk_atom_intern ("application/x-rhythmbox-source", TRUE))) {
598 rb_debug ("genre, album, or artist type");
599 *pos = GTK_TREE_VIEW_DROP_AFTER;
600 return TRUE;
603 return FALSE;
606 static GdkAtom
607 rb_sourcelist_model_get_drag_target (RbTreeDragDest *drag_dest,
608 GtkWidget *widget,
609 GdkDragContext *context,
610 GtkTreePath *path,
611 GtkTargetList *target_list)
613 if (g_list_find (context->targets, gdk_atom_intern ("application/x-rhythmbox-source", TRUE))) {
614 /* always accept rb source path if offered */
615 return gdk_atom_intern ("application/x-rhythmbox-source", TRUE);
618 if (path) {
619 /* only accept text/uri-list drops into existing sources */
620 return gdk_atom_intern ("text/uri-list", FALSE);
623 return gtk_drag_dest_find_target (widget, context,
624 target_list);
627 static gboolean
628 rb_sourcelist_model_row_draggable (RbTreeDragSource *drag_source,
629 GList *path_list)
631 GtkTreeIter iter;
632 GtkTreePath *path;
633 GtkTreeModel *model = GTK_TREE_MODEL (drag_source);
635 /* we don't support multi selection */
636 g_return_val_if_fail (g_list_length (path_list) == 1, FALSE);
638 path = gtk_tree_row_reference_get_path (path_list->data);
640 if (path && gtk_tree_model_get_iter (model, &iter, path)) {
641 RBSource *source;
642 RBSourceListGroup group;
644 gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
645 RB_SOURCELIST_MODEL_COLUMN_SOURCE, &source, -1);
647 g_object_get (source, "sourcelist-group", &group, NULL);
648 g_object_unref (source);
650 return (group != RB_SOURCELIST_GROUP_FIXED &&
651 group != RB_SOURCELIST_GROUP_TRANSIENT);
654 return FALSE;
657 static gboolean
658 rb_sourcelist_model_drag_data_get (RbTreeDragSource *drag_source,
659 GList *path_list,
660 GtkSelectionData *selection_data)
662 char *path_str;
663 GtkTreePath *path;
664 guint target;
666 path = gtk_tree_row_reference_get_path (path_list->data);
667 if (path == NULL)
668 return FALSE;
670 if (!gtk_target_list_find (sourcelist_drag_target_list,
671 selection_data->target,
672 &target)) {
673 return FALSE;
676 if (target == TARGET_SOURCE) {
677 rb_debug ("getting drag data as rb source path");
678 path_str = gtk_tree_path_to_string (path);
679 gtk_selection_data_set (selection_data,
680 selection_data->target,
681 8, (guchar *) path_str,
682 strlen (path_str));
683 g_free (path_str);
684 gtk_tree_path_free (path);
685 return TRUE;
686 } else if (target == TARGET_URIS) {
687 RBSource *source;
688 RhythmDBQueryModel *query_model;
689 GtkTreeIter iter;
690 GString *data;
691 gboolean first = TRUE;
693 rb_debug ("getting drag data as uri list");
694 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (drag_source), &iter, path))
695 return FALSE;
697 data = g_string_new ("");
698 gtk_tree_model_get (GTK_TREE_MODEL (drag_source), &iter,
699 RB_SOURCELIST_MODEL_COLUMN_SOURCE, &source, -1);
700 g_object_get (source, "query-model", &query_model, NULL);
701 g_object_unref (source);
703 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (query_model), &iter)) {
704 g_object_unref (query_model);
705 return FALSE;
708 do {
709 RhythmDBEntry *entry;
711 if (first) {
712 g_string_append(data, "\r\n");
713 first = FALSE;
716 entry = rhythmdb_query_model_iter_to_entry (query_model, &iter);
717 g_string_append (data, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
719 rhythmdb_entry_unref (entry);
721 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (query_model), &iter));
723 g_object_unref (query_model);
725 gtk_selection_data_set (selection_data,
726 selection_data->target,
727 8, (guchar *) data->str,
728 data->len);
730 g_string_free (data, TRUE);
731 return TRUE;
733 } else {
734 /* unsupported target */
735 return FALSE;
739 static gboolean
740 rb_sourcelist_model_drag_data_delete (RbTreeDragSource *drag_source,
741 GList *paths)
743 return TRUE;
746 static void
747 rb_sourcelist_model_row_inserted_cb (GtkTreeModel *model,
748 GtkTreePath *path,
749 GtkTreeIter *iter,
750 RBSourceListModel *sourcelist)
752 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sourcelist));
755 static void
756 rb_sourcelist_model_row_deleted_cb (GtkTreeModel *model,
757 GtkTreePath *path,
758 RBSourceListModel *sourcelist)
760 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sourcelist));
763 GtkTreePath *
764 rb_sourcelist_model_get_group_path (RBSourceListModel *sourcelist,
765 RBSourceListGroup group)
767 return gtk_tree_row_reference_get_path (sourcelist->priv->groups[group]);