1 /*
2 |  Copyright (C) 2002-2007 Jorg Schuler <jcsjcs at users sourceforge net>
3 |  Part of the gtkpod project.
4 |
5 |  URL: http://www.gtkpod.org/
6 |  URL: http://gtkpod.sourceforge.net/
7 |
8 |  This program is free software; you can redistribute it and/or modify
9 |  it under the terms of the GNU General Public License as published by
10 |  the Free Software Foundation; either version 2 of the License, or
11 |  (at your option) any later version.
12 |
13 |  This program is distributed in the hope that it will be useful,
14 |  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 |  GNU General Public License for more details.
17 |
18 |  You should have received a copy of the GNU General Public License
19 |  along with this program; if not, write to the Free Software
20 |  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 |
22 |  iTunes and iPod are trademarks of Apple
23 |
24 |  This product is not supported/written/published by Apple!
25 |
26 |  $Id$
27 */
28 
29 #ifdef HAVE_CONFIG_H
30 #  include <config.h>
31 #endif
32 
33 #include <gdk/gdkkeysyms.h>
34 #include <gtk/gtk.h>
35 #include <pango/pango.h>
36 #include <limits.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <unistd.h>
43 
44 #include "prefs.h"
45 #include "details.h"
46 #include "display_private.h"
47 #include "display_itdb.h"
48 #include "itdb.h"
49 #include "info.h"
50 #include "misc.h"
51 #include "misc_track.h"
52 #include "file.h"
53 #include "context_menus.h"
54 #include "rb_cell_renderer_rating.h"
55 
56 /* pointer to the treeview for the track display */
57 static GtkTreeView *track_treeview = NULL;
58 /* array with pointers to the columns used in the track display */
59 static GtkTreeViewColumn *tm_columns[TM_NUM_COLUMNS];
60 /* column in which track pointer is stored */
61 static const gint READOUT_COL = 0;
62 
63 /* compare function to be used for string comparisons */
64 static gint (*string_compare_func) (const gchar *str1, const gchar *str2) = compare_string;
65 
66 static GtkTreeViewColumn *tm_add_column (TM_item tm_item, gint position);
67 static TM_item tm_lookup_col_id (GtkTreeViewColumn *column);
68 
69 /* Drag and drop definitions */
70 static GtkTargetEntry tm_drag_types [] = {
71     { DND_GTKPOD_TM_PATHLIST_TYPE, 0, DND_GTKPOD_TM_PATHLIST },
72     { DND_GTKPOD_TRACKLIST_TYPE, 0, DND_GTKPOD_TRACKLIST },
73     { "text/uri-list", 0, DND_TEXT_URI_LIST },
74     { "text/plain", 0, DND_TEXT_PLAIN },
75     { "STRING", 0, DND_TEXT_PLAIN }
76 };
77 static GtkTargetEntry tm_drop_types [] = {
78     { DND_GTKPOD_TM_PATHLIST_TYPE, 0, DND_GTKPOD_TM_PATHLIST },
79     { "text/uri-list", 0, DND_TEXT_URI_LIST },
80     { "text/plain", 0, DND_TEXT_PLAIN },
81     { "STRING", 0, DND_TEXT_PLAIN }
82 };
83 
84 /* prefs strings */
85 const gchar *TM_PREFS_SEARCH_COLUMN = "tm_prefs_search_column";
86 const gchar *KEY_DISPLAY_SEARCH_ENTRY ="display_search_entry";
87 
88 /* Convenience functions */
filter_tracks(GtkTreeModel * model,GtkTreeIter * iter,gpointer entry)89 static gboolean filter_tracks (GtkTreeModel *model, GtkTreeIter *iter, gpointer entry)
90 {
91     Track *tr;
92 	gboolean result = FALSE;
93 	const gchar *text = gtk_entry_get_text (GTK_ENTRY (entry));
94 	int i;
95 
96 	gtk_tree_model_get(model, iter, READOUT_COL, &tr, -1);
97 
98 	if (tr)
99 	{
100 	    if (text[0] == 0x0)   return TRUE;
101 	    for (i = 0; i < TM_NUM_COLUMNS; i++)
102 	    {
103 		gint visible = prefs_get_int_index("col_visible", i);
104 		gchar *data;
105 
106 		if (!visible)
107 			continue;
108 
109 		data = track_get_text (tr, TM_to_T (i));
110 
111 		if (data && utf8_strcasestr (data, text))
112 		{
113 			g_free (data);
114 			result = TRUE;
115 			break;
116 		}
117 
118 		g_free (data);
119 	    }
120 	}
121 
122 	return result;
123 }
124 
get_model_as_store(GtkTreeModel * model)125 static GtkListStore *get_model_as_store (GtkTreeModel *model)
126 {
127 	if (!GTK_IS_TREE_MODEL_FILTER (model))
128 		return GTK_LIST_STORE (model);
129 	else
130 		return GTK_LIST_STORE (gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model)));
131 }
132 
convert_iter(GtkTreeModel * model,GtkTreeIter * from,GtkTreeIter * to)133 static void convert_iter (GtkTreeModel *model, GtkTreeIter *from, GtkTreeIter *to)
134 {
135     if (!GTK_IS_TREE_MODEL_FILTER (model))
136         *to = *from;
137     else
138         gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), to, from);
139 }
140 
141 /*static void update_model_view (GtkTreeModel *model)
142 {
143     if (GTK_IS_TREE_MODEL_FILTER (model))
144         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
145 }*/
146 
get_filter(GtkTreeView * tree)147 static GtkTreeModelFilter *get_filter (GtkTreeView *tree)
148 {
149 	GtkTreeModel *model = gtk_tree_view_get_model (tree);
150 
151 	if (GTK_IS_TREE_MODEL_FILTER (model))
152 		return GTK_TREE_MODEL_FILTER (model);
153 	else
154 	{
155 		GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (model, NULL));
156 		GtkWidget *search_entry = gtkpod_xml_get_widget (main_window_xml, "search_entry");
157 
158 		gtk_tree_model_filter_set_visible_func (filter, filter_tracks, search_entry, NULL);
159 		gtk_tree_model_filter_refilter (filter);
160 		gtk_tree_view_set_model (tree, GTK_TREE_MODEL (filter));
161 
162 		return filter;
163 	}
164 }
165 
on_search_entry_changed(GtkEditable * editable,gpointer user_data)166 G_MODULE_EXPORT void on_search_entry_changed (GtkEditable *editable, gpointer user_data)
167 {
168 	gtk_tree_model_filter_refilter (get_filter (track_treeview));
169 }
170 
on_searchbar_down_button_clicked(GtkWidget * widget,gpointer data)171 G_MODULE_EXPORT void on_searchbar_down_button_clicked (GtkWidget *widget, gpointer data)
172 {
173 	prefs_set_int (KEY_DISPLAY_SEARCH_ENTRY, FALSE);
174 
175 	display_show_hide_searchbar ();
176 }
177 
on_searchbar_up_button_clicked(GtkWidget * widget,gpointer data)178 G_MODULE_EXPORT void on_searchbar_up_button_clicked (GtkWidget *widget, gpointer data)
179 {
180 	prefs_set_int (KEY_DISPLAY_SEARCH_ENTRY, TRUE);
181 
182 	display_show_hide_searchbar ();
183 }
184 
185 /* ---------------------------------------------------------------- */
186 /* Section for track display                                        */
187 /* DND -- Drag And Drop                                             */
188 /* ---------------------------------------------------------------- */
189 
190 /* Move the paths listed in @data before or after (according to @pos)
191    @path. Used for DND */
tm_move_pathlist(gchar * data,GtkTreePath * path,GtkTreeViewDropPosition pos)192 static gboolean tm_move_pathlist (gchar *data,
193 				  GtkTreePath *path,
194 				  GtkTreeViewDropPosition pos)
195 {
196     GtkTreeIter temp;
197     GtkTreeIter to_iter;
198     GtkTreeIter *from_iter;
199     GtkTreeModel *model;
200     GtkListStore *store;
201     GList *iterlist = NULL;
202     GList *link;
203     gchar **paths, **pathp;
204 
205     g_return_val_if_fail (data, FALSE);
206     g_return_val_if_fail (*data, FALSE);
207 
208     model = gtk_tree_view_get_model (track_treeview);
209     g_return_val_if_fail (model, FALSE);
210     store = get_model_as_store (model);
211     g_return_val_if_fail (store, FALSE);
212 
213     g_return_val_if_fail (gtk_tree_model_get_iter (model, &temp, path), FALSE);
214     convert_iter (model, &temp, &to_iter);
215 
216     /* split the path list into individual strings */
217     paths = g_strsplit (data, "\n", -1);
218     pathp = paths;
219 
220     /* Convert the list of paths into a list of iters */
221     while (*pathp)
222     {
223         if ((strlen (*pathp) > 0) && gtk_tree_model_get_iter_from_string (model, &temp, *pathp))
224         {
225             from_iter = g_new (GtkTreeIter, 1);
226             convert_iter (model, &temp, from_iter);
227             iterlist = g_list_append (iterlist, from_iter);
228         }
229 
230         ++pathp;
231     }
232 
233     g_strfreev (paths);
234 
235     /* Move the iters in iterlist before or after @to_iter */
236     switch (pos)
237     {
238     case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
239     case GTK_TREE_VIEW_DROP_AFTER:
240     for (link = g_list_last (iterlist); link; link = link->prev)
241     {
242         from_iter = (GtkTreeIter *)link->data;
243         gtk_list_store_move_after (store, from_iter, &to_iter);
244     }
245     break;
246     case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
247     case GTK_TREE_VIEW_DROP_BEFORE:
248     for (link = g_list_first (iterlist); link; link = link->next)
249     {
250         from_iter = (GtkTreeIter *)link->data;
251         gtk_list_store_move_before (store,
252                     from_iter, &to_iter);
253     }
254     break;
255     }
256 
257     /* free iterlist */
258     for (link = iterlist; link; link = link->next)
259 	g_free (link->data);
260     g_list_free (iterlist);
261 
262 /*    update_model_view (model); -- not needed */
263     tm_rows_reordered ();
264     return TRUE;
265 }
266 
267 
268 /*
269  * utility function for appending ipod track ids for track view (DND)
270  */
271 static void
on_tm_dnd_get_track_foreach(GtkTreeModel * tm,GtkTreePath * tp,GtkTreeIter * i,gpointer data)272 on_tm_dnd_get_track_foreach(GtkTreeModel *tm, GtkTreePath *tp,
273 			    GtkTreeIter *i, gpointer data)
274 {
275     Track *tr;
276     GString *tracklist = (GString *)data;
277 
278     g_return_if_fail (tracklist);
279 
280     gtk_tree_model_get(tm, i, READOUT_COL, &tr, -1);
281     g_return_if_fail (tr);
282 
283     g_string_append_printf (tracklist, "%p\n", tr);
284 }
285 
286 
287 /*
288  * utility function for appending path for track view (DND)
289  */
290 static void
on_tm_dnd_get_path_foreach(GtkTreeModel * tm,GtkTreePath * tp,GtkTreeIter * iter,gpointer data)291 on_tm_dnd_get_path_foreach(GtkTreeModel *tm, GtkTreePath *tp,
292 			   GtkTreeIter *iter, gpointer data)
293 {
294     GString *filelist = (GString *)data;
295     gchar *ps = gtk_tree_path_to_string (tp);
296     g_string_append_printf (filelist, "%s\n", ps);
297     g_free (ps);
298 }
299 
300 /*
301  * utility function for appending file for track view (DND)
302  */
303 static void
on_tm_dnd_get_file_foreach(GtkTreeModel * tm,GtkTreePath * tp,GtkTreeIter * iter,gpointer data)304 on_tm_dnd_get_file_foreach(GtkTreeModel *tm, GtkTreePath *tp,
305 			   GtkTreeIter *iter, gpointer data)
306 {
307     Track *track;
308     GString *filelist = (GString *)data;
309     gchar *name;
310 
311     gtk_tree_model_get(tm, iter, READOUT_COL, &track, -1);
312     name = get_file_name_from_source (track, SOURCE_PREFER_LOCAL);
313     if (name)
314     {
315 	g_string_append_printf (filelist, "file:%s\n", name);
316 	g_free (name);
317     }
318 }
319 
320 /*
321  * utility function for appending file-uri for track view (DND)
322  */
323 static void
on_tm_dnd_get_uri_foreach(GtkTreeModel * tm,GtkTreePath * tp,GtkTreeIter * iter,gpointer data)324 on_tm_dnd_get_uri_foreach(GtkTreeModel *tm, GtkTreePath *tp,
325 			  GtkTreeIter *iter, gpointer data)
326 {
327     Track *track;
328     GString *filelist = (GString *)data;
329     gchar *name;
330 
331     gtk_tree_model_get(tm, iter, READOUT_COL, &track, -1);
332     name = get_file_name_from_source (track, SOURCE_PREFER_LOCAL);
333     if (name)
334     {
335 	gchar *uri = g_filename_to_uri (name, NULL, NULL);
336 	if (uri)
337 	{
338 	    g_string_append_printf (filelist, "%s\n", uri);
339 	    g_free (uri);
340 	}
341 	g_free (name);
342     }
343 }
344 
tm_drag_begin(GtkWidget * widget,GdkDragContext * dc,gpointer user_data)345 static void tm_drag_begin (GtkWidget *widget,
346 			   GdkDragContext *dc,
347 			   gpointer user_data)
348 {
349     tm_stop_editing (TRUE);
350 }
351 
352 
353 /* remove dragged playlist after successful MOVE */
tm_drag_data_delete(GtkWidget * widget,GdkDragContext * dc,gpointer user_data)354 static void tm_drag_data_delete (GtkWidget *widget,
355 			   GdkDragContext *dc,
356 			   gpointer user_data)
357 {
358     GtkTreeSelection *ts;
359     Playlist *pl = pm_get_selected_playlist ();
360     gint num;
361 
362 /*     puts ("tm_drag_data_delete"); */
363 
364     g_return_if_fail (widget);
365     ts = gtk_tree_view_get_selection (GTK_TREE_VIEW(widget));
366     g_return_if_fail (ts);
367     /* number of selected tracks */
368     num = gtk_tree_selection_count_selected_rows (ts);
369     if (num == 0) return;
370 
371     /* Check if we really have to delete the tracks */
372     if (!itdb_playlist_is_mpl (pl))
373     {	/* get list of selected tracks */
374 	GString *reply = g_string_sized_new (2000);
375 	gchar *str;
376 	Track *track;
377 
378 	gtk_tree_selection_selected_foreach(ts,
379 					    on_tm_dnd_get_track_foreach,
380 					    reply);
381 	str = reply->str;
382 	while(parse_tracks_from_string(&str, &track))
383 	{
384 	    gp_playlist_remove_track (pl, track, DELETE_ACTION_PLAYLIST);
385 	}
386 	g_string_free (reply, TRUE);
387 
388 	gtkpod_statusbar_message (ngettext ("Moved one track",
389 					    "Moved %d tracks", num), num);
390     }
391     else
392     {
393 	gtkpod_statusbar_message (ngettext ("Copied one track",
394 					    "Copied %d tracks", num), num);
395     }
396 }
397 
398 
399 
tm_drag_end(GtkWidget * widget,GdkDragContext * dc,gpointer user_data)400 static void tm_drag_end (GtkWidget *widget,
401 			 GdkDragContext *dc,
402 			 gpointer user_data)
403 {
404 /*     puts ("tm_drag_end"); */
405     display_remove_autoscroll_row_timeout (widget);
406     gtkpod_tracks_statusbar_update ();
407 }
408 
409 
tm_drag_drop(GtkWidget * widget,GdkDragContext * dc,gint x,gint y,guint time,gpointer user_data)410 static gboolean tm_drag_drop (GtkWidget *widget,
411 			      GdkDragContext *dc,
412 			      gint x,
413 			      gint y,
414 			      guint time,
415 			      gpointer user_data)
416 {
417     GdkAtom target;
418 
419 /*     puts ("tm_drag_data_drop"); */
420 
421     display_remove_autoscroll_row_timeout (widget);
422 
423     target = gtk_drag_dest_find_target (widget, dc, NULL);
424 
425     if (target != GDK_NONE)
426     {
427 	gtk_drag_get_data (widget, dc, target, time);
428 	return TRUE;
429     }
430     return FALSE;
431 }
432 
tm_drag_leave(GtkWidget * widget,GdkDragContext * dc,guint time,gpointer user_data)433 static void tm_drag_leave (GtkWidget *widget,
434 			   GdkDragContext *dc,
435 			   guint time,
436 			   gpointer user_data)
437 {
438 /*     puts ("tm_drag_leave"); */
439     display_remove_autoscroll_row_timeout (widget);
440 }
441 
442 
443 
tm_drag_motion(GtkWidget * widget,GdkDragContext * dc,gint x,gint y,guint time,gpointer user_data)444 static gboolean tm_drag_motion (GtkWidget *widget,
445 				GdkDragContext *dc,
446 				gint x,
447 				gint y,
448 				guint time,
449 				gpointer user_data)
450 {
451     GtkTreeView *treeview;
452     GdkAtom target;
453     GtkTreePath *path = NULL;
454     GtkTreeViewDropPosition pos;
455     iTunesDB *itdb;
456     ExtraiTunesDBData *eitdb;
457 
458 /*     printf ("drag_motion  suggested: %d actions: %d\n", */
459 /*  	    dc->suggested_action, dc->actions); */
460 
461 /*     printf ("x: %d y: %d\n", x, y); */
462 
463     g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
464 
465     treeview = GTK_TREE_VIEW (widget);
466 
467     display_install_autoscroll_row_timeout (widget);
468 
469     itdb = gp_get_selected_itdb ();
470     /* no drop is possible if no playlist/repository is selected */
471     if (itdb == NULL)
472     {
473 	gdk_drag_status (dc, 0, time);
474 	return FALSE;
475     }
476     eitdb = itdb->userdata;
477     g_return_val_if_fail (eitdb, FALSE);
478     /* no drop is possible if no repository is loaded */
479     if (!eitdb->itdb_imported)
480     {
481 	gdk_drag_status (dc, 0, time);
482 	return FALSE;
483     }
484 
485     /* optically set destination row if available */
486     if (gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
487 					   x, y, &path, &pos))
488     {
489 	/* drops are only allowed before and after -- not onto
490 	   existing paths */
491 	switch (pos)
492 	{
493 	case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
494 	case GTK_TREE_VIEW_DROP_AFTER:
495 	    gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), path,
496 					     GTK_TREE_VIEW_DROP_AFTER);
497 	    break;
498 	case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
499 	case GTK_TREE_VIEW_DROP_BEFORE:
500 	    gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), path,
501 					     GTK_TREE_VIEW_DROP_BEFORE);
502 	    break;
503 	}
504 
505  	gtk_tree_path_free (path);
506  	path = NULL;
507     }
508     else
509     {
510 	path = gtk_tree_path_new_first ();
511 	gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), path,
512 					 GTK_TREE_VIEW_DROP_BEFORE);
513 	gtk_tree_path_free (path);
514 	path = NULL;
515     }
516 
517     target = gtk_drag_dest_find_target (widget, dc, NULL);
518 
519     /* no drop possible if no valid target can be found */
520     if (target == GDK_NONE)
521     {
522 	gdk_drag_status (dc, 0, time);
523 	return FALSE;
524     }
525 
526     if (widget == gtk_drag_get_source_widget (dc))
527     {   /* drag is within the same widget */
528 	gint column;
529 	GtkSortType order;
530 	GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
531 	g_return_val_if_fail (model, FALSE);
532 	if(gtk_tree_sortable_get_sort_column_id (
533 	       GTK_TREE_SORTABLE (model), &column, &order))
534 	{   /* don't allow move because the model is sorted */
535 	    gdk_drag_status (dc, 0, time);
536 	    return FALSE;
537 	}
538 	else
539 	{   /* only allow moves within the same widget */
540 	    gdk_drag_status (dc, GDK_ACTION_MOVE, time);
541 	}
542     }
543     else
544     {  /* whatever the source suggests */
545 	gdk_drag_status (dc, dc->suggested_action, time);
546     }
547 
548     return TRUE;
549 }
550 
551 
tm_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * data,guint info,guint time,gpointer user_data)552 static void tm_drag_data_get (GtkWidget       *widget,
553 			      GdkDragContext  *context,
554 			      GtkSelectionData *data,
555 			      guint            info,
556 			      guint            time,
557 			      gpointer         user_data)
558 {
559     GtkTreeSelection *ts = NULL;
560     GString *reply = g_string_sized_new (2000);
561 
562 /*     printf("tm drag get info: %d\n", info); */
563     if((data) && (ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget))))
564     {
565 	switch (info)
566 	{
567 	case DND_GTKPOD_TRACKLIST:
568 	    gtk_tree_selection_selected_foreach(ts,
569 				    on_tm_dnd_get_track_foreach, reply);
570 	    break;
571 	case DND_GTKPOD_TM_PATHLIST:
572 	    gtk_tree_selection_selected_foreach(ts,
573 				    on_tm_dnd_get_path_foreach, reply);
574 	    break;
575 	case DND_TEXT_URI_LIST:
576 	    gtk_tree_selection_selected_foreach(ts,
577 				    on_tm_dnd_get_uri_foreach, reply);
578 	    break;
579 	case DND_TEXT_PLAIN:
580 	    gtk_tree_selection_selected_foreach(ts,
581 				    on_tm_dnd_get_file_foreach, reply);
582 	    break;
583 	default:
584 	    g_warning ("Programming error: tm_drag_data_get received unknown info type (%d)\n", info);
585 	    break;
586 	}
587     }
588     gtk_selection_data_set(data, data->target, 8, reply->str, reply->len);
589     g_string_free (reply, TRUE);
590 }
591 
tm_drag_data_received(GtkWidget * widget,GdkDragContext * dc,gint x,gint y,GtkSelectionData * data,guint info,guint time,gpointer user_data)592 static void tm_drag_data_received (GtkWidget       *widget,
593 				   GdkDragContext  *dc,
594 				   gint             x,
595 				   gint             y,
596 				   GtkSelectionData *data,
597 				   guint            info,
598 				   guint            time,
599 				   gpointer         user_data)
600 {
601     GtkTreePath *path = NULL;
602     GtkTreeModel *model = NULL;
603     GtkTreeViewDropPosition pos = 0;
604     gboolean result = FALSE;
605 
606     /* printf ("sm drop received info: %d\n", info); */
607 
608     /* sometimes we get empty dnd data, ignore */
609     if(widgets_blocked || (!dc) ||
610        (!data) || (data->length < 0)) return;
611     /* yet another check, i think it's an 8 bit per byte check */
612     if(data->format != 8) return;
613 
614     display_remove_autoscroll_row_timeout (widget);
615 
616     model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
617     g_return_if_fail (model);
618     if (!gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
619 					    x, y, &path, &pos))
620     {
621 	gint py;
622 	gdk_window_get_pointer (
623 	    gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget)),
624 	    NULL, &py, NULL);
625 	if (py < 5)
626 	{
627 	    /* initialize with first displayed and drop before */
628 	    GtkTreeIter iter;
629 	    if (gtk_tree_model_get_iter_first (model, &iter))
630 	    {
631 		path = gtk_tree_model_get_path (model, &iter);
632 		pos = GTK_TREE_VIEW_DROP_BEFORE;
633 	    }
634 	}
635 	else
636 	{   /* initialize with last path if available and drop after */
637 	    GtkTreeIter iter;
638 	    if (gtk_tree_model_get_iter_first (model, &iter))
639 	    {
640 		GtkTreeIter last_valid_iter;
641 		do
642 		{
643 		    last_valid_iter = iter;
644 		} while (gtk_tree_model_iter_next (model, &iter));
645 		path = gtk_tree_model_get_path (model, &last_valid_iter);
646 		pos = GTK_TREE_VIEW_DROP_AFTER;
647 	    }
648 	}
649     }
650 
651     if (path)
652     {   /* map position onto BEFORE or AFTER */
653 	switch (pos)
654 	{
655 	case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
656 	case GTK_TREE_VIEW_DROP_AFTER:
657 	    pos = GTK_TREE_VIEW_DROP_AFTER;
658 	    break;
659 	case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
660 	case GTK_TREE_VIEW_DROP_BEFORE:
661 	    pos = GTK_TREE_VIEW_DROP_BEFORE;
662 	    break;
663 	}
664     }
665 
666     switch (info)
667     {
668     case DND_GTKPOD_TM_PATHLIST:
669 	g_return_if_fail (path);
670 	result = tm_move_pathlist (data->data, path, pos);
671 	dc->action = GDK_ACTION_MOVE;
672 	gtk_drag_finish (dc, TRUE, FALSE, time);
673 	break;
674     case DND_TEXT_PLAIN:
675 	result = tm_add_filelist (data->data, path, pos);
676 	dc->action = dc->suggested_action;
677 	if (dc->action == GDK_ACTION_MOVE)
678 	    gtk_drag_finish (dc, TRUE, TRUE, time);
679 	else
680 	    gtk_drag_finish (dc, TRUE, FALSE, time);
681 	break;
682     case DND_TEXT_URI_LIST:
683 	result = tm_add_filelist (data->data, path, pos);
684 	dc->action = dc->suggested_action;
685 	if (dc->action == GDK_ACTION_MOVE)
686 	    gtk_drag_finish (dc, TRUE, TRUE, time);
687 	else
688 	    gtk_drag_finish (dc, TRUE, FALSE, time);
689 	break;
690     default:
691 	dc->action = 0;
692 	gtk_drag_finish (dc, FALSE, FALSE, time);
693 /* 	puts ("tm_drag_data_received(): should not be reached"); */
694 	break;
695     }
696     if (path) gtk_tree_path_free(path);
697 }
698 
699 /* ---------------------------------------------------------------- */
700 /* Section for track display                                        */
701 /* other callbacks                                                  */
702 /* ---------------------------------------------------------------- */
703 
704 static gboolean
on_track_treeview_key_release_event(GtkWidget * widget,GdkEventKey * event,gpointer user_data)705 on_track_treeview_key_release_event     (GtkWidget       *widget,
706 					GdkEventKey     *event,
707 					gpointer         user_data)
708 {
709     guint mods;
710     mods = event->state;
711 
712     if(!widgets_blocked && (mods & GDK_CONTROL_MASK))
713     {
714 	switch(event->keyval)
715 	{
716 /* 	    case GDK_u: */
717 /* 		gp_do_selected_tracks (update_tracks); */
718 /* 		break; */
719 	    default:
720 		break;
721 	}
722     }
723     return FALSE;
724 }
725 
726 /* ---------------------------------------------------------------- */
727 /* Section for track display                                        */
728 /* ---------------------------------------------------------------- */
729 
730 /* Append track to the track model (or write into @into_iter if != 0) */
tm_add_track_to_track_model(Track * track,GtkTreeIter * into_iter)731 void tm_add_track_to_track_model (Track *track, GtkTreeIter *into_iter)
732 {
733     GtkTreeIter iter;
734     GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
735 
736     g_return_if_fail (model);
737 
738     if (into_iter)
739     {
740         convert_iter (model, into_iter, &iter);
741     }
742     else
743     {
744         gtk_list_store_append (get_model_as_store (model), &iter);
745     }
746 
747     gtk_list_store_set (get_model_as_store (model), &iter, READOUT_COL, track, -1);
748 /*    update_model_view (model); -- not needed */
749 }
750 
751 
752 
753 /* Used by remove_track() to remove track from model by calling
754    gtk_tree_model_foreach ().
755    Entry is deleted if data == track */
tm_delete_track(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)756 static gboolean tm_delete_track (GtkTreeModel *model,
757 				GtkTreePath *path,
758 				GtkTreeIter *iter,
759 				gpointer data)
760 {
761     Track *track;
762 
763     gtk_tree_model_get (model, iter, READOUT_COL, &track, -1);
764 
765     if(track == (Track *)data)
766     {
767         GtkTreeIter temp;
768 
769         GtkTreeSelection *selection = gtk_tree_view_get_selection
770         (track_treeview);
771         /*       printf("unselect...\n"); */
772         gtk_tree_selection_unselect_iter (selection, iter);
773         /*       printf("...unselect done\n"); */
774 
775         convert_iter (model, iter, &temp);
776         gtk_list_store_remove (get_model_as_store (model), &temp);
777 /*        update_model_view (model); -- not needed */
778         return TRUE;
779     }
780     return FALSE;
781 }
782 
783 
784 /* Remove track from the display model */
tm_remove_track(Track * track)785 void tm_remove_track (Track *track)
786 {
787     GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
788 
789     if (model)
790     {
791         gtk_tree_model_foreach (model, tm_delete_track, track);
792 /*        update_model_view (model); -- not needed */
793     }
794 }
795 
796 
797 /* Remove all tracks from the display model */
tm_remove_all_tracks()798 void tm_remove_all_tracks ()
799 {
800     GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
801     GtkWidget *search_entry = gtkpod_xml_get_widget (main_window_xml, "search_entry");
802 
803     /* remove all tracks, including tracks filtered out */
804     gtk_list_store_clear (get_model_as_store (model));
805 
806     /* reset filter text -- if many tracks are added with the filter
807      * activated, a lot of time is needed */
808     gtk_entry_set_text (GTK_ENTRY (search_entry), "");
809 
810     tm_store_col_order ();
811     tm_update_default_sizes ();
812 }
813 
814 /* find out at which position column @tm_item is displayed */
815 /* static gint tm_get_col_position (TM_item tm_item) */
816 /* { */
817 /*     gint i; */
818 /*     GtkTreeViewColumn *col; */
819 
820 /*     if (!track_treeview) return -1; */
821 
822 /*     for (i=0; i<TM_NUM_COLUMNS_PREFS; ++i) */
823 /*     { */
824 /* 	col = gtk_tree_view_get_column (track_treeview, i); */
825 /* 	if (col->sort_column_id == tm_item) return i; */
826 /*     } */
827 /*     return -1; */
828 /* } */
829 
830 
831 /* store the order of the track view columns */
tm_store_col_order(void)832 void tm_store_col_order (void)
833 {
834     gint i;
835     GtkTreeViewColumn *col;
836 
837     for (i=0; i<TM_NUM_COLUMNS; ++i)
838     {
839 	col = gtk_tree_view_get_column (track_treeview, i);
840 	prefs_set_int_index("col_order", i, col->sort_column_id);
841     }
842 }
843 
844 
845 /* Used by tm_track_changed() to find the track that
846    changed name. If found, emit a "row changed" signal */
tm_model_track_changed(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)847 static gboolean tm_model_track_changed (GtkTreeModel *model,
848 				       GtkTreePath *path,
849 				       GtkTreeIter *iter,
850 				       gpointer data)
851 {
852   Track *track;
853 
854   gtk_tree_model_get (model, iter, READOUT_COL, &track, -1);
855   if(track == (Track *)data) {
856     gtk_tree_model_row_changed (model, path, iter);
857     return TRUE;
858   }
859   return FALSE;
860 }
861 
862 
863 /* One of the tracks has changed (this happens when the
864    iTunesDB is read and some IDs are renumbered */
tm_track_changed(Track * track)865 void tm_track_changed (Track *track)
866 {
867   GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
868   /*  printf("tm_track_changed enter\n");*/
869   if (model != NULL)
870     gtk_tree_model_foreach (model, tm_model_track_changed, track);
871   /*  printf("tm_track_changed exit\n");*/
872 }
873 
874 
875 
876 #if ((GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION < 2))
877 /* gtk_tree_selection_get_selected_rows() was introduced in 2.2 */
878 struct gtsgsr
879 {
880     GtkTreeModel **model;
881     GList        **list;
882 };
883 
gtssf(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)884 void  gtssf  (GtkTreeModel *model,
885 	      GtkTreePath *path,
886 	      GtkTreeIter *iter,
887 	      gpointer data)
888 {
889     struct gtsgsr *gts = data;
890     *gts->model = model;
891     *gts->list = g_list_append (*gts->list, gtk_tree_path_copy (path));
892 }
893 
gtk_tree_selection_get_selected_rows(GtkTreeSelection * selection,GtkTreeModel ** model)894 GList *gtk_tree_selection_get_selected_rows (GtkTreeSelection *selection,
895                                              GtkTreeModel     **model)
896 {
897     struct gtsgsr gts;
898     GList *list = NULL;
899 
900     gts.model = model;
901     gts.list = &list;
902 
903     gtk_tree_selection_selected_foreach (selection, gtssf, &gts);
904     return list;
905 }
906 #endif
907 
908 
tm_rating_edited(RBCellRendererRating * renderer,const gchar * path_string,double rating)909 static void tm_rating_edited (RBCellRendererRating *renderer,
910 							  const gchar *path_string,
911 							  double rating)
912 {
913 	GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
914 	GtkTreeIter iter;
915     Track *track;
916 	GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
917 
918 	g_return_if_fail (model);
919 	g_return_if_fail (path);
920 	g_return_if_fail (gtk_tree_model_get_iter (model, &iter, path));
921 
922 	gtk_tree_path_free (path);
923 	gtk_tree_model_get(model, &iter, READOUT_COL, &track, -1);
924 
925 	if ((int) rating * ITDB_RATING_STEP != track->rating)
926 	{
927 		track->rating = (int) rating * ITDB_RATING_STEP;
928 		track->time_modified = time (NULL);
929 		pm_track_changed (track);
930 		data_changed (track->itdb);
931 
932 		if (prefs_get_int("id3_write"))
933 		{
934 			write_tags_to_file (track);
935 			gp_duplicate_remove (NULL, NULL);
936 		}
937 	}
938 }
939 
940 /* Called when editable cell is being edited. Stores new data to the
941    track list. ID3 tags in the corresponding files are updated as
942    well, if activated in the pref settings */
943 static void
tm_cell_edited(GtkCellRendererText * renderer,const gchar * path_string,const gchar * new_text,gpointer data)944 tm_cell_edited (GtkCellRendererText *renderer,
945 		const gchar         *path_string,
946 		const gchar         *new_text,
947 		gpointer             data)
948 {
949   GtkTreeModel *model;
950   GtkTreeSelection *selection;
951   TM_item column;
952   gboolean multi_edit;
953   gint sel_rows_num;
954   GList *row_list, *row_node, *first;
955 
956 
957   column = (TM_item) g_object_get_data(G_OBJECT(renderer), "column");
958   multi_edit = prefs_get_int("multi_edit");
959 /*  if (column == TM_COLUMN_TITLE)
960       multi_edit &= prefs_get_int("multi_edit_title"); */
961   selection = gtk_tree_view_get_selection(track_treeview);
962   row_list = gtk_tree_selection_get_selected_rows(selection, &model);
963 
964 /*   printf("tm_cell_edited: column: %d\n", column); */
965 
966   sel_rows_num = g_list_length (row_list);
967 
968   /* block widgets and update display if multi-edit is active */
969   if (multi_edit && (sel_rows_num > 1)) block_widgets ();
970 
971   first = g_list_first (row_list);
972 
973   for (row_node = first;
974        row_node && (multi_edit || (row_node == first));
975        row_node = g_list_next(row_node))
976   {
977      Track *track;
978      ExtraTrackData *etr;
979      gboolean changed = FALSE;
980      GtkTreeIter iter;
981      gchar *str;
982 
983      gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) row_node->data);
984      gtk_tree_model_get(model, &iter, READOUT_COL, &track, -1);
985      g_return_if_fail (track);
986      etr = track->userdata;
987      g_return_if_fail (etr);
988 
989 
990      changed = FALSE;
991 
992      switch(column)
993      {
994      case TM_COLUMN_TITLE:
995      case TM_COLUMN_ALBUM:
996      case TM_COLUMN_ALBUMARTIST:
997      case TM_COLUMN_ARTIST:
998      case TM_COLUMN_GENRE:
999      case TM_COLUMN_COMPOSER:
1000      case TM_COLUMN_COMMENT:
1001      case TM_COLUMN_FILETYPE:
1002      case TM_COLUMN_GROUPING:
1003      case TM_COLUMN_CATEGORY:
1004      case TM_COLUMN_DESCRIPTION:
1005      case TM_COLUMN_PODCASTURL:
1006      case TM_COLUMN_PODCASTRSS:
1007      case TM_COLUMN_SUBTITLE:
1008      case TM_COLUMN_TRACK_NR:
1009      case TM_COLUMN_TRACKLEN:
1010      case TM_COLUMN_CD_NR:
1011      case TM_COLUMN_YEAR:
1012      case TM_COLUMN_PLAYCOUNT:
1013      case TM_COLUMN_RATING:
1014      case TM_COLUMN_TIME_ADDED:
1015      case TM_COLUMN_TIME_PLAYED:
1016      case TM_COLUMN_TIME_MODIFIED:
1017      case TM_COLUMN_TIME_RELEASED:
1018      case TM_COLUMN_VOLUME:
1019      case TM_COLUMN_SOUNDCHECK:
1020      case TM_COLUMN_BITRATE:
1021      case TM_COLUMN_SAMPLERATE:
1022      case TM_COLUMN_BPM:
1023      case TM_COLUMN_MEDIA_TYPE:
1024      case TM_COLUMN_TV_SHOW:
1025      case TM_COLUMN_TV_EPISODE:
1026      case TM_COLUMN_TV_NETWORK:
1027      case TM_COLUMN_SEASON_NR:
1028      case TM_COLUMN_EPISODE_NR:
1029      case TM_COLUMN_SORT_TITLE:
1030      case TM_COLUMN_SORT_ALBUM:
1031      case TM_COLUMN_SORT_ALBUMARTIST:
1032      case TM_COLUMN_SORT_COMPOSER:
1033      case TM_COLUMN_SORT_TVSHOW:
1034      case TM_COLUMN_SORT_ARTIST:
1035 	 changed = track_set_text (track, new_text, TM_to_T (column));
1036 	 if (changed && (column == TM_COLUMN_TRACKLEN))
1037 	 {  /* be on the safe side and reset starttime, stoptime and
1038 	     * filesize */
1039 	     gchar *path = get_file_name_from_source (track,
1040 						      SOURCE_PREFER_LOCAL);
1041 	     track->starttime = 0;
1042 	     track->stoptime = 0;
1043 	     if (path)
1044 	     {
1045 		 struct stat filestat;
1046 		 stat (path, &filestat);
1047 		 track->size = filestat.st_size;
1048 	     }
1049 	 }
1050 	 /* redisplay some items to be on the safe side */
1051 	 switch (column)
1052 	 {
1053 	 case TM_COLUMN_TRACK_NR:
1054 	 case TM_COLUMN_CD_NR:
1055 	 case TM_COLUMN_TRACKLEN:
1056 	 case TM_COLUMN_TIME_ADDED:
1057 	 case TM_COLUMN_TIME_PLAYED:
1058 	 case TM_COLUMN_TIME_MODIFIED:
1059 	 case TM_COLUMN_TIME_RELEASED:
1060 	     str = track_get_text (track, TM_to_T (column));
1061 	     g_object_set (G_OBJECT (renderer), "text", str, NULL);
1062 	     g_free (str);
1063 	     break;
1064 	 default:
1065 	     break;
1066 	 }
1067 	 break;
1068      case TM_COLUMN_IPOD_ID:
1069      case TM_COLUMN_PC_PATH:
1070      case TM_COLUMN_TRANSFERRED:
1071      case TM_COLUMN_SIZE:
1072      case TM_COLUMN_IPOD_PATH:
1073      case TM_COLUMN_COMPILATION:
1074      case TM_COLUMN_THUMB_PATH:
1075      case TM_COLUMN_LYRICS:
1076      case TM_NUM_COLUMNS:
1077 	 /* These are not editable text fields */
1078 	 break;
1079      }
1080 /*      printf ("  changed: %d\n", changed); */
1081      if (changed)
1082      {
1083 	track->time_modified = time (NULL);
1084         pm_track_changed (track);    /* notify playlist model... */
1085         data_changed (track->itdb); /* indicate that data has changed */
1086 
1087         if (prefs_get_int("id3_write"))
1088         {
1089 	    /* T_item tag_id;*/
1090            /* should we update all ID3 tags or just the one
1091               changed? -- obsoleted in 0.71*/
1092 /*           if (prefs_get_id3_writeall ()) tag_id = T_ALL;
1093 	     else                           tag_id = TM_to_T (column);*/
1094            write_tags_to_file (track);
1095            /* display possible duplicates that have been removed */
1096            gp_duplicate_remove (NULL, NULL);
1097         }
1098      }
1099      while (widgets_blocked && gtk_events_pending ())  gtk_main_iteration ();
1100   }
1101 
1102   if (multi_edit && (sel_rows_num > 1)) release_widgets ();
1103 
1104   g_list_foreach(row_list, (GFunc) gtk_tree_path_free, NULL);
1105   g_list_free(row_list);
1106 }
1107 
update_text_column_layout(GtkTreeViewColumn * tree_column,GtkCellRenderer * renderer,const gchar * text)1108 static void update_text_column_layout (GtkTreeViewColumn *tree_column,
1109 				       GtkCellRenderer *renderer,
1110 				       const gchar* text)
1111 {
1112 	GtkWidget* tree_widget;
1113 	PangoLayout* layout;
1114 	guint xpad;
1115 	int col_width;
1116 	int new_width;
1117 
1118 	tree_widget = gtk_tree_view_column_get_tree_view (tree_column);
1119 	if (!tree_widget) return;
1120 
1121 	layout = gtk_widget_create_pango_layout (tree_widget, text);
1122 
1123 	/* Expand the width, if necessary. This is done manually
1124 	   because the column is set to fixed width for performance
1125 	   reasons. */
1126 	col_width = gtk_tree_view_column_get_fixed_width (tree_column);
1127 	g_object_get (G_OBJECT (renderer), "xpad", &xpad, NULL);
1128 	pango_layout_get_pixel_size (layout, &new_width, NULL);
1129 	new_width += xpad;
1130 	if (col_width < new_width)
1131 	{
1132 		gtk_tree_view_column_set_fixed_width (tree_column, new_width);
1133 	}
1134 
1135 	g_object_unref (G_OBJECT (layout));
1136 }
1137 
1138 /* The track data is stored in a separate list (static GList *tracks)
1139    and only pointers to the corresponding Track structure are placed
1140    into the model.
1141    This function reads the data for the given cell from the list and
1142    passes it to the renderer. */
tm_cell_data_text_func(GtkTreeViewColumn * tree_column,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer data)1143 static void tm_cell_data_text_func (GtkTreeViewColumn *tree_column,
1144 				    GtkCellRenderer   *renderer,
1145 				    GtkTreeModel      *model,
1146 				    GtkTreeIter       *iter,
1147 				    gpointer           data)
1148 {
1149 	Track *track;
1150 	ExtraTrackData *etr;
1151 	iTunesDB *itdb;
1152 	TM_item column;
1153 	gchar *text;
1154 
1155 	column = (TM_item)g_object_get_data (G_OBJECT (renderer), "column");
1156 
1157 	g_return_if_fail ((column >= 0) && (column < TM_NUM_COLUMNS));
1158 
1159 	gtk_tree_model_get (model, iter, READOUT_COL, &track, -1);
1160 	g_return_if_fail (track);
1161 	etr = track->userdata;
1162 	g_return_if_fail (etr);
1163 	itdb = track->itdb;
1164 	g_return_if_fail (itdb);
1165 
1166 	text = track_get_text (track, TM_to_T (column));
1167 
1168 	g_object_set (G_OBJECT (renderer), "text", text, NULL);
1169 
1170 	update_text_column_layout (tree_column, renderer, text);
1171 
1172 	g_free (text);
1173 }
1174 
1175 
1176 /* The track data is stored in a separate list (static GList *tracks)
1177    and only pointers to the corresponding Track structure are placed
1178    into the model.
1179    This function reads the data for the given cell from the list and
1180    passes it to the renderer. */
tm_cell_data_toggle_func(GtkTreeViewColumn * tree_column,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer data)1181 static void tm_cell_data_toggle_func (GtkTreeViewColumn *tree_column,
1182 				      GtkCellRenderer   *renderer,
1183 				      GtkTreeModel      *model,
1184 				      GtkTreeIter       *iter,
1185 				      gpointer           data)
1186 {
1187 	Track *track;
1188 	ExtraTrackData *etr;
1189 	iTunesDB *itdb;
1190 	TM_item column;
1191 
1192 	column = (TM_item)g_object_get_data (G_OBJECT (renderer), "column");
1193 
1194 	g_return_if_fail ((column >= 0) && (column < TM_NUM_COLUMNS));
1195 
1196 	gtk_tree_model_get (model, iter, READOUT_COL, &track, -1);
1197 	g_return_if_fail (track);
1198 	etr = track->userdata;
1199 	g_return_if_fail (etr);
1200 	itdb = track->itdb;
1201 	g_return_if_fail (itdb);
1202 
1203 	switch (column)
1204 	{
1205 	case TM_COLUMN_LYRICS:
1206 	  g_object_set (G_OBJECT (renderer),
1207 			"active", track->lyrics_flag,
1208 			NULL);
1209 	  break;
1210 	case TM_COLUMN_TRANSFERRED:
1211 	  g_object_set (G_OBJECT (renderer),
1212 			"active", track->transferred,
1213 			NULL);
1214 	  break;
1215 	case TM_COLUMN_COMPILATION:
1216 	  g_object_set (G_OBJECT (renderer),
1217 			"active", track->compilation,
1218 			NULL);
1219 	  break;
1220 	default:
1221 	  g_return_if_reached();
1222 	}
1223 }
1224 
1225 
1226 /* The track data is stored in a separate list (static GList *tracks)
1227    and only pointers to the corresponding Track structure are placed
1228    into the model.
1229    This function reads the data for the given cell from the list and
1230    passes it to the renderer. */
tm_cell_data_rating_func(GtkTreeViewColumn * tree_column,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer data)1231 static void tm_cell_data_rating_func (GtkTreeViewColumn *tree_column,
1232 				      GtkCellRenderer   *renderer,
1233 				      GtkTreeModel      *model,
1234 				      GtkTreeIter       *iter,
1235 				      gpointer           data)
1236 {
1237 	Track *track;
1238 	ExtraTrackData *etr;
1239 	iTunesDB *itdb;
1240 	TM_item column;
1241 
1242 	column = (TM_item)g_object_get_data (G_OBJECT (renderer), "column");
1243 
1244 	g_return_if_fail ((column >= 0) && (column < TM_NUM_COLUMNS));
1245 
1246 	gtk_tree_model_get (model, iter, READOUT_COL, &track, -1);
1247 	g_return_if_fail (track);
1248 	etr = track->userdata;
1249 	g_return_if_fail (etr);
1250 	itdb = track->itdb;
1251 	g_return_if_fail (itdb);
1252 
1253 	switch (column)
1254 	{
1255 	case TM_COLUMN_RATING:
1256 	  g_object_set (G_OBJECT (renderer),
1257 			"rating", (double)(track->rating / ITDB_RATING_STEP),
1258 			NULL);
1259 	  break;
1260 	default:
1261 	  g_return_if_reached();
1262 	}
1263 }
1264 
1265 
1266 
1267 /* This function is analogous to tm_cell_data_func(), but is only used
1268    for the title column to distinguish between the text and the toggle
1269    button there. The other toggle buttons (e.g. compilation) can
1270    easily be handled in the original tm_cell_data_func() */
tm_cell_data_func_toggle(GtkTreeViewColumn * tree_column,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer data)1271 static void tm_cell_data_func_toggle (GtkTreeViewColumn *tree_column,
1272 				      GtkCellRenderer   *renderer,
1273 				      GtkTreeModel      *model,
1274 				      GtkTreeIter       *iter,
1275 				      gpointer           data)
1276 {
1277   Track *track;
1278   TM_item column;
1279 
1280   column = (TM_item)g_object_get_data (G_OBJECT (renderer), "column");
1281   gtk_tree_model_get (model, iter, READOUT_COL, &track, -1);
1282 
1283   /* printf ("tm_cell_data_func_toggle() entered\n"); */
1284 
1285   switch (column)
1286   {
1287   case TM_COLUMN_TITLE:
1288       g_object_set (G_OBJECT (renderer),
1289 		    "active", !track->checked,
1290 		    "activatable", TRUE, NULL);
1291       break;
1292   default:
1293       g_warning ("Programming error: unknown column in tm_cell_data_func_toggle: %d\n", column);
1294       break;
1295   }
1296 }
1297 
1298 
1299 
1300 /* Called when a toggle cell is being changed. Stores new data to the
1301    track list. */
1302 static void
tm_cell_toggled(GtkCellRendererToggle * renderer,gchar * arg1,gpointer user_data)1303 tm_cell_toggled (GtkCellRendererToggle *renderer,
1304 		 gchar *arg1,
1305 		 gpointer user_data)
1306 {
1307   GtkTreeModel *model;
1308   GtkTreeSelection *selection;
1309   TM_item column;
1310   gboolean multi_edit;
1311   gint sel_rows_num;
1312   GList *row_list, *row_node, *first;
1313   gboolean active;
1314   GList *selected_tracks = NULL;
1315 
1316   column = (TM_item) g_object_get_data(G_OBJECT(renderer), "column");
1317   multi_edit = prefs_get_int("multi_edit");
1318   selection = gtk_tree_view_get_selection(track_treeview);
1319   row_list = gtk_tree_selection_get_selected_rows(selection, &model);
1320 
1321   /* printf("tm_cell_toggled: column: %d, arg1: %p\n", column, arg1); */
1322 
1323   sel_rows_num = g_list_length (row_list);
1324 
1325   /* block widgets and update display if multi-edit is active */
1326   if (multi_edit && (sel_rows_num > 1)) block_widgets ();
1327 
1328   first = g_list_first (row_list);
1329 
1330   /* active will show the old state -- before the toggle */
1331   g_object_get (G_OBJECT (renderer), "active", &active, NULL);
1332 
1333   for (row_node = first;
1334        row_node && (multi_edit || (row_node == first));
1335        row_node = g_list_next(row_node))
1336   {
1337      Track *track;
1338      gboolean changed;
1339      GtkTreeIter iter;
1340 
1341      gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) row_node->data);
1342      gtk_tree_model_get(model, &iter, READOUT_COL, &track, -1);
1343      changed = FALSE;
1344 
1345      switch(column)
1346      {
1347      case TM_COLUMN_TITLE:
1348 	 if ((active && (track->checked == 0)) ||
1349 	     (!active && (track->checked == 1)))
1350 	     changed = TRUE;
1351 	 if (active) track->checked = 1;
1352 	 else        track->checked = 0;
1353 	 break;
1354      case TM_COLUMN_COMPILATION:
1355 	 if ((!active && (track->compilation == 0)) ||
1356 	     (active && (track->compilation == 1)))
1357 	     changed = TRUE;
1358 	 if (!active) track->compilation = 1;
1359 	 else        track->compilation = 0;
1360         break;
1361      case TM_COLUMN_LYRICS:
1362 	 /* collect all selected tracks -- then call "edit details" */
1363 	 selected_tracks = g_list_append (selected_tracks, track);
1364 	 break;
1365      case TM_COLUMN_ARTIST:
1366      case TM_COLUMN_ALBUM:
1367      case TM_COLUMN_GENRE:
1368      case TM_COLUMN_COMPOSER:
1369      case TM_COLUMN_TRACK_NR:
1370      case TM_COLUMN_IPOD_ID:
1371      case TM_COLUMN_PC_PATH:
1372      case TM_COLUMN_TRANSFERRED:
1373      case TM_COLUMN_SIZE:
1374      case TM_COLUMN_TRACKLEN:
1375      case TM_COLUMN_BITRATE:
1376      case TM_COLUMN_PLAYCOUNT:
1377      case TM_COLUMN_RATING:
1378      case TM_COLUMN_TIME_PLAYED:
1379      case TM_COLUMN_TIME_MODIFIED:
1380      case TM_COLUMN_VOLUME:
1381      case TM_COLUMN_YEAR:
1382      case TM_COLUMN_CD_NR:
1383      case TM_COLUMN_TIME_ADDED:
1384      case TM_COLUMN_IPOD_PATH:
1385      case TM_COLUMN_SOUNDCHECK:
1386      case TM_COLUMN_SAMPLERATE:
1387      case TM_COLUMN_BPM:
1388      case TM_COLUMN_FILETYPE:
1389      case TM_COLUMN_GROUPING:
1390      case TM_COLUMN_COMMENT:
1391      case TM_COLUMN_CATEGORY:
1392      case TM_COLUMN_DESCRIPTION:
1393      case TM_COLUMN_PODCASTURL:
1394      case TM_COLUMN_PODCASTRSS:
1395      case TM_COLUMN_SUBTITLE:
1396      case TM_COLUMN_TIME_RELEASED:
1397      case TM_COLUMN_THUMB_PATH:
1398      case TM_COLUMN_MEDIA_TYPE:
1399      case TM_COLUMN_TV_SHOW:
1400      case TM_COLUMN_TV_EPISODE:
1401      case TM_COLUMN_TV_NETWORK:
1402      case TM_COLUMN_SEASON_NR:
1403      case TM_COLUMN_EPISODE_NR:
1404      case TM_COLUMN_ALBUMARTIST:
1405      case TM_COLUMN_SORT_ARTIST:
1406      case TM_COLUMN_SORT_TITLE:
1407      case TM_COLUMN_SORT_ALBUM:
1408      case TM_COLUMN_SORT_ALBUMARTIST:
1409      case TM_COLUMN_SORT_COMPOSER:
1410      case TM_COLUMN_SORT_TVSHOW:
1411      case TM_NUM_COLUMNS:
1412 	 /* these are not toggle buttons or are non-editable */
1413 	 break;
1414      }
1415 /*      printf ("  changed: %d\n", changed); */
1416      if (changed)
1417      {
1418 	track->time_modified = time (NULL);
1419 /*        pm_track_changed (track);  notify playlist model... -- not
1420  *        necessary here because only the track model is affected */
1421         data_changed (track->itdb);  /* indicate that data has changed */
1422 
1423         /* If the changed column is the compilation flag update the file
1424            if required */
1425         if (column == TM_COLUMN_COMPILATION)
1426            if (prefs_get_int("id3_write"))
1427               write_tags_to_file (track);
1428 
1429      }
1430      while (widgets_blocked && gtk_events_pending ())  gtk_main_iteration ();
1431   }
1432 
1433   if ((column == TM_COLUMN_LYRICS) && (selected_tracks != NULL))
1434   {
1435       /* set displayed page to the lyrics page */
1436       prefs_set_int (DETAILS_WINDOW_NOTEBOOK_PAGE, 3);
1437       details_edit (selected_tracks);
1438       g_list_free (selected_tracks);
1439   }
1440 
1441   if (multi_edit && (sel_rows_num > 1)) release_widgets ();
1442 
1443   g_list_foreach(row_list, (GFunc) gtk_tree_path_free, NULL);
1444   g_list_free(row_list);
1445 }
1446 
1447 
1448 
1449 
1450 /**
1451  * tm_get_nr_of_tracks - get the number of tracks displayed
1452  * currently in the track model Returns - the number of tracks displayed
1453  * currently
1454  */
1455 gint
tm_get_nr_of_tracks(void)1456 tm_get_nr_of_tracks(void)
1457 {
1458     gint result = 0;
1459     GtkTreeModel *tm = NULL;
1460 
1461     tm = gtk_tree_view_get_model (track_treeview);
1462     if (tm)
1463     {
1464 	result = gtk_tree_model_iter_n_children (tm, NULL);
1465     }
1466     return result;
1467 }
1468 
1469 
comp_int(gconstpointer a,gconstpointer b)1470 static gint comp_int (gconstpointer a, gconstpointer b)
1471 {
1472     return (GPOINTER_TO_INT(a)-(GPOINTER_TO_INT(b)));
1473 }
1474 
1475 
1476 /**
1477  * Reorder tracks in playlist to match order of tracks displayed in track
1478  * view. Only the subset of tracks currently displayed is reordered.
1479  * data_changed() is called when necessary.
1480  */
1481 void
tm_rows_reordered(void)1482 tm_rows_reordered (void)
1483 {
1484     Playlist *current_pl;
1485 
1486     g_return_if_fail (track_treeview);
1487     current_pl = pm_get_selected_playlist ();
1488 
1489     if(current_pl)
1490     {
1491 	GtkTreeModel *tm = NULL;
1492 	GtkTreeIter i;
1493 	GList *new_list = NULL, *old_pos_l = NULL;
1494 	gboolean valid = FALSE;
1495 	GList *nlp, *olp;
1496 	gboolean changed = FALSE;
1497 	iTunesDB *itdb = NULL;
1498 
1499 	tm = gtk_tree_view_get_model (track_treeview);
1500 	g_return_if_fail (tm);
1501 
1502 	valid = gtk_tree_model_get_iter_first (tm,&i);
1503 	while (valid)
1504 	{
1505 	    Track *new_track;
1506 	    gint old_position;
1507 
1508 	    gtk_tree_model_get (tm, &i, READOUT_COL, &new_track, -1);
1509 	    g_return_if_fail (new_track);
1510 
1511 	    if (!itdb) itdb = new_track->itdb;
1512 	    new_list = g_list_append (new_list, new_track);
1513 	    /* what position was this track in before? */
1514 	    old_position = g_list_index (current_pl->members, new_track);
1515 	    /* check if we already used this position before (can
1516 	       happen if track has been added to playlist more than
1517 	       once */
1518 	    while ((old_position != -1) &&
1519 		   g_list_find (old_pos_l, GINT_TO_POINTER(old_position)))
1520 	    {  /* find next occurence */
1521 		GList *link;
1522 		gint next;
1523 		link = g_list_nth (current_pl->members, old_position + 1);
1524 		next = g_list_index (link, new_track);
1525 		if (next == -1)   old_position = -1;
1526 		else              old_position += (1+next);
1527 	    }
1528 	    /* we make a sorted list of the old positions */
1529 	    old_pos_l = g_list_insert_sorted (old_pos_l,
1530 					      GINT_TO_POINTER(old_position),
1531 					      comp_int);
1532 	    valid = gtk_tree_model_iter_next (tm, &i);
1533 	}
1534 	nlp = new_list;
1535 	olp = old_pos_l;
1536 	while (nlp && olp)
1537 	{
1538 	    GList *old_link;
1539 	    guint position = GPOINTER_TO_INT(olp->data);
1540 
1541 	    /* if position == -1 one of the tracks in the track view
1542 	       could not be found in the selected playlist -> stop! */
1543 	    if (position == -1)
1544 	    {
1545 		g_warning ("Programming error: tm_rows_reordered_callback: track in track view was not in selected playlist\n");
1546 		g_return_if_reached ();
1547 	    }
1548 	    old_link = g_list_nth (current_pl->members, position);
1549 	    /* replace old track with new track */
1550 	    if (old_link->data != nlp->data)
1551 	    {
1552 		old_link->data = nlp->data;
1553 		changed = TRUE;
1554 	    }
1555 	    /* next */
1556 	    nlp = nlp->next;
1557 	    olp = olp->next;
1558 	}
1559 	g_list_free (new_list);
1560 	g_list_free (old_pos_l);
1561 	/* if we changed data, mark data as changed and adopt order in
1562 	   sort tabs */
1563 	if (changed)
1564 	{
1565 	    data_changed (itdb);
1566 	    st_adopt_order_in_playlist ();
1567 	}
1568     }
1569 }
1570 
1571 
1572 static void
on_trackids_list_foreach(GtkTreeModel * tm,GtkTreePath * tp,GtkTreeIter * i,gpointer data)1573 on_trackids_list_foreach ( GtkTreeModel *tm, GtkTreePath *tp,
1574 			   GtkTreeIter *i, gpointer data)
1575 {
1576     Track *tr = NULL;
1577     GList *l = *((GList**)data);
1578     gtk_tree_model_get(tm, i, READOUT_COL, &tr, -1);
1579     g_return_if_fail (tr);
1580     l = g_list_append(l, GUINT_TO_POINTER(tr->id));
1581     *((GList**)data) = l;
1582 }
1583 
1584 
1585 /* return a list containing the track IDs of all tracks currently being
1586    selected */
1587 GList *
tm_get_selected_trackids(void)1588 tm_get_selected_trackids(void)
1589 {
1590     GList *result = NULL;
1591     GtkTreeSelection *ts = NULL;
1592 
1593     if((ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(track_treeview))))
1594     {
1595 	gtk_tree_selection_selected_foreach(ts, on_trackids_list_foreach,
1596 					    &result);
1597     }
1598     return(result);
1599 }
1600 
1601 static    gboolean
on_all_trackids_list_foreach(GtkTreeModel * tm,GtkTreePath * tp,GtkTreeIter * i,gpointer data)1602 on_all_trackids_list_foreach (GtkTreeModel *tm, GtkTreePath *tp,
1603 			      GtkTreeIter *i, gpointer data)
1604 {
1605     on_trackids_list_foreach (tm, tp, i, data);
1606     return FALSE;
1607 }
1608 
1609 /* return a list containing the track IDs of all tracks currently being
1610    displayed */
1611 GList *
tm_get_all_trackids(void)1612 tm_get_all_trackids(void)
1613 {
1614     GList *result = NULL;
1615     GtkTreeModel *model;
1616 
1617     if((model = gtk_tree_view_get_model (track_treeview)))
1618     {
1619 	gtk_tree_model_foreach(model, on_all_trackids_list_foreach,
1620 			       &result);
1621     }
1622     return(result);
1623 }
1624 
1625 /* Prepends to list, so the list will be reversed. Make sure to
1626    reverse the result if you care about the order of the tracks. */
1627 static void
on_tracks_list_foreach(GtkTreeModel * tm,GtkTreePath * tp,GtkTreeIter * i,gpointer data)1628 on_tracks_list_foreach ( GtkTreeModel *tm, GtkTreePath *tp,
1629 			 GtkTreeIter *i, gpointer data)
1630 {
1631     Track *tr = NULL;
1632     GList *l = *((GList**)data);
1633 
1634     gtk_tree_model_get(tm, i, READOUT_COL, &tr, -1);
1635     g_return_if_fail (tr);
1636     l = g_list_prepend(l, tr);
1637     *((GList**)data) = l;
1638 }
1639 
1640 
1641 /* return a list containing pointers to all tracks currently being
1642    selected */
1643 GList *
tm_get_selected_tracks(void)1644 tm_get_selected_tracks(void)
1645 {
1646     GList *result = NULL;
1647     GtkTreeSelection *ts = NULL;
1648 
1649     if((ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(track_treeview))))
1650     {
1651 	gtk_tree_selection_selected_foreach(ts, on_tracks_list_foreach,
1652 					    &result);
1653 	result = g_list_reverse(result);
1654     }
1655     return(result);
1656 }
1657 
1658 
1659 
1660 /* used by tm_get_all_tracks */
on_all_tracks_list_foreach(GtkTreeModel * tm,GtkTreePath * tp,GtkTreeIter * i,gpointer data)1661 static gboolean on_all_tracks_list_foreach (GtkTreeModel *tm,
1662 					GtkTreePath *tp,
1663 					GtkTreeIter *i,
1664 					gpointer data)
1665 {
1666 on_tracks_list_foreach (tm, tp, i, data);
1667 return FALSE;
1668 }
1669 
1670 
1671 /* return a list containing pointers to all tracks currently being
1672    displayed. You must g_list_free() the list after use. */
1673 GList *
tm_get_all_tracks(void)1674 tm_get_all_tracks(void)
1675 {
1676     GList *result = NULL;
1677     GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
1678 
1679     g_return_val_if_fail (model, NULL);
1680 
1681     gtk_tree_model_foreach(model, on_all_tracks_list_foreach,
1682 			   &result);
1683     result = g_list_reverse(result);
1684     return result;
1685 }
1686 
1687 
1688 /* Stop editing. If @cancel is TRUE, the edited value will be
1689    discarded (I have the feeling that the "discarding" part does not
1690    work quite the way intended). */
tm_stop_editing(gboolean cancel)1691 void tm_stop_editing (gboolean cancel)
1692 {
1693     GtkTreeViewColumn *col;
1694 
1695     if (!track_treeview)  return;
1696 
1697     gtk_tree_view_get_cursor (track_treeview, NULL, &col);
1698     if (col)
1699     {
1700 	/* Before removing the widget we set multi_edit to FALSE. That
1701 	   way at most one entry will be changed (this also doesn't
1702 	   seem to work the way intended) */
1703 	gboolean me = prefs_get_int("multi_edit");
1704 	prefs_set_int("multi_edit", FALSE);
1705 	if (!cancel && col->editable_widget)
1706 	    gtk_cell_editable_editing_done (col->editable_widget);
1707 	if (col->editable_widget)
1708 	    gtk_cell_editable_remove_widget (col->editable_widget);
1709 	prefs_set_int("multi_edit", me);
1710     }
1711 }
1712 
1713 
1714 
1715 /* Function to compare @tm_item of @track1 and @track2. Used by
1716    tm_data_compare_func() */
tm_data_compare(Track * track1,Track * track2,TM_item tm_item)1717 static gint tm_data_compare (Track *track1, Track *track2,
1718 			     TM_item tm_item)
1719 {
1720   gint cmp = 0;
1721   ExtraTrackData *etr1, *etr2;
1722 
1723   /* string_compare_func is set to either compare_string_fuzzy or
1724      compare_string in tm_sort_column_changed() which is called
1725      once before the comparing begins. */
1726   switch (tm_item)
1727   {
1728   case TM_COLUMN_TITLE:
1729       cmp = string_compare_func (track1->title, track2->title);
1730       break;
1731   case TM_COLUMN_ALBUM:
1732       cmp = string_compare_func (track1->album, track2->album);
1733       break;
1734   case TM_COLUMN_ALBUMARTIST:
1735       cmp = string_compare_func (track1->albumartist, track2->albumartist);
1736       break;
1737   case TM_COLUMN_GENRE:
1738       cmp = string_compare_func (track1->genre, track2->genre);
1739       break;
1740   case TM_COLUMN_COMPOSER:
1741       cmp = string_compare_func (track1->composer, track2->composer);
1742       break;
1743   case TM_COLUMN_COMMENT:
1744       cmp = string_compare_func (track1->comment, track2->comment);
1745       break;
1746   case TM_COLUMN_FILETYPE:
1747       cmp = string_compare_func (track1->filetype, track2->filetype);
1748       break;
1749   case TM_COLUMN_GROUPING:
1750       cmp = string_compare_func (track1->grouping, track2->grouping);
1751       break;
1752   case TM_COLUMN_ARTIST:
1753       cmp = string_compare_func (track1->artist, track2->artist);
1754       break;
1755   case TM_COLUMN_CATEGORY:
1756       cmp = string_compare_func (track1->category, track2->category);
1757       break;
1758   case TM_COLUMN_DESCRIPTION:
1759       cmp = string_compare_func (track1->description, track2->description);
1760       break;
1761   case TM_COLUMN_PODCASTURL:
1762       cmp = string_compare_func (track1->podcasturl, track2->podcasturl);
1763       break;
1764   case TM_COLUMN_PODCASTRSS:
1765       cmp = string_compare_func (track1->podcastrss, track2->podcastrss);
1766       break;
1767   case TM_COLUMN_SUBTITLE:
1768       cmp = string_compare_func (track1->subtitle, track2->subtitle);
1769       break;
1770   case TM_COLUMN_TV_SHOW:
1771       cmp = string_compare_func (track1->tvshow, track2->tvshow);
1772       break;
1773   case TM_COLUMN_TV_EPISODE:
1774       cmp = string_compare_func (track1->tvepisode, track2->tvepisode);
1775       break;
1776   case TM_COLUMN_TV_NETWORK:
1777       cmp = string_compare_func (track1->tvnetwork, track2->tvnetwork);
1778       break;
1779   case TM_COLUMN_SORT_TITLE:
1780       cmp = string_compare_func (track1->sort_title, track2->sort_title);
1781       break;
1782   case TM_COLUMN_SORT_ALBUM:
1783       cmp = string_compare_func (track1->sort_album, track2->sort_album);
1784       break;
1785   case TM_COLUMN_SORT_ARTIST:
1786       cmp = string_compare_func (track1->sort_artist, track2->sort_artist);
1787       break;
1788   case TM_COLUMN_SORT_ALBUMARTIST:
1789       cmp = string_compare_func (track1->sort_albumartist,
1790 				 track2->sort_albumartist);
1791       break;
1792   case TM_COLUMN_SORT_COMPOSER:
1793       cmp = string_compare_func (track1->sort_composer, track2->sort_composer);
1794       break;
1795   case TM_COLUMN_SORT_TVSHOW:
1796       cmp = string_compare_func (track1->sort_tvshow, track2->sort_tvshow);
1797       break;
1798   case TM_COLUMN_TRACK_NR:
1799       cmp = track1->tracks - track2->tracks;
1800       if (cmp == 0) cmp = track1->track_nr - track2->track_nr;
1801       break;
1802   case TM_COLUMN_CD_NR:
1803       cmp = track1->cds - track2->cds;
1804       if (cmp == 0) cmp = track1->cd_nr - track2->cd_nr;
1805       break;
1806   case TM_COLUMN_IPOD_ID:
1807       cmp = track1->id - track2->id;
1808       break;
1809   case TM_COLUMN_PC_PATH:
1810       etr1 = track1->userdata;
1811       etr2 = track2->userdata;
1812       g_return_val_if_fail (etr1 && etr2, 0);
1813       cmp = g_utf8_collate (etr1->pc_path_utf8, etr2->pc_path_utf8);
1814       break;
1815   case TM_COLUMN_IPOD_PATH:
1816       cmp = g_utf8_collate (track1->ipod_path, track2->ipod_path);
1817       break;
1818   case TM_COLUMN_THUMB_PATH:
1819       etr1 = track1->userdata;
1820       etr2 = track2->userdata;
1821       g_return_val_if_fail (etr1 && etr2, 0);
1822       cmp = g_utf8_collate (etr1->thumb_path_utf8, etr2->thumb_path_utf8);
1823       break;
1824   case TM_COLUMN_TRANSFERRED:
1825       if(track1->transferred == track2->transferred)
1826 	  cmp = 0;
1827       else if(track1->transferred == TRUE)
1828 	  cmp = 1;
1829       else
1830 	  cmp = -1;
1831       break;
1832   case TM_COLUMN_COMPILATION:
1833       if(track1->compilation == track2->compilation)
1834 	  cmp = 0;
1835       else if(track1->compilation == TRUE)
1836 	  cmp = 1;
1837       else
1838 	  cmp = -1;
1839       break;
1840   case TM_COLUMN_SIZE:
1841       cmp = track1->size - track2->size;
1842       break;
1843   case TM_COLUMN_TRACKLEN:
1844       cmp = track1->tracklen - track2->tracklen;
1845       break;
1846   case TM_COLUMN_BITRATE:
1847       cmp = track1->bitrate - track2->bitrate;
1848       break;
1849   case TM_COLUMN_SAMPLERATE:
1850       cmp = track1->samplerate - track2->samplerate;
1851       break;
1852   case TM_COLUMN_BPM:
1853       cmp = track1->BPM - track2->BPM;
1854       break;
1855   case TM_COLUMN_PLAYCOUNT:
1856       cmp = track1->playcount - track2->playcount;
1857       break;
1858   case  TM_COLUMN_RATING:
1859       cmp = track1->rating - track2->rating;
1860       break;
1861   case TM_COLUMN_TIME_ADDED:
1862   case TM_COLUMN_TIME_PLAYED:
1863   case TM_COLUMN_TIME_MODIFIED:
1864   case TM_COLUMN_TIME_RELEASED:
1865       cmp = COMP (time_get_time (track1, TM_to_T (tm_item)),
1866 		  time_get_time (track2, TM_to_T (tm_item)));
1867       break;
1868   case  TM_COLUMN_VOLUME:
1869       cmp = track1->volume - track2->volume;
1870       break;
1871   case  TM_COLUMN_SOUNDCHECK:
1872       /* If soundcheck is unset (0) use 0 dB (1000) */
1873       cmp = (track1->soundcheck? track1->soundcheck:1000) -
1874 	  (track2->soundcheck? track2->soundcheck:1000);
1875       break;
1876   case TM_COLUMN_YEAR:
1877       cmp = track1->year - track2->year;
1878       break;
1879   case TM_COLUMN_SEASON_NR:
1880       cmp = track1->season_nr - track2->season_nr;
1881       break;
1882   case TM_COLUMN_EPISODE_NR:
1883       cmp = track1->episode_nr - track2->episode_nr;
1884       break;
1885   case TM_COLUMN_MEDIA_TYPE:
1886       cmp = track1->mediatype - track2->mediatype;
1887       break;
1888   case  TM_COLUMN_LYRICS:
1889       cmp = track1->lyrics_flag - track2->lyrics_flag;
1890       break;
1891   case TM_NUM_COLUMNS:
1892       break;
1893   }
1894   return cmp;
1895 }
1896 
1897 
1898 /* Function used to compare rows with user's search string */
tm_search_equal_func(GtkTreeModel * model,gint column,const gchar * key,GtkTreeIter * iter,gpointer search_data)1899 gboolean tm_search_equal_func (GtkTreeModel *model,
1900 			       gint column,
1901 			       const gchar *key,
1902 			       GtkTreeIter *iter,
1903 			       gpointer search_data)
1904 {
1905   Track *track1;
1906   gboolean cmp = 0;
1907   gtk_tree_model_get (model, iter, READOUT_COL, &track1, -1);
1908   switch ((TM_item)column)
1909   {
1910   case TM_COLUMN_TITLE:
1911   case TM_COLUMN_ALBUM:
1912   case TM_COLUMN_GENRE:
1913   case TM_COLUMN_COMPOSER:
1914   case TM_COLUMN_COMMENT:
1915   case TM_COLUMN_FILETYPE:
1916   case TM_COLUMN_GROUPING:
1917   case TM_COLUMN_ARTIST:
1918   case TM_COLUMN_CATEGORY:
1919   case TM_COLUMN_DESCRIPTION:
1920   case TM_COLUMN_PODCASTURL:
1921   case TM_COLUMN_PODCASTRSS:
1922   case TM_COLUMN_SUBTITLE:
1923   case TM_COLUMN_PC_PATH:
1924   case TM_COLUMN_YEAR:
1925   case TM_COLUMN_IPOD_PATH:
1926   case TM_COLUMN_COMPILATION:
1927   case TM_COLUMN_THUMB_PATH:
1928   case TM_COLUMN_TV_SHOW:
1929   case TM_COLUMN_TV_EPISODE:
1930   case TM_COLUMN_TV_NETWORK:
1931   case TM_COLUMN_ALBUMARTIST:
1932   case TM_COLUMN_SORT_ARTIST:
1933   case TM_COLUMN_SORT_TITLE:
1934   case TM_COLUMN_SORT_ALBUM:
1935   case TM_COLUMN_SORT_ALBUMARTIST:
1936   case TM_COLUMN_SORT_COMPOSER:
1937   case TM_COLUMN_SORT_TVSHOW:
1938       cmp = (compare_string_start_case_insensitive (
1939 		 track_get_item (track1, TM_to_T (column)),
1940 		 key) != 0);
1941       break;
1942   case TM_COLUMN_TRACK_NR:
1943   case TM_COLUMN_IPOD_ID:
1944   case TM_COLUMN_TRANSFERRED:
1945   case TM_COLUMN_SIZE:
1946   case TM_COLUMN_TRACKLEN:
1947   case TM_COLUMN_BITRATE:
1948   case TM_COLUMN_PLAYCOUNT:
1949   case TM_COLUMN_RATING:
1950   case TM_COLUMN_TIME_PLAYED:
1951   case TM_COLUMN_TIME_MODIFIED:
1952   case TM_COLUMN_VOLUME:
1953   case TM_COLUMN_CD_NR:
1954   case TM_COLUMN_TIME_ADDED:
1955   case TM_COLUMN_SOUNDCHECK:
1956   case TM_COLUMN_SAMPLERATE:
1957   case TM_COLUMN_BPM:
1958   case TM_COLUMN_TIME_RELEASED:
1959   case TM_COLUMN_MEDIA_TYPE:
1960   case TM_COLUMN_SEASON_NR:
1961   case TM_COLUMN_EPISODE_NR:
1962   case TM_COLUMN_LYRICS:
1963   case TM_NUM_COLUMNS:
1964       break;
1965   }
1966   return cmp;
1967 };
1968 
1969 /* Function used to compare two cells during sorting (track view) */
tm_data_compare_func(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer user_data)1970 gint tm_data_compare_func (GtkTreeModel *model,
1971 			GtkTreeIter *a,
1972 			GtkTreeIter *b,
1973 			gpointer user_data)
1974 {
1975   Track *track1;
1976   Track *track2;
1977   gint column;
1978   GtkSortType order;
1979   gint result;
1980 
1981   if(gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model),
1982 					   &column, &order) == FALSE)
1983       return 0;
1984 
1985   gtk_tree_model_get (model, a, READOUT_COL, &track1, -1);
1986   gtk_tree_model_get (model, b, READOUT_COL, &track2, -1);
1987   g_return_val_if_fail (track1 && track2, 0);
1988 
1989   result = tm_data_compare (track1, track2, column);
1990   /* implement stable sorting: if two items are the same, revert to
1991      the last relative positition */
1992   if (result == 0)
1993   {
1994       ExtraTrackData *etr1 = track1->userdata;
1995       ExtraTrackData *etr2 = track2->userdata;
1996       g_return_val_if_fail (etr1 && etr2, 0);
1997       result = etr1->sortindex - etr2->sortindex;
1998   }
1999   return result;
2000 }
2001 
2002 
2003 /* set/read the counter used to remember how often the sort column has
2004    been clicked.
2005    @inc: negative: reset counter to 0
2006    @inc: positive or zero : add to counter
2007    return value: new value of the counter */
tm_sort_counter(gint inc)2008 gint tm_sort_counter (gint inc)
2009 {
2010     static gint cnt = 0;
2011 
2012     if (inc <0)
2013     {
2014 	cnt = 0;
2015     }
2016     else
2017     {
2018 	cnt += inc;
2019     }
2020     return cnt;
2021 }
2022 
2023 
2024 /* Redisplays the tracks in the track view according to the order
2025  * stored in the sort tab view. This only works if the track view is
2026  * not sorted --> skip if sorted */
2027 
tm_adopt_order_in_sorttab(void)2028 void tm_adopt_order_in_sorttab (void)
2029 {
2030     if (prefs_get_int("tm_sort") == SORT_NONE)
2031     {
2032 	GList *gl, *tracks = NULL;
2033 
2034 	/* retrieve the currently displayed tracks (non ordered) from
2035 	   the last sort tab or from the selected playlist if no sort
2036 	   tabs are being used */
2037 	tm_remove_all_tracks ();
2038 	tracks = display_get_selected_members (prefs_get_int("sort_tab_num")-1);
2039 	for (gl=tracks; gl; gl=gl->next)
2040 	    tm_add_track_to_track_model ((Track *)gl->data, NULL);
2041     }
2042 }
2043 
2044 
2045 /* redisplay the contents of the track view in it's unsorted order */
tm_unsort(void)2046 static void tm_unsort (void)
2047 {
2048     if (track_treeview)
2049     {
2050 	GtkTreeModel *model= gtk_tree_view_get_model (track_treeview);
2051 
2052 	if (GTK_IS_TREE_MODEL_FILTER (model))
2053 	{
2054 	    model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(model));
2055 	}
2056 
2057 	prefs_set_int("tm_sort", SORT_NONE);
2058 	if (!BROKEN_GTK_TREE_SORT)
2059 	{
2060 /* no need to comment this out -- searching still works, but for lack
2061    of a ctrl-g only the first occurence will be found */
2062 /*	    gtk_tree_view_set_enable_search (GTK_TREE_VIEW
2063  * (track_treeview), FALSE);*/
2064 	    gtk_tree_sortable_set_sort_column_id
2065 		(GTK_TREE_SORTABLE (model),
2066 		 GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
2067 		 GTK_SORT_ASCENDING);
2068 	    tm_adopt_order_in_sorttab ();
2069 	}
2070 	else
2071 	{
2072 	    gtkpod_warning (_("Cannot unsort track view because of a bug in the GTK lib you are using (%d.%d.%d < 2.5.4). Once you sort the track view, you cannot go back to the unsorted state.\n\n"), gtk_major_version, gtk_minor_version, gtk_micro_version);
2073 	}
2074 	tm_sort_counter (-1);
2075     }
2076 }
2077 
2078 
tm_set_search_column(TM_item newcol)2079 static void tm_set_search_column (TM_item newcol)
2080 {
2081 /*     printf ("track_treeview: %p, col: %d\n", track_treeview, newcol); */
2082     g_return_if_fail (track_treeview);
2083 
2084     gtk_tree_view_set_search_column (GTK_TREE_VIEW (track_treeview),
2085 				       newcol);
2086     switch (newcol)
2087     {
2088     case TM_COLUMN_TITLE:
2089     case TM_COLUMN_ALBUM:
2090     case TM_COLUMN_GENRE:
2091     case TM_COLUMN_COMPOSER:
2092     case TM_COLUMN_COMMENT:
2093     case TM_COLUMN_FILETYPE:
2094     case TM_COLUMN_GROUPING:
2095     case TM_COLUMN_ARTIST:
2096     case TM_COLUMN_CATEGORY:
2097     case TM_COLUMN_DESCRIPTION:
2098     case TM_COLUMN_PODCASTURL:
2099     case TM_COLUMN_PODCASTRSS:
2100     case TM_COLUMN_SUBTITLE:
2101     case TM_COLUMN_PC_PATH:
2102     case TM_COLUMN_YEAR:
2103     case TM_COLUMN_IPOD_PATH:
2104     case TM_COLUMN_COMPILATION:
2105     case TM_COLUMN_THUMB_PATH:
2106     case TM_COLUMN_TV_SHOW:
2107     case TM_COLUMN_TV_EPISODE:
2108     case TM_COLUMN_TV_NETWORK:
2109     case TM_COLUMN_ALBUMARTIST:
2110     case TM_COLUMN_SORT_ARTIST:
2111     case TM_COLUMN_SORT_TITLE:
2112     case TM_COLUMN_SORT_ALBUM:
2113     case TM_COLUMN_SORT_ALBUMARTIST:
2114     case TM_COLUMN_SORT_COMPOSER:
2115     case TM_COLUMN_SORT_TVSHOW:
2116 	gtk_tree_view_set_enable_search (GTK_TREE_VIEW (track_treeview), TRUE);
2117 	break;
2118     case TM_COLUMN_TRACK_NR:
2119     case TM_COLUMN_IPOD_ID:
2120     case TM_COLUMN_TRANSFERRED:
2121     case TM_COLUMN_SIZE:
2122     case TM_COLUMN_TRACKLEN:
2123     case TM_COLUMN_BITRATE:
2124     case TM_COLUMN_PLAYCOUNT:
2125     case TM_COLUMN_RATING:
2126     case TM_COLUMN_TIME_PLAYED:
2127     case TM_COLUMN_TIME_MODIFIED:
2128     case TM_COLUMN_VOLUME:
2129     case TM_COLUMN_CD_NR:
2130     case TM_COLUMN_TIME_ADDED:
2131     case TM_COLUMN_SOUNDCHECK:
2132     case TM_COLUMN_SAMPLERATE:
2133     case TM_COLUMN_BPM:
2134     case TM_COLUMN_TIME_RELEASED:
2135     case TM_COLUMN_MEDIA_TYPE:
2136     case TM_COLUMN_SEASON_NR:
2137     case TM_COLUMN_EPISODE_NR:
2138     case TM_COLUMN_LYRICS:
2139     case TM_NUM_COLUMNS:
2140 	gtk_tree_view_set_enable_search (GTK_TREE_VIEW (track_treeview), FALSE);
2141 	break;
2142     }
2143     prefs_set_int (TM_PREFS_SEARCH_COLUMN, newcol);
2144 }
2145 
2146 
2147 /* This is called before when changing the sort order or the sort
2148    column, and before doing the sorting */
tm_sort_column_changed(GtkTreeSortable * ts,gpointer user_data)2149 static void tm_sort_column_changed (GtkTreeSortable *ts,
2150 				    gpointer user_data)
2151 {
2152     static gint lastcol = -1; /* which column was sorted last time? */
2153     gchar *buf;
2154     gint newcol;
2155     GtkSortType order;
2156     GList *tracks, *gl;
2157     gint32 i, inc;
2158 
2159     gtk_tree_sortable_get_sort_column_id (ts, &newcol, &order);
2160 
2161 /*     printf ("scc -- col: %d, order: %d\n", newcol, order);  */
2162 
2163     /* set compare function for strings (to speed up sorting) */
2164     buf = g_strdup_printf ("sort_ign_field_%d", TM_to_T (newcol));
2165     if (prefs_get_int (buf))
2166 	string_compare_func = compare_string_fuzzy;
2167     else
2168 	string_compare_func = compare_string;
2169     g_free (buf);
2170 
2171     /* don't do anything if no sort column is set */
2172     if (newcol == -2)
2173     {
2174 	lastcol = newcol;
2175 	return;
2176     }
2177 
2178     if (newcol != lastcol)
2179     {
2180 	tm_sort_counter (-1);
2181 	lastcol = newcol;
2182     }
2183 
2184     if (tm_sort_counter (1) >= 3)
2185     { /* after clicking three times, reset sort order! */
2186 	tm_unsort ();  /* also resets sort counter */
2187     }
2188     else
2189     {
2190 	prefs_set_int("tm_sort", order);
2191     }
2192     prefs_set_int("tm_sortcol", newcol);
2193 
2194     tm_set_search_column (newcol);
2195 
2196     if(prefs_get_int("tm_autostore"))  tm_rows_reordered ();
2197     sort_window_update ();
2198 
2199     /* stable sorting: index original order */
2200     tracks = tm_get_all_tracks ();
2201     /* make numbering ascending or decending depending on sort order
2202        (then we don't have to worry about the sort order when doing
2203        the comparison in tm_data_compare_func() */
2204     if (order == GTK_SORT_ASCENDING)
2205     {
2206 	i=0;
2207 	inc = 1;
2208     }
2209     else
2210     {
2211 	i=-1;
2212 	inc = -1;
2213     }
2214     for (gl=tracks; gl; gl=gl->next)
2215     {
2216 	ExtraTrackData *etr;
2217 	Track *tr = gl->data;
2218 	g_return_if_fail (tr);
2219 	etr = tr->userdata;
2220 	g_return_if_fail (etr);
2221 	etr->sortindex = i;
2222 	i+=inc;
2223     }
2224     g_list_free (tracks);
2225 }
2226 
2227 
tm_sort(TM_item col,GtkSortType order)2228 void tm_sort (TM_item col, GtkSortType order)
2229 {
2230     if (track_treeview)
2231     {
2232 	GtkTreeModel *model= gtk_tree_view_get_model (track_treeview);
2233 
2234 	if (GTK_IS_TREE_MODEL_FILTER (model))
2235 	{
2236 	    model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(model));
2237 	}
2238 
2239 	if (order != SORT_NONE)
2240 	{
2241 	    gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
2242 						  col, order);
2243 	}
2244 	else
2245 	{ /* only unsort if treeview is sorted */
2246 	    gint column;
2247 	    GtkSortType order;
2248 	    if (gtk_tree_sortable_get_sort_column_id
2249 		(GTK_TREE_SORTABLE (model), &column, &order))
2250 	    {
2251 		/* column == -2 actually is not defined, but it means
2252 		   that the model is unsorted. The sortable interface
2253 		   is badly implemented in gtk 2.4 */
2254 		if (column != -2)
2255 		    tm_unsort ();
2256 	    }
2257 	}
2258     }
2259 }
2260 
2261 
2262 static void
tm_setup_renderer(GtkCellRenderer * renderer)2263 tm_setup_renderer(GtkCellRenderer *renderer)
2264 {
2265     TM_item column;
2266     column = (TM_item)g_object_get_data (G_OBJECT (renderer), "column");
2267 
2268     g_return_if_fail ((column >= 0) && (column < TM_NUM_COLUMNS));
2269 
2270     switch (column)
2271     {
2272     case TM_COLUMN_TITLE:
2273     case TM_COLUMN_ARTIST:
2274     case TM_COLUMN_ALBUM:
2275     case TM_COLUMN_GENRE:
2276     case TM_COLUMN_COMPOSER:
2277     case TM_COLUMN_COMMENT:
2278     case TM_COLUMN_FILETYPE:
2279     case TM_COLUMN_GROUPING:
2280     case TM_COLUMN_CATEGORY:
2281     case TM_COLUMN_DESCRIPTION:
2282     case TM_COLUMN_PODCASTURL:
2283     case TM_COLUMN_PODCASTRSS:
2284     case TM_COLUMN_SUBTITLE:
2285     case TM_COLUMN_TIME_PLAYED:
2286     case TM_COLUMN_TIME_MODIFIED:
2287     case TM_COLUMN_TIME_ADDED:
2288     case TM_COLUMN_TIME_RELEASED:
2289     case TM_COLUMN_TV_SHOW:
2290     case TM_COLUMN_TV_EPISODE:
2291     case TM_COLUMN_TV_NETWORK:
2292     case TM_COLUMN_ALBUMARTIST:
2293     case TM_COLUMN_SORT_ARTIST:
2294     case TM_COLUMN_SORT_TITLE:
2295     case TM_COLUMN_SORT_ALBUM:
2296     case TM_COLUMN_SORT_ALBUMARTIST:
2297     case TM_COLUMN_SORT_COMPOSER:
2298     case TM_COLUMN_SORT_TVSHOW:
2299       g_object_set (G_OBJECT (renderer),
2300 		    "editable", TRUE,
2301 		    "xalign", 0.0, NULL);
2302       break;
2303     case TM_COLUMN_MEDIA_TYPE:
2304       g_object_set (G_OBJECT (renderer),
2305 		    "editable", FALSE,
2306 		    "xalign", 0.0, NULL);
2307       break;
2308     case TM_COLUMN_TRACK_NR:
2309     case TM_COLUMN_CD_NR:
2310     case TM_COLUMN_BITRATE:
2311     case TM_COLUMN_SAMPLERATE:
2312     case TM_COLUMN_BPM:
2313     case TM_COLUMN_PLAYCOUNT:
2314     case TM_COLUMN_YEAR:
2315     case TM_COLUMN_VOLUME:
2316     case TM_COLUMN_SOUNDCHECK:
2317     case TM_COLUMN_TRACKLEN:
2318     case TM_COLUMN_SEASON_NR:
2319     case TM_COLUMN_EPISODE_NR:
2320       g_object_set (G_OBJECT (renderer),
2321 		    "editable", TRUE,
2322 		    "xalign", 1.0, NULL);
2323       break;
2324     case TM_COLUMN_IPOD_ID:
2325     case TM_COLUMN_SIZE:
2326       g_object_set (G_OBJECT (renderer),
2327 		    "editable", FALSE,
2328 		    "xalign", 1.0, NULL);
2329       break;
2330     case TM_COLUMN_PC_PATH:
2331     case TM_COLUMN_IPOD_PATH:
2332     case TM_COLUMN_THUMB_PATH:
2333       g_object_set (G_OBJECT (renderer),
2334 		    "editable", FALSE,
2335 		    "xalign", 0.0, NULL);
2336       break;
2337     case TM_COLUMN_LYRICS:
2338       g_object_set (G_OBJECT (renderer),
2339 		    "activatable", TRUE, NULL);
2340       break;
2341     case TM_COLUMN_TRANSFERRED:
2342       g_object_set (G_OBJECT (renderer),
2343 		    "activatable", FALSE, NULL);
2344       break;
2345     case TM_COLUMN_COMPILATION:
2346       g_object_set (G_OBJECT (renderer),
2347 		    "activatable", TRUE, NULL);
2348       break;
2349     case TM_COLUMN_RATING:
2350       break;
2351     case TM_NUM_COLUMNS:
2352       g_return_if_reached();
2353     }
2354 }
2355 
2356 /* Adds the columns to our track_treeview */
tm_add_column(TM_item tm_item,gint pos)2357 static GtkTreeViewColumn *tm_add_column (TM_item tm_item, gint pos)
2358 {
2359 	GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
2360 	GtkTreeViewColumn *col = NULL;
2361 	const gchar *text;
2362 	GtkCellRenderer *renderer = NULL;  /* default */
2363 	GtkTooltips *tt;
2364 	GtkTreeCellDataFunc cell_data_func = tm_cell_data_text_func;
2365 
2366 
2367 	g_return_val_if_fail (gtkpod_window, NULL);
2368 	tt = g_object_get_data (G_OBJECT (gtkpod_window), "main_tooltips");
2369 	g_return_val_if_fail (tt, NULL);
2370 
2371 	g_return_val_if_fail (tm_item >= 0, NULL);
2372 	g_return_val_if_fail (tm_item < TM_NUM_COLUMNS, NULL);
2373 
2374 	text = gettext (get_tm_string (tm_item));
2375 
2376 	g_return_val_if_fail (text, NULL);
2377 
2378 	col = gtk_tree_view_column_new ();
2379 
2380 	switch (tm_item)
2381 	{
2382 	case TM_COLUMN_TITLE:
2383 	  /* Add additional toggle box for 'checked' property */
2384 	  renderer = gtk_cell_renderer_toggle_new ();
2385 	  g_object_set_data (G_OBJECT (renderer), "column",
2386 			 (gint *)tm_item);
2387 	  g_signal_connect (G_OBJECT (renderer), "toggled",
2388 			G_CALLBACK (tm_cell_toggled), model);
2389 	  gtk_tree_view_column_pack_start (col, renderer, FALSE);
2390 	  gtk_tree_view_column_set_cell_data_func (col, renderer,
2391 						   tm_cell_data_func_toggle,
2392 						   NULL, NULL);
2393 	  renderer = NULL;
2394 	  break;
2395 	case TM_COLUMN_ARTIST:
2396 	case TM_COLUMN_ALBUM:
2397 	case TM_COLUMN_GENRE:
2398 	case TM_COLUMN_COMPOSER:
2399 	case TM_COLUMN_COMMENT:
2400 	case TM_COLUMN_FILETYPE:
2401 	case TM_COLUMN_GROUPING:
2402 	case TM_COLUMN_BITRATE:
2403 	case TM_COLUMN_SAMPLERATE:
2404 	case TM_COLUMN_BPM:
2405 	case TM_COLUMN_CATEGORY:
2406 	case TM_COLUMN_DESCRIPTION:
2407 	case TM_COLUMN_PODCASTURL:
2408 	case TM_COLUMN_PODCASTRSS:
2409 	case TM_COLUMN_SUBTITLE:
2410 	case TM_COLUMN_PC_PATH:
2411 	case TM_COLUMN_IPOD_PATH:
2412 	case TM_COLUMN_THUMB_PATH:
2413 	case TM_COLUMN_SIZE:
2414 	case TM_COLUMN_MEDIA_TYPE:
2415 	case TM_COLUMN_TV_SHOW:
2416 	case TM_COLUMN_TV_EPISODE:
2417 	case TM_COLUMN_TV_NETWORK:
2418 	case TM_COLUMN_SEASON_NR:
2419 	case TM_COLUMN_EPISODE_NR:
2420 	case TM_COLUMN_ALBUMARTIST:
2421 	case TM_COLUMN_SORT_ARTIST:
2422 	case TM_COLUMN_SORT_TITLE:
2423 	case TM_COLUMN_SORT_ALBUM:
2424 	case TM_COLUMN_SORT_ALBUMARTIST:
2425 	case TM_COLUMN_SORT_COMPOSER:
2426 	case TM_COLUMN_SORT_TVSHOW:
2427 	  break;
2428 	/* for some column names we want to use shorter alternatives to
2429 	 get_tm_string() */
2430 	case TM_COLUMN_RATING:
2431 	  text = _("Rtng");
2432 	  break;
2433 	case TM_COLUMN_TRACK_NR:
2434 	  text = _("#");
2435 	  break;
2436 	case TM_COLUMN_CD_NR:
2437 	  text = _("CD");
2438 	  break;
2439 	case TM_COLUMN_IPOD_ID:
2440 	  text = _("ID");
2441 	  break;
2442 	case TM_COLUMN_TRANSFERRED:
2443 	  text = _("Trnsfrd");
2444 	  renderer = gtk_cell_renderer_toggle_new ();
2445 	  cell_data_func = tm_cell_data_toggle_func;
2446 	  break;
2447 	case TM_COLUMN_LYRICS:
2448 	  renderer = gtk_cell_renderer_toggle_new ();
2449 	  cell_data_func = tm_cell_data_toggle_func;
2450 	  g_signal_connect (G_OBJECT (renderer), "toggled",
2451 			G_CALLBACK (tm_cell_toggled), model);
2452 	  break;
2453 	case TM_COLUMN_COMPILATION:
2454 	  text = _("Cmpl");
2455 	  renderer = gtk_cell_renderer_toggle_new ();
2456 	  cell_data_func = tm_cell_data_toggle_func;
2457 	  g_signal_connect (G_OBJECT (renderer), "toggled",
2458 			G_CALLBACK (tm_cell_toggled), model);
2459 	  break;
2460 	case TM_COLUMN_TRACKLEN:
2461 	  text = _("Time");
2462 	  break;
2463 	case TM_COLUMN_PLAYCOUNT:
2464 	  text = _("Plycnt");
2465 	  break;
2466 	case TM_COLUMN_TIME_PLAYED:
2467 	  text = _("Played");
2468 	  break;
2469 	case TM_COLUMN_TIME_MODIFIED:
2470 	  text = _("Modified");
2471 	  break;
2472 	case TM_COLUMN_TIME_ADDED:
2473 	  text = _("Added");
2474 	  break;
2475 	case TM_COLUMN_TIME_RELEASED:
2476 	  text = _("Released");
2477 	  break;
2478 	case TM_COLUMN_YEAR:
2479 	  text = _("Year");
2480 	  break;
2481 	case TM_COLUMN_VOLUME:
2482 	  text = _("Vol.");
2483 	  break;
2484 	case TM_COLUMN_SOUNDCHECK:
2485 	  text = _("Sndchk.");
2486 	  break;
2487 	case TM_NUM_COLUMNS:
2488 	  g_return_val_if_reached (NULL);
2489 	  break;
2490 	}
2491 
2492 	if (!renderer)
2493 	{
2494 		if (tm_item == TM_COLUMN_RATING)
2495 		{
2496 			renderer = rb_cell_renderer_rating_new ();
2497 			cell_data_func = tm_cell_data_rating_func;
2498 			g_signal_connect (G_OBJECT (renderer), "rated",
2499 							  G_CALLBACK (tm_rating_edited), NULL);
2500 		}
2501 		else
2502 		{
2503 			/* text renderer -- editable/not editable is done in
2504 			   tm_cell_data_func() */
2505 			renderer = gtk_cell_renderer_text_new ();
2506 			g_signal_connect (G_OBJECT (renderer), "edited",
2507 							  G_CALLBACK (tm_cell_edited), model);
2508 		}
2509 	}
2510 
2511 	g_object_set_data (G_OBJECT (renderer), "column",
2512 			 (gint *)tm_item);
2513 
2514 	tm_setup_renderer(renderer);
2515 
2516 	gtk_tree_view_column_set_title (col, text);
2517 	gtk_tree_view_column_pack_start (col, renderer, FALSE);
2518 	gtk_tree_view_column_set_cell_data_func (col, renderer,
2519 						 cell_data_func, NULL, NULL);
2520 	gtk_tree_view_column_set_sort_column_id (col, tm_item);
2521 	gtk_tree_view_column_set_resizable (col, TRUE);
2522 	gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
2523 	gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (model), tm_item,
2524 				   tm_data_compare_func, NULL, NULL);
2525 	gtk_tree_view_column_set_reorderable (col, TRUE);
2526 
2527 	gtk_tree_view_insert_column (track_treeview, col, pos);
2528 	tm_columns[tm_item] = col;
2529 
2530 	if (pos != -1)
2531 	{
2532 	  gtk_tree_view_column_set_visible (col,
2533 					prefs_get_int_index("col_visible", tm_item));
2534 	}
2535 	if (get_tm_tooltip (tm_item))
2536 	  gtk_tooltips_set_tip (tt, col->button,
2537 				gettext (get_tm_tooltip (tm_item)),
2538 				NULL);
2539 	return col;
2540 }
2541 
2542 
2543 /* Adds the columns to our track_treeview */
tm_add_columns(void)2544 static void tm_add_columns (void)
2545 {
2546     gint i;
2547 
2548     for (i = 0 ; i < TM_NUM_COLUMNS; ++i)
2549     {
2550         tm_add_column (prefs_get_int_index("col_order", i), -1);
2551     }
2552 
2553     tm_show_preferred_columns ();
2554 }
2555 
tm_select_current_position(gint x,gint y)2556 static void tm_select_current_position (gint x, gint y)
2557 {
2558     if (track_treeview)
2559     {
2560 	GtkTreePath *path;
2561 
2562 	gtk_tree_view_get_path_at_pos (track_treeview,
2563 				       x, y, &path, NULL, NULL, NULL);
2564 	if (path)
2565 	{
2566 	    GtkTreeSelection *ts = gtk_tree_view_get_selection (track_treeview);
2567 	    gtk_tree_selection_select_path (ts, path);
2568 	    gtk_tree_path_free (path);
2569 	}
2570     }
2571 }
2572 
2573 static gboolean
tm_button_press_event(GtkWidget * w,GdkEventButton * e,gpointer data)2574 tm_button_press_event(GtkWidget *w, GdkEventButton *e, gpointer data)
2575 {
2576     if(w && e)
2577     {
2578 	switch(e->button)
2579 	{
2580 	case 1:
2581 /*
2582 	    printf ("Pressed in cell %d (%f/%f)\n\n",
2583 		    tree_view_get_cell_from_pos(GTK_TREE_VIEW(w),
2584 						(guint)e->x, (guint)e->y, NULL),
2585 		    e->x, e->y);
2586 */
2587 	    break;
2588 	case 3:
2589 	    tm_select_current_position (e->x, e->y);
2590 	    tm_context_menu_init ();
2591 	    return TRUE;
2592 	default:
2593 	    break;
2594 	}
2595     }
2596     return(FALSE);
2597 }
2598 
2599 static gboolean
tm_selection_changed_cb(gpointer data)2600 tm_selection_changed_cb (gpointer data)
2601 {
2602     GtkTreeView *treeview = GTK_TREE_VIEW (data);
2603     GtkTreePath *path;
2604     GtkTreeViewColumn *column;
2605     TM_item col_id;
2606 
2607     gtk_tree_view_get_cursor (treeview, &path, &column);
2608     if (path)
2609     {
2610 	col_id = tm_lookup_col_id (column);
2611 	if (col_id != -1)  tm_set_search_column (col_id);
2612     }
2613     info_update_track_view ();
2614 
2615     /* update the coverart display */
2616     GList *selected = display_get_selection (prefs_get_int("sort_tab_num"));
2617     if (selected != NULL)
2618     {
2619     	Track *track = selected->data;
2620     	if(track != NULL)
2621     		coverart_select_cover (track);
2622     }
2623 
2624     return FALSE;
2625 }
2626 
2627 /* called when the track selection changes */
2628 static void
tm_selection_changed(GtkTreeSelection * selection,gpointer data)2629 tm_selection_changed(GtkTreeSelection *selection, gpointer data)
2630 {
2631     g_idle_add (tm_selection_changed_cb,
2632 		gtk_tree_selection_get_tree_view (selection));
2633 }
2634 
2635 
2636 /* Create tracks treeview */
tm_create_treeview(void)2637 void tm_create_treeview (void)
2638 {
2639     GtkTreeModel *model = NULL;
2640     GtkWidget *track_window = gtkpod_xml_get_widget (main_window_xml, "track_window");
2641     GtkTreeSelection *select;
2642     gint col;
2643     GtkWidget *stv = gtk_tree_view_new ();
2644 
2645     /* create tree view */
2646     if (track_treeview)
2647     {   /* delete old tree view */
2648       model = gtk_tree_view_get_model (track_treeview);
2649       /* FIXME: how to delete model? */
2650       gtk_widget_destroy (GTK_WIDGET (track_treeview));
2651     }
2652     track_treeview = GTK_TREE_VIEW (stv);
2653     gtk_widget_show (stv);
2654     gtk_container_add (GTK_CONTAINER (track_window), stv);
2655     /* create model (we only need one column for the model -- only a
2656     * pointer to the track has to be stored) */
2657     model = GTK_TREE_MODEL (
2658       gtk_list_store_new (1, G_TYPE_POINTER));
2659     gtk_tree_view_set_model (track_treeview, GTK_TREE_MODEL (model));
2660     gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (track_treeview), TRUE);
2661     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (track_treeview), TRUE);
2662     gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (track_treeview),
2663                        tm_search_equal_func,
2664                        NULL,
2665                        NULL);
2666     select = gtk_tree_view_get_selection (track_treeview);
2667     gtk_tree_selection_set_mode (select,
2668                    GTK_SELECTION_MULTIPLE);
2669     g_signal_connect (G_OBJECT (select) , "changed",
2670             G_CALLBACK (tm_selection_changed),
2671             NULL);
2672 
2673     tm_add_columns ();
2674 
2675     /*   gtk_drag_source_set (GTK_WIDGET (track_treeview), GDK_BUTTON1_MASK, */
2676     /* 		       tm_drag_types, TGNR (tm_drag_types), */
2677     /* 		       GDK_ACTION_COPY|GDK_ACTION_MOVE); */
2678     /*   gtk_tree_view_enable_model_drag_dest(track_treeview, tm_drop_types, */
2679     /* 				       TGNR (tm_drop_types), */
2680     /* 				       GDK_ACTION_COPY|GDK_ACTION_MOVE); */
2681     /*   /\* need the gtk_drag_dest_set() with no actions ("0") so that the */
2682     /*      data_received callback gets the correct info value. This is most */
2683     /*      likely a bug... *\/ */
2684     /*   gtk_drag_dest_set_target_list (GTK_WIDGET (track_treeview), */
2685     /* 				 gtk_target_list_new (tm_drop_types, */
2686     /* 						      TGNR (tm_drop_types))); */
2687 
2688 
2689     gtk_drag_source_set (GTK_WIDGET (track_treeview),
2690                GDK_BUTTON1_MASK,
2691                tm_drag_types, TGNR (tm_drag_types),
2692                GDK_ACTION_COPY|GDK_ACTION_MOVE);
2693     gtk_drag_dest_set (GTK_WIDGET (track_treeview),
2694              0,
2695              tm_drop_types, TGNR (tm_drop_types),
2696              GDK_ACTION_COPY|GDK_ACTION_MOVE);
2697 
2698 
2699     g_signal_connect ((gpointer) track_treeview, "drag-begin",
2700             G_CALLBACK (tm_drag_begin),
2701             NULL);
2702 
2703     g_signal_connect ((gpointer) track_treeview, "drag-data-delete",
2704             G_CALLBACK (tm_drag_data_delete),
2705             NULL);
2706 
2707     g_signal_connect ((gpointer) track_treeview, "drag-data-get",
2708             G_CALLBACK (tm_drag_data_get),
2709             NULL);
2710 
2711     g_signal_connect ((gpointer) track_treeview, "drag-data-received",
2712             G_CALLBACK (tm_drag_data_received),
2713             NULL);
2714 
2715     g_signal_connect ((gpointer) track_treeview, "drag-drop",
2716             G_CALLBACK (tm_drag_drop),
2717             NULL);
2718 
2719     g_signal_connect ((gpointer) track_treeview, "drag-end",
2720             G_CALLBACK (tm_drag_end),
2721             NULL);
2722 
2723     g_signal_connect ((gpointer) track_treeview, "drag-leave",
2724             G_CALLBACK (tm_drag_leave),
2725             NULL);
2726 
2727     g_signal_connect ((gpointer) track_treeview, "drag-motion",
2728             G_CALLBACK (tm_drag_motion),
2729             NULL);
2730 
2731     g_signal_connect_after ((gpointer) stv, "key_release_event",
2732               G_CALLBACK (on_track_treeview_key_release_event),
2733               NULL);
2734     g_signal_connect ((gpointer) track_treeview, "button-press-event",
2735             G_CALLBACK (tm_button_press_event),
2736             NULL);
2737     g_signal_connect (G_OBJECT (model), "sort-column-changed",
2738             G_CALLBACK (tm_sort_column_changed),
2739             (gpointer)0);
2740 
2741     /* set correct column for typeahead */
2742     if (prefs_get_int_value (TM_PREFS_SEARCH_COLUMN, &col))
2743     {
2744       tm_set_search_column (col);
2745     }
2746     else
2747     {   /* reasonable default */
2748       tm_set_search_column (TM_COLUMN_TITLE);
2749     }
2750 }
2751 
2752 
2753 void
tm_show_preferred_columns(void)2754 tm_show_preferred_columns (void)
2755 {
2756     gint i;
2757     gboolean horizontal_scrollbar = prefs_get_int ("horizontal_scrollbar");
2758     GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (gtkpod_xml_get_widget (main_window_xml,
2759                                                                               "track_window"));
2760 
2761     if (horizontal_scrollbar)
2762         gtk_scrolled_window_set_policy (scroller, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2763     else
2764         gtk_scrolled_window_set_policy (scroller, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
2765 
2766     for (i = 0; i < TM_NUM_COLUMNS; ++i)
2767     {
2768 	TM_item tm_item = prefs_get_int_index ("col_order", i);
2769 	GtkTreeViewColumn *tvc = gtk_tree_view_get_column (track_treeview, i);
2770 	gboolean visible = prefs_get_int_index ("col_visible", tm_item);
2771 	gint col_width;
2772 
2773         gtk_tree_view_column_set_visible (tvc, visible);
2774 
2775 	col_width = prefs_get_int_index ("tm_col_width", tm_item);
2776 
2777 	if (col_width == 0)
2778 	    col_width = 80;
2779 
2780         if (horizontal_scrollbar)
2781         {
2782 	    gtk_tree_view_column_set_fixed_width (tvc, col_width);
2783             gtk_tree_view_column_set_min_width (tvc, -1);
2784             gtk_tree_view_column_set_expand (tvc, FALSE);
2785         }
2786         else
2787         {
2788             switch (tm_item)
2789             {
2790             case TM_COLUMN_TITLE:
2791             case TM_COLUMN_ARTIST:
2792             case TM_COLUMN_ALBUM:
2793             case TM_COLUMN_GENRE:
2794             case TM_COLUMN_COMPOSER:
2795             case TM_COLUMN_COMMENT:
2796             case TM_COLUMN_CATEGORY:
2797             case TM_COLUMN_DESCRIPTION:
2798             case TM_COLUMN_PODCASTURL:
2799             case TM_COLUMN_PODCASTRSS:
2800             case TM_COLUMN_SUBTITLE:
2801             case TM_COLUMN_PC_PATH:
2802             case TM_COLUMN_IPOD_PATH:
2803             case TM_COLUMN_THUMB_PATH:
2804             case TM_COLUMN_TV_SHOW:
2805             case TM_COLUMN_TV_EPISODE:
2806             case TM_COLUMN_TV_NETWORK:
2807             case TM_COLUMN_ALBUMARTIST:
2808                 gtk_tree_view_column_set_min_width (tvc, 0);
2809                 gtk_tree_view_column_set_expand (tvc, TRUE);
2810                 break;
2811             default:
2812                 gtk_tree_view_column_set_min_width (tvc, 80);
2813                 gtk_tree_view_column_set_fixed_width (tvc, col_width);
2814                 gtk_tree_view_column_set_expand (tvc, FALSE);
2815                 break;
2816             }
2817         }
2818     }
2819 }
2820 
2821 
2822 /* update the cfg structure (preferences) with the current sizes /
2823    positions (called by display_update_default_sizes():
2824    column widths of track model */
tm_update_default_sizes(void)2825 void tm_update_default_sizes (void)
2826 {
2827     TM_item tm_item;
2828     GtkTreeViewColumn *col;
2829     gint col_width;
2830 
2831     /* column widths */
2832     for (tm_item=0; tm_item<TM_NUM_COLUMNS; ++tm_item)
2833     {
2834 	col = tm_columns [tm_item];
2835 	if (col)
2836 	{
2837 	    col_width = gtk_tree_view_column_get_width (col);
2838 
2839 	    if (col_width > 0)
2840 	    {   /* columns not displayed seem to be 0 pixels wide --
2841 		   only change the visible columns */
2842 		prefs_set_int_index ("tm_col_width", tm_item, col_width);
2843 	    }
2844 	}
2845     }
2846 }
2847 
2848 
2849 /* get the TM_ITEM column id for @column. Returns -1 if column could
2850    not be found */
tm_lookup_col_id(GtkTreeViewColumn * column)2851 static TM_item tm_lookup_col_id (GtkTreeViewColumn *column)
2852 {
2853     gint i;
2854 
2855     if (column)
2856     {
2857 	for (i=0; i<TM_NUM_COLUMNS; ++i)
2858 	{
2859 	    if (column == tm_columns[i]) return i;
2860 	}
2861     }
2862     return -1;
2863 }
2864 
2865 
2866 /* Compare function to avoid sorting */
tm_nosort_comp(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer user_data)2867 static gint tm_nosort_comp (GtkTreeModel *model,
2868 			    GtkTreeIter *a,
2869 			    GtkTreeIter *b,
2870 			    gpointer user_data)
2871 {
2872     return 0;
2873 }
2874 
2875 
2876 /* Disable sorting of the view during lengthy updates. */
2877 /* @enable: TRUE: enable, FALSE: disable */
tm_enable_disable_view_sort(gboolean enable)2878 void tm_enable_disable_view_sort (gboolean enable)
2879 {
2880     static gint disable_count = 0;
2881 
2882     if (enable)
2883     {
2884 	disable_count--;
2885 	if (disable_count < 0)
2886 	    fprintf (stderr, "Programming error: disable_count < 0\n");
2887 	if (disable_count == 0 && track_treeview)
2888 	{
2889 	    if (prefs_get_int("tm_sort") != SORT_NONE)
2890 	    {
2891 		/* Re-enable sorting */
2892 		GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
2893 
2894 		if (GTK_IS_TREE_MODEL_FILTER (model))
2895 		{
2896 		    model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(model));
2897 		}
2898 
2899 		if (BROKEN_GTK_TREE_SORT)
2900 		{
2901 		    gtk_tree_sortable_set_sort_func (
2902 			GTK_TREE_SORTABLE (model),
2903 			prefs_get_int("tm_sortcol"),
2904 			tm_data_compare_func, NULL, NULL);
2905 		}
2906 		else
2907 		{
2908 		    gtk_tree_sortable_set_sort_column_id (
2909 			GTK_TREE_SORTABLE (model),
2910 			prefs_get_int("tm_sortcol"),
2911 			prefs_get_int("tm_sort"));
2912 		}
2913 	    }
2914 	}
2915     }
2916     else
2917     {
2918 	if (disable_count == 0 && track_treeview)
2919 	{
2920 	    if (prefs_get_int("tm_sort") != SORT_NONE)
2921 	    {
2922 		/* Disable sorting */
2923 		GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
2924 
2925 		if (GTK_IS_TREE_MODEL_FILTER (model))
2926 		{
2927 		    model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(model));
2928 		}
2929 
2930 		if (BROKEN_GTK_TREE_SORT)
2931 		{
2932 		    gtk_tree_sortable_set_sort_func (
2933 			GTK_TREE_SORTABLE (model),
2934 			prefs_get_int("tm_sortcol"),
2935 			tm_nosort_comp, NULL, NULL);
2936 		}
2937 		else
2938 		{
2939 		    gtk_tree_sortable_set_sort_column_id (
2940 			GTK_TREE_SORTABLE (model),
2941 			GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
2942 			prefs_get_int("tm_sort"));
2943 		}
2944 	    }
2945 	}
2946 	disable_count++;
2947     }
2948 }
2949 
2950 
2951 
2952 /* Callback for adding tracks within tm_add_filelist */
tm_addtrackfunc(Playlist * plitem,Track * track,gpointer data)2953 void tm_addtrackfunc (Playlist *plitem, Track *track, gpointer data)
2954 {
2955     struct asf_data *asf = (struct asf_data *)data;
2956     GtkTreeModel *model;
2957     GtkTreeIter new_iter;
2958 
2959     model = gtk_tree_view_get_model (track_treeview);
2960 
2961 /*    printf("plitem: %p\n", plitem);
2962       if (plitem) printf("plitem->type: %d\n", plitem->type);*/
2963     /* add to playlist but not to the display */
2964     gp_playlist_add_track (plitem, track, FALSE);
2965 
2966     /* create new iter in track view */
2967     switch (asf->pos)
2968     {
2969     case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
2970     case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
2971     case GTK_TREE_VIEW_DROP_AFTER:
2972 	gtk_list_store_insert_after (get_model_as_store (model),
2973 				     &new_iter, asf->to_iter);
2974 	break;
2975     case GTK_TREE_VIEW_DROP_BEFORE:
2976 	gtk_list_store_insert_before (get_model_as_store (model),
2977 				      &new_iter, asf->to_iter);
2978 	break;
2979     }
2980     /* set the iter */
2981     tm_add_track_to_track_model (track, &new_iter);
2982 }
2983 
2984 
2985 /* DND: insert a list of files before/after @path
2986    @data: list of files
2987    @path: where to drop (NULL to drop at the end)
2988    @pos:  before/after... (ignored if @path is NULL)
2989 */
tm_add_filelist(gchar * data,GtkTreePath * path,GtkTreeViewDropPosition pos)2990 gboolean tm_add_filelist (gchar *data,
2991 			  GtkTreePath *path,
2992 			  GtkTreeViewDropPosition pos)
2993 {
2994     GtkTreeModel *model;
2995     gchar *buf = NULL, *use_data;
2996     gchar **files, **filep;
2997     Playlist *current_playlist = pm_get_selected_playlist ();
2998 
2999     g_return_val_if_fail (data, FALSE);
3000     g_return_val_if_fail (*data, FALSE);
3001     g_return_val_if_fail (current_playlist, FALSE);
3002 
3003     model = gtk_tree_view_get_model (track_treeview);
3004     g_return_val_if_fail (model, FALSE);
3005     if (path)
3006     {
3007     }
3008 
3009     if (pos != GTK_TREE_VIEW_DROP_BEFORE)
3010     {   /* need to reverse the list of files -- otherwise wie add them
3011 	 * in reverse order */
3012 	/* split the path list into individual strings */
3013 	gint len = strlen (data) + 1;
3014 	files = g_strsplit (data, "\n", -1);
3015 	filep = files;
3016 	/* find the end of the list */
3017 	while (*filep) ++filep;
3018 	/* reserve memory */
3019 	buf = g_malloc0 (len);
3020 	/* reverse the list */
3021 	while (filep != files)
3022 	{
3023 	    --filep;
3024 	    g_strlcat (buf, *filep, len);
3025 	    g_strlcat (buf, "\n", len);
3026 	}
3027 	g_strfreev (files);
3028 	use_data = buf;
3029     }
3030     else
3031     {
3032 	use_data = data;
3033     }
3034 
3035 /*     printf("filelist: (%s) -> (%s)\n", data, use_data); */
3036     /* initialize add-track-struct */
3037     if (path)
3038     {   /* add where specified (@path/@pos) */
3039         GtkTreeIter to_iter;
3040         GtkTreeIter temp;
3041 
3042         struct asf_data asf;
3043         if (!gtk_tree_model_get_iter (model, &to_iter, path))
3044             g_return_val_if_reached (FALSE);
3045 
3046         convert_iter (model, &to_iter, &temp);
3047         asf.to_iter = &temp;
3048         asf.pos = pos;
3049         add_text_plain_to_playlist (current_playlist->itdb,
3050                         current_playlist, use_data, 0,
3051                         tm_addtrackfunc, &asf);
3052     }
3053     else
3054     {   /* add to the end */
3055 	add_text_plain_to_playlist (current_playlist->itdb,
3056 				    current_playlist, use_data, 0,
3057 				    NULL, NULL);
3058     }
3059 
3060 /*    update_model_view (model); -- not needed */
3061     tm_rows_reordered ();
3062     C_FREE (buf);
3063     return TRUE;
3064 }
3065