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