~ubuntu-branches/ubuntu/quantal/gtkpod-aac/quantal

« back to all changes in this revision

Viewing changes to src/display_tracks.c

  • Committer: Bazaar Package Importer
  • Author(s): Steve Kowalik
  • Date: 2007-07-17 18:25:25 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20070717182525-rhl5w4pk8lbk6pna
Tags: 0.99.10-2ubuntu1
* Resynchronise with gtkpod 0.9.10-2.
* Hack in dpatch support, since it was removed.
* Rename debian/patches/03-configure.dpatch to
  debian/patches/aac-configure.dpatch.
* Update debian/gtkpod-aac.diff.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
|  Copyright (C) 2002-2007 Jorg Schuler <jcsjcs at users sourceforge net>
 
3
|  Part of the gtkpod project.
 
4
|
 
5
|  URL: http://www.gtkpod.org/
 
6
|  URL: http://gtkpod.sourceforge.net/
 
7
|
 
8
|  This program is free software; you can redistribute it and/or modify
 
9
|  it under the terms of the GNU General Public License as published by
 
10
|  the Free Software Foundation; either version 2 of the License, or
 
11
|  (at your option) any later version.
 
12
|
 
13
|  This program is distributed in the hope that it will be useful,
 
14
|  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
|  GNU General Public License for more details.
 
17
|
 
18
|  You should have received a copy of the GNU General Public License
 
19
|  along with this program; if not, write to the Free Software
 
20
|  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
21
|
 
22
|  iTunes and iPod are trademarks of Apple
 
23
|
 
24
|  This product is not supported/written/published by Apple!
 
25
|
 
26
|  $Id: display_tracks.c 1589 2007-06-25 14:23:59Z jcsjcs $
 
27
*/
 
28
 
 
29
#ifdef HAVE_CONFIG_H
 
30
#  include <config.h>
 
31
#endif
 
32
 
 
33
#include <gdk/gdkkeysyms.h>
 
34
#include <gtk/gtk.h>
 
35
#include <limits.h>
 
36
#include <stdio.h>
 
37
#include <stdlib.h>
 
38
#include <string.h>
 
39
#include <sys/stat.h>
 
40
#include <sys/types.h>
 
41
#include <unistd.h>
 
42
 
 
43
#include "prefs.h"
 
44
#include "display_private.h"
 
45
#include "display_itdb.h"
 
46
#include "itdb.h"
 
47
#include "info.h"
 
48
#include "misc.h"
 
49
#include "misc_track.h"
 
50
#include "file.h"
 
51
#include "context_menus.h"
 
52
 
 
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;
 
59
 
 
60
/* compare function to be used for string comparisons */
 
61
static gint (*string_compare_func) (const gchar *str1, const gchar *str2) = compare_string;
 
62
 
 
63
static GtkTreeViewColumn *tm_add_column (TM_item tm_item, gint position);
 
64
static TM_item tm_lookup_col_id (GtkTreeViewColumn *column);
 
65
 
 
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 }
 
73
};
 
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 }
 
79
};
 
80
 
 
81
/* prefs strings */
 
82
const gchar *TM_PREFS_SEARCH_COLUMN = "tm_prefs_search_column";
 
83
 
 
84
 
 
85
/* ---------------------------------------------------------------- */
 
86
/* Section for track display                                        */
 
87
/* DND -- Drag And Drop                                             */
 
88
/* ---------------------------------------------------------------- */
 
89
 
 
90
 
 
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,
 
94
                                  GtkTreePath *path,
 
95
                                  GtkTreeViewDropPosition pos)
 
96
{
 
97
    GtkTreeIter to_iter;
 
98
    GtkTreeIter *from_iter;
 
99
    GtkTreeModel *model;
 
100
    GList *iterlist = NULL;
 
101
    GList *link;
 
102
    gchar **paths, **pathp;
 
103
 
 
104
    g_return_val_if_fail (data, FALSE);
 
105
    g_return_val_if_fail (*data, FALSE);
 
106
 
 
107
    model = gtk_tree_view_get_model (track_treeview);
 
108
    g_return_val_if_fail (model, FALSE);
 
109
 
 
110
    g_return_val_if_fail (gtk_tree_model_get_iter (model, &to_iter, path),
 
111
                          FALSE);
 
112
 
 
113
    /* split the path list into individual strings */
 
114
    paths = g_strsplit (data, "\n", -1);
 
115
    pathp = paths;
 
116
    /* Convert the list of paths into a list of iters */
 
117
    while (*pathp)
 
118
    {
 
119
        from_iter = g_malloc (sizeof (GtkTreeIter));
 
120
        if ((strlen (*pathp) > 0) &&
 
121
            gtk_tree_model_get_iter_from_string (model, from_iter, *pathp))
 
122
        {
 
123
            iterlist = g_list_append (iterlist, from_iter);
 
124
        }
 
125
        ++pathp;
 
126
    }
 
127
    g_strfreev (paths);
 
128
    /* Move the iters in iterlist before or after @to_iter */
 
129
    switch (pos)
 
130
    {
 
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)
 
134
        {
 
135
            from_iter = (GtkTreeIter *)link->data;
 
136
            gtk_list_store_move_after (GTK_LIST_STORE (model),
 
137
                                       from_iter, &to_iter);
 
138
        }
 
139
        break;
 
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)
 
143
        {
 
144
            from_iter = (GtkTreeIter *)link->data;
 
145
            gtk_list_store_move_before (GTK_LIST_STORE (model),
 
146
                                        from_iter, &to_iter);
 
147
        }
 
148
        break;
 
149
    }
 
150
 
 
151
    /* free iterlist */
 
152
    for (link = iterlist; link; link = link->next)
 
153
        g_free (link->data);
 
154
    g_list_free (iterlist);
 
155
 
 
156
    tm_rows_reordered ();
 
157
    return TRUE;
 
158
}
 
159
 
 
160
 
 
161
/*
 
162
 * utility function for appending ipod track ids for track view (DND)
 
163
 */
 
164
static void
 
165
on_tm_dnd_get_track_foreach(GtkTreeModel *tm, GtkTreePath *tp,
 
166
                            GtkTreeIter *i, gpointer data)
 
167
{
 
168
    Track *tr;
 
169
    GString *tracklist = (GString *)data;
 
170
 
 
171
    g_return_if_fail (tracklist);
 
172
 
 
173
    gtk_tree_model_get(tm, i, READOUT_COL, &tr, -1);
 
174
    g_return_if_fail (tr);
 
175
 
 
176
    g_string_append_printf (tracklist, "%p\n", tr);
 
177
}
 
178
 
 
179
 
 
180
/*
 
181
 * utility function for appending path for track view (DND)
 
182
 */
 
183
static void
 
184
on_tm_dnd_get_path_foreach(GtkTreeModel *tm, GtkTreePath *tp,
 
185
                           GtkTreeIter *iter, gpointer data)
 
186
{
 
187
    GString *filelist = (GString *)data;
 
188
    gchar *ps = gtk_tree_path_to_string (tp);
 
189
    g_string_append_printf (filelist, "%s\n", ps);
 
190
    g_free (ps);
 
191
}
 
192
 
 
193
/*
 
194
 * utility function for appending file for track view (DND)
 
195
 */
 
196
static void
 
197
on_tm_dnd_get_file_foreach(GtkTreeModel *tm, GtkTreePath *tp,
 
198
                           GtkTreeIter *iter, gpointer data)
 
199
{
 
200
    Track *track;
 
201
    GString *filelist = (GString *)data;
 
202
    gchar *name;
 
203
 
 
204
    gtk_tree_model_get(tm, iter, READOUT_COL, &track, -1);
 
205
    name = get_file_name_from_source (track, SOURCE_PREFER_LOCAL);
 
206
    if (name)
 
207
    {
 
208
        g_string_append_printf (filelist, "file:%s\n", name);
 
209
        g_free (name);
 
210
    }
 
211
}
 
212
 
 
213
/*
 
214
 * utility function for appending file-uri for track view (DND)
 
215
 */
 
216
static void
 
217
on_tm_dnd_get_uri_foreach(GtkTreeModel *tm, GtkTreePath *tp,
 
218
                          GtkTreeIter *iter, gpointer data)
 
219
{
 
220
    Track *track;
 
221
    GString *filelist = (GString *)data;
 
222
    gchar *name;
 
223
 
 
224
    gtk_tree_model_get(tm, iter, READOUT_COL, &track, -1);
 
225
    name = get_file_name_from_source (track, SOURCE_PREFER_LOCAL);
 
226
    if (name)
 
227
    {
 
228
        gchar *uri = g_filename_to_uri (name, NULL, NULL);
 
229
        if (uri)
 
230
        {
 
231
            g_string_append_printf (filelist, "%s\n", uri);
 
232
            g_free (uri);
 
233
        }
 
234
        g_free (name);
 
235
    }
 
236
}
 
237
 
 
238
static void tm_drag_begin (GtkWidget *widget,
 
239
                           GdkDragContext *dc,
 
240
                           gpointer user_data)
 
241
{
 
242
    tm_stop_editing (TRUE);
 
243
}
 
244
 
 
245
 
 
246
/* remove dragged playlist after successful MOVE */
 
247
static void tm_drag_data_delete (GtkWidget *widget,
 
248
                           GdkDragContext *dc,
 
249
                           gpointer user_data)
 
250
{
 
251
    GtkTreeSelection *ts;
 
252
    Playlist *pl = pm_get_selected_playlist ();
 
253
    gint num;
 
254
 
 
255
/*     puts ("tm_drag_data_delete"); */
 
256
 
 
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;
 
263
 
 
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);
 
268
        gchar *str;
 
269
        Track *track;
 
270
 
 
271
        gtk_tree_selection_selected_foreach(ts,
 
272
                                            on_tm_dnd_get_track_foreach,
 
273
                                            reply);
 
274
        str = reply->str;
 
275
        while(parse_tracks_from_string(&str, &track))
 
276
        {
 
277
            gp_playlist_remove_track (pl, track, DELETE_ACTION_PLAYLIST);
 
278
        }
 
279
        g_string_free (reply, TRUE);
 
280
 
 
281
        gtkpod_statusbar_message (ngettext ("Moved one track",
 
282
                                            "Moved %d tracks", num), num);
 
283
    }
 
284
    else
 
285
    {
 
286
        gtkpod_statusbar_message (ngettext ("Copied one track",
 
287
                                            "Copied %d tracks", num), num);
 
288
    }
 
289
}
 
290
 
 
291
 
 
292
 
 
293
static void tm_drag_end (GtkWidget *widget,
 
294
                         GdkDragContext *dc,
 
295
                         gpointer user_data)
 
296
{
 
297
/*     puts ("tm_drag_end"); */
 
298
    display_remove_autoscroll_row_timeout (widget);
 
299
    gtkpod_tracks_statusbar_update ();
 
300
}
 
301
 
 
302
 
 
303
static gboolean tm_drag_drop (GtkWidget *widget,
 
304
                              GdkDragContext *dc,
 
305
                              gint x,
 
306
                              gint y,
 
307
                              guint time,
 
308
                              gpointer user_data)
 
309
{
 
310
    GdkAtom target;
 
311
 
 
312
/*     puts ("tm_drag_data_drop"); */
 
313
 
 
314
    display_remove_autoscroll_row_timeout (widget);
 
315
 
 
316
    target = gtk_drag_dest_find_target (widget, dc, NULL);
 
317
 
 
318
    if (target != GDK_NONE)
 
319
    {
 
320
        gtk_drag_get_data (widget, dc, target, time);
 
321
        return TRUE;
 
322
    }
 
323
    return FALSE;
 
324
}
 
325
 
 
326
static void tm_drag_leave (GtkWidget *widget,
 
327
                           GdkDragContext *dc,
 
328
                           guint time,
 
329
                           gpointer user_data)
 
330
{
 
331
/*     puts ("tm_drag_leave"); */
 
332
    display_remove_autoscroll_row_timeout (widget);
 
333
}
 
334
 
 
335
 
 
336
 
 
337
static gboolean tm_drag_motion (GtkWidget *widget,
 
338
                                GdkDragContext *dc,
 
339
                                gint x,
 
340
                                gint y,
 
341
                                guint time,
 
342
                                gpointer user_data)
 
343
{
 
344
    GtkTreeView *treeview;
 
345
    GdkAtom target;
 
346
    GtkTreePath *path = NULL;
 
347
    GtkTreeViewDropPosition pos;
 
348
    iTunesDB *itdb;
 
349
    ExtraiTunesDBData *eitdb;
 
350
 
 
351
/*     printf ("drag_motion  suggested: %d actions: %d\n", */
 
352
/*          dc->suggested_action, dc->actions); */
 
353
 
 
354
/*     printf ("x: %d y: %d\n", x, y); */
 
355
 
 
356
    g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
 
357
 
 
358
    treeview = GTK_TREE_VIEW (widget);
 
359
 
 
360
    display_install_autoscroll_row_timeout (widget);
 
361
 
 
362
    itdb = gp_get_selected_itdb ();
 
363
    /* no drop is possible if no playlist/repository is selected */
 
364
    if (itdb == NULL)
 
365
    {
 
366
        gdk_drag_status (dc, 0, time);
 
367
        return FALSE;
 
368
    }
 
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)
 
373
    {
 
374
        gdk_drag_status (dc, 0, time);
 
375
        return FALSE;
 
376
    }
 
377
 
 
378
    /* optically set destination row if available */
 
379
    if (gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
 
380
                                           x, y, &path, &pos))
 
381
    {
 
382
        /* drops are only allowed before and after -- not onto
 
383
           existing paths */
 
384
        switch (pos)
 
385
        {
 
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);
 
390
            break;
 
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);
 
395
            break;
 
396
        }
 
397
 
 
398
        gtk_tree_path_free (path);
 
399
        path = NULL;
 
400
    }
 
401
    else
 
402
    {
 
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);
 
407
        path = NULL;
 
408
    }
 
409
 
 
410
    target = gtk_drag_dest_find_target (widget, dc, NULL);
 
411
 
 
412
    /* no drop possible if no valid target can be found */
 
413
    if (target == GDK_NONE)
 
414
    {
 
415
        gdk_drag_status (dc, 0, time);
 
416
        return FALSE;
 
417
    }
 
418
 
 
419
    if (widget == gtk_drag_get_source_widget (dc))
 
420
    {   /* drag is within the same widget */
 
421
        gint column;
 
422
        GtkSortType order;
 
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);
 
429
            return FALSE;
 
430
        }
 
431
        else
 
432
        {   /* only allow moves within the same widget */
 
433
            gdk_drag_status (dc, GDK_ACTION_MOVE, time);
 
434
        }
 
435
    }
 
436
    else
 
437
    {  /* whatever the source suggests */
 
438
        gdk_drag_status (dc, dc->suggested_action, time);
 
439
    }
 
440
 
 
441
    return TRUE;
 
442
}
 
443
 
 
444
 
 
445
static void tm_drag_data_get (GtkWidget       *widget,
 
446
                              GdkDragContext  *context,
 
447
                              GtkSelectionData *data,
 
448
                              guint            info,
 
449
                              guint            time,
 
450
                              gpointer         user_data)
 
451
{
 
452
    GtkTreeSelection *ts = NULL;
 
453
    GString *reply = g_string_sized_new (2000);
 
454
 
 
455
/*     printf("tm drag get info: %d\n", info); */
 
456
    if((data) && (ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget))))
 
457
    {
 
458
        switch (info)
 
459
        {
 
460
        case DND_GTKPOD_TRACKLIST:
 
461
            gtk_tree_selection_selected_foreach(ts,
 
462
                                    on_tm_dnd_get_track_foreach, reply);
 
463
            break;
 
464
        case DND_GTKPOD_TM_PATHLIST:
 
465
            gtk_tree_selection_selected_foreach(ts,
 
466
                                    on_tm_dnd_get_path_foreach, reply);
 
467
            break;
 
468
        case DND_TEXT_URI_LIST:
 
469
            gtk_tree_selection_selected_foreach(ts,
 
470
                                    on_tm_dnd_get_uri_foreach, reply);
 
471
            break;
 
472
        case DND_TEXT_PLAIN:
 
473
            gtk_tree_selection_selected_foreach(ts,
 
474
                                    on_tm_dnd_get_file_foreach, reply);
 
475
            break;
 
476
        default:
 
477
            g_warning ("Programming error: tm_drag_data_get received unknown info type (%d)\n", info);
 
478
            break;
 
479
        }
 
480
    }
 
481
    gtk_selection_data_set(data, data->target, 8, reply->str, reply->len);
 
482
    g_string_free (reply, TRUE);
 
483
}
 
484
 
 
485
static void tm_drag_data_received (GtkWidget       *widget,
 
486
                                   GdkDragContext  *dc,
 
487
                                   gint             x,
 
488
                                   gint             y,
 
489
                                   GtkSelectionData *data,
 
490
                                   guint            info,
 
491
                                   guint            time,
 
492
                                   gpointer         user_data)
 
493
{
 
494
    GtkTreePath *path = NULL;
 
495
    GtkTreeModel *model = NULL;
 
496
    GtkTreeViewDropPosition pos = 0;
 
497
    gboolean result = FALSE;
 
498
 
 
499
    /* printf ("sm drop received info: %d\n", info); */
 
500
 
 
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;
 
506
 
 
507
    display_remove_autoscroll_row_timeout (widget);
 
508
 
 
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),
 
512
                                            x, y, &path, &pos))
 
513
    {
 
514
        gint py;
 
515
        gdk_window_get_pointer (
 
516
            gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget)),
 
517
            NULL, &py, NULL);
 
518
        if (py < 5)
 
519
        {
 
520
            /* initialize with first displayed and drop before */
 
521
            GtkTreeIter iter;
 
522
            if (gtk_tree_model_get_iter_first (model, &iter))
 
523
            {
 
524
                path = gtk_tree_model_get_path (model, &iter);
 
525
                pos = GTK_TREE_VIEW_DROP_BEFORE;
 
526
            }
 
527
        }
 
528
        else
 
529
        {   /* initialize with last path if available and drop after */
 
530
            GtkTreeIter iter;
 
531
            if (gtk_tree_model_get_iter_first (model, &iter))
 
532
            {
 
533
                GtkTreeIter last_valid_iter;
 
534
                do
 
535
                {
 
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;
 
540
            }
 
541
        }
 
542
    }
 
543
 
 
544
    if (path)
 
545
    {   /* map position onto BEFORE or AFTER */
 
546
        switch (pos)
 
547
        {
 
548
        case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
 
549
        case GTK_TREE_VIEW_DROP_AFTER:
 
550
            pos = GTK_TREE_VIEW_DROP_AFTER;
 
551
            break;
 
552
        case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
 
553
        case GTK_TREE_VIEW_DROP_BEFORE:
 
554
            pos = GTK_TREE_VIEW_DROP_BEFORE;
 
555
            break;
 
556
        }
 
557
    }
 
558
 
 
559
    switch (info)
 
560
    {
 
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);
 
566
        break;
 
567
    case DND_TEXT_PLAIN:
 
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);
 
572
        else
 
573
            gtk_drag_finish (dc, TRUE, FALSE, time);
 
574
        break;
 
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);
 
580
        else
 
581
            gtk_drag_finish (dc, TRUE, FALSE, time);
 
582
        break;
 
583
    default:
 
584
        dc->action = 0;
 
585
        gtk_drag_finish (dc, FALSE, FALSE, time);
 
586
/*      puts ("tm_drag_data_received(): should not be reached"); */
 
587
        break;
 
588
    }
 
589
    if (path) gtk_tree_path_free(path);
 
590
}
 
591
 
 
592
/* ---------------------------------------------------------------- */
 
593
/* Section for track display                                        */
 
594
/* other callbacks                                                  */
 
595
/* ---------------------------------------------------------------- */
 
596
 
 
597
static gboolean
 
598
on_track_treeview_key_release_event     (GtkWidget       *widget,
 
599
                                        GdkEventKey     *event,
 
600
                                        gpointer         user_data)
 
601
{
 
602
    guint mods;
 
603
    mods = event->state;
 
604
 
 
605
    if(!widgets_blocked && (mods & GDK_CONTROL_MASK))
 
606
    {
 
607
        switch(event->keyval)
 
608
        {
 
609
/*          case GDK_u: */
 
610
/*              gp_do_selected_tracks (update_tracks); */
 
611
/*              break; */
 
612
            default:
 
613
                break;
 
614
        }
 
615
    }
 
616
    return FALSE;
 
617
}
 
618
 
 
619
/* ---------------------------------------------------------------- */
 
620
/* Section for track display                                        */
 
621
/* ---------------------------------------------------------------- */
 
622
 
 
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)
 
625
{
 
626
    GtkTreeIter iter;
 
627
    GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
 
628
 
 
629
    g_return_if_fail (model != NULL);
 
630
 
 
631
    if (into_iter)
 
632
        iter = *into_iter;
 
633
    else
 
634
        gtk_list_store_append (GTK_LIST_STORE (model), &iter);
 
635
 
 
636
    gtk_list_store_set (GTK_LIST_STORE (model), &iter,
 
637
                        READOUT_COL, track, -1);
 
638
}
 
639
 
 
640
 
 
641
 
 
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,
 
646
                                GtkTreePath *path,
 
647
                                GtkTreeIter *iter,
 
648
                                gpointer data)
 
649
{
 
650
  Track *track;
 
651
 
 
652
  gtk_tree_model_get (model, iter, READOUT_COL, &track, -1);
 
653
  if(track == (Track *)data)
 
654
  {
 
655
      GtkTreeSelection *selection = gtk_tree_view_get_selection
 
656
          (track_treeview);
 
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);
 
661
      return TRUE;
 
662
  }
 
663
  return FALSE;
 
664
}
 
665
 
 
666
 
 
667
/* Remove track from the display model */
 
668
void tm_remove_track (Track *track)
 
669
{
 
670
  GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
 
671
  if (model != NULL)
 
672
    gtk_tree_model_foreach (model, tm_delete_track, track);
 
673
}
 
674
 
 
675
 
 
676
/* Remove all tracks from the display model */
 
677
void tm_remove_all_tracks ()
 
678
{
 
679
  GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
 
680
  GtkTreeIter iter;
 
681
 
 
682
  while (gtk_tree_model_get_iter_first (model, &iter))
 
683
  {
 
684
      gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
 
685
  }
 
686
  tm_store_col_order ();
 
687
  tm_update_default_sizes ();
 
688
}
 
689
 
 
690
/* find out at which position column @tm_item is displayed */
 
691
/* static gint tm_get_col_position (TM_item tm_item) */
 
692
/* { */
 
693
/*     gint i; */
 
694
/*     GtkTreeViewColumn *col; */
 
695
 
 
696
/*     if (!track_treeview) return -1; */
 
697
 
 
698
/*     for (i=0; i<TM_NUM_COLUMNS_PREFS; ++i) */
 
699
/*     { */
 
700
/*      col = gtk_tree_view_get_column (track_treeview, i); */
 
701
/*      if (col->sort_column_id == tm_item) return i; */
 
702
/*     } */
 
703
/*     return -1; */
 
704
/* } */
 
705
 
 
706
 
 
707
/* store the order of the track view columns */
 
708
void tm_store_col_order (void)
 
709
{
 
710
    gint i;
 
711
    GtkTreeViewColumn *col;
 
712
 
 
713
    for (i=0; i<TM_NUM_COLUMNS; ++i)
 
714
    {
 
715
        col = gtk_tree_view_get_column (track_treeview, i);
 
716
        prefs_set_int_index("col_order", i, col->sort_column_id);
 
717
    }
 
718
}
 
719
 
 
720
 
 
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,
 
724
                                       GtkTreePath *path,
 
725
                                       GtkTreeIter *iter,
 
726
                                       gpointer data)
 
727
{
 
728
  Track *track;
 
729
 
 
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);
 
733
    return TRUE;
 
734
  }
 
735
  return FALSE;
 
736
}
 
737
 
 
738
 
 
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)
 
742
{
 
743
  GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
 
744
  /*  printf("tm_track_changed enter\n");*/
 
745
  if (model != NULL)
 
746
    gtk_tree_model_foreach (model, tm_model_track_changed, track);
 
747
  /*  printf("tm_track_changed exit\n");*/
 
748
}
 
749
 
 
750
 
 
751
 
 
752
#if ((GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION < 2))
 
753
/* gtk_tree_selection_get_selected_rows() was introduced in 2.2 */
 
754
struct gtsgsr
 
755
{
 
756
    GtkTreeModel **model;
 
757
    GList        **list;
 
758
};
 
759
 
 
760
void  gtssf  (GtkTreeModel *model,
 
761
              GtkTreePath *path,
 
762
              GtkTreeIter *iter,
 
763
              gpointer data)
 
764
{
 
765
    struct gtsgsr *gts = data;
 
766
    *gts->model = model;
 
767
    *gts->list = g_list_append (*gts->list, gtk_tree_path_copy (path));
 
768
}
 
769
 
 
770
GList *gtk_tree_selection_get_selected_rows (GtkTreeSelection *selection,
 
771
                                             GtkTreeModel     **model)
 
772
{
 
773
    struct gtsgsr gts;
 
774
    GList *list = NULL;
 
775
 
 
776
    gts.model = model;
 
777
    gts.list = &list;
 
778
 
 
779
    gtk_tree_selection_selected_foreach (selection, gtssf, &gts);
 
780
    return list;
 
781
}
 
782
#endif
 
783
 
 
784
 
 
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 */
 
788
static void
 
789
tm_cell_edited (GtkCellRendererText *renderer,
 
790
                const gchar         *path_string,
 
791
                const gchar         *new_text,
 
792
                gpointer             data)
 
793
{
 
794
  GtkTreeModel *model;
 
795
  GtkTreeSelection *selection;
 
796
  TM_item column;
 
797
  gboolean multi_edit;
 
798
  gint sel_rows_num;
 
799
  GList *row_list, *row_node, *first;
 
800
 
 
801
 
 
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);
 
808
 
 
809
/*   printf("tm_cell_edited: column: %d\n", column); */
 
810
 
 
811
  sel_rows_num = g_list_length (row_list);
 
812
 
 
813
  /* block widgets and update display if multi-edit is active */
 
814
  if (multi_edit && (sel_rows_num > 1)) block_widgets ();
 
815
 
 
816
  first = g_list_first (row_list);
 
817
 
 
818
  for (row_node = first;
 
819
       row_node && (multi_edit || (row_node == first));
 
820
       row_node = g_list_next(row_node))
 
821
  {
 
822
     Track *track;
 
823
     ExtraTrackData *etr;
 
824
     gboolean changed;
 
825
     GtkTreeIter iter;
 
826
     gchar *str;
 
827
 
 
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);
 
833
 
 
834
 
 
835
     changed = FALSE;
 
836
 
 
837
     switch(column)
 
838
     {
 
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:
 
855
     case TM_COLUMN_YEAR:
 
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:
 
866
     case TM_COLUMN_BPM:
 
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
 
876
             * filesize */
 
877
             gchar *path = get_file_name_from_source (track,
 
878
                                                      SOURCE_PREFER_LOCAL);
 
879
             track->starttime = 0;
 
880
             track->stoptime = 0;
 
881
             if (path)
 
882
             {
 
883
                 struct stat filestat;
 
884
                 stat (path, &filestat);
 
885
                 track->size = filestat.st_size;
 
886
             }
 
887
         }
 
888
         /* redisplay some items to be on the safe side */
 
889
         switch (column)
 
890
         {
 
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);
 
900
             g_free (str);
 
901
             break;
 
902
         default:
 
903
             break;
 
904
         }
 
905
         break;
 
906
     default:
 
907
        g_warning ("Programming error: tm_cell_edited: unknown track cell (%d) edited\n", column);
 
908
        break;
 
909
     }
 
910
/*      printf ("  changed: %d\n", changed); */
 
911
     if (changed)
 
912
     {
 
913
        track->time_modified = time (NULL);
 
914
        pm_track_changed (track);    /* notify playlist model... */
 
915
        data_changed (track->itdb); /* indicate that data has changed */
 
916
 
 
917
        if (prefs_get_int("id3_write"))
 
918
        {
 
919
            /* T_item tag_id;*/
 
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);
 
927
        }
 
928
     }
 
929
     while (widgets_blocked && gtk_events_pending ())  gtk_main_iteration ();
 
930
  }
 
931
 
 
932
  if (multi_edit && (sel_rows_num > 1)) release_widgets ();
 
933
 
 
934
  g_list_foreach(row_list, (GFunc) gtk_tree_path_free, NULL);
 
935
  g_list_free(row_list);
 
936
}
 
937
 
 
938
 
 
939
/* The track data is stored in a separate list (static GList *tracks)
 
940
   and only pointers to the corresponding Track structure are placed
 
941
   into the model.
 
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,
 
946
                               GtkTreeModel      *model,
 
947
                               GtkTreeIter       *iter,
 
948
                               gpointer           data)
 
949
{
 
950
  Track *track;
 
951
  ExtraTrackData *etr;
 
952
  iTunesDB *itdb;
 
953
  TM_item column;
 
954
  gchar *text;
 
955
 
 
956
  column = (TM_item)g_object_get_data (G_OBJECT (renderer), "column");
 
957
 
 
958
  g_return_if_fail ((column >= 0) && (column < TM_NUM_COLUMNS));
 
959
 
 
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);
 
964
  itdb = track->itdb;
 
965
  g_return_if_fail (itdb);
 
966
 
 
967
  text = track_get_text (track, TM_to_T (column));
 
968
 
 
969
  switch (column)
 
970
  {
 
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),
 
999
                    "text", text,
 
1000
                    "editable", TRUE,
 
1001
                    "xalign", 0.0, NULL);
 
1002
      break;
 
1003
  case TM_COLUMN_MEDIA_TYPE:
 
1004
      g_object_set (G_OBJECT (renderer),
 
1005
                    "text", text,
 
1006
                    "editable", FALSE,
 
1007
                    "xalign", 0.0, NULL);
 
1008
      break;
 
1009
  case TM_COLUMN_TRACK_NR:
 
1010
  case TM_COLUMN_CD_NR:
 
1011
  case TM_COLUMN_BITRATE:
 
1012
  case TM_COLUMN_SAMPLERATE:
 
1013
  case TM_COLUMN_BPM:
 
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),
 
1023
                    "text", text,
 
1024
                    "editable", TRUE,
 
1025
                    "xalign", 1.0, NULL);
 
1026
      break;
 
1027
  case TM_COLUMN_IPOD_ID:
 
1028
  case TM_COLUMN_SIZE:
 
1029
      g_object_set (G_OBJECT (renderer),
 
1030
                    "text", text,
 
1031
                    "editable", FALSE,
 
1032
                    "xalign", 1.0, NULL);
 
1033
      break;
 
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),
 
1038
                    "text", text,
 
1039
                    "editable", FALSE,
 
1040
                    "xalign", 0.0, NULL);
 
1041
      break;
 
1042
  case TM_COLUMN_TRANSFERRED:
 
1043
      g_object_set (G_OBJECT (renderer),
 
1044
                    "active", track->transferred,
 
1045
                    "activatable", FALSE, NULL);
 
1046
      break;
 
1047
  case TM_COLUMN_COMPILATION:
 
1048
      g_object_set (G_OBJECT (renderer),
 
1049
                    "active", track->compilation,
 
1050
                    "activatable", TRUE, NULL);
 
1051
      break;
 
1052
  case TM_NUM_COLUMNS:
 
1053
      break;
 
1054
  }
 
1055
  g_free (text);
 
1056
}
 
1057
 
 
1058
 
 
1059
 
 
1060
/* Called when a toggle cell is being changed. Stores new data to the
 
1061
   track list. */
 
1062
static void
 
1063
tm_cell_toggled (GtkCellRendererToggle *renderer,
 
1064
                 gchar *arg1,
 
1065
                 gpointer user_data)
 
1066
{
 
1067
  GtkTreeModel *model;
 
1068
  GtkTreeSelection *selection;
 
1069
  TM_item column;
 
1070
  gboolean multi_edit;
 
1071
  gint sel_rows_num;
 
1072
  GList *row_list, *row_node, *first;
 
1073
  gboolean active;
 
1074
 
 
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);
 
1079
 
 
1080
/*  printf("tm_cell_toggled: column: %d, arg1: %p\n", column, arg1); */
 
1081
 
 
1082
  sel_rows_num = g_list_length (row_list);
 
1083
 
 
1084
  /* block widgets and update display if multi-edit is active */
 
1085
  if (multi_edit && (sel_rows_num > 1)) block_widgets ();
 
1086
 
 
1087
  first = g_list_first (row_list);
 
1088
 
 
1089
  /* active will show the old state -- before the toggle */
 
1090
  g_object_get (G_OBJECT (renderer), "active", &active, NULL);
 
1091
 
 
1092
  for (row_node = first;
 
1093
       row_node && (multi_edit || (row_node == first));
 
1094
       row_node = g_list_next(row_node))
 
1095
  {
 
1096
     Track *track;
 
1097
     gboolean changed;
 
1098
     GtkTreeIter iter;
 
1099
 
 
1100
     gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) row_node->data);
 
1101
     gtk_tree_model_get(model, &iter, READOUT_COL, &track, -1);
 
1102
     changed = FALSE;
 
1103
 
 
1104
     switch(column)
 
1105
     {
 
1106
     case TM_COLUMN_TITLE:
 
1107
         if ((active && (track->checked == 0)) ||
 
1108
             (!active && (track->checked == 1)))
 
1109
             changed = TRUE;
 
1110
         if (active) track->checked = 1;
 
1111
         else        track->checked = 0;
 
1112
         break;
 
1113
     case TM_COLUMN_COMPILATION:
 
1114
         if ((!active && (track->compilation == 0)) ||
 
1115
             (active && (track->compilation == 1)))
 
1116
             changed = TRUE;
 
1117
         if (!active) track->compilation = 1;
 
1118
         else        track->compilation = 0;
 
1119
        break;
 
1120
     default:
 
1121
        g_warning ("Programming error: tm_cell_toggled: unknown track cell (%d) edited\n", column);
 
1122
        break;
 
1123
     }
 
1124
/*      printf ("  changed: %d\n", changed); */
 
1125
     if (changed)
 
1126
     {
 
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 */
 
1131
        
 
1132
        /* If the changed column is the compilation flag update the file
 
1133
           if required */
 
1134
        if (column == TM_COLUMN_COMPILATION)
 
1135
           if (prefs_get_int("id3_write"))
 
1136
              write_tags_to_file (track);
 
1137
        
 
1138
     }
 
1139
     while (widgets_blocked && gtk_events_pending ())  gtk_main_iteration ();
 
1140
  }
 
1141
 
 
1142
  if (multi_edit && (sel_rows_num > 1)) release_widgets ();
 
1143
 
 
1144
  g_list_foreach(row_list, (GFunc) gtk_tree_path_free, NULL);
 
1145
  g_list_free(row_list);
 
1146
}
 
1147
 
 
1148
 
 
1149
 
 
1150
/* The track data is stored in a separate list (static GList *tracks)
 
1151
   and only pointers to the corresponding Track structure are placed
 
1152
   into the model.
 
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,
 
1158
                                      GtkTreeIter       *iter,
 
1159
                                      gpointer           data)
 
1160
{
 
1161
  Track *track;
 
1162
  TM_item column;
 
1163
 
 
1164
  column = (TM_item)g_object_get_data (G_OBJECT (renderer), "column");
 
1165
  gtk_tree_model_get (model, iter, READOUT_COL, &track, -1);
 
1166
 
 
1167
  switch (column)
 
1168
  {
 
1169
  case TM_COLUMN_TITLE:
 
1170
      g_object_set (G_OBJECT (renderer),
 
1171
                    "active", !track->checked,
 
1172
                    "activatable", TRUE, NULL);
 
1173
      break;
 
1174
  default:
 
1175
      g_warning ("Programming error: unknown column in tm_cell_data_func_toggle: %d\n", column);
 
1176
      break;
 
1177
  }
 
1178
}
 
1179
 
 
1180
/**
 
1181
 * tm_get_nr_of_tracks - get the number of tracks displayed
 
1182
 * currently in the track model Returns - the number of tracks displayed
 
1183
 * currently
 
1184
 */
 
1185
gint
 
1186
tm_get_nr_of_tracks(void)
 
1187
{
 
1188
    gint result = 0;
 
1189
    GtkTreeModel *tm = NULL;
 
1190
 
 
1191
    tm = gtk_tree_view_get_model (GTK_TREE_VIEW(track_treeview));
 
1192
    if (tm)
 
1193
    {
 
1194
        result = gtk_tree_model_iter_n_children (tm, NULL);
 
1195
    }
 
1196
    return result;
 
1197
}
 
1198
 
 
1199
 
 
1200
static gint comp_int (gconstpointer a, gconstpointer b)
 
1201
{
 
1202
    return (GPOINTER_TO_INT(a)-(GPOINTER_TO_INT(b)));
 
1203
}
 
1204
 
 
1205
 
 
1206
/**
 
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.
 
1210
 */
 
1211
void
 
1212
tm_rows_reordered (void)
 
1213
{
 
1214
    Playlist *current_pl;
 
1215
 
 
1216
    g_return_if_fail (track_treeview);
 
1217
    current_pl = pm_get_selected_playlist ();
 
1218
 
 
1219
    if(current_pl)
 
1220
    {
 
1221
        GtkTreeModel *tm = NULL;
 
1222
        GtkTreeIter i;
 
1223
        GList *new_list = NULL, *old_pos_l = NULL;
 
1224
        gboolean valid = FALSE;
 
1225
        GList *nlp, *olp;
 
1226
        gboolean changed = FALSE;
 
1227
        iTunesDB *itdb = NULL;
 
1228
 
 
1229
        tm = gtk_tree_view_get_model (track_treeview);
 
1230
        g_return_if_fail (tm);
 
1231
 
 
1232
        valid = gtk_tree_model_get_iter_first (tm,&i);
 
1233
        while (valid)
 
1234
        {
 
1235
            Track *new_track;
 
1236
            gint old_position;
 
1237
 
 
1238
            gtk_tree_model_get (tm, &i, READOUT_COL, &new_track, -1);
 
1239
            g_return_if_fail (new_track);
 
1240
 
 
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
 
1247
               once */
 
1248
            while ((old_position != -1) &&
 
1249
                   g_list_find (old_pos_l, GINT_TO_POINTER(old_position)))
 
1250
            {  /* find next occurence */
 
1251
                GList *link;
 
1252
                gint next;
 
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);
 
1257
            }
 
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),
 
1261
                                              comp_int);
 
1262
            valid = gtk_tree_model_iter_next (tm, &i);
 
1263
        }
 
1264
        nlp = new_list;
 
1265
        olp = old_pos_l;
 
1266
        while (nlp && olp)
 
1267
        {
 
1268
            GList *old_link;
 
1269
            guint position = GPOINTER_TO_INT(olp->data);
 
1270
 
 
1271
            /* if position == -1 one of the tracks in the track view
 
1272
               could not be found in the selected playlist -> stop! */
 
1273
            if (position == -1)
 
1274
            {
 
1275
                g_warning ("Programming error: tm_rows_reordered_callback: track in track view was not in selected playlist\n");
 
1276
                g_return_if_reached ();
 
1277
            }
 
1278
            old_link = g_list_nth (current_pl->members, position);
 
1279
            /* replace old track with new track */
 
1280
            if (old_link->data != nlp->data)
 
1281
            {
 
1282
                old_link->data = nlp->data;
 
1283
                changed = TRUE;
 
1284
            }
 
1285
            /* next */
 
1286
            nlp = nlp->next;
 
1287
            olp = olp->next;
 
1288
        }
 
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
 
1292
           sort tabs */
 
1293
        if (changed)
 
1294
        {
 
1295
            data_changed (itdb);
 
1296
            st_adopt_order_in_playlist ();
 
1297
        }
 
1298
    }
 
1299
}
 
1300
 
 
1301
 
 
1302
static void
 
1303
on_trackids_list_foreach ( GtkTreeModel *tm, GtkTreePath *tp,
 
1304
                           GtkTreeIter *i, gpointer data)
 
1305
{
 
1306
    Track *tr = NULL;
 
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;
 
1312
}
 
1313
 
 
1314
 
 
1315
/* return a list containing the track IDs of all tracks currently being
 
1316
   selected */
 
1317
GList *
 
1318
tm_get_selected_trackids(void)
 
1319
{
 
1320
    GList *result = NULL;
 
1321
    GtkTreeSelection *ts = NULL;
 
1322
 
 
1323
    if((ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(track_treeview))))
 
1324
    {
 
1325
        gtk_tree_selection_selected_foreach(ts, on_trackids_list_foreach,
 
1326
                                            &result);
 
1327
    }
 
1328
    return(result);
 
1329
}
 
1330
 
 
1331
/* return a list containing the track IDs of all tracks currently being
 
1332
   displayed */
 
1333
GList *
 
1334
tm_get_all_trackids(void)
 
1335
{
 
1336
    gboolean
 
1337
        on_all_trackids_list_foreach (GtkTreeModel *tm, GtkTreePath *tp,
 
1338
                                      GtkTreeIter *i, gpointer data)
 
1339
        {
 
1340
            on_trackids_list_foreach (tm, tp, i, data);
 
1341
            return FALSE;
 
1342
        }
 
1343
    GList *result = NULL;
 
1344
    GtkTreeModel *model;
 
1345
 
 
1346
    if((model = gtk_tree_view_get_model (track_treeview)))
 
1347
    {
 
1348
        gtk_tree_model_foreach(model, on_all_trackids_list_foreach,
 
1349
                               &result);
 
1350
    }
 
1351
    return(result);
 
1352
}
 
1353
 
 
1354
static void
 
1355
on_tracks_list_foreach ( GtkTreeModel *tm, GtkTreePath *tp,
 
1356
                         GtkTreeIter *i, gpointer data)
 
1357
{
 
1358
    Track *tr = NULL;
 
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;
 
1364
}
 
1365
 
 
1366
 
 
1367
/* return a list containing pointers to all tracks currently being
 
1368
   selected */
 
1369
GList *
 
1370
tm_get_selected_tracks(void)
 
1371
{
 
1372
    GList *result = NULL;
 
1373
    GtkTreeSelection *ts = NULL;
 
1374
 
 
1375
    if((ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(track_treeview))))
 
1376
    {
 
1377
        gtk_tree_selection_selected_foreach(ts, on_tracks_list_foreach,
 
1378
                                            &result);
 
1379
    }
 
1380
    return(result);
 
1381
}
 
1382
 
 
1383
 
 
1384
 
 
1385
    /* used by tm_get_all_tracks */
 
1386
    static gboolean on_all_tracks_list_foreach (GtkTreeModel *tm,
 
1387
                                                GtkTreePath *tp,
 
1388
                                                GtkTreeIter *i,
 
1389
                                                gpointer data)
 
1390
    {
 
1391
        on_tracks_list_foreach (tm, tp, i, data);
 
1392
        return FALSE;
 
1393
    }
 
1394
 
 
1395
 
 
1396
/* return a list containing pointers to all tracks currently being
 
1397
   displayed. You must g_list_free() the list after use. */
 
1398
GList *
 
1399
tm_get_all_tracks(void)
 
1400
{
 
1401
    GList *result = NULL;
 
1402
    GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
 
1403
 
 
1404
    g_return_val_if_fail (model, NULL);
 
1405
 
 
1406
    gtk_tree_model_foreach(model, on_all_tracks_list_foreach,
 
1407
                           &result);
 
1408
    return result;
 
1409
}
 
1410
 
 
1411
 
 
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)
 
1416
{
 
1417
    GtkTreeViewColumn *col;
 
1418
 
 
1419
    if (!track_treeview)  return;
 
1420
 
 
1421
    gtk_tree_view_get_cursor (track_treeview, NULL, &col);
 
1422
    if (col)
 
1423
    {
 
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);
 
1434
    }
 
1435
}
 
1436
 
 
1437
 
 
1438
 
 
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,
 
1442
                             TM_item tm_item)
 
1443
{
 
1444
  gint cmp = 0;
 
1445
  ExtraTrackData *etr1, *etr2;
 
1446
 
 
1447
 
 
1448
  g_return_val_if_fail (track1 && track2, 0);
 
1449
 
 
1450
  switch (tm_item)
 
1451
  {
 
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)));
 
1474
      break;
 
1475
  case TM_COLUMN_TRACK_NR:
 
1476
      cmp = track1->tracks - track2->tracks;
 
1477
      if (cmp == 0) cmp = track1->track_nr - track2->track_nr;
 
1478
      break;
 
1479
  case TM_COLUMN_CD_NR:
 
1480
      cmp = track1->cds - track2->cds;
 
1481
      if (cmp == 0) cmp = track1->cd_nr - track2->cd_nr;
 
1482
      break;
 
1483
  case TM_COLUMN_IPOD_ID:
 
1484
      cmp = track1->id - track2->id;
 
1485
      break;
 
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);
 
1491
      break;
 
1492
  case TM_COLUMN_IPOD_PATH:
 
1493
      cmp = g_utf8_collate (track1->ipod_path, track2->ipod_path);
 
1494
      break;
 
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);
 
1500
      break;
 
1501
  case TM_COLUMN_TRANSFERRED:
 
1502
      if(track1->transferred == track2->transferred)
 
1503
          cmp = 0;
 
1504
      else if(track1->transferred == TRUE)
 
1505
          cmp = 1;
 
1506
      else
 
1507
          cmp = -1;
 
1508
      break;
 
1509
  case TM_COLUMN_COMPILATION:
 
1510
      if(track1->compilation == track2->compilation)
 
1511
          cmp = 0;
 
1512
      else if(track1->compilation == TRUE)
 
1513
          cmp = 1;
 
1514
      else
 
1515
          cmp = -1;
 
1516
      break;
 
1517
  case TM_COLUMN_SIZE:
 
1518
      cmp = track1->size - track2->size;
 
1519
      break;
 
1520
  case TM_COLUMN_TRACKLEN:
 
1521
      cmp = track1->tracklen - track2->tracklen;
 
1522
      break;
 
1523
  case TM_COLUMN_BITRATE:
 
1524
      cmp = track1->bitrate - track2->bitrate;
 
1525
      break;
 
1526
  case TM_COLUMN_SAMPLERATE:
 
1527
      cmp = track1->samplerate - track2->samplerate;
 
1528
      break;
 
1529
  case TM_COLUMN_BPM:
 
1530
      cmp = track1->BPM - track2->BPM;
 
1531
      break;
 
1532
  case TM_COLUMN_PLAYCOUNT:
 
1533
      cmp = track1->playcount - track2->playcount;
 
1534
      break;
 
1535
  case  TM_COLUMN_RATING:
 
1536
      cmp = track1->rating - track2->rating;
 
1537
      break;
 
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)));
 
1544
      break;
 
1545
  case  TM_COLUMN_VOLUME:
 
1546
      cmp = track1->volume - track2->volume;
 
1547
      break;
 
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);
 
1552
      break;
 
1553
  case TM_COLUMN_YEAR:
 
1554
      cmp = track1->year - track2->year;
 
1555
      break;
 
1556
  case TM_COLUMN_SEASON_NR:
 
1557
      cmp = track1->season_nr - track2->season_nr;
 
1558
      break;
 
1559
  case TM_COLUMN_EPISODE_NR:
 
1560
      cmp = track1->episode_nr - track2->episode_nr;
 
1561
      break;
 
1562
  case TM_COLUMN_MEDIA_TYPE:
 
1563
      cmp = track1->mediatype - track2->mediatype;
 
1564
      break;
 
1565
  default:
 
1566
      g_warning ("Programming error: tm_data_compare_func: no sort method for tm_item %d\n", tm_item);
 
1567
      break;
 
1568
  }
 
1569
 
 
1570
  /* implement stable sorting: if two items are the same, revert to
 
1571
     the last relative positition */
 
1572
  if (cmp == 0)
 
1573
  {
 
1574
      etr1 = track1->userdata;
 
1575
      etr2 = track2->userdata;
 
1576
      g_return_val_if_fail (etr1 && etr2, 0);
 
1577
      cmp = etr1->sortindex - etr2->sortindex;
 
1578
  }
 
1579
  return cmp;
 
1580
}
 
1581
 
 
1582
 
 
1583
/* Function used to compare rows with user's search string */
 
1584
gboolean tm_search_equal_func (GtkTreeModel *model,
 
1585
                               gint column,
 
1586
                               const gchar *key,
 
1587
                               GtkTreeIter *iter,
 
1588
                               gpointer search_data)
 
1589
{
 
1590
  Track *track1;
 
1591
  gboolean cmp = 0;
 
1592
  gtk_tree_model_get (model, iter, READOUT_COL, &track1, -1);
 
1593
  switch (column)
 
1594
  {
 
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)),
 
1618
          key) != 0);
 
1619
    break;
 
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:
 
1635
  case TM_COLUMN_BPM:
 
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);
 
1642
      break;
 
1643
  }
 
1644
  return cmp;
 
1645
};
 
1646
 
 
1647
/* Function used to compare two cells during sorting (track view) */
 
1648
gint tm_data_compare_func (GtkTreeModel *model,
 
1649
                        GtkTreeIter *a,
 
1650
                        GtkTreeIter *b,
 
1651
                        gpointer user_data)
 
1652
{
 
1653
  Track *track1;
 
1654
  Track *track2;
 
1655
  gint column;
 
1656
  GtkSortType order;
 
1657
  gint result;
 
1658
 
 
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)
 
1663
      return 0;
 
1664
 
 
1665
  result = tm_data_compare (track1, track2, column);
 
1666
  return result;
 
1667
}
 
1668
 
 
1669
 
 
1670
/* set/read the counter used to remember how often the sort column has
 
1671
   been clicked.
 
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)
 
1676
{
 
1677
    static gint cnt = 0;
 
1678
 
 
1679
    if (inc <0)
 
1680
    {
 
1681
        cnt = 0;
 
1682
    }
 
1683
    else
 
1684
    {
 
1685
        cnt += inc;
 
1686
    }   
 
1687
    return cnt;
 
1688
}
 
1689
 
 
1690
 
 
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 */
 
1694
 
 
1695
void tm_adopt_order_in_sorttab (void)
 
1696
{
 
1697
    if (prefs_get_int("tm_sort") == SORT_NONE)
 
1698
    {
 
1699
        GList *gl, *tracks = NULL;
 
1700
 
 
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);
 
1708
    }
 
1709
}
 
1710
 
 
1711
 
 
1712
/* redisplay the contents of the track view in it's unsorted order */
 
1713
static void tm_unsort (void)
 
1714
{
 
1715
    if (track_treeview)
 
1716
    {
 
1717
        GtkTreeModel *model= gtk_tree_view_get_model (track_treeview);
 
1718
 
 
1719
        prefs_set_int("tm_sort", SORT_NONE);
 
1720
        if (!BROKEN_GTK_TREE_SORT)
 
1721
        {
 
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 ();
 
1731
        }
 
1732
        else
 
1733
        {
 
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);
 
1735
        }
 
1736
        tm_sort_counter (-1);
 
1737
    }
 
1738
}
 
1739
 
 
1740
 
 
1741
static void tm_set_search_column (gint newcol)
 
1742
{
 
1743
/*     printf ("track_treeview: %p, col: %d\n", track_treeview, newcol); */
 
1744
    g_return_if_fail (track_treeview);
 
1745
 
 
1746
    gtk_tree_view_set_search_column (GTK_TREE_VIEW (track_treeview),
 
1747
                                       newcol);
 
1748
    switch (newcol)
 
1749
    {
 
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);
 
1772
        break;
 
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:
 
1788
    case TM_COLUMN_BPM:
 
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);
 
1795
        break;
 
1796
    }
 
1797
    prefs_set_int (TM_PREFS_SEARCH_COLUMN, newcol);
 
1798
}
 
1799
 
 
1800
 
 
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,
 
1804
                                    gpointer user_data)
 
1805
{
 
1806
    static gint lastcol = -1; /* which column was sorted last time? */
 
1807
    gchar *buf;
 
1808
    gint newcol;
 
1809
    GtkSortType order;
 
1810
    GList *tracks, *gl;
 
1811
    gint32 i, inc;
 
1812
 
 
1813
    gtk_tree_sortable_get_sort_column_id (ts, &newcol, &order);
 
1814
 
 
1815
/*     printf ("scc -- col: %d, order: %d\n", newcol, order);  */
 
1816
 
 
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;
 
1821
    else
 
1822
        string_compare_func = compare_string;
 
1823
    g_free (buf);
 
1824
 
 
1825
    /* don't do anything if no sort column is set */
 
1826
    if (newcol == -2)
 
1827
    {
 
1828
        lastcol = newcol;
 
1829
        return;
 
1830
    }
 
1831
 
 
1832
    if (newcol != lastcol)
 
1833
    {
 
1834
        tm_sort_counter (-1);
 
1835
        lastcol = newcol;
 
1836
    }
 
1837
 
 
1838
    if (tm_sort_counter (1) >= 3)
 
1839
    { /* after clicking three times, reset sort order! */
 
1840
        tm_unsort ();  /* also resets sort counter */
 
1841
    }
 
1842
    else
 
1843
    {
 
1844
        prefs_set_int("tm_sort", order);
 
1845
    }
 
1846
    prefs_set_int("tm_sortcol", newcol);
 
1847
 
 
1848
    tm_set_search_column (newcol);
 
1849
 
 
1850
    if(prefs_get_int("tm_autostore"))  tm_rows_reordered ();
 
1851
    sort_window_update ();
 
1852
 
 
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)
 
1859
    {
 
1860
        i=0;
 
1861
        inc = 1;
 
1862
    }
 
1863
    else
 
1864
    {
 
1865
        i=-1;
 
1866
        inc = -1;
 
1867
    }
 
1868
    for (gl=tracks; gl; gl=gl->next)
 
1869
    {
 
1870
        ExtraTrackData *etr;
 
1871
        Track *tr = gl->data;
 
1872
        g_return_if_fail (tr);
 
1873
        etr = tr->userdata;
 
1874
        g_return_if_fail (etr);
 
1875
        etr->sortindex = i;
 
1876
        i+=inc;
 
1877
    }
 
1878
    g_list_free (tracks);
 
1879
}
 
1880
 
 
1881
 
 
1882
void tm_sort (TM_item col, GtkSortType order)
 
1883
{
 
1884
    if (track_treeview)
 
1885
    {
 
1886
        GtkTreeModel *model= gtk_tree_view_get_model (track_treeview);
 
1887
        if (order != SORT_NONE)
 
1888
        {
 
1889
            gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
 
1890
                                                  col, order);
 
1891
        }
 
1892
        else
 
1893
        { /* only unsort if treeview is sorted */
 
1894
            gint column;
 
1895
            GtkSortType order;
 
1896
            if (gtk_tree_sortable_get_sort_column_id
 
1897
                (GTK_TREE_SORTABLE (model), &column, &order))
 
1898
            {
 
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 */
 
1902
                if (column != -2)
 
1903
                    tm_unsort ();
 
1904
            }
 
1905
        }
 
1906
    }
 
1907
}
 
1908
 
 
1909
 
 
1910
 
 
1911
 
 
1912
 
 
1913
/* Adds the columns to our track_treeview */
 
1914
static GtkTreeViewColumn *tm_add_column (TM_item tm_item, gint pos)
 
1915
{
 
1916
  GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
 
1917
  GtkTreeViewColumn *col = NULL;
 
1918
  const gchar *text;
 
1919
  GtkCellRenderer *renderer = NULL;  /* default */
 
1920
  GtkTooltips *tt;
 
1921
 
 
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);
 
1925
 
 
1926
  g_return_val_if_fail (tm_item >= 0, NULL);
 
1927
  g_return_val_if_fail (tm_item < TM_NUM_COLUMNS, NULL);
 
1928
 
 
1929
  text = gettext (get_tm_string (tm_item));
 
1930
 
 
1931
  g_return_val_if_fail (text, NULL);
 
1932
 
 
1933
  col = gtk_tree_view_column_new ();
 
1934
 
 
1935
  switch (tm_item)
 
1936
  {
 
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",
 
1941
                         (gint *)tm_item);
 
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,
 
1947
                                               NULL, NULL);
 
1948
      renderer = NULL;
 
1949
      break;
 
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:
 
1959
  case TM_COLUMN_BPM:
 
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:
 
1982
      break;
 
1983
  /* for some column names we want to use shorter alternatives to
 
1984
     get_tm_string() */
 
1985
  case TM_COLUMN_RATING:
 
1986
      text = _("Rtng");
 
1987
      break;
 
1988
  case TM_COLUMN_TRACK_NR:
 
1989
      text = _("#");
 
1990
      break;
 
1991
  case TM_COLUMN_CD_NR:
 
1992
      text = _("CD");
 
1993
      break;
 
1994
  case TM_COLUMN_IPOD_ID:
 
1995
      text = _("ID");
 
1996
      break;
 
1997
  case TM_COLUMN_TRANSFERRED:
 
1998
      text = _("Trnsfrd");
 
1999
      renderer = gtk_cell_renderer_toggle_new ();
 
2000
      break;
 
2001
  case TM_COLUMN_COMPILATION:
 
2002
      text = _("Cmpl");
 
2003
      renderer = gtk_cell_renderer_toggle_new ();
 
2004
      g_signal_connect (G_OBJECT (renderer), "toggled",
 
2005
                        G_CALLBACK (tm_cell_toggled), model);
 
2006
      break;
 
2007
  case TM_COLUMN_TRACKLEN:
 
2008
      text = _("Time");
 
2009
      break;
 
2010
  case TM_COLUMN_PLAYCOUNT:
 
2011
      text = _("Plycnt");
 
2012
      break;
 
2013
  case TM_COLUMN_TIME_PLAYED:
 
2014
      text = _("Played");
 
2015
      break;
 
2016
  case TM_COLUMN_TIME_MODIFIED:
 
2017
      text = _("Modified");
 
2018
      break;
 
2019
  case TM_COLUMN_TIME_ADDED:
 
2020
      text = _("Added");
 
2021
      break;
 
2022
  case TM_COLUMN_TIME_RELEASED:
 
2023
      text = _("Released");
 
2024
      break;
 
2025
  case TM_COLUMN_YEAR:
 
2026
      text = _("Year");
 
2027
      break;
 
2028
  case TM_COLUMN_VOLUME:
 
2029
      text = _("Vol.");
 
2030
      break;
 
2031
  case TM_COLUMN_SOUNDCHECK:
 
2032
      text = _("Sndchk.");
 
2033
      break;
 
2034
  case TM_NUM_COLUMNS:
 
2035
      g_return_val_if_reached (NULL);
 
2036
      break;
 
2037
  }
 
2038
 
 
2039
  if (!renderer)
 
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);
 
2045
  }
 
2046
 
 
2047
  g_object_set_data (G_OBJECT (renderer), "column",
 
2048
                     (gint *)tm_item);
 
2049
 
 
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;
 
2065
 
 
2066
  if (pos != -1)
 
2067
  {
 
2068
      gtk_tree_view_column_set_visible (col,
 
2069
                                        prefs_get_int_index("col_visible", tm_item));
 
2070
  }
 
2071
  if (get_tm_tooltip (tm_item))
 
2072
      gtk_tooltips_set_tip (tt, col->button, 
 
2073
                            gettext (get_tm_tooltip (tm_item)),
 
2074
                            NULL);
 
2075
  return col;
 
2076
}
 
2077
 
 
2078
 
 
2079
/* Adds the columns to our track_treeview */
 
2080
static void tm_add_columns (void)
 
2081
{
 
2082
    gint i;
 
2083
 
 
2084
    for (i=0; i<TM_NUM_COLUMNS; ++i)
 
2085
    {
 
2086
        tm_add_column (prefs_get_int_index("col_order", i), -1);
 
2087
    }
 
2088
    tm_show_preferred_columns();
 
2089
}
 
2090
 
 
2091
static void tm_select_current_position (gint x, gint y)
 
2092
{
 
2093
    if (track_treeview)
 
2094
    {
 
2095
        GtkTreePath *path;
 
2096
 
 
2097
        gtk_tree_view_get_path_at_pos (track_treeview,
 
2098
                                       x, y, &path, NULL, NULL, NULL);
 
2099
        if (path)
 
2100
        {
 
2101
            GtkTreeSelection *ts = gtk_tree_view_get_selection (track_treeview);
 
2102
            gtk_tree_selection_select_path (ts, path);
 
2103
            gtk_tree_path_free (path);
 
2104
        }
 
2105
    }
 
2106
}
 
2107
 
 
2108
static gboolean
 
2109
tm_button_press_event(GtkWidget *w, GdkEventButton *e, gpointer data)
 
2110
{
 
2111
    if(w && e)
 
2112
    {
 
2113
        switch(e->button)
 
2114
        {
 
2115
        case 1:
 
2116
/*
 
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),
 
2120
                    e->x, e->y);
 
2121
*/
 
2122
            break;
 
2123
        case 3:
 
2124
            tm_select_current_position (e->x, e->y);
 
2125
            tm_context_menu_init ();
 
2126
            return TRUE;
 
2127
        default:
 
2128
            break;
 
2129
        }
 
2130
    }
 
2131
    return(FALSE);
 
2132
}
 
2133
 
 
2134
/* called when the track selection changes */
 
2135
static void
 
2136
tm_selection_changed_event(GtkTreeSelection *selection, gpointer data)
 
2137
{
 
2138
    GtkTreeView *treeview = gtk_tree_selection_get_tree_view (selection);
 
2139
    GtkTreePath *path;
 
2140
    GtkTreeViewColumn *column;
 
2141
    TM_item col_id;
 
2142
 
 
2143
    gtk_tree_view_get_cursor (treeview, &path, &column);
 
2144
    if (path)
 
2145
    {
 
2146
        col_id = tm_lookup_col_id (column);
 
2147
        if (col_id != -1)  tm_set_search_column (col_id);
 
2148
    }
 
2149
    info_update_track_view_selected ();
 
2150
    
 
2151
    /* update the coverart display */
 
2152
    GList *selected = display_get_selection (prefs_get_int("sort_tab_num"));
 
2153
    if (selected != NULL)
 
2154
    {
 
2155
        Track *track = selected->data;
 
2156
        if(track != NULL)
 
2157
                coverart_select_cover (track);
 
2158
    }
 
2159
}
 
2160
 
 
2161
 
 
2162
/* Create tracks treeview */
 
2163
void tm_create_treeview (void)
 
2164
{
 
2165
  GtkTreeModel *model = NULL;
 
2166
  GtkWidget *track_window = gtkpod_xml_get_widget (main_window_xml, "track_window");
 
2167
  GtkTreeSelection *select;
 
2168
  gint col;
 
2169
  GtkWidget *stv = gtk_tree_view_new ();
 
2170
 
 
2171
  /* create tree view */
 
2172
  if (track_treeview)
 
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));
 
2177
  }
 
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,
 
2189
                                       NULL,
 
2190
                                       NULL);
 
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),
 
2196
                    NULL);
 
2197
  tm_add_columns ();
 
2198
 
 
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))); */
 
2211
 
 
2212
 
 
2213
  gtk_drag_source_set (GTK_WIDGET (track_treeview),
 
2214
                       GDK_BUTTON1_MASK,
 
2215
                       tm_drag_types, TGNR (tm_drag_types),
 
2216
                       GDK_ACTION_COPY|GDK_ACTION_MOVE);
 
2217
  gtk_drag_dest_set (GTK_WIDGET (track_treeview),
 
2218
                     0,
 
2219
                     tm_drop_types, TGNR (tm_drop_types),
 
2220
                     GDK_ACTION_COPY|GDK_ACTION_MOVE);
 
2221
 
 
2222
 
 
2223
  g_signal_connect ((gpointer) track_treeview, "drag-begin",
 
2224
                    G_CALLBACK (tm_drag_begin),
 
2225
                    NULL);
 
2226
 
 
2227
  g_signal_connect ((gpointer) track_treeview, "drag-data-delete",
 
2228
                    G_CALLBACK (tm_drag_data_delete),
 
2229
                    NULL);
 
2230
 
 
2231
  g_signal_connect ((gpointer) track_treeview, "drag-data-get",
 
2232
                    G_CALLBACK (tm_drag_data_get),
 
2233
                    NULL);
 
2234
 
 
2235
  g_signal_connect ((gpointer) track_treeview, "drag-data-received",
 
2236
                    G_CALLBACK (tm_drag_data_received),
 
2237
                    NULL);
 
2238
 
 
2239
  g_signal_connect ((gpointer) track_treeview, "drag-drop",
 
2240
                    G_CALLBACK (tm_drag_drop),
 
2241
                    NULL);
 
2242
 
 
2243
  g_signal_connect ((gpointer) track_treeview, "drag-end",
 
2244
                    G_CALLBACK (tm_drag_end),
 
2245
                    NULL);
 
2246
 
 
2247
  g_signal_connect ((gpointer) track_treeview, "drag-leave",
 
2248
                    G_CALLBACK (tm_drag_leave),
 
2249
                    NULL);
 
2250
 
 
2251
  g_signal_connect ((gpointer) track_treeview, "drag-motion",
 
2252
                    G_CALLBACK (tm_drag_motion),
 
2253
                    NULL);
 
2254
 
 
2255
  g_signal_connect_after ((gpointer) stv, "key_release_event",
 
2256
                          G_CALLBACK (on_track_treeview_key_release_event),
 
2257
                          NULL);
 
2258
  g_signal_connect ((gpointer) track_treeview, "button-press-event",
 
2259
                    G_CALLBACK (tm_button_press_event),
 
2260
                    NULL);
 
2261
  g_signal_connect (G_OBJECT (model), "sort-column-changed",
 
2262
                    G_CALLBACK (tm_sort_column_changed),
 
2263
                    (gpointer)0);
 
2264
 
 
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))
 
2269
  {
 
2270
      tm_set_search_column (col);
 
2271
  }
 
2272
  else
 
2273
  {   /* reasonable default */
 
2274
      tm_set_search_column (TM_COLUMN_TITLE);
 
2275
  }
 
2276
}
 
2277
 
 
2278
 
 
2279
void
 
2280
tm_show_preferred_columns(void)
 
2281
{
 
2282
    GtkTreeViewColumn *tvc = NULL;
 
2283
    gboolean visible;
 
2284
    gint i;
 
2285
 
 
2286
    for (i=0; i<TM_NUM_COLUMNS; ++i)
 
2287
    {
 
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);
 
2292
    }
 
2293
}
 
2294
 
 
2295
 
 
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)
 
2300
{
 
2301
    gint i;
 
2302
    GtkTreeViewColumn *col;
 
2303
    gint col_width;
 
2304
 
 
2305
    /* column widths */
 
2306
    for (i=0; i<TM_NUM_COLUMNS; ++i)
 
2307
    {
 
2308
        col = tm_columns [i];
 
2309
        if (col)
 
2310
        {
 
2311
    col_width = gtk_tree_view_column_get_width (col);
 
2312
    
 
2313
    if (col_width > 0)
 
2314
            prefs_set_int_index ("tm_col_width", i, col_width);
 
2315
    else
 
2316
      prefs_set_int_index ("tm_col_width", i, 80);
 
2317
   
 
2318
        }
 
2319
    }
 
2320
}
 
2321
 
 
2322
 
 
2323
/* get the TM_ITEM column id for @column. Returns -1 if column could
 
2324
   not be found */
 
2325
static TM_item tm_lookup_col_id (GtkTreeViewColumn *column)
 
2326
{
 
2327
    gint i;
 
2328
 
 
2329
    if (column)
 
2330
    {
 
2331
        for (i=0; i<TM_NUM_COLUMNS; ++i)
 
2332
        {
 
2333
            if (column == tm_columns[i]) return i;
 
2334
        }
 
2335
    }
 
2336
    return -1;
 
2337
}
 
2338
 
 
2339
 
 
2340
/* Compare function to avoid sorting */
 
2341
static gint tm_nosort_comp (GtkTreeModel *model,
 
2342
                            GtkTreeIter *a,
 
2343
                            GtkTreeIter *b,
 
2344
                            gpointer user_data)
 
2345
{
 
2346
    return 0;
 
2347
}
 
2348
 
 
2349
 
 
2350
/* Disable sorting of the view during lengthy updates. */
 
2351
/* @enable: TRUE: enable, FALSE: disable */
 
2352
void tm_enable_disable_view_sort (gboolean enable)
 
2353
{
 
2354
    static gint disable_count = 0;
 
2355
 
 
2356
    if (enable)
 
2357
    {
 
2358
        disable_count--;
 
2359
        if (disable_count < 0)
 
2360
            fprintf (stderr, "Programming error: disable_count < 0\n");
 
2361
        if (disable_count == 0 && track_treeview)
 
2362
        {
 
2363
            if (prefs_get_int("tm_sort") != SORT_NONE)
 
2364
            {
 
2365
                /* Re-enable sorting */
 
2366
                GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
 
2367
                if (BROKEN_GTK_TREE_SORT)
 
2368
                {
 
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);
 
2373
                }
 
2374
                else
 
2375
                {
 
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"));
 
2380
                }
 
2381
            }
 
2382
        }
 
2383
    }
 
2384
    else
 
2385
    {
 
2386
        if (disable_count == 0 && track_treeview)
 
2387
        {
 
2388
            if (prefs_get_int("tm_sort") != SORT_NONE)
 
2389
            {
 
2390
                /* Disable sorting */
 
2391
                GtkTreeModel *model = gtk_tree_view_get_model (track_treeview);
 
2392
                if (BROKEN_GTK_TREE_SORT)
 
2393
                {
 
2394
                    gtk_tree_sortable_set_sort_func (
 
2395
                        GTK_TREE_SORTABLE (model),
 
2396
                        prefs_get_int("tm_sortcol"),
 
2397
                        tm_nosort_comp, NULL, NULL);
 
2398
                }
 
2399
                else
 
2400
                {
 
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"));
 
2405
                }
 
2406
            }
 
2407
        }
 
2408
        disable_count++;
 
2409
    }
 
2410
}
 
2411
 
 
2412
 
 
2413
 
 
2414
/* Callback for adding tracks within tm_add_filelist */
 
2415
void tm_addtrackfunc (Playlist *plitem, Track *track, gpointer data)
 
2416
{
 
2417
    struct asf_data *asf = (struct asf_data *)data;
 
2418
    GtkTreeModel *model;
 
2419
    GtkTreeIter new_iter;
 
2420
 
 
2421
    model = gtk_tree_view_get_model (track_treeview);
 
2422
 
 
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);
 
2427
 
 
2428
    /* create new iter in track view */
 
2429
    switch (asf->pos)
 
2430
    {
 
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);
 
2436
        break;
 
2437
    case GTK_TREE_VIEW_DROP_BEFORE:
 
2438
        gtk_list_store_insert_before (GTK_LIST_STORE (model),
 
2439
                                      &new_iter, asf->to_iter);
 
2440
        break;
 
2441
    }
 
2442
    /* set the iter */
 
2443
    tm_add_track_to_track_model (track, &new_iter);
 
2444
}
 
2445
 
 
2446
 
 
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)
 
2451
*/
 
2452
gboolean tm_add_filelist (gchar *data,
 
2453
                          GtkTreePath *path,
 
2454
                          GtkTreeViewDropPosition pos)
 
2455
{
 
2456
    GtkTreeModel *model;
 
2457
    gchar *buf = NULL, *use_data;
 
2458
    gchar **files, **filep;
 
2459
    Playlist *current_playlist = pm_get_selected_playlist ();
 
2460
 
 
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);
 
2464
 
 
2465
    model = gtk_tree_view_get_model (track_treeview);
 
2466
    g_return_val_if_fail (model, FALSE);
 
2467
    if (path)
 
2468
    {
 
2469
    }
 
2470
 
 
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);
 
2477
        filep = files;
 
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)
 
2484
        {
 
2485
            --filep;
 
2486
            g_strlcat (buf, *filep, len);
 
2487
            g_strlcat (buf, "\n", len);
 
2488
        }
 
2489
        g_strfreev (files);
 
2490
        use_data = buf;
 
2491
    }
 
2492
    else
 
2493
    {
 
2494
        use_data = data;
 
2495
    }
 
2496
 
 
2497
/*     printf("filelist: (%s) -> (%s)\n", data, use_data); */
 
2498
    /* initialize add-track-struct */
 
2499
    if (path)
 
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;
 
2506
        asf.pos = pos;
 
2507
        add_text_plain_to_playlist (current_playlist->itdb,
 
2508
                                    current_playlist, use_data, 0,
 
2509
                                    tm_addtrackfunc, &asf);
 
2510
    }
 
2511
    else
 
2512
    {   /* add to the end */
 
2513
        add_text_plain_to_playlist (current_playlist->itdb,
 
2514
                                    current_playlist, use_data, 0,
 
2515
                                    NULL, NULL);
 
2516
    }
 
2517
    tm_rows_reordered ();
 
2518
    C_FREE (buf);
 
2519
    return TRUE;
 
2520
}