1
/* Time-stamp: <2006-06-23 23:52:48 jcs>
3
| Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>
4
| Part of the gtkpod project.
6
| URL: http://www.gtkpod.org/
7
| URL: http://gtkpod.sourceforge.net/
9
| This program is free software; you can redistribute it and/or modify
10
| it under the terms of the GNU General Public License as published by
11
| the Free Software Foundation; either version 2 of the License, or
12
| (at your option) any later version.
14
| This program is distributed in the hope that it will be useful,
15
| but WITHOUT ANY WARRANTY; without even the implied warranty of
16
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
| GNU General Public License for more details.
19
| You should have received a copy of the GNU General Public License
20
| along with this program; if not, write to the Free Software
21
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
| iTunes and iPod are trademarks of Apple
25
| This product is not supported/written/published by Apple!
27
| $Id: display_songs.c,v 1.112 2006/09/21 15:03:13 jcsjcs Exp $
34
#include <gdk/gdkkeysyms.h>
41
#include <sys/types.h>
45
#include "display_private.h"
46
#include "display_itdb.h"
50
#include "misc_track.h"
52
#include "context_menus.h"
54
/* pointer to the treeview for the track display */
55
static GtkTreeView *track_treeview = NULL;
56
/* array with pointers to the columns used in the track display */
57
static GtkTreeViewColumn *tm_columns[TM_NUM_COLUMNS];
58
/* column in which track pointer is stored */
59
static const gint READOUT_COL = 0;
61
/* compare function to be used for string comparisons */
62
static gint (*string_compare_func) (const gchar *str1, const gchar *str2) = compare_string;
64
static GtkTreeViewColumn *tm_add_column (TM_item tm_item, gint position);
65
static TM_item tm_lookup_col_id (GtkTreeViewColumn *column);
67
/* Drag and drop definitions */
68
static GtkTargetEntry tm_drag_types [] = {
69
{ DND_GTKPOD_TM_PATHLIST_TYPE, 0, DND_GTKPOD_TM_PATHLIST },
70
{ DND_GTKPOD_TRACKLIST_TYPE, 0, DND_GTKPOD_TRACKLIST },
71
{ "text/uri-list", 0, DND_TEXT_URI_LIST },
72
{ "text/plain", 0, DND_TEXT_PLAIN },
73
{ "STRING", 0, DND_TEXT_PLAIN }
75
static GtkTargetEntry tm_drop_types [] = {
76
{ DND_GTKPOD_TM_PATHLIST_TYPE, 0, DND_GTKPOD_TM_PATHLIST },
77
{ "text/uri-list", 0, DND_TEXT_URI_LIST },
78
{ "text/plain", 0, DND_TEXT_PLAIN },
79
{ "STRING", 0, DND_TEXT_PLAIN }
83
const gchar *TM_PREFS_SEARCH_COLUMN = "tm_prefs_search_column";
86
/* ---------------------------------------------------------------- */
87
/* Section for track display */
88
/* DND -- Drag And Drop */
89
/* ---------------------------------------------------------------- */
92
/* Move the paths listed in @data before or after (according to @pos)
93
@path. Used for DND */
94
static gboolean tm_move_pathlist (gchar *data,
96
GtkTreeViewDropPosition pos)
99
GtkTreeIter *from_iter;
101
GList *iterlist = NULL;
103
gchar **paths, **pathp;
105
g_return_val_if_fail (data, FALSE);
106
g_return_val_if_fail (*data, FALSE);
108
model = gtk_tree_view_get_model (track_treeview);
109
g_return_val_if_fail (model, FALSE);
111
g_return_val_if_fail (gtk_tree_model_get_iter (model, &to_iter, path),
114
/* split the path list into individual strings */
115
paths = g_strsplit (data, "\n", -1);
117
/* Convert the list of paths into a list of iters */
120
from_iter = g_malloc (sizeof (GtkTreeIter));
121
if ((strlen (*pathp) > 0) &&
122
gtk_tree_model_get_iter_from_string (model, from_iter, *pathp))
124
iterlist = g_list_append (iterlist, from_iter);
129
/* Move the iters in iterlist before or after @to_iter */
132
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
133
case GTK_TREE_VIEW_DROP_AFTER:
134
for (link = g_list_last (iterlist); link; link = link->prev)
136
from_iter = (GtkTreeIter *)link->data;
137
gtk_list_store_move_after (GTK_LIST_STORE (model),
138
from_iter, &to_iter);
141
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
142
case GTK_TREE_VIEW_DROP_BEFORE:
143
for (link = g_list_first (iterlist); link; link = link->next)
145
from_iter = (GtkTreeIter *)link->data;
146
gtk_list_store_move_before (GTK_LIST_STORE (model),
147
from_iter, &to_iter);
153
for (link = iterlist; link; link = link->next)
155
g_list_free (iterlist);
157
tm_rows_reordered ();
163
* utility function for appending ipod track ids for track view (DND)
166
on_tm_dnd_get_track_foreach(GtkTreeModel *tm, GtkTreePath *tp,
167
GtkTreeIter *i, gpointer data)
170
GString *tracklist = (GString *)data;
172
g_return_if_fail (tracklist);
174
gtk_tree_model_get(tm, i, READOUT_COL, &tr, -1);
175
g_return_if_fail (tr);
177
g_string_append_printf (tracklist, "%p\n", tr);
182
* utility function for appending path for track view (DND)
185
on_tm_dnd_get_path_foreach(GtkTreeModel *tm, GtkTreePath *tp,
186
GtkTreeIter *iter, gpointer data)
188
GString *filelist = (GString *)data;
189
gchar *ps = gtk_tree_path_to_string (tp);
190
g_string_append_printf (filelist, "%s\n", ps);
195
* utility function for appending file for track view (DND)
198
on_tm_dnd_get_file_foreach(GtkTreeModel *tm, GtkTreePath *tp,
199
GtkTreeIter *iter, gpointer data)
202
GString *filelist = (GString *)data;
205
gtk_tree_model_get(tm, iter, READOUT_COL, &track, -1);
206
name = get_file_name_from_source (track, SOURCE_PREFER_LOCAL);
209
g_string_append_printf (filelist, "file:%s\n", name);
215
* utility function for appending file-uri for track view (DND)
218
on_tm_dnd_get_uri_foreach(GtkTreeModel *tm, GtkTreePath *tp,
219
GtkTreeIter *iter, gpointer data)
222
GString *filelist = (GString *)data;
225
gtk_tree_model_get(tm, iter, READOUT_COL, &track, -1);
226
name = get_file_name_from_source (track, SOURCE_PREFER_LOCAL);
229
gchar *uri = g_filename_to_uri (name, NULL, NULL);
232
g_string_append_printf (filelist, "%s\n", uri);
239
static void tm_drag_begin (GtkWidget *widget,
243
/* puts ("tm_drag_begin"); */
247
/* remove dragged playlist after successful MOVE */
248
static void tm_drag_data_delete (GtkWidget *widget,
252
GtkTreeSelection *ts;
253
Playlist *pl = pm_get_selected_playlist ();
256
/* puts ("tm_drag_data_delete"); */
258
g_return_if_fail (widget);
259
ts = gtk_tree_view_get_selection (GTK_TREE_VIEW(widget));
260
g_return_if_fail (ts);
261
/* number of selected tracks */
262
num = gtk_tree_selection_count_selected_rows (ts);
263
if (num == 0) return;
265
/* Check if we really have to delete the tracks */
266
if (!itdb_playlist_is_mpl (pl))
267
{ /* get list of selected tracks */
268
GString *reply = g_string_sized_new (2000);
272
gtk_tree_selection_selected_foreach(ts,
273
on_tm_dnd_get_track_foreach,
276
while(parse_tracks_from_string(&str, &track))
278
gp_playlist_remove_track (pl, track, DELETE_ACTION_PLAYLIST);
280
g_string_free (reply, TRUE);
282
gtkpod_statusbar_message (ngettext ("Moved one track",
283
"Moved %d tracks", num), num);
287
gtkpod_statusbar_message (ngettext ("Copied one track",
288
"Copied %d tracks", num), num);
294
static void tm_drag_end (GtkWidget *widget,
298
/* puts ("tm_drag_end"); */
299
display_remove_autoscroll_row_timeout (widget);
300
gtkpod_tracks_statusbar_update ();
304
static gboolean tm_drag_drop (GtkWidget *widget,
313
/* puts ("tm_drag_data_drop"); */
315
display_remove_autoscroll_row_timeout (widget);
317
target = gtk_drag_dest_find_target (widget, dc, NULL);
319
if (target != GDK_NONE)
321
gtk_drag_get_data (widget, dc, target, time);
327
static void tm_drag_leave (GtkWidget *widget,
332
/* puts ("tm_drag_leave"); */
333
display_remove_autoscroll_row_timeout (widget);
338
static gboolean tm_drag_motion (GtkWidget *widget,
345
GtkTreeView *treeview;
347
GtkTreePath *path = NULL;
348
GtkTreeViewDropPosition pos;
350
/* printf ("drag_motion suggested: %d actions: %d\n", */
351
/* dc->suggested_action, dc->actions); */
353
/* printf ("x: %d y: %d\n", x, y); */
355
g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
357
treeview = GTK_TREE_VIEW (widget);
359
display_install_autoscroll_row_timeout (widget);
361
/* optically set destination row if available */
362
if (gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
365
/* drops are only allowed before and after -- not onto
369
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
370
case GTK_TREE_VIEW_DROP_AFTER:
371
gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), path,
372
GTK_TREE_VIEW_DROP_AFTER);
374
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
375
case GTK_TREE_VIEW_DROP_BEFORE:
376
gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), path,
377
GTK_TREE_VIEW_DROP_BEFORE);
381
gtk_tree_path_free (path);
386
path = gtk_tree_path_new_first ();
387
gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), path,
388
GTK_TREE_VIEW_DROP_BEFORE);
389
gtk_tree_path_free (path);
395
if (pm_get_selected_playlist () == NULL)
396
{ /* no drop possible if no playlist is selected */
397
gdk_drag_status (dc, 0, time);
401
target = gtk_drag_dest_find_target (widget, dc, NULL);
403
/* no drop possible if no valid target can be found */
404
if (target == GDK_NONE)
406
gdk_drag_status (dc, 0, time);
410
if (widget == gtk_drag_get_source_widget (dc))
411
{ /* drag is within the same widget */
414
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
415
g_return_val_if_fail (model, FALSE);
416
if(gtk_tree_sortable_get_sort_column_id (
417
GTK_TREE_SORTABLE (model), &column, &order))
418
{ /* don't allow move because the model is sorted */
419
gdk_drag_status (dc, 0, time);
423
{ /* only allow moves within the same widget */
424
gdk_drag_status (dc, GDK_ACTION_MOVE, time);
428
{ /* whatever the source suggests */
429
gdk_drag_status (dc, dc->suggested_action, time);
436
static void tm_drag_data_get (GtkWidget *widget,
437
GdkDragContext *context,
438
GtkSelectionData *data,
443
GtkTreeSelection *ts = NULL;
444
GString *reply = g_string_sized_new (2000);
446
/* printf("tm drag get info: %d\n", info); */
447
if((data) && (ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget))))
451
case DND_GTKPOD_TRACKLIST:
452
gtk_tree_selection_selected_foreach(ts,
453
on_tm_dnd_get_track_foreach, reply);
455
case DND_GTKPOD_TM_PATHLIST:
456
gtk_tree_selection_selected_foreach(ts,
457
on_tm_dnd_get_path_foreach, reply);
459
case DND_TEXT_URI_LIST:
460
gtk_tree_selection_selected_foreach(ts,
461
on_tm_dnd_get_uri_foreach, reply);
464
gtk_tree_selection_selected_foreach(ts,
465
on_tm_dnd_get_file_foreach, reply);
468
g_warning ("Programming error: tm_drag_data_get received unknown info type (%d)\n", info);
472
gtk_selection_data_set(data, data->target, 8, reply->str, reply->len);
473
g_string_free (reply, TRUE);
476
static void tm_drag_data_received (GtkWidget *widget,
480
GtkSelectionData *data,
485
GtkTreePath *path = NULL;
486
GtkTreeModel *model = NULL;
487
GtkTreeViewDropPosition pos = 0;
488
gboolean result = FALSE;
490
/* printf ("sm drop received info: %d\n", info); */
492
/* sometimes we get empty dnd data, ignore */
493
if(widgets_blocked || (!dc) ||
494
(!data) || (data->length < 0)) return;
495
/* yet another check, i think it's an 8 bit per byte check */
496
if(data->format != 8) return;
498
display_remove_autoscroll_row_timeout (widget);
500
model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
501
g_return_if_fail (model);
502
if (!gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
506
gdk_window_get_pointer (
507
gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget)),
511
/* initialize with first displayed and drop before */
513
if (gtk_tree_model_get_iter_first (model, &iter))
515
path = gtk_tree_model_get_path (model, &iter);
516
pos = GTK_TREE_VIEW_DROP_BEFORE;
520
{ /* initialize with last path if available and drop after */
522
if (gtk_tree_model_get_iter_first (model, &iter))
524
GtkTreeIter last_valid_iter;
527
last_valid_iter = iter;
528
} while (gtk_tree_model_iter_next (model, &iter));
529
path = gtk_tree_model_get_path (model, &last_valid_iter);
530
pos = GTK_TREE_VIEW_DROP_AFTER;
536
{ /* map position onto BEFORE or AFTER */
539
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
540
case GTK_TREE_VIEW_DROP_AFTER:
541
pos = GTK_TREE_VIEW_DROP_AFTER;
543
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
544
case GTK_TREE_VIEW_DROP_BEFORE:
545
pos = GTK_TREE_VIEW_DROP_BEFORE;
552
case DND_GTKPOD_TM_PATHLIST:
553
g_return_if_fail (path);
554
result = tm_move_pathlist (data->data, path, pos);
555
dc->action = GDK_ACTION_MOVE;
556
gtk_drag_finish (dc, TRUE, FALSE, time);
559
result = tm_add_filelist (data->data, path, pos);
560
dc->action = dc->suggested_action;
561
if (dc->action == GDK_ACTION_MOVE)
562
gtk_drag_finish (dc, TRUE, TRUE, time);
564
gtk_drag_finish (dc, TRUE, FALSE, time);
566
case DND_TEXT_URI_LIST:
567
result = tm_add_filelist (data->data, path, pos);
568
dc->action = dc->suggested_action;
569
if (dc->action == GDK_ACTION_MOVE)
570
gtk_drag_finish (dc, TRUE, TRUE, time);
572
gtk_drag_finish (dc, TRUE, FALSE, time);
576
gtk_drag_finish (dc, FALSE, FALSE, time);
577
/* puts ("tm_drag_data_received(): should not be reached"); */
580
if (path) gtk_tree_path_free(path);
583
/* ---------------------------------------------------------------- */
584
/* Section for track display */
585
/* other callbacks */
586
/* ---------------------------------------------------------------- */
589
on_track_treeview_key_release_event (GtkWidget *widget,
596
if(!widgets_blocked && (mods & GDK_CONTROL_MASK))
598
switch(event->keyval)
601
/* gp_do_selected_tracks (update_tracks); */
610
/* ---------------------------------------------------------------- */
611
/* Section for track display */
612
/* ---------------------------------------------------------------- */
614
/* Append track to the track model (or write into @into_iter if != 0) */
615
void tm_add_track_to_track_model (Track *track, GtkTreeIter *into_iter)
618
GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
620
g_return_if_fail (model != NULL);
625
gtk_list_store_append (GTK_LIST_STORE (model), &iter);
627
gtk_list_store_set (GTK_LIST_STORE (model), &iter,
628
READOUT_COL, track, -1);
633
/* Used by remove_track() to remove track from model by calling
634
gtk_tree_model_foreach ().
635
Entry is deleted if data == track */
636
static gboolean tm_delete_track (GtkTreeModel *model,
643
gtk_tree_model_get (model, iter, READOUT_COL, &track, -1);
644
if(track == (Track *)data)
646
GtkTreeSelection *selection = gtk_tree_view_get_selection
648
/* printf("unselect...\n"); */
649
gtk_tree_selection_unselect_iter (selection, iter);
650
/* printf("...unselect done\n"); */
651
gtk_list_store_remove (GTK_LIST_STORE (model), iter);
658
/* Remove track from the display model */
659
void tm_remove_track (Track *track)
661
GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
663
gtk_tree_model_foreach (model, tm_delete_track, track);
667
/* Remove all tracks from the display model */
668
void tm_remove_all_tracks ()
670
GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
673
while (gtk_tree_model_get_iter_first (model, &iter))
675
gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
677
tm_store_col_order ();
678
tm_update_default_sizes ();
681
/* find out at which position column @tm_item is displayed */
682
/* static gint tm_get_col_position (TM_item tm_item) */
685
/* GtkTreeViewColumn *col; */
687
/* if (!track_treeview) return -1; */
689
/* for (i=0; i<TM_NUM_COLUMNS_PREFS; ++i) */
691
/* col = gtk_tree_view_get_column (track_treeview, i); */
692
/* if (col->sort_column_id == tm_item) return i; */
698
/* store the order of the track view columns */
699
void tm_store_col_order (void)
702
GtkTreeViewColumn *col;
704
for (i=0; i<TM_NUM_COLUMNS; ++i)
706
col = gtk_tree_view_get_column (track_treeview, i);
707
prefs_set_int_index("col_order", i, col->sort_column_id);
712
/* Used by tm_track_changed() to find the track that
713
changed name. If found, emit a "row changed" signal */
714
static gboolean tm_model_track_changed (GtkTreeModel *model,
721
gtk_tree_model_get (model, iter, READOUT_COL, &track, -1);
722
if(track == (Track *)data) {
723
gtk_tree_model_row_changed (model, path, iter);
730
/* One of the tracks has changed (this happens when the
731
iTunesDB is read and some IDs are renumbered */
732
void tm_track_changed (Track *track)
734
GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
735
/* printf("tm_track_changed enter\n");*/
737
gtk_tree_model_foreach (model, tm_model_track_changed, track);
738
/* printf("tm_track_changed exit\n");*/
743
#if ((GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION < 2))
744
/* gtk_tree_selection_get_selected_rows() was introduced in 2.2 */
747
GtkTreeModel **model;
751
void gtssf (GtkTreeModel *model,
756
struct gtsgsr *gts = data;
758
*gts->list = g_list_append (*gts->list, gtk_tree_path_copy (path));
761
GList *gtk_tree_selection_get_selected_rows (GtkTreeSelection *selection,
762
GtkTreeModel **model)
770
gtk_tree_selection_selected_foreach (selection, gtssf, >s);
776
/* Called when editable cell is being edited. Stores new data to the
777
track list. ID3 tags in the corresponding files are updated as
778
well, if activated in the pref settings */
780
tm_cell_edited (GtkCellRendererText *renderer,
781
const gchar *path_string,
782
const gchar *new_text,
786
GtkTreeSelection *selection;
790
GList *row_list, *row_node, *first;
793
column = (TM_item) g_object_get_data(G_OBJECT(renderer), "column");
794
multi_edit = prefs_get_int("multi_edit");
795
if (column == TM_COLUMN_TITLE)
796
multi_edit &= prefs_get_int("multi_edit_title");
797
selection = gtk_tree_view_get_selection(track_treeview);
798
row_list = gtk_tree_selection_get_selected_rows(selection, &model);
800
/* printf("tm_cell_edited: column: %d\n", column); */
802
sel_rows_num = g_list_length (row_list);
804
/* block widgets and update display if multi-edit is active */
805
if (multi_edit && (sel_rows_num > 1)) block_widgets ();
807
first = g_list_first (row_list);
809
for (row_node = first;
810
row_node && (multi_edit || (row_node == first));
811
row_node = g_list_next(row_node))
819
gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) row_node->data);
820
gtk_tree_model_get(model, &iter, READOUT_COL, &track, -1);
821
g_return_if_fail (track);
822
etr = track->userdata;
823
g_return_if_fail (etr);
830
case TM_COLUMN_TITLE:
831
case TM_COLUMN_ALBUM:
832
case TM_COLUMN_ARTIST:
833
case TM_COLUMN_GENRE:
834
case TM_COLUMN_COMPOSER:
835
case TM_COLUMN_COMMENT:
836
case TM_COLUMN_FILETYPE:
837
case TM_COLUMN_GROUPING:
838
case TM_COLUMN_CATEGORY:
839
case TM_COLUMN_DESCRIPTION:
840
case TM_COLUMN_PODCASTURL:
841
case TM_COLUMN_PODCASTRSS:
842
case TM_COLUMN_SUBTITLE:
843
case TM_COLUMN_TRACK_NR:
844
case TM_COLUMN_TRACKLEN:
845
case TM_COLUMN_CD_NR:
847
case TM_COLUMN_PLAYCOUNT:
848
case TM_COLUMN_RATING:
849
case TM_COLUMN_TIME_ADDED:
850
case TM_COLUMN_TIME_PLAYED:
851
case TM_COLUMN_TIME_MODIFIED:
852
case TM_COLUMN_TIME_RELEASED:
853
case TM_COLUMN_VOLUME:
854
case TM_COLUMN_SOUNDCHECK:
855
case TM_COLUMN_BITRATE:
856
case TM_COLUMN_SAMPLERATE:
858
changed = track_set_text (track, new_text, TM_to_T (column));
859
if (changed && (column == TM_COLUMN_TRACKLEN))
860
{ /* be on the safe side and reset starttime, stoptime and
862
gchar *path = get_file_name_from_source (track,
863
SOURCE_PREFER_LOCAL);
864
track->starttime = 0;
868
struct stat filestat;
869
stat (path, &filestat);
870
track->size = filestat.st_size;
873
/* redisplay some items to be on the safe side */
876
case TM_COLUMN_TRACK_NR:
877
case TM_COLUMN_CD_NR:
878
case TM_COLUMN_TRACKLEN:
879
case TM_COLUMN_TIME_ADDED:
880
case TM_COLUMN_TIME_PLAYED:
881
case TM_COLUMN_TIME_MODIFIED:
882
case TM_COLUMN_TIME_RELEASED:
883
str = track_get_text (track, TM_to_T (column));
884
g_object_set (G_OBJECT (renderer), "text", str, NULL);
892
g_warning ("Programming error: tm_cell_edited: unknown track cell (%d) edited\n", column);
895
/* printf (" changed: %d\n", changed); */
898
track->time_modified = itdb_time_get_mac_time ();
899
pm_track_changed (track); /* notify playlist model... */
900
data_changed (track->itdb); /* indicate that data has changed */
902
if (prefs_get_int("id3_write"))
905
/* should we update all ID3 tags or just the one
906
changed? -- obsoleted in 0.71*/
907
/* if (prefs_get_id3_writeall ()) tag_id = T_ALL;
908
else tag_id = TM_to_T (column);*/
909
write_tags_to_file (track);
910
/* display possible duplicates that have been removed */
911
gp_duplicate_remove (NULL, NULL);
914
while (widgets_blocked && gtk_events_pending ()) gtk_main_iteration ();
917
if (multi_edit && (sel_rows_num > 1)) release_widgets ();
919
g_list_foreach(row_list, (GFunc) gtk_tree_path_free, NULL);
920
g_list_free(row_list);
924
/* The track data is stored in a separate list (static GList *tracks)
925
and only pointers to the corresponding Track structure are placed
927
This function reads the data for the given cell from the list and
928
passes it to the renderer. */
929
static void tm_cell_data_func (GtkTreeViewColumn *tree_column,
930
GtkCellRenderer *renderer,
941
column = (TM_item)g_object_get_data (G_OBJECT (renderer), "column");
943
g_return_if_fail ((column >= 0) && (column < TM_NUM_COLUMNS));
945
gtk_tree_model_get (model, iter, READOUT_COL, &track, -1);
946
g_return_if_fail (track);
947
etr = track->userdata;
948
g_return_if_fail (etr);
950
g_return_if_fail (itdb);
952
text = track_get_text (track, TM_to_T (column));
956
case TM_COLUMN_TITLE:
957
case TM_COLUMN_ARTIST:
958
case TM_COLUMN_ALBUM:
959
case TM_COLUMN_GENRE:
960
case TM_COLUMN_COMPOSER:
961
case TM_COLUMN_COMMENT:
962
case TM_COLUMN_FILETYPE:
963
case TM_COLUMN_GROUPING:
964
case TM_COLUMN_CATEGORY:
965
case TM_COLUMN_DESCRIPTION:
966
case TM_COLUMN_PODCASTURL:
967
case TM_COLUMN_PODCASTRSS:
968
case TM_COLUMN_SUBTITLE:
969
case TM_COLUMN_TIME_PLAYED:
970
case TM_COLUMN_TIME_MODIFIED:
971
case TM_COLUMN_TIME_ADDED:
972
case TM_COLUMN_TIME_RELEASED:
973
g_object_set (G_OBJECT (renderer),
976
"xalign", 0.0, NULL);
978
case TM_COLUMN_TRACK_NR:
979
case TM_COLUMN_CD_NR:
980
case TM_COLUMN_BITRATE:
981
case TM_COLUMN_SAMPLERATE:
983
case TM_COLUMN_PLAYCOUNT:
985
case TM_COLUMN_RATING:
986
case TM_COLUMN_VOLUME:
987
case TM_COLUMN_SOUNDCHECK:
988
case TM_COLUMN_TRACKLEN:
989
g_object_set (G_OBJECT (renderer),
992
"xalign", 1.0, NULL);
994
case TM_COLUMN_IPOD_ID:
996
g_object_set (G_OBJECT (renderer),
999
"xalign", 1.0, NULL);
1001
case TM_COLUMN_PC_PATH:
1002
case TM_COLUMN_IPOD_PATH:
1003
case TM_COLUMN_THUMB_PATH:
1004
g_object_set (G_OBJECT (renderer),
1007
"xalign", 0.0, NULL);
1009
case TM_COLUMN_TRANSFERRED:
1010
g_object_set (G_OBJECT (renderer),
1011
"active", track->transferred,
1012
"activatable", FALSE, NULL);
1014
case TM_COLUMN_COMPILATION:
1015
g_object_set (G_OBJECT (renderer),
1016
"active", track->compilation,
1017
"activatable", TRUE, NULL);
1019
case TM_NUM_COLUMNS:
1027
/* Called when a toggle cell is being changed. Stores new data to the
1030
tm_cell_toggled (GtkCellRendererToggle *renderer,
1034
GtkTreeModel *model;
1035
GtkTreeSelection *selection;
1037
gboolean multi_edit;
1039
GList *row_list, *row_node, *first;
1042
column = (TM_item) g_object_get_data(G_OBJECT(renderer), "column");
1043
multi_edit = prefs_get_int("multi_edit");
1044
selection = gtk_tree_view_get_selection(track_treeview);
1045
row_list = gtk_tree_selection_get_selected_rows(selection, &model);
1047
/* printf("tm_cell_toggled: column: %d, arg1: %p\n", column, arg1); */
1049
sel_rows_num = g_list_length (row_list);
1051
/* block widgets and update display if multi-edit is active */
1052
if (multi_edit && (sel_rows_num > 1)) block_widgets ();
1054
first = g_list_first (row_list);
1056
/* active will show the old state -- before the toggle */
1057
g_object_get (G_OBJECT (renderer), "active", &active, NULL);
1059
for (row_node = first;
1060
row_node && (multi_edit || (row_node == first));
1061
row_node = g_list_next(row_node))
1067
gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) row_node->data);
1068
gtk_tree_model_get(model, &iter, READOUT_COL, &track, -1);
1073
case TM_COLUMN_TITLE:
1074
if ((active && (track->checked == 0)) ||
1075
(!active && (track->checked == 1)))
1077
if (active) track->checked = 1;
1078
else track->checked = 0;
1080
case TM_COLUMN_COMPILATION:
1081
if ((!active && (track->compilation == 0)) ||
1082
(active && (track->compilation == 1)))
1084
if (!active) track->compilation = 1;
1085
else track->compilation = 0;
1088
g_warning ("Programming error: tm_cell_toggled: unknown track cell (%d) edited\n", column);
1091
/* printf (" changed: %d\n", changed); */
1094
track->time_modified = itdb_time_get_mac_time ();
1095
/* pm_track_changed (track); notify playlist model... -- not
1096
* necessary here because only the track model is affected */
1097
data_changed (track->itdb); /* indicate that data has changed */
1099
/* If the changed column is the compilation flag update the file
1101
if (column == TM_COLUMN_COMPILATION)
1102
if (prefs_get_int("id3_write"))
1103
write_tags_to_file (track);
1106
while (widgets_blocked && gtk_events_pending ()) gtk_main_iteration ();
1109
if (multi_edit && (sel_rows_num > 1)) release_widgets ();
1111
g_list_foreach(row_list, (GFunc) gtk_tree_path_free, NULL);
1112
g_list_free(row_list);
1117
/* The track data is stored in a separate list (static GList *tracks)
1118
and only pointers to the corresponding Track structure are placed
1120
This function reads the data for the given cell from the list and
1121
passes it to the renderer. */
1122
static void tm_cell_data_func_toggle (GtkTreeViewColumn *tree_column,
1123
GtkCellRenderer *renderer,
1124
GtkTreeModel *model,
1131
column = (TM_item)g_object_get_data (G_OBJECT (renderer), "column");
1132
gtk_tree_model_get (model, iter, READOUT_COL, &track, -1);
1136
case TM_COLUMN_TITLE:
1137
g_object_set (G_OBJECT (renderer),
1138
"active", !track->checked,
1139
"activatable", TRUE, NULL);
1142
g_warning ("Programming error: unknown column in tm_cell_data_func_toggle: %d\n", column);
1148
* tm_get_nr_of_tracks - get the number of tracks displayed
1149
* currently in the track model Returns - the number of tracks displayed
1153
tm_get_nr_of_tracks(void)
1157
gboolean valid = FALSE;
1158
GtkTreeModel *tm = NULL;
1160
if((tm = gtk_tree_view_get_model(GTK_TREE_VIEW(track_treeview))))
1162
if((valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(tm),&i)))
1165
while((valid = gtk_tree_model_iter_next(tm,&i)))
1174
static gint comp_int (gconstpointer a, gconstpointer b)
1176
return (GPOINTER_TO_INT(a)-(GPOINTER_TO_INT(b)));
1181
* Reorder tracks in playlist to match order of tracks displayed in track
1182
* view. Only the subset of tracks currently displayed is reordered.
1183
* data_changed() is called when necessary.
1186
tm_rows_reordered (void)
1188
Playlist *current_pl;
1190
g_return_if_fail (track_treeview);
1191
current_pl = pm_get_selected_playlist ();
1195
GtkTreeModel *tm = NULL;
1197
GList *new_list = NULL, *old_pos_l = NULL;
1198
gboolean valid = FALSE;
1200
gboolean changed = FALSE;
1201
iTunesDB *itdb = NULL;
1203
tm = gtk_tree_view_get_model (track_treeview);
1204
g_return_if_fail (tm);
1206
valid = gtk_tree_model_get_iter_first (tm,&i);
1212
gtk_tree_model_get (tm, &i, READOUT_COL, &new_track, -1);
1213
g_return_if_fail (new_track);
1215
if (!itdb) itdb = new_track->itdb;
1216
new_list = g_list_append (new_list, new_track);
1217
/* what position was this track in before? */
1218
old_position = g_list_index (current_pl->members, new_track);
1219
/* check if we already used this position before (can
1220
happen if track has been added to playlist more than
1222
while ((old_position != -1) &&
1223
g_list_find (old_pos_l, GINT_TO_POINTER(old_position)))
1224
{ /* find next occurence */
1227
link = g_list_nth (current_pl->members, old_position + 1);
1228
next = g_list_index (link, new_track);
1229
if (next == -1) old_position = -1;
1230
else old_position += (1+next);
1232
/* we make a sorted list of the old positions */
1233
old_pos_l = g_list_insert_sorted (old_pos_l,
1234
GINT_TO_POINTER(old_position),
1236
valid = gtk_tree_model_iter_next (tm, &i);
1243
guint position = GPOINTER_TO_INT(olp->data);
1245
/* if position == -1 one of the tracks in the track view
1246
could not be found in the selected playlist -> stop! */
1249
g_warning ("Programming error: tm_rows_reordered_callback: track in track view was not in selected playlist\n");
1250
g_return_if_reached ();
1252
old_link = g_list_nth (current_pl->members, position);
1253
/* replace old track with new track */
1254
if (old_link->data != nlp->data)
1256
old_link->data = nlp->data;
1263
g_list_free (new_list);
1264
g_list_free (old_pos_l);
1265
/* if we changed data, mark data as changed and adopt order in
1269
data_changed (itdb);
1270
st_adopt_order_in_playlist ();
1277
on_trackids_list_foreach ( GtkTreeModel *tm, GtkTreePath *tp,
1278
GtkTreeIter *i, gpointer data)
1281
GList *l = *((GList**)data);
1282
gtk_tree_model_get(tm, i, READOUT_COL, &tr, -1);
1283
g_return_if_fail (tr);
1284
l = g_list_append(l, GUINT_TO_POINTER(tr->id));
1285
*((GList**)data) = l;
1289
/* return a list containing the track IDs of all tracks currently being
1292
tm_get_selected_trackids(void)
1294
GList *result = NULL;
1295
GtkTreeSelection *ts = NULL;
1297
if((ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(track_treeview))))
1299
gtk_tree_selection_selected_foreach(ts, on_trackids_list_foreach,
1305
/* return a list containing the track IDs of all tracks currently being
1308
tm_get_all_trackids(void)
1311
on_all_trackids_list_foreach (GtkTreeModel *tm, GtkTreePath *tp,
1312
GtkTreeIter *i, gpointer data)
1314
on_trackids_list_foreach (tm, tp, i, data);
1317
GList *result = NULL;
1318
GtkTreeModel *model;
1320
if((model = gtk_tree_view_get_model (track_treeview)))
1322
gtk_tree_model_foreach(model, on_all_trackids_list_foreach,
1329
on_tracks_list_foreach ( GtkTreeModel *tm, GtkTreePath *tp,
1330
GtkTreeIter *i, gpointer data)
1333
GList *l = *((GList**)data);
1334
gtk_tree_model_get(tm, i, READOUT_COL, &tr, -1);
1335
g_return_if_fail (tr);
1336
l = g_list_append(l, tr);
1337
*((GList**)data) = l;
1341
/* return a list containing pointers to all tracks currently being
1344
tm_get_selected_tracks(void)
1346
GList *result = NULL;
1347
GtkTreeSelection *ts = NULL;
1349
if((ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(track_treeview))))
1351
gtk_tree_selection_selected_foreach(ts, on_tracks_list_foreach,
1359
/* used by tm_get_all_tracks */
1360
static gboolean on_all_tracks_list_foreach (GtkTreeModel *tm,
1365
on_tracks_list_foreach (tm, tp, i, data);
1370
/* return a list containing pointers to all tracks currently being
1371
displayed. You must g_list_free() the list after use. */
1373
tm_get_all_tracks(void)
1375
GList *result = NULL;
1376
GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
1378
g_return_val_if_fail (model, NULL);
1380
gtk_tree_model_foreach(model, on_all_tracks_list_foreach,
1386
/* Stop editing. If @cancel is TRUE, the edited value will be
1387
discarded (I have the feeling that the "discarding" part does not
1388
work quite the way intended). */
1389
void tm_stop_editing (gboolean cancel)
1391
GtkTreeViewColumn *col;
1393
if (!track_treeview) return;
1395
gtk_tree_view_get_cursor (track_treeview, NULL, &col);
1398
/* Before removing the widget we set multi_edit to FALSE. That
1399
way at most one entry will be changed (this also doesn't
1400
seem to work the way intended) */
1401
gboolean me = prefs_get_int("multi_edit");
1402
prefs_set_int("multi_edit", FALSE);
1403
if (!cancel && col->editable_widget)
1404
gtk_cell_editable_editing_done (col->editable_widget);
1405
if (col->editable_widget)
1406
gtk_cell_editable_remove_widget (col->editable_widget);
1407
prefs_set_int("multi_edit", me);
1413
/* Function to compare @tm_item of @track1 and @track2. Used by
1414
tm_data_compare_func() */
1415
static gint tm_data_compare (Track *track1, Track *track2,
1419
ExtraTrackData *etr1, *etr2;
1422
g_return_val_if_fail (track1 && track2, 0);
1426
case TM_COLUMN_TITLE:
1427
case TM_COLUMN_ALBUM:
1428
case TM_COLUMN_GENRE:
1429
case TM_COLUMN_COMPOSER:
1430
case TM_COLUMN_COMMENT:
1431
case TM_COLUMN_FILETYPE:
1432
case TM_COLUMN_GROUPING:
1433
case TM_COLUMN_ARTIST:
1434
case TM_COLUMN_CATEGORY:
1435
case TM_COLUMN_DESCRIPTION:
1436
case TM_COLUMN_PODCASTURL:
1437
case TM_COLUMN_PODCASTRSS:
1438
case TM_COLUMN_SUBTITLE:
1439
/* string_compare_func is set to either compare_string_fuzzy or
1440
compare_string in tm_sort_column_changed() which is called
1441
once before the comparing begins. */
1442
cmp = string_compare_func (
1443
track_get_item (track1, TM_to_T (tm_item)),
1444
track_get_item (track2, TM_to_T (tm_item)));
1446
case TM_COLUMN_TRACK_NR:
1447
cmp = track1->tracks - track2->tracks;
1448
if (cmp == 0) cmp = track1->track_nr - track2->track_nr;
1450
case TM_COLUMN_CD_NR:
1451
cmp = track1->cds - track2->cds;
1452
if (cmp == 0) cmp = track1->cd_nr - track2->cd_nr;
1454
case TM_COLUMN_IPOD_ID:
1455
cmp = track1->id - track2->id;
1457
case TM_COLUMN_PC_PATH:
1458
etr1 = track1->userdata;
1459
etr2 = track2->userdata;
1460
g_return_val_if_fail (etr1 && etr2, 0);
1461
cmp = g_utf8_collate (etr1->pc_path_utf8, etr2->pc_path_utf8);
1463
case TM_COLUMN_IPOD_PATH:
1464
cmp = g_utf8_collate (track1->ipod_path, track2->ipod_path);
1466
case TM_COLUMN_THUMB_PATH:
1467
etr1 = track1->userdata;
1468
etr2 = track2->userdata;
1469
g_return_val_if_fail (etr1 && etr2, 0);
1470
cmp = g_utf8_collate (etr1->thumb_path_utf8, etr2->thumb_path_utf8);
1472
case TM_COLUMN_TRANSFERRED:
1473
if(track1->transferred == track2->transferred)
1475
else if(track1->transferred == TRUE)
1480
case TM_COLUMN_COMPILATION:
1481
if(track1->compilation == track2->compilation)
1483
else if(track1->compilation == TRUE)
1488
case TM_COLUMN_SIZE:
1489
cmp = track1->size - track2->size;
1491
case TM_COLUMN_TRACKLEN:
1492
cmp = track1->tracklen - track2->tracklen;
1494
case TM_COLUMN_BITRATE:
1495
cmp = track1->bitrate - track2->bitrate;
1497
case TM_COLUMN_SAMPLERATE:
1498
cmp = track1->samplerate - track2->samplerate;
1501
cmp = track1->BPM - track2->BPM;
1503
case TM_COLUMN_PLAYCOUNT:
1504
cmp = track1->playcount - track2->playcount;
1506
case TM_COLUMN_RATING:
1507
cmp = track1->rating - track2->rating;
1509
case TM_COLUMN_TIME_ADDED:
1510
case TM_COLUMN_TIME_PLAYED:
1511
case TM_COLUMN_TIME_MODIFIED:
1512
case TM_COLUMN_TIME_RELEASED:
1513
cmp = COMP (time_get_time (track1, TM_to_T (tm_item)),
1514
time_get_time (track2, TM_to_T (tm_item)));
1516
case TM_COLUMN_VOLUME:
1517
cmp = track1->volume - track2->volume;
1519
case TM_COLUMN_SOUNDCHECK:
1520
/* If soundcheck is unset (0) use 0 dB (1000) */
1521
cmp = (track1->soundcheck? track1->soundcheck:1000) -
1522
(track2->soundcheck? track2->soundcheck:1000);
1524
case TM_COLUMN_YEAR:
1525
cmp = track1->year - track2->year;
1528
g_warning ("Programming error: tm_data_compare_func: no sort method for tm_item %d\n", tm_item);
1532
/* implement stable sorting: if two items are the same, revert to
1533
the last relative positition */
1536
etr1 = track1->userdata;
1537
etr2 = track2->userdata;
1538
g_return_val_if_fail (etr1 && etr2, 0);
1539
cmp = etr1->sortindex - etr2->sortindex;
1545
/* Function used to compare rows with user's search string */
1546
gboolean tm_search_equal_func (GtkTreeModel *model,
1550
gpointer search_data)
1554
gtk_tree_model_get (model, iter, READOUT_COL, &track1, -1);
1557
case TM_COLUMN_TITLE:
1558
case TM_COLUMN_ALBUM:
1559
case TM_COLUMN_GENRE:
1560
case TM_COLUMN_COMPOSER:
1561
case TM_COLUMN_COMMENT:
1562
case TM_COLUMN_FILETYPE:
1563
case TM_COLUMN_GROUPING:
1564
case TM_COLUMN_ARTIST:
1565
case TM_COLUMN_CATEGORY:
1566
case TM_COLUMN_DESCRIPTION:
1567
case TM_COLUMN_PODCASTURL:
1568
case TM_COLUMN_PODCASTRSS:
1569
case TM_COLUMN_SUBTITLE:
1570
case TM_COLUMN_PC_PATH:
1571
case TM_COLUMN_YEAR:
1572
case TM_COLUMN_IPOD_PATH:
1573
case TM_COLUMN_COMPILATION:
1574
case TM_COLUMN_THUMB_PATH:
1575
cmp = (compare_string_start_case_insensitive (
1576
track_get_item (track1, TM_to_T (column)),
1579
case TM_COLUMN_TRACK_NR:
1580
case TM_COLUMN_IPOD_ID:
1581
case TM_COLUMN_TRANSFERRED:
1582
case TM_COLUMN_SIZE:
1583
case TM_COLUMN_TRACKLEN:
1584
case TM_COLUMN_BITRATE:
1585
case TM_COLUMN_PLAYCOUNT:
1586
case TM_COLUMN_RATING:
1587
case TM_COLUMN_TIME_PLAYED:
1588
case TM_COLUMN_TIME_MODIFIED:
1589
case TM_COLUMN_VOLUME:
1590
case TM_COLUMN_CD_NR:
1591
case TM_COLUMN_TIME_ADDED:
1592
case TM_COLUMN_SOUNDCHECK:
1593
case TM_COLUMN_SAMPLERATE:
1595
case TM_COLUMN_TIME_RELEASED:
1596
case TM_NUM_COLUMNS:
1597
g_warning ("Programming error: tm_search_equal_func: no sort method for column %d\n", column);
1603
/* Function used to compare two cells during sorting (track view) */
1604
gint tm_data_compare_func (GtkTreeModel *model,
1615
gtk_tree_model_get (model, a, READOUT_COL, &track1, -1);
1616
gtk_tree_model_get (model, b, READOUT_COL, &track2, -1);
1617
if(gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model),
1618
&column, &order) == FALSE)
1621
result = tm_data_compare (track1, track2, column);
1626
/* set/read the counter used to remember how often the sort column has
1628
@inc: negative: reset counter to 0
1629
@inc: positive or zero : add to counter
1630
return value: new value of the counter */
1631
gint tm_sort_counter (gint inc)
1633
static gint cnt = 0;
1647
/* Redisplays the tracks in the track view according to the order
1648
* stored in the sort tab view. This only works if the track view is
1649
* not sorted --> skip if sorted */
1651
void tm_adopt_order_in_sorttab (void)
1653
if (prefs_get_int("tm_sort_") == SORT_NONE)
1655
GList *gl, *tracks = NULL;
1657
/* retrieve the currently displayed tracks (non ordered) from
1658
the last sort tab or from the selected playlist if no sort
1659
tabs are being used */
1660
tm_remove_all_tracks ();
1661
tracks = display_get_selected_members (prefs_get_int("sort_tab_num")-1);
1662
for (gl=tracks; gl; gl=gl->next)
1663
tm_add_track_to_track_model ((Track *)gl->data, NULL);
1668
/* redisplay the contents of the track view in it's unsorted order */
1669
static void tm_unsort (void)
1673
GtkTreeModel *model= gtk_tree_view_get_model (track_treeview);
1675
prefs_set_int("tm_sort_", SORT_NONE);
1676
if (!BROKEN_GTK_TREE_SORT)
1678
/* no need to comment this out -- searching still works, but for lack
1679
of a ctrl-g only the first occurence will be found */
1680
/* gtk_tree_view_set_enable_search (GTK_TREE_VIEW
1681
* (track_treeview), FALSE);*/
1682
gtk_tree_sortable_set_sort_column_id
1683
(GTK_TREE_SORTABLE (model),
1684
GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
1685
GTK_SORT_ASCENDING);
1686
tm_adopt_order_in_sorttab ();
1690
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);
1692
tm_sort_counter (-1);
1697
static void tm_set_search_column (gint newcol)
1699
/* printf ("track_treeview: %p, col: %d\n", track_treeview, newcol); */
1700
g_return_if_fail (track_treeview);
1702
gtk_tree_view_set_search_column (GTK_TREE_VIEW (track_treeview),
1706
case TM_COLUMN_TITLE:
1707
case TM_COLUMN_ALBUM:
1708
case TM_COLUMN_GENRE:
1709
case TM_COLUMN_COMPOSER:
1710
case TM_COLUMN_COMMENT:
1711
case TM_COLUMN_FILETYPE:
1712
case TM_COLUMN_GROUPING:
1713
case TM_COLUMN_ARTIST:
1714
case TM_COLUMN_CATEGORY:
1715
case TM_COLUMN_DESCRIPTION:
1716
case TM_COLUMN_PODCASTURL:
1717
case TM_COLUMN_PODCASTRSS:
1718
case TM_COLUMN_SUBTITLE:
1719
case TM_COLUMN_PC_PATH:
1720
case TM_COLUMN_YEAR:
1721
case TM_COLUMN_IPOD_PATH:
1722
case TM_COLUMN_COMPILATION:
1723
case TM_COLUMN_THUMB_PATH:
1724
gtk_tree_view_set_enable_search (GTK_TREE_VIEW (track_treeview), TRUE);
1726
case TM_COLUMN_TRACK_NR:
1727
case TM_COLUMN_IPOD_ID:
1728
case TM_COLUMN_TRANSFERRED:
1729
case TM_COLUMN_SIZE:
1730
case TM_COLUMN_TRACKLEN:
1731
case TM_COLUMN_BITRATE:
1732
case TM_COLUMN_PLAYCOUNT:
1733
case TM_COLUMN_RATING:
1734
case TM_COLUMN_TIME_PLAYED:
1735
case TM_COLUMN_TIME_MODIFIED:
1736
case TM_COLUMN_VOLUME:
1737
case TM_COLUMN_CD_NR:
1738
case TM_COLUMN_TIME_ADDED:
1739
case TM_COLUMN_SOUNDCHECK:
1740
case TM_COLUMN_SAMPLERATE:
1742
case TM_COLUMN_TIME_RELEASED:
1743
case TM_NUM_COLUMNS:
1744
gtk_tree_view_set_enable_search (GTK_TREE_VIEW (track_treeview), FALSE);
1747
prefs_set_int (TM_PREFS_SEARCH_COLUMN, newcol);
1751
/* This is called before when changing the sort order or the sort
1752
column, and before doing the sorting */
1753
static void tm_sort_column_changed (GtkTreeSortable *ts,
1756
static gint lastcol = -1; /* which column was sorted last time? */
1763
gtk_tree_sortable_get_sort_column_id (ts, &newcol, &order);
1765
/* printf ("scc -- col: %d, order: %d\n", newcol, order); */
1767
/* set compare function for strings (to speed up sorting) */
1768
buf = g_strdup_printf ("sort_ign_field_%d", TM_to_T (newcol));
1769
if (prefs_get_int (buf))
1770
string_compare_func = compare_string_fuzzy;
1772
string_compare_func = compare_string;
1775
/* don't do anything if no sort column is set */
1782
if (newcol != lastcol)
1784
tm_sort_counter (-1);
1788
if (tm_sort_counter (1) >= 3)
1789
{ /* after clicking three times, reset sort order! */
1790
tm_unsort (); /* also resets sort counter */
1794
prefs_set_int("tm_sort_", order);
1796
prefs_set_int("tm_sortcol", newcol);
1798
tm_set_search_column (newcol);
1800
if(prefs_get_int("tm_autostore")) tm_rows_reordered ();
1801
sort_window_update ();
1803
/* stable sorting: index original order */
1804
tracks = tm_get_all_tracks ();
1805
/* make numbering ascending or decending depending on sort order
1806
(then we don't have to worry about the sort order when doing
1807
the comparison in tm_data_compare_func() */
1808
if (order == GTK_SORT_ASCENDING)
1818
for (gl=tracks; gl; gl=gl->next)
1820
ExtraTrackData *etr;
1821
Track *tr = gl->data;
1822
g_return_if_fail (tr);
1824
g_return_if_fail (etr);
1828
g_list_free (tracks);
1832
void tm_sort (TM_item col, GtkSortType order)
1836
GtkTreeModel *model= gtk_tree_view_get_model (track_treeview);
1837
if (order != SORT_NONE)
1839
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
1843
{ /* only unsort if treeview is sorted */
1846
if (gtk_tree_sortable_get_sort_column_id
1847
(GTK_TREE_SORTABLE (model), &column, &order))
1849
/* column == -2 actually is not defined, but it means
1850
that the model is unsorted. The sortable interface
1851
is badly implemented in gtk 2.4 */
1863
/* Adds the columns to our track_treeview */
1864
static GtkTreeViewColumn *tm_add_column (TM_item tm_item, gint pos)
1866
GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
1867
GtkTreeViewColumn *col = NULL;
1869
GtkCellRenderer *renderer = NULL; /* default */
1872
g_return_val_if_fail (gtkpod_window, NULL);
1873
tt = g_object_get_data (G_OBJECT (gtkpod_window), "main_tooltips");
1874
g_return_val_if_fail (tt, NULL);
1876
g_return_val_if_fail (tm_item >= 0, NULL);
1877
g_return_val_if_fail (tm_item < TM_NUM_COLUMNS, NULL);
1879
text = gettext (get_tm_string (tm_item));
1881
g_return_val_if_fail (text, NULL);
1883
col = gtk_tree_view_column_new ();
1887
case TM_COLUMN_TITLE:
1888
/* Add additional toggle box for 'checked' property */
1889
renderer = gtk_cell_renderer_toggle_new ();
1890
g_object_set_data (G_OBJECT (renderer), "column",
1892
g_signal_connect (G_OBJECT (renderer), "toggled",
1893
G_CALLBACK (tm_cell_toggled), model);
1894
gtk_tree_view_column_pack_start (col, renderer, FALSE);
1895
gtk_tree_view_column_set_cell_data_func (col, renderer,
1896
tm_cell_data_func_toggle,
1900
case TM_COLUMN_ARTIST:
1901
case TM_COLUMN_ALBUM:
1902
case TM_COLUMN_GENRE:
1903
case TM_COLUMN_COMPOSER:
1904
case TM_COLUMN_COMMENT:
1905
case TM_COLUMN_FILETYPE:
1906
case TM_COLUMN_GROUPING:
1907
case TM_COLUMN_BITRATE:
1908
case TM_COLUMN_SAMPLERATE:
1910
case TM_COLUMN_CATEGORY:
1911
case TM_COLUMN_DESCRIPTION:
1912
case TM_COLUMN_PODCASTURL:
1913
case TM_COLUMN_PODCASTRSS:
1914
case TM_COLUMN_SUBTITLE:
1915
case TM_COLUMN_PC_PATH:
1916
case TM_COLUMN_IPOD_PATH:
1917
case TM_COLUMN_THUMB_PATH:
1918
case TM_COLUMN_SIZE:
1920
/* for some column names we want to use shorter alternatives to
1922
case TM_COLUMN_RATING:
1925
case TM_COLUMN_TRACK_NR:
1928
case TM_COLUMN_CD_NR:
1931
case TM_COLUMN_IPOD_ID:
1934
case TM_COLUMN_TRANSFERRED:
1935
text = _("Trnsfrd");
1936
renderer = gtk_cell_renderer_toggle_new ();
1938
case TM_COLUMN_COMPILATION:
1940
renderer = gtk_cell_renderer_toggle_new ();
1941
g_signal_connect (G_OBJECT (renderer), "toggled",
1942
G_CALLBACK (tm_cell_toggled), model);
1944
case TM_COLUMN_TRACKLEN:
1947
case TM_COLUMN_PLAYCOUNT:
1950
case TM_COLUMN_TIME_PLAYED:
1953
case TM_COLUMN_TIME_MODIFIED:
1954
text = _("Modified");
1956
case TM_COLUMN_TIME_ADDED:
1959
case TM_COLUMN_TIME_RELEASED:
1960
text = _("Released");
1962
case TM_COLUMN_YEAR:
1965
case TM_COLUMN_VOLUME:
1968
case TM_COLUMN_SOUNDCHECK:
1969
text = _("Sndchk.");
1971
case TM_NUM_COLUMNS:
1972
g_return_val_if_reached (NULL);
1977
{ /* text renderer -- editable/not editable is done in
1978
tm_cell_data_func() */
1979
renderer = gtk_cell_renderer_text_new ();
1980
g_signal_connect (G_OBJECT (renderer), "edited",
1981
G_CALLBACK (tm_cell_edited), model);
1984
g_object_set_data (G_OBJECT (renderer), "column",
1987
gtk_tree_view_column_set_title (col, text);
1988
gtk_tree_view_column_pack_start (col, renderer, FALSE);
1989
gtk_tree_view_column_set_cell_data_func (col, renderer,
1990
tm_cell_data_func, NULL, NULL);
1991
gtk_tree_view_column_set_sort_column_id (col, tm_item);
1992
gtk_tree_view_column_set_resizable (col, TRUE);
1993
/* gtk_tree_view_column_set_clickable(column, TRUE); */
1994
gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
1995
gtk_tree_view_column_set_fixed_width (col,
1996
prefs_get_int_index("tm_col_width", tm_item));
1997
gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (model), tm_item,
1998
tm_data_compare_func, NULL, NULL);
1999
gtk_tree_view_column_set_reorderable (col, TRUE);
2000
gtk_tree_view_insert_column (track_treeview, col, pos);
2001
tm_columns[tm_item] = col;
2005
gtk_tree_view_column_set_visible (col,
2006
prefs_get_int_index("col_visible", tm_item));
2008
if (get_tm_tooltip (tm_item))
2009
gtk_tooltips_set_tip (tt, col->button,
2010
gettext (get_tm_tooltip (tm_item)),
2016
/* Adds the columns to our track_treeview */
2017
static void tm_add_columns (void)
2021
for (i=0; i<TM_NUM_COLUMNS; ++i)
2023
tm_add_column (prefs_get_int_index("col_order", i), -1);
2025
tm_show_preferred_columns();
2028
static void tm_select_current_position (gint x, gint y)
2034
gtk_tree_view_get_path_at_pos (track_treeview,
2035
x, y, &path, NULL, NULL, NULL);
2038
GtkTreeSelection *ts = gtk_tree_view_get_selection (track_treeview);
2039
gtk_tree_selection_select_path (ts, path);
2040
gtk_tree_path_free (path);
2046
tm_button_press_event(GtkWidget *w, GdkEventButton *e, gpointer data)
2054
printf ("Pressed in cell %d (%f/%f)\n\n",
2055
tree_view_get_cell_from_pos(GTK_TREE_VIEW(w),
2056
(guint)e->x, (guint)e->y, NULL),
2061
tm_select_current_position (e->x, e->y);
2062
tm_context_menu_init ();
2071
/* called when the track selection changes */
2073
tm_selection_changed_event(GtkTreeSelection *selection, gpointer data)
2075
GtkTreeView *treeview = gtk_tree_selection_get_tree_view (selection);
2077
GtkTreeViewColumn *column;
2080
gtk_tree_view_get_cursor (treeview, &path, &column);
2083
col_id = tm_lookup_col_id (column);
2084
if (col_id != -1) tm_set_search_column (col_id);
2086
info_update_track_view_selected ();
2090
/* Create tracks treeview */
2091
void tm_create_treeview (void)
2093
GtkTreeModel *model = NULL;
2094
GtkWidget *track_window = gtkpod_xml_get_widget (main_window_xml, "track_window");
2095
GtkTreeSelection *select;
2097
GtkWidget *stv = gtk_tree_view_new ();
2099
/* create tree view */
2101
{ /* delete old tree view */
2102
model = gtk_tree_view_get_model (track_treeview);
2103
/* FIXME: how to delete model? */
2104
gtk_widget_destroy (GTK_WIDGET (track_treeview));
2106
track_treeview = GTK_TREE_VIEW (stv);
2107
gtk_widget_show (stv);
2108
gtk_container_add (GTK_CONTAINER (track_window), stv);
2109
/* create model (we only need one column for the model -- only a
2110
* pointer to the track has to be stored) */
2111
model = GTK_TREE_MODEL (
2112
gtk_list_store_new (1, G_TYPE_POINTER));
2113
gtk_tree_view_set_model (track_treeview, GTK_TREE_MODEL (model));
2114
gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (track_treeview), TRUE);
2115
gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (track_treeview),
2116
tm_search_equal_func,
2119
select = gtk_tree_view_get_selection (track_treeview);
2120
gtk_tree_selection_set_mode (select,
2121
GTK_SELECTION_MULTIPLE);
2122
g_signal_connect (G_OBJECT (select) , "changed",
2123
G_CALLBACK (tm_selection_changed_event),
2127
/* gtk_drag_source_set (GTK_WIDGET (track_treeview), GDK_BUTTON1_MASK, */
2128
/* tm_drag_types, TGNR (tm_drag_types), */
2129
/* GDK_ACTION_COPY|GDK_ACTION_MOVE); */
2130
/* gtk_tree_view_enable_model_drag_dest(track_treeview, tm_drop_types, */
2131
/* TGNR (tm_drop_types), */
2132
/* GDK_ACTION_COPY|GDK_ACTION_MOVE); */
2133
/* /\* need the gtk_drag_dest_set() with no actions ("0") so that the */
2134
/* data_received callback gets the correct info value. This is most */
2135
/* likely a bug... *\/ */
2136
/* gtk_drag_dest_set_target_list (GTK_WIDGET (track_treeview), */
2137
/* gtk_target_list_new (tm_drop_types, */
2138
/* TGNR (tm_drop_types))); */
2141
gtk_drag_source_set (GTK_WIDGET (track_treeview),
2143
tm_drag_types, TGNR (tm_drag_types),
2144
GDK_ACTION_COPY|GDK_ACTION_MOVE);
2145
gtk_drag_dest_set (GTK_WIDGET (track_treeview),
2147
tm_drop_types, TGNR (tm_drop_types),
2148
GDK_ACTION_COPY|GDK_ACTION_MOVE);
2151
g_signal_connect ((gpointer) track_treeview, "drag-begin",
2152
G_CALLBACK (tm_drag_begin),
2155
g_signal_connect ((gpointer) track_treeview, "drag-data-delete",
2156
G_CALLBACK (tm_drag_data_delete),
2159
g_signal_connect ((gpointer) track_treeview, "drag-data-get",
2160
G_CALLBACK (tm_drag_data_get),
2163
g_signal_connect ((gpointer) track_treeview, "drag-data-received",
2164
G_CALLBACK (tm_drag_data_received),
2167
g_signal_connect ((gpointer) track_treeview, "drag-drop",
2168
G_CALLBACK (tm_drag_drop),
2171
g_signal_connect ((gpointer) track_treeview, "drag-end",
2172
G_CALLBACK (tm_drag_end),
2175
g_signal_connect ((gpointer) track_treeview, "drag-leave",
2176
G_CALLBACK (tm_drag_leave),
2179
g_signal_connect ((gpointer) track_treeview, "drag-motion",
2180
G_CALLBACK (tm_drag_motion),
2183
g_signal_connect_after ((gpointer) stv, "key_release_event",
2184
G_CALLBACK (on_track_treeview_key_release_event),
2186
g_signal_connect ((gpointer) track_treeview, "button-press-event",
2187
G_CALLBACK (tm_button_press_event),
2189
g_signal_connect (G_OBJECT (model), "sort-column-changed",
2190
G_CALLBACK (tm_sort_column_changed),
2193
/* initialize sorting */
2194
tm_sort (prefs_get_int("tm_sortcol"), prefs_get_int("tm_sort_"));
2195
/* set correct column for typeahead */
2196
if (prefs_get_int_value (TM_PREFS_SEARCH_COLUMN, &col))
2198
tm_set_search_column (col);
2201
{ /* reasonable default */
2202
tm_set_search_column (TM_COLUMN_TITLE);
2208
tm_show_preferred_columns(void)
2210
GtkTreeViewColumn *tvc = NULL;
2214
for (i=0; i<TM_NUM_COLUMNS; ++i)
2216
tvc = gtk_tree_view_get_column (track_treeview, i);
2217
visible = prefs_get_int_index("col_visible",
2218
prefs_get_int_index("col_order", i));
2219
gtk_tree_view_column_set_visible (tvc, visible);
2224
/* update the cfg structure (preferences) with the current sizes /
2225
positions (called by display_update_default_sizes():
2226
column widths of track model */
2227
void tm_update_default_sizes (void)
2230
GtkTreeViewColumn *col;
2234
for (i=0; i<TM_NUM_COLUMNS; ++i)
2236
col = tm_columns [i];
2239
col_width = gtk_tree_view_column_get_width (col);
2242
prefs_set_int_index ("tm_col_width", i, col_width);
2244
prefs_set_int_index ("tm_col_width", i, 80);
2251
/* get the TM_ITEM column id for @column. Returns -1 if column could
2253
static TM_item tm_lookup_col_id (GtkTreeViewColumn *column)
2259
for (i=0; i<TM_NUM_COLUMNS; ++i)
2261
if (column == tm_columns[i]) return i;
2268
/* Compare function to avoid sorting */
2269
static gint tm_nosort_comp (GtkTreeModel *model,
2278
/* Disable sorting of the view during lengthy updates. */
2279
/* @enable: TRUE: enable, FALSE: disable */
2280
void tm_enable_disable_view_sort (gboolean enable)
2282
static gint disable_count = 0;
2287
if (disable_count < 0)
2288
fprintf (stderr, "Programming error: disable_count < 0\n");
2289
if (disable_count == 0 && track_treeview)
2291
if ((prefs_get_int("tm_sort_") != SORT_NONE) &&
2294
/* Re-enable sorting */
2295
GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
2296
if (BROKEN_GTK_TREE_SORT)
2298
gtk_tree_sortable_set_sort_func (
2299
GTK_TREE_SORTABLE (model),
2300
prefs_get_int("tm_sortcol"),
2301
tm_data_compare_func, NULL, NULL);
2305
gtk_tree_sortable_set_sort_column_id (
2306
GTK_TREE_SORTABLE (model),
2307
prefs_get_int("tm_sortcol"),
2308
prefs_get_int("tm_sort_"));
2315
if (disable_count == 0 && track_treeview)
2317
if ((prefs_get_int("tm_sort_") != SORT_NONE) &&
2318
sorting_disabled ())
2320
/* Disable sorting */
2321
GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
2322
if (BROKEN_GTK_TREE_SORT)
2324
gtk_tree_sortable_set_sort_func (
2325
GTK_TREE_SORTABLE (model),
2326
prefs_get_int("tm_sortcol"),
2327
tm_nosort_comp, NULL, NULL);
2331
gtk_tree_sortable_set_sort_column_id (
2332
GTK_TREE_SORTABLE (model),
2333
GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
2334
prefs_get_int("tm_sort_"));
2344
/* Callback for adding tracks within tm_add_filelist */
2345
void tm_addtrackfunc (Playlist *plitem, Track *track, gpointer data)
2347
struct asf_data *asf = (struct asf_data *)data;
2348
GtkTreeModel *model;
2349
GtkTreeIter new_iter;
2351
model = gtk_tree_view_get_model (track_treeview);
2353
/* printf("plitem: %p\n", plitem);
2354
if (plitem) printf("plitem->type: %d\n", plitem->type);*/
2355
/* add to playlist but not to the display */
2356
gp_playlist_add_track (plitem, track, FALSE);
2358
/* create new iter in track view */
2361
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
2362
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
2363
case GTK_TREE_VIEW_DROP_AFTER:
2364
gtk_list_store_insert_after (GTK_LIST_STORE (model),
2365
&new_iter, asf->to_iter);
2367
case GTK_TREE_VIEW_DROP_BEFORE:
2368
gtk_list_store_insert_before (GTK_LIST_STORE (model),
2369
&new_iter, asf->to_iter);
2373
tm_add_track_to_track_model (track, &new_iter);
2377
/* DND: insert a list of files before/after @path
2378
@data: list of files
2379
@path: where to drop (NULL to drop at the end)
2380
@pos: before/after... (ignored if @path is NULL)
2382
gboolean tm_add_filelist (gchar *data,
2384
GtkTreeViewDropPosition pos)
2386
GtkTreeModel *model;
2387
gchar *buf = NULL, *use_data;
2388
gchar **files, **filep;
2389
Playlist *current_playlist = pm_get_selected_playlist ();
2391
g_return_val_if_fail (data, FALSE);
2392
g_return_val_if_fail (*data, FALSE);
2393
g_return_val_if_fail (current_playlist, FALSE);
2395
model = gtk_tree_view_get_model (track_treeview);
2396
g_return_val_if_fail (model, FALSE);
2401
if (pos != GTK_TREE_VIEW_DROP_BEFORE)
2402
{ /* need to reverse the list of files -- otherwise wie add them
2403
* in reverse order */
2404
/* split the path list into individual strings */
2405
gint len = strlen (data) + 1;
2406
files = g_strsplit (data, "\n", -1);
2408
/* find the end of the list */
2409
while (*filep) ++filep;
2410
/* reserve memory */
2411
buf = g_malloc0 (len);
2412
/* reverse the list */
2413
while (filep != files)
2416
g_strlcat (buf, *filep, len);
2417
g_strlcat (buf, "\n", len);
2427
/* printf("filelist: (%s) -> (%s)\n", data, use_data); */
2428
/* initialize add-track-struct */
2430
{ /* add where specified (@path/@pos) */
2431
GtkTreeIter to_iter;
2432
struct asf_data asf;
2433
if (!gtk_tree_model_get_iter (model, &to_iter, path))
2434
g_return_val_if_reached (FALSE);
2435
asf.to_iter = &to_iter;
2437
add_text_plain_to_playlist (current_playlist->itdb,
2438
current_playlist, use_data, 0,
2439
tm_addtrackfunc, &asf);
2442
{ /* add to the end */
2443
add_text_plain_to_playlist (current_playlist->itdb,
2444
current_playlist, use_data, 0,
2447
tm_rows_reordered ();