2
| Copyright (C) 2002-2007 Jorg Schuler <jcsjcs at users sourceforge net>
3
| Part of the gtkpod project.
5
| URL: http://www.gtkpod.org/
6
| URL: http://gtkpod.sourceforge.net/
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.
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.
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
22
| iTunes and iPod are trademarks of Apple
24
| This product is not supported/written/published by Apple!
26
| $Id: display_tracks.c 1589 2007-06-25 14:23:59Z jcsjcs $
33
#include <gdk/gdkkeysyms.h>
40
#include <sys/types.h>
44
#include "display_private.h"
45
#include "display_itdb.h"
49
#include "misc_track.h"
51
#include "context_menus.h"
53
/* pointer to the treeview for the track display */
54
static GtkTreeView *track_treeview = NULL;
55
/* array with pointers to the columns used in the track display */
56
static GtkTreeViewColumn *tm_columns[TM_NUM_COLUMNS];
57
/* column in which track pointer is stored */
58
static const gint READOUT_COL = 0;
60
/* compare function to be used for string comparisons */
61
static gint (*string_compare_func) (const gchar *str1, const gchar *str2) = compare_string;
63
static GtkTreeViewColumn *tm_add_column (TM_item tm_item, gint position);
64
static TM_item tm_lookup_col_id (GtkTreeViewColumn *column);
66
/* Drag and drop definitions */
67
static GtkTargetEntry tm_drag_types [] = {
68
{ DND_GTKPOD_TM_PATHLIST_TYPE, 0, DND_GTKPOD_TM_PATHLIST },
69
{ DND_GTKPOD_TRACKLIST_TYPE, 0, DND_GTKPOD_TRACKLIST },
70
{ "text/uri-list", 0, DND_TEXT_URI_LIST },
71
{ "text/plain", 0, DND_TEXT_PLAIN },
72
{ "STRING", 0, DND_TEXT_PLAIN }
74
static GtkTargetEntry tm_drop_types [] = {
75
{ DND_GTKPOD_TM_PATHLIST_TYPE, 0, DND_GTKPOD_TM_PATHLIST },
76
{ "text/uri-list", 0, DND_TEXT_URI_LIST },
77
{ "text/plain", 0, DND_TEXT_PLAIN },
78
{ "STRING", 0, DND_TEXT_PLAIN }
82
const gchar *TM_PREFS_SEARCH_COLUMN = "tm_prefs_search_column";
85
/* ---------------------------------------------------------------- */
86
/* Section for track display */
87
/* DND -- Drag And Drop */
88
/* ---------------------------------------------------------------- */
91
/* Move the paths listed in @data before or after (according to @pos)
92
@path. Used for DND */
93
static gboolean tm_move_pathlist (gchar *data,
95
GtkTreeViewDropPosition pos)
98
GtkTreeIter *from_iter;
100
GList *iterlist = NULL;
102
gchar **paths, **pathp;
104
g_return_val_if_fail (data, FALSE);
105
g_return_val_if_fail (*data, FALSE);
107
model = gtk_tree_view_get_model (track_treeview);
108
g_return_val_if_fail (model, FALSE);
110
g_return_val_if_fail (gtk_tree_model_get_iter (model, &to_iter, path),
113
/* split the path list into individual strings */
114
paths = g_strsplit (data, "\n", -1);
116
/* Convert the list of paths into a list of iters */
119
from_iter = g_malloc (sizeof (GtkTreeIter));
120
if ((strlen (*pathp) > 0) &&
121
gtk_tree_model_get_iter_from_string (model, from_iter, *pathp))
123
iterlist = g_list_append (iterlist, from_iter);
128
/* Move the iters in iterlist before or after @to_iter */
131
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
132
case GTK_TREE_VIEW_DROP_AFTER:
133
for (link = g_list_last (iterlist); link; link = link->prev)
135
from_iter = (GtkTreeIter *)link->data;
136
gtk_list_store_move_after (GTK_LIST_STORE (model),
137
from_iter, &to_iter);
140
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
141
case GTK_TREE_VIEW_DROP_BEFORE:
142
for (link = g_list_first (iterlist); link; link = link->next)
144
from_iter = (GtkTreeIter *)link->data;
145
gtk_list_store_move_before (GTK_LIST_STORE (model),
146
from_iter, &to_iter);
152
for (link = iterlist; link; link = link->next)
154
g_list_free (iterlist);
156
tm_rows_reordered ();
162
* utility function for appending ipod track ids for track view (DND)
165
on_tm_dnd_get_track_foreach(GtkTreeModel *tm, GtkTreePath *tp,
166
GtkTreeIter *i, gpointer data)
169
GString *tracklist = (GString *)data;
171
g_return_if_fail (tracklist);
173
gtk_tree_model_get(tm, i, READOUT_COL, &tr, -1);
174
g_return_if_fail (tr);
176
g_string_append_printf (tracklist, "%p\n", tr);
181
* utility function for appending path for track view (DND)
184
on_tm_dnd_get_path_foreach(GtkTreeModel *tm, GtkTreePath *tp,
185
GtkTreeIter *iter, gpointer data)
187
GString *filelist = (GString *)data;
188
gchar *ps = gtk_tree_path_to_string (tp);
189
g_string_append_printf (filelist, "%s\n", ps);
194
* utility function for appending file for track view (DND)
197
on_tm_dnd_get_file_foreach(GtkTreeModel *tm, GtkTreePath *tp,
198
GtkTreeIter *iter, gpointer data)
201
GString *filelist = (GString *)data;
204
gtk_tree_model_get(tm, iter, READOUT_COL, &track, -1);
205
name = get_file_name_from_source (track, SOURCE_PREFER_LOCAL);
208
g_string_append_printf (filelist, "file:%s\n", name);
214
* utility function for appending file-uri for track view (DND)
217
on_tm_dnd_get_uri_foreach(GtkTreeModel *tm, GtkTreePath *tp,
218
GtkTreeIter *iter, gpointer data)
221
GString *filelist = (GString *)data;
224
gtk_tree_model_get(tm, iter, READOUT_COL, &track, -1);
225
name = get_file_name_from_source (track, SOURCE_PREFER_LOCAL);
228
gchar *uri = g_filename_to_uri (name, NULL, NULL);
231
g_string_append_printf (filelist, "%s\n", uri);
238
static void tm_drag_begin (GtkWidget *widget,
242
tm_stop_editing (TRUE);
246
/* remove dragged playlist after successful MOVE */
247
static void tm_drag_data_delete (GtkWidget *widget,
251
GtkTreeSelection *ts;
252
Playlist *pl = pm_get_selected_playlist ();
255
/* puts ("tm_drag_data_delete"); */
257
g_return_if_fail (widget);
258
ts = gtk_tree_view_get_selection (GTK_TREE_VIEW(widget));
259
g_return_if_fail (ts);
260
/* number of selected tracks */
261
num = gtk_tree_selection_count_selected_rows (ts);
262
if (num == 0) return;
264
/* Check if we really have to delete the tracks */
265
if (!itdb_playlist_is_mpl (pl))
266
{ /* get list of selected tracks */
267
GString *reply = g_string_sized_new (2000);
271
gtk_tree_selection_selected_foreach(ts,
272
on_tm_dnd_get_track_foreach,
275
while(parse_tracks_from_string(&str, &track))
277
gp_playlist_remove_track (pl, track, DELETE_ACTION_PLAYLIST);
279
g_string_free (reply, TRUE);
281
gtkpod_statusbar_message (ngettext ("Moved one track",
282
"Moved %d tracks", num), num);
286
gtkpod_statusbar_message (ngettext ("Copied one track",
287
"Copied %d tracks", num), num);
293
static void tm_drag_end (GtkWidget *widget,
297
/* puts ("tm_drag_end"); */
298
display_remove_autoscroll_row_timeout (widget);
299
gtkpod_tracks_statusbar_update ();
303
static gboolean tm_drag_drop (GtkWidget *widget,
312
/* puts ("tm_drag_data_drop"); */
314
display_remove_autoscroll_row_timeout (widget);
316
target = gtk_drag_dest_find_target (widget, dc, NULL);
318
if (target != GDK_NONE)
320
gtk_drag_get_data (widget, dc, target, time);
326
static void tm_drag_leave (GtkWidget *widget,
331
/* puts ("tm_drag_leave"); */
332
display_remove_autoscroll_row_timeout (widget);
337
static gboolean tm_drag_motion (GtkWidget *widget,
344
GtkTreeView *treeview;
346
GtkTreePath *path = NULL;
347
GtkTreeViewDropPosition pos;
349
ExtraiTunesDBData *eitdb;
351
/* printf ("drag_motion suggested: %d actions: %d\n", */
352
/* dc->suggested_action, dc->actions); */
354
/* printf ("x: %d y: %d\n", x, y); */
356
g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
358
treeview = GTK_TREE_VIEW (widget);
360
display_install_autoscroll_row_timeout (widget);
362
itdb = gp_get_selected_itdb ();
363
/* no drop is possible if no playlist/repository is selected */
366
gdk_drag_status (dc, 0, time);
369
eitdb = itdb->userdata;
370
g_return_val_if_fail (eitdb, FALSE);
371
/* no drop is possible if no repository is loaded */
372
if (!eitdb->itdb_imported)
374
gdk_drag_status (dc, 0, time);
378
/* optically set destination row if available */
379
if (gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
382
/* drops are only allowed before and after -- not onto
386
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
387
case GTK_TREE_VIEW_DROP_AFTER:
388
gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), path,
389
GTK_TREE_VIEW_DROP_AFTER);
391
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
392
case GTK_TREE_VIEW_DROP_BEFORE:
393
gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), path,
394
GTK_TREE_VIEW_DROP_BEFORE);
398
gtk_tree_path_free (path);
403
path = gtk_tree_path_new_first ();
404
gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), path,
405
GTK_TREE_VIEW_DROP_BEFORE);
406
gtk_tree_path_free (path);
410
target = gtk_drag_dest_find_target (widget, dc, NULL);
412
/* no drop possible if no valid target can be found */
413
if (target == GDK_NONE)
415
gdk_drag_status (dc, 0, time);
419
if (widget == gtk_drag_get_source_widget (dc))
420
{ /* drag is within the same widget */
423
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
424
g_return_val_if_fail (model, FALSE);
425
if(gtk_tree_sortable_get_sort_column_id (
426
GTK_TREE_SORTABLE (model), &column, &order))
427
{ /* don't allow move because the model is sorted */
428
gdk_drag_status (dc, 0, time);
432
{ /* only allow moves within the same widget */
433
gdk_drag_status (dc, GDK_ACTION_MOVE, time);
437
{ /* whatever the source suggests */
438
gdk_drag_status (dc, dc->suggested_action, time);
445
static void tm_drag_data_get (GtkWidget *widget,
446
GdkDragContext *context,
447
GtkSelectionData *data,
452
GtkTreeSelection *ts = NULL;
453
GString *reply = g_string_sized_new (2000);
455
/* printf("tm drag get info: %d\n", info); */
456
if((data) && (ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget))))
460
case DND_GTKPOD_TRACKLIST:
461
gtk_tree_selection_selected_foreach(ts,
462
on_tm_dnd_get_track_foreach, reply);
464
case DND_GTKPOD_TM_PATHLIST:
465
gtk_tree_selection_selected_foreach(ts,
466
on_tm_dnd_get_path_foreach, reply);
468
case DND_TEXT_URI_LIST:
469
gtk_tree_selection_selected_foreach(ts,
470
on_tm_dnd_get_uri_foreach, reply);
473
gtk_tree_selection_selected_foreach(ts,
474
on_tm_dnd_get_file_foreach, reply);
477
g_warning ("Programming error: tm_drag_data_get received unknown info type (%d)\n", info);
481
gtk_selection_data_set(data, data->target, 8, reply->str, reply->len);
482
g_string_free (reply, TRUE);
485
static void tm_drag_data_received (GtkWidget *widget,
489
GtkSelectionData *data,
494
GtkTreePath *path = NULL;
495
GtkTreeModel *model = NULL;
496
GtkTreeViewDropPosition pos = 0;
497
gboolean result = FALSE;
499
/* printf ("sm drop received info: %d\n", info); */
501
/* sometimes we get empty dnd data, ignore */
502
if(widgets_blocked || (!dc) ||
503
(!data) || (data->length < 0)) return;
504
/* yet another check, i think it's an 8 bit per byte check */
505
if(data->format != 8) return;
507
display_remove_autoscroll_row_timeout (widget);
509
model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
510
g_return_if_fail (model);
511
if (!gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
515
gdk_window_get_pointer (
516
gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget)),
520
/* initialize with first displayed and drop before */
522
if (gtk_tree_model_get_iter_first (model, &iter))
524
path = gtk_tree_model_get_path (model, &iter);
525
pos = GTK_TREE_VIEW_DROP_BEFORE;
529
{ /* initialize with last path if available and drop after */
531
if (gtk_tree_model_get_iter_first (model, &iter))
533
GtkTreeIter last_valid_iter;
536
last_valid_iter = iter;
537
} while (gtk_tree_model_iter_next (model, &iter));
538
path = gtk_tree_model_get_path (model, &last_valid_iter);
539
pos = GTK_TREE_VIEW_DROP_AFTER;
545
{ /* map position onto BEFORE or AFTER */
548
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
549
case GTK_TREE_VIEW_DROP_AFTER:
550
pos = GTK_TREE_VIEW_DROP_AFTER;
552
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
553
case GTK_TREE_VIEW_DROP_BEFORE:
554
pos = GTK_TREE_VIEW_DROP_BEFORE;
561
case DND_GTKPOD_TM_PATHLIST:
562
g_return_if_fail (path);
563
result = tm_move_pathlist (data->data, path, pos);
564
dc->action = GDK_ACTION_MOVE;
565
gtk_drag_finish (dc, TRUE, FALSE, time);
568
result = tm_add_filelist (data->data, path, pos);
569
dc->action = dc->suggested_action;
570
if (dc->action == GDK_ACTION_MOVE)
571
gtk_drag_finish (dc, TRUE, TRUE, time);
573
gtk_drag_finish (dc, TRUE, FALSE, time);
575
case DND_TEXT_URI_LIST:
576
result = tm_add_filelist (data->data, path, pos);
577
dc->action = dc->suggested_action;
578
if (dc->action == GDK_ACTION_MOVE)
579
gtk_drag_finish (dc, TRUE, TRUE, time);
581
gtk_drag_finish (dc, TRUE, FALSE, time);
585
gtk_drag_finish (dc, FALSE, FALSE, time);
586
/* puts ("tm_drag_data_received(): should not be reached"); */
589
if (path) gtk_tree_path_free(path);
592
/* ---------------------------------------------------------------- */
593
/* Section for track display */
594
/* other callbacks */
595
/* ---------------------------------------------------------------- */
598
on_track_treeview_key_release_event (GtkWidget *widget,
605
if(!widgets_blocked && (mods & GDK_CONTROL_MASK))
607
switch(event->keyval)
610
/* gp_do_selected_tracks (update_tracks); */
619
/* ---------------------------------------------------------------- */
620
/* Section for track display */
621
/* ---------------------------------------------------------------- */
623
/* Append track to the track model (or write into @into_iter if != 0) */
624
void tm_add_track_to_track_model (Track *track, GtkTreeIter *into_iter)
627
GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
629
g_return_if_fail (model != NULL);
634
gtk_list_store_append (GTK_LIST_STORE (model), &iter);
636
gtk_list_store_set (GTK_LIST_STORE (model), &iter,
637
READOUT_COL, track, -1);
642
/* Used by remove_track() to remove track from model by calling
643
gtk_tree_model_foreach ().
644
Entry is deleted if data == track */
645
static gboolean tm_delete_track (GtkTreeModel *model,
652
gtk_tree_model_get (model, iter, READOUT_COL, &track, -1);
653
if(track == (Track *)data)
655
GtkTreeSelection *selection = gtk_tree_view_get_selection
657
/* printf("unselect...\n"); */
658
gtk_tree_selection_unselect_iter (selection, iter);
659
/* printf("...unselect done\n"); */
660
gtk_list_store_remove (GTK_LIST_STORE (model), iter);
667
/* Remove track from the display model */
668
void tm_remove_track (Track *track)
670
GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
672
gtk_tree_model_foreach (model, tm_delete_track, track);
676
/* Remove all tracks from the display model */
677
void tm_remove_all_tracks ()
679
GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
682
while (gtk_tree_model_get_iter_first (model, &iter))
684
gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
686
tm_store_col_order ();
687
tm_update_default_sizes ();
690
/* find out at which position column @tm_item is displayed */
691
/* static gint tm_get_col_position (TM_item tm_item) */
694
/* GtkTreeViewColumn *col; */
696
/* if (!track_treeview) return -1; */
698
/* for (i=0; i<TM_NUM_COLUMNS_PREFS; ++i) */
700
/* col = gtk_tree_view_get_column (track_treeview, i); */
701
/* if (col->sort_column_id == tm_item) return i; */
707
/* store the order of the track view columns */
708
void tm_store_col_order (void)
711
GtkTreeViewColumn *col;
713
for (i=0; i<TM_NUM_COLUMNS; ++i)
715
col = gtk_tree_view_get_column (track_treeview, i);
716
prefs_set_int_index("col_order", i, col->sort_column_id);
721
/* Used by tm_track_changed() to find the track that
722
changed name. If found, emit a "row changed" signal */
723
static gboolean tm_model_track_changed (GtkTreeModel *model,
730
gtk_tree_model_get (model, iter, READOUT_COL, &track, -1);
731
if(track == (Track *)data) {
732
gtk_tree_model_row_changed (model, path, iter);
739
/* One of the tracks has changed (this happens when the
740
iTunesDB is read and some IDs are renumbered */
741
void tm_track_changed (Track *track)
743
GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
744
/* printf("tm_track_changed enter\n");*/
746
gtk_tree_model_foreach (model, tm_model_track_changed, track);
747
/* printf("tm_track_changed exit\n");*/
752
#if ((GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION < 2))
753
/* gtk_tree_selection_get_selected_rows() was introduced in 2.2 */
756
GtkTreeModel **model;
760
void gtssf (GtkTreeModel *model,
765
struct gtsgsr *gts = data;
767
*gts->list = g_list_append (*gts->list, gtk_tree_path_copy (path));
770
GList *gtk_tree_selection_get_selected_rows (GtkTreeSelection *selection,
771
GtkTreeModel **model)
779
gtk_tree_selection_selected_foreach (selection, gtssf, >s);
785
/* Called when editable cell is being edited. Stores new data to the
786
track list. ID3 tags in the corresponding files are updated as
787
well, if activated in the pref settings */
789
tm_cell_edited (GtkCellRendererText *renderer,
790
const gchar *path_string,
791
const gchar *new_text,
795
GtkTreeSelection *selection;
799
GList *row_list, *row_node, *first;
802
column = (TM_item) g_object_get_data(G_OBJECT(renderer), "column");
803
multi_edit = prefs_get_int("multi_edit");
804
if (column == TM_COLUMN_TITLE)
805
multi_edit &= prefs_get_int("multi_edit_title");
806
selection = gtk_tree_view_get_selection(track_treeview);
807
row_list = gtk_tree_selection_get_selected_rows(selection, &model);
809
/* printf("tm_cell_edited: column: %d\n", column); */
811
sel_rows_num = g_list_length (row_list);
813
/* block widgets and update display if multi-edit is active */
814
if (multi_edit && (sel_rows_num > 1)) block_widgets ();
816
first = g_list_first (row_list);
818
for (row_node = first;
819
row_node && (multi_edit || (row_node == first));
820
row_node = g_list_next(row_node))
828
gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) row_node->data);
829
gtk_tree_model_get(model, &iter, READOUT_COL, &track, -1);
830
g_return_if_fail (track);
831
etr = track->userdata;
832
g_return_if_fail (etr);
839
case TM_COLUMN_TITLE:
840
case TM_COLUMN_ALBUM:
841
case TM_COLUMN_ARTIST:
842
case TM_COLUMN_GENRE:
843
case TM_COLUMN_COMPOSER:
844
case TM_COLUMN_COMMENT:
845
case TM_COLUMN_FILETYPE:
846
case TM_COLUMN_GROUPING:
847
case TM_COLUMN_CATEGORY:
848
case TM_COLUMN_DESCRIPTION:
849
case TM_COLUMN_PODCASTURL:
850
case TM_COLUMN_PODCASTRSS:
851
case TM_COLUMN_SUBTITLE:
852
case TM_COLUMN_TRACK_NR:
853
case TM_COLUMN_TRACKLEN:
854
case TM_COLUMN_CD_NR:
856
case TM_COLUMN_PLAYCOUNT:
857
case TM_COLUMN_RATING:
858
case TM_COLUMN_TIME_ADDED:
859
case TM_COLUMN_TIME_PLAYED:
860
case TM_COLUMN_TIME_MODIFIED:
861
case TM_COLUMN_TIME_RELEASED:
862
case TM_COLUMN_VOLUME:
863
case TM_COLUMN_SOUNDCHECK:
864
case TM_COLUMN_BITRATE:
865
case TM_COLUMN_SAMPLERATE:
867
case TM_COLUMN_MEDIA_TYPE:
868
case TM_COLUMN_TV_SHOW:
869
case TM_COLUMN_TV_EPISODE:
870
case TM_COLUMN_TV_NETWORK:
871
case TM_COLUMN_SEASON_NR:
872
case TM_COLUMN_EPISODE_NR:
873
changed = track_set_text (track, new_text, TM_to_T (column));
874
if (changed && (column == TM_COLUMN_TRACKLEN))
875
{ /* be on the safe side and reset starttime, stoptime and
877
gchar *path = get_file_name_from_source (track,
878
SOURCE_PREFER_LOCAL);
879
track->starttime = 0;
883
struct stat filestat;
884
stat (path, &filestat);
885
track->size = filestat.st_size;
888
/* redisplay some items to be on the safe side */
891
case TM_COLUMN_TRACK_NR:
892
case TM_COLUMN_CD_NR:
893
case TM_COLUMN_TRACKLEN:
894
case TM_COLUMN_TIME_ADDED:
895
case TM_COLUMN_TIME_PLAYED:
896
case TM_COLUMN_TIME_MODIFIED:
897
case TM_COLUMN_TIME_RELEASED:
898
str = track_get_text (track, TM_to_T (column));
899
g_object_set (G_OBJECT (renderer), "text", str, NULL);
907
g_warning ("Programming error: tm_cell_edited: unknown track cell (%d) edited\n", column);
910
/* printf (" changed: %d\n", changed); */
913
track->time_modified = time (NULL);
914
pm_track_changed (track); /* notify playlist model... */
915
data_changed (track->itdb); /* indicate that data has changed */
917
if (prefs_get_int("id3_write"))
920
/* should we update all ID3 tags or just the one
921
changed? -- obsoleted in 0.71*/
922
/* if (prefs_get_id3_writeall ()) tag_id = T_ALL;
923
else tag_id = TM_to_T (column);*/
924
write_tags_to_file (track);
925
/* display possible duplicates that have been removed */
926
gp_duplicate_remove (NULL, NULL);
929
while (widgets_blocked && gtk_events_pending ()) gtk_main_iteration ();
932
if (multi_edit && (sel_rows_num > 1)) release_widgets ();
934
g_list_foreach(row_list, (GFunc) gtk_tree_path_free, NULL);
935
g_list_free(row_list);
939
/* The track data is stored in a separate list (static GList *tracks)
940
and only pointers to the corresponding Track structure are placed
942
This function reads the data for the given cell from the list and
943
passes it to the renderer. */
944
static void tm_cell_data_func (GtkTreeViewColumn *tree_column,
945
GtkCellRenderer *renderer,
956
column = (TM_item)g_object_get_data (G_OBJECT (renderer), "column");
958
g_return_if_fail ((column >= 0) && (column < TM_NUM_COLUMNS));
960
gtk_tree_model_get (model, iter, READOUT_COL, &track, -1);
961
g_return_if_fail (track);
962
etr = track->userdata;
963
g_return_if_fail (etr);
965
g_return_if_fail (itdb);
967
text = track_get_text (track, TM_to_T (column));
971
case TM_COLUMN_TITLE:
972
case TM_COLUMN_ARTIST:
973
case TM_COLUMN_ALBUM:
974
case TM_COLUMN_GENRE:
975
case TM_COLUMN_COMPOSER:
976
case TM_COLUMN_COMMENT:
977
case TM_COLUMN_FILETYPE:
978
case TM_COLUMN_GROUPING:
979
case TM_COLUMN_CATEGORY:
980
case TM_COLUMN_DESCRIPTION:
981
case TM_COLUMN_PODCASTURL:
982
case TM_COLUMN_PODCASTRSS:
983
case TM_COLUMN_SUBTITLE:
984
case TM_COLUMN_TIME_PLAYED:
985
case TM_COLUMN_TIME_MODIFIED:
986
case TM_COLUMN_TIME_ADDED:
987
case TM_COLUMN_TIME_RELEASED:
988
case TM_COLUMN_TV_SHOW:
989
case TM_COLUMN_TV_EPISODE:
990
case TM_COLUMN_TV_NETWORK:
991
case TM_COLUMN_ALBUMARTIST:
992
case TM_COLUMN_SORT_ARTIST:
993
case TM_COLUMN_SORT_TITLE:
994
case TM_COLUMN_SORT_ALBUM:
995
case TM_COLUMN_SORT_ALBUMARTIST:
996
case TM_COLUMN_SORT_COMPOSER:
997
case TM_COLUMN_SORT_TVSHOW:
998
g_object_set (G_OBJECT (renderer),
1001
"xalign", 0.0, NULL);
1003
case TM_COLUMN_MEDIA_TYPE:
1004
g_object_set (G_OBJECT (renderer),
1007
"xalign", 0.0, NULL);
1009
case TM_COLUMN_TRACK_NR:
1010
case TM_COLUMN_CD_NR:
1011
case TM_COLUMN_BITRATE:
1012
case TM_COLUMN_SAMPLERATE:
1014
case TM_COLUMN_PLAYCOUNT:
1015
case TM_COLUMN_YEAR:
1016
case TM_COLUMN_RATING:
1017
case TM_COLUMN_VOLUME:
1018
case TM_COLUMN_SOUNDCHECK:
1019
case TM_COLUMN_TRACKLEN:
1020
case TM_COLUMN_SEASON_NR:
1021
case TM_COLUMN_EPISODE_NR:
1022
g_object_set (G_OBJECT (renderer),
1025
"xalign", 1.0, NULL);
1027
case TM_COLUMN_IPOD_ID:
1028
case TM_COLUMN_SIZE:
1029
g_object_set (G_OBJECT (renderer),
1032
"xalign", 1.0, NULL);
1034
case TM_COLUMN_PC_PATH:
1035
case TM_COLUMN_IPOD_PATH:
1036
case TM_COLUMN_THUMB_PATH:
1037
g_object_set (G_OBJECT (renderer),
1040
"xalign", 0.0, NULL);
1042
case TM_COLUMN_TRANSFERRED:
1043
g_object_set (G_OBJECT (renderer),
1044
"active", track->transferred,
1045
"activatable", FALSE, NULL);
1047
case TM_COLUMN_COMPILATION:
1048
g_object_set (G_OBJECT (renderer),
1049
"active", track->compilation,
1050
"activatable", TRUE, NULL);
1052
case TM_NUM_COLUMNS:
1060
/* Called when a toggle cell is being changed. Stores new data to the
1063
tm_cell_toggled (GtkCellRendererToggle *renderer,
1067
GtkTreeModel *model;
1068
GtkTreeSelection *selection;
1070
gboolean multi_edit;
1072
GList *row_list, *row_node, *first;
1075
column = (TM_item) g_object_get_data(G_OBJECT(renderer), "column");
1076
multi_edit = prefs_get_int("multi_edit");
1077
selection = gtk_tree_view_get_selection(track_treeview);
1078
row_list = gtk_tree_selection_get_selected_rows(selection, &model);
1080
/* printf("tm_cell_toggled: column: %d, arg1: %p\n", column, arg1); */
1082
sel_rows_num = g_list_length (row_list);
1084
/* block widgets and update display if multi-edit is active */
1085
if (multi_edit && (sel_rows_num > 1)) block_widgets ();
1087
first = g_list_first (row_list);
1089
/* active will show the old state -- before the toggle */
1090
g_object_get (G_OBJECT (renderer), "active", &active, NULL);
1092
for (row_node = first;
1093
row_node && (multi_edit || (row_node == first));
1094
row_node = g_list_next(row_node))
1100
gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) row_node->data);
1101
gtk_tree_model_get(model, &iter, READOUT_COL, &track, -1);
1106
case TM_COLUMN_TITLE:
1107
if ((active && (track->checked == 0)) ||
1108
(!active && (track->checked == 1)))
1110
if (active) track->checked = 1;
1111
else track->checked = 0;
1113
case TM_COLUMN_COMPILATION:
1114
if ((!active && (track->compilation == 0)) ||
1115
(active && (track->compilation == 1)))
1117
if (!active) track->compilation = 1;
1118
else track->compilation = 0;
1121
g_warning ("Programming error: tm_cell_toggled: unknown track cell (%d) edited\n", column);
1124
/* printf (" changed: %d\n", changed); */
1127
track->time_modified = time (NULL);
1128
/* pm_track_changed (track); notify playlist model... -- not
1129
* necessary here because only the track model is affected */
1130
data_changed (track->itdb); /* indicate that data has changed */
1132
/* If the changed column is the compilation flag update the file
1134
if (column == TM_COLUMN_COMPILATION)
1135
if (prefs_get_int("id3_write"))
1136
write_tags_to_file (track);
1139
while (widgets_blocked && gtk_events_pending ()) gtk_main_iteration ();
1142
if (multi_edit && (sel_rows_num > 1)) release_widgets ();
1144
g_list_foreach(row_list, (GFunc) gtk_tree_path_free, NULL);
1145
g_list_free(row_list);
1150
/* The track data is stored in a separate list (static GList *tracks)
1151
and only pointers to the corresponding Track structure are placed
1153
This function reads the data for the given cell from the list and
1154
passes it to the renderer. */
1155
static void tm_cell_data_func_toggle (GtkTreeViewColumn *tree_column,
1156
GtkCellRenderer *renderer,
1157
GtkTreeModel *model,
1164
column = (TM_item)g_object_get_data (G_OBJECT (renderer), "column");
1165
gtk_tree_model_get (model, iter, READOUT_COL, &track, -1);
1169
case TM_COLUMN_TITLE:
1170
g_object_set (G_OBJECT (renderer),
1171
"active", !track->checked,
1172
"activatable", TRUE, NULL);
1175
g_warning ("Programming error: unknown column in tm_cell_data_func_toggle: %d\n", column);
1181
* tm_get_nr_of_tracks - get the number of tracks displayed
1182
* currently in the track model Returns - the number of tracks displayed
1186
tm_get_nr_of_tracks(void)
1189
GtkTreeModel *tm = NULL;
1191
tm = gtk_tree_view_get_model (GTK_TREE_VIEW(track_treeview));
1194
result = gtk_tree_model_iter_n_children (tm, NULL);
1200
static gint comp_int (gconstpointer a, gconstpointer b)
1202
return (GPOINTER_TO_INT(a)-(GPOINTER_TO_INT(b)));
1207
* Reorder tracks in playlist to match order of tracks displayed in track
1208
* view. Only the subset of tracks currently displayed is reordered.
1209
* data_changed() is called when necessary.
1212
tm_rows_reordered (void)
1214
Playlist *current_pl;
1216
g_return_if_fail (track_treeview);
1217
current_pl = pm_get_selected_playlist ();
1221
GtkTreeModel *tm = NULL;
1223
GList *new_list = NULL, *old_pos_l = NULL;
1224
gboolean valid = FALSE;
1226
gboolean changed = FALSE;
1227
iTunesDB *itdb = NULL;
1229
tm = gtk_tree_view_get_model (track_treeview);
1230
g_return_if_fail (tm);
1232
valid = gtk_tree_model_get_iter_first (tm,&i);
1238
gtk_tree_model_get (tm, &i, READOUT_COL, &new_track, -1);
1239
g_return_if_fail (new_track);
1241
if (!itdb) itdb = new_track->itdb;
1242
new_list = g_list_append (new_list, new_track);
1243
/* what position was this track in before? */
1244
old_position = g_list_index (current_pl->members, new_track);
1245
/* check if we already used this position before (can
1246
happen if track has been added to playlist more than
1248
while ((old_position != -1) &&
1249
g_list_find (old_pos_l, GINT_TO_POINTER(old_position)))
1250
{ /* find next occurence */
1253
link = g_list_nth (current_pl->members, old_position + 1);
1254
next = g_list_index (link, new_track);
1255
if (next == -1) old_position = -1;
1256
else old_position += (1+next);
1258
/* we make a sorted list of the old positions */
1259
old_pos_l = g_list_insert_sorted (old_pos_l,
1260
GINT_TO_POINTER(old_position),
1262
valid = gtk_tree_model_iter_next (tm, &i);
1269
guint position = GPOINTER_TO_INT(olp->data);
1271
/* if position == -1 one of the tracks in the track view
1272
could not be found in the selected playlist -> stop! */
1275
g_warning ("Programming error: tm_rows_reordered_callback: track in track view was not in selected playlist\n");
1276
g_return_if_reached ();
1278
old_link = g_list_nth (current_pl->members, position);
1279
/* replace old track with new track */
1280
if (old_link->data != nlp->data)
1282
old_link->data = nlp->data;
1289
g_list_free (new_list);
1290
g_list_free (old_pos_l);
1291
/* if we changed data, mark data as changed and adopt order in
1295
data_changed (itdb);
1296
st_adopt_order_in_playlist ();
1303
on_trackids_list_foreach ( GtkTreeModel *tm, GtkTreePath *tp,
1304
GtkTreeIter *i, gpointer data)
1307
GList *l = *((GList**)data);
1308
gtk_tree_model_get(tm, i, READOUT_COL, &tr, -1);
1309
g_return_if_fail (tr);
1310
l = g_list_append(l, GUINT_TO_POINTER(tr->id));
1311
*((GList**)data) = l;
1315
/* return a list containing the track IDs of all tracks currently being
1318
tm_get_selected_trackids(void)
1320
GList *result = NULL;
1321
GtkTreeSelection *ts = NULL;
1323
if((ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(track_treeview))))
1325
gtk_tree_selection_selected_foreach(ts, on_trackids_list_foreach,
1331
/* return a list containing the track IDs of all tracks currently being
1334
tm_get_all_trackids(void)
1337
on_all_trackids_list_foreach (GtkTreeModel *tm, GtkTreePath *tp,
1338
GtkTreeIter *i, gpointer data)
1340
on_trackids_list_foreach (tm, tp, i, data);
1343
GList *result = NULL;
1344
GtkTreeModel *model;
1346
if((model = gtk_tree_view_get_model (track_treeview)))
1348
gtk_tree_model_foreach(model, on_all_trackids_list_foreach,
1355
on_tracks_list_foreach ( GtkTreeModel *tm, GtkTreePath *tp,
1356
GtkTreeIter *i, gpointer data)
1359
GList *l = *((GList**)data);
1360
gtk_tree_model_get(tm, i, READOUT_COL, &tr, -1);
1361
g_return_if_fail (tr);
1362
l = g_list_append(l, tr);
1363
*((GList**)data) = l;
1367
/* return a list containing pointers to all tracks currently being
1370
tm_get_selected_tracks(void)
1372
GList *result = NULL;
1373
GtkTreeSelection *ts = NULL;
1375
if((ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(track_treeview))))
1377
gtk_tree_selection_selected_foreach(ts, on_tracks_list_foreach,
1385
/* used by tm_get_all_tracks */
1386
static gboolean on_all_tracks_list_foreach (GtkTreeModel *tm,
1391
on_tracks_list_foreach (tm, tp, i, data);
1396
/* return a list containing pointers to all tracks currently being
1397
displayed. You must g_list_free() the list after use. */
1399
tm_get_all_tracks(void)
1401
GList *result = NULL;
1402
GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
1404
g_return_val_if_fail (model, NULL);
1406
gtk_tree_model_foreach(model, on_all_tracks_list_foreach,
1412
/* Stop editing. If @cancel is TRUE, the edited value will be
1413
discarded (I have the feeling that the "discarding" part does not
1414
work quite the way intended). */
1415
void tm_stop_editing (gboolean cancel)
1417
GtkTreeViewColumn *col;
1419
if (!track_treeview) return;
1421
gtk_tree_view_get_cursor (track_treeview, NULL, &col);
1424
/* Before removing the widget we set multi_edit to FALSE. That
1425
way at most one entry will be changed (this also doesn't
1426
seem to work the way intended) */
1427
gboolean me = prefs_get_int("multi_edit");
1428
prefs_set_int("multi_edit", FALSE);
1429
if (!cancel && col->editable_widget)
1430
gtk_cell_editable_editing_done (col->editable_widget);
1431
if (col->editable_widget)
1432
gtk_cell_editable_remove_widget (col->editable_widget);
1433
prefs_set_int("multi_edit", me);
1439
/* Function to compare @tm_item of @track1 and @track2. Used by
1440
tm_data_compare_func() */
1441
static gint tm_data_compare (Track *track1, Track *track2,
1445
ExtraTrackData *etr1, *etr2;
1448
g_return_val_if_fail (track1 && track2, 0);
1452
case TM_COLUMN_TITLE:
1453
case TM_COLUMN_ALBUM:
1454
case TM_COLUMN_GENRE:
1455
case TM_COLUMN_COMPOSER:
1456
case TM_COLUMN_COMMENT:
1457
case TM_COLUMN_FILETYPE:
1458
case TM_COLUMN_GROUPING:
1459
case TM_COLUMN_ARTIST:
1460
case TM_COLUMN_CATEGORY:
1461
case TM_COLUMN_DESCRIPTION:
1462
case TM_COLUMN_PODCASTURL:
1463
case TM_COLUMN_PODCASTRSS:
1464
case TM_COLUMN_SUBTITLE:
1465
case TM_COLUMN_TV_SHOW:
1466
case TM_COLUMN_TV_EPISODE:
1467
case TM_COLUMN_TV_NETWORK:
1468
/* string_compare_func is set to either compare_string_fuzzy or
1469
compare_string in tm_sort_column_changed() which is called
1470
once before the comparing begins. */
1471
cmp = string_compare_func (
1472
track_get_item (track1, TM_to_T (tm_item)),
1473
track_get_item (track2, TM_to_T (tm_item)));
1475
case TM_COLUMN_TRACK_NR:
1476
cmp = track1->tracks - track2->tracks;
1477
if (cmp == 0) cmp = track1->track_nr - track2->track_nr;
1479
case TM_COLUMN_CD_NR:
1480
cmp = track1->cds - track2->cds;
1481
if (cmp == 0) cmp = track1->cd_nr - track2->cd_nr;
1483
case TM_COLUMN_IPOD_ID:
1484
cmp = track1->id - track2->id;
1486
case TM_COLUMN_PC_PATH:
1487
etr1 = track1->userdata;
1488
etr2 = track2->userdata;
1489
g_return_val_if_fail (etr1 && etr2, 0);
1490
cmp = g_utf8_collate (etr1->pc_path_utf8, etr2->pc_path_utf8);
1492
case TM_COLUMN_IPOD_PATH:
1493
cmp = g_utf8_collate (track1->ipod_path, track2->ipod_path);
1495
case TM_COLUMN_THUMB_PATH:
1496
etr1 = track1->userdata;
1497
etr2 = track2->userdata;
1498
g_return_val_if_fail (etr1 && etr2, 0);
1499
cmp = g_utf8_collate (etr1->thumb_path_utf8, etr2->thumb_path_utf8);
1501
case TM_COLUMN_TRANSFERRED:
1502
if(track1->transferred == track2->transferred)
1504
else if(track1->transferred == TRUE)
1509
case TM_COLUMN_COMPILATION:
1510
if(track1->compilation == track2->compilation)
1512
else if(track1->compilation == TRUE)
1517
case TM_COLUMN_SIZE:
1518
cmp = track1->size - track2->size;
1520
case TM_COLUMN_TRACKLEN:
1521
cmp = track1->tracklen - track2->tracklen;
1523
case TM_COLUMN_BITRATE:
1524
cmp = track1->bitrate - track2->bitrate;
1526
case TM_COLUMN_SAMPLERATE:
1527
cmp = track1->samplerate - track2->samplerate;
1530
cmp = track1->BPM - track2->BPM;
1532
case TM_COLUMN_PLAYCOUNT:
1533
cmp = track1->playcount - track2->playcount;
1535
case TM_COLUMN_RATING:
1536
cmp = track1->rating - track2->rating;
1538
case TM_COLUMN_TIME_ADDED:
1539
case TM_COLUMN_TIME_PLAYED:
1540
case TM_COLUMN_TIME_MODIFIED:
1541
case TM_COLUMN_TIME_RELEASED:
1542
cmp = COMP (time_get_time (track1, TM_to_T (tm_item)),
1543
time_get_time (track2, TM_to_T (tm_item)));
1545
case TM_COLUMN_VOLUME:
1546
cmp = track1->volume - track2->volume;
1548
case TM_COLUMN_SOUNDCHECK:
1549
/* If soundcheck is unset (0) use 0 dB (1000) */
1550
cmp = (track1->soundcheck? track1->soundcheck:1000) -
1551
(track2->soundcheck? track2->soundcheck:1000);
1553
case TM_COLUMN_YEAR:
1554
cmp = track1->year - track2->year;
1556
case TM_COLUMN_SEASON_NR:
1557
cmp = track1->season_nr - track2->season_nr;
1559
case TM_COLUMN_EPISODE_NR:
1560
cmp = track1->episode_nr - track2->episode_nr;
1562
case TM_COLUMN_MEDIA_TYPE:
1563
cmp = track1->mediatype - track2->mediatype;
1566
g_warning ("Programming error: tm_data_compare_func: no sort method for tm_item %d\n", tm_item);
1570
/* implement stable sorting: if two items are the same, revert to
1571
the last relative positition */
1574
etr1 = track1->userdata;
1575
etr2 = track2->userdata;
1576
g_return_val_if_fail (etr1 && etr2, 0);
1577
cmp = etr1->sortindex - etr2->sortindex;
1583
/* Function used to compare rows with user's search string */
1584
gboolean tm_search_equal_func (GtkTreeModel *model,
1588
gpointer search_data)
1592
gtk_tree_model_get (model, iter, READOUT_COL, &track1, -1);
1595
case TM_COLUMN_TITLE:
1596
case TM_COLUMN_ALBUM:
1597
case TM_COLUMN_GENRE:
1598
case TM_COLUMN_COMPOSER:
1599
case TM_COLUMN_COMMENT:
1600
case TM_COLUMN_FILETYPE:
1601
case TM_COLUMN_GROUPING:
1602
case TM_COLUMN_ARTIST:
1603
case TM_COLUMN_CATEGORY:
1604
case TM_COLUMN_DESCRIPTION:
1605
case TM_COLUMN_PODCASTURL:
1606
case TM_COLUMN_PODCASTRSS:
1607
case TM_COLUMN_SUBTITLE:
1608
case TM_COLUMN_PC_PATH:
1609
case TM_COLUMN_YEAR:
1610
case TM_COLUMN_IPOD_PATH:
1611
case TM_COLUMN_COMPILATION:
1612
case TM_COLUMN_THUMB_PATH:
1613
case TM_COLUMN_TV_SHOW:
1614
case TM_COLUMN_TV_EPISODE:
1615
case TM_COLUMN_TV_NETWORK:
1616
cmp = (compare_string_start_case_insensitive (
1617
track_get_item (track1, TM_to_T (column)),
1620
case TM_COLUMN_TRACK_NR:
1621
case TM_COLUMN_IPOD_ID:
1622
case TM_COLUMN_TRANSFERRED:
1623
case TM_COLUMN_SIZE:
1624
case TM_COLUMN_TRACKLEN:
1625
case TM_COLUMN_BITRATE:
1626
case TM_COLUMN_PLAYCOUNT:
1627
case TM_COLUMN_RATING:
1628
case TM_COLUMN_TIME_PLAYED:
1629
case TM_COLUMN_TIME_MODIFIED:
1630
case TM_COLUMN_VOLUME:
1631
case TM_COLUMN_CD_NR:
1632
case TM_COLUMN_TIME_ADDED:
1633
case TM_COLUMN_SOUNDCHECK:
1634
case TM_COLUMN_SAMPLERATE:
1636
case TM_COLUMN_TIME_RELEASED:
1637
case TM_COLUMN_MEDIA_TYPE:
1638
case TM_COLUMN_SEASON_NR:
1639
case TM_COLUMN_EPISODE_NR:
1640
case TM_NUM_COLUMNS:
1641
g_warning ("Programming error: tm_search_equal_func: no sort method for column %d\n", column);
1647
/* Function used to compare two cells during sorting (track view) */
1648
gint tm_data_compare_func (GtkTreeModel *model,
1659
gtk_tree_model_get (model, a, READOUT_COL, &track1, -1);
1660
gtk_tree_model_get (model, b, READOUT_COL, &track2, -1);
1661
if(gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model),
1662
&column, &order) == FALSE)
1665
result = tm_data_compare (track1, track2, column);
1670
/* set/read the counter used to remember how often the sort column has
1672
@inc: negative: reset counter to 0
1673
@inc: positive or zero : add to counter
1674
return value: new value of the counter */
1675
gint tm_sort_counter (gint inc)
1677
static gint cnt = 0;
1691
/* Redisplays the tracks in the track view according to the order
1692
* stored in the sort tab view. This only works if the track view is
1693
* not sorted --> skip if sorted */
1695
void tm_adopt_order_in_sorttab (void)
1697
if (prefs_get_int("tm_sort") == SORT_NONE)
1699
GList *gl, *tracks = NULL;
1701
/* retrieve the currently displayed tracks (non ordered) from
1702
the last sort tab or from the selected playlist if no sort
1703
tabs are being used */
1704
tm_remove_all_tracks ();
1705
tracks = display_get_selected_members (prefs_get_int("sort_tab_num")-1);
1706
for (gl=tracks; gl; gl=gl->next)
1707
tm_add_track_to_track_model ((Track *)gl->data, NULL);
1712
/* redisplay the contents of the track view in it's unsorted order */
1713
static void tm_unsort (void)
1717
GtkTreeModel *model= gtk_tree_view_get_model (track_treeview);
1719
prefs_set_int("tm_sort", SORT_NONE);
1720
if (!BROKEN_GTK_TREE_SORT)
1722
/* no need to comment this out -- searching still works, but for lack
1723
of a ctrl-g only the first occurence will be found */
1724
/* gtk_tree_view_set_enable_search (GTK_TREE_VIEW
1725
* (track_treeview), FALSE);*/
1726
gtk_tree_sortable_set_sort_column_id
1727
(GTK_TREE_SORTABLE (model),
1728
GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
1729
GTK_SORT_ASCENDING);
1730
tm_adopt_order_in_sorttab ();
1734
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);
1736
tm_sort_counter (-1);
1741
static void tm_set_search_column (gint newcol)
1743
/* printf ("track_treeview: %p, col: %d\n", track_treeview, newcol); */
1744
g_return_if_fail (track_treeview);
1746
gtk_tree_view_set_search_column (GTK_TREE_VIEW (track_treeview),
1750
case TM_COLUMN_TITLE:
1751
case TM_COLUMN_ALBUM:
1752
case TM_COLUMN_GENRE:
1753
case TM_COLUMN_COMPOSER:
1754
case TM_COLUMN_COMMENT:
1755
case TM_COLUMN_FILETYPE:
1756
case TM_COLUMN_GROUPING:
1757
case TM_COLUMN_ARTIST:
1758
case TM_COLUMN_CATEGORY:
1759
case TM_COLUMN_DESCRIPTION:
1760
case TM_COLUMN_PODCASTURL:
1761
case TM_COLUMN_PODCASTRSS:
1762
case TM_COLUMN_SUBTITLE:
1763
case TM_COLUMN_PC_PATH:
1764
case TM_COLUMN_YEAR:
1765
case TM_COLUMN_IPOD_PATH:
1766
case TM_COLUMN_COMPILATION:
1767
case TM_COLUMN_THUMB_PATH:
1768
case TM_COLUMN_TV_SHOW:
1769
case TM_COLUMN_TV_EPISODE:
1770
case TM_COLUMN_TV_NETWORK:
1771
gtk_tree_view_set_enable_search (GTK_TREE_VIEW (track_treeview), TRUE);
1773
case TM_COLUMN_TRACK_NR:
1774
case TM_COLUMN_IPOD_ID:
1775
case TM_COLUMN_TRANSFERRED:
1776
case TM_COLUMN_SIZE:
1777
case TM_COLUMN_TRACKLEN:
1778
case TM_COLUMN_BITRATE:
1779
case TM_COLUMN_PLAYCOUNT:
1780
case TM_COLUMN_RATING:
1781
case TM_COLUMN_TIME_PLAYED:
1782
case TM_COLUMN_TIME_MODIFIED:
1783
case TM_COLUMN_VOLUME:
1784
case TM_COLUMN_CD_NR:
1785
case TM_COLUMN_TIME_ADDED:
1786
case TM_COLUMN_SOUNDCHECK:
1787
case TM_COLUMN_SAMPLERATE:
1789
case TM_COLUMN_TIME_RELEASED:
1790
case TM_COLUMN_MEDIA_TYPE:
1791
case TM_COLUMN_SEASON_NR:
1792
case TM_COLUMN_EPISODE_NR:
1793
case TM_NUM_COLUMNS:
1794
gtk_tree_view_set_enable_search (GTK_TREE_VIEW (track_treeview), FALSE);
1797
prefs_set_int (TM_PREFS_SEARCH_COLUMN, newcol);
1801
/* This is called before when changing the sort order or the sort
1802
column, and before doing the sorting */
1803
static void tm_sort_column_changed (GtkTreeSortable *ts,
1806
static gint lastcol = -1; /* which column was sorted last time? */
1813
gtk_tree_sortable_get_sort_column_id (ts, &newcol, &order);
1815
/* printf ("scc -- col: %d, order: %d\n", newcol, order); */
1817
/* set compare function for strings (to speed up sorting) */
1818
buf = g_strdup_printf ("sort_ign_field_%d", TM_to_T (newcol));
1819
if (prefs_get_int (buf))
1820
string_compare_func = compare_string_fuzzy;
1822
string_compare_func = compare_string;
1825
/* don't do anything if no sort column is set */
1832
if (newcol != lastcol)
1834
tm_sort_counter (-1);
1838
if (tm_sort_counter (1) >= 3)
1839
{ /* after clicking three times, reset sort order! */
1840
tm_unsort (); /* also resets sort counter */
1844
prefs_set_int("tm_sort", order);
1846
prefs_set_int("tm_sortcol", newcol);
1848
tm_set_search_column (newcol);
1850
if(prefs_get_int("tm_autostore")) tm_rows_reordered ();
1851
sort_window_update ();
1853
/* stable sorting: index original order */
1854
tracks = tm_get_all_tracks ();
1855
/* make numbering ascending or decending depending on sort order
1856
(then we don't have to worry about the sort order when doing
1857
the comparison in tm_data_compare_func() */
1858
if (order == GTK_SORT_ASCENDING)
1868
for (gl=tracks; gl; gl=gl->next)
1870
ExtraTrackData *etr;
1871
Track *tr = gl->data;
1872
g_return_if_fail (tr);
1874
g_return_if_fail (etr);
1878
g_list_free (tracks);
1882
void tm_sort (TM_item col, GtkSortType order)
1886
GtkTreeModel *model= gtk_tree_view_get_model (track_treeview);
1887
if (order != SORT_NONE)
1889
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
1893
{ /* only unsort if treeview is sorted */
1896
if (gtk_tree_sortable_get_sort_column_id
1897
(GTK_TREE_SORTABLE (model), &column, &order))
1899
/* column == -2 actually is not defined, but it means
1900
that the model is unsorted. The sortable interface
1901
is badly implemented in gtk 2.4 */
1913
/* Adds the columns to our track_treeview */
1914
static GtkTreeViewColumn *tm_add_column (TM_item tm_item, gint pos)
1916
GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
1917
GtkTreeViewColumn *col = NULL;
1919
GtkCellRenderer *renderer = NULL; /* default */
1922
g_return_val_if_fail (gtkpod_window, NULL);
1923
tt = g_object_get_data (G_OBJECT (gtkpod_window), "main_tooltips");
1924
g_return_val_if_fail (tt, NULL);
1926
g_return_val_if_fail (tm_item >= 0, NULL);
1927
g_return_val_if_fail (tm_item < TM_NUM_COLUMNS, NULL);
1929
text = gettext (get_tm_string (tm_item));
1931
g_return_val_if_fail (text, NULL);
1933
col = gtk_tree_view_column_new ();
1937
case TM_COLUMN_TITLE:
1938
/* Add additional toggle box for 'checked' property */
1939
renderer = gtk_cell_renderer_toggle_new ();
1940
g_object_set_data (G_OBJECT (renderer), "column",
1942
g_signal_connect (G_OBJECT (renderer), "toggled",
1943
G_CALLBACK (tm_cell_toggled), model);
1944
gtk_tree_view_column_pack_start (col, renderer, FALSE);
1945
gtk_tree_view_column_set_cell_data_func (col, renderer,
1946
tm_cell_data_func_toggle,
1950
case TM_COLUMN_ARTIST:
1951
case TM_COLUMN_ALBUM:
1952
case TM_COLUMN_GENRE:
1953
case TM_COLUMN_COMPOSER:
1954
case TM_COLUMN_COMMENT:
1955
case TM_COLUMN_FILETYPE:
1956
case TM_COLUMN_GROUPING:
1957
case TM_COLUMN_BITRATE:
1958
case TM_COLUMN_SAMPLERATE:
1960
case TM_COLUMN_CATEGORY:
1961
case TM_COLUMN_DESCRIPTION:
1962
case TM_COLUMN_PODCASTURL:
1963
case TM_COLUMN_PODCASTRSS:
1964
case TM_COLUMN_SUBTITLE:
1965
case TM_COLUMN_PC_PATH:
1966
case TM_COLUMN_IPOD_PATH:
1967
case TM_COLUMN_THUMB_PATH:
1968
case TM_COLUMN_SIZE:
1969
case TM_COLUMN_MEDIA_TYPE:
1970
case TM_COLUMN_TV_SHOW:
1971
case TM_COLUMN_TV_EPISODE:
1972
case TM_COLUMN_TV_NETWORK:
1973
case TM_COLUMN_SEASON_NR:
1974
case TM_COLUMN_EPISODE_NR:
1975
case TM_COLUMN_ALBUMARTIST:
1976
case TM_COLUMN_SORT_ARTIST:
1977
case TM_COLUMN_SORT_TITLE:
1978
case TM_COLUMN_SORT_ALBUM:
1979
case TM_COLUMN_SORT_ALBUMARTIST:
1980
case TM_COLUMN_SORT_COMPOSER:
1981
case TM_COLUMN_SORT_TVSHOW:
1983
/* for some column names we want to use shorter alternatives to
1985
case TM_COLUMN_RATING:
1988
case TM_COLUMN_TRACK_NR:
1991
case TM_COLUMN_CD_NR:
1994
case TM_COLUMN_IPOD_ID:
1997
case TM_COLUMN_TRANSFERRED:
1998
text = _("Trnsfrd");
1999
renderer = gtk_cell_renderer_toggle_new ();
2001
case TM_COLUMN_COMPILATION:
2003
renderer = gtk_cell_renderer_toggle_new ();
2004
g_signal_connect (G_OBJECT (renderer), "toggled",
2005
G_CALLBACK (tm_cell_toggled), model);
2007
case TM_COLUMN_TRACKLEN:
2010
case TM_COLUMN_PLAYCOUNT:
2013
case TM_COLUMN_TIME_PLAYED:
2016
case TM_COLUMN_TIME_MODIFIED:
2017
text = _("Modified");
2019
case TM_COLUMN_TIME_ADDED:
2022
case TM_COLUMN_TIME_RELEASED:
2023
text = _("Released");
2025
case TM_COLUMN_YEAR:
2028
case TM_COLUMN_VOLUME:
2031
case TM_COLUMN_SOUNDCHECK:
2032
text = _("Sndchk.");
2034
case TM_NUM_COLUMNS:
2035
g_return_val_if_reached (NULL);
2040
{ /* text renderer -- editable/not editable is done in
2041
tm_cell_data_func() */
2042
renderer = gtk_cell_renderer_text_new ();
2043
g_signal_connect (G_OBJECT (renderer), "edited",
2044
G_CALLBACK (tm_cell_edited), model);
2047
g_object_set_data (G_OBJECT (renderer), "column",
2050
gtk_tree_view_column_set_title (col, text);
2051
gtk_tree_view_column_pack_start (col, renderer, FALSE);
2052
gtk_tree_view_column_set_cell_data_func (col, renderer,
2053
tm_cell_data_func, NULL, NULL);
2054
gtk_tree_view_column_set_sort_column_id (col, tm_item);
2055
gtk_tree_view_column_set_resizable (col, TRUE);
2056
/* gtk_tree_view_column_set_clickable(column, TRUE); */
2057
gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
2058
gtk_tree_view_column_set_fixed_width (col,
2059
prefs_get_int_index("tm_col_width", tm_item));
2060
gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (model), tm_item,
2061
tm_data_compare_func, NULL, NULL);
2062
gtk_tree_view_column_set_reorderable (col, TRUE);
2063
gtk_tree_view_insert_column (track_treeview, col, pos);
2064
tm_columns[tm_item] = col;
2068
gtk_tree_view_column_set_visible (col,
2069
prefs_get_int_index("col_visible", tm_item));
2071
if (get_tm_tooltip (tm_item))
2072
gtk_tooltips_set_tip (tt, col->button,
2073
gettext (get_tm_tooltip (tm_item)),
2079
/* Adds the columns to our track_treeview */
2080
static void tm_add_columns (void)
2084
for (i=0; i<TM_NUM_COLUMNS; ++i)
2086
tm_add_column (prefs_get_int_index("col_order", i), -1);
2088
tm_show_preferred_columns();
2091
static void tm_select_current_position (gint x, gint y)
2097
gtk_tree_view_get_path_at_pos (track_treeview,
2098
x, y, &path, NULL, NULL, NULL);
2101
GtkTreeSelection *ts = gtk_tree_view_get_selection (track_treeview);
2102
gtk_tree_selection_select_path (ts, path);
2103
gtk_tree_path_free (path);
2109
tm_button_press_event(GtkWidget *w, GdkEventButton *e, gpointer data)
2117
printf ("Pressed in cell %d (%f/%f)\n\n",
2118
tree_view_get_cell_from_pos(GTK_TREE_VIEW(w),
2119
(guint)e->x, (guint)e->y, NULL),
2124
tm_select_current_position (e->x, e->y);
2125
tm_context_menu_init ();
2134
/* called when the track selection changes */
2136
tm_selection_changed_event(GtkTreeSelection *selection, gpointer data)
2138
GtkTreeView *treeview = gtk_tree_selection_get_tree_view (selection);
2140
GtkTreeViewColumn *column;
2143
gtk_tree_view_get_cursor (treeview, &path, &column);
2146
col_id = tm_lookup_col_id (column);
2147
if (col_id != -1) tm_set_search_column (col_id);
2149
info_update_track_view_selected ();
2151
/* update the coverart display */
2152
GList *selected = display_get_selection (prefs_get_int("sort_tab_num"));
2153
if (selected != NULL)
2155
Track *track = selected->data;
2157
coverart_select_cover (track);
2162
/* Create tracks treeview */
2163
void tm_create_treeview (void)
2165
GtkTreeModel *model = NULL;
2166
GtkWidget *track_window = gtkpod_xml_get_widget (main_window_xml, "track_window");
2167
GtkTreeSelection *select;
2169
GtkWidget *stv = gtk_tree_view_new ();
2171
/* create tree view */
2173
{ /* delete old tree view */
2174
model = gtk_tree_view_get_model (track_treeview);
2175
/* FIXME: how to delete model? */
2176
gtk_widget_destroy (GTK_WIDGET (track_treeview));
2178
track_treeview = GTK_TREE_VIEW (stv);
2179
gtk_widget_show (stv);
2180
gtk_container_add (GTK_CONTAINER (track_window), stv);
2181
/* create model (we only need one column for the model -- only a
2182
* pointer to the track has to be stored) */
2183
model = GTK_TREE_MODEL (
2184
gtk_list_store_new (1, G_TYPE_POINTER));
2185
gtk_tree_view_set_model (track_treeview, GTK_TREE_MODEL (model));
2186
gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (track_treeview), TRUE);
2187
gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (track_treeview),
2188
tm_search_equal_func,
2191
select = gtk_tree_view_get_selection (track_treeview);
2192
gtk_tree_selection_set_mode (select,
2193
GTK_SELECTION_MULTIPLE);
2194
g_signal_connect (G_OBJECT (select) , "changed",
2195
G_CALLBACK (tm_selection_changed_event),
2199
/* gtk_drag_source_set (GTK_WIDGET (track_treeview), GDK_BUTTON1_MASK, */
2200
/* tm_drag_types, TGNR (tm_drag_types), */
2201
/* GDK_ACTION_COPY|GDK_ACTION_MOVE); */
2202
/* gtk_tree_view_enable_model_drag_dest(track_treeview, tm_drop_types, */
2203
/* TGNR (tm_drop_types), */
2204
/* GDK_ACTION_COPY|GDK_ACTION_MOVE); */
2205
/* /\* need the gtk_drag_dest_set() with no actions ("0") so that the */
2206
/* data_received callback gets the correct info value. This is most */
2207
/* likely a bug... *\/ */
2208
/* gtk_drag_dest_set_target_list (GTK_WIDGET (track_treeview), */
2209
/* gtk_target_list_new (tm_drop_types, */
2210
/* TGNR (tm_drop_types))); */
2213
gtk_drag_source_set (GTK_WIDGET (track_treeview),
2215
tm_drag_types, TGNR (tm_drag_types),
2216
GDK_ACTION_COPY|GDK_ACTION_MOVE);
2217
gtk_drag_dest_set (GTK_WIDGET (track_treeview),
2219
tm_drop_types, TGNR (tm_drop_types),
2220
GDK_ACTION_COPY|GDK_ACTION_MOVE);
2223
g_signal_connect ((gpointer) track_treeview, "drag-begin",
2224
G_CALLBACK (tm_drag_begin),
2227
g_signal_connect ((gpointer) track_treeview, "drag-data-delete",
2228
G_CALLBACK (tm_drag_data_delete),
2231
g_signal_connect ((gpointer) track_treeview, "drag-data-get",
2232
G_CALLBACK (tm_drag_data_get),
2235
g_signal_connect ((gpointer) track_treeview, "drag-data-received",
2236
G_CALLBACK (tm_drag_data_received),
2239
g_signal_connect ((gpointer) track_treeview, "drag-drop",
2240
G_CALLBACK (tm_drag_drop),
2243
g_signal_connect ((gpointer) track_treeview, "drag-end",
2244
G_CALLBACK (tm_drag_end),
2247
g_signal_connect ((gpointer) track_treeview, "drag-leave",
2248
G_CALLBACK (tm_drag_leave),
2251
g_signal_connect ((gpointer) track_treeview, "drag-motion",
2252
G_CALLBACK (tm_drag_motion),
2255
g_signal_connect_after ((gpointer) stv, "key_release_event",
2256
G_CALLBACK (on_track_treeview_key_release_event),
2258
g_signal_connect ((gpointer) track_treeview, "button-press-event",
2259
G_CALLBACK (tm_button_press_event),
2261
g_signal_connect (G_OBJECT (model), "sort-column-changed",
2262
G_CALLBACK (tm_sort_column_changed),
2265
/* initialize sorting */
2266
tm_sort (prefs_get_int("tm_sortcol"), prefs_get_int("tm_sort"));
2267
/* set correct column for typeahead */
2268
if (prefs_get_int_value (TM_PREFS_SEARCH_COLUMN, &col))
2270
tm_set_search_column (col);
2273
{ /* reasonable default */
2274
tm_set_search_column (TM_COLUMN_TITLE);
2280
tm_show_preferred_columns(void)
2282
GtkTreeViewColumn *tvc = NULL;
2286
for (i=0; i<TM_NUM_COLUMNS; ++i)
2288
tvc = gtk_tree_view_get_column (track_treeview, i);
2289
visible = prefs_get_int_index("col_visible",
2290
prefs_get_int_index("col_order", i));
2291
gtk_tree_view_column_set_visible (tvc, visible);
2296
/* update the cfg structure (preferences) with the current sizes /
2297
positions (called by display_update_default_sizes():
2298
column widths of track model */
2299
void tm_update_default_sizes (void)
2302
GtkTreeViewColumn *col;
2306
for (i=0; i<TM_NUM_COLUMNS; ++i)
2308
col = tm_columns [i];
2311
col_width = gtk_tree_view_column_get_width (col);
2314
prefs_set_int_index ("tm_col_width", i, col_width);
2316
prefs_set_int_index ("tm_col_width", i, 80);
2323
/* get the TM_ITEM column id for @column. Returns -1 if column could
2325
static TM_item tm_lookup_col_id (GtkTreeViewColumn *column)
2331
for (i=0; i<TM_NUM_COLUMNS; ++i)
2333
if (column == tm_columns[i]) return i;
2340
/* Compare function to avoid sorting */
2341
static gint tm_nosort_comp (GtkTreeModel *model,
2350
/* Disable sorting of the view during lengthy updates. */
2351
/* @enable: TRUE: enable, FALSE: disable */
2352
void tm_enable_disable_view_sort (gboolean enable)
2354
static gint disable_count = 0;
2359
if (disable_count < 0)
2360
fprintf (stderr, "Programming error: disable_count < 0\n");
2361
if (disable_count == 0 && track_treeview)
2363
if (prefs_get_int("tm_sort") != SORT_NONE)
2365
/* Re-enable sorting */
2366
GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
2367
if (BROKEN_GTK_TREE_SORT)
2369
gtk_tree_sortable_set_sort_func (
2370
GTK_TREE_SORTABLE (model),
2371
prefs_get_int("tm_sortcol"),
2372
tm_data_compare_func, NULL, NULL);
2376
gtk_tree_sortable_set_sort_column_id (
2377
GTK_TREE_SORTABLE (model),
2378
prefs_get_int("tm_sortcol"),
2379
prefs_get_int("tm_sort"));
2386
if (disable_count == 0 && track_treeview)
2388
if (prefs_get_int("tm_sort") != SORT_NONE)
2390
/* Disable sorting */
2391
GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
2392
if (BROKEN_GTK_TREE_SORT)
2394
gtk_tree_sortable_set_sort_func (
2395
GTK_TREE_SORTABLE (model),
2396
prefs_get_int("tm_sortcol"),
2397
tm_nosort_comp, NULL, NULL);
2401
gtk_tree_sortable_set_sort_column_id (
2402
GTK_TREE_SORTABLE (model),
2403
GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
2404
prefs_get_int("tm_sort"));
2414
/* Callback for adding tracks within tm_add_filelist */
2415
void tm_addtrackfunc (Playlist *plitem, Track *track, gpointer data)
2417
struct asf_data *asf = (struct asf_data *)data;
2418
GtkTreeModel *model;
2419
GtkTreeIter new_iter;
2421
model = gtk_tree_view_get_model (track_treeview);
2423
/* printf("plitem: %p\n", plitem);
2424
if (plitem) printf("plitem->type: %d\n", plitem->type);*/
2425
/* add to playlist but not to the display */
2426
gp_playlist_add_track (plitem, track, FALSE);
2428
/* create new iter in track view */
2431
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
2432
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
2433
case GTK_TREE_VIEW_DROP_AFTER:
2434
gtk_list_store_insert_after (GTK_LIST_STORE (model),
2435
&new_iter, asf->to_iter);
2437
case GTK_TREE_VIEW_DROP_BEFORE:
2438
gtk_list_store_insert_before (GTK_LIST_STORE (model),
2439
&new_iter, asf->to_iter);
2443
tm_add_track_to_track_model (track, &new_iter);
2447
/* DND: insert a list of files before/after @path
2448
@data: list of files
2449
@path: where to drop (NULL to drop at the end)
2450
@pos: before/after... (ignored if @path is NULL)
2452
gboolean tm_add_filelist (gchar *data,
2454
GtkTreeViewDropPosition pos)
2456
GtkTreeModel *model;
2457
gchar *buf = NULL, *use_data;
2458
gchar **files, **filep;
2459
Playlist *current_playlist = pm_get_selected_playlist ();
2461
g_return_val_if_fail (data, FALSE);
2462
g_return_val_if_fail (*data, FALSE);
2463
g_return_val_if_fail (current_playlist, FALSE);
2465
model = gtk_tree_view_get_model (track_treeview);
2466
g_return_val_if_fail (model, FALSE);
2471
if (pos != GTK_TREE_VIEW_DROP_BEFORE)
2472
{ /* need to reverse the list of files -- otherwise wie add them
2473
* in reverse order */
2474
/* split the path list into individual strings */
2475
gint len = strlen (data) + 1;
2476
files = g_strsplit (data, "\n", -1);
2478
/* find the end of the list */
2479
while (*filep) ++filep;
2480
/* reserve memory */
2481
buf = g_malloc0 (len);
2482
/* reverse the list */
2483
while (filep != files)
2486
g_strlcat (buf, *filep, len);
2487
g_strlcat (buf, "\n", len);
2497
/* printf("filelist: (%s) -> (%s)\n", data, use_data); */
2498
/* initialize add-track-struct */
2500
{ /* add where specified (@path/@pos) */
2501
GtkTreeIter to_iter;
2502
struct asf_data asf;
2503
if (!gtk_tree_model_get_iter (model, &to_iter, path))
2504
g_return_val_if_reached (FALSE);
2505
asf.to_iter = &to_iter;
2507
add_text_plain_to_playlist (current_playlist->itdb,
2508
current_playlist, use_data, 0,
2509
tm_addtrackfunc, &asf);
2512
{ /* add to the end */
2513
add_text_plain_to_playlist (current_playlist->itdb,
2514
current_playlist, use_data, 0,
2517
tm_rows_reordered ();