2
* arch-tag: Implementation of widget to display RhythmDB entries
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>
36
#include "rb-tree-dnd.h"
37
#include "rb-tree-view-column.h"
38
#include "rb-entry-view.h"
39
#include "rb-dialog.h"
43
#include "rhythmdb-query-model.h"
44
#include "rb-cell-renderer-pixbuf.h"
45
#include "rb-cell-renderer-rating.h"
46
#include "rb-string-helpers.h"
47
#include "rb-stock-icons.h"
48
#include "rb-preferences.h"
49
#include "rb-tree-view.h"
50
#include "eel-gconf-extensions.h"
52
static const GtkTargetEntry rb_entry_view_drag_types[] = {{ "text/uri-list", 0, 0 }};
54
struct RBEntryViewColumnSortData
56
GCompareDataFunc func;
60
static void rb_entry_view_class_init (RBEntryViewClass *klass);
61
static void rb_entry_view_init (RBEntryView *view);
62
static GObject *rb_entry_view_constructor (GType type, guint n_construct_properties,
63
GObjectConstructParam *construct_properties);
64
static void rb_entry_view_finalize (GObject *object);
65
static void rb_entry_view_set_property (GObject *object,
69
static void rb_entry_view_get_property (GObject *object,
73
static void rb_entry_view_selection_changed_cb (GtkTreeSelection *selection,
75
static void rb_entry_view_row_activated_cb (GtkTreeView *treeview,
77
GtkTreeViewColumn *column,
79
static void rb_entry_view_row_inserted_cb (GtkTreeModel *model,
83
static void rb_entry_view_row_deleted_cb (GtkTreeModel *model,
86
static void rb_entry_view_row_changed_cb (GtkTreeModel *model,
90
static gboolean emit_entry_changed (RBEntryView *view);
91
static void queue_changed_sig (RBEntryView *view);
92
static void rb_entry_view_sync_columns_visible (RBEntryView *view);
93
static void rb_entry_view_columns_config_changed_cb (GConfClient* client,
97
static void rb_entry_view_sort_key_changed_cb (GConfClient* client,
101
/* static gboolean rb_entry_view_dummy_drag_drop_cb (GtkWidget *widget, */
102
/* GdkDragContext *drag_context, */
103
/* int x, int y, guint time, */
104
/* gpointer user_data); */
105
static void rb_entry_view_rated_cb (RBCellRendererRating *cellrating,
109
static gboolean rb_entry_view_button_press_cb (GtkTreeView *treeview,
110
GdkEventButton *event,
112
static void rb_entry_view_entry_is_visible (RBEntryView *view, RhythmDBEntry *entry,
113
gboolean *realized, gboolean *visible,
115
static void rb_entry_view_scroll_to_iter (RBEntryView *view,
118
struct RBEntryViewReverseSortingData
120
GCompareDataFunc func;
124
static gint reverse_sorting_func (gpointer a, gpointer b, struct RBEntryViewReverseSortingData *data);
126
struct RBEntryViewPrivate
130
RhythmDBQueryModel *model;
133
GtkTreeSelection *selection;
136
RhythmDBQueryModel *playing_model;
137
RhythmDBEntry *playing_entry;
138
gboolean playing_entry_in_view;
139
GtkTreeIter playing_entry_iter;
141
gboolean is_drag_source;
142
gboolean is_drag_dest;
144
GdkPixbuf *playing_pixbuf;
145
GdkPixbuf *paused_pixbuf;
148
guint sorting_gconf_notification_id;
149
GList *clickable_columns;
150
GtkTreeViewColumn *sorting_column;
152
struct RBEntryViewReverseSortingData *reverse_sorting_data;
155
gboolean have_selection;
157
gboolean keep_selection;
159
RhythmDBEntry *selected_entry;
161
gboolean change_sig_queued;
164
gboolean selection_lock;
166
GHashTable *column_key_map;
168
guint gconf_notification_id;
169
GHashTable *propid_column_map;
170
GHashTable *column_sort_data_map;
184
PLAYING_ENTRY_DELETED,
201
static GObjectClass *parent_class = NULL;
203
static guint rb_entry_view_signals[LAST_SIGNAL] = { 0 };
206
rb_entry_view_get_type (void)
208
static GType rb_entry_view_type = 0;
210
if (rb_entry_view_type == 0)
212
static const GTypeInfo our_info =
214
sizeof (RBEntryViewClass),
217
(GClassInitFunc) rb_entry_view_class_init,
220
sizeof (RBEntryView),
222
(GInstanceInitFunc) rb_entry_view_init
225
rb_entry_view_type = g_type_register_static (GTK_TYPE_SCROLLED_WINDOW,
230
return rb_entry_view_type;
234
rb_entry_view_class_init (RBEntryViewClass *klass)
236
GObjectClass *object_class = G_OBJECT_CLASS (klass);
238
parent_class = g_type_class_peek_parent (klass);
240
object_class->finalize = rb_entry_view_finalize;
241
object_class->constructor = rb_entry_view_constructor;
243
object_class->set_property = rb_entry_view_set_property;
244
object_class->get_property = rb_entry_view_get_property;
246
g_object_class_install_property (object_class,
248
g_param_spec_object ("db",
252
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
253
g_object_class_install_property (object_class,
255
g_param_spec_object ("model",
256
"RhythmDBQueryModel",
257
"RhythmDBQueryModel",
258
RHYTHMDB_TYPE_QUERY_MODEL,
261
g_object_class_install_property (object_class,
263
g_param_spec_pointer ("playing-entry",
267
g_object_class_install_property (object_class,
269
g_param_spec_string ("sort-key",
273
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
274
g_object_class_install_property (object_class,
276
g_param_spec_boolean ("is-drag-source",
278
"whether or not this is a drag source",
280
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
281
g_object_class_install_property (object_class,
283
g_param_spec_boolean ("is-drag-dest",
285
"whether or not this is a drag dest",
287
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
288
rb_entry_view_signals[ENTRY_ADDED] =
289
g_signal_new ("entry-added",
290
G_OBJECT_CLASS_TYPE (object_class),
292
G_STRUCT_OFFSET (RBEntryViewClass, entry_added),
294
g_cclosure_marshal_VOID__POINTER,
298
rb_entry_view_signals[ENTRY_DELETED] =
299
g_signal_new ("entry-deleted",
300
G_OBJECT_CLASS_TYPE (object_class),
302
G_STRUCT_OFFSET (RBEntryViewClass, entry_deleted),
304
g_cclosure_marshal_VOID__POINTER,
308
rb_entry_view_signals[ENTRIES_REPLACED] =
309
g_signal_new ("entries-replaced",
310
G_OBJECT_CLASS_TYPE (object_class),
312
G_STRUCT_OFFSET (RBEntryViewClass, entries_replaced),
314
g_cclosure_marshal_VOID__VOID,
317
rb_entry_view_signals[ENTRY_ACTIVATED] =
318
g_signal_new ("entry-activated",
319
G_OBJECT_CLASS_TYPE (object_class),
321
G_STRUCT_OFFSET (RBEntryViewClass, entry_activated),
323
g_cclosure_marshal_VOID__POINTER,
327
rb_entry_view_signals[ENTRY_SELECTED] =
328
g_signal_new ("entry-selected",
329
G_OBJECT_CLASS_TYPE (object_class),
331
G_STRUCT_OFFSET (RBEntryViewClass, entry_selected),
333
g_cclosure_marshal_VOID__POINTER,
337
rb_entry_view_signals[CHANGED] =
338
g_signal_new ("changed",
339
G_OBJECT_CLASS_TYPE (object_class),
341
G_STRUCT_OFFSET (RBEntryViewClass, changed),
343
g_cclosure_marshal_VOID__VOID,
346
rb_entry_view_signals[SHOW_POPUP] =
347
g_signal_new ("show_popup",
348
G_OBJECT_CLASS_TYPE (object_class),
350
G_STRUCT_OFFSET (RBEntryViewClass, show_popup),
352
g_cclosure_marshal_VOID__VOID,
355
rb_entry_view_signals[PLAYING_ENTRY_DELETED] =
356
g_signal_new ("playing_entry_deleted",
357
G_OBJECT_CLASS_TYPE (object_class),
359
G_STRUCT_OFFSET (RBEntryViewClass, playing_entry_removed),
361
g_cclosure_marshal_VOID__POINTER,
364
rb_entry_view_signals[HAVE_SEL_CHANGED] =
365
g_signal_new ("have_selection_changed",
366
G_OBJECT_CLASS_TYPE (object_class),
368
G_STRUCT_OFFSET (RBEntryViewClass, have_selection_changed),
370
g_cclosure_marshal_VOID__BOOLEAN,
374
rb_entry_view_signals[SORT_ORDER_CHANGED] =
375
g_signal_new ("sort-order-changed",
376
G_OBJECT_CLASS_TYPE (object_class),
378
G_STRUCT_OFFSET (RBEntryViewClass, sort_order_changed),
380
g_cclosure_marshal_VOID__VOID,
386
rb_entry_view_init (RBEntryView *view)
388
view->priv = g_new0 (RBEntryViewPrivate, 1);
390
view->priv->playing_pixbuf = rb_pixbuf_new_from_stock (RB_STOCK_PLAYING,
393
view->priv->paused_pixbuf = rb_pixbuf_new_from_stock (RB_STOCK_PAUSED,
396
view->priv->propid_column_map = g_hash_table_new (NULL, NULL);
397
view->priv->column_sort_data_map = g_hash_table_new_full (NULL, NULL, NULL, g_free);
398
view->priv->column_key_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
402
rb_entry_view_finalize (GObject *object)
406
g_return_if_fail (object != NULL);
407
g_return_if_fail (RB_IS_ENTRY_VIEW (object));
409
view = RB_ENTRY_VIEW (object);
411
g_return_if_fail (view->priv != NULL);
413
if (view->priv->change_sig_queued)
414
g_source_remove (view->priv->change_sig_id);
416
if (view->priv->gconf_notification_id > 0)
417
eel_gconf_notification_remove (view->priv->gconf_notification_id);
418
if (view->priv->sorting_gconf_notification_id > 0)
419
eel_gconf_notification_remove (view->priv->sorting_gconf_notification_id);
421
g_list_free (view->priv->clickable_columns);
423
g_hash_table_destroy (view->priv->propid_column_map);
424
g_hash_table_destroy (view->priv->column_sort_data_map);
425
g_hash_table_destroy (view->priv->column_key_map);
427
g_object_unref (G_OBJECT (view->priv->playing_pixbuf));
428
g_object_unref (G_OBJECT (view->priv->paused_pixbuf));
430
g_free (view->priv->sorting_key);
434
G_OBJECT_CLASS (parent_class)->finalize (object);
439
rb_entry_view_set_property (GObject *object,
444
RBEntryView *view = RB_ENTRY_VIEW (object);
449
view->priv->db = g_value_get_object (value);
451
case PROP_SORTING_KEY:
452
g_free (view->priv->sorting_key);
453
view->priv->sorting_key = g_value_dup_string (value);
457
RhythmDBQueryModel *new_model;
458
struct RBEntryViewColumnSortData *sort_data;
460
if (view->priv->model) {
461
rhythmdb_query_model_cancel (view->priv->model);
462
rhythmdb_query_model_set_connected (RHYTHMDB_QUERY_MODEL (view->priv->model), FALSE);
463
g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->model),
464
G_CALLBACK (rb_entry_view_row_inserted_cb),
466
g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->model),
467
G_CALLBACK (rb_entry_view_row_deleted_cb),
469
g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->model),
470
G_CALLBACK (rb_entry_view_row_changed_cb),
473
new_model = g_value_get_object (value);
475
rhythmdb_query_model_set_connected (RHYTHMDB_QUERY_MODEL (new_model),
478
g_signal_connect_object (G_OBJECT (new_model),
480
G_CALLBACK (rb_entry_view_row_inserted_cb),
483
g_signal_connect_object (G_OBJECT (new_model),
485
G_CALLBACK (rb_entry_view_row_deleted_cb),
488
g_signal_connect_object (G_OBJECT (new_model),
490
G_CALLBACK (rb_entry_view_row_changed_cb),
494
if (view->priv->sorting_column) {
495
sort_data = g_hash_table_lookup (view->priv->column_sort_data_map,
496
view->priv->sorting_column);
497
g_assert (sort_data);
499
if (view->priv->sorting_order != GTK_SORT_DESCENDING) {
500
g_object_set (G_OBJECT (new_model), "sort-func",
501
sort_data->func, "sort-data", sort_data->data, NULL);
503
g_free (view->priv->reverse_sorting_data);
504
view->priv->reverse_sorting_data
505
= g_new (struct RBEntryViewReverseSortingData, 1);
506
view->priv->reverse_sorting_data->func = sort_data->func;
507
view->priv->reverse_sorting_data->data = sort_data->data;
509
g_object_set (G_OBJECT (new_model), "sort-func",
510
reverse_sorting_func, "sort-data",
511
view->priv->reverse_sorting_data, NULL);
515
gtk_tree_view_set_model (GTK_TREE_VIEW (view->priv->treeview),
516
GTK_TREE_MODEL (new_model));
517
view->priv->model = new_model;
518
view->priv->have_selection = FALSE;
520
if (view->priv->resorting) {
521
/* When the sort order changes, the model is replaced
522
* but the set of entries doesn't change. */
523
view->priv->resorting = FALSE;
525
g_signal_emit (G_OBJECT (view), rb_entry_view_signals[ENTRIES_REPLACED], 0);
528
queue_changed_sig (view);
532
case PROP_PLAYING_ENTRY:
536
RhythmDBEntry *entry;
537
gboolean realized, visible;
539
entry = g_value_get_pointer (value);
541
if (view->priv->playing_entry != NULL
542
&& view->priv->playing_entry_in_view) {
543
path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->priv->playing_model),
544
&view->priv->playing_entry_iter);
545
gtk_tree_model_row_changed (GTK_TREE_MODEL (view->priv->playing_model),
546
path, &view->priv->playing_entry_iter);
547
gtk_tree_path_free (path);
548
g_object_unref (G_OBJECT (view->priv->playing_model));
551
view->priv->playing_entry = entry;
552
g_object_ref (G_OBJECT (view->priv->model));
553
view->priv->playing_model = view->priv->model;
555
if (view->priv->playing_entry != NULL) {
556
view->priv->playing_entry_in_view =
557
rhythmdb_query_model_entry_to_iter (view->priv->model,
558
view->priv->playing_entry,
559
&view->priv->playing_entry_iter);
560
if (view->priv->playing_entry_in_view) {
561
path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->priv->model),
562
&view->priv->playing_entry_iter);
563
gtk_tree_model_row_changed (GTK_TREE_MODEL (view->priv->model),
564
path, &view->priv->playing_entry_iter);
565
gtk_tree_path_free (path);
569
if (view->priv->playing_entry
570
&& view->priv->playing_entry_in_view) {
571
rb_entry_view_entry_is_visible (view, view->priv->playing_entry,
572
&realized, &visible, &iter);
573
if (realized && !visible)
574
rb_entry_view_scroll_to_iter (view, &iter);
578
case PROP_IS_DRAG_SOURCE:
579
view->priv->is_drag_source = g_value_get_boolean (value);
581
case PROP_IS_DRAG_DEST:
582
view->priv->is_drag_dest = g_value_get_boolean (value);
585
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
591
rb_entry_view_get_property (GObject *object,
596
RBEntryView *view = RB_ENTRY_VIEW (object);
601
g_value_set_object (value, view->priv->db);
603
case PROP_SORTING_KEY:
604
g_value_set_string (value, view->priv->sorting_key);
606
case PROP_PLAYING_ENTRY:
607
g_value_set_pointer (value, view->priv->playing_entry);
609
case PROP_IS_DRAG_SOURCE:
610
g_value_set_boolean (value, view->priv->is_drag_source);
612
case PROP_IS_DRAG_DEST:
613
g_value_set_boolean (value, view->priv->is_drag_dest);
616
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
622
rb_entry_view_new (RhythmDB *db, const char *sort_key,
623
gboolean is_drag_source, gboolean is_drag_dest)
627
view = RB_ENTRY_VIEW (g_object_new (RB_TYPE_ENTRY_VIEW,
630
"hscrollbar_policy", GTK_POLICY_AUTOMATIC,
631
"vscrollbar_policy", GTK_POLICY_ALWAYS,
632
"shadow_type", GTK_SHADOW_IN,
634
"sort-key", sort_key,
635
"is-drag-source", is_drag_source,
636
"is-drag-dest", is_drag_dest,
639
g_return_val_if_fail (view->priv != NULL, NULL);
645
rb_entry_view_set_model (RBEntryView *view, RhythmDBQueryModel *model)
647
g_object_set (G_OBJECT (view), "model", model, NULL);
651
rb_entry_view_busy (RBEntryView *view)
653
return view->priv->model &&
654
rhythmdb_query_model_has_pending_changes (RHYTHMDB_QUERY_MODEL (view->priv->model));
658
rb_entry_view_get_duration (RBEntryView *view)
660
return rhythmdb_query_model_get_duration (RHYTHMDB_QUERY_MODEL (view->priv->model));
664
rb_entry_view_get_total_size (RBEntryView *view)
666
return rhythmdb_query_model_get_size (RHYTHMDB_QUERY_MODEL (view->priv->model));
669
static RhythmDBEntry *
670
entry_from_tree_path (RBEntryView *view, GtkTreePath *path)
672
GtkTreeIter entry_iter;
673
RhythmDBEntry *entry;
675
g_assert (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->priv->model), &entry_iter, path));
676
gtk_tree_model_get (GTK_TREE_MODEL (view->priv->model), &entry_iter, 0,
681
static inline RhythmDBEntry *
682
entry_from_tree_iter (RBEntryView *view, GtkTreeIter *iter)
684
RhythmDBEntry *entry;
685
gtk_tree_model_get (GTK_TREE_MODEL (view->priv->model), iter, 0,
691
reverse_sorting_func (gpointer a, gpointer b, struct RBEntryViewReverseSortingData *data)
693
return - data->func (a, b, data->data);
696
/* Sweet name, eh? */
697
struct RBEntryViewCellDataFuncData {
699
RhythmDBPropType propid;
703
rb_entry_view_album_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
707
const char *a_str = NULL;
708
const char *b_str = NULL;
711
/* Sort by album name */
712
a_str = rhythmdb_entry_get_string (view->priv->db, a, RHYTHMDB_PROP_ALBUM_SORT_KEY);
713
b_str = rhythmdb_entry_get_string (view->priv->db, b, RHYTHMDB_PROP_ALBUM_SORT_KEY);
715
ret = strcmp (a_str, b_str);
719
/* Then by disc number, */
720
a_int = rhythmdb_entry_get_int (view->priv->db, a, RHYTHMDB_PROP_DISC_NUMBER);
721
b_int = rhythmdb_entry_get_int (view->priv->db, b, RHYTHMDB_PROP_DISC_NUMBER);
723
return (a_int < b_int ? -1 : 1);
725
/* by track number */
726
a_int = rhythmdb_entry_get_int (view->priv->db, a, RHYTHMDB_PROP_TRACK_NUMBER);
727
b_int = rhythmdb_entry_get_int (view->priv->db, b, RHYTHMDB_PROP_TRACK_NUMBER);
730
return (a_int < b_int ? -1 : 1);
732
/* And finally by title */
733
a_str = rhythmdb_entry_get_string (view->priv->db, a, RHYTHMDB_PROP_TITLE_SORT_KEY);
734
b_str = rhythmdb_entry_get_string (view->priv->db, b, RHYTHMDB_PROP_TITLE_SORT_KEY);
736
return strcmp (a_str, b_str);
740
rb_entry_view_artist_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
743
const char *a_str = NULL;
744
const char *b_str = NULL;
747
a_str = rhythmdb_entry_get_string (view->priv->db, a, RHYTHMDB_PROP_ARTIST_SORT_KEY);
748
b_str = rhythmdb_entry_get_string (view->priv->db, b, RHYTHMDB_PROP_ARTIST_SORT_KEY);
750
ret = strcmp (a_str, b_str);
754
return rb_entry_view_album_sort_func (a, b, view);
758
rb_entry_view_genre_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
761
const char *a_str = NULL;
762
const char *b_str = NULL;
765
a_str = rhythmdb_entry_get_string (view->priv->db, a, RHYTHMDB_PROP_GENRE_SORT_KEY);
766
b_str = rhythmdb_entry_get_string (view->priv->db, b, RHYTHMDB_PROP_GENRE_SORT_KEY);
768
ret = strcmp (a_str, b_str);
772
return rb_entry_view_artist_sort_func (a, b, view);
776
rb_entry_view_track_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
779
return rb_entry_view_album_sort_func (a, b, view);
784
rb_entry_view_int_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
785
struct RBEntryViewCellDataFuncData *data)
790
a_val = rhythmdb_entry_get_int (data->view->priv->db, a, data->propid);
791
b_val = rhythmdb_entry_get_int (data->view->priv->db, b, data->propid);
793
ret = (a_val == b_val ? 0 : (a_val > b_val ? 1 : -1));
799
rb_entry_view_double_ceiling_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
800
struct RBEntryViewCellDataFuncData *data)
802
gdouble a_val, b_val;
805
a_val = ceil (rhythmdb_entry_get_double (data->view->priv->db, a, data->propid));
806
b_val = ceil (rhythmdb_entry_get_double (data->view->priv->db, b, data->propid));
808
ret = (a_val == b_val ? 0 : (a_val > b_val ? 1 : -1));
814
rb_entry_view_long_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
815
struct RBEntryViewCellDataFuncData *data)
820
a_val = rhythmdb_entry_get_long (data->view->priv->db, a, data->propid);
821
b_val = rhythmdb_entry_get_long (data->view->priv->db, b, data->propid);
823
ret = (a_val == b_val ? 0 : (a_val > b_val ? 1 : -1));
829
rb_entry_view_string_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
830
struct RBEntryViewCellDataFuncData *data)
836
a_val = rhythmdb_entry_get_string (data->view->priv->db, a, data->propid);
837
b_val = rhythmdb_entry_get_string (data->view->priv->db, b, data->propid);
839
ret = strcmp (a_val, b_val);
845
rb_entry_view_playing_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
846
GtkTreeModel *tree_model, GtkTreeIter *iter,
849
RhythmDBEntry *entry;
852
entry = entry_from_tree_iter (view, iter);
854
if (entry == view->priv->playing_entry && view->priv->playing)
855
pixbuf = view->priv->playing_pixbuf;
856
else if (entry == view->priv->playing_entry)
857
pixbuf = view->priv->paused_pixbuf;
861
g_object_set (G_OBJECT (renderer), "pixbuf", pixbuf, NULL);
865
rb_entry_view_rating_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
866
GtkTreeModel *tree_model, GtkTreeIter *iter,
869
RhythmDBEntry *entry;
872
entry = entry_from_tree_iter (view, iter);
874
rhythmdb_read_lock (view->priv->db);
876
rating = rhythmdb_entry_get_double (view->priv->db, entry, RHYTHMDB_PROP_RATING);
878
rhythmdb_read_unlock (view->priv->db);
880
g_object_set (G_OBJECT (renderer), "rating", rating, NULL);
884
rb_entry_view_intstr_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
885
GtkTreeModel *tree_model, GtkTreeIter *iter,
886
struct RBEntryViewCellDataFuncData *data)
888
RhythmDBEntry *entry;
892
entry = entry_from_tree_iter (data->view, iter);
894
rhythmdb_read_lock (data->view->priv->db);
896
val = rhythmdb_entry_get_int (data->view->priv->db, entry, data->propid);
897
rhythmdb_read_unlock (data->view->priv->db);
900
str = g_strdup_printf ("%d", val);
904
g_object_set (G_OBJECT (renderer), "text", str, NULL);
909
rb_entry_view_play_count_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
910
GtkTreeModel *tree_model, GtkTreeIter *iter,
911
struct RBEntryViewCellDataFuncData *data)
913
RhythmDBEntry *entry;
917
entry = entry_from_tree_iter (data->view, iter);
919
rhythmdb_read_lock (data->view->priv->db);
921
i = rhythmdb_entry_get_int (data->view->priv->db, entry, data->propid);
925
str = g_strdup_printf ("%d", i);
927
rhythmdb_read_unlock (data->view->priv->db);
929
g_object_set (G_OBJECT (renderer), "text", str, NULL);
935
rb_entry_view_duration_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
936
GtkTreeModel *tree_model, GtkTreeIter *iter,
937
struct RBEntryViewCellDataFuncData *data)
939
RhythmDBEntry *entry;
942
int minutes, seconds;
944
entry = entry_from_tree_iter (data->view, iter);
946
rhythmdb_read_lock (data->view->priv->db);
948
duration = rhythmdb_entry_get_long (data->view->priv->db, entry,
951
rhythmdb_read_unlock (data->view->priv->db);
953
minutes = duration / 60;
954
seconds = duration % 60;
956
if (minutes == 0 && seconds == 0)
957
str = g_strdup (_("Unknown"));
959
str = g_strdup_printf (_("%d:%02d"), minutes, seconds);
961
g_object_set (G_OBJECT (renderer), "text", str, NULL);
967
rb_entry_view_quality_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
968
GtkTreeModel *tree_model, GtkTreeIter *iter,
969
struct RBEntryViewCellDataFuncData *data)
971
RhythmDBEntry *entry;
974
entry = entry_from_tree_iter (data->view, iter);
976
rhythmdb_read_lock (data->view->priv->db);
978
bitrate = rhythmdb_entry_get_int (data->view->priv->db, entry,
981
rhythmdb_read_unlock (data->view->priv->db);
984
g_object_set (G_OBJECT (renderer), "text", _("Unknown"), NULL);
985
} else if (bitrate <= 80) {
986
g_object_set (G_OBJECT (renderer), "text", _("Very Low"), NULL);
987
} else if (bitrate <= 112) {
988
g_object_set (G_OBJECT (renderer), "text", _("Low"), NULL);
989
} else if (bitrate <= 160) {
990
g_object_set (G_OBJECT (renderer), "text", _("Regular"), NULL);
991
} else if (bitrate <= 224) {
992
g_object_set (G_OBJECT (renderer), "text", _("High"), NULL);
993
} else if (bitrate <= 1410) {
994
g_object_set (G_OBJECT (renderer), "text", _("Very High"), NULL);
996
g_object_set (G_OBJECT (renderer), "text", _("Perfect"), NULL);
1002
rb_entry_view_string_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
1003
GtkTreeModel *tree_model, GtkTreeIter *iter,
1004
struct RBEntryViewCellDataFuncData *data)
1006
RhythmDBEntry *entry;
1009
entry = entry_from_tree_iter (data->view, iter);
1011
rhythmdb_read_lock (data->view->priv->db);
1013
str = rhythmdb_entry_get_string (data->view->priv->db, entry, data->propid);
1015
g_object_set (G_OBJECT (renderer), "text", str, NULL);
1017
rhythmdb_read_unlock (data->view->priv->db);
1021
rb_entry_view_sync_sorting (RBEntryView *view)
1023
GList *columns, *tem;
1024
GtkTreeViewColumn *column;
1029
if (!view->priv->sorting_key)
1032
sorttype = eel_gconf_get_string (view->priv->sorting_key);
1034
if (!sorttype || !*sorttype) {
1035
rb_debug ("no sorting data available in gconf!");
1039
if (!strchr (sorttype, ',')) {
1040
g_warning ("malformed sort data");
1044
strs = g_strsplit (sorttype, ",", 0);
1046
column = g_hash_table_lookup (view->priv->column_key_map, strs[0]);
1050
if (!strcmp ("ascending", strs[1]))
1051
direction = GTK_SORT_ASCENDING;
1052
else if (!strcmp ("descending", strs[1]))
1053
direction = GTK_SORT_DESCENDING;
1054
else if (!strcmp ("none", strs[1]))
1059
columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (view->priv->treeview));
1060
for (tem = columns; tem; tem = tem->next)
1061
gtk_tree_view_column_set_sort_indicator (tem->data, FALSE);
1062
g_list_free (columns);
1064
view->priv->sorting_column = column;
1065
view->priv->sorting_order = direction;
1067
if (view->priv->sorting_order != -1) {
1068
gtk_tree_view_column_set_sort_indicator (column, TRUE);
1069
gtk_tree_view_column_set_sort_order (column, view->priv->sorting_order);
1071
rb_debug ("emitting sort order changed");
1072
g_signal_emit (G_OBJECT (view), rb_entry_view_signals[SORT_ORDER_CHANGED], 0);
1079
rb_entry_view_get_sorting_type (RBEntryView *view)
1081
return eel_gconf_get_string (view->priv->sorting_key);
1085
rb_entry_view_set_resorting (RBEntryView *view)
1087
if (view->priv->resorting)
1088
g_warning ("Sort order changed while resorting");
1089
view->priv->resorting = TRUE;
1093
rb_entry_view_column_clicked_cb (GtkTreeViewColumn *column, RBEntryView *view)
1095
GString *key = g_string_new ("");
1099
rb_debug ("sorting on column %p", column);
1100
g_string_append (key, (char*) g_object_get_data (G_OBJECT (column), "rb-entry-view-key"));
1101
g_string_append_c (key, ',');
1103
is_sorted = gtk_tree_view_column_get_sort_indicator (column);
1106
sorting_order = gtk_tree_view_column_get_sort_order (column);
1107
if (sorting_order == GTK_SORT_ASCENDING)
1108
sorting_order = GTK_SORT_DESCENDING;
1112
sorting_order = GTK_SORT_ASCENDING;
1114
switch (sorting_order)
1117
g_string_append (key, "none");
1119
case GTK_SORT_ASCENDING:
1120
g_string_append (key, "ascending");
1122
case GTK_SORT_DESCENDING:
1123
g_string_append (key, "descending");
1126
g_assert_not_reached ();
1128
eel_gconf_set_string (view->priv->sorting_key, key->str);
1129
g_string_free (key, TRUE);
1133
rb_entry_view_append_column (RBEntryView *view, RBEntryViewColumn coltype)
1135
GtkTreeViewColumn *column;
1136
GtkCellRenderer *renderer;
1137
struct RBEntryViewCellDataFuncData *cell_data;
1138
struct RBEntryViewCellDataFuncData *sort_data;
1139
const char *title = NULL;
1140
const char *key = NULL;
1141
GtkTreeCellDataFunc cell_data_func = NULL;
1142
GCompareDataFunc sort_func = NULL;
1143
gpointer real_sort_data = NULL;
1144
RhythmDBPropType propid;
1146
column = GTK_TREE_VIEW_COLUMN (rb_tree_view_column_new ());
1148
if (coltype == RB_ENTRY_VIEW_COL_RATING) {
1151
propid = RHYTHMDB_PROP_RATING;
1153
sort_data = g_new0 (struct RBEntryViewCellDataFuncData, 1);
1154
sort_data->view = view;
1155
sort_data->propid = propid;
1156
sort_func = (GCompareDataFunc) rb_entry_view_double_ceiling_sort_func;
1158
renderer = rb_cell_renderer_rating_new ();
1159
gtk_tree_view_column_pack_start (column, renderer, TRUE);
1160
gtk_tree_view_column_set_cell_data_func (column, renderer,
1161
(GtkTreeCellDataFunc)
1162
rb_entry_view_rating_cell_data_func,
1165
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1166
gtk_tree_view_column_set_clickable (column, TRUE);
1167
gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, NULL);
1168
gtk_tree_view_column_set_fixed_width (column, width * 5 + 5);
1169
g_signal_connect_object (renderer,
1171
G_CALLBACK (rb_entry_view_rated_cb),
1174
real_sort_data = sort_data;
1175
title = _("_Rating");
1180
cell_data = g_new0 (struct RBEntryViewCellDataFuncData, 1);
1181
cell_data->view = view;
1182
sort_data = g_new0 (struct RBEntryViewCellDataFuncData, 1);
1183
sort_data->view = view;
1187
case RB_ENTRY_VIEW_COL_TRACK_NUMBER:
1188
propid = RHYTHMDB_PROP_TRACK_NUMBER;
1189
cell_data->propid = propid;
1190
cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_intstr_cell_data_func;
1191
sort_func = (GCompareDataFunc) rb_entry_view_track_sort_func;
1192
real_sort_data = view;
1193
title = _("Tra_ck");
1196
case RB_ENTRY_VIEW_COL_TITLE:
1197
propid = RHYTHMDB_PROP_TITLE;
1198
cell_data->propid = propid;
1199
cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1200
sort_data->propid = RHYTHMDB_PROP_TITLE_SORT_KEY;
1201
sort_func = (GCompareDataFunc) rb_entry_view_string_sort_func;
1202
title = _("_Title");
1204
rb_tree_view_column_set_expand (RB_TREE_VIEW_COLUMN (column), TRUE);
1206
case RB_ENTRY_VIEW_COL_ARTIST:
1207
propid = RHYTHMDB_PROP_ARTIST;
1208
cell_data->propid = propid;
1209
cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1210
sort_data->propid = RHYTHMDB_PROP_ARTIST_SORT_KEY;
1211
sort_func = (GCompareDataFunc) rb_entry_view_artist_sort_func;
1212
real_sort_data = view;
1213
title = _("Art_ist");
1215
rb_tree_view_column_set_expand (RB_TREE_VIEW_COLUMN (column), TRUE);
1217
case RB_ENTRY_VIEW_COL_ALBUM:
1218
propid = RHYTHMDB_PROP_ALBUM;
1219
cell_data->propid = propid;
1220
cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1221
sort_data->propid = RHYTHMDB_PROP_ALBUM_SORT_KEY;
1222
sort_func = (GCompareDataFunc) rb_entry_view_album_sort_func;
1223
real_sort_data = view;
1224
title = _("A_lbum");
1226
rb_tree_view_column_set_expand (RB_TREE_VIEW_COLUMN (column), TRUE);
1228
case RB_ENTRY_VIEW_COL_GENRE:
1229
propid = RHYTHMDB_PROP_GENRE;
1230
cell_data->propid = propid;
1231
cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1232
sort_data->propid = RHYTHMDB_PROP_GENRE_SORT_KEY;
1233
sort_func = (GCompareDataFunc) rb_entry_view_genre_sort_func;
1234
real_sort_data = view;
1235
title = _("Ge_nre");
1237
rb_tree_view_column_set_expand (RB_TREE_VIEW_COLUMN (column), TRUE);
1239
case RB_ENTRY_VIEW_COL_DURATION:
1240
propid = RHYTHMDB_PROP_DURATION;
1241
cell_data->propid = propid;
1242
cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_duration_cell_data_func;
1243
sort_data->propid = cell_data->propid;
1244
sort_func = (GCompareDataFunc) rb_entry_view_long_sort_func;
1248
case RB_ENTRY_VIEW_COL_QUALITY:
1249
propid = RHYTHMDB_PROP_BITRATE;
1250
cell_data->propid = propid;
1251
cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_quality_cell_data_func;
1252
sort_data->propid = cell_data->propid;
1253
sort_func = (GCompareDataFunc) rb_entry_view_int_sort_func;
1254
title = _("_Quality");
1257
/* RB_ENTRY_VIEW_COL_RATING at bottom */
1258
case RB_ENTRY_VIEW_COL_PLAY_COUNT:
1259
propid = RHYTHMDB_PROP_PLAY_COUNT;
1260
cell_data->propid = propid;
1261
cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_play_count_cell_data_func;
1262
sort_data->propid = cell_data->propid;
1263
sort_func = (GCompareDataFunc) rb_entry_view_int_sort_func;
1264
title = _("_Play Count");
1267
case RB_ENTRY_VIEW_COL_LAST_PLAYED:
1268
propid = RHYTHMDB_PROP_LAST_PLAYED;
1269
cell_data->propid = RHYTHMDB_PROP_LAST_PLAYED_STR;
1270
cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1271
sort_data->propid = RHYTHMDB_PROP_LAST_PLAYED;
1272
sort_func = (GCompareDataFunc) rb_entry_view_long_sort_func;
1273
title = _("L_ast Played");
1276
case RB_ENTRY_VIEW_COL_RATING:
1278
g_assert_not_reached ();
1283
renderer = gtk_cell_renderer_text_new ();
1284
gtk_tree_view_column_pack_start (column, renderer, TRUE);
1285
gtk_tree_view_column_set_cell_data_func (column, renderer,
1286
cell_data_func, cell_data, g_free);
1287
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1288
gtk_tree_view_column_set_clickable (column, TRUE);
1289
gtk_tree_view_column_set_resizable (column, TRUE);
1292
g_hash_table_insert (view->priv->propid_column_map, GINT_TO_POINTER (propid), column);
1293
rb_entry_view_append_column_custom (view, column, title, key, sort_func,
1294
real_sort_data ? real_sort_data : sort_data);
1298
rb_entry_view_append_column_custom (RBEntryView *view,
1299
GtkTreeViewColumn *column,
1302
GCompareDataFunc sort_func, gpointer user_data)
1304
struct RBEntryViewColumnSortData *sortdata;
1306
gtk_tree_view_column_set_title (column, title);
1307
gtk_tree_view_column_set_reorderable (column, FALSE);
1309
if (gtk_tree_view_column_get_clickable (column))
1310
view->priv->clickable_columns = g_list_append (view->priv->clickable_columns, column);
1312
g_signal_connect_object (G_OBJECT (column), "clicked",
1313
G_CALLBACK (rb_entry_view_column_clicked_cb),
1316
g_object_set_data_full (G_OBJECT (column), "rb-entry-view-key",
1317
g_strdup (key), g_free);
1319
rb_debug ("appending column: %p (%s)", column, title);
1321
gtk_tree_view_append_column (GTK_TREE_VIEW (view->priv->treeview), column);
1323
sortdata = g_new (struct RBEntryViewColumnSortData, 1);
1324
sortdata->func = (GCompareDataFunc) sort_func;
1325
sortdata->data = user_data;
1326
g_hash_table_insert (view->priv->column_sort_data_map, column, sortdata);
1327
g_hash_table_insert (view->priv->column_key_map, g_strdup (key), column);
1329
rb_entry_view_sync_columns_visible (view);
1330
rb_entry_view_sync_sorting (view);
1334
rb_entry_view_set_columns_clickable (RBEntryView *view, gboolean clickable)
1336
GList *columns, *tem;
1338
columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (view->priv->treeview));
1339
for (tem = columns; tem; tem = tem->next)
1340
gtk_tree_view_column_set_clickable (tem->data, clickable);
1341
g_list_free (columns);
1345
rb_entry_view_constructor (GType type, guint n_construct_properties,
1346
GObjectConstructParam *construct_properties)
1349
RBEntryViewClass *klass;
1350
GObjectClass *parent_class;
1351
klass = RB_ENTRY_VIEW_CLASS (g_type_class_peek (RB_TYPE_ENTRY_VIEW));
1353
parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
1354
view = RB_ENTRY_VIEW (parent_class->constructor (type, n_construct_properties,
1355
construct_properties));
1357
view->priv->treeview = GTK_WIDGET (rb_tree_view_new ());
1359
g_signal_connect_object (G_OBJECT (view->priv->treeview),
1360
"button_press_event",
1361
G_CALLBACK (rb_entry_view_button_press_cb),
1364
g_signal_connect_object (G_OBJECT (view->priv->treeview),
1366
G_CALLBACK (rb_entry_view_row_activated_cb),
1369
view->priv->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view->priv->treeview));
1370
g_signal_connect_object (G_OBJECT (view->priv->selection),
1372
G_CALLBACK (rb_entry_view_selection_changed_cb),
1376
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view->priv->treeview), TRUE);
1377
gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view->priv->treeview), TRUE);
1378
gtk_tree_selection_set_mode (view->priv->selection, GTK_SELECTION_MULTIPLE);
1380
if (view->priv->is_drag_source)
1381
rb_tree_dnd_add_drag_source_support (GTK_TREE_VIEW (view->priv->treeview),
1383
rb_entry_view_drag_types,
1384
G_N_ELEMENTS (rb_entry_view_drag_types),
1385
GDK_ACTION_COPY | GDK_ACTION_MOVE);
1386
if (view->priv->is_drag_dest)
1387
rb_tree_dnd_add_drag_dest_support (GTK_TREE_VIEW (view->priv->treeview),
1388
RB_TREE_DEST_CAN_DROP_BETWEEN | RB_TREE_DEST_EMPTY_VIEW_DROP,
1389
rb_entry_view_drag_types,
1390
G_N_ELEMENTS (rb_entry_view_drag_types),
1391
GDK_ACTION_COPY | GDK_ACTION_MOVE);
1393
gtk_container_add (GTK_CONTAINER (view), view->priv->treeview);
1396
GtkTreeViewColumn *column;
1397
GtkTooltips *tooltip;
1398
GtkCellRenderer *renderer;
1401
tooltip = gtk_tooltips_new ();
1403
/* Playing icon column */
1404
column = GTK_TREE_VIEW_COLUMN (rb_tree_view_column_new ());
1405
renderer = rb_cell_renderer_pixbuf_new ();
1406
gtk_tree_view_column_pack_start (column, renderer, TRUE);
1407
gtk_tree_view_column_set_cell_data_func (column, renderer,
1408
(GtkTreeCellDataFunc)
1409
rb_entry_view_playing_cell_data_func,
1412
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1413
gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, NULL);
1414
gtk_tree_view_column_set_fixed_width (column, width + 5);
1415
gtk_tree_view_append_column (GTK_TREE_VIEW (view->priv->treeview), column);
1417
gtk_tooltips_set_tip (GTK_TOOLTIPS (tooltip), GTK_WIDGET (column->button),
1418
_("Now Playing"), NULL);
1421
view->priv->gconf_notification_id =
1422
eel_gconf_notification_add (CONF_UI_COLUMNS_SETUP,
1423
rb_entry_view_columns_config_changed_cb,
1425
if (view->priv->sorting_key)
1426
view->priv->sorting_gconf_notification_id =
1427
eel_gconf_notification_add (view->priv->sorting_key,
1428
rb_entry_view_sort_key_changed_cb,
1432
RhythmDBQueryModel *query_model;
1433
query_model = rhythmdb_query_model_new_empty (view->priv->db);
1434
rb_entry_view_set_model (view, RHYTHMDB_QUERY_MODEL (query_model));
1435
g_object_unref (G_OBJECT (query_model));
1438
return G_OBJECT (view);
1441
/* static gboolean */
1442
/* rb_entry_view_dummy_drag_drop_cb (GtkWidget *widget, */
1443
/* GdkDragContext *drag_context, */
1444
/* int x, int y, guint time, */
1445
/* gpointer user_data) */
1447
/* g_signal_stop_emission_by_name (widget, "drag_drop"); */
1455
rb_entry_view_rated_cb (RBCellRendererRating *cellrating,
1456
const char *path_string,
1461
RhythmDBEntry *entry;
1462
GValue value = { 0, };
1464
g_return_if_fail (rating >= 0 && rating <= 5 );
1465
g_return_if_fail (path_string != NULL);
1467
path = gtk_tree_path_new_from_string (path_string);
1468
entry = entry_from_tree_path (view, path);
1469
gtk_tree_path_free (path);
1471
g_value_init (&value, G_TYPE_DOUBLE);
1472
g_value_set_double (&value, rating);
1473
rhythmdb_entry_queue_set (view->priv->db, entry, RHYTHMDB_PROP_RATING,
1475
g_value_unset (&value);
1477
/* since the user changed the rating, stop auto-rating */
1478
g_value_init (&value, G_TYPE_BOOLEAN);
1479
g_value_set_boolean (&value, FALSE);
1480
rhythmdb_entry_queue_set (view->priv->db, entry, RHYTHMDB_PROP_AUTO_RATE,
1482
g_value_unset (&value);
1486
rb_entry_view_set_playing_entry (RBEntryView *view,
1487
RhythmDBEntry *entry)
1489
g_return_if_fail (RB_IS_ENTRY_VIEW (view));
1491
g_object_set (G_OBJECT (view), "playing-entry", entry, NULL);
1495
rb_entry_view_get_playing_entry (RBEntryView *view)
1497
g_return_val_if_fail (RB_IS_ENTRY_VIEW (view), NULL);
1499
return view->priv->playing_entry;
1503
rb_entry_view_get_first_entry (RBEntryView *view)
1507
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (view->priv->model), &iter))
1508
return entry_from_tree_iter (view, &iter);
1514
rb_entry_view_get_next_from_entry (RBEntryView *view, RhythmDBEntry *entry)
1518
g_return_val_if_fail (entry != NULL, NULL);
1520
if (!rhythmdb_query_model_entry_to_iter (view->priv->model,
1522
/* If the entry isn't in the entryview, the "next" entry is the first. */
1523
return rb_entry_view_get_first_entry (view);
1526
if (gtk_tree_model_iter_next (GTK_TREE_MODEL (view->priv->model),
1528
return entry_from_tree_iter (view, &iter);
1534
rb_entry_view_get_previous_from_entry (RBEntryView *view, RhythmDBEntry *entry)
1540
g_return_val_if_fail (entry != NULL, NULL);
1542
if (!rhythmdb_query_model_entry_to_iter (view->priv->model,
1546
path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->priv->model), &iter);
1548
if (!gtk_tree_path_prev (path)) {
1549
gtk_tree_path_free (path);
1553
g_assert (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->priv->model), &iter, path));
1554
gtk_tree_path_free (path);
1555
ret = entry_from_tree_iter (view, &iter);
1561
rb_entry_view_get_next_entry (RBEntryView *view)
1563
if (view->priv->playing_entry == NULL)
1566
return rb_entry_view_get_next_from_entry (view,
1567
view->priv->playing_entry);
1571
rb_entry_view_get_previous_entry (RBEntryView *view)
1573
if (view->priv->playing_entry == NULL)
1576
return rb_entry_view_get_previous_from_entry (view,
1577
view->priv->playing_entry);
1581
harvest_entries (GtkTreeModel *model,
1586
GList **list = (GList **) data;
1587
RhythmDBEntry *entry;
1589
gtk_tree_model_get (model, iter, 0, &entry, -1);
1591
*list = g_list_append (*list, entry);
1597
rb_entry_view_get_selected_entries (RBEntryView *view)
1601
gtk_tree_selection_selected_foreach (view->priv->selection,
1602
(GtkTreeSelectionForeachFunc) harvest_entries,
1609
rb_entry_view_get_random_entry (RBEntryView *view)
1616
n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (view->priv->model), NULL);
1619
else if ((n_rows - 1) > 0)
1620
index = g_random_int_range (0, n_rows);
1624
path_str = g_strdup_printf ("%d", index);
1625
path = gtk_tree_path_new_from_string (path_str);
1628
gtk_tree_model_get_iter (GTK_TREE_MODEL (view->priv->model),
1631
gtk_tree_path_free (path);
1633
return entry_from_tree_iter (view, &iter);
1637
rb_entry_view_button_press_cb (GtkTreeView *treeview,
1638
GdkEventButton *event,
1641
if (event->button == 3) {
1643
RhythmDBEntry *entry;
1645
gtk_tree_view_get_path_at_pos (treeview, event->x, event->y, &path, NULL, NULL, NULL);
1648
entry = entry_from_tree_path (view, path);
1650
selected = rb_entry_view_get_selected_entries (view);
1652
if (!g_list_find (selected, entry))
1653
rb_entry_view_select_entry (view, entry);
1655
g_list_free (selected);
1657
g_signal_emit (G_OBJECT (view), rb_entry_view_signals[SHOW_POPUP], 0);
1659
return view->priv->have_selection;
1666
queue_changed_sig (RBEntryView *view)
1668
if (!view->priv->change_sig_queued) {
1669
rb_debug ("queueing changed signal");
1670
view->priv->change_sig_id = g_timeout_add (250, (GSourceFunc) emit_entry_changed, view);
1672
view->priv->change_sig_queued = TRUE;
1676
rb_entry_view_selection_changed_cb (GtkTreeSelection *selection,
1680
RhythmDBEntry *selected_entry = NULL;
1683
if (view->priv->selection_lock == TRUE)
1686
sel = rb_entry_view_get_selected_entries (view);
1687
available = (sel != NULL);
1689
selected_entry = (g_list_first (sel))->data;
1691
if (available != view->priv->have_selection) {
1692
queue_changed_sig (view);
1693
view->priv->have_selection = available;
1695
g_signal_emit (G_OBJECT (view), rb_entry_view_signals[HAVE_SEL_CHANGED], 0, available);
1698
if (selected_entry != NULL && selected_entry != view->priv->selected_entry)
1699
g_signal_emit (G_OBJECT (view), rb_entry_view_signals[ENTRY_SELECTED], 0, selected_entry);
1701
view->priv->selected_entry = selected_entry;
1707
rb_entry_view_have_selection (RBEntryView *view)
1709
return view->priv->have_selection;
1713
rb_entry_view_row_activated_cb (GtkTreeView *treeview,
1715
GtkTreeViewColumn *column,
1718
RhythmDBEntry *entry;
1720
rb_debug ("row activated");
1722
entry = entry_from_tree_path (view, path);
1724
rb_debug ("emitting entry activated");
1725
g_signal_emit (G_OBJECT (view), rb_entry_view_signals[ENTRY_ACTIVATED], 0, entry);
1729
rb_entry_view_row_inserted_cb (GtkTreeModel *model,
1734
RhythmDBEntry *entry = entry_from_tree_path (view, path);
1736
rb_debug ("row added");
1737
g_signal_emit (G_OBJECT (view), rb_entry_view_signals[ENTRY_ADDED], 0, entry);
1738
queue_changed_sig (view);
1742
rb_entry_view_row_deleted_cb (GtkTreeModel *model,
1746
RhythmDBEntry *entry = entry_from_tree_path (view, path);
1748
if (entry == view->priv->playing_entry) {
1749
view->priv->playing_entry = NULL;
1751
rb_debug ("emitting playing entry destroyed");
1753
g_signal_emit (G_OBJECT (view), rb_entry_view_signals[PLAYING_ENTRY_DELETED],
1754
0, view->priv->playing_entry);
1757
rb_debug ("row deleted");
1758
g_signal_emit (G_OBJECT (view), rb_entry_view_signals[ENTRY_DELETED], 0, entry);
1759
queue_changed_sig (view);
1763
rb_entry_view_row_changed_cb (GtkTreeModel *model,
1768
rb_debug ("row changed");
1769
queue_changed_sig (view);
1773
rb_entry_view_get_num_entries (RBEntryView *view)
1775
return gtk_tree_model_iter_n_children (GTK_TREE_MODEL (view->priv->model),
1780
rb_entry_view_select_all (RBEntryView *view)
1782
gtk_tree_selection_select_all (view->priv->selection);
1786
rb_entry_view_select_none (RBEntryView *view)
1788
view->priv->selection_lock = TRUE;
1790
gtk_tree_selection_unselect_all (view->priv->selection);
1792
view->priv->selected_entry = NULL;
1794
view->priv->selection_lock = FALSE;
1798
rb_entry_view_select_entry (RBEntryView *view,
1799
RhythmDBEntry *entry)
1806
view->priv->selection_lock = TRUE;
1808
rb_entry_view_select_none (view);
1810
if (rhythmdb_query_model_entry_to_iter (view->priv->model,
1812
gtk_tree_selection_select_iter (view->priv->selection, &iter);
1815
view->priv->selection_lock = FALSE;
1819
rb_entry_view_scroll_to_entry (RBEntryView *view,
1820
RhythmDBEntry *entry)
1824
if (rhythmdb_query_model_entry_to_iter (view->priv->model,
1826
rb_entry_view_scroll_to_iter (view, &iter);
1831
rb_entry_view_scroll_to_iter (RBEntryView *view,
1836
path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->priv->model), iter);
1837
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (view->priv->treeview), path,
1838
gtk_tree_view_get_column (GTK_TREE_VIEW (view->priv->treeview), 0),
1840
gtk_tree_view_set_cursor (GTK_TREE_VIEW (view->priv->treeview), path,
1841
gtk_tree_view_get_column (GTK_TREE_VIEW (view->priv->treeview), 0), FALSE);
1843
gtk_tree_path_free (path);
1847
rb_entry_view_get_entry_visible (RBEntryView *view,
1848
RhythmDBEntry *entry)
1851
gboolean realized, visible;
1853
if (view->priv->playing_model != view->priv->model)
1856
rb_entry_view_entry_is_visible (view, entry, &realized, &visible,
1858
return realized && visible;
1862
rb_entry_view_get_entry_contained (RBEntryView *view,
1863
RhythmDBEntry *entry)
1867
return rhythmdb_query_model_entry_to_iter (view->priv->model,
1872
rb_entry_view_entry_is_visible (RBEntryView *view,
1873
RhythmDBEntry *entry,
1884
g_return_if_fail (entry != NULL);
1886
if (!GTK_WIDGET_REALIZED (view))
1891
if (!rhythmdb_query_model_entry_to_iter (view->priv->model,
1895
path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->priv->model), iter);
1896
gtk_tree_view_get_cell_area (GTK_TREE_VIEW (view->priv->treeview),
1898
gtk_tree_view_get_column (GTK_TREE_VIEW (view->priv->treeview), 0),
1901
gtk_tree_path_free (path);
1903
*visible = (rect.y != 0 && rect.height != 0);
1907
emit_entry_changed (RBEntryView *view)
1909
GDK_THREADS_ENTER ();
1911
g_signal_emit (G_OBJECT (view), rb_entry_view_signals[CHANGED], 0);
1913
view->priv->change_sig_queued = FALSE;
1915
GDK_THREADS_LEAVE ();
1921
/* playing_entry_deleted_cb (RhythmDB *db, RBEntryView *view) */
1923
/* rb_debug ("emitting playing entry destroyed"); */
1924
/* g_signal_emit (G_OBJECT (view), rb_entry_view_signals[PLAYING_ENTRY_DELETED], */
1925
/* 0, view->priv->playing_entry); */
1929
rb_entry_view_enable_drag_source (RBEntryView *view,
1930
const GtkTargetEntry *targets,
1933
g_return_if_fail (view != NULL);
1935
rb_tree_dnd_add_drag_source_support (GTK_TREE_VIEW (view->priv->treeview),
1936
GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
1937
targets, n_targets, GDK_ACTION_COPY);
1941
rb_entry_view_sort_key_changed_cb (GConfClient* client,
1946
RBEntryView *view = user_data;
1948
g_return_if_fail (RB_IS_ENTRY_VIEW (view));
1950
rb_entry_view_sync_sorting (view);
1954
rb_entry_view_columns_config_changed_cb (GConfClient* client,
1959
RBEntryView *view = user_data;
1961
g_return_if_fail (RB_IS_ENTRY_VIEW (view));
1963
rb_entry_view_sync_columns_visible (view);
1967
propid_from_name (const char *name)
1969
GEnumClass *prop_class = g_type_class_ref (RHYTHMDB_TYPE_PROP);
1970
GEnumClass *unsaved_prop_class = g_type_class_ref (RHYTHMDB_TYPE_UNSAVED_PROP);
1974
ev = g_enum_get_value_by_name (prop_class, name);
1976
ev = g_enum_get_value_by_name (unsaved_prop_class, name);
1985
set_column_not_visible (guint propid, GtkTreeViewColumn *column, gpointer unused)
1987
/* title is always visible */
1988
if (propid == RHYTHMDB_PROP_TITLE)
1991
gtk_tree_view_column_set_visible (column, FALSE);
1995
rb_entry_view_sync_columns_visible (RBEntryView *view)
1998
GList *visible_properties = NULL, *tem;
1999
char *config = eel_gconf_get_string (CONF_UI_COLUMNS_SETUP);
2001
g_return_if_fail (view != NULL);
2002
g_return_if_fail (config != NULL);
2004
g_hash_table_foreach (view->priv->propid_column_map, (GHFunc) set_column_not_visible, NULL);
2006
items = g_strsplit (config, ",", 0);
2007
if (items != NULL) {
2009
for (i = 0; items[i] != NULL && *(items[i]); i++) {
2010
int value = propid_from_name (items[i]);
2012
if ((value >= 0) && (value < RHYTHMDB_NUM_PROPERTIES))
2013
visible_properties = g_list_append (visible_properties, GINT_TO_POINTER (value));
2018
for (tem = visible_properties; tem; tem = tem->next) {
2019
GtkTreeViewColumn *column
2020
= g_hash_table_lookup (view->priv->propid_column_map, tem->data);
2022
gtk_tree_view_column_set_visible (column, TRUE);
2025
g_list_free (visible_properties);
2030
rb_entry_view_set_playing (RBEntryView *view,
2033
g_return_if_fail (RB_IS_ENTRY_VIEW (view));
2035
view->priv->playing = TRUE;
2039
rb_entry_view_poll_model (RBEntryView *view)
2043
g_get_current_time (&timeout);
2044
g_time_val_add (&timeout, G_USEC_PER_SEC*0.75);
2046
return rhythmdb_query_model_poll (view->priv->model, &timeout);