2 * arch-tag: Implementation of widget to display RhythmDB properties
4 * Copyright (C) 2003 Colin Walters <walters@verbum.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 #include <gtk/gtktreeview.h>
24 #include <gtk/gtktreeselection.h>
25 #include <gtk/gtkcellrenderertext.h>
26 #include <gtk/gtkiconfactory.h>
27 #include <gtk/gtktooltips.h>
28 #include <gdk/gdkkeysyms.h>
30 #include <libgnome/gnome-i18n.h>
31 #include <libgnomevfs/gnome-vfs-utils.h>
35 #include "rb-property-view.h"
36 #include "rb-dialog.h"
39 #include "rhythmdb-property-model.h"
40 #include "rb-stock-icons.h"
41 #include "eel-gconf-extensions.h"
43 static void rb_property_view_class_init (RBPropertyViewClass
*klass
);
44 static void rb_property_view_init (RBPropertyView
*view
);
45 static void rb_property_view_finalize (GObject
*object
);
46 static void rb_property_view_set_property (GObject
*object
,
50 static void rb_property_view_get_property (GObject
*object
,
54 static GObject
* rb_property_view_constructor (GType type
, guint n_construct_properties
,
55 GObjectConstructParam
*construct_properties
);
56 static void rb_property_view_row_activated_cb (GtkTreeView
*treeview
,
58 GtkTreeViewColumn
*column
,
59 RBPropertyView
*view
);
60 static void rb_property_view_selection_changed_cb (GtkTreeSelection
*selection
,
61 RBPropertyView
*view
);
62 static void rb_property_view_pre_row_deleted_cb (RhythmDBPropertyModel
*model
,
63 RBPropertyView
*view
);
64 static void rb_property_view_post_row_deleted_cb (GtkTreeModel
*model
,
66 RBPropertyView
*view
);
68 struct RBPropertyViewPrivate
72 RhythmDBPropType propid
;
74 RhythmDBPropertyModel
*prop_model
;
79 GtkTreeSelection
*selection
;
81 gboolean handling_row_deletion
;
102 static guint rb_property_view_signals
[LAST_SIGNAL
] = { 0 };
111 static const GtkTargetEntry targets_album
[] = {
112 { "text/x-rhythmbox-album", 0, TARGET_ALBUMS
},
113 { "text/uri-list", 0, TARGET_URIS
},
115 static const GtkTargetEntry targets_genre
[] = {
116 { "text/x-rhythmbox-genre", 0, TARGET_GENRE
},
117 { "text/uri-list", 0, TARGET_URIS
},
119 static const GtkTargetEntry targets_artist
[] = {
120 { "text/x-rhythmbox-artist", 0, TARGET_ARTISTS
},
121 { "text/uri-list", 0, TARGET_URIS
},
124 G_DEFINE_TYPE (RBPropertyView
, rb_property_view
, GTK_TYPE_SCROLLED_WINDOW
)
128 rb_property_view_class_init (RBPropertyViewClass
*klass
)
130 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
132 object_class
->finalize
= rb_property_view_finalize
;
133 object_class
->constructor
= rb_property_view_constructor
;
135 object_class
->set_property
= rb_property_view_set_property
;
136 object_class
->get_property
= rb_property_view_get_property
;
138 g_object_class_install_property (object_class
,
140 g_param_spec_object ("db",
144 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
146 g_object_class_install_property (object_class
,
148 g_param_spec_enum ("prop",
153 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
155 g_object_class_install_property (object_class
,
157 g_param_spec_string ("title",
161 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
162 g_object_class_install_property (object_class
,
164 g_param_spec_object ("property-model",
166 "RhythmDBPropertyModel",
167 RHYTHMDB_TYPE_PROPERTY_MODEL
,
170 rb_property_view_signals
[PROPERTY_ACTIVATED
] =
171 g_signal_new ("property-activated",
172 G_OBJECT_CLASS_TYPE (object_class
),
174 G_STRUCT_OFFSET (RBPropertyViewClass
, property_activated
),
176 g_cclosure_marshal_VOID__STRING
,
181 rb_property_view_signals
[PROPERTY_SELECTED
] =
182 g_signal_new ("property-selected",
183 G_OBJECT_CLASS_TYPE (object_class
),
185 G_STRUCT_OFFSET (RBPropertyViewClass
, property_selected
),
187 g_cclosure_marshal_VOID__STRING
,
192 rb_property_view_signals
[PROPERTIES_SELECTED
] =
193 g_signal_new ("properties-selected",
194 G_OBJECT_CLASS_TYPE (object_class
),
196 G_STRUCT_OFFSET (RBPropertyViewClass
, properties_selected
),
198 g_cclosure_marshal_VOID__POINTER
,
203 rb_property_view_signals
[SELECTION_RESET
] =
204 g_signal_new ("property-selection-reset",
205 G_OBJECT_CLASS_TYPE (object_class
),
207 G_STRUCT_OFFSET (RBPropertyViewClass
, selection_reset
),
209 g_cclosure_marshal_VOID__VOID
,
216 rb_property_view_init (RBPropertyView
*view
)
218 view
->priv
= g_new0 (RBPropertyViewPrivate
, 1);
222 rb_property_view_finalize (GObject
*object
)
224 RBPropertyView
*view
;
226 g_return_if_fail (object
!= NULL
);
227 g_return_if_fail (RB_IS_PROPERTY_VIEW (object
));
229 view
= RB_PROPERTY_VIEW (object
);
231 g_return_if_fail (view
->priv
!= NULL
);
233 g_free (view
->priv
->title
);
236 G_OBJECT_CLASS (rb_property_view_parent_class
)->finalize (object
);
241 rb_property_view_set_property (GObject
*object
,
246 RBPropertyView
*view
= RB_PROPERTY_VIEW (object
);
251 view
->priv
->db
= g_value_get_object (value
);
254 view
->priv
->propid
= g_value_get_enum (value
);
257 view
->priv
->title
= g_value_dup_string (value
);
263 if (view
->priv
->prop_model
) {
264 g_signal_handlers_disconnect_by_func (G_OBJECT (view
->priv
->prop_model
),
265 G_CALLBACK (rb_property_view_pre_row_deleted_cb
),
267 g_signal_handlers_disconnect_by_func (G_OBJECT (view
->priv
->prop_model
),
268 G_CALLBACK (rb_property_view_post_row_deleted_cb
),
270 g_object_unref (G_OBJECT (view
->priv
->prop_model
));
273 view
->priv
->prop_model
= g_value_get_object (value
);
275 if (!view
->priv
->prop_model
)
278 gtk_tree_view_set_model (GTK_TREE_VIEW (view
->priv
->treeview
),
279 GTK_TREE_MODEL (view
->priv
->prop_model
));
281 g_object_ref (G_OBJECT (view
->priv
->prop_model
));
282 g_signal_connect_object (G_OBJECT (view
->priv
->prop_model
),
284 G_CALLBACK (rb_property_view_pre_row_deleted_cb
),
287 g_signal_connect_object (G_OBJECT (view
->priv
->prop_model
),
289 G_CALLBACK (rb_property_view_post_row_deleted_cb
),
293 g_signal_handlers_block_by_func (G_OBJECT (view
->priv
->selection
),
294 G_CALLBACK (rb_property_view_selection_changed_cb
),
297 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (view
->priv
->prop_model
), &iter
);
298 gtk_tree_selection_unselect_all (view
->priv
->selection
);
299 gtk_tree_selection_select_iter (view
->priv
->selection
, &iter
);
300 g_signal_handlers_unblock_by_func (G_OBJECT (view
->priv
->selection
),
301 G_CALLBACK (rb_property_view_selection_changed_cb
),
307 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
313 rb_property_view_get_property (GObject
*object
,
318 RBPropertyView
*view
= RB_PROPERTY_VIEW (object
);
323 g_value_set_object (value
, view
->priv
->db
);
326 g_value_set_enum (value
, view
->priv
->propid
);
329 g_value_set_string (value
, view
->priv
->title
);
332 g_value_set_object (value
, view
->priv
->prop_model
);
335 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
341 rb_property_view_new (RhythmDB
*db
, guint propid
, const char *title
)
343 RBPropertyView
*view
;
345 view
= RB_PROPERTY_VIEW (g_object_new (RB_TYPE_PROPERTY_VIEW
,
348 "hscrollbar_policy", GTK_POLICY_AUTOMATIC
,
349 "vscrollbar_policy", GTK_POLICY_ALWAYS
,
350 "shadow_type", GTK_SHADOW_IN
,
356 g_return_val_if_fail (view
->priv
!= NULL
, NULL
);
362 rb_property_view_set_selection_mode (RBPropertyView
*view
,
363 GtkSelectionMode mode
)
365 g_return_if_fail (mode
== GTK_SELECTION_SINGLE
|| mode
== GTK_SELECTION_MULTIPLE
);
366 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (view
->priv
->treeview
)),
371 rb_property_view_reset (RBPropertyView
*view
)
373 RhythmDBPropertyModel
*model
;
375 model
= rhythmdb_property_model_new (view
->priv
->db
, view
->priv
->propid
);
377 g_object_set (G_OBJECT (view
), "property-model", model
, NULL
);
378 g_object_unref (G_OBJECT (model
));
381 RhythmDBPropertyModel
*
382 rb_property_view_get_model (RBPropertyView
*view
)
384 RhythmDBPropertyModel
*model
;
386 g_object_get (G_OBJECT (view
), "property-model", &model
, NULL
);
391 rb_property_view_set_model (RBPropertyView
*view
, RhythmDBPropertyModel
*model
)
393 g_object_set (G_OBJECT (view
), "property-model", model
, NULL
);
397 rb_property_view_pre_row_deleted_cb (RhythmDBPropertyModel
*model
,
398 RBPropertyView
*view
)
400 view
->priv
->handling_row_deletion
= TRUE
;
401 rb_debug ("pre row deleted");
405 rb_property_view_post_row_deleted_cb (GtkTreeModel
*model
,
407 RBPropertyView
*view
)
409 view
->priv
->handling_row_deletion
= FALSE
;
410 rb_debug ("post row deleted");
411 if (gtk_tree_selection_count_selected_rows (view
->priv
->selection
) == 0) {
412 GtkTreeIter first_iter
;
413 rb_debug ("no rows selected, signalling reset");
414 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (view
->priv
->prop_model
),
416 g_signal_handlers_block_by_func (G_OBJECT (view
->priv
->selection
),
417 G_CALLBACK (rb_property_view_selection_changed_cb
),
419 gtk_tree_selection_select_iter (view
->priv
->selection
, &first_iter
);
420 g_signal_emit (G_OBJECT (view
), rb_property_view_signals
[SELECTION_RESET
], 0);
421 g_signal_handlers_unblock_by_func (G_OBJECT (view
->priv
->selection
),
422 G_CALLBACK (rb_property_view_selection_changed_cb
),
428 rb_property_view_get_num_properties (RBPropertyView
*view
)
430 return gtk_tree_model_iter_n_children (GTK_TREE_MODEL (view
->priv
->prop_model
),
435 rb_property_view_cell_data_func (GtkTreeViewColumn
*column
, GtkCellRenderer
*renderer
,
436 GtkTreeModel
*tree_model
, GtkTreeIter
*iter
,
437 RBPropertyView
*view
)
442 gtk_tree_model_get (GTK_TREE_MODEL (tree_model
), iter
, 0, &str
,
445 g_object_set (G_OBJECT (renderer
), "text", str
,
446 "weight", G_UNLIKELY (bold
) ? PANGO_WEIGHT_BOLD
: PANGO_WEIGHT_NORMAL
,
452 rb_property_view_constructor (GType type
, guint n_construct_properties
,
453 GObjectConstructParam
*construct_properties
)
455 GtkTreeViewColumn
*column
;
456 GtkCellRenderer
*renderer
;
458 RBPropertyView
*view
;
459 RBPropertyViewClass
*klass
;
461 klass
= RB_PROPERTY_VIEW_CLASS (g_type_class_peek (RB_TYPE_PROPERTY_VIEW
));
463 view
= RB_PROPERTY_VIEW (G_OBJECT_CLASS (rb_property_view_parent_class
)->
464 constructor (type
, n_construct_properties
, construct_properties
));
466 view
->priv
->prop_model
= rhythmdb_property_model_new (view
->priv
->db
, view
->priv
->propid
);
467 view
->priv
->treeview
= GTK_WIDGET (gtk_tree_view_new_with_model (GTK_TREE_MODEL (view
->priv
->prop_model
)));
469 rhythmdb_property_model_enable_drag (view
->priv
->prop_model
,
470 GTK_TREE_VIEW (view
->priv
->treeview
));
472 g_signal_connect_object (G_OBJECT (view
->priv
->treeview
),
474 G_CALLBACK (rb_property_view_row_activated_cb
),
478 view
->priv
->selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (view
->priv
->treeview
));
479 g_signal_connect_object (G_OBJECT (view
->priv
->selection
),
481 G_CALLBACK (rb_property_view_selection_changed_cb
),
485 gtk_container_add (GTK_CONTAINER (view
), view
->priv
->treeview
);
487 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view
->priv
->treeview
), TRUE
);
488 gtk_tree_selection_set_mode (view
->priv
->selection
, GTK_SELECTION_SINGLE
);
490 column
= gtk_tree_view_column_new ();
491 renderer
= gtk_cell_renderer_text_new ();
492 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
493 gtk_tree_view_column_set_cell_data_func (column
, renderer
,
494 (GtkTreeCellDataFunc
) rb_property_view_cell_data_func
,
496 gtk_tree_view_column_set_title (column
, view
->priv
->title
);
497 gtk_tree_view_column_set_sizing (column
, GTK_TREE_VIEW_COLUMN_FIXED
);
498 gtk_tree_view_append_column (GTK_TREE_VIEW (view
->priv
->treeview
),
501 return G_OBJECT (view
);
505 rb_property_view_row_activated_cb (GtkTreeView
*treeview
,
507 GtkTreeViewColumn
*column
,
508 RBPropertyView
*view
)
514 rb_debug ("row activated");
515 gtk_tree_model_get_iter (GTK_TREE_MODEL (view
->priv
->prop_model
), &iter
, path
);
517 gtk_tree_model_get (GTK_TREE_MODEL (view
->priv
->prop_model
), &iter
, 0,
518 &val
, 1, &is_all
, -1);
520 rb_debug ("emitting property activated");
521 g_signal_emit (G_OBJECT (view
), rb_property_view_signals
[PROPERTY_ACTIVATED
], 0,
522 is_all
? NULL
: val
);
526 rb_property_view_set_selection (RBPropertyView
*view
, const GList
*vals
)
528 view
->priv
->handling_row_deletion
= TRUE
;
530 gtk_tree_selection_unselect_all (view
->priv
->selection
);
532 for (; vals
; vals
= vals
->next
) {
535 if (rhythmdb_property_model_iter_from_string (view
->priv
->prop_model
, vals
->data
, &iter
)) {
538 gtk_tree_selection_select_iter (view
->priv
->selection
, &iter
);
539 path
= gtk_tree_model_get_path (GTK_TREE_MODEL (view
->priv
->prop_model
), &iter
);
541 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (view
->priv
->treeview
),
544 gtk_tree_path_free (path
);
550 view
->priv
->handling_row_deletion
= FALSE
;
551 rb_property_view_selection_changed_cb (view
->priv
->selection
, view
);
555 rb_property_view_selection_changed_cb (GtkTreeSelection
*selection
,
556 RBPropertyView
*view
)
558 const char *selected_prop
;
563 if (view
->priv
->handling_row_deletion
)
566 rb_debug ("selection changed");
567 if (gtk_tree_selection_get_mode (selection
) == GTK_SELECTION_MULTIPLE
) {
568 GList
*selected_rows
, *tem
;
569 GList
*selected_properties
= NULL
;
571 selected_rows
= gtk_tree_selection_get_selected_rows (view
->priv
->selection
, &model
);
572 for (tem
= selected_rows
; tem
; tem
= tem
->next
) {
573 g_assert (gtk_tree_model_get_iter (model
, &iter
, tem
->data
));
574 gtk_tree_model_get (model
, &iter
, 0, &selected_prop
, 1, &is_all
, -1);
576 g_list_free (selected_properties
);
577 selected_properties
= NULL
;
580 selected_properties
= g_list_append (selected_properties
,
581 g_strdup (selected_prop
));
584 g_list_foreach (selected_rows
, (GFunc
) gtk_tree_path_free
, NULL
);
585 g_list_free (selected_rows
);
588 g_signal_handlers_block_by_func (G_OBJECT (view
->priv
->selection
),
589 G_CALLBACK (rb_property_view_selection_changed_cb
),
591 gtk_tree_selection_unselect_all (selection
);
592 gtk_tree_model_get_iter_first (model
, &iter
);
593 gtk_tree_selection_select_iter (selection
, &iter
);
594 g_signal_handlers_unblock_by_func (G_OBJECT (view
->priv
->selection
),
595 G_CALLBACK (rb_property_view_selection_changed_cb
),
598 g_signal_emit (G_OBJECT (view
), rb_property_view_signals
[PROPERTIES_SELECTED
], 0,
599 selected_properties
);
601 if (gtk_tree_selection_get_selected (view
->priv
->selection
, &model
, &iter
)) {
602 gtk_tree_model_get (model
, &iter
, 0, &selected_prop
, 1, &is_all
, -1);
603 g_signal_emit (G_OBJECT (view
), rb_property_view_signals
[PROPERTY_SELECTED
], 0,
604 is_all
? NULL
: selected_prop
);
610 rb_property_view_get_treeview (RBPropertyView
*view
)
612 return view
->priv
->treeview
;