~ubuntu-branches/ubuntu/quantal/rhythmbox/quantal

« back to all changes in this revision

Viewing changes to widgets/rb-entry-view.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2005-03-25 23:58:54 UTC
  • Revision ID: james.westby@ubuntu.com-20050325235854-2212vevw1u4074w8
Tags: upstream-0.8.8
ImportĀ upstreamĀ versionĀ 0.8.8

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  arch-tag: Implementation of widget to display RhythmDB entries
 
3
 *
 
4
 *  Copyright (C) 2003 Colin Walters <walters@verbum.org>
 
5
 *
 
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.
 
10
 *
 
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.
 
15
 *
 
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.
 
19
 *
 
20
 */
 
21
 
 
22
#include <gtk/gtktreeview.h>
 
23
 
 
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>
 
29
#include <config.h>
 
30
#include <libgnome/gnome-i18n.h>
 
31
#include <libgnomevfs/gnome-vfs-utils.h>
 
32
#include <string.h>
 
33
#include <stdlib.h>
 
34
#include <math.h>
 
35
 
 
36
#include "rb-tree-dnd.h"
 
37
#include "rb-tree-view-column.h"
 
38
#include "rb-entry-view.h"
 
39
#include "rb-dialog.h"
 
40
#include "rb-debug.h"
 
41
#include "rb-util.h"
 
42
#include "rhythmdb.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"
 
51
 
 
52
static const GtkTargetEntry rb_entry_view_drag_types[] = {{  "text/uri-list", 0, 0 }};
 
53
 
 
54
struct RBEntryViewColumnSortData
 
55
{
 
56
        GCompareDataFunc func;
 
57
        gpointer data;
 
58
};
 
59
 
 
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,
 
66
                                       guint prop_id,
 
67
                                       const GValue *value,
 
68
                                       GParamSpec *pspec);
 
69
static void rb_entry_view_get_property (GObject *object,
 
70
                                       guint prop_id,
 
71
                                       GValue *value,
 
72
                                       GParamSpec *pspec);
 
73
static void rb_entry_view_selection_changed_cb (GtkTreeSelection *selection,
 
74
                                               RBEntryView *view);
 
75
static void rb_entry_view_row_activated_cb (GtkTreeView *treeview,
 
76
                                           GtkTreePath *path,
 
77
                                           GtkTreeViewColumn *column,
 
78
                                           RBEntryView *view);
 
79
static void rb_entry_view_row_inserted_cb (GtkTreeModel *model,
 
80
                                           GtkTreePath *path,
 
81
                                           GtkTreeIter *iter,
 
82
                                           RBEntryView *view);
 
83
static void rb_entry_view_row_deleted_cb (GtkTreeModel *model,
 
84
                                          GtkTreePath *path,
 
85
                                          RBEntryView *view);
 
86
static void rb_entry_view_row_changed_cb (GtkTreeModel *model,
 
87
                                          GtkTreePath *path,
 
88
                                          GtkTreeIter *iter,
 
89
                                          RBEntryView *view);
 
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,
 
94
                                                    guint cnxn_id,
 
95
                                                    GConfEntry *entry,
 
96
                                                    gpointer user_data);
 
97
static void rb_entry_view_sort_key_changed_cb (GConfClient* client,
 
98
                                               guint cnxn_id,
 
99
                                               GConfEntry *entry,
 
100
                                               gpointer user_data);
 
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,
 
106
                                   const char *path,
 
107
                                   double rating,
 
108
                                   RBEntryView *view);
 
109
static gboolean rb_entry_view_button_press_cb (GtkTreeView *treeview,
 
110
                                              GdkEventButton *event,
 
111
                                              RBEntryView *view);
 
112
static void rb_entry_view_entry_is_visible (RBEntryView *view, RhythmDBEntry *entry,
 
113
                                            gboolean *realized, gboolean *visible,
 
114
                                            GtkTreeIter *iter);
 
115
static void rb_entry_view_scroll_to_iter (RBEntryView *view,
 
116
                                          GtkTreeIter *iter);
 
117
 
 
118
struct RBEntryViewReverseSortingData
 
119
{
 
120
        GCompareDataFunc func;
 
121
        gpointer data;
 
122
};
 
123
 
 
124
static gint reverse_sorting_func (gpointer a, gpointer b, struct RBEntryViewReverseSortingData *data);
 
125
 
 
126
struct RBEntryViewPrivate
 
127
{
 
128
        RhythmDB *db;
 
129
        
 
130
        RhythmDBQueryModel *model;
 
131
 
 
132
        GtkWidget *treeview;
 
133
        GtkTreeSelection *selection;
 
134
 
 
135
        gboolean playing;
 
136
        RhythmDBQueryModel *playing_model;
 
137
        RhythmDBEntry *playing_entry;
 
138
        gboolean playing_entry_in_view;
 
139
        GtkTreeIter playing_entry_iter;
 
140
 
 
141
        gboolean is_drag_source;
 
142
        gboolean is_drag_dest;
 
143
 
 
144
        GdkPixbuf *playing_pixbuf;
 
145
        GdkPixbuf *paused_pixbuf;
 
146
        
 
147
        char *sorting_key;
 
148
        guint sorting_gconf_notification_id;
 
149
        GList *clickable_columns;
 
150
        GtkTreeViewColumn *sorting_column;
 
151
        gint sorting_order;
 
152
        struct RBEntryViewReverseSortingData *reverse_sorting_data;
 
153
        gboolean resorting;
 
154
 
 
155
        gboolean have_selection;
 
156
 
 
157
        gboolean keep_selection;
 
158
 
 
159
        RhythmDBEntry *selected_entry;
 
160
 
 
161
        gboolean change_sig_queued;
 
162
        guint change_sig_id;
 
163
 
 
164
        gboolean selection_lock;
 
165
 
 
166
        GHashTable *column_key_map;
 
167
 
 
168
        guint gconf_notification_id;
 
169
        GHashTable *propid_column_map;
 
170
        GHashTable *column_sort_data_map;
 
171
 
 
172
        gboolean idle;
 
173
};
 
174
 
 
175
enum
 
176
{
 
177
        ENTRY_ADDED,
 
178
        ENTRY_DELETED,
 
179
        ENTRIES_REPLACED,
 
180
        ENTRY_SELECTED,
 
181
        ENTRY_ACTIVATED,
 
182
        CHANGED,
 
183
        SHOW_POPUP,
 
184
        PLAYING_ENTRY_DELETED,
 
185
        HAVE_SEL_CHANGED,
 
186
        SORT_ORDER_CHANGED,
 
187
        LAST_SIGNAL
 
188
};
 
189
 
 
190
enum
 
191
{
 
192
        PROP_0,
 
193
        PROP_DB,
 
194
        PROP_MODEL,
 
195
        PROP_PLAYING_ENTRY,
 
196
        PROP_SORTING_KEY,
 
197
        PROP_IS_DRAG_SOURCE,
 
198
        PROP_IS_DRAG_DEST,
 
199
};
 
200
 
 
201
static GObjectClass *parent_class = NULL;
 
202
 
 
203
static guint rb_entry_view_signals[LAST_SIGNAL] = { 0 };
 
204
 
 
205
GType
 
206
rb_entry_view_get_type (void)
 
207
{
 
208
        static GType rb_entry_view_type = 0;
 
209
 
 
210
        if (rb_entry_view_type == 0)
 
211
        {
 
212
                static const GTypeInfo our_info =
 
213
                {
 
214
                        sizeof (RBEntryViewClass),
 
215
                        NULL,
 
216
                        NULL,
 
217
                        (GClassInitFunc) rb_entry_view_class_init,
 
218
                        NULL,
 
219
                        NULL,
 
220
                        sizeof (RBEntryView),
 
221
                        0,
 
222
                        (GInstanceInitFunc) rb_entry_view_init
 
223
                };
 
224
 
 
225
                rb_entry_view_type = g_type_register_static (GTK_TYPE_SCROLLED_WINDOW,
 
226
                                                            "RBEntryView",
 
227
                                                            &our_info, 0);
 
228
        }
 
229
 
 
230
        return rb_entry_view_type;
 
231
}
 
232
 
 
233
static void
 
234
rb_entry_view_class_init (RBEntryViewClass *klass)
 
235
{
 
236
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
237
 
 
238
        parent_class = g_type_class_peek_parent (klass);
 
239
 
 
240
        object_class->finalize = rb_entry_view_finalize;
 
241
        object_class->constructor = rb_entry_view_constructor;
 
242
 
 
243
        object_class->set_property = rb_entry_view_set_property;
 
244
        object_class->get_property = rb_entry_view_get_property;
 
245
 
 
246
        g_object_class_install_property (object_class,
 
247
                                         PROP_DB,
 
248
                                         g_param_spec_object ("db",
 
249
                                                              "RhythmDB",
 
250
                                                              "RhythmDB database",
 
251
                                                              RHYTHMDB_TYPE,
 
252
                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
253
        g_object_class_install_property (object_class,
 
254
                                         PROP_MODEL,
 
255
                                         g_param_spec_object ("model",
 
256
                                                              "RhythmDBQueryModel",
 
257
                                                              "RhythmDBQueryModel",
 
258
                                                              RHYTHMDB_TYPE_QUERY_MODEL,
 
259
                                                              G_PARAM_READWRITE));
 
260
 
 
261
        g_object_class_install_property (object_class,
 
262
                                         PROP_PLAYING_ENTRY,
 
263
                                         g_param_spec_pointer ("playing-entry",
 
264
                                                               "Playing entry",
 
265
                                                               "Playing entry",
 
266
                                                               G_PARAM_READWRITE));
 
267
        g_object_class_install_property (object_class,
 
268
                                         PROP_SORTING_KEY,
 
269
                                         g_param_spec_string ("sort-key",
 
270
                                                              "sorting key",
 
271
                                                              "sorting key",
 
272
                                                              "",
 
273
                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
274
        g_object_class_install_property (object_class,
 
275
                                         PROP_IS_DRAG_SOURCE,
 
276
                                         g_param_spec_boolean ("is-drag-source",
 
277
                                                               "is drag source",
 
278
                                                               "whether or not this is a drag source",
 
279
                                                               FALSE,
 
280
                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
281
        g_object_class_install_property (object_class,
 
282
                                         PROP_IS_DRAG_DEST,
 
283
                                         g_param_spec_boolean ("is-drag-dest",
 
284
                                                               "is drag dest",
 
285
                                                               "whether or not this is a drag dest",
 
286
                                                               FALSE,
 
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),
 
291
                              G_SIGNAL_RUN_LAST,
 
292
                              G_STRUCT_OFFSET (RBEntryViewClass, entry_added),
 
293
                              NULL, NULL,
 
294
                              g_cclosure_marshal_VOID__POINTER,
 
295
                              G_TYPE_NONE,
 
296
                              1,
 
297
                              G_TYPE_POINTER);
 
298
        rb_entry_view_signals[ENTRY_DELETED] =
 
299
                g_signal_new ("entry-deleted",
 
300
                              G_OBJECT_CLASS_TYPE (object_class),
 
301
                              G_SIGNAL_RUN_LAST,
 
302
                              G_STRUCT_OFFSET (RBEntryViewClass, entry_deleted),
 
303
                              NULL, NULL,
 
304
                              g_cclosure_marshal_VOID__POINTER,
 
305
                              G_TYPE_NONE,
 
306
                              1,
 
307
                              G_TYPE_POINTER);
 
308
        rb_entry_view_signals[ENTRIES_REPLACED] =
 
309
                g_signal_new ("entries-replaced",
 
310
                              G_OBJECT_CLASS_TYPE (object_class),
 
311
                              G_SIGNAL_RUN_LAST,
 
312
                              G_STRUCT_OFFSET (RBEntryViewClass, entries_replaced),
 
313
                              NULL, NULL,
 
314
                              g_cclosure_marshal_VOID__VOID,
 
315
                              G_TYPE_NONE,
 
316
                              0);
 
317
        rb_entry_view_signals[ENTRY_ACTIVATED] =
 
318
                g_signal_new ("entry-activated",
 
319
                              G_OBJECT_CLASS_TYPE (object_class),
 
320
                              G_SIGNAL_RUN_LAST,
 
321
                              G_STRUCT_OFFSET (RBEntryViewClass, entry_activated),
 
322
                              NULL, NULL,
 
323
                              g_cclosure_marshal_VOID__POINTER,
 
324
                              G_TYPE_NONE,
 
325
                              1,
 
326
                              G_TYPE_POINTER);
 
327
        rb_entry_view_signals[ENTRY_SELECTED] =
 
328
                g_signal_new ("entry-selected",
 
329
                              G_OBJECT_CLASS_TYPE (object_class),
 
330
                              G_SIGNAL_RUN_LAST,
 
331
                              G_STRUCT_OFFSET (RBEntryViewClass, entry_selected),
 
332
                              NULL, NULL,
 
333
                              g_cclosure_marshal_VOID__POINTER,
 
334
                              G_TYPE_NONE,
 
335
                              1,
 
336
                              G_TYPE_POINTER);
 
337
        rb_entry_view_signals[CHANGED] =
 
338
                g_signal_new ("changed",
 
339
                              G_OBJECT_CLASS_TYPE (object_class),
 
340
                              G_SIGNAL_RUN_LAST,
 
341
                              G_STRUCT_OFFSET (RBEntryViewClass, changed),
 
342
                              NULL, NULL,
 
343
                              g_cclosure_marshal_VOID__VOID,
 
344
                              G_TYPE_NONE,
 
345
                              0);
 
346
        rb_entry_view_signals[SHOW_POPUP] =
 
347
                g_signal_new ("show_popup",
 
348
                              G_OBJECT_CLASS_TYPE (object_class),
 
349
                              G_SIGNAL_RUN_LAST,
 
350
                              G_STRUCT_OFFSET (RBEntryViewClass, show_popup),
 
351
                              NULL, NULL,
 
352
                              g_cclosure_marshal_VOID__VOID,
 
353
                              G_TYPE_NONE,
 
354
                              0);
 
355
        rb_entry_view_signals[PLAYING_ENTRY_DELETED] =
 
356
                g_signal_new ("playing_entry_deleted",
 
357
                              G_OBJECT_CLASS_TYPE (object_class),
 
358
                              G_SIGNAL_RUN_LAST,
 
359
                              G_STRUCT_OFFSET (RBEntryViewClass, playing_entry_removed),
 
360
                              NULL, NULL,
 
361
                              g_cclosure_marshal_VOID__POINTER,
 
362
                              G_TYPE_NONE,
 
363
                              1, G_TYPE_POINTER);
 
364
        rb_entry_view_signals[HAVE_SEL_CHANGED] =
 
365
                g_signal_new ("have_selection_changed",
 
366
                              G_OBJECT_CLASS_TYPE (object_class),
 
367
                              G_SIGNAL_RUN_LAST,
 
368
                              G_STRUCT_OFFSET (RBEntryViewClass, have_selection_changed),
 
369
                              NULL, NULL,
 
370
                              g_cclosure_marshal_VOID__BOOLEAN,
 
371
                              G_TYPE_NONE,
 
372
                              1,
 
373
                              G_TYPE_BOOLEAN);
 
374
        rb_entry_view_signals[SORT_ORDER_CHANGED] =
 
375
                g_signal_new ("sort-order-changed",
 
376
                              G_OBJECT_CLASS_TYPE (object_class),
 
377
                              G_SIGNAL_RUN_LAST,
 
378
                              G_STRUCT_OFFSET (RBEntryViewClass, sort_order_changed),
 
379
                              NULL, NULL,
 
380
                              g_cclosure_marshal_VOID__VOID,
 
381
                              G_TYPE_NONE,
 
382
                              0);
 
383
}
 
384
 
 
385
static void
 
386
rb_entry_view_init (RBEntryView *view)
 
387
{
 
388
        view->priv = g_new0 (RBEntryViewPrivate, 1);
 
389
 
 
390
        view->priv->playing_pixbuf = rb_pixbuf_new_from_stock (RB_STOCK_PLAYING,
 
391
                                                               GTK_ICON_SIZE_MENU);
 
392
 
 
393
        view->priv->paused_pixbuf = rb_pixbuf_new_from_stock (RB_STOCK_PAUSED,
 
394
                                                              GTK_ICON_SIZE_MENU);
 
395
 
 
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);
 
399
}
 
400
 
 
401
static void
 
402
rb_entry_view_finalize (GObject *object)
 
403
{
 
404
        RBEntryView *view;
 
405
 
 
406
        g_return_if_fail (object != NULL);
 
407
        g_return_if_fail (RB_IS_ENTRY_VIEW (object));
 
408
 
 
409
        view = RB_ENTRY_VIEW (object);
 
410
 
 
411
        g_return_if_fail (view->priv != NULL);
 
412
 
 
413
        if (view->priv->change_sig_queued)
 
414
                g_source_remove (view->priv->change_sig_id);
 
415
 
 
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);
 
420
 
 
421
        g_list_free (view->priv->clickable_columns);
 
422
 
 
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);
 
426
 
 
427
        g_object_unref (G_OBJECT (view->priv->playing_pixbuf));
 
428
        g_object_unref (G_OBJECT (view->priv->paused_pixbuf));
 
429
 
 
430
        g_free (view->priv->sorting_key);
 
431
 
 
432
        g_free (view->priv);
 
433
 
 
434
        G_OBJECT_CLASS (parent_class)->finalize (object);
 
435
}
 
436
 
 
437
 
 
438
static void
 
439
rb_entry_view_set_property (GObject *object,
 
440
                           guint prop_id,
 
441
                           const GValue *value,
 
442
                           GParamSpec *pspec)
 
443
{
 
444
        RBEntryView *view = RB_ENTRY_VIEW (object);
 
445
 
 
446
        switch (prop_id)
 
447
        {
 
448
        case PROP_DB:
 
449
                view->priv->db = g_value_get_object (value);
 
450
                break;
 
451
        case PROP_SORTING_KEY:
 
452
                g_free (view->priv->sorting_key);
 
453
                view->priv->sorting_key = g_value_dup_string (value);
 
454
                break;
 
455
        case PROP_MODEL:
 
456
        {
 
457
                RhythmDBQueryModel *new_model;
 
458
                struct RBEntryViewColumnSortData *sort_data;
 
459
                
 
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),
 
465
                                                              view);
 
466
                        g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->model),
 
467
                                                              G_CALLBACK (rb_entry_view_row_deleted_cb),
 
468
                                                              view);
 
469
                        g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->model),
 
470
                                                              G_CALLBACK (rb_entry_view_row_changed_cb),
 
471
                                                              view);
 
472
                }
 
473
                new_model = g_value_get_object (value);
 
474
 
 
475
                rhythmdb_query_model_set_connected (RHYTHMDB_QUERY_MODEL (new_model),
 
476
                                                    TRUE);
 
477
 
 
478
                g_signal_connect_object (G_OBJECT (new_model),
 
479
                                         "row_inserted",
 
480
                                         G_CALLBACK (rb_entry_view_row_inserted_cb),
 
481
                                         view,
 
482
                                         0);
 
483
                g_signal_connect_object (G_OBJECT (new_model),
 
484
                                         "row_deleted",
 
485
                                         G_CALLBACK (rb_entry_view_row_deleted_cb),
 
486
                                         view,
 
487
                                         0);
 
488
                g_signal_connect_object (G_OBJECT (new_model),
 
489
                                         "row_changed",
 
490
                                         G_CALLBACK (rb_entry_view_row_changed_cb),
 
491
                                         view,
 
492
                                         0);
 
493
 
 
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);
 
498
 
 
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);
 
502
                        } else {
 
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;
 
508
                                
 
509
                                g_object_set (G_OBJECT (new_model), "sort-func",
 
510
                                              reverse_sorting_func, "sort-data",
 
511
                                              view->priv->reverse_sorting_data, NULL);
 
512
                        }
 
513
                }
 
514
 
 
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;
 
519
 
 
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;
 
524
                } else {
 
525
                        g_signal_emit (G_OBJECT (view), rb_entry_view_signals[ENTRIES_REPLACED], 0);
 
526
                }
 
527
 
 
528
                queue_changed_sig (view);
 
529
 
 
530
                break;
 
531
        }
 
532
        case PROP_PLAYING_ENTRY:
 
533
        {
 
534
                GtkTreeIter iter;
 
535
                GtkTreePath *path;
 
536
                RhythmDBEntry *entry;
 
537
                gboolean realized, visible;
 
538
 
 
539
                entry = g_value_get_pointer (value);
 
540
                
 
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));
 
549
                }
 
550
                
 
551
                view->priv->playing_entry = entry;
 
552
                g_object_ref (G_OBJECT (view->priv->model));
 
553
                view->priv->playing_model = view->priv->model;
 
554
 
 
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);
 
566
                        }
 
567
                }
 
568
 
 
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);
 
575
                }
 
576
        }
 
577
        break;
 
578
        case PROP_IS_DRAG_SOURCE:
 
579
                view->priv->is_drag_source = g_value_get_boolean (value);
 
580
                break;
 
581
        case PROP_IS_DRAG_DEST:
 
582
                view->priv->is_drag_dest = g_value_get_boolean (value);
 
583
                break;
 
584
        default:
 
585
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
586
                break;
 
587
        }
 
588
}
 
589
 
 
590
static void 
 
591
rb_entry_view_get_property (GObject *object,
 
592
                           guint prop_id,
 
593
                           GValue *value,
 
594
                           GParamSpec *pspec)
 
595
{
 
596
        RBEntryView *view = RB_ENTRY_VIEW (object);
 
597
 
 
598
        switch (prop_id)
 
599
        {
 
600
        case PROP_DB:
 
601
                g_value_set_object (value, view->priv->db);
 
602
                break;
 
603
        case PROP_SORTING_KEY:
 
604
                g_value_set_string (value, view->priv->sorting_key);
 
605
                break;
 
606
        case PROP_PLAYING_ENTRY:
 
607
                g_value_set_pointer (value, view->priv->playing_entry);
 
608
                break;
 
609
        case PROP_IS_DRAG_SOURCE:
 
610
                g_value_set_boolean (value, view->priv->is_drag_source);
 
611
                break;
 
612
        case PROP_IS_DRAG_DEST:
 
613
                g_value_set_boolean (value, view->priv->is_drag_dest);
 
614
                break;
 
615
        default:
 
616
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
617
                break;
 
618
        }
 
619
}
 
620
 
 
621
RBEntryView *
 
622
rb_entry_view_new (RhythmDB *db, const char *sort_key,
 
623
                   gboolean is_drag_source, gboolean is_drag_dest) 
 
624
{
 
625
        RBEntryView *view;
 
626
 
 
627
        view = RB_ENTRY_VIEW (g_object_new (RB_TYPE_ENTRY_VIEW,
 
628
                                           "hadjustment", NULL,
 
629
                                           "vadjustment", NULL,
 
630
                                           "hscrollbar_policy", GTK_POLICY_AUTOMATIC,
 
631
                                           "vscrollbar_policy", GTK_POLICY_ALWAYS,
 
632
                                           "shadow_type", GTK_SHADOW_IN,
 
633
                                            "db", db,
 
634
                                            "sort-key", sort_key,
 
635
                                            "is-drag-source", is_drag_source,
 
636
                                            "is-drag-dest", is_drag_dest,
 
637
                                           NULL));
 
638
 
 
639
        g_return_val_if_fail (view->priv != NULL, NULL);
 
640
 
 
641
        return view;
 
642
}
 
643
 
 
644
void
 
645
rb_entry_view_set_model (RBEntryView *view, RhythmDBQueryModel *model)
 
646
{
 
647
        g_object_set (G_OBJECT (view), "model", model, NULL);
 
648
}
 
649
 
 
650
gboolean
 
651
rb_entry_view_busy (RBEntryView *view)
 
652
{
 
653
        return view->priv->model &&
 
654
                rhythmdb_query_model_has_pending_changes (RHYTHMDB_QUERY_MODEL (view->priv->model));
 
655
}
 
656
 
 
657
glong
 
658
rb_entry_view_get_duration (RBEntryView *view)
 
659
{
 
660
        return rhythmdb_query_model_get_duration (RHYTHMDB_QUERY_MODEL (view->priv->model));
 
661
}
 
662
 
 
663
GnomeVFSFileSize
 
664
rb_entry_view_get_total_size (RBEntryView *view)
 
665
{
 
666
        return rhythmdb_query_model_get_size (RHYTHMDB_QUERY_MODEL (view->priv->model));
 
667
}
 
668
 
 
669
static RhythmDBEntry *
 
670
entry_from_tree_path (RBEntryView *view, GtkTreePath *path)
 
671
{
 
672
        GtkTreeIter entry_iter;
 
673
        RhythmDBEntry *entry;
 
674
 
 
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,
 
677
                            &entry, -1);
 
678
        return entry;
 
679
}
 
680
 
 
681
static inline RhythmDBEntry *
 
682
entry_from_tree_iter (RBEntryView *view, GtkTreeIter *iter)
 
683
{
 
684
        RhythmDBEntry *entry;
 
685
        gtk_tree_model_get (GTK_TREE_MODEL (view->priv->model), iter, 0,
 
686
                            &entry, -1);
 
687
        return entry;
 
688
}
 
689
 
 
690
static gint
 
691
reverse_sorting_func (gpointer a, gpointer b, struct RBEntryViewReverseSortingData *data)
 
692
{
 
693
        return - data->func (a, b, data->data);
 
694
}
 
695
 
 
696
/* Sweet name, eh? */
 
697
struct RBEntryViewCellDataFuncData {
 
698
        RBEntryView *view;
 
699
        RhythmDBPropType propid;
 
700
};
 
701
 
 
702
static gint
 
703
rb_entry_view_album_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
 
704
                               RBEntryView *view)
 
705
{
 
706
        gint a_int, b_int;
 
707
        const char *a_str = NULL;
 
708
        const char *b_str = NULL;
 
709
        gint ret;
 
710
 
 
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);
 
714
 
 
715
        ret = strcmp (a_str, b_str);
 
716
        if (ret != 0)
 
717
                return ret;
 
718
 
 
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);
 
722
        if (a_int != b_int)
 
723
                return (a_int < b_int ? -1 : 1);
 
724
 
 
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);
 
728
 
 
729
        if (a_int != b_int)
 
730
                return (a_int < b_int ? -1 : 1);
 
731
 
 
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);
 
735
 
 
736
        return strcmp (a_str, b_str);
 
737
}
 
738
 
 
739
static gint
 
740
rb_entry_view_artist_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
 
741
                                RBEntryView *view)
 
742
{
 
743
        const char *a_str = NULL;
 
744
        const char *b_str = NULL;
 
745
        gint ret;
 
746
 
 
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);
 
749
 
 
750
        ret = strcmp (a_str, b_str);
 
751
        if (ret != 0)
 
752
                return ret;
 
753
 
 
754
        return rb_entry_view_album_sort_func (a, b, view);
 
755
}
 
756
 
 
757
static gint
 
758
rb_entry_view_genre_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
 
759
                               RBEntryView *view)
 
760
{
 
761
        const char *a_str = NULL;
 
762
        const char *b_str = NULL;
 
763
        gint ret;
 
764
 
 
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);
 
767
 
 
768
        ret = strcmp (a_str, b_str);
 
769
        if (ret != 0)
 
770
                return ret;
 
771
 
 
772
        return rb_entry_view_artist_sort_func (a, b, view);
 
773
}
 
774
 
 
775
static gint
 
776
rb_entry_view_track_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
 
777
                               RBEntryView *view)
 
778
{
 
779
        return rb_entry_view_album_sort_func (a, b, view);
 
780
}
 
781
 
 
782
 
 
783
static gint
 
784
rb_entry_view_int_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
 
785
                             struct RBEntryViewCellDataFuncData *data)
 
786
{
 
787
        gint a_val, b_val;
 
788
        gint ret;
 
789
 
 
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);
 
792
 
 
793
        ret = (a_val == b_val ? 0 : (a_val > b_val ? 1 : -1));
 
794
 
 
795
        return ret;
 
796
}
 
797
 
 
798
static gint
 
799
rb_entry_view_double_ceiling_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
 
800
                                       struct RBEntryViewCellDataFuncData *data)
 
801
{
 
802
        gdouble a_val, b_val;
 
803
        gint ret;
 
804
 
 
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));
 
807
 
 
808
        ret = (a_val == b_val ? 0 : (a_val > b_val ? 1 : -1));
 
809
 
 
810
        return ret;
 
811
}
 
812
 
 
813
static gint
 
814
rb_entry_view_long_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
 
815
                              struct RBEntryViewCellDataFuncData *data)
 
816
{
 
817
        glong a_val, b_val;
 
818
        gint ret;
 
819
 
 
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);
 
822
 
 
823
        ret = (a_val == b_val ? 0 : (a_val > b_val ? 1 : -1));
 
824
 
 
825
        return ret;
 
826
}
 
827
 
 
828
static gint
 
829
rb_entry_view_string_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
 
830
                                struct RBEntryViewCellDataFuncData *data)
 
831
{
 
832
        const char *a_val;
 
833
        const char *b_val;      
 
834
        gint ret;
 
835
 
 
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);
 
838
 
 
839
        ret = strcmp (a_val, b_val);
 
840
 
 
841
        return ret;
 
842
}
 
843
 
 
844
static void
 
845
rb_entry_view_playing_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
846
                                      GtkTreeModel *tree_model, GtkTreeIter *iter,
 
847
                                      RBEntryView *view)
 
848
{
 
849
        RhythmDBEntry *entry;
 
850
        GdkPixbuf *pixbuf;
 
851
 
 
852
        entry = entry_from_tree_iter (view, iter);
 
853
 
 
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;
 
858
        else
 
859
                pixbuf = NULL;
 
860
 
 
861
        g_object_set (G_OBJECT (renderer), "pixbuf", pixbuf, NULL);
 
862
}
 
863
 
 
864
static void
 
865
rb_entry_view_rating_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
866
                                     GtkTreeModel *tree_model, GtkTreeIter *iter,
 
867
                                     RBEntryView *view)
 
868
{
 
869
        RhythmDBEntry *entry;
 
870
        gdouble rating;
 
871
 
 
872
        entry = entry_from_tree_iter (view, iter);
 
873
 
 
874
        rhythmdb_read_lock (view->priv->db);
 
875
 
 
876
        rating = rhythmdb_entry_get_double (view->priv->db, entry, RHYTHMDB_PROP_RATING);
 
877
 
 
878
        rhythmdb_read_unlock (view->priv->db);
 
879
 
 
880
        g_object_set (G_OBJECT (renderer), "rating", rating, NULL);
 
881
}
 
882
 
 
883
static void
 
884
rb_entry_view_intstr_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
885
                                  GtkTreeModel *tree_model, GtkTreeIter *iter,
 
886
                                  struct RBEntryViewCellDataFuncData *data)
 
887
{
 
888
        RhythmDBEntry *entry;
 
889
        char *str;
 
890
        int val;
 
891
 
 
892
        entry = entry_from_tree_iter (data->view, iter);
 
893
 
 
894
        rhythmdb_read_lock (data->view->priv->db);
 
895
 
 
896
        val = rhythmdb_entry_get_int (data->view->priv->db, entry, data->propid);
 
897
        rhythmdb_read_unlock (data->view->priv->db);
 
898
 
 
899
        if (val >= 0)
 
900
                str = g_strdup_printf ("%d", val);
 
901
        else
 
902
                str = g_strdup ("");
 
903
 
 
904
        g_object_set (G_OBJECT (renderer), "text", str, NULL);
 
905
        g_free (str);
 
906
}
 
907
 
 
908
static void
 
909
rb_entry_view_play_count_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
910
                                         GtkTreeModel *tree_model, GtkTreeIter *iter,
 
911
                                         struct RBEntryViewCellDataFuncData *data)
 
912
{
 
913
        RhythmDBEntry *entry;
 
914
        int i;
 
915
        char *str;
 
916
 
 
917
        entry = entry_from_tree_iter (data->view, iter);
 
918
 
 
919
        rhythmdb_read_lock (data->view->priv->db);
 
920
 
 
921
        i = rhythmdb_entry_get_int (data->view->priv->db, entry, data->propid);
 
922
        if (i == 0)
 
923
                str = _("Never");
 
924
        else
 
925
                str = g_strdup_printf ("%d", i);
 
926
 
 
927
        rhythmdb_read_unlock (data->view->priv->db);
 
928
 
 
929
        g_object_set (G_OBJECT (renderer), "text", str, NULL);
 
930
        if (i != 0)
 
931
                g_free (str);
 
932
}
 
933
 
 
934
static void
 
935
rb_entry_view_duration_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
936
                                       GtkTreeModel *tree_model, GtkTreeIter *iter,
 
937
                                       struct RBEntryViewCellDataFuncData *data)
 
938
{
 
939
        RhythmDBEntry *entry;
 
940
        char *str;
 
941
        long duration;
 
942
        int minutes, seconds;
 
943
 
 
944
        entry = entry_from_tree_iter (data->view, iter);
 
945
 
 
946
        rhythmdb_read_lock (data->view->priv->db);
 
947
 
 
948
        duration  = rhythmdb_entry_get_long (data->view->priv->db, entry,
 
949
                                             data->propid);
 
950
 
 
951
        rhythmdb_read_unlock (data->view->priv->db);
 
952
 
 
953
        minutes = duration / 60;
 
954
        seconds = duration % 60;
 
955
 
 
956
        if (minutes == 0 && seconds == 0)
 
957
                str = g_strdup (_("Unknown"));
 
958
        else
 
959
                str = g_strdup_printf (_("%d:%02d"), minutes, seconds);
 
960
 
 
961
        g_object_set (G_OBJECT (renderer), "text", str, NULL);
 
962
 
 
963
        g_free (str);
 
964
}
 
965
 
 
966
static void
 
967
rb_entry_view_quality_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
968
                                      GtkTreeModel *tree_model, GtkTreeIter *iter,
 
969
                                      struct RBEntryViewCellDataFuncData *data)
 
970
{
 
971
        RhythmDBEntry *entry;
 
972
        guint bitrate;
 
973
 
 
974
        entry = entry_from_tree_iter (data->view, iter);
 
975
 
 
976
        rhythmdb_read_lock (data->view->priv->db);
 
977
 
 
978
        bitrate = rhythmdb_entry_get_int (data->view->priv->db, entry,
 
979
                                          data->propid);
 
980
 
 
981
        rhythmdb_read_unlock (data->view->priv->db);
 
982
 
 
983
        if (bitrate <= 0) {
 
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);
 
995
        } else {
 
996
                g_object_set (G_OBJECT (renderer), "text", _("Perfect"), NULL);
 
997
        }
 
998
}
 
999
 
 
1000
 
 
1001
static void
 
1002
rb_entry_view_string_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
1003
                                     GtkTreeModel *tree_model, GtkTreeIter *iter,
 
1004
                                     struct RBEntryViewCellDataFuncData *data)
 
1005
{
 
1006
        RhythmDBEntry *entry;
 
1007
        const char *str;
 
1008
 
 
1009
        entry = entry_from_tree_iter (data->view, iter);
 
1010
 
 
1011
        rhythmdb_read_lock (data->view->priv->db);
 
1012
 
 
1013
        str = rhythmdb_entry_get_string (data->view->priv->db, entry, data->propid);
 
1014
 
 
1015
        g_object_set (G_OBJECT (renderer), "text", str, NULL);
 
1016
 
 
1017
        rhythmdb_read_unlock (data->view->priv->db);
 
1018
}
 
1019
 
 
1020
static void
 
1021
rb_entry_view_sync_sorting (RBEntryView *view)
 
1022
{
 
1023
        GList *columns, *tem;
 
1024
        GtkTreeViewColumn *column;
 
1025
        char **strs;
 
1026
        gint direction;
 
1027
        char *sorttype;
 
1028
 
 
1029
        if (!view->priv->sorting_key)
 
1030
                return;
 
1031
 
 
1032
        sorttype = eel_gconf_get_string (view->priv->sorting_key);
 
1033
 
 
1034
        if (!sorttype || !*sorttype) {
 
1035
                rb_debug ("no sorting data available in gconf!");
 
1036
                return;
 
1037
        }
 
1038
 
 
1039
        if (!strchr (sorttype, ',')) {
 
1040
                g_warning ("malformed sort data");
 
1041
                return;
 
1042
        }
 
1043
        
 
1044
        strs = g_strsplit (sorttype, ",", 0);
 
1045
 
 
1046
        column = g_hash_table_lookup (view->priv->column_key_map, strs[0]);
 
1047
        if (!column)
 
1048
                goto free_out;
 
1049
 
 
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]))
 
1055
                direction = -1;
 
1056
        else
 
1057
                goto free_out;
 
1058
 
 
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);
 
1063
        
 
1064
        view->priv->sorting_column = column;
 
1065
        view->priv->sorting_order = direction;
 
1066
 
 
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);
 
1070
 
 
1071
                rb_debug ("emitting sort order changed");
 
1072
                g_signal_emit (G_OBJECT (view), rb_entry_view_signals[SORT_ORDER_CHANGED], 0); 
 
1073
        }
 
1074
free_out:
 
1075
        g_strfreev (strs);
 
1076
}
 
1077
 
 
1078
const char *
 
1079
rb_entry_view_get_sorting_type (RBEntryView *view)
 
1080
{
 
1081
        return eel_gconf_get_string (view->priv->sorting_key);
 
1082
}
 
1083
 
 
1084
void
 
1085
rb_entry_view_set_resorting (RBEntryView *view)
 
1086
{
 
1087
        if (view->priv->resorting)
 
1088
                g_warning ("Sort order changed while resorting");
 
1089
        view->priv->resorting = TRUE;
 
1090
}
 
1091
 
 
1092
static void
 
1093
rb_entry_view_column_clicked_cb (GtkTreeViewColumn *column, RBEntryView *view)
 
1094
{
 
1095
        GString *key = g_string_new ("");
 
1096
        gboolean is_sorted;
 
1097
        gint sorting_order;
 
1098
 
 
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, ',');
 
1102
 
 
1103
        is_sorted = gtk_tree_view_column_get_sort_indicator (column);
 
1104
 
 
1105
        if (is_sorted) {
 
1106
                sorting_order = gtk_tree_view_column_get_sort_order (column);
 
1107
                if (sorting_order == GTK_SORT_ASCENDING)
 
1108
                        sorting_order = GTK_SORT_DESCENDING;
 
1109
                else
 
1110
                        sorting_order = -1;
 
1111
        } else
 
1112
                sorting_order = GTK_SORT_ASCENDING;
 
1113
 
 
1114
        switch (sorting_order)
 
1115
        {
 
1116
        case -1:
 
1117
                g_string_append (key, "none");
 
1118
                break;
 
1119
        case GTK_SORT_ASCENDING:
 
1120
                g_string_append (key, "ascending");
 
1121
                break;
 
1122
        case GTK_SORT_DESCENDING:
 
1123
                g_string_append (key, "descending");
 
1124
                break;
 
1125
        default:
 
1126
                g_assert_not_reached ();
 
1127
        }
 
1128
        eel_gconf_set_string (view->priv->sorting_key, key->str);
 
1129
        g_string_free (key, TRUE);
 
1130
}
 
1131
 
 
1132
void
 
1133
rb_entry_view_append_column (RBEntryView *view, RBEntryViewColumn coltype)
 
1134
{
 
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;
 
1145
 
 
1146
        column = GTK_TREE_VIEW_COLUMN (rb_tree_view_column_new ());
 
1147
 
 
1148
        if (coltype == RB_ENTRY_VIEW_COL_RATING) {
 
1149
                guint width;
 
1150
 
 
1151
                propid = RHYTHMDB_PROP_RATING;
 
1152
 
 
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;
 
1157
 
 
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,
 
1163
                                                         view,
 
1164
                                                         NULL);
 
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,
 
1170
                                         "rated",
 
1171
                                         G_CALLBACK (rb_entry_view_rated_cb),
 
1172
                                         G_OBJECT (view),
 
1173
                                         0);
 
1174
                real_sort_data = sort_data;
 
1175
                title = _("_Rating");
 
1176
                key = "Rating";
 
1177
                goto append;
 
1178
        }
 
1179
 
 
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;
 
1184
 
 
1185
        switch (coltype)
 
1186
        {
 
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");
 
1194
                key = "Track";
 
1195
                break;
 
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");
 
1203
                key = "Title";
 
1204
                rb_tree_view_column_set_expand (RB_TREE_VIEW_COLUMN (column), TRUE);
 
1205
                break;
 
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");
 
1214
                key = "Artist";
 
1215
                rb_tree_view_column_set_expand (RB_TREE_VIEW_COLUMN (column), TRUE);
 
1216
                break;
 
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");
 
1225
                key = "Album";
 
1226
                rb_tree_view_column_set_expand (RB_TREE_VIEW_COLUMN (column), TRUE);
 
1227
                break;
 
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");
 
1236
                key = "Genre";
 
1237
                rb_tree_view_column_set_expand (RB_TREE_VIEW_COLUMN (column), TRUE);
 
1238
                break;
 
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;
 
1245
                title = _("Ti_me");
 
1246
                key = "Time";
 
1247
                break;
 
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");
 
1255
                key = "Quality";
 
1256
                break;
 
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");
 
1265
                key = "PlayCount";
 
1266
                break;
 
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");
 
1274
                key = "LastPlayed";
 
1275
                break;
 
1276
        case RB_ENTRY_VIEW_COL_RATING:
 
1277
        default:
 
1278
                g_assert_not_reached ();
 
1279
                propid = -1;
 
1280
                break;
 
1281
        }
 
1282
 
 
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);
 
1290
 
 
1291
append:
 
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);
 
1295
}
 
1296
 
 
1297
void
 
1298
rb_entry_view_append_column_custom (RBEntryView *view,
 
1299
                                    GtkTreeViewColumn *column,
 
1300
                                    const char *title,
 
1301
                                    const char *key,
 
1302
                                    GCompareDataFunc sort_func, gpointer user_data)
 
1303
{
 
1304
        struct RBEntryViewColumnSortData *sortdata;
 
1305
 
 
1306
        gtk_tree_view_column_set_title (column, title);
 
1307
        gtk_tree_view_column_set_reorderable (column, FALSE);
 
1308
 
 
1309
        if (gtk_tree_view_column_get_clickable (column))
 
1310
                view->priv->clickable_columns = g_list_append (view->priv->clickable_columns, column);
 
1311
 
 
1312
        g_signal_connect_object (G_OBJECT (column), "clicked",
 
1313
                                 G_CALLBACK (rb_entry_view_column_clicked_cb),
 
1314
                                 view, 0);
 
1315
 
 
1316
        g_object_set_data_full (G_OBJECT (column), "rb-entry-view-key",
 
1317
                                g_strdup (key), g_free);
 
1318
 
 
1319
        rb_debug ("appending column: %p (%s)", column, title);
 
1320
 
 
1321
        gtk_tree_view_append_column (GTK_TREE_VIEW (view->priv->treeview), column);
 
1322
 
 
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);
 
1328
 
 
1329
        rb_entry_view_sync_columns_visible (view);
 
1330
        rb_entry_view_sync_sorting (view);
 
1331
}
 
1332
 
 
1333
void
 
1334
rb_entry_view_set_columns_clickable (RBEntryView *view, gboolean clickable)
 
1335
{
 
1336
        GList *columns, *tem;
 
1337
 
 
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);
 
1342
}
 
1343
 
 
1344
static GObject *
 
1345
rb_entry_view_constructor (GType type, guint n_construct_properties,
 
1346
                           GObjectConstructParam *construct_properties)
 
1347
{
 
1348
        RBEntryView *view;
 
1349
        RBEntryViewClass *klass;
 
1350
        GObjectClass *parent_class;
 
1351
        klass = RB_ENTRY_VIEW_CLASS (g_type_class_peek (RB_TYPE_ENTRY_VIEW));
 
1352
 
 
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));
 
1356
 
 
1357
        view->priv->treeview = GTK_WIDGET (rb_tree_view_new ());
 
1358
 
 
1359
        g_signal_connect_object (G_OBJECT (view->priv->treeview),
 
1360
                                 "button_press_event",
 
1361
                                 G_CALLBACK (rb_entry_view_button_press_cb),
 
1362
                                 view,
 
1363
                                 0);
 
1364
        g_signal_connect_object (G_OBJECT (view->priv->treeview),
 
1365
                                 "row_activated",
 
1366
                                 G_CALLBACK (rb_entry_view_row_activated_cb),
 
1367
                                 view,
 
1368
                                 0);
 
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),
 
1371
                                 "changed",
 
1372
                                 G_CALLBACK (rb_entry_view_selection_changed_cb),
 
1373
                                 view,
 
1374
                                 0);
 
1375
 
 
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);
 
1379
        
 
1380
        if (view->priv->is_drag_source)
 
1381
                rb_tree_dnd_add_drag_source_support (GTK_TREE_VIEW (view->priv->treeview),
 
1382
                                                     GDK_BUTTON1_MASK,
 
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);
 
1392
 
 
1393
        gtk_container_add (GTK_CONTAINER (view), view->priv->treeview);
 
1394
 
 
1395
        {
 
1396
                GtkTreeViewColumn *column;
 
1397
                GtkTooltips *tooltip;
 
1398
                GtkCellRenderer *renderer;
 
1399
                guint width;
 
1400
 
 
1401
                tooltip = gtk_tooltips_new ();
 
1402
                
 
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,
 
1410
                                                         view,
 
1411
                                                         NULL);
 
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);
 
1416
 
 
1417
                gtk_tooltips_set_tip (GTK_TOOLTIPS (tooltip), GTK_WIDGET (column->button),
 
1418
                                       _("Now Playing"), NULL);
 
1419
        }
 
1420
 
 
1421
        view->priv->gconf_notification_id = 
 
1422
                eel_gconf_notification_add (CONF_UI_COLUMNS_SETUP,
 
1423
                                            rb_entry_view_columns_config_changed_cb,
 
1424
                                            view);
 
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,
 
1429
                                                    view);
 
1430
        
 
1431
        {
 
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));
 
1436
        }
 
1437
                
 
1438
        return G_OBJECT (view);
 
1439
}
 
1440
 
 
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) */
 
1446
/* { */
 
1447
/*      g_signal_stop_emission_by_name (widget, "drag_drop"); */
 
1448
 
 
1449
/*      return TRUE; */
 
1450
/* } */
 
1451
 
 
1452
 
 
1453
 
 
1454
static void
 
1455
rb_entry_view_rated_cb (RBCellRendererRating *cellrating,
 
1456
                       const char *path_string,
 
1457
                       double rating,
 
1458
                       RBEntryView *view)
 
1459
{
 
1460
        GtkTreePath *path;
 
1461
        RhythmDBEntry *entry;
 
1462
        GValue value = { 0, };
 
1463
 
 
1464
        g_return_if_fail (rating >= 0 && rating <= 5 );
 
1465
        g_return_if_fail (path_string != NULL);
 
1466
 
 
1467
        path = gtk_tree_path_new_from_string (path_string);
 
1468
        entry = entry_from_tree_path (view, path);
 
1469
        gtk_tree_path_free (path);
 
1470
 
 
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,
 
1474
                                  &value);
 
1475
        g_value_unset (&value);
 
1476
 
 
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,
 
1481
                                  &value);
 
1482
        g_value_unset (&value);
 
1483
}
 
1484
 
 
1485
void
 
1486
rb_entry_view_set_playing_entry (RBEntryView *view,
 
1487
                                 RhythmDBEntry *entry)
 
1488
{
 
1489
        g_return_if_fail (RB_IS_ENTRY_VIEW (view));
 
1490
 
 
1491
        g_object_set (G_OBJECT (view), "playing-entry", entry, NULL);
 
1492
}
 
1493
 
 
1494
RhythmDBEntry *
 
1495
rb_entry_view_get_playing_entry (RBEntryView *view)
 
1496
{
 
1497
        g_return_val_if_fail (RB_IS_ENTRY_VIEW (view), NULL);
 
1498
 
 
1499
        return view->priv->playing_entry;
 
1500
}
 
1501
 
 
1502
RhythmDBEntry *
 
1503
rb_entry_view_get_first_entry (RBEntryView *view)
 
1504
{
 
1505
        GtkTreeIter iter;
 
1506
 
 
1507
        if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (view->priv->model), &iter))
 
1508
                return entry_from_tree_iter (view, &iter);
 
1509
 
 
1510
        return NULL;
 
1511
}
 
1512
 
 
1513
RhythmDBEntry *
 
1514
rb_entry_view_get_next_from_entry (RBEntryView *view, RhythmDBEntry *entry)
 
1515
{
 
1516
        GtkTreeIter iter;
 
1517
 
 
1518
        g_return_val_if_fail (entry != NULL, NULL);
 
1519
 
 
1520
        if (!rhythmdb_query_model_entry_to_iter (view->priv->model,
 
1521
                                                 entry, &iter)) {
 
1522
                /* If the entry isn't in the entryview, the "next" entry is the first. */
 
1523
                return rb_entry_view_get_first_entry (view);
 
1524
        }
 
1525
        
 
1526
        if (gtk_tree_model_iter_next (GTK_TREE_MODEL (view->priv->model),
 
1527
                                      &iter))
 
1528
                return entry_from_tree_iter (view, &iter);
 
1529
 
 
1530
        return NULL;
 
1531
}
 
1532
 
 
1533
RhythmDBEntry *
 
1534
rb_entry_view_get_previous_from_entry (RBEntryView *view, RhythmDBEntry *entry)
 
1535
{
 
1536
        GtkTreeIter iter;
 
1537
        GtkTreePath *path;
 
1538
        RhythmDBEntry *ret;
 
1539
 
 
1540
        g_return_val_if_fail (entry != NULL, NULL);
 
1541
 
 
1542
        if (!rhythmdb_query_model_entry_to_iter (view->priv->model,
 
1543
                                                 entry, &iter))
 
1544
                return NULL;
 
1545
 
 
1546
        path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->priv->model), &iter);
 
1547
        g_assert (path);
 
1548
        if (!gtk_tree_path_prev (path)) {
 
1549
                gtk_tree_path_free (path);
 
1550
                return NULL;
 
1551
        }
 
1552
 
 
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);
 
1556
        return ret;
 
1557
}
 
1558
 
 
1559
 
 
1560
RhythmDBEntry *
 
1561
rb_entry_view_get_next_entry (RBEntryView *view)
 
1562
{
 
1563
        if (view->priv->playing_entry == NULL)
 
1564
                return NULL;
 
1565
 
 
1566
        return rb_entry_view_get_next_from_entry (view,
 
1567
                                                  view->priv->playing_entry);
 
1568
}
 
1569
 
 
1570
RhythmDBEntry *
 
1571
rb_entry_view_get_previous_entry (RBEntryView *view)
 
1572
{
 
1573
        if (view->priv->playing_entry == NULL)
 
1574
                return NULL;
 
1575
        
 
1576
        return rb_entry_view_get_previous_from_entry (view,
 
1577
                                                      view->priv->playing_entry);
 
1578
}
 
1579
 
 
1580
static gboolean
 
1581
harvest_entries (GtkTreeModel *model,
 
1582
                 GtkTreePath *path,
 
1583
                 GtkTreeIter *iter,
 
1584
                 void **data)
 
1585
{
 
1586
        GList **list = (GList **) data;
 
1587
        RhythmDBEntry *entry;
 
1588
 
 
1589
        gtk_tree_model_get (model, iter, 0, &entry, -1);
 
1590
 
 
1591
        *list = g_list_append (*list, entry);
 
1592
 
 
1593
        return FALSE;
 
1594
}
 
1595
 
 
1596
GList *
 
1597
rb_entry_view_get_selected_entries (RBEntryView *view)
 
1598
{
 
1599
        GList *list = NULL;
 
1600
 
 
1601
        gtk_tree_selection_selected_foreach (view->priv->selection,
 
1602
                                             (GtkTreeSelectionForeachFunc) harvest_entries,
 
1603
                                             (gpointer) &list);
 
1604
 
 
1605
        return list;
 
1606
}
 
1607
 
 
1608
RhythmDBEntry *
 
1609
rb_entry_view_get_random_entry (RBEntryView *view)
 
1610
{
 
1611
        GtkTreePath *path;
 
1612
        GtkTreeIter iter;
 
1613
        char *path_str;
 
1614
        int index, n_rows;
 
1615
 
 
1616
        n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (view->priv->model), NULL);
 
1617
        if (n_rows == 0)
 
1618
                return NULL;
 
1619
        else if ((n_rows - 1) > 0)
 
1620
                index = g_random_int_range (0, n_rows);
 
1621
        else
 
1622
                index = 0;
 
1623
 
 
1624
        path_str = g_strdup_printf ("%d", index);
 
1625
        path = gtk_tree_path_new_from_string (path_str);
 
1626
        g_free (path_str);
 
1627
 
 
1628
        gtk_tree_model_get_iter (GTK_TREE_MODEL (view->priv->model),
 
1629
                                 &iter, path);
 
1630
 
 
1631
        gtk_tree_path_free (path);
 
1632
 
 
1633
        return entry_from_tree_iter (view, &iter);
 
1634
}
 
1635
 
 
1636
static gboolean
 
1637
rb_entry_view_button_press_cb (GtkTreeView *treeview,
 
1638
                              GdkEventButton *event,
 
1639
                              RBEntryView *view)
 
1640
{
 
1641
        if (event->button == 3) {
 
1642
                GtkTreePath *path;
 
1643
                RhythmDBEntry *entry;
 
1644
 
 
1645
                gtk_tree_view_get_path_at_pos (treeview, event->x, event->y, &path, NULL, NULL, NULL);
 
1646
                if (path != NULL) {
 
1647
                        GList *selected;
 
1648
                        entry = entry_from_tree_path (view, path);
 
1649
 
 
1650
                        selected = rb_entry_view_get_selected_entries (view);
 
1651
                        
 
1652
                        if (!g_list_find (selected, entry))
 
1653
                                rb_entry_view_select_entry (view, entry);
 
1654
 
 
1655
                        g_list_free (selected);
 
1656
                        
 
1657
                        g_signal_emit (G_OBJECT (view), rb_entry_view_signals[SHOW_POPUP], 0);
 
1658
                }
 
1659
                return view->priv->have_selection;
 
1660
        }
 
1661
 
 
1662
        return FALSE;
 
1663
}
 
1664
 
 
1665
static void
 
1666
queue_changed_sig (RBEntryView *view)
 
1667
{
 
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);
 
1671
        }
 
1672
        view->priv->change_sig_queued = TRUE;
 
1673
}
 
1674
 
 
1675
static void
 
1676
rb_entry_view_selection_changed_cb (GtkTreeSelection *selection,
 
1677
                                   RBEntryView *view)
 
1678
{
 
1679
        gboolean available;
 
1680
        RhythmDBEntry *selected_entry = NULL;
 
1681
        GList *sel;
 
1682
 
 
1683
        if (view->priv->selection_lock == TRUE)
 
1684
                return;
 
1685
 
 
1686
        sel = rb_entry_view_get_selected_entries (view);
 
1687
        available = (sel != NULL);
 
1688
        if (sel != NULL)
 
1689
                selected_entry = (g_list_first (sel))->data;
 
1690
 
 
1691
        if (available != view->priv->have_selection) {
 
1692
                queue_changed_sig (view);
 
1693
                view->priv->have_selection = available;
 
1694
 
 
1695
                g_signal_emit (G_OBJECT (view), rb_entry_view_signals[HAVE_SEL_CHANGED], 0, available);
 
1696
        }
 
1697
 
 
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);
 
1700
 
 
1701
        view->priv->selected_entry = selected_entry;
 
1702
 
 
1703
        g_list_free (sel);
 
1704
}
 
1705
 
 
1706
gboolean
 
1707
rb_entry_view_have_selection (RBEntryView *view)
 
1708
{
 
1709
        return view->priv->have_selection;
 
1710
}
 
1711
 
 
1712
static void
 
1713
rb_entry_view_row_activated_cb (GtkTreeView *treeview,
 
1714
                               GtkTreePath *path,
 
1715
                               GtkTreeViewColumn *column,
 
1716
                               RBEntryView *view)
 
1717
{
 
1718
        RhythmDBEntry *entry;
 
1719
 
 
1720
        rb_debug ("row activated");
 
1721
 
 
1722
        entry = entry_from_tree_path (view, path);
 
1723
 
 
1724
        rb_debug ("emitting entry activated");
 
1725
        g_signal_emit (G_OBJECT (view), rb_entry_view_signals[ENTRY_ACTIVATED], 0, entry);
 
1726
}
 
1727
 
 
1728
static void
 
1729
rb_entry_view_row_inserted_cb (GtkTreeModel *model,
 
1730
                               GtkTreePath *path,
 
1731
                               GtkTreeIter *iter,
 
1732
                               RBEntryView *view)
 
1733
{
 
1734
        RhythmDBEntry *entry = entry_from_tree_path (view, path);
 
1735
 
 
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);
 
1739
}
 
1740
 
 
1741
static void
 
1742
rb_entry_view_row_deleted_cb (GtkTreeModel *model,
 
1743
                              GtkTreePath *path,
 
1744
                              RBEntryView *view)
 
1745
{
 
1746
        RhythmDBEntry *entry = entry_from_tree_path (view, path);
 
1747
 
 
1748
        if (entry == view->priv->playing_entry) {
 
1749
                view->priv->playing_entry = NULL;
 
1750
                
 
1751
                rb_debug ("emitting playing entry destroyed");
 
1752
                
 
1753
                g_signal_emit (G_OBJECT (view), rb_entry_view_signals[PLAYING_ENTRY_DELETED],
 
1754
                               0, view->priv->playing_entry);
 
1755
        }
 
1756
        
 
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);
 
1760
}
 
1761
 
 
1762
static void
 
1763
rb_entry_view_row_changed_cb (GtkTreeModel *model,
 
1764
                              GtkTreePath *path,
 
1765
                              GtkTreeIter *iter,
 
1766
                              RBEntryView *view)
 
1767
{
 
1768
        rb_debug ("row changed");
 
1769
        queue_changed_sig (view);
 
1770
}
 
1771
 
 
1772
guint
 
1773
rb_entry_view_get_num_entries (RBEntryView *view)
 
1774
{
 
1775
        return gtk_tree_model_iter_n_children (GTK_TREE_MODEL (view->priv->model),
 
1776
                                               NULL);
 
1777
}
 
1778
 
 
1779
void
 
1780
rb_entry_view_select_all (RBEntryView *view)
 
1781
{
 
1782
        gtk_tree_selection_select_all (view->priv->selection);
 
1783
}
 
1784
 
 
1785
void
 
1786
rb_entry_view_select_none (RBEntryView *view)
 
1787
{
 
1788
        view->priv->selection_lock = TRUE;
 
1789
 
 
1790
        gtk_tree_selection_unselect_all (view->priv->selection);
 
1791
 
 
1792
        view->priv->selected_entry = NULL;
 
1793
 
 
1794
        view->priv->selection_lock = FALSE;
 
1795
}
 
1796
 
 
1797
void
 
1798
rb_entry_view_select_entry (RBEntryView *view,
 
1799
                            RhythmDBEntry *entry)
 
1800
{
 
1801
        GtkTreeIter iter;
 
1802
 
 
1803
        if (entry == NULL)
 
1804
                return;
 
1805
 
 
1806
        view->priv->selection_lock = TRUE;
 
1807
 
 
1808
        rb_entry_view_select_none (view);
 
1809
 
 
1810
        if (rhythmdb_query_model_entry_to_iter (view->priv->model,
 
1811
                                                entry, &iter)) {
 
1812
                gtk_tree_selection_select_iter (view->priv->selection, &iter);
 
1813
        }
 
1814
 
 
1815
        view->priv->selection_lock = FALSE;
 
1816
}
 
1817
 
 
1818
void
 
1819
rb_entry_view_scroll_to_entry (RBEntryView *view,
 
1820
                               RhythmDBEntry *entry)
 
1821
{
 
1822
        GtkTreeIter iter;
 
1823
        
 
1824
        if (rhythmdb_query_model_entry_to_iter (view->priv->model,
 
1825
                                                entry, &iter)) {
 
1826
                rb_entry_view_scroll_to_iter (view, &iter);
 
1827
        }
 
1828
}
 
1829
 
 
1830
static void
 
1831
rb_entry_view_scroll_to_iter (RBEntryView *view,
 
1832
                              GtkTreeIter *iter)
 
1833
{
 
1834
        GtkTreePath *path;
 
1835
 
 
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),
 
1839
                                      TRUE, 0.5, 0.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);
 
1842
 
 
1843
        gtk_tree_path_free (path);
 
1844
}
 
1845
 
 
1846
gboolean
 
1847
rb_entry_view_get_entry_visible (RBEntryView *view,
 
1848
                                 RhythmDBEntry *entry)
 
1849
{
 
1850
        GtkTreeIter unused;
 
1851
        gboolean realized, visible;
 
1852
 
 
1853
        if (view->priv->playing_model != view->priv->model)
 
1854
                return FALSE;
 
1855
 
 
1856
        rb_entry_view_entry_is_visible (view, entry, &realized, &visible,
 
1857
                                        &unused);
 
1858
        return realized && visible;
 
1859
}
 
1860
 
 
1861
gboolean
 
1862
rb_entry_view_get_entry_contained (RBEntryView *view,
 
1863
                                   RhythmDBEntry *entry)
 
1864
{
 
1865
        GtkTreeIter unused;
 
1866
 
 
1867
        return rhythmdb_query_model_entry_to_iter (view->priv->model,
 
1868
                                                   entry, &unused);
 
1869
}
 
1870
 
 
1871
static void
 
1872
rb_entry_view_entry_is_visible (RBEntryView *view,
 
1873
                                RhythmDBEntry *entry,
 
1874
                                gboolean *realized,
 
1875
                                gboolean *visible,
 
1876
                                GtkTreeIter *iter)
 
1877
{
 
1878
        GtkTreePath *path;
 
1879
        GdkRectangle rect;
 
1880
 
 
1881
        *realized = FALSE;
 
1882
        *visible = FALSE;
 
1883
 
 
1884
        g_return_if_fail (entry != NULL);
 
1885
 
 
1886
        if (!GTK_WIDGET_REALIZED (view))
 
1887
                return;
 
1888
 
 
1889
        *realized = TRUE;
 
1890
 
 
1891
        if (!rhythmdb_query_model_entry_to_iter (view->priv->model,
 
1892
                                                 entry, iter))
 
1893
                return;
 
1894
 
 
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),
 
1897
                                     path,
 
1898
                                     gtk_tree_view_get_column (GTK_TREE_VIEW (view->priv->treeview), 0),
 
1899
                                     &rect);
 
1900
 
 
1901
        gtk_tree_path_free (path);
 
1902
 
 
1903
        *visible = (rect.y != 0 && rect.height != 0);
 
1904
}
 
1905
 
 
1906
static gboolean
 
1907
emit_entry_changed (RBEntryView *view)
 
1908
{
 
1909
        GDK_THREADS_ENTER ();
 
1910
 
 
1911
        g_signal_emit (G_OBJECT (view), rb_entry_view_signals[CHANGED], 0);
 
1912
 
 
1913
        view->priv->change_sig_queued = FALSE;
 
1914
 
 
1915
        GDK_THREADS_LEAVE ();
 
1916
 
 
1917
        return FALSE;
 
1918
}
 
1919
 
 
1920
/* static void */
 
1921
/* playing_entry_deleted_cb (RhythmDB *db, RBEntryView *view) */
 
1922
/* { */
 
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); */
 
1926
/* } */
 
1927
 
 
1928
void
 
1929
rb_entry_view_enable_drag_source (RBEntryView *view,
 
1930
                                 const GtkTargetEntry *targets,
 
1931
                                 int n_targets)
 
1932
{
 
1933
        g_return_if_fail (view != NULL);
 
1934
 
 
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);
 
1938
}
 
1939
 
 
1940
static void
 
1941
rb_entry_view_sort_key_changed_cb (GConfClient* client,
 
1942
                                   guint cnxn_id,
 
1943
                                   GConfEntry *entry,
 
1944
                                   gpointer user_data)
 
1945
{
 
1946
        RBEntryView *view = user_data;
 
1947
 
 
1948
        g_return_if_fail (RB_IS_ENTRY_VIEW (view));
 
1949
 
 
1950
        rb_entry_view_sync_sorting (view);
 
1951
}
 
1952
 
 
1953
static void
 
1954
rb_entry_view_columns_config_changed_cb (GConfClient* client,
 
1955
                                        guint cnxn_id,
 
1956
                                        GConfEntry *entry,
 
1957
                                        gpointer user_data)
 
1958
{
 
1959
        RBEntryView *view = user_data;
 
1960
 
 
1961
        g_return_if_fail (RB_IS_ENTRY_VIEW (view));
 
1962
 
 
1963
        rb_entry_view_sync_columns_visible (view);
 
1964
}
 
1965
 
 
1966
static gint
 
1967
propid_from_name (const char *name)
 
1968
{
 
1969
        GEnumClass *prop_class = g_type_class_ref (RHYTHMDB_TYPE_PROP);
 
1970
        GEnumClass *unsaved_prop_class = g_type_class_ref (RHYTHMDB_TYPE_UNSAVED_PROP);
 
1971
        GEnumValue *ev;
 
1972
        int ret;
 
1973
 
 
1974
        ev = g_enum_get_value_by_name (prop_class, name);
 
1975
        if (!ev)
 
1976
                ev = g_enum_get_value_by_name (unsaved_prop_class, name);
 
1977
        if (ev)
 
1978
                ret = ev->value;
 
1979
        else
 
1980
                ret = -1;
 
1981
        return ret;
 
1982
}
 
1983
 
 
1984
static void
 
1985
set_column_not_visible (guint propid, GtkTreeViewColumn *column, gpointer unused)
 
1986
{
 
1987
        /* title is always visible */
 
1988
        if (propid == RHYTHMDB_PROP_TITLE)
 
1989
                return;
 
1990
        
 
1991
        gtk_tree_view_column_set_visible (column, FALSE);
 
1992
}
 
1993
 
 
1994
static void
 
1995
rb_entry_view_sync_columns_visible (RBEntryView *view)
 
1996
{
 
1997
        char **items;
 
1998
        GList *visible_properties = NULL, *tem;
 
1999
        char *config = eel_gconf_get_string (CONF_UI_COLUMNS_SETUP);
 
2000
 
 
2001
        g_return_if_fail (view != NULL);
 
2002
        g_return_if_fail (config != NULL);
 
2003
 
 
2004
        g_hash_table_foreach (view->priv->propid_column_map, (GHFunc) set_column_not_visible, NULL);
 
2005
 
 
2006
        items = g_strsplit (config, ",", 0);
 
2007
        if (items != NULL) {
 
2008
                int i;
 
2009
                for (i = 0; items[i] != NULL && *(items[i]); i++) {
 
2010
                        int value = propid_from_name (items[i]);
 
2011
 
 
2012
                        if ((value >= 0) && (value < RHYTHMDB_NUM_PROPERTIES))
 
2013
                                visible_properties = g_list_append (visible_properties, GINT_TO_POINTER (value));
 
2014
                }
 
2015
                g_strfreev (items);
 
2016
        }
 
2017
 
 
2018
        for (tem = visible_properties; tem; tem = tem->next) {
 
2019
                GtkTreeViewColumn *column
 
2020
                        = g_hash_table_lookup (view->priv->propid_column_map, tem->data);
 
2021
                if (column)
 
2022
                        gtk_tree_view_column_set_visible (column, TRUE);
 
2023
        }
 
2024
 
 
2025
        g_list_free (visible_properties);
 
2026
        g_free (config);
 
2027
}
 
2028
 
 
2029
void
 
2030
rb_entry_view_set_playing (RBEntryView *view,
 
2031
                           gboolean playing)
 
2032
{
 
2033
        g_return_if_fail (RB_IS_ENTRY_VIEW (view));
 
2034
 
 
2035
        view->priv->playing = TRUE;
 
2036
}
 
2037
 
 
2038
gboolean
 
2039
rb_entry_view_poll_model (RBEntryView *view)
 
2040
{
 
2041
        GTimeVal timeout;
 
2042
 
 
2043
        g_get_current_time (&timeout);
 
2044
        g_time_val_add (&timeout, G_USEC_PER_SEC*0.75);
 
2045
 
 
2046
        return rhythmdb_query_model_poll (view->priv->model, &timeout);
 
2047
}
 
2048