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