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