~ubuntu-branches/ubuntu/precise/rhythmbox/precise-201203091205

« back to all changes in this revision

Viewing changes to sources/rb-playlist-source.c

Tags: upstream-0.9.2cvs20060102
ImportĀ upstreamĀ versionĀ 0.9.2cvs20060102

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 */
23
23
 
24
24
#include <config.h>
 
25
 
 
26
#include <unistd.h>
 
27
#include <string.h>
 
28
 
 
29
#include <libxml/tree.h>
 
30
#include <glib/gi18n.h>
25
31
#include <gtk/gtk.h>
26
 
#include <libgnome/gnome-i18n.h>
27
32
#include <libgnomevfs/gnome-vfs-uri.h>
28
33
#include <totem-pl-parser.h>
29
 
#include <libxml/tree.h>
30
 
#include <unistd.h>
31
 
#include <string.h>
32
34
 
33
35
#include "rb-stock-icons.h"
34
36
#include "rb-entry-view.h"
43
45
#include "eel-gconf-extensions.h"
44
46
#include "rb-song-info.h"
45
47
 
46
 
#define RB_PLAYLIST_XML_VERSION "1.0"
47
 
 
48
 
#define RB_PLAYLIST_PLAYLIST (xmlChar *) "playlist"
49
 
#define RB_PLAYLIST_TYPE (xmlChar *) "type"
50
 
#define RB_PLAYLIST_AUTOMATIC (xmlChar *) "automatic"
51
 
#define RB_PLAYLIST_STATIC (xmlChar *) "static"
52
 
#define RB_PLAYLIST_NAME (xmlChar *) "name"
53
 
#define RB_PLAYLIST_LIMIT_COUNT (xmlChar *) "limit-count"
54
 
#define RB_PLAYLIST_LIMIT_SIZE (xmlChar *) "limit-size"
55
 
#define RB_PLAYLIST_LIMIT_TIME (xmlChar *) "limit-time"
56
 
#define RB_PLAYLIST_SORT_KEY (xmlChar *) "sort-key"
57
 
#define RB_PLAYLIST_SORT_DIRECTION (xmlChar *) "sort-direction"
58
 
#define RB_PLAYLIST_LIMIT (xmlChar *) "limit"
59
 
#define RB_PLAYLIST_LOCATION (xmlChar *) "location"
 
48
#include "rb-playlist-xml.h"
 
49
#include "rb-static-playlist-source.h"
 
50
#include "rb-auto-playlist-source.h"
60
51
 
61
52
static void rb_playlist_source_class_init (RBPlaylistSourceClass *klass);
62
53
static void rb_playlist_source_init (RBPlaylistSource *source);
63
54
static GObject *rb_playlist_source_constructor (GType type, guint n_construct_properties,
64
55
                                                GObjectConstructParam *construct_properties);
65
56
static void rb_playlist_source_dispose (GObject *object);
 
57
static void rb_playlist_source_finalize (GObject *object);
66
58
static void rb_playlist_source_set_property (GObject *object,
67
59
                                          guint prop_id,
68
60
                                          const GValue *value,
73
65
                                          GParamSpec *pspec);
74
66
 
75
67
/* source methods */
76
 
static char *impl_get_status (RBSource *source);
77
68
static const char *impl_get_browser_key (RBSource *source);
78
 
static GdkPixbuf *impl_get_pixbuf (RBSource *source);
79
69
static RBEntryView *impl_get_entry_view (RBSource *source);
80
 
static gboolean impl_can_cut (RBSource *asource);
81
 
static GList * impl_cut (RBSource *source);
82
 
static void impl_paste (RBSource *asource, GList *entries);
83
 
static void impl_delete (RBSource *source);
 
70
static void impl_move_to_trash (RBSource *asource);
84
71
static void impl_song_properties (RBSource *source);
85
 
static gboolean impl_receive_drag (RBSource *source, GtkSelectionData *data);
86
72
static gboolean impl_show_popup (RBSource *source);
87
 
static void rb_playlist_source_entry_added_cb (RhythmDB *db, RhythmDBEntry *entry,
88
 
                                               RBPlaylistSource *source);
89
 
static void rb_playlist_source_songs_sort_order_changed_cb (RBEntryView *view,
90
 
                                                            RBPlaylistSource *source);
91
73
 
92
74
static void rb_playlist_source_songs_show_popup_cb (RBEntryView *view, RBPlaylistSource *playlist_view);
93
75
static void rb_playlist_source_drop_cb (GtkWidget *widget,
98
80
                                     guint info,
99
81
                                     guint time,
100
82
                                     gpointer user_data);
101
 
static void rb_playlist_source_add_list_uri (RBPlaylistSource *source,
102
 
                                          GList *list);
103
 
static void rb_playlist_source_do_query (RBPlaylistSource *source,
104
 
                                         GPtrArray *query,
105
 
                                         guint limit_count,
106
 
                                         guint limit_mb,
107
 
                                         guint limit_time);
108
 
static void rb_playlist_source_row_inserted (GtkTreeModel *treemodel,
109
 
                                             GtkTreePath *path,
110
 
                                             GtkTreeIter *iter,
111
 
                                             RBPlaylistSource *source);
112
 
static void rb_playlist_source_non_entry_dropped (GtkTreeModel *model,
113
 
                                                  const char *uri,
114
 
                                                  RBPlaylistSource *source);
 
83
 
 
84
 
 
85
static void rb_playlist_source_row_deleted (GtkTreeModel *model,
 
86
                                            GtkTreePath *path,
 
87
                                            RBPlaylistSource *playlist);
 
88
static void default_show_entry_view_popup (RBPlaylistSource *source,
 
89
                                           RBEntryView *view);
 
90
static void rb_playlist_source_entry_added_cb (RhythmDB *db, RhythmDBEntry *entry,
 
91
                                               RBPlaylistSource *source);
 
92
static void rb_playlist_source_track_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
93
                                                     GtkTreeModel *tree_model, GtkTreeIter *iter,
 
94
                                                     RBPlaylistSource *source);
115
95
 
116
96
#define PLAYLIST_SOURCE_SONGS_POPUP_PATH "/PlaylistViewPopup"
117
97
#define PLAYLIST_SOURCE_POPUP_PATH "/PlaylistSourcePopup"
118
 
#define PLAYLIST_SOURCE_AUTOMATIC_POPUP_PATH "/SmartPlaylistSourcePopup"
119
98
 
120
99
struct RBPlaylistSourcePrivate
121
100
{
122
 
        gboolean disposed;
123
 
        
124
101
        RhythmDB *db;
125
102
 
126
 
        gboolean automatic;
127
103
        GHashTable *entries;
128
104
 
129
105
        RhythmDBEntryType entry_type;
130
106
        RhythmDBQueryModel *model;
131
 
        gboolean query_resetting;
132
107
 
133
108
        GtkWidget *vbox;
134
 
        GdkPixbuf *normal_pixbuf;
135
 
        GdkPixbuf *smartypants_pixbuf;
136
109
 
137
110
        RBEntryView *songs;
138
111
 
142
115
        char *title;
143
116
};
144
117
 
 
118
#define RB_PLAYLIST_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_PLAYLIST_SOURCE, RBPlaylistSourcePrivate))
 
119
 
145
120
enum
146
121
{
147
122
        PROP_0,
148
 
        PROP_AUTOMATIC,
149
 
        PROP_QUERY_MODEL,
 
123
        PROP_DB,
150
124
        PROP_DIRTY,
151
125
        PROP_LOCAL,
152
126
        PROP_ENTRY_TYPE
154
128
 
155
129
static const GtkTargetEntry target_uri [] = { { "text/uri-list", 0, 0 } };
156
130
 
157
 
G_DEFINE_TYPE (RBPlaylistSource, rb_playlist_source, RB_TYPE_SOURCE);
 
131
G_DEFINE_ABSTRACT_TYPE (RBPlaylistSource, rb_playlist_source, RB_TYPE_SOURCE);
158
132
 
159
133
 
160
134
static void
164
138
        RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
165
139
 
166
140
        object_class->dispose = rb_playlist_source_dispose;
 
141
        object_class->finalize = rb_playlist_source_finalize;
167
142
        object_class->constructor = rb_playlist_source_constructor;
168
143
 
169
144
        object_class->set_property = rb_playlist_source_set_property;
170
145
        object_class->get_property = rb_playlist_source_get_property;
171
146
 
172
 
        source_class->impl_get_status = impl_get_status;
173
147
        source_class->impl_get_browser_key = impl_get_browser_key;
174
 
        source_class->impl_get_pixbuf  = impl_get_pixbuf;
175
148
        source_class->impl_get_entry_view = impl_get_entry_view;
176
149
        source_class->impl_can_rename = (RBSourceFeatureFunc) rb_true_function;
177
150
        source_class->impl_can_search = (RBSourceFeatureFunc) rb_false_function;
178
 
        source_class->impl_can_cut = impl_can_cut;
 
151
        source_class->impl_can_cut = (RBSourceFeatureFunc) rb_false_function;
179
152
        source_class->impl_can_copy = (RBSourceFeatureFunc) rb_true_function;
180
 
        source_class->impl_can_delete = impl_can_cut;
181
 
        source_class->impl_cut = impl_cut;
182
 
        source_class->impl_paste = impl_paste;
183
 
        source_class->impl_delete = impl_delete;
 
153
        source_class->impl_can_delete = (RBSourceFeatureFunc) rb_false_function;
 
154
        source_class->impl_can_add_to_queue = (RBSourceFeatureFunc) rb_true_function;
 
155
        source_class->impl_can_move_to_trash = (RBSourceFeatureFunc) rb_true_function;
 
156
        source_class->impl_move_to_trash = impl_move_to_trash;
184
157
        source_class->impl_song_properties = impl_song_properties;
185
158
        source_class->impl_can_pause = (RBSourceFeatureFunc) rb_true_function;
186
159
        source_class->impl_have_url = (RBSourceFeatureFunc) rb_false_function;
187
 
        source_class->impl_receive_drag = impl_receive_drag;
188
160
        source_class->impl_show_popup = impl_show_popup;
189
161
 
190
 
        g_object_class_install_property (object_class,
191
 
                                         PROP_AUTOMATIC,
192
 
                                         g_param_spec_boolean ("automatic",
193
 
                                                               "automatic",
194
 
                                                               "whether this playlist is a smartypants",
195
 
                                                               FALSE,
196
 
                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
162
        klass->impl_show_entry_view_popup = default_show_entry_view_popup;
197
163
 
198
164
        g_object_class_install_property (object_class,
199
 
                                         PROP_QUERY_MODEL,
200
 
                                         g_param_spec_object ("query-model",
201
 
                                                              "query-model",
202
 
                                                              "query model for the playlist",
203
 
                                                              RHYTHMDB_TYPE_QUERY_MODEL,
 
165
                                         PROP_DB,
 
166
                                         g_param_spec_object ("db",
 
167
                                                              "db",
 
168
                                                              "rhythmdb instance",
 
169
                                                              RHYTHMDB_TYPE,
204
170
                                                              G_PARAM_READABLE));
205
 
 
206
171
        g_object_class_install_property (object_class,
207
172
                                         PROP_DIRTY,
208
173
                                         g_param_spec_boolean ("dirty",
210
175
                                                               "whether this playlist should be saved",
211
176
                                                               FALSE,
212
177
                                                               G_PARAM_READABLE));
213
 
 
214
178
        g_object_class_install_property (object_class,
215
179
                                         PROP_LOCAL,
216
180
                                         g_param_spec_boolean ("is-local",
217
181
                                                               "is-local",
218
182
                                                               "whether this playlist is attached to the local library",
219
 
                                                               FALSE,
 
183
                                                               TRUE,
220
184
                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
221
185
        g_object_class_install_property (object_class,
222
186
                                         PROP_ENTRY_TYPE,
226
190
                                                           -1, G_MAXINT, -1,
227
191
                                                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
228
192
 
 
193
        g_type_class_add_private (klass, sizeof (RBPlaylistSourcePrivate));
229
194
}
230
195
 
231
196
static void
232
 
rb_playlist_source_track_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
233
 
                                         GtkTreeModel *tree_model, GtkTreeIter *iter,
234
 
                                         RBPlaylistSource *source)
 
197
rb_playlist_source_init (RBPlaylistSource *source)
235
198
{
236
 
        char *str;
237
 
        int val;
238
 
 
239
 
        gtk_tree_model_get (tree_model, iter, 1, &val, -1);
240
 
 
241
 
        if (val >= 0)
242
 
                str = g_strdup_printf ("%d", val);
243
 
        else
244
 
                str = g_strdup ("");
245
 
 
246
 
        g_object_set (G_OBJECT (renderer), "text", str, NULL);
247
 
        g_free (str);
 
199
        source->priv = RB_PLAYLIST_SOURCE_GET_PRIVATE (source);
248
200
}
249
201
 
250
 
 
251
202
static GObject *
252
203
rb_playlist_source_constructor (GType type, guint n_construct_properties,
253
204
                                GObjectConstructParam *construct_properties)
254
205
{
 
206
        GObject *shell_player;
255
207
        RBPlaylistSource *source;
256
208
        RBPlaylistSourceClass *klass;
257
209
        RBShell *shell;
262
214
                        constructor (type, n_construct_properties, construct_properties));
263
215
 
264
216
        g_object_get (G_OBJECT (source), "shell", &shell, NULL);
265
 
        g_object_get (RB_SHELL (shell), "db", &source->priv->db, NULL);
 
217
        g_object_get (G_OBJECT (shell), "db", &source->priv->db, NULL);
 
218
        shell_player = rb_shell_get_player (shell);
266
219
        g_object_unref (G_OBJECT (shell));
267
220
 
268
 
        g_signal_connect_object (G_OBJECT (source->priv->db), "entry_added",
269
 
                                 G_CALLBACK (rb_playlist_source_entry_added_cb),
270
 
                                 source, 0);
271
 
        g_signal_connect_object (G_OBJECT (source->priv->db), "entry_restored",
272
 
                                 G_CALLBACK (rb_playlist_source_entry_added_cb),
273
 
                                 source, 0);
274
 
 
275
221
        source->priv->vbox = gtk_vbox_new (FALSE, 5);
276
222
 
277
223
        gtk_container_add (GTK_CONTAINER (source), source->priv->vbox);
278
 
                
279
 
        source->priv->model = rhythmdb_query_model_new_empty (source->priv->db);
280
224
 
281
225
        source->priv->entries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
282
 
                
283
 
        source->priv->songs = rb_entry_view_new (source->priv->db, NULL, TRUE, TRUE);
284
 
 
285
 
        /* watch these to find out when things were dropping into the entry view */
286
 
        g_signal_connect_object (G_OBJECT (source->priv->model), "row-inserted",
287
 
                         G_CALLBACK (rb_playlist_source_row_inserted),
288
 
                         source, 0);
289
 
        g_signal_connect_object (G_OBJECT (source->priv->model), "non-entry-dropped",
290
 
                         G_CALLBACK (rb_playlist_source_non_entry_dropped),
291
 
                         source, 0);
292
 
 
293
 
        rb_entry_view_set_model (source->priv->songs, RHYTHMDB_QUERY_MODEL (source->priv->model));
 
226
 
 
227
        g_signal_connect_object (G_OBJECT (source->priv->db), "entry_added",
 
228
                                 G_CALLBACK (rb_playlist_source_entry_added_cb),
 
229
                                 source, 0);
 
230
        g_signal_connect_object (G_OBJECT (source->priv->db), "entry_restored",
 
231
                                 G_CALLBACK (rb_playlist_source_entry_added_cb),
 
232
                                 source, 0);
 
233
 
 
234
        source->priv->songs = rb_entry_view_new (source->priv->db, shell_player, 
 
235
                                                 NULL, TRUE, TRUE);
 
236
 
 
237
        rb_playlist_source_set_query_model (source, rhythmdb_query_model_new_empty (source->priv->db));
294
238
 
295
239
        {
 
240
                const char *title = _("Trac_k");
 
241
                const char *strings[3] = {0};
 
242
 
296
243
                GtkTreeViewColumn *column = gtk_tree_view_column_new ();
297
244
                GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
298
245
                gtk_tree_view_column_pack_start (column, renderer, TRUE);
299
246
 
300
247
                gtk_tree_view_column_set_clickable (column, TRUE);
 
248
                gtk_tree_view_column_set_resizable (column, TRUE);
 
249
                gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
 
250
 
 
251
                strings[0] = title;
 
252
                strings[1] = "9999";
 
253
                rb_entry_view_set_fixed_column_width (source->priv->songs, column, renderer,
 
254
                                                      strings);
301
255
                gtk_tree_view_column_set_cell_data_func (column, renderer,
302
256
                                                         (GtkTreeCellDataFunc)
303
257
                                                         rb_playlist_source_track_cell_data_func,
304
258
                                                         source, NULL);
305
 
                rb_entry_view_append_column_custom (source->priv->songs, column, 
306
 
                                                    _("Trac_k"), "PlaylistTrack", NULL, NULL);
 
259
                rb_entry_view_append_column_custom (source->priv->songs, column, title,
 
260
                                                    "PlaylistTrack", NULL, NULL);
307
261
        }
308
262
 
309
263
        rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_TITLE);
316
270
        rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_PLAY_COUNT);
317
271
        rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_LAST_PLAYED);
318
272
        rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_FIRST_SEEN);
319
 
 
320
 
        g_signal_connect_object (G_OBJECT (source->priv->songs), "show_popup",
321
 
                                 G_CALLBACK (rb_playlist_source_songs_show_popup_cb), source, 0);
322
 
        g_signal_connect_object (G_OBJECT (source->priv->songs), "sort-order-changed",
323
 
                                 G_CALLBACK (rb_playlist_source_songs_sort_order_changed_cb), source, 0);
324
 
                
325
 
        g_signal_connect_object (G_OBJECT (source->priv->songs), "drag_data_received",
326
 
                                 G_CALLBACK (rb_playlist_source_drop_cb), source, 0);
327
 
        gtk_drag_dest_set (GTK_WIDGET (source->priv->songs), GTK_DEST_DEFAULT_ALL,
328
 
                           target_uri, G_N_ELEMENTS (target_uri), GDK_ACTION_COPY);
329
 
 
330
 
        {
331
 
                GtkIconTheme *theme = gtk_icon_theme_get_default();
332
 
                gint size;
333
 
 
334
 
                gtk_icon_size_lookup (GTK_ICON_SIZE_LARGE_TOOLBAR, &size, NULL);
335
 
                source->priv->normal_pixbuf = gtk_icon_theme_load_icon (theme,
336
 
                                                                        "stock_playlist",
337
 
                                                                        size,
338
 
                                                                        0, NULL);
339
 
                source->priv->smartypants_pixbuf = gtk_icon_theme_load_icon (theme,
340
 
                                                                             "stock_smart-playlist",
341
 
                                                                             size,
342
 
                                                                             0, NULL);
343
 
        }
344
 
                
345
273
        rb_entry_view_set_columns_clickable (source->priv->songs, FALSE);
346
 
        source->priv->query_resetting = FALSE;
 
274
 
 
275
        rb_playlist_source_setup_entry_view (source, source->priv->songs);
347
276
 
348
277
        gtk_box_pack_start_defaults (GTK_BOX (source->priv->vbox), GTK_WIDGET (source->priv->songs));
349
278
                
350
279
        gtk_widget_show_all (GTK_WIDGET (source));
351
 
                        
 
280
 
352
281
        return G_OBJECT (source);
353
282
}
354
283
 
355
284
static void
356
 
rb_playlist_source_songs_show_popup_cb (RBEntryView *view,
357
 
                                        RBPlaylistSource *playlist_view)
358
 
{
359
 
        _rb_source_show_popup (RB_SOURCE (playlist_view), 
360
 
                               PLAYLIST_SOURCE_SONGS_POPUP_PATH);
361
 
}
362
 
 
363
 
static void
364
 
rb_playlist_source_init (RBPlaylistSource *source)
365
 
{
366
 
        source->priv = g_new0 (RBPlaylistSourcePrivate, 1);
367
 
}
368
 
 
369
 
static void
370
285
rb_playlist_source_dispose (GObject *object)
371
286
{
372
 
        RBPlaylistSource *source;
373
 
        source = RB_PLAYLIST_SOURCE (object);
374
 
 
375
 
        if (source->priv) {
376
 
                g_hash_table_destroy (source->priv->entries);
 
287
        RBPlaylistSource *source = RB_PLAYLIST_SOURCE (object);
 
288
        if (source->priv->db) {
377
289
                g_object_unref (source->priv->db);
378
 
                g_free (source->priv->title);
379
 
        
380
 
                g_free (source->priv);
381
 
                source->priv = NULL;
 
290
                source->priv->db = NULL;
382
291
        }
383
292
 
384
293
        G_OBJECT_CLASS (rb_playlist_source_parent_class)->dispose (object);
385
294
}
386
295
 
387
296
static void
 
297
rb_playlist_source_finalize (GObject *object)
 
298
{
 
299
        RBPlaylistSource *source;
 
300
 
 
301
        g_return_if_fail (object != NULL);
 
302
        g_return_if_fail (RB_IS_PLAYLIST_SOURCE (object));
 
303
 
 
304
        source = RB_PLAYLIST_SOURCE (object);
 
305
        g_return_if_fail (source->priv != NULL);
 
306
 
 
307
        g_hash_table_destroy (source->priv->entries);
 
308
 
 
309
        g_free (source->priv->title);
 
310
        source->priv = NULL;
 
311
 
 
312
        G_OBJECT_CLASS (rb_playlist_source_parent_class)->finalize (object);
 
313
}
 
314
 
 
315
static void
388
316
rb_playlist_source_set_property (GObject *object,
389
317
                              guint prop_id,
390
318
                              const GValue *value,
394
322
 
395
323
        switch (prop_id)
396
324
        {
397
 
        case PROP_AUTOMATIC:
398
 
                source->priv->automatic = g_value_get_boolean (value);
399
 
                break;
400
325
        case PROP_LOCAL:
401
326
                source->priv->is_local = g_value_get_boolean (value);
402
327
                break;
419
344
 
420
345
        switch (prop_id)
421
346
        {
422
 
        case PROP_AUTOMATIC:
423
 
                g_value_set_boolean (value, source->priv->automatic);
 
347
        case PROP_DB:
 
348
                g_value_set_object (value, source->priv->db);
424
349
                break;
425
350
        case PROP_DIRTY:
426
351
                g_value_set_boolean (value, source->priv->dirty);
427
352
                break;
428
 
        case PROP_QUERY_MODEL:
429
 
                g_value_set_object (value, source->priv->model);
430
 
                break;
431
353
        case PROP_LOCAL:
432
354
                g_value_set_boolean (value, source->priv->is_local);
433
355
                break;
440
362
        }
441
363
}
442
364
 
443
 
RBSource *
444
 
rb_playlist_source_new (RBShell *shell, gboolean automatic, gboolean local, RhythmDBEntryType entry_type)
445
 
{
446
 
        RBSource *source;
447
 
        
448
 
        source = RB_SOURCE (g_object_new (RB_TYPE_PLAYLIST_SOURCE,
449
 
                                          "name", _("Unknown"),
450
 
                                          "shell", shell,
451
 
                                          "automatic", automatic,
452
 
                                          "is-local", local,
453
 
                                          "entry-type", entry_type,
454
 
                                          NULL));
455
 
 
456
 
        return source;
457
 
}
458
 
 
459
 
static void
460
 
rb_playlist_source_entry_added_cb (RhythmDB *db, RhythmDBEntry *entry,
461
 
                                   RBPlaylistSource *source)
462
 
{
463
 
        const char *location;
464
 
 
465
 
        if (source->priv->automatic)
466
 
                return;
467
 
        
468
 
        location = entry->location;
469
 
        if (g_hash_table_lookup (source->priv->entries, location)) {
470
 
                rhythmdb_query_model_add_entry (source->priv->model, entry);
471
 
                source->priv->dirty = TRUE;
472
 
        }
473
 
}
474
 
 
475
 
void
476
 
rb_playlist_source_set_query (RBPlaylistSource *source,
477
 
                              GPtrArray *query,
478
 
                              guint limit_count,
479
 
                              guint limit_mb,
480
 
                              guint limit_time,
481
 
                              const char *sort_key,
482
 
                              gint sort_direction)
483
 
{
484
 
        g_assert (source->priv->automatic);
485
 
 
486
 
        source->priv->query_resetting = TRUE;
487
 
 
488
 
        /* playlists that aren't limited, with a particular sort order, are user-orderable */
489
 
        rb_entry_view_set_columns_clickable (source->priv->songs, (limit_count == 0 && limit_mb == 0));
490
 
        rb_entry_view_set_sorting_order (source->priv->songs, sort_key, sort_direction);
491
 
 
492
 
        rb_playlist_source_do_query (source, query, limit_count, limit_mb, limit_time);
493
 
        rhythmdb_query_free (query);
494
 
        source->priv->query_resetting = FALSE;
495
 
}
496
 
 
497
 
void
498
 
rb_playlist_source_get_query (RBPlaylistSource *source,
499
 
                              GPtrArray **query,
500
 
                              guint *limit_count,
501
 
                              guint *limit_mb,
502
 
                              guint *limit_time,
503
 
                              const char **sort_key,
504
 
                              gint *sort_direction)
505
 
{
506
 
        g_assert (source->priv->automatic);
507
 
 
508
 
        g_object_get (G_OBJECT (source->priv->model),
509
 
                      "query", query,
510
 
                      "max-count", limit_count,
511
 
                      "max-size", limit_mb,
512
 
                      "max-time", limit_time,
513
 
                      NULL);
514
 
 
515
 
        rb_entry_view_get_sorting_order (source->priv->songs, sort_key, sort_direction);
516
 
}
517
 
 
518
 
static char *
519
 
impl_get_status (RBSource *asource)
520
 
{
521
 
 
522
 
        RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource);
523
 
        gchar *status;
524
 
 
525
 
        status = rhythmdb_compute_status_normal (rb_entry_view_get_num_entries (source->priv->songs),
526
 
                                                 rb_entry_view_get_duration (source->priv->songs),
527
 
                                                 rb_entry_view_get_total_size (source->priv->songs));
528
 
        return status;
529
 
}
 
365
static void
 
366
default_show_entry_view_popup (RBPlaylistSource *source, RBEntryView *view)
 
367
{
 
368
        _rb_source_show_popup (RB_SOURCE (source), PLAYLIST_SOURCE_SONGS_POPUP_PATH);
 
369
}
 
370
 
 
371
static void
 
372
rb_playlist_source_songs_show_popup_cb (RBEntryView *view,
 
373
                                        RBPlaylistSource *source)
 
374
{
 
375
        RBPlaylistSourceClass *klass = RB_PLAYLIST_SOURCE_GET_CLASS (source);
 
376
        if (klass->impl_show_entry_view_popup)
 
377
                klass->impl_show_entry_view_popup (source, view);
 
378
}
 
379
 
530
380
 
531
381
static const char *
532
382
impl_get_browser_key (RBSource *source)
534
384
        return NULL;
535
385
}
536
386
 
537
 
static GdkPixbuf *
538
 
impl_get_pixbuf (RBSource *asource)
539
 
{
540
 
        RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource);
541
 
 
542
 
        return source->priv->automatic ? source->priv->smartypants_pixbuf : source->priv->normal_pixbuf;
543
 
}
544
 
 
545
387
static RBEntryView *
546
388
impl_get_entry_view (RBSource *asource)
547
389
{
550
392
        return source->priv->songs;
551
393
}
552
394
 
553
 
static gboolean
554
 
impl_can_cut (RBSource *asource)
555
 
{
556
 
        RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource);
557
 
        return !source->priv->automatic;
558
 
}
559
 
 
560
 
static GList *
561
 
impl_cut (RBSource *asource)
562
 
{
563
 
        RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource);
564
 
        GList *sel = rb_entry_view_get_selected_entries (source->priv->songs);
565
 
        GList *tem;
566
 
 
567
 
        for (tem = sel; tem; tem = tem->next)
568
 
                rb_playlist_source_remove_entry (source, tem->data);
569
 
 
570
 
        return sel;
571
 
}
572
 
 
573
 
static void
574
 
impl_paste (RBSource *asource, GList *entries)
575
 
{
576
 
        RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource);
577
 
 
578
 
        for (; entries; entries = g_list_next (entries))
579
 
                rb_playlist_source_add_entry (source, entries->data);
580
 
}
581
 
 
582
 
static void
583
 
impl_delete (RBSource *asource)
 
395
static void
 
396
impl_move_to_trash (RBSource *asource)
584
397
{
585
398
        RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource);
586
399
        GList *sel, *tem;
 
400
 
587
401
        sel = rb_entry_view_get_selected_entries (source->priv->songs);
588
 
        for (tem = sel; tem != NULL; tem = tem->next)
589
 
                rb_playlist_source_remove_entry (source, tem->data);
 
402
        for (tem = sel; tem != NULL; tem = tem->next) {
 
403
                rhythmdb_entry_move_to_trash (source->priv->db,
 
404
                                (RhythmDBEntry *) tem->data);
 
405
                rhythmdb_commit (source->priv->db);
 
406
        }
590
407
        g_list_free (sel);
591
408
}
592
409
 
598
415
 
599
416
        g_return_if_fail (source->priv->songs != NULL);
600
417
 
601
 
        song_info = rb_song_info_new (source->priv->songs);
 
418
        song_info = rb_song_info_new (asource, NULL);
602
419
        if (song_info)
603
420
                gtk_widget_show_all (song_info);
604
421
        else
605
422
                rb_debug ("failed to create dialog, or no selection!");
606
423
}
607
424
 
608
 
static RhythmDBPropType
609
 
rb_playlist_source_drag_atom_to_prop (GdkAtom smasher)
610
 
{
611
 
        if (smasher == gdk_atom_intern ("text/x-rhythmbox-album", TRUE))
612
 
                return RHYTHMDB_PROP_ALBUM;
613
 
        else if (smasher == gdk_atom_intern ("text/x-rhythmbox-artist", TRUE))
614
 
                return RHYTHMDB_PROP_ARTIST;
615
 
        else if (smasher == gdk_atom_intern ("text/x-rhythmbox-genre", TRUE))
616
 
                return RHYTHMDB_PROP_GENRE;
617
 
        else {
618
 
                g_assert_not_reached ();
619
 
                return 0;
620
 
        }
621
 
}
622
 
 
623
 
static gboolean
624
 
impl_receive_drag (RBSource *asource, GtkSelectionData *data)
625
 
{
626
 
        GList *list;
627
 
        RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource);
628
 
 
629
 
        if (data->type == gdk_atom_intern ("text/uri-list", TRUE)) {
630
 
                list = gnome_vfs_uri_list_parse ((char *) data->data);
631
 
 
632
 
                if (list != NULL)
633
 
                        rb_playlist_source_add_list_uri (source, list);
634
 
                else
635
 
                        return FALSE;
636
 
 
637
 
        } else {
638
 
                GPtrArray *subquery = NULL;
639
 
                gchar **names = g_strsplit ((char *)data->data, "\r\n", 0);
640
 
                guint propid = rb_playlist_source_drag_atom_to_prop (data->type);
641
 
                int i;
642
 
 
643
 
                for (i=0; names[i]; i++) {
644
 
                        if (subquery == NULL) 
645
 
                                subquery = rhythmdb_query_parse (source->priv->db,
646
 
                                                                 RHYTHMDB_QUERY_PROP_EQUALS,
647
 
                                                                 propid,
648
 
                                                                 names[i],
649
 
                                                                 RHYTHMDB_QUERY_END);
650
 
                        else
651
 
                                rhythmdb_query_append (source->priv->db,
652
 
                                                       subquery,
653
 
                                                       RHYTHMDB_QUERY_DISJUNCTION,
654
 
                                                       RHYTHMDB_QUERY_PROP_EQUALS,
655
 
                                                       propid,
656
 
                                                       names[i],
657
 
                                                       RHYTHMDB_QUERY_END);
658
 
                }
659
 
 
660
 
                g_strfreev (names);
661
 
 
662
 
                if (subquery) {
663
 
                        RhythmDBEntryType qtype = RHYTHMDB_ENTRY_TYPE_SONG;
664
 
                        GPtrArray *query;
665
 
                        
666
 
                        if (source->priv->entry_type != -1)
667
 
                                qtype = source->priv->entry_type;
668
 
 
669
 
                        query = rhythmdb_query_parse (source->priv->db,
670
 
                                                      RHYTHMDB_QUERY_PROP_EQUALS,
671
 
                                                      RHYTHMDB_PROP_TYPE,
672
 
                                                      qtype,
673
 
                                                      RHYTHMDB_QUERY_SUBQUERY,
674
 
                                                      subquery,
675
 
                                                      RHYTHMDB_QUERY_END);
676
 
                        rb_playlist_source_set_query (source, query, 0, 0, 0, NULL, 0);
677
 
                }
678
 
        }
679
 
 
680
 
        return TRUE;
681
 
}
682
 
 
683
425
static gboolean
684
426
impl_show_popup (RBSource *asource)
685
427
{
686
 
        RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource);
687
 
 
688
 
        if (source->priv->automatic) {
689
 
                _rb_source_show_popup (RB_SOURCE (asource), PLAYLIST_SOURCE_AUTOMATIC_POPUP_PATH);
690
 
        } else {
691
 
                _rb_source_show_popup (RB_SOURCE (asource), PLAYLIST_SOURCE_POPUP_PATH);
692
 
        }
 
428
        _rb_source_show_popup (asource, PLAYLIST_SOURCE_POPUP_PATH);
693
429
        return TRUE;
694
430
}
695
431
 
714
450
        if (target == GDK_NONE)
715
451
                return;
716
452
 
717
 
        impl_receive_drag (RB_SOURCE (source), data);
 
453
        rb_source_receive_drag (RB_SOURCE (source), data);
718
454
 
719
455
        gtk_drag_finish (context, TRUE, FALSE, time);
720
456
}
721
457
 
722
 
 
723
 
 
724
 
static void
725
 
rb_playlist_source_add_location_swapped (const char *uri, RBPlaylistSource *source)
726
 
{
727
 
        rb_playlist_source_add_location (source, uri);
728
 
}
729
 
 
730
 
void
731
 
rb_playlist_source_add_location (RBPlaylistSource *source,
732
 
                                 const char *location)
733
 
{
734
 
        RhythmDBEntry *entry;
735
 
        
736
 
        entry = rhythmdb_entry_lookup_by_location (source->priv->db, location);
737
 
        if (entry != NULL && 
738
 
            source->priv->entry_type != -1 &&
739
 
            rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TYPE) != source->priv->entry_type) {
740
 
                rb_debug ("attempting to add an entry of the wrong type to playlist");
741
 
                return;
742
 
        }
743
 
 
744
 
        if (rb_uri_is_directory (location)) {
745
 
                rb_uri_handle_recursively (location,
746
 
                                           (GFunc) rb_playlist_source_add_location_swapped,
747
 
                                           NULL,
748
 
                                           source);
749
 
        } else {
750
 
                if (g_hash_table_lookup (source->priv->entries, location)) {
751
 
                        return;
752
 
                }
753
 
 
754
 
                g_hash_table_insert (source->priv->entries,
755
 
                                     g_strdup (location), GINT_TO_POINTER (1));
756
 
 
757
 
                if (entry != NULL) {
758
 
                        rhythmdb_query_model_add_entry (source->priv->model, entry);
759
 
 
760
 
                        source->priv->dirty = TRUE;
761
 
                }
762
 
        }
763
 
}
764
 
 
765
 
void
766
 
rb_playlist_source_add_locations (RBPlaylistSource *source,
767
 
                                  GList *locations)
768
 
{
769
 
        GList *l;
770
 
 
771
 
        for (l = locations; l; l = l->next) {
772
 
                const gchar *uri = (const gchar *)l->data;
773
 
                
774
 
                rb_playlist_source_add_location (RB_PLAYLIST_SOURCE (source), 
775
 
                                                 uri);
776
 
        }
777
 
}
778
 
 
779
 
 
780
 
void
781
 
rb_playlist_source_remove_location (RBPlaylistSource *source,
782
 
                                    const char *location)
783
 
{
784
 
        RhythmDBEntry *entry;
785
 
 
786
 
        g_return_if_fail (g_hash_table_lookup (source->priv->entries, location) != NULL);
787
 
 
788
 
        g_hash_table_remove (source->priv->entries, location);
789
 
        entry = rhythmdb_entry_lookup_by_location (source->priv->db, location);
790
 
        if (entry != NULL) {
791
 
                gboolean removed;
792
 
                
793
 
                removed = rhythmdb_query_model_remove_entry (source->priv->model, entry);
794
 
                g_assert (removed); /* if this fails, the model and the playlist are out of sync */
795
 
                source->priv->dirty = TRUE;
796
 
        }
797
 
}
798
 
 
799
 
void
800
 
rb_playlist_source_add_entry (RBPlaylistSource *source,
801
 
                              RhythmDBEntry *entry)
802
 
{
803
 
        rb_playlist_source_add_location (source, entry->location);
804
 
}
805
 
 
806
 
void
807
 
rb_playlist_source_remove_entry (RBPlaylistSource *source,
808
 
                                 RhythmDBEntry *entry)
809
 
{
810
 
        rb_playlist_source_remove_location (source, entry->location);
811
 
}
812
 
 
813
 
static void
814
 
rb_playlist_source_add_list_uri (RBPlaylistSource *source,
815
 
                                 GList *list)
816
 
{
817
 
        GList *i, *uri_list = NULL;
818
 
 
819
 
        g_return_if_fail (list != NULL);
820
 
 
821
 
        for (i = list; i != NULL; i = g_list_next (i))
822
 
                uri_list = g_list_prepend (uri_list, gnome_vfs_uri_to_string ((const GnomeVFSURI *) i->data, 0));
823
 
        uri_list = g_list_reverse (uri_list);
824
 
 
825
 
        gnome_vfs_uri_list_free (list);
826
 
 
827
 
        if (uri_list == NULL)
828
 
                return;
829
 
 
830
 
        for (i = uri_list; i != NULL; i = i->next) {
831
 
                char *uri = i->data;
832
 
                if (uri != NULL) {
833
 
                        rhythmdb_add_uri (source->priv->db, uri);
834
 
                        rb_playlist_source_add_location (source, uri);
835
 
                }
836
 
 
837
 
                g_free (uri);
838
 
        }
839
 
 
840
 
        g_list_free (uri_list);
841
 
}
842
 
 
843
458
static void
844
459
playlist_iter_func (GtkTreeModel *model, GtkTreeIter *iter, char **uri, char **title, gpointer user_data)
845
460
{
858
473
        GError *error = NULL;
859
474
        char *name;
860
475
 
861
 
 
862
476
        rb_debug ("saving playlist");
863
477
        playlist = totem_pl_parser_new ();
864
478
 
867
481
        totem_pl_parser_write_with_title (playlist, GTK_TREE_MODEL (source->priv->model),
868
482
                                          playlist_iter_func, uri, name,
869
483
                                          TOTEM_PL_PARSER_PLS, NULL, &error);
 
484
        g_free (name);
870
485
        if (error != NULL)
871
486
                rb_error_dialog (NULL, _("Couldn't save playlist"),
872
487
                                 "%s", error->message);
873
488
}
874
489
 
875
 
static void
 
490
static gboolean
876
491
burn_playlist_iter_func (GtkTreeModel *model, GtkTreeIter *iter, char **uri, char **artist, char **title, gulong *duration)
877
492
{
878
493
        RhythmDBEntry *entry;
883
498
        *artist = g_strdup (rb_refstring_get (entry->artist));
884
499
        *title = g_strdup (rb_refstring_get (entry->title));
885
500
        *duration = entry->duration;
 
501
 
 
502
        return TRUE;
886
503
}
887
504
 
888
505
void
889
506
rb_playlist_source_burn_playlist (RBPlaylistSource *source)
890
507
{
891
508
        GtkWidget *recorder;
 
509
        GtkWidget *parent;
892
510
        char *name;
893
511
        RBShell *shell;
 
512
        gboolean res;
 
513
        GError *error;
894
514
 
895
515
        /* don't burn if the playlist is empty */
896
516
        if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (source->priv->model), NULL) == 0)
900
520
 
901
521
        g_object_get (source, "name", &name, "shell", &shell, NULL);
902
522
 
903
 
        recorder = rb_playlist_source_recorder_new (gtk_widget_get_toplevel (GTK_WIDGET (source)),
 
523
        parent = gtk_widget_get_toplevel (GTK_WIDGET (source));
 
524
        recorder = rb_playlist_source_recorder_new (parent,
904
525
                                                    shell,
905
526
                                                    name);
906
527
        g_object_unref (shell);
907
528
        g_free (name);
908
529
 
909
 
        rb_playlist_source_recorder_add_from_model (RB_PLAYLIST_SOURCE_RECORDER (recorder),
910
 
                                                    GTK_TREE_MODEL (source->priv->model),
911
 
                                                    burn_playlist_iter_func,
912
 
                                                    NULL);
 
530
        error = NULL;
 
531
        res = rb_playlist_source_recorder_add_from_model (RB_PLAYLIST_SOURCE_RECORDER (recorder),
 
532
                                                          GTK_TREE_MODEL (source->priv->model),
 
533
                                                          burn_playlist_iter_func,
 
534
                                                          &error);
 
535
        if (! res) {
 
536
                rb_error_dialog (GTK_WINDOW (parent),
 
537
                                 _("Unable to create audio CD"),
 
538
                                 "%s", error->message);
 
539
                g_error_free (error);
 
540
 
 
541
                gtk_widget_destroy (recorder);
 
542
 
 
543
                return;
 
544
        }
913
545
 
914
546
        g_signal_connect (recorder,
915
547
                          "response",
923
555
rb_playlist_source_new_from_xml (RBShell *shell,
924
556
                                 xmlNodePtr node)
925
557
{
926
 
        RBPlaylistSource *source;
927
 
        xmlNodePtr child;
928
558
        xmlChar *tmp;
929
 
 
930
 
        source = RB_PLAYLIST_SOURCE (rb_playlist_source_new (shell, FALSE, TRUE, RHYTHMDB_ENTRY_TYPE_SONG));
 
559
        RBSource *source;
931
560
 
932
561
        tmp = xmlGetProp (node, RB_PLAYLIST_TYPE);
933
562
        if (!xmlStrcmp (tmp, RB_PLAYLIST_AUTOMATIC))
934
 
                source->priv->automatic = TRUE;
935
 
        g_free (tmp);
936
 
 
 
563
                source = rb_auto_playlist_source_new_from_xml (shell, node);
 
564
        else if (!xmlStrcmp (tmp, RB_PLAYLIST_STATIC))
 
565
                source = rb_static_playlist_source_new_from_xml (shell, node);
 
566
        else
 
567
                g_assert_not_reached ();
 
568
        
937
569
        tmp = xmlGetProp (node, RB_PLAYLIST_NAME);
938
570
        g_object_set (G_OBJECT (source), "name", tmp, NULL);
939
571
        g_free (tmp);
940
572
 
941
 
        if (source->priv->automatic) {
942
 
                GPtrArray *query;
943
 
                gint limit_count = 0, limit_mb = 0, limit_time = 0;
944
 
                gchar *sort_key = NULL;
945
 
                gint sort_direction = 0;
946
 
 
947
 
                child = node->children;
948
 
                while (xmlNodeIsText (child))
949
 
                        child = child->next;
950
 
 
951
 
                query = rhythmdb_query_deserialize (source->priv->db, child);
952
 
                tmp = xmlGetProp (node, RB_PLAYLIST_LIMIT_COUNT);
953
 
                if (!tmp) /* Backwards compatibility */
954
 
                        tmp = xmlGetProp (node, RB_PLAYLIST_LIMIT);
955
 
                if (tmp) {
956
 
                        limit_count = atoi ((char*) tmp);
957
 
                        g_free (tmp);
958
 
                }
959
 
                tmp = xmlGetProp (node, RB_PLAYLIST_LIMIT_SIZE);
960
 
                if (tmp) {
961
 
                        limit_mb = atoi ((char*) tmp);
962
 
                        g_free (tmp);
963
 
                }
964
 
                tmp = xmlGetProp (node, RB_PLAYLIST_LIMIT_TIME);
965
 
                if (tmp) {
966
 
                        limit_time = atoi ((char*) tmp);
967
 
                        g_free (tmp);
968
 
                }
969
 
 
970
 
                sort_key = (gchar*) xmlGetProp (node, RB_PLAYLIST_SORT_KEY);
971
 
                if (sort_key && *sort_key) {
972
 
                        tmp = xmlGetProp (node, RB_PLAYLIST_SORT_DIRECTION);
973
 
                        if (tmp) {
974
 
                                sort_direction = atoi ((char*) tmp);
975
 
                                g_free (tmp);
976
 
                        }
977
 
                } else {
978
 
                        g_free (sort_key);
979
 
                        sort_key = NULL;
980
 
                        sort_direction = 0;
981
 
                }
982
 
 
983
 
                rb_playlist_source_set_query (source, query,
984
 
                                              limit_count,
985
 
                                              limit_mb,
986
 
                                              limit_time,
987
 
                                              sort_key,
988
 
                                              sort_direction);
989
 
                g_free (sort_key);
990
 
        } else {
991
 
                for (child = node->children; child; child = child->next) {
992
 
                        xmlChar *location;
993
 
 
994
 
                        if (xmlNodeIsText (child))
995
 
                                continue;
996
 
                
997
 
                        if (xmlStrcmp (child->name, RB_PLAYLIST_LOCATION))
998
 
                                continue;
999
 
                
1000
 
                        location = xmlNodeGetContent (child);
1001
 
                        rb_playlist_source_add_location (source,
1002
 
                                                         (char *) location);
1003
 
                }
1004
 
        }
1005
 
 
1006
 
        return RB_SOURCE (source);
 
573
        return source;
1007
574
}
1008
575
 
1009
576
void
1011
578
{
1012
579
        xmlNodePtr node;
1013
580
        xmlChar *name;
1014
 
        GtkTreeIter iter;
 
581
        RBPlaylistSourceClass *klass = RB_PLAYLIST_SOURCE_GET_CLASS (source);
1015
582
 
1016
583
        node = xmlNewChild (parent_node, NULL, RB_PLAYLIST_PLAYLIST, NULL);
1017
584
        g_object_get (G_OBJECT (source), "name", &name, NULL);
1018
585
        xmlSetProp (node, RB_PLAYLIST_NAME, name);
1019
 
        xmlSetProp (node, RB_PLAYLIST_TYPE, source->priv->automatic ? RB_PLAYLIST_AUTOMATIC : RB_PLAYLIST_STATIC);
1020
 
 
1021
 
        if (!source->priv->automatic) {
1022
 
                if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (source->priv->model),
1023
 
                                                    &iter))
1024
 
                        return;
1025
 
                do { 
1026
 
                        xmlNodePtr child_node = xmlNewChild (node, NULL, RB_PLAYLIST_LOCATION, NULL);
1027
 
                        RhythmDBEntry *entry;
1028
 
                        xmlChar *encoded;
1029
 
 
1030
 
                        gtk_tree_model_get (GTK_TREE_MODEL (source->priv->model), &iter, 0, &entry, -1);
1031
 
 
1032
 
                        encoded = xmlEncodeEntitiesReentrant (NULL, BAD_CAST entry->location);
1033
 
 
1034
 
                        xmlNodeSetContent (child_node, encoded);
1035
 
                        g_free (encoded);
1036
 
                } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (source->priv->model),
1037
 
                                                   &iter));
1038
 
        } else {
1039
 
                GPtrArray *query;
1040
 
                guint max_count, max_size_mb, max_time;
1041
 
                const gchar *sort_key;
1042
 
                gint sort_direction;
1043
 
                gchar *temp_str;
1044
 
 
1045
 
                rb_playlist_source_get_query (source,
1046
 
                                              &query,
1047
 
                                              &max_count, &max_size_mb, &max_time,
1048
 
                                              &sort_key, &sort_direction);
1049
 
                temp_str = g_strdup_printf ("%d", max_count);
1050
 
                xmlSetProp (node, RB_PLAYLIST_LIMIT_COUNT, BAD_CAST temp_str);
1051
 
                g_free (temp_str);
1052
 
                temp_str = g_strdup_printf ("%d", max_size_mb);
1053
 
                xmlSetProp (node, RB_PLAYLIST_LIMIT_SIZE, BAD_CAST temp_str);
1054
 
                g_free (temp_str);
1055
 
                temp_str = g_strdup_printf ("%d", max_time);
1056
 
                xmlSetProp (node, RB_PLAYLIST_LIMIT_TIME, BAD_CAST temp_str);
1057
 
                g_free (temp_str);
1058
 
 
1059
 
                if (sort_key && *sort_key) {
1060
 
                        xmlSetProp (node, RB_PLAYLIST_SORT_KEY, BAD_CAST sort_key);
1061
 
                        temp_str = g_strdup_printf ("%d", sort_direction);
1062
 
                        xmlSetProp (node, RB_PLAYLIST_SORT_DIRECTION, BAD_CAST temp_str);
1063
 
                        g_free (temp_str);
1064
 
                }
1065
 
 
1066
 
                rhythmdb_query_serialize (source->priv->db, query, node);
1067
 
        }
 
586
        g_free (name);
 
587
 
 
588
        klass->impl_save_contents_to_xml (source, node);
1068
589
 
1069
590
        source->priv->dirty = FALSE;
1070
591
}
1071
592
 
1072
 
static void
1073
 
rb_playlist_source_songs_sort_order_changed_cb (RBEntryView *view, RBPlaylistSource *source)
1074
 
{
1075
 
        GPtrArray *query;
1076
 
        guint limit_count, limit_mb, limit_time;
1077
 
 
1078
 
        g_assert (source->priv->automatic);
1079
 
 
1080
 
        /* don't process this if we are in the middle of setting a query */
1081
 
        if (source->priv->query_resetting)
1082
 
                return;
1083
 
        rb_debug ("sort order changed");
1084
 
 
1085
 
        /* need to re-run query with the same settings*/
1086
 
        g_object_get (G_OBJECT (source->priv->model),
1087
 
                      "query", &query,
1088
 
                      "max-count", &limit_count,
1089
 
                      "max-size", &limit_mb,
1090
 
                      "max-size", &limit_time,
1091
 
                      NULL);
1092
 
 
1093
 
        rb_playlist_source_do_query (source, query, limit_count, limit_mb, limit_time);
1094
 
        rhythmdb_query_free (query);
1095
 
}
1096
 
 
1097
 
static void
1098
 
rb_playlist_source_do_query (RBPlaylistSource *source,
1099
 
                              GPtrArray *query,
1100
 
                              guint limit_count,
1101
 
                              guint limit_mb,
1102
 
                              guint limit_time)
1103
 
{
1104
 
        g_assert (source->priv->automatic);
1105
 
 
1106
 
        source->priv->model = g_object_new (RHYTHMDB_TYPE_QUERY_MODEL,
1107
 
                                    "db", source->priv->db,
1108
 
                                    "max-count", limit_count,
1109
 
                                    "max-size", limit_mb, 
1110
 
                                    "max-time", limit_time, 
1111
 
                                    NULL);
1112
 
 
1113
 
        rb_entry_view_set_model (source->priv->songs, source->priv->model);
1114
 
        rhythmdb_do_full_query_async_parsed (source->priv->db, GTK_TREE_MODEL (source->priv->model), query);
1115
 
 
1116
 
        /* emit notification the the property has changed */
1117
 
        g_object_notify (G_OBJECT (source), "query-model");
1118
 
}
1119
 
 
1120
 
static void
1121
 
rb_playlist_source_non_entry_dropped (GtkTreeModel *model,
1122
 
                                      const char *uri,
1123
 
                                      RBPlaylistSource *source)
1124
 
{
1125
 
        rb_playlist_source_add_location (source, uri);
1126
 
}
1127
 
 
1128
 
static void
1129
 
rb_playlist_source_row_inserted (GtkTreeModel *model,
1130
 
                                 GtkTreePath *path,
1131
 
                                 GtkTreeIter *iter,
1132
 
                                 RBPlaylistSource *source)
1133
 
{
1134
 
        RhythmDBEntry *entry;
1135
 
 
1136
 
        gtk_tree_model_get (model, iter, 0, &entry, -1);
1137
 
 
1138
 
        rb_playlist_source_add_location (source, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
1139
 
}
 
593
 
 
594
static void
 
595
rb_playlist_source_row_deleted (GtkTreeModel *model,
 
596
                                GtkTreePath *path,
 
597
                                RBPlaylistSource *source)
 
598
{
 
599
        RhythmDBEntry *entry = 
 
600
                rhythmdb_query_model_tree_path_to_entry (RHYTHMDB_QUERY_MODEL (model),
 
601
                                                         path);
 
602
        g_hash_table_remove (source->priv->entries, entry->location);
 
603
}
 
604
 
 
605
static void
 
606
rb_playlist_source_entry_added_cb (RhythmDB *db, 
 
607
                                   RhythmDBEntry *entry,
 
608
                                   RBPlaylistSource *source)
 
609
{
 
610
        const char *location;
 
611
 
 
612
        location = entry->location;
 
613
        if (g_hash_table_lookup (source->priv->entries, location)) {
 
614
                rhythmdb_query_model_add_entry (source->priv->model, entry, -1);
 
615
                source->priv->dirty = TRUE;
 
616
        }
 
617
}
 
618
 
 
619
static void
 
620
rb_playlist_source_track_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
 
621
                                         GtkTreeModel *tree_model, GtkTreeIter *iter,
 
622
                                         RBPlaylistSource *source)
 
623
{
 
624
        char *str;
 
625
        int val;
 
626
 
 
627
        gtk_tree_model_get (tree_model, iter, 1, &val, -1);
 
628
 
 
629
        if (val >= 0)
 
630
                str = g_strdup_printf ("%d", val);
 
631
        else
 
632
                str = g_strdup ("");
 
633
 
 
634
        g_object_set (G_OBJECT (renderer), "text", str, NULL);
 
635
        g_free (str);
 
636
}
 
637
 
 
638
void
 
639
rb_playlist_source_setup_entry_view (RBPlaylistSource *source,
 
640
                                     RBEntryView *entry_view)
 
641
{
 
642
        g_signal_connect_object (G_OBJECT (entry_view), "show_popup",
 
643
                                 G_CALLBACK (rb_playlist_source_songs_show_popup_cb), source, 0);
 
644
        g_signal_connect_object (G_OBJECT (entry_view), "drag_data_received",
 
645
                                 G_CALLBACK (rb_playlist_source_drop_cb), source, 0);
 
646
        gtk_drag_dest_set (GTK_WIDGET (entry_view), GTK_DEST_DEFAULT_ALL,
 
647
                           target_uri, G_N_ELEMENTS (target_uri), GDK_ACTION_COPY);
 
648
}
 
649
 
 
650
void
 
651
rb_playlist_source_set_query_model (RBPlaylistSource *source,
 
652
                                    RhythmDBQueryModel *model)
 
653
{
 
654
        if (source->priv->model) {
 
655
                /* if the query model is replaced, the set of entries in
 
656
                 * the playlist will change, so we should mark the playlist dirty.
 
657
                 */
 
658
                source->priv->dirty = TRUE;
 
659
                g_signal_handlers_disconnect_by_func (G_OBJECT (source->priv->model),
 
660
                                                      G_CALLBACK (rb_playlist_source_row_deleted),
 
661
                                                      source);
 
662
                g_object_unref (source->priv->model);
 
663
        }
 
664
        source->priv->model = model;
 
665
 
 
666
        if (model) {
 
667
                g_object_ref (G_OBJECT (model));
 
668
                g_signal_connect_object (G_OBJECT (source->priv->model), "row_deleted",
 
669
                                         G_CALLBACK (rb_playlist_source_row_deleted), source, 0);
 
670
        }
 
671
 
 
672
        rb_entry_view_set_model (source->priv->songs, RHYTHMDB_QUERY_MODEL (source->priv->model));
 
673
        
 
674
        g_object_set (G_OBJECT (source), "query-model", source->priv->model, NULL);
 
675
}
 
676
 
 
677
RhythmDB *
 
678
rb_playlist_source_get_db (RBPlaylistSource *source)
 
679
{
 
680
        return source->priv->db;
 
681
}
 
682
 
 
683
RhythmDBQueryModel *
 
684
rb_playlist_source_get_query_model (RBPlaylistSource *source)
 
685
{
 
686
        return source->priv->model;
 
687
}
 
688
 
 
689
void
 
690
rb_playlist_source_mark_dirty (RBPlaylistSource *source)
 
691
{
 
692
        source->priv->dirty = TRUE;
 
693
}
 
694
 
 
695
gboolean
 
696
rb_playlist_source_location_in_map (RBPlaylistSource *source,
 
697
                                    const char *location)
 
698
{
 
699
        return (g_hash_table_lookup (source->priv->entries, location) != NULL);
 
700
}
 
701
 
 
702
gboolean
 
703
rb_playlist_source_add_to_map (RBPlaylistSource *source,
 
704
                               const char *location)
 
705
{
 
706
        if (g_hash_table_lookup (source->priv->entries, location)) {
 
707
                return FALSE;
 
708
        }
 
709
 
 
710
        g_hash_table_insert (source->priv->entries,
 
711
                             g_strdup (location), GINT_TO_POINTER (1));
 
712
        return TRUE;
 
713
}
 
714