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

« back to all changes in this revision

Viewing changes to plugins/grilo/rb-grilo-source.c

  • Committer: Package Import Robot
  • Author(s): Sebastien Bacher, Rico Tzschichholz
  • Date: 2011-12-05 19:31:23 UTC
  • mfrom: (1.1.60)
  • Revision ID: package-import@ubuntu.com-20111205193123-89047p8yplb0w1vx
Tags: 2.90.1~20111126.89c872b0-0ubuntu1
* Upload the new version to Ubuntu, should solve those issues:
  - the lack of rhythmbox-client command (lp: #875064)
  - the music sharing preferences dialog (lp: #894153)
  - several segfaults (lp: #859195, #814614)
* debian/control.in:
  - let the rhythmbox gir depends on gir1.2-peas-1.0 (lp: #874973)

[ Rico Tzschichholz ]
* New upstream git snapshot

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
 
2
 *
 
3
 *  Copyright (C) 2011 Jonathan Matthew
 
4
 *
 
5
 *  This program is free software; you can redistribute it and/or modify
 
6
 *  it under the terms of the GNU General Public License as published by
 
7
 *  the Free Software Foundation; either version 2 of the License, or
 
8
 *  (at your option) any later version.
 
9
 *
 
10
 *  The Rhythmbox authors hereby grant permission for non-GPL compatible
 
11
 *  GStreamer plugins to be used and distributed together with GStreamer
 
12
 *  and Rhythmbox. This permission is above and beyond the permissions granted
 
13
 *  by the GPL license by which Rhythmbox is covered. If you modify this code
 
14
 *  you may extend this exception to your version of the code, but you are not
 
15
 *  obligated to do so. If you do not wish to do so, delete this exception
 
16
 *  statement from your version.
 
17
 *
 
18
 *  This program is distributed in the hope that it will be useful,
 
19
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
20
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
21
 *  GNU General Public License for more details.
 
22
 *
 
23
 *  You should have received a copy of the GNU General Public License
 
24
 *  along with this program; if not, write to the Free Software
 
25
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
 
26
 *
 
27
 */
 
28
 
 
29
#include "config.h"
 
30
 
 
31
#include <string.h>
 
32
#include <gtk/gtk.h>
 
33
#include <glib/gi18n.h>
 
34
#include <grilo.h>
 
35
 
 
36
#include "rhythmdb.h"
 
37
#include "rb-shell.h"
 
38
#include "rb-shell-player.h"
 
39
#include "rb-grilo-source.h"
 
40
#include "rb-util.h"
 
41
#include "rb-debug.h"
 
42
#include "rb-file-helpers.h"
 
43
#include "rb-gst-media-types.h"
 
44
#include "rb-search-entry.h"
 
45
 
 
46
/* number of items to check before giving up on finding any
 
47
 * of a particular type
 
48
 */
 
49
#define CONTAINER_GIVE_UP_POINT         100
 
50
 
 
51
/* maximum number of tracks to fetch before stopping and
 
52
 * requiring the user to ask for more.
 
53
 */
 
54
#define CONTAINER_MAX_TRACKS            1000
 
55
 
 
56
/* number of items to fetch at once */
 
57
#define CONTAINER_FETCH_SIZE            50
 
58
 
 
59
enum {
 
60
        CONTAINER_UNKNOWN_MEDIA = 0,
 
61
        CONTAINER_NO_MEDIA,
 
62
        CONTAINER_HAS_MEDIA
 
63
};
 
64
 
 
65
enum
 
66
{
 
67
        PROP_0,
 
68
        PROP_GRILO_SOURCE
 
69
};
 
70
 
 
71
static void rb_grilo_source_dispose (GObject *object);
 
72
static void rb_grilo_source_finalize (GObject *object);
 
73
static void rb_grilo_source_constructed (GObject *object);
 
74
static void impl_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
 
75
static void impl_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
 
76
 
 
77
static void browser_selection_changed_cb (GtkTreeSelection *selection, RBGriloSource *source);
 
78
static void browser_row_expanded_cb (GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, RBGriloSource *source);
 
79
static void scroll_adjust_changed_cb (GtkAdjustment *adjustment, RBGriloSource *source);
 
80
static void scroll_adjust_value_changed_cb (GtkAdjustment *adjustment, RBGriloSource *source);
 
81
static gboolean maybe_expand_container (RBGriloSource *source);
 
82
static void fetch_more_cb (GtkInfoBar *bar, gint response, RBGriloSource *source);
 
83
static void search_cb (RBSearchEntry *search, const char *text, RBGriloSource *source);
 
84
static void notify_sort_order_cb (GObject *object, GParamSpec *pspec, RBGriloSource *source);
 
85
 
 
86
static void impl_delete_thyself (RBDisplayPage *page);
 
87
static void impl_selected (RBDisplayPage *page);
 
88
static void impl_deselected (RBDisplayPage *page);
 
89
 
 
90
static RBEntryView *impl_get_entry_view (RBSource *source);
 
91
 
 
92
struct _RBGriloSourcePrivate
 
93
{
 
94
        GrlMediaSource *grilo_source;
 
95
        GList *grilo_keys;
 
96
 
 
97
        RhythmDBEntryType *entry_type;
 
98
 
 
99
        /* some widgets and things */
 
100
        GtkWidget *paned;
 
101
        RhythmDBQueryModel *query_model;
 
102
        RBEntryView *entry_view;
 
103
        GtkTreeStore *browser_model;
 
104
        GtkWidget *browser_view;
 
105
        gboolean done_initial_browse;
 
106
        GtkWidget *info_bar;
 
107
        GtkWidget *info_bar_label;
 
108
        RBSearchEntry *search_entry;
 
109
 
 
110
        /* current browsing operation (should allow multiple concurrent ops?) */
 
111
        guint browse_op;
 
112
        GrlMedia *browse_container;
 
113
        GtkTreeIter browse_container_iter;
 
114
        guint browse_position;
 
115
        gboolean browse_got_results;
 
116
        gboolean browse_got_media;
 
117
        guint maybe_expand_idle;
 
118
 
 
119
        /* current media browse operation */
 
120
        guint media_browse_op;
 
121
        char *search_text;
 
122
        GrlMedia *media_browse_container;
 
123
        GtkTreeIter media_browse_container_iter;
 
124
        guint media_browse_position;
 
125
        gboolean media_browse_got_results;
 
126
        gboolean media_browse_got_containers;
 
127
        guint media_browse_limit;
 
128
 
 
129
        RhythmDB *db;
 
130
};
 
131
 
 
132
G_DEFINE_DYNAMIC_TYPE (RBGriloSource, rb_grilo_source, RB_TYPE_SOURCE)
 
133
 
 
134
/* entry type */
 
135
 
 
136
G_DEFINE_DYNAMIC_TYPE (RBGriloEntryType, rb_grilo_entry_type, RHYTHMDB_TYPE_ENTRY_TYPE);
 
137
 
 
138
static void
 
139
rb_grilo_entry_type_destroy_entry (RhythmDBEntryType *etype, RhythmDBEntry *entry)
 
140
{
 
141
        RBGriloEntryData *data;
 
142
 
 
143
        data = RHYTHMDB_ENTRY_GET_TYPE_DATA (entry, RBGriloEntryData);
 
144
        g_object_unref (data->grilo_data);
 
145
        if (data->grilo_container != NULL)
 
146
                g_object_unref (data->grilo_container);
 
147
}
 
148
 
 
149
static void
 
150
rb_grilo_entry_type_class_init (RBGriloEntryTypeClass *klass)
 
151
{
 
152
        RhythmDBEntryTypeClass *etype_class = RHYTHMDB_ENTRY_TYPE_CLASS (klass);
 
153
        etype_class->can_sync_metadata = (RhythmDBEntryTypeBooleanFunc) rb_true_function;
 
154
        etype_class->sync_metadata = (RhythmDBEntryTypeSyncFunc) rb_null_function;
 
155
        etype_class->destroy_entry = rb_grilo_entry_type_destroy_entry;
 
156
}
 
157
 
 
158
static void
 
159
rb_grilo_entry_type_class_finalize (RBGriloEntryTypeClass *klass)
 
160
{
 
161
}
 
162
 
 
163
static void
 
164
rb_grilo_entry_type_init (RBGriloEntryType *etype)
 
165
{
 
166
}
 
167
 
 
168
static void
 
169
rb_grilo_source_class_init (RBGriloSourceClass *klass)
 
170
{
 
171
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
172
        RBDisplayPageClass *page_class = RB_DISPLAY_PAGE_CLASS (klass);
 
173
        RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
 
174
 
 
175
        object_class->constructed = rb_grilo_source_constructed;
 
176
        object_class->dispose = rb_grilo_source_dispose;
 
177
        object_class->finalize = rb_grilo_source_finalize;
 
178
        object_class->set_property = impl_set_property;
 
179
        object_class->get_property = impl_get_property;
 
180
 
 
181
        page_class->delete_thyself = impl_delete_thyself;
 
182
        page_class->selected = impl_selected;
 
183
        page_class->deselected = impl_deselected;
 
184
 
 
185
        source_class->impl_get_entry_view = impl_get_entry_view;
 
186
 
 
187
        g_object_class_install_property (object_class,
 
188
                                         PROP_GRILO_SOURCE,
 
189
                                         g_param_spec_object ("grilo-source",
 
190
                                                              "grilo source",
 
191
                                                              "grilo source object",
 
192
                                                              GRL_TYPE_MEDIA_SOURCE,
 
193
                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
194
 
 
195
        g_type_class_add_private (klass, sizeof (RBGriloSourcePrivate));
 
196
}
 
197
 
 
198
static void
 
199
rb_grilo_source_class_finalize (RBGriloSourceClass *klass)
 
200
{
 
201
}
 
202
 
 
203
static void
 
204
rb_grilo_source_init (RBGriloSource *self)
 
205
{
 
206
        self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, RB_TYPE_GRILO_SOURCE, RBGriloSourcePrivate);
 
207
}
 
208
 
 
209
static void
 
210
rb_grilo_source_finalize (GObject *object)
 
211
{
 
212
        RBGriloSource *source = RB_GRILO_SOURCE (object);
 
213
 
 
214
        g_free (source->priv->search_text);
 
215
 
 
216
        g_list_free (source->priv->grilo_keys);
 
217
 
 
218
        G_OBJECT_CLASS (rb_grilo_source_parent_class)->finalize (object);
 
219
}
 
220
 
 
221
static void
 
222
rb_grilo_source_dispose (GObject *object)
 
223
{
 
224
        RBGriloSource *source = RB_GRILO_SOURCE (object);
 
225
 
 
226
        if (source->priv->browse_op != 0) {
 
227
                grl_operation_cancel (source->priv->browse_op);
 
228
                source->priv->browse_op = 0;
 
229
        }
 
230
 
 
231
        if (source->priv->media_browse_op != 0) {
 
232
                grl_operation_cancel (source->priv->media_browse_op);
 
233
                source->priv->media_browse_op = 0;
 
234
        }
 
235
 
 
236
        if (source->priv->query_model != NULL) {
 
237
                g_object_unref (source->priv->query_model);
 
238
                source->priv->query_model = NULL;
 
239
        }
 
240
 
 
241
        if (source->priv->entry_type != NULL) {
 
242
                g_object_unref (source->priv->entry_type);
 
243
                source->priv->entry_type = NULL;
 
244
        }
 
245
 
 
246
        if (source->priv->maybe_expand_idle != 0) {
 
247
                g_source_remove (source->priv->maybe_expand_idle);
 
248
                source->priv->maybe_expand_idle = 0;
 
249
        }
 
250
 
 
251
        G_OBJECT_CLASS (rb_grilo_source_parent_class)->dispose (object);
 
252
}
 
253
 
 
254
static void
 
255
rb_grilo_source_constructed (GObject *object)
 
256
{
 
257
        RBGriloSource *source;
 
258
        RBShell *shell;
 
259
        RBShellPlayer *shell_player;
 
260
        const GList *source_keys;
 
261
        GtkTreeViewColumn *column;
 
262
        GtkCellRenderer *renderer;
 
263
        GtkTreeSelection *selection;
 
264
        GtkWidget *scrolled;
 
265
        GtkWidget *browserbox;
 
266
        GtkWidget *vbox;
 
267
        GtkWidget *mainbox;
 
268
        GtkAdjustment *adjustment;
 
269
 
 
270
        RB_CHAIN_GOBJECT_METHOD (rb_grilo_source_parent_class, constructed, object);
 
271
        source = RB_GRILO_SOURCE (object);
 
272
 
 
273
        g_object_get (source, "shell", &shell, NULL);
 
274
        g_object_get (shell,
 
275
                      "db", &source->priv->db,
 
276
                      "shell-player", &shell_player,
 
277
                      NULL);
 
278
        g_object_unref (shell);
 
279
 
 
280
        g_object_get (source, "entry-type", &source->priv->entry_type, NULL);
 
281
 
 
282
        source->priv->entry_view = rb_entry_view_new (source->priv->db, G_OBJECT (shell_player), TRUE, FALSE);
 
283
        g_object_unref (shell_player);
 
284
        g_signal_connect (source->priv->entry_view,
 
285
                          "notify::sort-order",
 
286
                          G_CALLBACK (notify_sort_order_cb),
 
287
                          source);
 
288
 
 
289
        source_keys = grl_metadata_source_supported_keys (GRL_METADATA_SOURCE (source->priv->grilo_source));
 
290
 
 
291
        if (g_list_find ((GList *)source_keys, GRL_METADATA_KEY_TRACK_NUMBER)) {
 
292
                rb_entry_view_append_column (source->priv->entry_view, RB_ENTRY_VIEW_COL_TRACK_NUMBER, FALSE);
 
293
                source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys,
 
294
                                                           GRL_METADATA_KEY_TRACK_NUMBER);
 
295
        }
 
296
 
 
297
        if (g_list_find ((GList *)source_keys, GRL_METADATA_KEY_TITLE)) {
 
298
                rb_entry_view_append_column (source->priv->entry_view, RB_ENTRY_VIEW_COL_TITLE, TRUE);
 
299
                source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys,
 
300
                                                           GRL_METADATA_KEY_TITLE);
 
301
        }
 
302
 
 
303
        if (g_list_find ((GList *)source_keys, GRL_METADATA_KEY_GENRE)) {
 
304
                rb_entry_view_append_column (source->priv->entry_view, RB_ENTRY_VIEW_COL_GENRE, FALSE);
 
305
                source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys,
 
306
                                                           GRL_METADATA_KEY_GENRE);
 
307
        }
 
308
        if (g_list_find ((GList *)source_keys, GRL_METADATA_KEY_ARTIST)) {
 
309
                rb_entry_view_append_column (source->priv->entry_view, RB_ENTRY_VIEW_COL_ARTIST, FALSE);
 
310
                source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys,
 
311
                                                           GRL_METADATA_KEY_ARTIST);
 
312
        }
 
313
        if (g_list_find ((GList *)source_keys, GRL_METADATA_KEY_ALBUM)) {
 
314
                rb_entry_view_append_column (source->priv->entry_view, RB_ENTRY_VIEW_COL_ALBUM, FALSE);
 
315
                source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys,
 
316
                                                           GRL_METADATA_KEY_ALBUM);
 
317
        }
 
318
        /*
 
319
        if (g_list_find ((GList *)source_keys, GRL_METADATA_KEY_DATE)) {
 
320
                rb_entry_view_append_column (source->priv->entry_view, RB_ENTRY_VIEW_COL_YEAR, FALSE);
 
321
                source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys,
 
322
                                                           GRL_METADATA_KEY_DATE);
 
323
        }
 
324
        */
 
325
        if (g_list_find ((GList *)source_keys, GRL_METADATA_KEY_DURATION)) {
 
326
                rb_entry_view_append_column (source->priv->entry_view, RB_ENTRY_VIEW_COL_DURATION, FALSE);
 
327
                source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys,
 
328
                                                           GRL_METADATA_KEY_DURATION);
 
329
        }
 
330
 
 
331
        source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys, GRL_METADATA_KEY_CHILDCOUNT);
 
332
        source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys, GRL_METADATA_KEY_URL);
 
333
        source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys, GRL_METADATA_KEY_THUMBNAIL);
 
334
 
 
335
        /* probably add an image column too? */
 
336
        source->priv->browser_model = gtk_tree_store_new (4, GRL_TYPE_MEDIA, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
 
337
        source->priv->browser_view = gtk_tree_view_new ();
 
338
        gtk_tree_view_set_model (GTK_TREE_VIEW (source->priv->browser_view), GTK_TREE_MODEL (source->priv->browser_model));
 
339
 
 
340
        column = gtk_tree_view_column_new ();
 
341
        renderer = gtk_cell_renderer_text_new ();
 
342
        gtk_tree_view_column_set_title (column, _("Browse"));
 
343
        gtk_tree_view_column_pack_start (column, renderer, FALSE);
 
344
        gtk_tree_view_column_add_attribute (column, renderer, "text", 1);
 
345
        gtk_tree_view_column_set_expand (column, TRUE);
 
346
        gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
 
347
 
 
348
        gtk_tree_view_append_column (GTK_TREE_VIEW (source->priv->browser_view), column);
 
349
        gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (source->priv->browser_view), TRUE);
 
350
        gtk_tree_view_set_expander_column (GTK_TREE_VIEW (source->priv->browser_view), column);
 
351
        gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (source->priv->browser_view), TRUE);
 
352
        gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (source->priv->browser_view), TRUE);
 
353
 
 
354
        g_signal_connect (source->priv->browser_view, "row-expanded", G_CALLBACK (browser_row_expanded_cb), source);
 
355
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (source->priv->browser_view));
 
356
        gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);  /* should be multiple eventually */
 
357
        g_signal_connect (selection, "changed", G_CALLBACK (browser_selection_changed_cb), source);
 
358
 
 
359
        scrolled = gtk_scrolled_window_new (NULL, NULL);
 
360
        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_IN);
 
361
        adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled));
 
362
        g_signal_connect (adjustment, "changed", G_CALLBACK (scroll_adjust_changed_cb), source);
 
363
        g_signal_connect (adjustment, "value-changed", G_CALLBACK (scroll_adjust_value_changed_cb), source);
 
364
 
 
365
        browserbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
 
366
 
 
367
        /* search bar (if the source supports searching) */
 
368
        if (grl_metadata_source_supported_operations (GRL_METADATA_SOURCE (source->priv->grilo_source)) & GRL_OP_SEARCH) {
 
369
                source->priv->search_entry = rb_search_entry_new (FALSE);
 
370
                g_object_set (source->priv->search_entry, "explicit-mode", TRUE, NULL);
 
371
                g_signal_connect (source->priv->search_entry, "search", G_CALLBACK (search_cb), source);
 
372
                g_signal_connect (source->priv->search_entry, "activate", G_CALLBACK (search_cb), source);
 
373
                gtk_box_pack_start (GTK_BOX (browserbox), GTK_WIDGET (source->priv->search_entry), FALSE, FALSE, 6);
 
374
        }
 
375
        gtk_container_add (GTK_CONTAINER (scrolled), source->priv->browser_view);
 
376
        gtk_box_pack_start (GTK_BOX (browserbox), scrolled, TRUE, TRUE, 0);
 
377
 
 
378
        mainbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
 
379
        gtk_box_pack_start (GTK_BOX (source), mainbox, TRUE, TRUE, 0);
 
380
 
 
381
        /* info bar */
 
382
        source->priv->info_bar_label = gtk_label_new ("");
 
383
        source->priv->info_bar = gtk_info_bar_new ();
 
384
        gtk_info_bar_set_message_type (GTK_INFO_BAR (source->priv->info_bar), GTK_MESSAGE_INFO);
 
385
        gtk_info_bar_add_button (GTK_INFO_BAR (source->priv->info_bar), _("Fetch more tracks"), GTK_RESPONSE_OK);
 
386
        gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (source->priv->info_bar))),
 
387
                           source->priv->info_bar_label);
 
388
        gtk_widget_show (GTK_WIDGET (source->priv->info_bar_label));
 
389
        gtk_widget_set_no_show_all (GTK_WIDGET (source->priv->info_bar), TRUE);
 
390
        g_signal_connect (source->priv->info_bar, "response", G_CALLBACK (fetch_more_cb), source);
 
391
 
 
392
        /* don't allow the browser to be hidden? */
 
393
        source->priv->paned = gtk_hpaned_new ();
 
394
        rb_source_bind_settings (RB_SOURCE (source), GTK_WIDGET (source->priv->entry_view), source->priv->paned, NULL);
 
395
        gtk_paned_pack1 (GTK_PANED (source->priv->paned), browserbox, FALSE, FALSE);
 
396
 
 
397
        vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
 
398
        gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (source->priv->entry_view), TRUE, TRUE, 0);
 
399
        gtk_box_pack_start (GTK_BOX (vbox), source->priv->info_bar, FALSE, FALSE, 0);
 
400
        gtk_paned_pack2 (GTK_PANED (source->priv->paned), vbox, TRUE, FALSE);
 
401
 
 
402
        gtk_box_pack_start (GTK_BOX (mainbox), source->priv->paned, TRUE, TRUE, 0);
 
403
 
 
404
        gtk_widget_show_all (GTK_WIDGET (source));
 
405
}
 
406
 
 
407
RBSource *
 
408
rb_grilo_source_new (GObject *plugin, GrlMediaSource *grilo_source)
 
409
{
 
410
        GObject *source;
 
411
        RBShell *shell;
 
412
        GSettings *settings;
 
413
        RhythmDBEntryType *entry_type;
 
414
        RhythmDB *db;
 
415
        char *name;
 
416
 
 
417
        name = g_strdup_printf ("grilo:%s", grl_media_plugin_get_id (GRL_MEDIA_PLUGIN (grilo_source)));
 
418
 
 
419
        g_object_get (plugin, "object", &shell, NULL);
 
420
        g_object_get (shell, "db", &db, NULL);
 
421
        entry_type = g_object_new (rb_grilo_entry_type_get_type (),
 
422
                                   "db", db,
 
423
                                   "name", name,
 
424
                                   "save-to-disk", FALSE,
 
425
                                   "category", RHYTHMDB_ENTRY_NORMAL,
 
426
                                   "type-data-size", sizeof(RBGriloEntryData),
 
427
                                   NULL);
 
428
        rhythmdb_register_entry_type (db, entry_type);
 
429
        g_object_unref (db);
 
430
        g_free (name);
 
431
 
 
432
        settings = g_settings_new ("org.gnome.rhythmbox.plugins.grilo");
 
433
        source = g_object_new (RB_TYPE_GRILO_SOURCE,
 
434
                               "name", grl_metadata_source_get_name (GRL_METADATA_SOURCE (grilo_source)),
 
435
                               "entry-type", entry_type,
 
436
                               "shell", shell,
 
437
                               "plugin", plugin,
 
438
                               "show-browser", FALSE,
 
439
                               "settings", g_settings_get_child (settings, "source"),
 
440
                               "grilo-source", grilo_source,
 
441
                               NULL);
 
442
        g_object_unref (settings);
 
443
 
 
444
        rb_shell_register_entry_type_for_source (shell, RB_SOURCE (source), entry_type);
 
445
 
 
446
        g_object_unref (shell);
 
447
        return RB_SOURCE (source);
 
448
}
 
449
 
 
450
static void
 
451
impl_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
 
452
{
 
453
        RBGriloSource *source = RB_GRILO_SOURCE (object);
 
454
        switch (prop_id) {
 
455
        case PROP_GRILO_SOURCE:
 
456
                source->priv->grilo_source = g_value_get_object (value);
 
457
                break;
 
458
        default:
 
459
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
460
                break;
 
461
        }
 
462
}
 
463
 
 
464
static void
 
465
impl_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 
466
{
 
467
        RBGriloSource *source = RB_GRILO_SOURCE (object);
 
468
 
 
469
        switch (prop_id) {
 
470
        case PROP_GRILO_SOURCE:
 
471
                g_value_set_object (value, source->priv->grilo_source);
 
472
                break;
 
473
        default:
 
474
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
475
                break;
 
476
        }
 
477
}
 
478
 
 
479
static void
 
480
impl_delete_thyself (RBDisplayPage *page)
 
481
{
 
482
        RBGriloSource *source = RB_GRILO_SOURCE (page);
 
483
        RhythmDBEntryType *entry_type;
 
484
 
 
485
        if (source->priv->browse_op != 0) {
 
486
                grl_operation_cancel (source->priv->browse_op);
 
487
                source->priv->browse_op = 0;
 
488
        }
 
489
 
 
490
        if (source->priv->media_browse_op != 0) {
 
491
                grl_operation_cancel (source->priv->media_browse_op);
 
492
                source->priv->media_browse_op = 0;
 
493
        }
 
494
 
 
495
        g_object_get (source, "entry-type", &entry_type, NULL);
 
496
        rhythmdb_entry_delete_by_type (source->priv->db, entry_type);
 
497
        g_object_unref (entry_type);
 
498
 
 
499
        rhythmdb_commit (source->priv->db);
 
500
}
 
501
 
 
502
void
 
503
_rb_grilo_source_register_type (GTypeModule *module)
 
504
{
 
505
        rb_grilo_source_register_type (module);
 
506
        rb_grilo_entry_type_register_type (module);
 
507
}
 
508
 
 
509
/* grilo media -> rhythmdb entry */
 
510
 
 
511
static void
 
512
set_string_prop_from_key (RhythmDB *db, RhythmDBEntry *entry, RhythmDBPropType prop, GrlData *data, GrlKeyID key)
 
513
{
 
514
        GValue v = {0,};
 
515
        if (grl_data_has_key (data, key) == FALSE)
 
516
                return;
 
517
 
 
518
        g_value_init (&v, G_TYPE_STRING);
 
519
        g_value_set_string (&v, grl_data_get_string (data, key));
 
520
        rhythmdb_entry_set (db, entry, prop, &v);
 
521
        g_value_unset (&v);
 
522
}
 
523
 
 
524
static RhythmDBEntry *
 
525
create_entry_for_media (RhythmDB *db, RhythmDBEntryType *entry_type, GrlData *data, GrlData *container)
 
526
{
 
527
        RhythmDBEntry *entry;
 
528
        RBGriloEntryData *entry_data;
 
529
 
 
530
        entry = rhythmdb_entry_lookup_by_location (db, grl_media_get_url (GRL_MEDIA (data)));
 
531
        if (entry != NULL) {
 
532
                return entry;
 
533
        }
 
534
 
 
535
        rb_debug ("creating entry for %s / %s", grl_media_get_url (GRL_MEDIA (data)), grl_media_get_id (GRL_MEDIA (data)));
 
536
 
 
537
        entry = rhythmdb_entry_new (db, entry_type, grl_media_get_url (GRL_MEDIA (data)));      /* just use the url? */
 
538
        if (entry == NULL) {
 
539
                /* crap. */
 
540
                return NULL;
 
541
        }
 
542
 
 
543
        set_string_prop_from_key (db, entry, RHYTHMDB_PROP_TITLE, data, GRL_METADATA_KEY_TITLE);
 
544
        set_string_prop_from_key (db, entry, RHYTHMDB_PROP_ALBUM, data, GRL_METADATA_KEY_ALBUM);
 
545
        set_string_prop_from_key (db, entry, RHYTHMDB_PROP_ARTIST, data, GRL_METADATA_KEY_ARTIST);
 
546
        set_string_prop_from_key (db, entry, RHYTHMDB_PROP_GENRE, data, GRL_METADATA_KEY_GENRE);
 
547
        set_string_prop_from_key (db, entry, RHYTHMDB_PROP_TITLE, data, GRL_METADATA_KEY_TITLE);
 
548
 
 
549
        if (grl_data_has_key (data, GRL_METADATA_KEY_DATE)) {
 
550
                /* something - grilo has this as a string? */
 
551
        }
 
552
 
 
553
        if (grl_data_has_key (data, GRL_METADATA_KEY_BITRATE)) {
 
554
                GValue v = {0,};
 
555
                g_value_init (&v, G_TYPE_ULONG);
 
556
                g_value_set_ulong (&v, grl_data_get_int (data, GRL_METADATA_KEY_BITRATE));
 
557
                rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_BITRATE, &v);
 
558
                g_value_unset (&v);
 
559
        }
 
560
 
 
561
        if (grl_data_has_key (data, GRL_METADATA_KEY_DURATION)) {
 
562
                /* this is probably in seconds */
 
563
                GValue v = {0,};
 
564
                g_value_init (&v, G_TYPE_ULONG);
 
565
                g_value_set_ulong (&v, grl_data_get_int (data, GRL_METADATA_KEY_DURATION));
 
566
                rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_DURATION, &v);
 
567
                g_value_unset (&v);
 
568
        }
 
569
 
 
570
        if (grl_data_has_key (data, GRL_METADATA_KEY_MIME)) {
 
571
                const char *media_type;
 
572
                media_type = rb_gst_mime_type_to_media_type (grl_data_get_string (data, GRL_METADATA_KEY_MIME));
 
573
                if (media_type) {
 
574
                        GValue v = {0,};
 
575
                        g_value_init (&v, G_TYPE_STRING);
 
576
                        g_value_set_string (&v, media_type);
 
577
                        rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_MEDIA_TYPE, &v);
 
578
                        g_value_unset (&v);
 
579
                }
 
580
        }
 
581
 
 
582
        if (grl_data_has_key (data, GRL_METADATA_KEY_TRACK_NUMBER)) {
 
583
                GValue v = {0,};
 
584
                g_value_init (&v, G_TYPE_ULONG);
 
585
                g_value_set_ulong (&v, grl_data_get_int (data, GRL_METADATA_KEY_TRACK_NUMBER));
 
586
                rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_TRACK_NUMBER, &v);
 
587
                g_value_unset (&v);
 
588
        }
 
589
 
 
590
        /* rating and play count? */
 
591
 
 
592
        entry_data = RHYTHMDB_ENTRY_GET_TYPE_DATA (entry, RBGriloEntryData);
 
593
        entry_data->grilo_data = g_object_ref (data);
 
594
        if (container != NULL) {
 
595
                entry_data->grilo_container = g_object_ref (container);
 
596
        }
 
597
 
 
598
        /* might want to consider batching this */
 
599
        rhythmdb_commit (db);
 
600
 
 
601
        return entry;
 
602
}
 
603
 
 
604
/* container browsing */
 
605
 
 
606
static void browse_next (RBGriloSource *source);
 
607
 
 
608
static void
 
609
delete_marker_row (RBGriloSource *source, GtkTreeIter *iter)
 
610
{
 
611
        GtkTreeIter marker_iter;
 
612
        if (gtk_tree_model_iter_children (GTK_TREE_MODEL (source->priv->browser_model), &marker_iter, iter)) {
 
613
                do {
 
614
                        GrlMedia *container;
 
615
                        gtk_tree_model_get (GTK_TREE_MODEL (source->priv->browser_model), &marker_iter,
 
616
                                            0, &container,
 
617
                                            -1);
 
618
                        if (container == NULL) {
 
619
                                gtk_tree_store_remove (GTK_TREE_STORE (source->priv->browser_model), &marker_iter);
 
620
                                break;
 
621
                        }
 
622
                } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (source->priv->browser_model), &marker_iter));
 
623
        }
 
624
}
 
625
 
 
626
static void
 
627
set_container_type (RBGriloSource *source, GtkTreeIter *iter, gboolean has_media)
 
628
{
 
629
        int container_type;
 
630
 
 
631
        gtk_tree_model_get (GTK_TREE_MODEL (source->priv->browser_model),
 
632
                            iter,
 
633
                            2, &container_type,
 
634
                            -1);
 
635
        if (container_type == CONTAINER_UNKNOWN_MEDIA) {
 
636
                container_type = has_media ? CONTAINER_HAS_MEDIA : CONTAINER_NO_MEDIA;
 
637
        }
 
638
 
 
639
        gtk_tree_store_set (source->priv->browser_model,
 
640
                            iter,
 
641
                            2, container_type,
 
642
                            -1);
 
643
}
 
644
 
 
645
static void
 
646
grilo_browse_cb (GrlMediaSource *grilo_source, guint operation_id, GrlMedia *media, guint remaining, RBGriloSource *source, const GError *error)
 
647
{
 
648
        if (operation_id != source->priv->browse_op) {
 
649
                return;
 
650
        }
 
651
 
 
652
        if (error != NULL) {
 
653
                /* do something? */
 
654
                rb_debug ("got error for %s: %s", grl_metadata_source_get_name (GRL_METADATA_SOURCE (grilo_source)), error->message);
 
655
                return;
 
656
        }
 
657
 
 
658
        if (media != NULL) {
 
659
                source->priv->browse_got_results = TRUE;
 
660
                source->priv->browse_position++;
 
661
        }
 
662
 
 
663
        if (media && GRL_IS_MEDIA_BOX (media)) {
 
664
 
 
665
                GtkTreeIter new_row;
 
666
                if (source->priv->browse_container == NULL) {
 
667
                        /* insert at the end */
 
668
                        gtk_tree_store_insert_with_values (source->priv->browser_model,
 
669
                                                           &new_row,
 
670
                                                           NULL,
 
671
                                                           -1,
 
672
                                                           0, g_object_ref (media),
 
673
                                                           1, grl_media_get_title (media),
 
674
                                                           2, CONTAINER_UNKNOWN_MEDIA,
 
675
                                                           3, 0,
 
676
                                                           -1);
 
677
                } else {
 
678
                        int n;
 
679
                        /* insert before the expand marker row */
 
680
                        n = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (source->priv->browser_model),
 
681
                                                            &source->priv->browse_container_iter);
 
682
                        gtk_tree_store_insert_with_values (source->priv->browser_model,
 
683
                                                           &new_row,
 
684
                                                           &source->priv->browse_container_iter,
 
685
                                                           n - 1,
 
686
                                                           0, g_object_ref (media),
 
687
                                                           1, grl_media_get_title (media),
 
688
                                                           2, CONTAINER_UNKNOWN_MEDIA,
 
689
                                                           3, 0,
 
690
                                                           -1);
 
691
                }
 
692
 
 
693
                /* and insert an expand marker below it too */
 
694
                gtk_tree_store_insert_with_values (source->priv->browser_model,
 
695
                                                   NULL,
 
696
                                                   &new_row,
 
697
                                                   -1,
 
698
                                                   0, NULL,
 
699
                                                   1, "...",    /* needs to be translatable? */
 
700
                                                   2, CONTAINER_NO_MEDIA,
 
701
                                                   3, 0,
 
702
                                                   -1);
 
703
        } else if (media && GRL_IS_MEDIA_AUDIO (media)) {
 
704
                source->priv->browse_got_media = TRUE;
 
705
        }
 
706
 
 
707
        if (remaining == 0) {
 
708
                source->priv->browse_op = 0;
 
709
                if (source->priv->browse_got_results == FALSE &&
 
710
                    source->priv->browse_container != NULL) {
 
711
                        /* no more results for this container, so delete the marker row */
 
712
                        delete_marker_row (source, &source->priv->browse_container_iter);
 
713
 
 
714
                        set_container_type (source, &source->priv->browse_container_iter, source->priv->browse_got_media);
 
715
                        gtk_tree_store_set (source->priv->browser_model,
 
716
                                            &source->priv->browse_container_iter,
 
717
                                            3, -1,
 
718
                                            -1);
 
719
                } else if (source->priv->browse_container != NULL) {
 
720
                        if (source->priv->browse_position >= CONTAINER_GIVE_UP_POINT &&
 
721
                            gtk_tree_model_iter_n_children (GTK_TREE_MODEL (source->priv->browser_model),
 
722
                                                            &source->priv->browse_container_iter) == 1) {
 
723
                                /* no containers yet, so remove the marker row */
 
724
                                delete_marker_row (source, &source->priv->browse_container_iter);
 
725
                        } else {
 
726
                                /* store browse position for next time we want more */
 
727
                                gtk_tree_store_set (source->priv->browser_model,
 
728
                                                    &source->priv->browse_container_iter,
 
729
                                                    3, source->priv->browse_position,
 
730
                                                    -1);
 
731
                                maybe_expand_container (source);
 
732
                        }
 
733
 
 
734
                } else if (source->priv->browse_got_results && source->priv->browse_container == NULL) {
 
735
                        /* get all top-level containers */
 
736
                        browse_next (source);
 
737
                }
 
738
        }
 
739
}
 
740
 
 
741
static void
 
742
browse_next (RBGriloSource *source)
 
743
{
 
744
        rb_debug ("next browse op for %s (%d)",
 
745
                  grl_metadata_source_get_name (GRL_METADATA_SOURCE (source->priv->grilo_source)),
 
746
                  source->priv->browse_position);
 
747
        source->priv->browse_got_results = FALSE;
 
748
        source->priv->browse_op = grl_media_source_browse (source->priv->grilo_source,
 
749
                                                           source->priv->browse_container,
 
750
                                                           source->priv->grilo_keys,
 
751
                                                           source->priv->browse_position,
 
752
                                                           CONTAINER_FETCH_SIZE,
 
753
                                                           GRL_RESOLVE_NORMAL,
 
754
                                                           (GrlMediaSourceResultCb) grilo_browse_cb,
 
755
                                                           source);
 
756
}
 
757
 
 
758
static void
 
759
start_browse (RBGriloSource *source, GrlMedia *container, GtkTreeIter *container_iter, int position)
 
760
{
 
761
        rb_debug ("starting browse op for %s", grl_metadata_source_get_name (GRL_METADATA_SOURCE (source->priv->grilo_source)));
 
762
 
 
763
        /* cancel existing operation? */
 
764
        if (source->priv->browse_op != 0) {
 
765
                grl_operation_cancel (source->priv->browse_op);
 
766
        }
 
767
 
 
768
        if (source->priv->browse_container != NULL) {
 
769
                g_object_unref (source->priv->browse_container);
 
770
        }
 
771
        source->priv->browse_container = container;
 
772
        if (container_iter != NULL) {
 
773
                /* hrm, probably have to use row references here.. */
 
774
                source->priv->browse_container_iter = *container_iter;
 
775
        }
 
776
        source->priv->browse_position = position;
 
777
        source->priv->browse_got_media = FALSE;
 
778
 
 
779
        browse_next (source);
 
780
}
 
781
 
 
782
/* media browsing */
 
783
 
 
784
static void media_browse_next (RBGriloSource *source);
 
785
 
 
786
static void
 
787
grilo_media_browse_cb (GrlMediaSource *grilo_source, guint operation_id, GrlMedia *media, guint remaining, RBGriloSource *source, const GError *error)
 
788
{
 
789
        if (operation_id != source->priv->media_browse_op) {
 
790
                return;
 
791
        }
 
792
 
 
793
        if (error != NULL) {
 
794
                /* do something? */
 
795
                rb_debug ("got error for %s: %s",
 
796
                          grl_metadata_source_get_name (GRL_METADATA_SOURCE (grilo_source)),
 
797
                          error->message);
 
798
                return;
 
799
        }
 
800
 
 
801
        GDK_THREADS_ENTER ();
 
802
        if (media != NULL) {
 
803
                source->priv->media_browse_got_results = TRUE;
 
804
                source->priv->media_browse_position++;
 
805
 
 
806
                if (GRL_IS_MEDIA_AUDIO (media)) {
 
807
                        RhythmDBEntry *entry;
 
808
                        entry = create_entry_for_media (source->priv->db,
 
809
                                                        source->priv->entry_type,
 
810
                                                        GRL_DATA (media),
 
811
                                                        GRL_DATA (source->priv->browse_container));
 
812
                        if (entry != NULL) {
 
813
                                rhythmdb_query_model_add_entry (source->priv->query_model, entry, -1);
 
814
                        }
 
815
                } else if (GRL_IS_MEDIA_BOX (media)) {
 
816
                        source->priv->media_browse_got_containers = TRUE;
 
817
                }
 
818
        }
 
819
 
 
820
        if (remaining == 0) {
 
821
                source->priv->media_browse_op = 0;
 
822
 
 
823
                if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (source->priv->query_model), NULL) == 0 &&
 
824
                    source->priv->media_browse_position >= CONTAINER_GIVE_UP_POINT) {
 
825
                        /* if we don't find any media within the first 100 or so results,
 
826
                         * assume this container doesn't have any.  otherwise we'd load
 
827
                         * the entire thing when it got selected, which would suck.
 
828
                         */
 
829
                        rb_debug ("didn't find any media in %s, giving up", grl_media_get_title (source->priv->media_browse_container));
 
830
                        gtk_tree_store_set (source->priv->browser_model,
 
831
                                            &source->priv->media_browse_container_iter,
 
832
                                            2, CONTAINER_NO_MEDIA,
 
833
                                            -1);
 
834
                } else if (source->priv->media_browse_got_results) {
 
835
                        if (source->priv->media_browse_position < source->priv->media_browse_limit) {
 
836
                                media_browse_next (source);
 
837
                        } else {
 
838
                                char *text;
 
839
 
 
840
                                text = g_strdup_printf (ngettext ("Only showing %d result",
 
841
                                                                  "Only showing %d results",
 
842
                                                                  source->priv->media_browse_position),
 
843
                                                        source->priv->media_browse_position);
 
844
                                gtk_label_set_text (GTK_LABEL (source->priv->info_bar_label), text);
 
845
                                g_free (text);
 
846
 
 
847
                                gtk_widget_show (source->priv->info_bar);
 
848
                        }
 
849
                } else if (source->priv->media_browse_got_containers == FALSE &&
 
850
                           source->priv->media_browse_container != NULL) {
 
851
                        /* make sure there's no marker row for this container */
 
852
                        delete_marker_row (source, &source->priv->media_browse_container_iter);
 
853
                }
 
854
        }
 
855
        GDK_THREADS_LEAVE ();
 
856
}
 
857
 
 
858
static void
 
859
media_browse_next (RBGriloSource *source)
 
860
{
 
861
        rb_debug ("next media_browse op for %s (%d)",
 
862
                  grl_metadata_source_get_name (GRL_METADATA_SOURCE (source->priv->grilo_source)),
 
863
                  source->priv->media_browse_position);
 
864
 
 
865
        source->priv->media_browse_got_results = FALSE;
 
866
        if (source->priv->media_browse_container != NULL) {
 
867
                source->priv->media_browse_op =
 
868
                        grl_media_source_browse (source->priv->grilo_source,
 
869
                                                 source->priv->media_browse_container,
 
870
                                                 source->priv->grilo_keys,
 
871
                                                 source->priv->media_browse_position,
 
872
                                                 CONTAINER_FETCH_SIZE,
 
873
                                                 GRL_RESOLVE_NORMAL,
 
874
                                                 (GrlMediaSourceResultCb) grilo_media_browse_cb,
 
875
                                                 source);
 
876
        } else {
 
877
                source->priv->media_browse_op =
 
878
                        grl_media_source_search (source->priv->grilo_source,
 
879
                                                 source->priv->search_text,
 
880
                                                 source->priv->grilo_keys,
 
881
                                                 source->priv->media_browse_position,
 
882
                                                 CONTAINER_FETCH_SIZE,
 
883
                                                 GRL_RESOLVE_NORMAL,
 
884
                                                 (GrlMediaSourceResultCb) grilo_media_browse_cb,
 
885
                                                 source);
 
886
        }
 
887
}
 
888
 
 
889
static void
 
890
start_media_browse (RBGriloSource *source, GrlMedia *container, GtkTreeIter *container_iter, guint limit)
 
891
{
 
892
        rb_debug ("starting media browse for %s",
 
893
                  grl_metadata_source_get_name (GRL_METADATA_SOURCE (source->priv->grilo_source)));
 
894
 
 
895
        /* cancel existing operation? */
 
896
        if (source->priv->media_browse_op != 0) {
 
897
                grl_operation_cancel (source->priv->media_browse_op);
 
898
        }
 
899
 
 
900
        if (source->priv->media_browse_container != NULL) {
 
901
                g_object_unref (source->priv->media_browse_container);
 
902
        }
 
903
        source->priv->media_browse_container = container;
 
904
        if (container_iter != NULL) {
 
905
                /* hrm, probably have to use row references here.. */
 
906
                source->priv->media_browse_container_iter = *container_iter;
 
907
        }
 
908
        source->priv->media_browse_position = 0;
 
909
        source->priv->media_browse_limit = limit;
 
910
        source->priv->media_browse_got_containers = FALSE;
 
911
 
 
912
        if (source->priv->query_model != NULL) {
 
913
                g_object_unref (source->priv->query_model);
 
914
        }
 
915
        source->priv->query_model = rhythmdb_query_model_new_empty (source->priv->db);
 
916
        rb_entry_view_set_model (RB_ENTRY_VIEW (source->priv->entry_view), source->priv->query_model);
 
917
        g_object_set (source, "query-model", source->priv->query_model, NULL);
 
918
 
 
919
        media_browse_next (source);
 
920
}
 
921
 
 
922
static void
 
923
fetch_more_cb (GtkInfoBar *bar, gint response, RBGriloSource *source)
 
924
{
 
925
        if (response != GTK_RESPONSE_OK) {
 
926
                return;
 
927
        }
 
928
 
 
929
        gtk_widget_hide (GTK_WIDGET (bar));
 
930
        source->priv->media_browse_limit += CONTAINER_MAX_TRACKS;
 
931
        media_browse_next (source);
 
932
}
 
933
 
 
934
static gboolean
 
935
expand_from_marker (RBGriloSource *source, GtkTreeIter *iter)
 
936
{
 
937
        /* this is a marker row, fetch more containers underneath the parent */
 
938
        GrlMedia *container;
 
939
        GtkTreeIter browse;
 
940
        int position;
 
941
        gtk_tree_model_iter_parent (GTK_TREE_MODEL (source->priv->browser_model), &browse, iter);
 
942
        gtk_tree_model_get (GTK_TREE_MODEL (source->priv->browser_model),
 
943
                            &browse,
 
944
                            0, &container,
 
945
                            3, &position,
 
946
                            -1);
 
947
        if (position >= 0) {
 
948
                start_browse (source, container, &browse, position);
 
949
                return TRUE;
 
950
        }
 
951
 
 
952
        return FALSE;
 
953
}
 
954
 
 
955
static void
 
956
browser_selection_changed_cb (GtkTreeSelection *selection, RBGriloSource *source)
 
957
{
 
958
        GtkTreeIter iter;
 
959
        GrlMedia *container;
 
960
        int container_type;
 
961
 
 
962
        gtk_widget_hide (GTK_WIDGET (source->priv->info_bar));
 
963
        if (gtk_tree_selection_get_selected (selection, NULL, &iter) == FALSE) {
 
964
                rb_debug ("nothing selected");
 
965
                return;
 
966
        }
 
967
 
 
968
        if (source->priv->search_entry != NULL) {
 
969
                rb_search_entry_clear (source->priv->search_entry);
 
970
        }
 
971
 
 
972
        gtk_tree_model_get (GTK_TREE_MODEL (source->priv->browser_model), &iter,
 
973
                            0, &container,
 
974
                            2, &container_type,
 
975
                            -1);
 
976
 
 
977
        if (container == NULL) {
 
978
                expand_from_marker (source, &iter);
 
979
        } else if (container_type != CONTAINER_NO_MEDIA) {
 
980
                /* fetch media directly under this container */
 
981
                start_media_browse (source, container, &iter, CONTAINER_MAX_TRACKS);
 
982
        } else {
 
983
                /* clear the track list? */
 
984
        }
 
985
}
 
986
 
 
987
static gboolean
 
988
maybe_expand_container (RBGriloSource *source)
 
989
{
 
990
        GtkTreePath *path;
 
991
        GtkTreePath *end;
 
992
        GtkTreeIter iter;
 
993
        GtkTreeIter end_iter;
 
994
        GtkTreeIter next;
 
995
        GrlMedia *container;
 
996
        gboolean last;
 
997
 
 
998
        source->priv->maybe_expand_idle = 0;
 
999
 
 
1000
        if (source->priv->browse_op != 0) {
 
1001
                rb_debug ("not expanding, already browsing");
 
1002
                return FALSE;
 
1003
        }
 
1004
 
 
1005
        /* if we find a visible marker row, find more results */
 
1006
        if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (source->priv->browser_view), &path, &end) == FALSE) {
 
1007
                rb_debug ("not expanding, nothing to expand");
 
1008
                return FALSE;
 
1009
        }
 
1010
 
 
1011
        gtk_tree_model_get_iter (GTK_TREE_MODEL (source->priv->browser_model), &iter, path);
 
1012
        gtk_tree_model_get_iter (GTK_TREE_MODEL (source->priv->browser_model), &end_iter, end);
 
1013
 
 
1014
        do {
 
1015
                gtk_tree_path_free (path);
 
1016
                path = gtk_tree_model_get_path (GTK_TREE_MODEL (source->priv->browser_model), &iter);
 
1017
                last = (gtk_tree_path_compare (path, end) >= 0);
 
1018
                gtk_tree_model_get (GTK_TREE_MODEL (source->priv->browser_model), &iter,
 
1019
                                    0, &container,
 
1020
                                    -1);
 
1021
                if (container == NULL) {
 
1022
                        if (expand_from_marker (source, &iter)) {
 
1023
                                rb_debug ("expanding");
 
1024
                                break;
 
1025
                        }
 
1026
                }
 
1027
 
 
1028
                next = iter;
 
1029
                if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (source->priv->browser_view), path) &&
 
1030
                    gtk_tree_model_iter_has_child (GTK_TREE_MODEL (source->priv->browser_model), &iter)) {
 
1031
                        gtk_tree_model_iter_children (GTK_TREE_MODEL (source->priv->browser_model), &iter, &next);
 
1032
                } else if (gtk_tree_model_iter_next (GTK_TREE_MODEL (source->priv->browser_model), &next)) {
 
1033
                        iter = next;
 
1034
                } else {
 
1035
                        if (gtk_tree_model_iter_parent (GTK_TREE_MODEL (source->priv->browser_model), &next, &iter) == FALSE) {
 
1036
                                break;
 
1037
                        }
 
1038
                        iter = next;
 
1039
                        if (gtk_tree_model_iter_next (GTK_TREE_MODEL (source->priv->browser_model), &iter) == FALSE) {
 
1040
                                break;
 
1041
                        }
 
1042
                }
 
1043
        } while (last == FALSE);
 
1044
 
 
1045
        gtk_tree_path_free (path);
 
1046
        gtk_tree_path_free (end);
 
1047
        return FALSE;
 
1048
}
 
1049
 
 
1050
static void
 
1051
maybe_expand_container_idle (RBGriloSource *source)
 
1052
{
 
1053
        if (source->priv->maybe_expand_idle == 0) {
 
1054
                source->priv->maybe_expand_idle = g_idle_add ((GSourceFunc)maybe_expand_container, source);
 
1055
        }
 
1056
}
 
1057
 
 
1058
static void
 
1059
browser_row_expanded_cb (GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, RBGriloSource *source)
 
1060
{
 
1061
        maybe_expand_container_idle (source);
 
1062
}
 
1063
 
 
1064
static void
 
1065
scroll_adjust_changed_cb (GtkAdjustment *adjustment, RBGriloSource *source)
 
1066
{
 
1067
        maybe_expand_container_idle (source);
 
1068
}
 
1069
 
 
1070
static void
 
1071
scroll_adjust_value_changed_cb (GtkAdjustment *adjustment, RBGriloSource *source)
 
1072
{
 
1073
        maybe_expand_container_idle (source);
 
1074
}
 
1075
 
 
1076
static void
 
1077
impl_selected (RBDisplayPage *page)
 
1078
{
 
1079
        RBGriloSource *source = RB_GRILO_SOURCE (page);
 
1080
 
 
1081
        RB_DISPLAY_PAGE_CLASS (rb_grilo_source_parent_class)->selected (page);
 
1082
 
 
1083
        if (source->priv->done_initial_browse == FALSE) {
 
1084
                source->priv->done_initial_browse = TRUE;
 
1085
                start_browse (source, NULL, NULL, 0);
 
1086
        }
 
1087
 
 
1088
        rb_search_entry_set_mnemonic (source->priv->search_entry, TRUE);
 
1089
}
 
1090
 
 
1091
static void
 
1092
impl_deselected (RBDisplayPage *page)
 
1093
{
 
1094
        RBGriloSource *source = RB_GRILO_SOURCE (page);
 
1095
 
 
1096
        RB_DISPLAY_PAGE_CLASS (rb_grilo_source_parent_class)->deselected (page);
 
1097
 
 
1098
        rb_search_entry_set_mnemonic (source->priv->search_entry, FALSE);
 
1099
}
 
1100
 
 
1101
static RBEntryView *
 
1102
impl_get_entry_view (RBSource *bsource)
 
1103
{
 
1104
        RBGriloSource *source = RB_GRILO_SOURCE (bsource);
 
1105
        return source->priv->entry_view;
 
1106
}
 
1107
 
 
1108
static void
 
1109
search_cb (RBSearchEntry *search, const char *text, RBGriloSource *source)
 
1110
{
 
1111
        g_free (source->priv->search_text);
 
1112
        source->priv->search_text = g_strdup (text);
 
1113
 
 
1114
        gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (source->priv->browser_view)));
 
1115
 
 
1116
        start_media_browse (source, NULL, NULL, CONTAINER_MAX_TRACKS);
 
1117
}
 
1118
 
 
1119
static void
 
1120
notify_sort_order_cb (GObject *object, GParamSpec *pspec, RBGriloSource *source)
 
1121
{
 
1122
        rb_entry_view_resort_model (RB_ENTRY_VIEW (object));
 
1123
}