1 /*
2 * Copyright (C) 2009 Marc Pavot <marc.pavot@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 */
19
20 #include "sources/ario-tree-albums.h"
21 #include <gtk/gtk.h>
22 #include <string.h>
23 #include "lib/ario-conf.h"
24 #include <glib/gi18n.h>
25
26 #include "ario-debug.h"
27 #include "ario-util.h"
28 #include "covers/ario-cover.h"
29 #include "covers/ario-cover-handler.h"
30 #include "preferences/ario-preferences.h"
31 #include "shell/ario-shell-coverselect.h"
32
33 static void ario_tree_albums_finalize (GObject *object);
34 static void ario_tree_albums_build_tree (ArioTree *parent_tree,
35 GtkTreeView *treeview);
36 static void ario_tree_albums_fill_tree (ArioTree *parent_tree);
37 static GdkPixbuf* ario_tree_albums_get_dnd_pixbuf (ArioTree *tree);
38 static void ario_tree_albums_cover_changed_cb (ArioCoverHandler *cover_handler,
39 ArioTreeAlbums *tree);
40 static void ario_tree_albums_album_sort_changed_cb (guint notification_id,
41 ArioTreeAlbums *tree);
42 static void ario_tree_albums_covertree_visible_changed_cb (guint notification_id,
43 ArioTree *tree);
44
45 struct ArioTreeAlbumsPrivate
46 {
47 int album_sort;
48
49 guint covertree_notif;
50 guint sort_notif;
51 };
52
53 /* Tree view columns */
54 enum
55 {
56 ALBUM_VALUE_COLUMN,
57 ALBUM_CRITERIA_COLUMN,
58 ALBUM_TEXT_COLUMN,
59 ALBUM_ALBUM_COLUMN,
60 ALBUM_COVER_COLUMN,
61 ALBUM_N_COLUMN
62 };
63
64 #define ARIO_TREE_ALBUMS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_ARIO_TREE_ALBUMS, ArioTreeAlbumsPrivate))
G_DEFINE_TYPE(ArioTreeAlbums,ario_tree_albums,TYPE_ARIO_TREE)65 G_DEFINE_TYPE (ArioTreeAlbums, ario_tree_albums, TYPE_ARIO_TREE)
66
67 static void
68 ario_tree_albums_class_init (ArioTreeAlbumsClass *klass)
69 {
70 ARIO_LOG_FUNCTION_START;
71 GObjectClass *object_class = G_OBJECT_CLASS (klass);
72 ArioTreeClass *tree_class = ARIO_TREE_CLASS (klass);
73
74 /* GObject virtual methods */
75 object_class->finalize = ario_tree_albums_finalize;
76
77 /* ArioTree virtual methods */
78 tree_class->build_tree = ario_tree_albums_build_tree;
79 tree_class->fill_tree = ario_tree_albums_fill_tree;
80 tree_class->get_dnd_pixbuf = ario_tree_albums_get_dnd_pixbuf;
81
82 /* Private attributes */
83 g_type_class_add_private (klass, sizeof (ArioTreeAlbumsPrivate));
84 }
85
86 static gint
ario_tree_albums_sort_func(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,ArioTreeAlbums * tree)87 ario_tree_albums_sort_func (GtkTreeModel *model,
88 GtkTreeIter *a,
89 GtkTreeIter *b,
90 ArioTreeAlbums *tree)
91 {
92 ArioServerAlbum *aalbum;
93 ArioServerAlbum *balbum;
94 int ret;
95
96 /* Get info about frist album */
97 gtk_tree_model_get (model, a,
98 ALBUM_ALBUM_COLUMN, &aalbum,
99 -1);
100
101 /* Get info about second album */
102 gtk_tree_model_get (model, b,
103 ALBUM_ALBUM_COLUMN, &balbum,
104 -1);
105
106 if (tree->priv->album_sort == SORT_YEAR) {
107 /* Albums with no year set are at the end */
108 if (aalbum->date && !balbum->date)
109 ret = -1;
110 else if (balbum->date && !aalbum->date)
111 ret = 1;
112 else if (aalbum->date && balbum->date) {
113 /* Compare date of the two albums */
114 ret = g_utf8_collate (aalbum->date, balbum->date);
115 if (ret == 0) {
116 /* Date is the same, sort albums alphabetically */
117 ret = g_utf8_collate (aalbum->album, balbum->album);
118 }
119 } else {
120 /* No date is set, sort albums alphabetically */
121 ret = g_utf8_collate (aalbum->album, balbum->album);
122 }
123 } else {
124 /* Sort albums alphabetically */
125 ret = g_utf8_collate (aalbum->album, balbum->album);
126 }
127
128 return ret;
129 }
130
131 static void
ario_tree_albums_init(ArioTreeAlbums * tree)132 ario_tree_albums_init (ArioTreeAlbums *tree)
133 {
134 ARIO_LOG_FUNCTION_START;
135 tree->priv = ARIO_TREE_ALBUMS_GET_PRIVATE (tree);
136 }
137
138 static gboolean
ario_tree_albums_album_free(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer userdata)139 ario_tree_albums_album_free (GtkTreeModel *model,
140 GtkTreePath *path,
141 GtkTreeIter *iter,
142 gpointer userdata)
143 {
144 ARIO_LOG_FUNCTION_START;
145 ArioServerAlbum *album;
146 g_return_val_if_fail (IS_ARIO_TREE_ALBUMS (userdata), FALSE);
147
148 gtk_tree_model_get (model, iter, ALBUM_ALBUM_COLUMN, &album, -1);
149
150 ario_server_free_album (album);
151 return FALSE;
152 }
153
154 static void
ario_tree_albums_finalize(GObject * object)155 ario_tree_albums_finalize (GObject *object)
156 {
157 ARIO_LOG_FUNCTION_START;
158 ArioTreeAlbums *tree;
159
160 g_return_if_fail (object != NULL);
161 g_return_if_fail (IS_ARIO_TREE_ALBUMS (object));
162
163 tree = ARIO_TREE_ALBUMS (object);
164
165 g_return_if_fail (tree->priv != NULL);
166 /* Remove notificqtions */
167 if (tree->priv->covertree_notif)
168 ario_conf_notification_remove (tree->priv->covertree_notif);
169
170 if (tree->priv->sort_notif)
171 ario_conf_notification_remove (tree->priv->sort_notif);
172
173 /* Free tree data */
174 gtk_tree_model_foreach (GTK_TREE_MODEL (tree->parent.model),
175 (GtkTreeModelForeachFunc) ario_tree_albums_album_free,
176 tree);
177
178 G_OBJECT_CLASS (ario_tree_albums_parent_class)->finalize (object);
179 }
180
181 static void
ario_tree_albums_build_tree(ArioTree * parent_tree,GtkTreeView * treeview)182 ario_tree_albums_build_tree (ArioTree *parent_tree,
183 GtkTreeView *treeview)
184 {
185 ARIO_LOG_FUNCTION_START;
186 ArioTreeAlbums *tree;
187 GtkTreeViewColumn *column;
188 GtkCellRenderer *renderer;
189
190 g_return_if_fail (IS_ARIO_TREE_ALBUMS (parent_tree));
191 tree = ARIO_TREE_ALBUMS (parent_tree);
192
193 /* Create cover column */
194 renderer = gtk_cell_renderer_pixbuf_new ();
195 /* Translators - This "Cover" refers to an album cover art */
196 column = gtk_tree_view_column_new_with_attributes (_("Cover"),
197 renderer,
198 "pixbuf",
199 ALBUM_COVER_COLUMN, NULL);
200 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
201 gtk_tree_view_column_set_fixed_width (column, COVER_SIZE + 30);
202 gtk_tree_view_column_set_spacing (column, 0);
203 gtk_tree_view_append_column (GTK_TREE_VIEW (tree->parent.tree),
204 column);
205 gtk_tree_view_column_set_visible (column,
206 !ario_conf_get_boolean (PREF_COVER_TREE_HIDDEN, PREF_COVER_TREE_HIDDEN_DEFAULT));
207
208 /* Create text column */
209 renderer = gtk_cell_renderer_text_new ();
210 column = gtk_tree_view_column_new_with_attributes (_("Album"),
211 renderer,
212 "text", ALBUM_TEXT_COLUMN,
213 NULL);
214 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
215 gtk_tree_view_column_set_expand (column, TRUE);
216 gtk_tree_view_append_column (GTK_TREE_VIEW (tree->parent.tree), column);
217
218 /* Create model */
219 tree->parent.model = gtk_list_store_new (ALBUM_N_COLUMN,
220 G_TYPE_STRING,
221 G_TYPE_POINTER,
222 G_TYPE_STRING,
223 G_TYPE_POINTER,
224 GDK_TYPE_PIXBUF);
225 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (tree->parent.model),
226 ALBUM_TEXT_COLUMN, GTK_SORT_ASCENDING);
227 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (tree->parent.model),
228 ALBUM_TEXT_COLUMN,
229 (GtkTreeIterCompareFunc) ario_tree_albums_sort_func,
230 tree,
231 NULL);
232
233 /* Connect signal to update covers when they change */
234 g_signal_connect_object (ario_cover_handler_get_instance (),
235 "cover_changed", G_CALLBACK (ario_tree_albums_cover_changed_cb),
236 tree, 0);
237
238 tree->priv->covertree_notif = ario_conf_notification_add (PREF_COVER_TREE_HIDDEN,
239 (ArioNotifyFunc) ario_tree_albums_covertree_visible_changed_cb,
240 tree);
241
242 tree->priv->album_sort = ario_conf_get_integer (PREF_ALBUM_SORT, PREF_ALBUM_SORT_DEFAULT);
243 tree->priv->sort_notif = ario_conf_notification_add (PREF_ALBUM_SORT,
244 (ArioNotifyFunc) ario_tree_albums_album_sort_changed_cb,
245 tree);
246 }
247
248 static void
get_selected_albums_foreach(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer userdata)249 get_selected_albums_foreach (GtkTreeModel *model,
250 GtkTreePath *path,
251 GtkTreeIter *iter,
252 gpointer userdata)
253 {
254 ARIO_LOG_FUNCTION_START;
255 GSList **albums = (GSList **) userdata;
256
257 ArioServerAlbum *server_album;
258
259 /* Append album to the list */
260 gtk_tree_model_get (model, iter,
261 ALBUM_ALBUM_COLUMN, &server_album, -1);
262
263 *albums = g_slist_append (*albums, server_album);
264 }
265
266 static gboolean
ario_tree_albums_covers_update(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer userdata)267 ario_tree_albums_covers_update (GtkTreeModel *model,
268 GtkTreePath *path,
269 GtkTreeIter *iter,
270 gpointer userdata)
271 {
272 ARIO_LOG_FUNCTION_START;
273 ArioTreeAlbums *tree = ARIO_TREE_ALBUMS (userdata);
274 ArioServerAlbum *album;
275 gchar *cover_path;
276 GdkPixbuf *cover;
277
278 g_return_val_if_fail (IS_ARIO_TREE_ALBUMS (tree), FALSE);
279
280 gtk_tree_model_get (model, iter, ALBUM_ALBUM_COLUMN, &album, -1);
281
282 /* Get cover path */
283 cover_path = ario_cover_make_cover_path (album->artist, album->album, SMALL_COVER);
284
285 /* The small cover exists, we show it */
286 cover = gdk_pixbuf_new_from_file_at_size (cover_path, COVER_SIZE, COVER_SIZE, NULL);
287 g_free (cover_path);
288
289 if (!GDK_IS_PIXBUF (cover)) {
290 /* There is no cover, we show a transparent picture */
291 cover = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, COVER_SIZE, COVER_SIZE);
292 gdk_pixbuf_fill (cover, 0);
293 }
294
295 /* Set cover in tree */
296 gtk_list_store_set (tree->parent.model, iter,
297 ALBUM_COVER_COLUMN, cover,
298 -1);
299
300 g_object_unref (G_OBJECT (cover));
301
302 return FALSE;
303 }
304
305 static void
ario_tree_albums_cover_changed_cb(ArioCoverHandler * cover_handler,ArioTreeAlbums * tree)306 ario_tree_albums_cover_changed_cb (ArioCoverHandler *cover_handler,
307 ArioTreeAlbums *tree)
308 {
309 ARIO_LOG_FUNCTION_START;
310 /* Update all covers */
311 gtk_tree_model_foreach (GTK_TREE_MODEL (tree->parent.model),
312 (GtkTreeModelForeachFunc) ario_tree_albums_covers_update,
313 tree);
314 }
315
316 static void
ario_tree_albums_album_sort_changed_cb(guint notification_id,ArioTreeAlbums * tree)317 ario_tree_albums_album_sort_changed_cb (guint notification_id,
318 ArioTreeAlbums *tree)
319 {
320 /* Change albums sort order */
321 tree->priv->album_sort = ario_conf_get_integer (PREF_ALBUM_SORT, PREF_ALBUM_SORT_DEFAULT);
322 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (tree->parent.model),
323 ALBUM_TEXT_COLUMN,
324 (GtkTreeIterCompareFunc) ario_tree_albums_sort_func,
325 tree,
326 NULL);
327 }
328
329 static void
ario_tree_albums_add_next_albums(ArioTreeAlbums * tree,const GSList * albums,ArioServerCriteria * criteria)330 ario_tree_albums_add_next_albums (ArioTreeAlbums *tree,
331 const GSList *albums,
332 ArioServerCriteria *criteria)
333 {
334 ARIO_LOG_FUNCTION_START;
335 const GSList *tmp;
336 ArioServerAlbum *server_album;
337 GtkTreeIter album_iter;
338 gchar *cover_path;
339 gchar *album;
340 gchar *album_date;
341 GdkPixbuf *cover;
342
343 /* For each album */
344 for (tmp = albums; tmp; tmp = g_slist_next (tmp)) {
345 server_album = tmp->data;
346 album_date = NULL;
347
348 /* Get cover path */
349 cover_path = ario_cover_make_cover_path (server_album->artist, server_album->album, SMALL_COVER);
350
351 /* The small cover exists, we show it */
352 cover = gdk_pixbuf_new_from_file_at_size (cover_path, COVER_SIZE, COVER_SIZE, NULL);
353 g_free (cover_path);
354
355 if (!GDK_IS_PIXBUF (cover)) {
356 /* There is no cover, we show a transparent picture */
357 cover = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, COVER_SIZE, COVER_SIZE);
358 gdk_pixbuf_fill (cover, 0);
359 }
360
361 /* Display date if any */
362 if (server_album->date) {
363 album_date = g_strdup_printf ("%s (%s)", server_album->album, server_album->date);
364 album = album_date;
365 } else {
366 album = server_album->album;
367 }
368
369 /* Append album to tree */
370 gtk_list_store_append (tree->parent.model, &album_iter);
371 gtk_list_store_set (tree->parent.model, &album_iter,
372 ALBUM_VALUE_COLUMN, server_album->album,
373 ALBUM_CRITERIA_COLUMN, criteria,
374 ALBUM_TEXT_COLUMN, album,
375 ALBUM_ALBUM_COLUMN, server_album,
376 ALBUM_COVER_COLUMN, cover,
377 -1);
378 g_object_unref (cover);
379 g_free (album_date);
380 }
381 }
382
383 static void
ario_tree_albums_fill_tree(ArioTree * parent_tree)384 ario_tree_albums_fill_tree (ArioTree *parent_tree)
385 {
386 ARIO_LOG_FUNCTION_START;
387 ArioTreeAlbums *tree;
388 GSList *albums, *tmp;
389
390 g_return_if_fail (IS_ARIO_TREE_ALBUMS (parent_tree));
391 tree = ARIO_TREE_ALBUMS (parent_tree);
392
393 /* Free tree data */
394 gtk_tree_model_foreach (GTK_TREE_MODEL (tree->parent.model),
395 (GtkTreeModelForeachFunc) ario_tree_albums_album_free,
396 tree);
397
398 /* Empty tree */
399 gtk_list_store_clear (tree->parent.model);
400
401 /* For each criteria */
402 for (tmp = tree->parent.criterias; tmp; tmp = g_slist_next (tmp)) {
403 /* Append albums corresponding to criteria */
404 albums = ario_server_get_albums (tmp->data);
405 ario_tree_albums_add_next_albums (tree, albums, tmp->data);
406 g_slist_free (albums);
407 }
408 }
409
410 static GdkPixbuf*
ario_tree_albums_get_dnd_pixbuf(ArioTree * tree)411 ario_tree_albums_get_dnd_pixbuf (ArioTree *tree)
412 {
413 ARIO_LOG_FUNCTION_START;
414 GSList *albums = NULL;
415 GdkPixbuf *pixbuf;
416
417 /* Get list of selected albums */
418 gtk_tree_selection_selected_foreach (tree->selection,
419 get_selected_albums_foreach,
420 &albums);
421
422 /* Get dnd pixbuf using list of albums */
423 pixbuf = ario_util_get_dnd_pixbuf_from_albums (albums);
424 g_slist_free (albums);
425
426 return pixbuf;
427 }
428
429 void
ario_tree_albums_cmd_albums_properties(ArioTreeAlbums * tree)430 ario_tree_albums_cmd_albums_properties (ArioTreeAlbums *tree)
431 {
432 ARIO_LOG_FUNCTION_START;
433 GtkWidget *coverselect;
434 GSList *albums = NULL;
435 ArioServerAlbum *ario_server_album;
436
437 /* Get list of selected albums */
438 gtk_tree_selection_selected_foreach (tree->parent.selection,
439 get_selected_albums_foreach,
440 &albums);
441
442 /* Launch coverselect dialog using first album */
443 ario_server_album = albums->data;
444 coverselect = ario_shell_coverselect_new (ario_server_album);
445 gtk_dialog_run (GTK_DIALOG (coverselect));
446 gtk_widget_destroy (coverselect);
447
448 g_slist_free (albums);
449 }
450
451 static void
ario_tree_albums_covertree_visible_changed_cb(guint notification_id,ArioTree * tree)452 ario_tree_albums_covertree_visible_changed_cb (guint notification_id,
453 ArioTree *tree)
454 {
455 ARIO_LOG_FUNCTION_START;
456 /* Change cover column visibility */
457 gtk_tree_view_column_set_visible (gtk_tree_view_get_column (GTK_TREE_VIEW (tree->tree), 0),
458 !ario_conf_get_boolean (PREF_COVER_TREE_HIDDEN, PREF_COVER_TREE_HIDDEN_DEFAULT));
459 /* Update display */
460 ario_tree_fill (tree);
461 }
462
463