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-songs.h"
21 #include <gtk/gtk.h>
22 #include <string.h>
23 #include <glib/gi18n.h>
24 
25 #include "ario-debug.h"
26 #include "ario-util.h"
27 #include "covers/ario-cover.h"
28 #include "lib/ario-conf.h"
29 #include "preferences/ario-preferences.h"
30 #include "shell/ario-shell-songinfos.h"
31 
32 static void ario_tree_songs_build_tree (ArioTree *parent_tree,
33                                         GtkTreeView *treeview);
34 static void ario_tree_songs_fill_tree (ArioTree *parent_tree);
35 static void ario_tree_songs_get_drag_source (const GtkTargetEntry** targets,
36                                              int* n_targets);
37 static void ario_tree_songs_append_drag_data (ArioTree *tree,
38                                               GtkTreeModel *model,
39                                               GtkTreeIter *iter,
40                                               ArioTreeStringData *data);
41 static void ario_tree_songs_add_to_playlist (ArioTree *tree,
42                                              const PlaylistAction action);
43 
44 /* Tree columns */
45 enum
46 {
47         SONG_VALUE_COLUMN,
48         SONG_CRITERIA_COLUMN,
49         SONG_TRACK_COLUMN,
50         SONG_FILENAME_COLUMN,
51         SONG_CD_COLUMN,
52         SONG_N_COLUMN
53 };
54 
55 /* Drag and drop targets */
56 static const GtkTargetEntry songs_targets  [] = {
57         { "text/songs-list", 0, 0 },
58 };
59 
G_DEFINE_TYPE(ArioTreeSongs,ario_tree_songs,TYPE_ARIO_TREE)60 G_DEFINE_TYPE (ArioTreeSongs, ario_tree_songs, TYPE_ARIO_TREE)
61 
62 static void
63 ario_tree_songs_class_init (ArioTreeSongsClass *klass)
64 {
65         ARIO_LOG_FUNCTION_START;
66         ArioTreeClass *tree_class = ARIO_TREE_CLASS (klass);
67 
68         /* ArioTree virtual methods */
69         tree_class->build_tree = ario_tree_songs_build_tree;
70         tree_class->fill_tree = ario_tree_songs_fill_tree;
71         tree_class->get_drag_source = ario_tree_songs_get_drag_source;
72         tree_class->append_drag_data = ario_tree_songs_append_drag_data;
73         tree_class->add_to_playlist = ario_tree_songs_add_to_playlist;
74 }
75 
76 static void
ario_tree_songs_init(ArioTreeSongs * tree)77 ario_tree_songs_init (ArioTreeSongs *tree)
78 {
79         ARIO_LOG_FUNCTION_START;
80 }
81 
82 static gint
ario_tree_songs_try_numeric_compare(gchar * a_char,gchar * b_char)83 ario_tree_songs_try_numeric_compare(gchar *a_char, gchar *b_char)
84 {
85         gint a, b;
86         gchar *end_a, *end_b;
87 
88         /* Try to convert to int */
89         a = g_ascii_strtoll(a_char, &end_a, 10);
90         b = g_ascii_strtoll(b_char, &end_b, 10);
91 
92         if (a_char != end_a && b_char != end_b) {
93                 return a == b? 0: (a < b? -1: 1);
94         }
95 
96         return ario_util_strcmp (a_char, b_char);
97 }
98 
99 static gint
ario_tree_songs_sort_func(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,ArioTreeSongs * tree)100 ario_tree_songs_sort_func (GtkTreeModel *model,
101                            GtkTreeIter *a,
102                            GtkTreeIter *b,
103                            ArioTreeSongs *tree)
104 {
105         gchar *atrack, *btrack;
106         gchar *adisc, *bdisc;
107         int ret;
108 
109         /* Get info about frist song */
110         gtk_tree_model_get (model, a,
111                             SONG_TRACK_COLUMN, &atrack,
112                             SONG_CD_COLUMN, &adisc,
113                             -1);
114 
115         /* Get info about second song */
116         gtk_tree_model_get (model, b,
117                             SONG_TRACK_COLUMN, &btrack,
118                             SONG_CD_COLUMN, &bdisc,
119                             -1);
120 
121         /* Songs with no disc set are at the end */
122         if (adisc && !bdisc)
123                 ret = -1;
124         else if (bdisc && !adisc)
125                 ret = 1;
126         else if (adisc && bdisc) {
127                 /* Compare disc of the two albums */
128                 ret = ario_tree_songs_try_numeric_compare (adisc, bdisc);
129                 if (ret == 0) {
130                         /* Disc is the same, sort songs by track */
131                         ret = ario_tree_songs_try_numeric_compare (atrack, btrack);
132                 }
133         } else {
134                 /* No disc is set, sort songs by track */
135                 ret = ario_tree_songs_try_numeric_compare (atrack, btrack);
136         }
137 
138         return ret;
139 }
140 
141 static void
ario_tree_songs_build_tree(ArioTree * parent_tree,GtkTreeView * treeview)142 ario_tree_songs_build_tree (ArioTree *parent_tree,
143                             GtkTreeView *treeview)
144 {
145         ARIO_LOG_FUNCTION_START;
146         ArioTreeSongs *tree;
147         GtkTreeViewColumn *column;
148         GtkCellRenderer *renderer;
149 
150         g_return_if_fail (IS_ARIO_TREE_SONGS (parent_tree));
151         tree = ARIO_TREE_SONGS (parent_tree);
152 
153         /* Create track column */
154         renderer = gtk_cell_renderer_text_new ();
155         column = gtk_tree_view_column_new_with_attributes (_("Track"),
156                                                            renderer,
157                                                            "text", SONG_TRACK_COLUMN,
158                                                            NULL);
159         gtk_tree_view_append_column (GTK_TREE_VIEW (tree->parent.tree), column);
160         /* Create title column */
161         renderer = gtk_cell_renderer_text_new ();
162         column = gtk_tree_view_column_new_with_attributes (_("Title"),
163                                                            renderer,
164                                                            "text", SONG_VALUE_COLUMN,
165                                                            NULL);
166         gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
167         gtk_tree_view_append_column (GTK_TREE_VIEW (tree->parent.tree), column);
168 
169         /* Create model */
170         tree->parent.model = gtk_list_store_new (SONG_N_COLUMN,
171                                                  G_TYPE_STRING,
172                                                  G_TYPE_POINTER,
173                                                  G_TYPE_STRING,
174                                                  G_TYPE_STRING,
175                                                  G_TYPE_STRING);
176         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (tree->parent.model),
177                                               SONG_TRACK_COLUMN,
178                                               GTK_SORT_ASCENDING);
179         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (tree->parent.model),
180                                          SONG_TRACK_COLUMN,
181                                          (GtkTreeIterCompareFunc) ario_tree_songs_sort_func,
182                                          tree,
183                                          NULL);
184 }
185 
186 static void
ario_tree_songs_add_next_songs(ArioTreeSongs * tree,const GSList * songs,ArioServerCriteria * criteria)187 ario_tree_songs_add_next_songs (ArioTreeSongs *tree,
188                                 const GSList *songs,
189                                 ArioServerCriteria *criteria)
190 {
191         ARIO_LOG_FUNCTION_START;
192         const GSList *tmp;
193         ArioServerSong *song;
194         GtkTreeIter iter;
195         gchar track[ARIO_MAX_TRACK_SIZE];
196         gchar *title;
197 
198         /* For each song */
199         for (tmp = songs; tmp; tmp = g_slist_next (tmp)) {
200                 song = tmp->data;
201                 /* Append song to tree */
202                 gtk_list_store_append (tree->parent.model, &iter);
203 
204                 ario_util_format_track_buf (song->track, track, ARIO_MAX_TRACK_SIZE);
205                 title = ario_util_format_title (song);
206                 gtk_list_store_set (tree->parent.model, &iter,
207                                     SONG_VALUE_COLUMN, title,
208                                     SONG_CRITERIA_COLUMN, criteria,
209                                     SONG_TRACK_COLUMN, track,
210                                     SONG_FILENAME_COLUMN, song->file,
211                                     SONG_CD_COLUMN, song->disc,
212                                     -1);
213         }
214 }
215 
216 static void
ario_tree_songs_fill_tree(ArioTree * parent_tree)217 ario_tree_songs_fill_tree (ArioTree *parent_tree)
218 {
219         ARIO_LOG_FUNCTION_START;
220         ArioTreeSongs *tree;
221         GSList *songs, *tmp;
222 
223         g_return_if_fail (IS_ARIO_TREE_SONGS (parent_tree));
224         tree = ARIO_TREE_SONGS (parent_tree);
225 
226         /* Empty tree */
227         gtk_list_store_clear (tree->parent.model);
228 
229         /* For each criteria */
230         for (tmp = tree->parent.criterias; tmp; tmp = g_slist_next (tmp)) {
231                 /* Get songs corresponding to criteria */
232                 songs = ario_server_get_songs (tmp->data, TRUE);
233 
234                 /* Add songs to playlist */
235                 ario_tree_songs_add_next_songs (tree, songs, tmp->data);
236                 g_slist_foreach (songs, (GFunc) ario_server_free_song, NULL);
237                 g_slist_free (songs);
238         }
239 }
240 
241 static void
ario_tree_songs_get_drag_source(const GtkTargetEntry ** targets,int * n_targets)242 ario_tree_songs_get_drag_source (const GtkTargetEntry** targets,
243                                  int* n_targets)
244 {
245         ARIO_LOG_FUNCTION_START;
246         *targets = songs_targets;
247         *n_targets = G_N_ELEMENTS (songs_targets);
248 }
249 
250 static void
ario_tree_songs_append_drag_data(ArioTree * tree,GtkTreeModel * model,GtkTreeIter * iter,ArioTreeStringData * data)251 ario_tree_songs_append_drag_data (ArioTree *tree,
252                                   GtkTreeModel *model,
253                                   GtkTreeIter *iter,
254                                   ArioTreeStringData *data)
255 {
256         ARIO_LOG_FUNCTION_START;
257         gchar *val;
258 
259         /* Append filename to drag data */
260         gtk_tree_model_get (model, iter, SONG_FILENAME_COLUMN, &val, -1);
261         g_string_append (data->string, val);
262         g_string_append (data->string, "\n");
263 
264         g_free (val);
265 }
266 
267 static void
get_selected_songs_foreach(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer userdata)268 get_selected_songs_foreach (GtkTreeModel *model,
269                             GtkTreePath *path,
270                             GtkTreeIter *iter,
271                             gpointer userdata)
272 {
273         ARIO_LOG_FUNCTION_START;
274         GSList **songs = (GSList **) userdata;
275         gchar *val = NULL;
276 
277         /* Append filename to list */
278         gtk_tree_model_get (model, iter, SONG_FILENAME_COLUMN, &val, -1);
279         *songs = g_slist_append (*songs, val);
280 }
281 
282 void
ario_tree_songs_cmd_songs_properties(ArioTreeSongs * tree)283 ario_tree_songs_cmd_songs_properties (ArioTreeSongs *tree)
284 {
285         ARIO_LOG_FUNCTION_START;
286         GSList *paths = NULL;
287         GtkWidget *songinfos;
288 
289         /* Get filenames of each selected songs */
290         gtk_tree_selection_selected_foreach (tree->parent.selection,
291                                              get_selected_songs_foreach,
292                                              &paths);
293 
294         if (paths) {
295                 /* Launch songinfos dialog on slected songs */
296                 songinfos = ario_shell_songinfos_new (paths);
297                 if (songinfos)
298                         gtk_widget_show_all (songinfos);
299 
300                 g_slist_foreach (paths, (GFunc) g_free, NULL);
301                 g_slist_free (paths);
302         }
303 }
304 
305 static void
ario_tree_songs_add_to_playlist(ArioTree * tree,const PlaylistAction action)306 ario_tree_songs_add_to_playlist (ArioTree *tree,
307                                  const PlaylistAction action)
308 {
309         ARIO_LOG_FUNCTION_START;
310         GSList *songs = NULL;
311 
312         /* Get filenames of each selected songs */
313         gtk_tree_selection_selected_foreach (tree->selection,
314                                              get_selected_songs_foreach,
315                                              &songs);
316 
317         if (songs) {
318                 /* Append songs to playlist */
319                 ario_server_playlist_append_songs (songs, action);
320 
321                 g_slist_foreach (songs, (GFunc) g_free, NULL);
322                 g_slist_free (songs);
323         }
324 
325 }
326