Updated Finnish translation
[rhythmbox.git] / widgets / rb-entry-view.c
blob612ebb5198001973e792f06393189792f5860a34
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * arch-tag: Implementation of widget to display RhythmDB entries
5 * Copyright (C) 2003 Colin Walters <walters@verbum.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "config.h"
25 #include <string.h>
26 #include <stdlib.h>
27 #include <math.h>
28 #include <time.h>
30 #include <glib/gi18n.h>
31 #include <gtk/gtk.h>
32 #include <libgnomevfs/gnome-vfs-utils.h>
34 #include "rb-tree-dnd.h"
35 #include "rb-entry-view.h"
36 #include "rb-dialog.h"
37 #include "rb-debug.h"
38 #include "rb-util.h"
39 #include "rhythmdb.h"
40 #include "rhythmdb-query-model.h"
41 #include "rb-cell-renderer-pixbuf.h"
42 #include "rb-cell-renderer-rating.h"
43 #include "rb-stock-icons.h"
44 #include "rb-preferences.h"
45 #include "eel-gconf-extensions.h"
46 #include "rb-shell-player.h"
47 #include "rb-cut-and-paste-code.h"
49 static const GtkTargetEntry rb_entry_view_drag_types[] = {{ "text/uri-list", 0, 0 }};
51 struct RBEntryViewColumnSortData
53 GCompareDataFunc func;
54 gpointer data;
57 static void rb_entry_view_class_init (RBEntryViewClass *klass);
58 static void rb_entry_view_init (RBEntryView *view);
59 static GObject *rb_entry_view_constructor (GType type, guint n_construct_properties,
60 GObjectConstructParam *construct_properties);
61 static void rb_entry_view_finalize (GObject *object);
62 static void rb_entry_view_set_property (GObject *object,
63 guint prop_id,
64 const GValue *value,
65 GParamSpec *pspec);
66 static void rb_entry_view_get_property (GObject *object,
67 guint prop_id,
68 GValue *value,
69 GParamSpec *pspec);
70 static void rb_entry_view_selection_changed_cb (GtkTreeSelection *selection,
71 RBEntryView *view);
72 static void rb_entry_view_grab_focus (GtkWidget *widget);
73 static void rb_entry_view_row_activated_cb (GtkTreeView *treeview,
74 GtkTreePath *path,
75 GtkTreeViewColumn *column,
76 RBEntryView *view);
77 static void rb_entry_view_row_inserted_cb (GtkTreeModel *model,
78 GtkTreePath *path,
79 GtkTreeIter *iter,
80 RBEntryView *view);
81 static void rb_entry_view_row_deleted_cb (GtkTreeModel *model,
82 GtkTreePath *path,
83 RBEntryView *view);
84 static void rb_entry_view_rows_reordered_cb (GtkTreeModel *model,
85 GtkTreePath *path,
86 GtkTreeIter *iter,
87 gint *order,
88 RBEntryView *view);
89 static void rb_entry_view_sync_columns_visible (RBEntryView *view);
90 static void rb_entry_view_columns_config_changed_cb (GConfClient* client,
91 guint cnxn_id,
92 GConfEntry *entry,
93 gpointer user_data);
94 static void rb_entry_view_sort_key_changed_cb (GConfClient* client,
95 guint cnxn_id,
96 GConfEntry *entry,
97 gpointer user_data);
98 static void rb_entry_view_rated_cb (RBCellRendererRating *cellrating,
99 const char *path,
100 double rating,
101 RBEntryView *view);
102 static void rb_entry_view_pixbuf_clicked_cb (RBEntryView *view,
103 const char *path,
104 RBCellRendererPixbuf *cellpixbuf);
105 static gboolean rb_entry_view_button_press_cb (GtkTreeView *treeview,
106 GdkEventButton *event,
107 RBEntryView *view);
108 static gboolean rb_entry_view_popup_menu_cb (GtkTreeView *treeview,
109 RBEntryView *view);
110 static void rb_entry_view_entry_is_visible (RBEntryView *view, RhythmDBEntry *entry,
111 gboolean *realized, gboolean *visible,
112 GtkTreeIter *iter);
113 static void rb_entry_view_scroll_to_iter (RBEntryView *view,
114 GtkTreeIter *iter);
115 static gboolean rb_entry_view_emit_row_changed (RBEntryView *view,
116 RhythmDBEntry *entry);
117 static void rb_entry_view_playing_song_changed (RBShellPlayer *player,
118 RhythmDBEntry *entry,
119 RBEntryView *view);
121 struct RBEntryViewPrivate
123 RhythmDB *db;
124 RBShellPlayer *shell_player;
126 RhythmDBQueryModel *model;
128 GtkWidget *treeview;
129 GtkTreeSelection *selection;
131 RBEntryViewState playing_state;
132 RhythmDBQueryModel *playing_model;
133 RhythmDBEntry *playing_entry;
134 gboolean playing_entry_in_view;
135 guint selection_changed_id;
137 gboolean is_drag_source;
138 gboolean is_drag_dest;
140 GdkPixbuf *playing_pixbuf;
141 GdkPixbuf *paused_pixbuf;
142 GdkPixbuf *error_pixbuf;
144 char *sorting_key;
145 guint sorting_gconf_notification_id;
146 GtkTreeViewColumn *sorting_column;
147 gint sorting_order;
148 char *sorting_column_name;
150 gboolean have_selection, have_complete_selection;
152 GHashTable *column_key_map;
154 guint gconf_notification_id;
155 GHashTable *propid_column_map;
156 GHashTable *column_sort_data_map;
159 #define RB_ENTRY_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_ENTRY_VIEW, RBEntryViewPrivate))
161 enum
163 ENTRY_ADDED,
164 ENTRY_DELETED,
165 ENTRIES_REPLACED,
166 SELECTION_CHANGED,
167 ENTRY_ACTIVATED,
168 SHOW_POPUP,
169 HAVE_SEL_CHANGED,
170 SORT_ORDER_CHANGED,
171 LAST_SIGNAL
174 enum
176 PROP_0,
177 PROP_DB,
178 PROP_SHELL_PLAYER,
179 PROP_MODEL,
180 PROP_SORTING_KEY,
181 PROP_IS_DRAG_SOURCE,
182 PROP_IS_DRAG_DEST,
183 PROP_PLAYING_STATE
186 G_DEFINE_TYPE (RBEntryView, rb_entry_view, GTK_TYPE_SCROLLED_WINDOW)
188 static guint rb_entry_view_signals[LAST_SIGNAL] = { 0 };
190 static GQuark rb_entry_view_column_always_visible;
192 static gboolean
193 type_ahead_search_func (GtkTreeModel *model,
194 gint column,
195 const gchar *key,
196 GtkTreeIter *iter,
197 gpointer search_data)
199 RhythmDBEntry *entry;
200 gchar *folded;
201 const gchar *entry_folded;
202 gboolean res;
204 gtk_tree_model_get (model, iter, 0, &entry, -1);
205 folded = rb_search_fold (key);
206 entry_folded = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE_FOLDED);
207 rhythmdb_entry_unref (entry);
209 if (entry_folded == NULL || folded == NULL)
210 return 1;
212 res = (strstr (entry_folded, folded) == NULL);
213 g_free (folded);
215 return res;
218 static void
219 rb_entry_view_class_init (RBEntryViewClass *klass)
221 GObjectClass *object_class = G_OBJECT_CLASS (klass);
222 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
224 object_class->finalize = rb_entry_view_finalize;
225 object_class->constructor = rb_entry_view_constructor;
227 object_class->set_property = rb_entry_view_set_property;
228 object_class->get_property = rb_entry_view_get_property;
230 widget_class->grab_focus = rb_entry_view_grab_focus;
232 g_object_class_install_property (object_class,
233 PROP_DB,
234 g_param_spec_object ("db",
235 "RhythmDB",
236 "RhythmDB database",
237 RHYTHMDB_TYPE,
238 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
239 g_object_class_install_property (object_class,
240 PROP_SHELL_PLAYER,
241 g_param_spec_object ("shell-player",
242 "RBShellPlayer",
243 "RBShellPlayer object",
244 RB_TYPE_SHELL_PLAYER,
245 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
246 g_object_class_install_property (object_class,
247 PROP_MODEL,
248 g_param_spec_object ("model",
249 "RhythmDBQueryModel",
250 "RhythmDBQueryModel",
251 RHYTHMDB_TYPE_QUERY_MODEL,
252 G_PARAM_READWRITE));
253 g_object_class_install_property (object_class,
254 PROP_SORTING_KEY,
255 g_param_spec_string ("sort-key",
256 "sorting key",
257 "sorting key",
259 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
260 g_object_class_install_property (object_class,
261 PROP_IS_DRAG_SOURCE,
262 g_param_spec_boolean ("is-drag-source",
263 "is drag source",
264 "whether or not this is a drag source",
265 FALSE,
266 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
267 g_object_class_install_property (object_class,
268 PROP_IS_DRAG_DEST,
269 g_param_spec_boolean ("is-drag-dest",
270 "is drag dest",
271 "whether or not this is a drag dest",
272 FALSE,
273 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
274 g_object_class_install_property (object_class,
275 PROP_PLAYING_STATE,
276 g_param_spec_int ("playing-state",
277 "playing state",
278 "playback state for this entry view",
279 RB_ENTRY_VIEW_NOT_PLAYING,
280 RB_ENTRY_VIEW_PAUSED,
281 RB_ENTRY_VIEW_NOT_PLAYING,
282 G_PARAM_READWRITE));
283 rb_entry_view_signals[ENTRY_ADDED] =
284 g_signal_new ("entry-added",
285 G_OBJECT_CLASS_TYPE (object_class),
286 G_SIGNAL_RUN_LAST,
287 G_STRUCT_OFFSET (RBEntryViewClass, entry_added),
288 NULL, NULL,
289 g_cclosure_marshal_VOID__BOXED,
290 G_TYPE_NONE,
292 RHYTHMDB_TYPE_ENTRY);
293 rb_entry_view_signals[ENTRY_DELETED] =
294 g_signal_new ("entry-deleted",
295 G_OBJECT_CLASS_TYPE (object_class),
296 G_SIGNAL_RUN_LAST,
297 G_STRUCT_OFFSET (RBEntryViewClass, entry_deleted),
298 NULL, NULL,
299 g_cclosure_marshal_VOID__BOXED,
300 G_TYPE_NONE,
302 RHYTHMDB_TYPE_ENTRY);
303 rb_entry_view_signals[ENTRIES_REPLACED] =
304 g_signal_new ("entries-replaced",
305 G_OBJECT_CLASS_TYPE (object_class),
306 G_SIGNAL_RUN_LAST,
307 G_STRUCT_OFFSET (RBEntryViewClass, entries_replaced),
308 NULL, NULL,
309 g_cclosure_marshal_VOID__VOID,
310 G_TYPE_NONE,
312 rb_entry_view_signals[ENTRY_ACTIVATED] =
313 g_signal_new ("entry-activated",
314 G_OBJECT_CLASS_TYPE (object_class),
315 G_SIGNAL_RUN_LAST,
316 G_STRUCT_OFFSET (RBEntryViewClass, entry_activated),
317 NULL, NULL,
318 g_cclosure_marshal_VOID__BOXED,
319 G_TYPE_NONE,
321 RHYTHMDB_TYPE_ENTRY);
322 rb_entry_view_signals[SELECTION_CHANGED] =
323 g_signal_new ("selection-changed",
324 G_OBJECT_CLASS_TYPE (object_class),
325 G_SIGNAL_RUN_LAST,
326 G_STRUCT_OFFSET (RBEntryViewClass, selection_changed),
327 NULL, NULL,
328 g_cclosure_marshal_VOID__VOID,
329 G_TYPE_NONE,
331 rb_entry_view_signals[SHOW_POPUP] =
332 g_signal_new ("show_popup",
333 G_OBJECT_CLASS_TYPE (object_class),
334 G_SIGNAL_RUN_LAST,
335 G_STRUCT_OFFSET (RBEntryViewClass, show_popup),
336 NULL, NULL,
337 g_cclosure_marshal_VOID__BOOLEAN,
338 G_TYPE_NONE,
340 G_TYPE_BOOLEAN);
341 rb_entry_view_signals[HAVE_SEL_CHANGED] =
342 g_signal_new ("have_selection_changed",
343 G_OBJECT_CLASS_TYPE (object_class),
344 G_SIGNAL_RUN_LAST,
345 G_STRUCT_OFFSET (RBEntryViewClass, have_selection_changed),
346 NULL, NULL,
347 g_cclosure_marshal_VOID__BOOLEAN,
348 G_TYPE_NONE,
350 G_TYPE_BOOLEAN);
351 rb_entry_view_signals[SORT_ORDER_CHANGED] =
352 g_signal_new ("sort-order-changed",
353 G_OBJECT_CLASS_TYPE (object_class),
354 G_SIGNAL_RUN_LAST,
355 G_STRUCT_OFFSET (RBEntryViewClass, sort_order_changed),
356 NULL, NULL,
357 g_cclosure_marshal_VOID__VOID,
358 G_TYPE_NONE,
361 g_type_class_add_private (klass, sizeof (RBEntryViewPrivate));
363 rb_entry_view_column_always_visible = g_quark_from_static_string ("rb_entry_view_column_always_visible");
366 static void
367 rb_entry_view_init (RBEntryView *view)
369 GtkIconTheme *icon_theme;
371 view->priv = RB_ENTRY_VIEW_GET_PRIVATE (view);
373 icon_theme = gtk_icon_theme_get_default ();
375 view->priv->playing_pixbuf = gtk_icon_theme_load_icon (icon_theme,
376 "stock_media-play",
379 NULL);
380 view->priv->paused_pixbuf = gtk_icon_theme_load_icon (icon_theme,
381 "stock_media-pause",
384 NULL);
385 view->priv->error_pixbuf = gtk_icon_theme_load_icon (icon_theme,
386 "stock_dialog-error",
389 NULL);
391 view->priv->propid_column_map = g_hash_table_new (NULL, NULL);
392 view->priv->column_sort_data_map = g_hash_table_new_full (NULL, NULL, NULL, g_free);
393 view->priv->column_key_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
396 static void
397 rb_entry_view_finalize (GObject *object)
399 RBEntryView *view;
401 g_return_if_fail (object != NULL);
402 g_return_if_fail (RB_IS_ENTRY_VIEW (object));
404 view = RB_ENTRY_VIEW (object);
406 g_return_if_fail (view->priv != NULL);
408 if (view->priv->gconf_notification_id > 0)
409 eel_gconf_notification_remove (view->priv->gconf_notification_id);
410 if (view->priv->sorting_gconf_notification_id > 0)
411 eel_gconf_notification_remove (view->priv->sorting_gconf_notification_id);
413 if (view->priv->selection_changed_id > 0)
414 g_source_remove (view->priv->selection_changed_id);
416 g_hash_table_destroy (view->priv->propid_column_map);
417 g_hash_table_destroy (view->priv->column_sort_data_map);
418 g_hash_table_destroy (view->priv->column_key_map);
420 if (view->priv->playing_pixbuf != NULL)
421 g_object_unref (view->priv->playing_pixbuf);
422 if (view->priv->paused_pixbuf != NULL)
423 g_object_unref (view->priv->paused_pixbuf);
424 if (view->priv->error_pixbuf != NULL)
425 g_object_unref (view->priv->error_pixbuf);
427 if (view->priv->playing_model != NULL) {
428 g_object_unref (view->priv->playing_model);
430 if (view->priv->model != NULL) {
431 g_object_unref (view->priv->model);
434 g_free (view->priv->sorting_key);
435 g_free (view->priv->sorting_column_name);
437 G_OBJECT_CLASS (rb_entry_view_parent_class)->finalize (object);
440 static void
441 rb_entry_view_set_shell_player_internal (RBEntryView *view,
442 RBShellPlayer *player)
444 if (view->priv->shell_player != NULL) {
445 g_signal_handlers_disconnect_by_func (view->priv->shell_player,
446 G_CALLBACK (rb_entry_view_playing_song_changed),
447 view);
450 view->priv->shell_player = player;
452 g_signal_connect_object (view->priv->shell_player,
453 "playing-song-changed",
454 G_CALLBACK (rb_entry_view_playing_song_changed),
455 view, 0);
458 static void
459 rb_entry_view_set_model_internal (RBEntryView *view,
460 RhythmDBQueryModel *model)
462 if (view->priv->model != NULL) {
463 g_signal_handlers_disconnect_by_func (view->priv->model,
464 G_CALLBACK (rb_entry_view_row_inserted_cb),
465 view);
466 g_signal_handlers_disconnect_by_func (view->priv->model,
467 G_CALLBACK (rb_entry_view_row_deleted_cb),
468 view);
469 g_signal_handlers_disconnect_by_func (view->priv->model,
470 G_CALLBACK (rb_entry_view_rows_reordered_cb),
471 view);
472 g_object_unref (view->priv->model);
475 gtk_tree_selection_unselect_all (view->priv->selection);
477 view->priv->model = model;
478 if (view->priv->model != NULL) {
479 g_object_ref (view->priv->model);
480 g_signal_connect_object (view->priv->model,
481 "row_inserted",
482 G_CALLBACK (rb_entry_view_row_inserted_cb),
483 view,
485 g_signal_connect_object (view->priv->model,
486 "row_deleted",
487 G_CALLBACK (rb_entry_view_row_deleted_cb),
488 view,
490 g_signal_connect_object (view->priv->model,
491 "rows_reordered",
492 G_CALLBACK (rb_entry_view_rows_reordered_cb),
493 view,
497 if (view->priv->sorting_column != NULL) {
498 rb_entry_view_resort_model (view);
501 if (view->priv->model != NULL) {
502 gtk_tree_view_set_model (GTK_TREE_VIEW (view->priv->treeview),
503 GTK_TREE_MODEL (view->priv->model));
506 view->priv->have_selection = FALSE;
507 view->priv->have_complete_selection = FALSE;
509 g_signal_emit (G_OBJECT (view), rb_entry_view_signals[ENTRIES_REPLACED], 0);
512 static void
513 rb_entry_view_set_property (GObject *object,
514 guint prop_id,
515 const GValue *value,
516 GParamSpec *pspec)
518 RBEntryView *view = RB_ENTRY_VIEW (object);
520 switch (prop_id) {
521 case PROP_DB:
522 view->priv->db = g_value_get_object (value);
523 break;
524 case PROP_SHELL_PLAYER:
525 rb_entry_view_set_shell_player_internal (view, g_value_get_object (value));
526 break;
527 case PROP_SORTING_KEY:
528 g_free (view->priv->sorting_key);
529 view->priv->sorting_key = g_value_dup_string (value);
530 break;
531 case PROP_MODEL:
532 rb_entry_view_set_model_internal (view, g_value_get_object (value));
533 break;
534 case PROP_IS_DRAG_SOURCE:
535 view->priv->is_drag_source = g_value_get_boolean (value);
536 break;
537 case PROP_IS_DRAG_DEST:
538 view->priv->is_drag_dest = g_value_get_boolean (value);
539 break;
540 case PROP_PLAYING_STATE:
541 view->priv->playing_state = g_value_get_int (value);
543 /* redraw the playing entry, as the icon will have changed */
544 if (view->priv->playing_entry != NULL) {
545 rb_entry_view_emit_row_changed (view, view->priv->playing_entry);
547 break;
548 default:
549 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
550 break;
554 static void
555 rb_entry_view_get_property (GObject *object,
556 guint prop_id,
557 GValue *value,
558 GParamSpec *pspec)
560 RBEntryView *view = RB_ENTRY_VIEW (object);
562 switch (prop_id) {
563 case PROP_DB:
564 g_value_set_object (value, view->priv->db);
565 break;
566 case PROP_SHELL_PLAYER:
567 g_value_set_object (value, view->priv->shell_player);
568 break;
569 case PROP_SORTING_KEY:
570 g_value_set_string (value, view->priv->sorting_key);
571 break;
572 case PROP_IS_DRAG_SOURCE:
573 g_value_set_boolean (value, view->priv->is_drag_source);
574 break;
575 case PROP_IS_DRAG_DEST:
576 g_value_set_boolean (value, view->priv->is_drag_dest);
577 break;
578 case PROP_PLAYING_STATE:
579 g_value_set_int (value, view->priv->playing_state);
580 break;
581 default:
582 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
583 break;
587 RBEntryView *
588 rb_entry_view_new (RhythmDB *db,
589 GObject *shell_player,
590 const char *sort_key,
591 gboolean is_drag_source,
592 gboolean is_drag_dest)
594 RBEntryView *view;
596 view = RB_ENTRY_VIEW (g_object_new (RB_TYPE_ENTRY_VIEW,
597 "hadjustment", NULL,
598 "vadjustment", NULL,
599 "hscrollbar_policy", GTK_POLICY_AUTOMATIC,
600 "vscrollbar_policy", GTK_POLICY_ALWAYS,
601 "shadow_type", GTK_SHADOW_IN,
602 "db", db,
603 "shell-player", RB_SHELL_PLAYER (shell_player),
604 "sort-key", sort_key,
605 "is-drag-source", is_drag_source,
606 "is-drag-dest", is_drag_dest,
607 NULL));
609 g_return_val_if_fail (view->priv != NULL, NULL);
611 return view;
614 void
615 rb_entry_view_set_model (RBEntryView *view,
616 RhythmDBQueryModel *model)
618 g_object_set (view, "model", model, NULL);
621 /* Sweet name, eh? */
622 struct RBEntryViewCellDataFuncData {
623 RBEntryView *view;
624 RhythmDBPropType propid;
627 static void
628 rb_entry_view_playing_cell_data_func (GtkTreeViewColumn *column,
629 GtkCellRenderer *renderer,
630 GtkTreeModel *tree_model,
631 GtkTreeIter *iter,
632 RBEntryView *view)
634 RhythmDBEntry *entry;
635 GdkPixbuf *pixbuf = NULL;
637 entry = rhythmdb_query_model_iter_to_entry (view->priv->model, iter);
639 if (entry == NULL) {
640 return;
643 if (entry == view->priv->playing_entry) {
644 switch (view->priv->playing_state) {
645 case RB_ENTRY_VIEW_PLAYING:
646 pixbuf = view->priv->playing_pixbuf;
647 break;
648 case RB_ENTRY_VIEW_PAUSED:
649 pixbuf = view->priv->paused_pixbuf;
650 break;
651 default:
652 pixbuf = NULL;
653 break;
657 if (pixbuf == NULL && rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_PLAYBACK_ERROR)) {
658 pixbuf = view->priv->error_pixbuf;
661 g_object_set (renderer, "pixbuf", pixbuf, NULL);
663 rhythmdb_entry_unref (entry);
666 static void
667 rb_entry_view_rating_cell_data_func (GtkTreeViewColumn *column,
668 GtkCellRenderer *renderer,
669 GtkTreeModel *tree_model,
670 GtkTreeIter *iter,
671 RBEntryView *view)
673 RhythmDBEntry *entry;
675 entry = rhythmdb_query_model_iter_to_entry (view->priv->model, iter);
677 g_object_set (renderer,
678 "rating", rhythmdb_entry_get_double (entry, RHYTHMDB_PROP_RATING),
679 NULL);
681 rhythmdb_entry_unref (entry);
684 static void
685 rb_entry_view_long_cell_data_func (GtkTreeViewColumn *column,
686 GtkCellRenderer *renderer,
687 GtkTreeModel *tree_model,
688 GtkTreeIter *iter,
689 struct RBEntryViewCellDataFuncData *data)
691 RhythmDBEntry *entry;
692 char *str;
693 gulong val;
695 entry = rhythmdb_query_model_iter_to_entry (data->view->priv->model, iter);
697 val = rhythmdb_entry_get_ulong (entry, data->propid);
699 if (val > 0)
700 str = g_strdup_printf ("%lu", val);
701 else
702 str = g_strdup ("");
704 g_object_set (renderer, "text", str, NULL);
705 g_free (str);
706 rhythmdb_entry_unref (entry);
709 static void
710 rb_entry_view_play_count_cell_data_func (GtkTreeViewColumn *column,
711 GtkCellRenderer *renderer,
712 GtkTreeModel *tree_model,
713 GtkTreeIter * iter,
714 struct RBEntryViewCellDataFuncData *data)
716 RhythmDBEntry *entry;
717 gulong i;
718 char *str;
720 entry = rhythmdb_query_model_iter_to_entry (data->view->priv->model, iter);
722 i = rhythmdb_entry_get_ulong (entry, data->propid);
723 if (i == 0)
724 str = _("Never");
725 else
726 str = g_strdup_printf ("%ld", i);
728 g_object_set (renderer, "text", str, NULL);
729 if (i != 0)
730 g_free (str);
732 rhythmdb_entry_unref (entry);
735 static void
736 rb_entry_view_duration_cell_data_func (GtkTreeViewColumn *column,
737 GtkCellRenderer *renderer,
738 GtkTreeModel *tree_model,
739 GtkTreeIter *iter,
740 struct RBEntryViewCellDataFuncData *data)
742 RhythmDBEntry *entry;
743 gulong duration;
744 char *str;
746 entry = rhythmdb_query_model_iter_to_entry (data->view->priv->model, iter);
747 duration = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
749 str = rb_make_duration_string (duration);
750 g_object_set (renderer, "text", str, NULL);
751 g_free (str);
752 rhythmdb_entry_unref (entry);
755 static void
756 rb_entry_view_year_cell_data_func (GtkTreeViewColumn *column,
757 GtkCellRenderer *renderer,
758 GtkTreeModel *tree_model,
759 GtkTreeIter *iter,
760 struct RBEntryViewCellDataFuncData *data)
762 RhythmDBEntry *entry;
763 char str[255];
764 int julian;
765 GDate *date;
767 entry = rhythmdb_query_model_iter_to_entry (data->view->priv->model, iter);
768 julian = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DATE);
770 if (julian > 0) {
771 date = g_date_new_julian (julian);
772 g_date_strftime (str, sizeof (str), "%Y", date);
773 g_object_set (renderer, "text", str, NULL);
774 g_date_free (date);
775 } else {
776 g_object_set (renderer, "text", _("Unknown"), NULL);
779 rhythmdb_entry_unref (entry);
782 static void
783 rb_entry_view_quality_cell_data_func (GtkTreeViewColumn *column,
784 GtkCellRenderer *renderer,
785 GtkTreeModel *tree_model,
786 GtkTreeIter *iter,
787 struct RBEntryViewCellDataFuncData *data)
789 RhythmDBEntry *entry;
790 guint bitrate;
792 entry = rhythmdb_query_model_iter_to_entry (data->view->priv->model, iter);
794 bitrate = rhythmdb_entry_get_ulong (entry, data->propid);
796 if (bitrate > 0) {
797 char *s = g_strdup_printf (_("%u kbps"), (guint)bitrate);
798 g_object_set (renderer, "text", s, NULL);
799 g_free (s);
800 } else {
801 g_object_set (renderer, "text", _("Unknown"), NULL);
804 rhythmdb_entry_unref (entry);
807 static void
808 rb_entry_view_location_cell_data_func (GtkTreeViewColumn *column,
809 GtkCellRenderer *renderer,
810 GtkTreeModel *tree_model,
811 GtkTreeIter *iter,
812 struct RBEntryViewCellDataFuncData *data)
814 RhythmDBEntry *entry;
815 const char *location;
816 char *str;
818 entry = rhythmdb_query_model_iter_to_entry (data->view->priv->model, iter);
820 location = rhythmdb_entry_get_string (entry, data->propid);
821 str = gnome_vfs_unescape_string_for_display (location);
823 g_object_set (renderer, "text", str, NULL);
824 g_free (str);
826 rhythmdb_entry_unref (entry);
829 static void
830 rb_entry_view_string_cell_data_func (GtkTreeViewColumn *column,
831 GtkCellRenderer *renderer,
832 GtkTreeModel *tree_model,
833 GtkTreeIter *iter,
834 struct RBEntryViewCellDataFuncData *data)
836 RhythmDBEntry *entry;
837 const char *str;
839 entry = rhythmdb_query_model_iter_to_entry (data->view->priv->model, iter);
841 str = rhythmdb_entry_get_string (entry, data->propid);
842 if (str != NULL) {
843 g_object_set (renderer, "text", str, NULL);
846 rhythmdb_entry_unref (entry);
849 static void
850 rb_entry_view_sync_sorting (RBEntryView *view)
852 GtkTreeViewColumn *column;
853 gint direction;
854 char *column_name;
856 direction = GTK_SORT_ASCENDING;
857 column_name = NULL;
858 rb_entry_view_get_sorting_order (view, &column_name, &direction);
860 if (column_name == NULL) {
861 return;
864 column = g_hash_table_lookup (view->priv->column_key_map, column_name);
865 if (column == NULL) {
866 g_free (column_name);
867 return;
870 rb_debug ("Updating EntryView sort order to %s:%d", column_name, direction);
872 /* remove the old sorting indicator */
873 if (view->priv->sorting_column)
874 gtk_tree_view_column_set_sort_indicator (view->priv->sorting_column, FALSE);
876 /* set the sorting order and indicator of the new sorting column */
877 view->priv->sorting_column = column;
878 gtk_tree_view_column_set_sort_indicator (column, TRUE);
879 gtk_tree_view_column_set_sort_order (column, direction);
881 rb_debug ("emitting sort order changed");
882 g_signal_emit (G_OBJECT (view), rb_entry_view_signals[SORT_ORDER_CHANGED], 0);
884 g_free (column_name);
887 const char *
888 rb_entry_view_get_sorting_type (RBEntryView *view)
890 char *sorttype;
891 GString *key = g_string_new (view->priv->sorting_column_name);
893 g_string_append_c (key, ',');
895 switch (view->priv->sorting_order)
897 case GTK_SORT_ASCENDING:
898 g_string_append (key, "ascending");
899 break;
900 case GTK_SORT_DESCENDING:
901 g_string_append (key, "descending");
902 break;
903 default:
904 g_assert_not_reached ();
907 sorttype = g_strdup(key->str);
908 g_string_free (key, TRUE);
910 return sorttype;
913 void
914 rb_entry_view_set_sorting_type (RBEntryView *view,
915 const char *sorttype)
917 char **strs;
919 if (!sorttype || !strchr (sorttype, ',')) {
920 rb_debug ("malformed sort data: %s", (sorttype) ? sorttype : "(null)");
921 return;
924 strs = g_strsplit (sorttype, ",", 0);
926 g_free (view->priv->sorting_column_name);
927 view->priv->sorting_column_name = g_strdup(strs[0]);
929 if (!strcmp ("ascending", strs[1]))
930 view->priv->sorting_order = GTK_SORT_ASCENDING;
931 else if (!strcmp ("descending", strs[1]))
932 view->priv->sorting_order = GTK_SORT_DESCENDING;
933 else {
934 g_warning ("atttempting to sort in unknown direction");
935 view->priv->sorting_order = GTK_SORT_ASCENDING;
938 g_strfreev (strs);
940 rb_entry_view_sync_sorting (view);
943 void
944 rb_entry_view_get_sorting_order (RBEntryView *view,
945 char **column_name,
946 gint *sort_order)
948 g_return_if_fail (RB_IS_ENTRY_VIEW (view));
950 if (column_name != NULL) {
951 *column_name = g_strdup (view->priv->sorting_column_name);
954 if (sort_order != NULL) {
955 *sort_order = view->priv->sorting_order;
959 void
960 rb_entry_view_set_sorting_order (RBEntryView *view,
961 const char *column_name,
962 gint sort_order)
964 if (column_name == NULL)
965 return;
967 g_free (view->priv->sorting_column_name);
968 view->priv->sorting_column_name = g_strdup (column_name);
969 view->priv->sorting_order = sort_order;
971 rb_entry_view_sync_sorting (view);
974 static void
975 rb_entry_view_column_clicked_cb (GtkTreeViewColumn *column, RBEntryView *view)
977 gint sort_order;
978 char *clicked_column;
980 rb_debug ("sorting on column %p", column);
982 /* identify the clicked column, and then update the sorting order */
983 clicked_column = (char*) g_object_get_data (G_OBJECT (column), "rb-entry-view-key");
984 sort_order = view->priv->sorting_order;
986 if (view->priv->sorting_column_name
987 && !strcmp(clicked_column, view->priv->sorting_column_name)
988 && (sort_order == GTK_SORT_ASCENDING))
989 sort_order = GTK_SORT_DESCENDING;
990 else
991 sort_order = GTK_SORT_ASCENDING;
993 rb_entry_view_set_sorting_order (view, clicked_column, sort_order);
995 /* update the sort order in GConf */
996 if (view->priv->sorting_key)
997 eel_gconf_set_string (view->priv->sorting_key, rb_entry_view_get_sorting_type(view));
1000 void
1001 rb_entry_view_append_column (RBEntryView *view,
1002 RBEntryViewColumn coltype,
1003 gboolean always_visible)
1005 GtkTreeViewColumn *column;
1006 GtkCellRenderer *renderer = NULL;
1007 struct RBEntryViewCellDataFuncData *cell_data;
1008 const char *title = NULL;
1009 const char *key = NULL;
1010 const char *strings[4] = {0};
1011 GtkTreeCellDataFunc cell_data_func = NULL;
1012 GCompareDataFunc sort_func = NULL;
1013 RhythmDBPropType propid;
1014 RhythmDBPropType sort_propid = RHYTHMDB_NUM_PROPERTIES;
1015 gboolean ellipsize = FALSE;
1016 gboolean resizable = TRUE;
1017 gint column_width = -1;
1019 column = gtk_tree_view_column_new ();
1021 cell_data = g_new0 (struct RBEntryViewCellDataFuncData, 1);
1022 cell_data->view = view;
1024 switch (coltype) {
1025 case RB_ENTRY_VIEW_COL_TRACK_NUMBER:
1026 propid = RHYTHMDB_PROP_TRACK_NUMBER;
1027 cell_data->propid = propid;
1028 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_long_cell_data_func;
1029 sort_func = (GCompareDataFunc) rhythmdb_query_model_track_sort_func;
1030 title = _("Trac_k");
1031 key = "Track";
1032 strings[0] = title;
1033 strings[1] = "9999";
1034 break;
1035 case RB_ENTRY_VIEW_COL_TITLE:
1036 propid = RHYTHMDB_PROP_TITLE;
1037 cell_data->propid = propid;
1038 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1039 sort_propid = RHYTHMDB_PROP_TITLE_SORT_KEY;
1040 sort_func = (GCompareDataFunc) rhythmdb_query_model_string_sort_func;
1041 title = _("_Title");
1042 key = "Title";
1043 ellipsize = TRUE;
1044 break;
1045 case RB_ENTRY_VIEW_COL_ARTIST:
1046 propid = RHYTHMDB_PROP_ARTIST;
1047 cell_data->propid = propid;
1048 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1049 sort_propid = RHYTHMDB_PROP_ARTIST_SORT_KEY;
1050 sort_func = (GCompareDataFunc) rhythmdb_query_model_artist_sort_func;
1051 title = _("Art_ist");
1052 key = "Artist";
1053 ellipsize = TRUE;
1054 break;
1055 case RB_ENTRY_VIEW_COL_ALBUM:
1056 propid = RHYTHMDB_PROP_ALBUM;
1057 cell_data->propid = propid;
1058 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1059 sort_propid = RHYTHMDB_PROP_ALBUM_SORT_KEY;
1060 sort_func = (GCompareDataFunc) rhythmdb_query_model_album_sort_func;
1061 title = _("_Album");
1062 key = "Album";
1063 ellipsize = TRUE;
1064 break;
1065 case RB_ENTRY_VIEW_COL_GENRE:
1066 propid = RHYTHMDB_PROP_GENRE;
1067 cell_data->propid = propid;
1068 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1069 sort_propid = RHYTHMDB_PROP_GENRE_SORT_KEY;
1070 sort_func = (GCompareDataFunc) rhythmdb_query_model_genre_sort_func;
1071 title = _("_Genre");
1072 key = "Genre";
1073 ellipsize = TRUE;
1074 break;
1075 case RB_ENTRY_VIEW_COL_DURATION:
1076 propid = RHYTHMDB_PROP_DURATION;
1077 cell_data->propid = propid;
1078 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_duration_cell_data_func;
1079 sort_propid = cell_data->propid;
1080 sort_func = (GCompareDataFunc) rhythmdb_query_model_ulong_sort_func;
1081 title = _("Tim_e");
1082 key = "Time";
1083 strings[0] = title;
1084 strings[1] = "000:00";
1085 strings[2] = _("Unknown");
1086 break;
1087 case RB_ENTRY_VIEW_COL_YEAR:
1088 propid = RHYTHMDB_PROP_DATE;
1089 cell_data->propid = propid;
1090 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_year_cell_data_func;
1091 sort_propid = cell_data->propid;
1092 sort_func = (GCompareDataFunc) rhythmdb_query_model_date_sort_func;
1093 title = _("_Year");
1094 key = "Year";
1095 strings[0] = title;
1096 strings[1] = "0000";
1097 strings[2] = _("Unknown");
1098 break;
1099 case RB_ENTRY_VIEW_COL_QUALITY:
1100 propid = RHYTHMDB_PROP_BITRATE;
1101 cell_data->propid = propid;
1102 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_quality_cell_data_func;
1103 sort_propid = cell_data->propid;
1104 sort_func = (GCompareDataFunc) rhythmdb_query_model_ulong_sort_func;
1105 title = _("_Quality");
1106 key = "Quality";
1107 strings[0] = title;
1108 strings[1] = _("000 kbps");
1109 strings[2] = _("Unknown");
1110 break;
1111 case RB_ENTRY_VIEW_COL_RATING:
1112 propid = RHYTHMDB_PROP_RATING;
1113 sort_func = (GCompareDataFunc) rhythmdb_query_model_double_ceiling_sort_func;
1115 gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &column_width, NULL);
1116 column_width = column_width * 5 + 5;
1117 resizable = FALSE;
1118 title = _("_Rating");
1119 key = "Rating";
1121 renderer = rb_cell_renderer_rating_new ();
1122 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1123 gtk_tree_view_column_set_cell_data_func (column, renderer,
1124 (GtkTreeCellDataFunc)
1125 rb_entry_view_rating_cell_data_func,
1126 view,
1127 NULL);
1128 g_signal_connect_object (renderer,
1129 "rated",
1130 G_CALLBACK (rb_entry_view_rated_cb),
1131 view,
1133 break;
1134 case RB_ENTRY_VIEW_COL_PLAY_COUNT:
1135 propid = RHYTHMDB_PROP_PLAY_COUNT;
1136 cell_data->propid = propid;
1137 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_play_count_cell_data_func;
1138 sort_propid = cell_data->propid;
1139 sort_func = (GCompareDataFunc) rhythmdb_query_model_ulong_sort_func;
1140 title = _("_Play Count");
1141 key = "PlayCount";
1142 strings[0] = title;
1143 strings[1] = _("Never");
1144 strings[2] = "9999";
1145 break;
1146 case RB_ENTRY_VIEW_COL_LAST_PLAYED:
1147 propid = RHYTHMDB_PROP_LAST_PLAYED;
1148 cell_data->propid = RHYTHMDB_PROP_LAST_PLAYED_STR;
1149 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1150 sort_propid = RHYTHMDB_PROP_LAST_PLAYED;
1151 sort_func = (GCompareDataFunc) rhythmdb_query_model_ulong_sort_func;
1152 title = _("_Last Played");
1153 key = "LastPlayed";
1154 strings[0] = title;
1155 strings[1] = rb_entry_view_get_time_date_column_sample ();
1156 strings[2] = _("Never");
1157 break;
1158 case RB_ENTRY_VIEW_COL_FIRST_SEEN:
1159 propid = RHYTHMDB_PROP_FIRST_SEEN;
1160 cell_data->propid = RHYTHMDB_PROP_FIRST_SEEN_STR;
1161 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1162 sort_propid = RHYTHMDB_PROP_FIRST_SEEN;
1163 sort_func = (GCompareDataFunc) rhythmdb_query_model_ulong_sort_func;
1164 title = _("_Date Added");
1165 key = "FirstSeen";
1166 strings[0] = title;
1167 strings[1] = rb_entry_view_get_time_date_column_sample ();
1168 break;
1169 case RB_ENTRY_VIEW_COL_LAST_SEEN:
1170 propid = RHYTHMDB_PROP_LAST_SEEN;
1171 cell_data->propid = RHYTHMDB_PROP_LAST_SEEN_STR;
1172 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1173 sort_propid = RHYTHMDB_PROP_LAST_SEEN;
1174 sort_func = (GCompareDataFunc) rhythmdb_query_model_ulong_sort_func;
1175 title = _("Last _Seen");
1176 key = "LastSeen";
1177 strings[0] = title;
1178 strings[1] = rb_entry_view_get_time_date_column_sample ();
1179 break;
1180 case RB_ENTRY_VIEW_COL_LOCATION:
1181 propid = RHYTHMDB_PROP_LOCATION;
1182 cell_data->propid = RHYTHMDB_PROP_LOCATION;
1183 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_location_cell_data_func;
1184 sort_propid = RHYTHMDB_PROP_LOCATION;
1185 sort_func = (GCompareDataFunc) rhythmdb_query_model_location_sort_func;
1186 title = _("L_ocation");
1187 key = "Location";
1188 ellipsize = TRUE;
1189 break;
1190 case RB_ENTRY_VIEW_COL_ERROR:
1191 propid = RHYTHMDB_PROP_PLAYBACK_ERROR;
1192 cell_data->propid = RHYTHMDB_PROP_PLAYBACK_ERROR;
1193 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1194 title = _("Error");
1195 key = "Error";
1196 ellipsize = TRUE;
1197 break;
1198 default:
1199 g_assert_not_reached ();
1200 propid = -1;
1201 break;
1204 if (sort_propid == RHYTHMDB_NUM_PROPERTIES)
1205 sort_propid = propid;
1207 if (renderer == NULL) {
1208 renderer = gtk_cell_renderer_text_new ();
1209 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1210 gtk_tree_view_column_set_cell_data_func (column, renderer,
1211 cell_data_func, cell_data, g_free);
1212 } else {
1213 g_free (cell_data);
1217 * Columns must either be expanding (ellipsized) or have a
1218 * fixed minimum width specified. Otherwise, gtk+ gives them a
1219 * width of 0.
1221 if (ellipsize) {
1222 g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
1223 gtk_tree_view_column_set_expand (GTK_TREE_VIEW_COLUMN (column), TRUE);
1224 } else if (column_width != -1) {
1225 gtk_tree_view_column_set_fixed_width (column, column_width);
1226 } else {
1227 rb_entry_view_set_fixed_column_width (view, column, renderer, strings);
1230 if (resizable)
1231 gtk_tree_view_column_set_resizable (column, TRUE);
1233 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1234 gtk_tree_view_column_set_clickable (column, TRUE);
1236 if (always_visible)
1237 g_object_set_qdata (G_OBJECT (column),
1238 rb_entry_view_column_always_visible,
1239 GINT_TO_POINTER (1));
1241 g_hash_table_insert (view->priv->propid_column_map, GINT_TO_POINTER (propid), column);
1243 rb_entry_view_append_column_custom (view, column, title, key, sort_func, GINT_TO_POINTER (sort_propid));
1246 void
1247 rb_entry_view_append_column_custom (RBEntryView *view,
1248 GtkTreeViewColumn *column,
1249 const char *title,
1250 const char *key,
1251 GCompareDataFunc sort_func,
1252 gpointer data)
1254 rb_entry_view_insert_column_custom (view, column, title, key, sort_func, data, -1);
1257 void
1258 rb_entry_view_insert_column_custom (RBEntryView *view,
1259 GtkTreeViewColumn *column,
1260 const char *title,
1261 const char *key,
1262 GCompareDataFunc sort_func,
1263 gpointer data,
1264 gint position)
1266 struct RBEntryViewColumnSortData *sortdata;
1268 gtk_tree_view_column_set_title (column, title);
1269 gtk_tree_view_column_set_reorderable (column, FALSE);
1271 g_signal_connect_object (column, "clicked",
1272 G_CALLBACK (rb_entry_view_column_clicked_cb),
1273 view, 0);
1275 g_object_set_data_full (G_OBJECT (column), "rb-entry-view-key",
1276 g_strdup (key), g_free);
1278 rb_debug ("appending column: %p (%s)", column, title);
1280 gtk_tree_view_insert_column (GTK_TREE_VIEW (view->priv->treeview), column, position);
1282 if (sort_func != NULL) {
1283 sortdata = g_new (struct RBEntryViewColumnSortData, 1);
1284 sortdata->func = (GCompareDataFunc) sort_func;
1285 sortdata->data = data;
1286 g_hash_table_insert (view->priv->column_sort_data_map, column, sortdata);
1288 g_hash_table_insert (view->priv->column_key_map, g_strdup (key), column);
1290 rb_entry_view_sync_columns_visible (view);
1291 rb_entry_view_sync_sorting (view);
1294 void
1295 rb_entry_view_set_columns_clickable (RBEntryView *view,
1296 gboolean clickable)
1298 GList *columns, *tem;
1300 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (view->priv->treeview));
1301 for (tem = columns; tem; tem = tem->next) {
1302 /* only columns we can sort on should be clickable */
1303 GtkTreeViewColumn *column = (GtkTreeViewColumn *) tem->data;
1304 if (g_hash_table_lookup (view->priv->column_sort_data_map, column) != NULL)
1305 gtk_tree_view_column_set_clickable (tem->data, clickable);
1307 g_list_free (columns);
1310 static GObject *
1311 rb_entry_view_constructor (GType type,
1312 guint n_construct_properties,
1313 GObjectConstructParam *construct_properties)
1315 RBEntryView *view;
1316 RBEntryViewClass *klass;
1317 klass = RB_ENTRY_VIEW_CLASS (g_type_class_peek (RB_TYPE_ENTRY_VIEW));
1319 view = RB_ENTRY_VIEW (G_OBJECT_CLASS (rb_entry_view_parent_class)
1320 ->constructor (type, n_construct_properties, construct_properties));
1322 view->priv->treeview = GTK_WIDGET (gtk_tree_view_new ());
1323 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (view->priv->treeview), TRUE);
1325 gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (view->priv->treeview),
1326 type_ahead_search_func,
1327 NULL, NULL);
1329 g_signal_connect_object (view->priv->treeview,
1330 "button_press_event",
1331 G_CALLBACK (rb_entry_view_button_press_cb),
1332 view,
1334 g_signal_connect_object (view->priv->treeview,
1335 "row_activated",
1336 G_CALLBACK (rb_entry_view_row_activated_cb),
1337 view,
1339 g_signal_connect_object (view->priv->treeview,
1340 "popup_menu",
1341 G_CALLBACK (rb_entry_view_popup_menu_cb),
1342 view,
1344 view->priv->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view->priv->treeview));
1345 g_signal_connect_object (view->priv->selection,
1346 "changed",
1347 G_CALLBACK (rb_entry_view_selection_changed_cb),
1348 view,
1351 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view->priv->treeview), TRUE);
1352 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view->priv->treeview), TRUE);
1353 gtk_tree_selection_set_mode (view->priv->selection, GTK_SELECTION_MULTIPLE);
1355 if (view->priv->is_drag_source) {
1356 rb_tree_dnd_add_drag_source_support (GTK_TREE_VIEW (view->priv->treeview),
1357 GDK_BUTTON1_MASK,
1358 rb_entry_view_drag_types,
1359 G_N_ELEMENTS (rb_entry_view_drag_types),
1360 GDK_ACTION_COPY);
1363 if (view->priv->is_drag_dest) {
1364 rb_tree_dnd_add_drag_dest_support (GTK_TREE_VIEW (view->priv->treeview),
1365 RB_TREE_DEST_CAN_DROP_BETWEEN | RB_TREE_DEST_EMPTY_VIEW_DROP,
1366 rb_entry_view_drag_types,
1367 G_N_ELEMENTS (rb_entry_view_drag_types),
1368 GDK_ACTION_COPY | GDK_ACTION_MOVE);
1371 gtk_container_add (GTK_CONTAINER (view), view->priv->treeview);
1374 GtkTreeViewColumn *column;
1375 GtkTooltips *tooltip;
1376 GtkCellRenderer *renderer;
1377 GtkWidget *image_widget;
1378 gint width;
1380 tooltip = gtk_tooltips_new ();
1382 /* Playing icon column */
1383 column = GTK_TREE_VIEW_COLUMN (gtk_tree_view_column_new ());
1384 renderer = rb_cell_renderer_pixbuf_new ();
1385 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1386 gtk_tree_view_column_set_cell_data_func (column, renderer,
1387 (GtkTreeCellDataFunc)
1388 rb_entry_view_playing_cell_data_func,
1389 view,
1390 NULL);
1392 image_widget = gtk_image_new_from_icon_name ("stock_volume-max", GTK_ICON_SIZE_MENU);
1393 gtk_tree_view_column_set_widget (column, image_widget);
1394 gtk_widget_show (image_widget);
1396 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1397 gtk_tree_view_column_set_clickable (column, FALSE);
1398 gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, NULL);
1399 gtk_tree_view_column_set_fixed_width (column, width + 5);
1400 gtk_tree_view_append_column (GTK_TREE_VIEW (view->priv->treeview), column);
1401 g_signal_connect_swapped (renderer,
1402 "pixbuf-clicked",
1403 G_CALLBACK (rb_entry_view_pixbuf_clicked_cb),
1404 view);
1406 gtk_tooltips_set_tip (GTK_TOOLTIPS (tooltip), GTK_WIDGET (column->button),
1407 _("Now Playing"), NULL);
1410 view->priv->gconf_notification_id =
1411 eel_gconf_notification_add (CONF_UI_COLUMNS_SETUP,
1412 rb_entry_view_columns_config_changed_cb,
1413 view);
1414 if (view->priv->sorting_key) {
1415 view->priv->sorting_gconf_notification_id =
1416 eel_gconf_notification_add (view->priv->sorting_key,
1417 rb_entry_view_sort_key_changed_cb,
1418 view);
1421 if (view->priv->sorting_key) {
1422 char *s = eel_gconf_get_string (view->priv->sorting_key);
1423 rb_entry_view_set_sorting_type (view, s);
1424 g_free (s);
1428 RhythmDBQueryModel *query_model;
1429 query_model = rhythmdb_query_model_new_empty (view->priv->db);
1430 rb_entry_view_set_model (view, RHYTHMDB_QUERY_MODEL (query_model));
1431 g_object_unref (query_model);
1434 return G_OBJECT (view);
1437 static void
1438 rb_entry_view_rated_cb (RBCellRendererRating *cellrating,
1439 const char *path_string,
1440 double rating,
1441 RBEntryView *view)
1443 GtkTreePath *path;
1444 RhythmDBEntry *entry;
1445 GValue value = { 0, };
1447 g_return_if_fail (rating >= 0 && rating <= 5 );
1448 g_return_if_fail (path_string != NULL);
1450 path = gtk_tree_path_new_from_string (path_string);
1451 entry = rhythmdb_query_model_tree_path_to_entry (view->priv->model, path);
1452 gtk_tree_path_free (path);
1454 g_value_init (&value, G_TYPE_DOUBLE);
1455 g_value_set_double (&value, rating);
1456 rhythmdb_entry_set (view->priv->db, entry, RHYTHMDB_PROP_RATING, &value);
1457 g_value_unset (&value);
1459 rhythmdb_commit (view->priv->db);
1461 rhythmdb_entry_unref (entry);
1464 static void
1465 rb_entry_view_pixbuf_clicked_cb (RBEntryView *view,
1466 const char *path_string,
1467 RBCellRendererPixbuf *cellpixbuf)
1469 GtkTreePath *path;
1470 RhythmDBEntry *entry;
1471 const gchar *error;
1473 g_return_if_fail (path_string != NULL);
1475 path = gtk_tree_path_new_from_string (path_string);
1476 entry = rhythmdb_query_model_tree_path_to_entry (view->priv->model, path);
1478 gtk_tree_path_free (path);
1480 error = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_PLAYBACK_ERROR);
1481 if (error) {
1482 rb_error_dialog (NULL, _("Playback Error"), "%s", error);
1485 rhythmdb_entry_unref (entry);
1488 static void
1489 rb_entry_view_playing_song_changed (RBShellPlayer *player,
1490 RhythmDBEntry *entry,
1491 RBEntryView *view)
1493 gboolean realized, visible;
1494 GtkTreeIter iter;
1496 g_return_if_fail (RB_IS_ENTRY_VIEW (view));
1498 if (view->priv->playing_entry != NULL) {
1499 if (view->priv->playing_state != RB_ENTRY_VIEW_NOT_PLAYING)
1500 rb_entry_view_emit_row_changed (view, view->priv->playing_entry);
1501 g_object_unref (view->priv->playing_model);
1504 view->priv->playing_entry = entry;
1505 view->priv->playing_model = view->priv->model;
1506 g_object_ref (view->priv->playing_model);
1508 if (view->priv->playing_state != RB_ENTRY_VIEW_NOT_PLAYING) {
1509 if (view->priv->playing_entry != NULL) {
1510 view->priv->playing_entry_in_view =
1511 rb_entry_view_emit_row_changed (view, view->priv->playing_entry);
1514 if (view->priv->playing_entry
1515 && view->priv->playing_entry_in_view) {
1516 rb_entry_view_entry_is_visible (view, view->priv->playing_entry,
1517 &realized, &visible, &iter);
1518 if (realized && !visible)
1519 rb_entry_view_scroll_to_iter (view, &iter);
1524 static gboolean
1525 harvest_entries (GtkTreeModel *model,
1526 GtkTreePath *path,
1527 GtkTreeIter *iter,
1528 GList **list)
1530 RhythmDBEntry *entry;
1532 gtk_tree_model_get (model, iter, 0, &entry, -1);
1534 *list = g_list_prepend (*list, entry);
1536 return FALSE;
1539 GList *
1540 rb_entry_view_get_selected_entries (RBEntryView *view)
1542 GList *list = NULL;
1544 gtk_tree_selection_selected_foreach (view->priv->selection,
1545 (GtkTreeSelectionForeachFunc) harvest_entries,
1546 (gpointer) &list);
1548 list = g_list_reverse (list);
1549 return list;
1552 static gboolean
1553 rb_entry_view_button_press_cb (GtkTreeView *treeview,
1554 GdkEventButton *event,
1555 RBEntryView *view)
1557 if (event->button == 3) {
1558 GtkTreePath *path;
1559 RhythmDBEntry *entry;
1561 gtk_tree_view_get_path_at_pos (treeview, event->x, event->y, &path, NULL, NULL, NULL);
1562 if (path != NULL) {
1563 GList *selected;
1564 entry = rhythmdb_query_model_tree_path_to_entry (view->priv->model, path);
1566 selected = rb_entry_view_get_selected_entries (view);
1568 if (!g_list_find (selected, entry))
1569 rb_entry_view_select_entry (view, entry);
1571 g_list_free (selected);
1573 rhythmdb_entry_unref (entry);
1575 g_signal_emit (G_OBJECT (view), rb_entry_view_signals[SHOW_POPUP], 0, (path != NULL));
1576 return TRUE;
1579 return FALSE;
1582 static gboolean
1583 rb_entry_view_popup_menu_cb (GtkTreeView *treeview,
1584 RBEntryView *view)
1586 if (gtk_tree_selection_count_selected_rows (gtk_tree_view_get_selection (treeview)) == 0)
1587 return FALSE;
1589 g_signal_emit (G_OBJECT (view), rb_entry_view_signals[SHOW_POPUP], 0);
1590 return TRUE;
1593 static gboolean
1594 rb_entry_view_emit_selection_changed (RBEntryView *view)
1596 gboolean available;
1597 gint sel_count;
1599 GDK_THREADS_ENTER ();
1600 sel_count = gtk_tree_selection_count_selected_rows (view->priv->selection);
1601 available = (sel_count > 0);
1603 if (available != view->priv->have_selection) {
1604 gint entry_count;
1606 entry_count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (view->priv->model), NULL);
1607 view->priv->have_complete_selection = (sel_count == entry_count);
1609 view->priv->have_selection = available;
1611 g_signal_emit (G_OBJECT (view), rb_entry_view_signals[HAVE_SEL_CHANGED], 0, available);
1614 view->priv->selection_changed_id = 0;
1615 g_signal_emit (G_OBJECT (view), rb_entry_view_signals[SELECTION_CHANGED], 0);
1617 GDK_THREADS_LEAVE ();
1618 return FALSE;
1621 static void
1622 rb_entry_view_selection_changed_cb (GtkTreeSelection *selection,
1623 RBEntryView *view)
1625 if (view->priv->selection_changed_id == 0)
1626 view->priv->selection_changed_id = g_idle_add ((GSourceFunc)rb_entry_view_emit_selection_changed, view);
1629 gboolean
1630 rb_entry_view_have_selection (RBEntryView *view)
1632 return view->priv->have_selection;
1635 gboolean
1636 rb_entry_view_have_complete_selection (RBEntryView *view)
1638 return view->priv->have_complete_selection;
1641 static void
1642 rb_entry_view_row_activated_cb (GtkTreeView *treeview,
1643 GtkTreePath *path,
1644 GtkTreeViewColumn *column,
1645 RBEntryView *view)
1647 RhythmDBEntry *entry;
1649 rb_debug ("row activated");
1651 entry = rhythmdb_query_model_tree_path_to_entry (view->priv->model, path);
1653 rb_debug ("emitting entry activated");
1654 g_signal_emit (G_OBJECT (view), rb_entry_view_signals[ENTRY_ACTIVATED], 0, entry);
1656 rhythmdb_entry_unref (entry);
1659 static void
1660 rb_entry_view_row_inserted_cb (GtkTreeModel *model,
1661 GtkTreePath *path,
1662 GtkTreeIter *iter,
1663 RBEntryView *view)
1665 RhythmDBEntry *entry = rhythmdb_query_model_tree_path_to_entry (RHYTHMDB_QUERY_MODEL (model), path);
1667 rb_debug ("row added");
1668 g_signal_emit (G_OBJECT (view), rb_entry_view_signals[ENTRY_ADDED], 0, entry);
1669 rhythmdb_entry_unref (entry);
1672 static void
1673 rb_entry_view_row_deleted_cb (GtkTreeModel *model,
1674 GtkTreePath *path,
1675 RBEntryView *view)
1677 RhythmDBEntry *entry = rhythmdb_query_model_tree_path_to_entry (RHYTHMDB_QUERY_MODEL (model), path);
1679 rb_debug ("row deleted");
1680 g_signal_emit (G_OBJECT (view), rb_entry_view_signals[ENTRY_DELETED], 0, entry);
1681 rhythmdb_entry_unref (entry);
1684 static void
1685 rb_entry_view_rows_reordered_cb (GtkTreeModel *model,
1686 GtkTreePath *path,
1687 GtkTreeIter *iter,
1688 gint *order,
1689 RBEntryView *view)
1691 GList *selected_rows;
1692 GList *i;
1693 gint model_size;
1694 gboolean scrolled = FALSE;
1696 rb_debug ("rows reordered");
1698 model_size = gtk_tree_model_iter_n_children (model, NULL);
1700 /* check if a selected row was moved; if so, we'll
1701 * need to move the selection too.
1703 selected_rows = gtk_tree_selection_get_selected_rows (view->priv->selection,
1704 NULL);
1705 for (i = selected_rows; i != NULL; i = i->next) {
1706 GtkTreePath *path = (GtkTreePath *)i->data;
1707 gint index = gtk_tree_path_get_indices (path)[0];
1708 gint newindex;
1709 if (order[index] != index) {
1710 GtkTreePath *newpath;
1711 gtk_tree_selection_unselect_path (view->priv->selection, path);
1713 for (newindex = 0; newindex < model_size; newindex++) {
1714 if (order[newindex] == index) {
1715 newpath = gtk_tree_path_new_from_indices (newindex, -1);
1716 gtk_tree_selection_select_path (view->priv->selection, newpath);
1717 if (!scrolled) {
1718 GtkTreeViewColumn *col;
1719 GtkTreeView *treeview = GTK_TREE_VIEW (view->priv->treeview);
1721 col = gtk_tree_view_get_column (treeview, 0);
1722 gtk_tree_view_scroll_to_cell (treeview, newpath, col, TRUE, 0.5, 0.0);
1723 scrolled = TRUE;
1725 gtk_tree_path_free (newpath);
1726 break;
1733 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1734 g_list_free (selected_rows);
1736 gtk_widget_queue_draw (GTK_WIDGET (view));
1739 void
1740 rb_entry_view_select_all (RBEntryView *view)
1742 gtk_tree_selection_select_all (view->priv->selection);
1745 void
1746 rb_entry_view_select_none (RBEntryView *view)
1748 gtk_tree_selection_unselect_all (view->priv->selection);
1751 void
1752 rb_entry_view_select_entry (RBEntryView *view,
1753 RhythmDBEntry *entry)
1755 GtkTreeIter iter;
1757 if (entry == NULL)
1758 return;
1760 rb_entry_view_select_none (view);
1762 if (rhythmdb_query_model_entry_to_iter (view->priv->model,
1763 entry, &iter)) {
1764 gtk_tree_selection_select_iter (view->priv->selection, &iter);
1768 void
1769 rb_entry_view_scroll_to_entry (RBEntryView *view,
1770 RhythmDBEntry *entry)
1772 GtkTreeIter iter;
1774 if (rhythmdb_query_model_entry_to_iter (view->priv->model,
1775 entry, &iter)) {
1776 rb_entry_view_scroll_to_iter (view, &iter);
1780 static void
1781 rb_entry_view_scroll_to_iter (RBEntryView *view,
1782 GtkTreeIter *iter)
1784 GtkTreePath *path;
1786 /* It's possible to we can be asked to scroll the play queue's entry
1787 * view to the playing entry before the view has ever been displayed.
1788 * This will result in gtk+ warnings, so we avoid it in this case.
1790 if (!GTK_WIDGET_REALIZED (view))
1791 return;
1793 path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->priv->model), iter);
1794 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (view->priv->treeview), path,
1795 gtk_tree_view_get_column (GTK_TREE_VIEW (view->priv->treeview), 0),
1796 TRUE, 0.5, 0.0);
1797 gtk_tree_view_set_cursor (GTK_TREE_VIEW (view->priv->treeview), path,
1798 gtk_tree_view_get_column (GTK_TREE_VIEW (view->priv->treeview), 0), FALSE);
1800 gtk_tree_path_free (path);
1803 gboolean
1804 rb_entry_view_get_entry_visible (RBEntryView *view,
1805 RhythmDBEntry *entry)
1807 GtkTreeIter unused;
1808 gboolean realized, visible;
1810 if (view->priv->playing_model != view->priv->model)
1811 return FALSE;
1813 rb_entry_view_entry_is_visible (view, entry, &realized, &visible,
1814 &unused);
1815 return realized && visible;
1818 gboolean
1819 rb_entry_view_get_entry_contained (RBEntryView *view,
1820 RhythmDBEntry *entry)
1822 GtkTreeIter unused;
1824 return rhythmdb_query_model_entry_to_iter (view->priv->model,
1825 entry, &unused);
1828 static void
1829 rb_entry_view_entry_is_visible (RBEntryView *view,
1830 RhythmDBEntry *entry,
1831 gboolean *realized,
1832 gboolean *visible,
1833 GtkTreeIter *iter)
1835 GtkTreePath *path;
1836 GdkRectangle rect;
1838 *realized = FALSE;
1839 *visible = FALSE;
1841 g_return_if_fail (entry != NULL);
1843 if (!GTK_WIDGET_REALIZED (view))
1844 return;
1846 *realized = TRUE;
1848 if (!rhythmdb_query_model_entry_to_iter (view->priv->model,
1849 entry, iter))
1850 return;
1852 path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->priv->model), iter);
1853 gtk_tree_view_get_cell_area (GTK_TREE_VIEW (view->priv->treeview),
1854 path,
1855 gtk_tree_view_get_column (GTK_TREE_VIEW (view->priv->treeview), 0),
1856 &rect);
1858 gtk_tree_path_free (path);
1860 *visible = (rect.y != 0 && rect.height != 0);
1863 void
1864 rb_entry_view_enable_drag_source (RBEntryView *view,
1865 const GtkTargetEntry *targets,
1866 int n_targets)
1868 g_return_if_fail (view != NULL);
1870 rb_tree_dnd_add_drag_source_support (GTK_TREE_VIEW (view->priv->treeview),
1871 GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
1872 targets, n_targets, GDK_ACTION_COPY);
1875 static void
1876 rb_entry_view_sort_key_changed_cb (GConfClient* client,
1877 guint cnxn_id,
1878 GConfEntry *entry,
1879 gpointer user_data)
1881 RBEntryView *view = user_data;
1883 g_return_if_fail (RB_IS_ENTRY_VIEW (view));
1885 rb_entry_view_set_sorting_type (view, eel_gconf_get_string (view->priv->sorting_key));
1888 static void
1889 rb_entry_view_columns_config_changed_cb (GConfClient* client,
1890 guint cnxn_id,
1891 GConfEntry *entry,
1892 gpointer user_data)
1894 RBEntryView *view = user_data;
1896 g_return_if_fail (RB_IS_ENTRY_VIEW (view));
1898 rb_entry_view_sync_columns_visible (view);
1901 static gint
1902 propid_from_name (const char *name)
1904 GEnumClass *prop_class = g_type_class_ref (RHYTHMDB_TYPE_PROP_TYPE);
1905 GEnumValue *ev;
1906 int ret;
1908 ev = g_enum_get_value_by_name (prop_class, name);
1909 if (ev)
1910 ret = ev->value;
1911 else
1912 ret = -1;
1913 return ret;
1916 static void
1917 set_column_visibility (guint propid,
1918 GtkTreeViewColumn *column,
1919 GList *visible_props)
1921 gboolean visible;
1923 if (g_object_get_qdata (G_OBJECT (column),
1924 rb_entry_view_column_always_visible) == GINT_TO_POINTER (1))
1925 visible = TRUE;
1926 else
1927 visible = (g_list_find (visible_props, GINT_TO_POINTER (propid)) != NULL);
1929 gtk_tree_view_column_set_visible (column, visible);
1932 static void
1933 rb_entry_view_sync_columns_visible (RBEntryView *view)
1935 char **items;
1936 GList *visible_properties = NULL;
1937 char *config = eel_gconf_get_string (CONF_UI_COLUMNS_SETUP);
1939 g_return_if_fail (view != NULL);
1940 g_return_if_fail (config != NULL);
1942 items = g_strsplit (config, ",", 0);
1943 if (items != NULL) {
1944 int i;
1945 for (i = 0; items[i] != NULL && *(items[i]); i++) {
1946 int value = propid_from_name (items[i]);
1948 if ((value >= 0) && (value < RHYTHMDB_NUM_PROPERTIES))
1949 visible_properties = g_list_prepend (visible_properties, GINT_TO_POINTER (value));
1951 g_strfreev (items);
1954 g_hash_table_foreach (view->priv->propid_column_map, (GHFunc) set_column_visibility, visible_properties);
1956 g_list_free (visible_properties);
1957 g_free (config);
1960 void
1961 rb_entry_view_set_state (RBEntryView *view,
1962 RBEntryViewState state)
1964 g_return_if_fail (RB_IS_ENTRY_VIEW (view));
1965 g_object_set (view, "playing-state", state, NULL);
1968 static void
1969 rb_entry_view_grab_focus (GtkWidget *widget)
1971 RBEntryView *view = RB_ENTRY_VIEW (widget);
1973 gtk_widget_grab_focus (GTK_WIDGET (view->priv->treeview));
1976 static gboolean
1977 rb_entry_view_emit_row_changed (RBEntryView *view,
1978 RhythmDBEntry *entry)
1980 GtkTreeIter iter;
1981 GtkTreePath *path;
1983 if (!rhythmdb_query_model_entry_to_iter (view->priv->model, entry, &iter))
1984 return FALSE;
1986 path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->priv->model),
1987 &iter);
1988 gtk_tree_model_row_changed (GTK_TREE_MODEL (view->priv->model),
1989 path, &iter);
1990 gtk_tree_path_free (path);
1991 return TRUE;
1994 void
1995 rb_entry_view_set_fixed_column_width (RBEntryView *view,
1996 GtkTreeViewColumn *column,
1997 GtkCellRenderer *renderer,
1998 const gchar **strings)
2000 gint max_width = 0;
2001 int i = 0;
2003 while (strings[i] != NULL) {
2004 gint width;
2005 g_object_set (renderer, "text", strings[i], NULL);
2006 gtk_cell_renderer_get_size (renderer,
2007 view->priv->treeview,
2008 NULL,
2009 NULL, NULL,
2010 &width, NULL);
2012 if (width > max_width)
2013 max_width = width;
2015 i++;
2018 /* include some arbitrary amount of padding, just to be safeish */
2019 gtk_tree_view_column_set_fixed_width (column, max_width + 5);
2022 const char *
2023 rb_entry_view_get_time_date_column_sample ()
2025 static const char *sample = NULL;
2027 * Currently, Japanese is the only translation that uses
2028 * anything other than %Y, %m ,%d, %H, and %M to format dates.
2029 * It uses %B (month name) and %e (days), and the values for
2030 * the month name appear to all consist of the month number
2031 * followed by a single character, so they're of consistent
2032 * width. So, this approach should work for every locale.
2034 * Midnight on September 30th, 2000 is the widest date/time I
2035 * can think of. 2000-09-30 00:00.
2038 if (sample == NULL) {
2039 /* s m h d M Y dw dY x */
2040 struct tm someday = { 0, 0, 0, 30, 9, 100, 6, 274, 0};
2042 /* Translators: Please keep the translated date format
2043 * compact, and avoid variable-width items such as month and
2044 * day names wherever possible. This allows us to disable
2045 * column autosizing, which makes the Rhythmbox UI much faster.
2047 sample = eel_strdup_strftime (_("%Y-%m-%d %H:%M"), &someday);
2049 return sample;
2052 void
2053 rb_entry_view_resort_model (RBEntryView *view)
2055 struct RBEntryViewColumnSortData *sort_data;
2057 g_assert (view->priv->sorting_column);
2058 sort_data = g_hash_table_lookup (view->priv->column_sort_data_map,
2059 view->priv->sorting_column);
2060 g_assert (sort_data);
2062 rhythmdb_query_model_set_sort_order (view->priv->model,
2063 sort_data->func,
2064 sort_data->data,
2065 NULL,
2066 (view->priv->sorting_order == GTK_SORT_DESCENDING));
2069 /* This should really be standard. */
2070 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
2072 GType
2073 rb_entry_view_column_get_type (void)
2075 static GType etype = 0;
2077 if (etype == 0) {
2078 static const GEnumValue values[] = {
2079 ENUM_ENTRY (RB_ENTRY_VIEW_COL_TRACK_NUMBER, "Track Number"),
2080 ENUM_ENTRY (RB_ENTRY_VIEW_COL_TITLE, "Title"),
2081 ENUM_ENTRY (RB_ENTRY_VIEW_COL_ARTIST, "Artist"),
2082 ENUM_ENTRY (RB_ENTRY_VIEW_COL_ALBUM, "Album"),
2083 ENUM_ENTRY (RB_ENTRY_VIEW_COL_GENRE, "Genre"),
2084 ENUM_ENTRY (RB_ENTRY_VIEW_COL_DURATION, "Duration"),
2085 ENUM_ENTRY (RB_ENTRY_VIEW_COL_QUALITY, "Quality"),
2086 ENUM_ENTRY (RB_ENTRY_VIEW_COL_RATING, "Rating"),
2087 ENUM_ENTRY (RB_ENTRY_VIEW_COL_PLAY_COUNT, "Play Count"),
2088 ENUM_ENTRY (RB_ENTRY_VIEW_COL_YEAR, "Year"),
2089 ENUM_ENTRY (RB_ENTRY_VIEW_COL_LAST_PLAYED, "Last Played"),
2090 ENUM_ENTRY (RB_ENTRY_VIEW_COL_FIRST_SEEN, "First Seen"),
2091 ENUM_ENTRY (RB_ENTRY_VIEW_COL_LAST_SEEN, "Last Seen"),
2092 ENUM_ENTRY (RB_ENTRY_VIEW_COL_LOCATION, "Location"),
2093 ENUM_ENTRY (RB_ENTRY_VIEW_COL_ERROR, "Error"),
2094 { 0, 0, 0 }
2097 etype = g_enum_register_static ("RBEntryViewColumn", values);
2100 return etype;
2103 GType
2104 rb_entry_view_state_get_type (void)
2106 static GType etype = 0;
2108 if (etype == 0) {
2109 static const GEnumValue values[] = {
2110 ENUM_ENTRY (RB_ENTRY_VIEW_NOT_PLAYING, "Not Playing"),
2111 ENUM_ENTRY (RB_ENTRY_VIEW_PLAYING, "Playing"),
2112 ENUM_ENTRY (RB_ENTRY_VIEW_PAUSED, "Paused"),
2113 { 0, 0, 0 }
2116 etype = g_enum_register_static ("RBEntryViewState", values);
2119 return etype;