udapted vi.po
[rhythmbox.git] / widgets / rb-entry-view.c
blob9bf6dd52c0b9b8585643ab396f1fe24c56fcec39
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[] = {
50 { "application/x-rhythmbox-entry", 0, 0 },
51 { "text/uri-list", 0, 1 }
54 struct RBEntryViewColumnSortData
56 GCompareDataFunc func;
57 gpointer data;
60 static void rb_entry_view_class_init (RBEntryViewClass *klass);
61 static void rb_entry_view_init (RBEntryView *view);
62 static GObject *rb_entry_view_constructor (GType type, guint n_construct_properties,
63 GObjectConstructParam *construct_properties);
64 static void rb_entry_view_dispose (GObject *object);
65 static void rb_entry_view_finalize (GObject *object);
66 static void rb_entry_view_set_property (GObject *object,
67 guint prop_id,
68 const GValue *value,
69 GParamSpec *pspec);
70 static void rb_entry_view_get_property (GObject *object,
71 guint prop_id,
72 GValue *value,
73 GParamSpec *pspec);
74 static void rb_entry_view_selection_changed_cb (GtkTreeSelection *selection,
75 RBEntryView *view);
76 static void rb_entry_view_grab_focus (GtkWidget *widget);
77 static void rb_entry_view_row_activated_cb (GtkTreeView *treeview,
78 GtkTreePath *path,
79 GtkTreeViewColumn *column,
80 RBEntryView *view);
81 static void rb_entry_view_row_inserted_cb (GtkTreeModel *model,
82 GtkTreePath *path,
83 GtkTreeIter *iter,
84 RBEntryView *view);
85 static void rb_entry_view_row_deleted_cb (GtkTreeModel *model,
86 GtkTreePath *path,
87 RBEntryView *view);
88 static void rb_entry_view_rows_reordered_cb (GtkTreeModel *model,
89 GtkTreePath *path,
90 GtkTreeIter *iter,
91 gint *order,
92 RBEntryView *view);
93 static void rb_entry_view_sync_columns_visible (RBEntryView *view);
94 static void rb_entry_view_columns_config_changed_cb (GConfClient* client,
95 guint cnxn_id,
96 GConfEntry *entry,
97 gpointer user_data);
98 static void rb_entry_view_sort_key_changed_cb (GConfClient* client,
99 guint cnxn_id,
100 GConfEntry *entry,
101 gpointer user_data);
102 static void rb_entry_view_rated_cb (RBCellRendererRating *cellrating,
103 const char *path,
104 double rating,
105 RBEntryView *view);
106 static void rb_entry_view_pixbuf_clicked_cb (RBEntryView *view,
107 const char *path,
108 RBCellRendererPixbuf *cellpixbuf);
109 static gboolean rb_entry_view_button_press_cb (GtkTreeView *treeview,
110 GdkEventButton *event,
111 RBEntryView *view);
112 static gboolean rb_entry_view_popup_menu_cb (GtkTreeView *treeview,
113 RBEntryView *view);
114 static void rb_entry_view_entry_is_visible (RBEntryView *view, RhythmDBEntry *entry,
115 gboolean *realized, gboolean *visible,
116 GtkTreeIter *iter);
117 static void rb_entry_view_scroll_to_iter (RBEntryView *view,
118 GtkTreeIter *iter);
119 static gboolean rb_entry_view_emit_row_changed (RBEntryView *view,
120 RhythmDBEntry *entry);
121 static void rb_entry_view_playing_song_changed (RBShellPlayer *player,
122 RhythmDBEntry *entry,
123 RBEntryView *view);
125 struct RBEntryViewPrivate
127 RhythmDB *db;
128 RBShellPlayer *shell_player;
130 RhythmDBQueryModel *model;
132 GtkWidget *treeview;
133 GtkTreeSelection *selection;
135 RBEntryViewState playing_state;
136 RhythmDBQueryModel *playing_model;
137 RhythmDBEntry *playing_entry;
138 gboolean playing_entry_in_view;
139 guint selection_changed_id;
141 gboolean is_drag_source;
142 gboolean is_drag_dest;
144 GdkPixbuf *playing_pixbuf;
145 GdkPixbuf *paused_pixbuf;
146 GdkPixbuf *error_pixbuf;
148 char *sorting_key;
149 guint sorting_gconf_notification_id;
150 GtkTreeViewColumn *sorting_column;
151 gint sorting_order;
152 char *sorting_column_name;
154 gboolean have_selection, have_complete_selection;
156 GHashTable *column_key_map;
158 guint gconf_notification_id;
159 GHashTable *propid_column_map;
160 GHashTable *column_sort_data_map;
163 #define RB_ENTRY_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_ENTRY_VIEW, RBEntryViewPrivate))
165 enum
167 ENTRY_ADDED,
168 ENTRY_DELETED,
169 ENTRIES_REPLACED,
170 SELECTION_CHANGED,
171 ENTRY_ACTIVATED,
172 SHOW_POPUP,
173 HAVE_SEL_CHANGED,
174 SORT_ORDER_CHANGED,
175 LAST_SIGNAL
178 enum
180 PROP_0,
181 PROP_DB,
182 PROP_SHELL_PLAYER,
183 PROP_MODEL,
184 PROP_SORTING_KEY,
185 PROP_IS_DRAG_SOURCE,
186 PROP_IS_DRAG_DEST,
187 PROP_PLAYING_STATE
190 G_DEFINE_TYPE (RBEntryView, rb_entry_view, GTK_TYPE_SCROLLED_WINDOW)
192 static guint rb_entry_view_signals[LAST_SIGNAL] = { 0 };
194 static GQuark rb_entry_view_column_always_visible;
196 static gboolean
197 type_ahead_search_func (GtkTreeModel *model,
198 gint column,
199 const gchar *key,
200 GtkTreeIter *iter,
201 gpointer search_data)
203 RhythmDBEntry *entry;
204 gchar *folded;
205 const gchar *entry_folded;
206 gboolean res;
208 gtk_tree_model_get (model, iter, 0, &entry, -1);
209 folded = rb_search_fold (key);
210 entry_folded = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE_FOLDED);
211 rhythmdb_entry_unref (entry);
213 if (entry_folded == NULL || folded == NULL)
214 return 1;
216 res = (strstr (entry_folded, folded) == NULL);
217 g_free (folded);
219 return res;
222 static void
223 rb_entry_view_class_init (RBEntryViewClass *klass)
225 GObjectClass *object_class = G_OBJECT_CLASS (klass);
226 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
228 object_class->dispose = rb_entry_view_dispose;
229 object_class->finalize = rb_entry_view_finalize;
230 object_class->constructor = rb_entry_view_constructor;
232 object_class->set_property = rb_entry_view_set_property;
233 object_class->get_property = rb_entry_view_get_property;
235 widget_class->grab_focus = rb_entry_view_grab_focus;
237 g_object_class_install_property (object_class,
238 PROP_DB,
239 g_param_spec_object ("db",
240 "RhythmDB",
241 "RhythmDB database",
242 RHYTHMDB_TYPE,
243 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
244 g_object_class_install_property (object_class,
245 PROP_SHELL_PLAYER,
246 g_param_spec_object ("shell-player",
247 "RBShellPlayer",
248 "RBShellPlayer object",
249 RB_TYPE_SHELL_PLAYER,
250 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
251 g_object_class_install_property (object_class,
252 PROP_MODEL,
253 g_param_spec_object ("model",
254 "RhythmDBQueryModel",
255 "RhythmDBQueryModel",
256 RHYTHMDB_TYPE_QUERY_MODEL,
257 G_PARAM_READWRITE));
258 g_object_class_install_property (object_class,
259 PROP_SORTING_KEY,
260 g_param_spec_string ("sort-key",
261 "sorting key",
262 "sorting key",
264 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
265 g_object_class_install_property (object_class,
266 PROP_IS_DRAG_SOURCE,
267 g_param_spec_boolean ("is-drag-source",
268 "is drag source",
269 "whether or not this is a drag source",
270 FALSE,
271 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
272 g_object_class_install_property (object_class,
273 PROP_IS_DRAG_DEST,
274 g_param_spec_boolean ("is-drag-dest",
275 "is drag dest",
276 "whether or not this is a drag dest",
277 FALSE,
278 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
279 g_object_class_install_property (object_class,
280 PROP_PLAYING_STATE,
281 g_param_spec_int ("playing-state",
282 "playing state",
283 "playback state for this entry view",
284 RB_ENTRY_VIEW_NOT_PLAYING,
285 RB_ENTRY_VIEW_PAUSED,
286 RB_ENTRY_VIEW_NOT_PLAYING,
287 G_PARAM_READWRITE));
288 rb_entry_view_signals[ENTRY_ADDED] =
289 g_signal_new ("entry-added",
290 G_OBJECT_CLASS_TYPE (object_class),
291 G_SIGNAL_RUN_LAST,
292 G_STRUCT_OFFSET (RBEntryViewClass, entry_added),
293 NULL, NULL,
294 g_cclosure_marshal_VOID__BOXED,
295 G_TYPE_NONE,
297 RHYTHMDB_TYPE_ENTRY);
298 rb_entry_view_signals[ENTRY_DELETED] =
299 g_signal_new ("entry-deleted",
300 G_OBJECT_CLASS_TYPE (object_class),
301 G_SIGNAL_RUN_LAST,
302 G_STRUCT_OFFSET (RBEntryViewClass, entry_deleted),
303 NULL, NULL,
304 g_cclosure_marshal_VOID__BOXED,
305 G_TYPE_NONE,
307 RHYTHMDB_TYPE_ENTRY);
308 rb_entry_view_signals[ENTRIES_REPLACED] =
309 g_signal_new ("entries-replaced",
310 G_OBJECT_CLASS_TYPE (object_class),
311 G_SIGNAL_RUN_LAST,
312 G_STRUCT_OFFSET (RBEntryViewClass, entries_replaced),
313 NULL, NULL,
314 g_cclosure_marshal_VOID__VOID,
315 G_TYPE_NONE,
317 rb_entry_view_signals[ENTRY_ACTIVATED] =
318 g_signal_new ("entry-activated",
319 G_OBJECT_CLASS_TYPE (object_class),
320 G_SIGNAL_RUN_LAST,
321 G_STRUCT_OFFSET (RBEntryViewClass, entry_activated),
322 NULL, NULL,
323 g_cclosure_marshal_VOID__BOXED,
324 G_TYPE_NONE,
326 RHYTHMDB_TYPE_ENTRY);
327 rb_entry_view_signals[SELECTION_CHANGED] =
328 g_signal_new ("selection-changed",
329 G_OBJECT_CLASS_TYPE (object_class),
330 G_SIGNAL_RUN_LAST,
331 G_STRUCT_OFFSET (RBEntryViewClass, selection_changed),
332 NULL, NULL,
333 g_cclosure_marshal_VOID__VOID,
334 G_TYPE_NONE,
336 rb_entry_view_signals[SHOW_POPUP] =
337 g_signal_new ("show_popup",
338 G_OBJECT_CLASS_TYPE (object_class),
339 G_SIGNAL_RUN_LAST,
340 G_STRUCT_OFFSET (RBEntryViewClass, show_popup),
341 NULL, NULL,
342 g_cclosure_marshal_VOID__BOOLEAN,
343 G_TYPE_NONE,
345 G_TYPE_BOOLEAN);
346 rb_entry_view_signals[HAVE_SEL_CHANGED] =
347 g_signal_new ("have_selection_changed",
348 G_OBJECT_CLASS_TYPE (object_class),
349 G_SIGNAL_RUN_LAST,
350 G_STRUCT_OFFSET (RBEntryViewClass, have_selection_changed),
351 NULL, NULL,
352 g_cclosure_marshal_VOID__BOOLEAN,
353 G_TYPE_NONE,
355 G_TYPE_BOOLEAN);
356 rb_entry_view_signals[SORT_ORDER_CHANGED] =
357 g_signal_new ("sort-order-changed",
358 G_OBJECT_CLASS_TYPE (object_class),
359 G_SIGNAL_RUN_LAST,
360 G_STRUCT_OFFSET (RBEntryViewClass, sort_order_changed),
361 NULL, NULL,
362 g_cclosure_marshal_VOID__VOID,
363 G_TYPE_NONE,
366 g_type_class_add_private (klass, sizeof (RBEntryViewPrivate));
368 rb_entry_view_column_always_visible = g_quark_from_static_string ("rb_entry_view_column_always_visible");
371 static void
372 rb_entry_view_init (RBEntryView *view)
374 GtkIconTheme *icon_theme;
376 view->priv = RB_ENTRY_VIEW_GET_PRIVATE (view);
378 icon_theme = gtk_icon_theme_get_default ();
380 view->priv->playing_pixbuf = gtk_icon_theme_load_icon (icon_theme,
381 "stock_media-play",
384 NULL);
385 view->priv->paused_pixbuf = gtk_icon_theme_load_icon (icon_theme,
386 "stock_media-pause",
389 NULL);
390 view->priv->error_pixbuf = gtk_icon_theme_load_icon (icon_theme,
391 "stock_dialog-error",
394 NULL);
396 view->priv->propid_column_map = g_hash_table_new (NULL, NULL);
397 view->priv->column_sort_data_map = g_hash_table_new_full (NULL, NULL, NULL, g_free);
398 view->priv->column_key_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
401 static void
402 rb_entry_view_dispose (GObject *object)
404 RBEntryView *view;
406 g_return_if_fail (object != NULL);
407 g_return_if_fail (RB_IS_ENTRY_VIEW (object));
409 view = RB_ENTRY_VIEW (object);
411 g_return_if_fail (view->priv != NULL);
413 if (view->priv->gconf_notification_id > 0) {
414 eel_gconf_notification_remove (view->priv->gconf_notification_id);
415 view->priv->gconf_notification_id = 0;
418 if (view->priv->sorting_gconf_notification_id > 0) {
419 eel_gconf_notification_remove (view->priv->sorting_gconf_notification_id);
420 view->priv->sorting_gconf_notification_id = 0;
423 if (view->priv->selection_changed_id > 0) {
424 g_source_remove (view->priv->selection_changed_id);
425 view->priv->selection_changed_id = 0;
428 if (view->priv->playing_pixbuf != NULL) {
429 g_object_unref (view->priv->playing_pixbuf);
430 view->priv->playing_pixbuf = NULL;
433 if (view->priv->paused_pixbuf != NULL) {
434 g_object_unref (view->priv->paused_pixbuf);
435 view->priv->paused_pixbuf = NULL;
438 if (view->priv->error_pixbuf != NULL) {
439 g_object_unref (view->priv->error_pixbuf);
440 view->priv->error_pixbuf = NULL;
443 if (view->priv->playing_model != NULL) {
444 g_object_unref (view->priv->playing_model);
445 view->priv->playing_model = NULL;
448 if (view->priv->model != NULL) {
449 g_object_unref (view->priv->model);
450 view->priv->model = NULL;
453 G_OBJECT_CLASS (rb_entry_view_parent_class)->dispose (object);
456 static void
457 rb_entry_view_finalize (GObject *object)
459 RBEntryView *view;
461 g_return_if_fail (object != NULL);
462 g_return_if_fail (RB_IS_ENTRY_VIEW (object));
464 view = RB_ENTRY_VIEW (object);
466 g_return_if_fail (view->priv != NULL);
468 g_hash_table_destroy (view->priv->propid_column_map);
469 g_hash_table_destroy (view->priv->column_sort_data_map);
470 g_hash_table_destroy (view->priv->column_key_map);
472 g_free (view->priv->sorting_key);
473 g_free (view->priv->sorting_column_name);
475 G_OBJECT_CLASS (rb_entry_view_parent_class)->finalize (object);
478 static void
479 rb_entry_view_set_shell_player_internal (RBEntryView *view,
480 RBShellPlayer *player)
482 if (view->priv->shell_player != NULL) {
483 g_signal_handlers_disconnect_by_func (view->priv->shell_player,
484 G_CALLBACK (rb_entry_view_playing_song_changed),
485 view);
488 view->priv->shell_player = player;
490 g_signal_connect_object (view->priv->shell_player,
491 "playing-song-changed",
492 G_CALLBACK (rb_entry_view_playing_song_changed),
493 view, 0);
496 static void
497 rb_entry_view_set_model_internal (RBEntryView *view,
498 RhythmDBQueryModel *model)
500 if (view->priv->model != NULL) {
501 g_signal_handlers_disconnect_by_func (view->priv->model,
502 G_CALLBACK (rb_entry_view_row_inserted_cb),
503 view);
504 g_signal_handlers_disconnect_by_func (view->priv->model,
505 G_CALLBACK (rb_entry_view_row_deleted_cb),
506 view);
507 g_signal_handlers_disconnect_by_func (view->priv->model,
508 G_CALLBACK (rb_entry_view_rows_reordered_cb),
509 view);
510 g_object_unref (view->priv->model);
513 gtk_tree_selection_unselect_all (view->priv->selection);
515 view->priv->model = model;
516 if (view->priv->model != NULL) {
517 g_object_ref (view->priv->model);
518 g_signal_connect_object (view->priv->model,
519 "row_inserted",
520 G_CALLBACK (rb_entry_view_row_inserted_cb),
521 view,
523 g_signal_connect_object (view->priv->model,
524 "row_deleted",
525 G_CALLBACK (rb_entry_view_row_deleted_cb),
526 view,
528 g_signal_connect_object (view->priv->model,
529 "rows_reordered",
530 G_CALLBACK (rb_entry_view_rows_reordered_cb),
531 view,
534 if (view->priv->sorting_column != NULL) {
535 rb_entry_view_resort_model (view);
538 gtk_tree_view_set_model (GTK_TREE_VIEW (view->priv->treeview),
539 GTK_TREE_MODEL (view->priv->model));
542 view->priv->have_selection = FALSE;
543 view->priv->have_complete_selection = FALSE;
545 g_signal_emit (G_OBJECT (view), rb_entry_view_signals[ENTRIES_REPLACED], 0);
548 static void
549 rb_entry_view_set_property (GObject *object,
550 guint prop_id,
551 const GValue *value,
552 GParamSpec *pspec)
554 RBEntryView *view = RB_ENTRY_VIEW (object);
556 switch (prop_id) {
557 case PROP_DB:
558 view->priv->db = g_value_get_object (value);
559 break;
560 case PROP_SHELL_PLAYER:
561 rb_entry_view_set_shell_player_internal (view, g_value_get_object (value));
562 break;
563 case PROP_SORTING_KEY:
564 g_free (view->priv->sorting_key);
565 view->priv->sorting_key = g_value_dup_string (value);
566 break;
567 case PROP_MODEL:
568 rb_entry_view_set_model_internal (view, g_value_get_object (value));
569 break;
570 case PROP_IS_DRAG_SOURCE:
571 view->priv->is_drag_source = g_value_get_boolean (value);
572 break;
573 case PROP_IS_DRAG_DEST:
574 view->priv->is_drag_dest = g_value_get_boolean (value);
575 break;
576 case PROP_PLAYING_STATE:
577 view->priv->playing_state = g_value_get_int (value);
579 /* redraw the playing entry, as the icon will have changed */
580 if (view->priv->playing_entry != NULL) {
581 rb_entry_view_emit_row_changed (view, view->priv->playing_entry);
583 break;
584 default:
585 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
586 break;
590 static void
591 rb_entry_view_get_property (GObject *object,
592 guint prop_id,
593 GValue *value,
594 GParamSpec *pspec)
596 RBEntryView *view = RB_ENTRY_VIEW (object);
598 switch (prop_id) {
599 case PROP_DB:
600 g_value_set_object (value, view->priv->db);
601 break;
602 case PROP_SHELL_PLAYER:
603 g_value_set_object (value, view->priv->shell_player);
604 break;
605 case PROP_SORTING_KEY:
606 g_value_set_string (value, view->priv->sorting_key);
607 break;
608 case PROP_IS_DRAG_SOURCE:
609 g_value_set_boolean (value, view->priv->is_drag_source);
610 break;
611 case PROP_IS_DRAG_DEST:
612 g_value_set_boolean (value, view->priv->is_drag_dest);
613 break;
614 case PROP_PLAYING_STATE:
615 g_value_set_int (value, view->priv->playing_state);
616 break;
617 default:
618 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
619 break;
623 RBEntryView *
624 rb_entry_view_new (RhythmDB *db,
625 GObject *shell_player,
626 const char *sort_key,
627 gboolean is_drag_source,
628 gboolean is_drag_dest)
630 RBEntryView *view;
632 view = RB_ENTRY_VIEW (g_object_new (RB_TYPE_ENTRY_VIEW,
633 "hadjustment", NULL,
634 "vadjustment", NULL,
635 "hscrollbar_policy", GTK_POLICY_AUTOMATIC,
636 "vscrollbar_policy", GTK_POLICY_ALWAYS,
637 "shadow_type", GTK_SHADOW_IN,
638 "db", db,
639 "shell-player", RB_SHELL_PLAYER (shell_player),
640 "sort-key", sort_key,
641 "is-drag-source", is_drag_source,
642 "is-drag-dest", is_drag_dest,
643 NULL));
645 g_return_val_if_fail (view->priv != NULL, NULL);
647 return view;
650 void
651 rb_entry_view_set_model (RBEntryView *view,
652 RhythmDBQueryModel *model)
654 g_object_set (view, "model", model, NULL);
657 /* Sweet name, eh? */
658 struct RBEntryViewCellDataFuncData {
659 RBEntryView *view;
660 RhythmDBPropType propid;
663 static void
664 rb_entry_view_playing_cell_data_func (GtkTreeViewColumn *column,
665 GtkCellRenderer *renderer,
666 GtkTreeModel *tree_model,
667 GtkTreeIter *iter,
668 RBEntryView *view)
670 RhythmDBEntry *entry;
671 GdkPixbuf *pixbuf = NULL;
673 entry = rhythmdb_query_model_iter_to_entry (view->priv->model, iter);
675 if (entry == NULL) {
676 return;
679 if (entry == view->priv->playing_entry) {
680 switch (view->priv->playing_state) {
681 case RB_ENTRY_VIEW_PLAYING:
682 pixbuf = view->priv->playing_pixbuf;
683 break;
684 case RB_ENTRY_VIEW_PAUSED:
685 pixbuf = view->priv->paused_pixbuf;
686 break;
687 default:
688 pixbuf = NULL;
689 break;
693 if (pixbuf == NULL && rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_PLAYBACK_ERROR)) {
694 pixbuf = view->priv->error_pixbuf;
697 g_object_set (renderer, "pixbuf", pixbuf, NULL);
699 rhythmdb_entry_unref (entry);
702 static void
703 rb_entry_view_rating_cell_data_func (GtkTreeViewColumn *column,
704 GtkCellRenderer *renderer,
705 GtkTreeModel *tree_model,
706 GtkTreeIter *iter,
707 RBEntryView *view)
709 RhythmDBEntry *entry;
711 entry = rhythmdb_query_model_iter_to_entry (view->priv->model, iter);
713 g_object_set (renderer,
714 "rating", rhythmdb_entry_get_double (entry, RHYTHMDB_PROP_RATING),
715 NULL);
717 rhythmdb_entry_unref (entry);
720 static void
721 rb_entry_view_long_cell_data_func (GtkTreeViewColumn *column,
722 GtkCellRenderer *renderer,
723 GtkTreeModel *tree_model,
724 GtkTreeIter *iter,
725 struct RBEntryViewCellDataFuncData *data)
727 RhythmDBEntry *entry;
728 char *str;
729 gulong val;
731 entry = rhythmdb_query_model_iter_to_entry (data->view->priv->model, iter);
733 val = rhythmdb_entry_get_ulong (entry, data->propid);
735 if (val > 0)
736 str = g_strdup_printf ("%lu", val);
737 else
738 str = g_strdup ("");
740 g_object_set (renderer, "text", str, NULL);
741 g_free (str);
742 rhythmdb_entry_unref (entry);
745 static void
746 rb_entry_view_play_count_cell_data_func (GtkTreeViewColumn *column,
747 GtkCellRenderer *renderer,
748 GtkTreeModel *tree_model,
749 GtkTreeIter * iter,
750 struct RBEntryViewCellDataFuncData *data)
752 RhythmDBEntry *entry;
753 gulong i;
754 char *str;
756 entry = rhythmdb_query_model_iter_to_entry (data->view->priv->model, iter);
758 i = rhythmdb_entry_get_ulong (entry, data->propid);
759 if (i == 0)
760 str = _("Never");
761 else
762 str = g_strdup_printf ("%ld", i);
764 g_object_set (renderer, "text", str, NULL);
765 if (i != 0)
766 g_free (str);
768 rhythmdb_entry_unref (entry);
771 static void
772 rb_entry_view_duration_cell_data_func (GtkTreeViewColumn *column,
773 GtkCellRenderer *renderer,
774 GtkTreeModel *tree_model,
775 GtkTreeIter *iter,
776 struct RBEntryViewCellDataFuncData *data)
778 RhythmDBEntry *entry;
779 gulong duration;
780 char *str;
782 entry = rhythmdb_query_model_iter_to_entry (data->view->priv->model, iter);
783 duration = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
785 str = rb_make_duration_string (duration);
786 g_object_set (renderer, "text", str, NULL);
787 g_free (str);
788 rhythmdb_entry_unref (entry);
791 static void
792 rb_entry_view_year_cell_data_func (GtkTreeViewColumn *column,
793 GtkCellRenderer *renderer,
794 GtkTreeModel *tree_model,
795 GtkTreeIter *iter,
796 struct RBEntryViewCellDataFuncData *data)
798 RhythmDBEntry *entry;
799 char str[255];
800 int julian;
801 GDate *date;
803 entry = rhythmdb_query_model_iter_to_entry (data->view->priv->model, iter);
804 julian = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DATE);
806 if (julian > 0) {
807 date = g_date_new_julian (julian);
808 g_date_strftime (str, sizeof (str), "%Y", date);
809 g_object_set (renderer, "text", str, NULL);
810 g_date_free (date);
811 } else {
812 g_object_set (renderer, "text", _("Unknown"), NULL);
815 rhythmdb_entry_unref (entry);
818 static void
819 rb_entry_view_quality_cell_data_func (GtkTreeViewColumn *column,
820 GtkCellRenderer *renderer,
821 GtkTreeModel *tree_model,
822 GtkTreeIter *iter,
823 struct RBEntryViewCellDataFuncData *data)
825 RhythmDBEntry *entry;
826 guint bitrate;
828 entry = rhythmdb_query_model_iter_to_entry (data->view->priv->model, iter);
830 bitrate = rhythmdb_entry_get_ulong (entry, data->propid);
832 if (bitrate > 0) {
833 char *s = g_strdup_printf (_("%u kbps"), (guint)bitrate);
834 g_object_set (renderer, "text", s, NULL);
835 g_free (s);
836 } else {
837 g_object_set (renderer, "text", _("Unknown"), NULL);
840 rhythmdb_entry_unref (entry);
843 static void
844 rb_entry_view_location_cell_data_func (GtkTreeViewColumn *column,
845 GtkCellRenderer *renderer,
846 GtkTreeModel *tree_model,
847 GtkTreeIter *iter,
848 struct RBEntryViewCellDataFuncData *data)
850 RhythmDBEntry *entry;
851 const char *location;
852 char *str;
854 entry = rhythmdb_query_model_iter_to_entry (data->view->priv->model, iter);
856 location = rhythmdb_entry_get_string (entry, data->propid);
857 str = gnome_vfs_unescape_string_for_display (location);
859 g_object_set (renderer, "text", str, NULL);
860 g_free (str);
862 rhythmdb_entry_unref (entry);
865 static void
866 rb_entry_view_string_cell_data_func (GtkTreeViewColumn *column,
867 GtkCellRenderer *renderer,
868 GtkTreeModel *tree_model,
869 GtkTreeIter *iter,
870 struct RBEntryViewCellDataFuncData *data)
872 RhythmDBEntry *entry;
873 const char *str;
875 entry = rhythmdb_query_model_iter_to_entry (data->view->priv->model, iter);
877 str = rhythmdb_entry_get_string (entry, data->propid);
878 if (str != NULL) {
879 g_object_set (renderer, "text", str, NULL);
882 rhythmdb_entry_unref (entry);
885 static void
886 rb_entry_view_sync_sorting (RBEntryView *view)
888 GtkTreeViewColumn *column;
889 gint direction;
890 char *column_name;
892 direction = GTK_SORT_ASCENDING;
893 column_name = NULL;
894 rb_entry_view_get_sorting_order (view, &column_name, &direction);
896 if (column_name == NULL) {
897 return;
900 column = g_hash_table_lookup (view->priv->column_key_map, column_name);
901 if (column == NULL) {
902 g_free (column_name);
903 return;
906 rb_debug ("Updating EntryView sort order to %s:%d", column_name, direction);
908 /* remove the old sorting indicator */
909 if (view->priv->sorting_column)
910 gtk_tree_view_column_set_sort_indicator (view->priv->sorting_column, FALSE);
912 /* set the sorting order and indicator of the new sorting column */
913 view->priv->sorting_column = column;
914 gtk_tree_view_column_set_sort_indicator (column, TRUE);
915 gtk_tree_view_column_set_sort_order (column, direction);
917 rb_debug ("emitting sort order changed");
918 g_signal_emit (G_OBJECT (view), rb_entry_view_signals[SORT_ORDER_CHANGED], 0);
920 g_free (column_name);
923 const char *
924 rb_entry_view_get_sorting_type (RBEntryView *view)
926 char *sorttype;
927 GString *key = g_string_new (view->priv->sorting_column_name);
929 g_string_append_c (key, ',');
931 switch (view->priv->sorting_order)
933 case GTK_SORT_ASCENDING:
934 g_string_append (key, "ascending");
935 break;
936 case GTK_SORT_DESCENDING:
937 g_string_append (key, "descending");
938 break;
939 default:
940 g_assert_not_reached ();
943 sorttype = g_strdup(key->str);
944 g_string_free (key, TRUE);
946 return sorttype;
949 void
950 rb_entry_view_set_sorting_type (RBEntryView *view,
951 const char *sorttype)
953 char **strs;
955 if (!sorttype || !strchr (sorttype, ',')) {
956 rb_debug ("malformed sort data: %s", (sorttype) ? sorttype : "(null)");
957 return;
960 strs = g_strsplit (sorttype, ",", 0);
962 g_free (view->priv->sorting_column_name);
963 view->priv->sorting_column_name = g_strdup(strs[0]);
965 if (!strcmp ("ascending", strs[1]))
966 view->priv->sorting_order = GTK_SORT_ASCENDING;
967 else if (!strcmp ("descending", strs[1]))
968 view->priv->sorting_order = GTK_SORT_DESCENDING;
969 else {
970 g_warning ("atttempting to sort in unknown direction");
971 view->priv->sorting_order = GTK_SORT_ASCENDING;
974 g_strfreev (strs);
976 rb_entry_view_sync_sorting (view);
979 void
980 rb_entry_view_get_sorting_order (RBEntryView *view,
981 char **column_name,
982 gint *sort_order)
984 g_return_if_fail (RB_IS_ENTRY_VIEW (view));
986 if (column_name != NULL) {
987 *column_name = g_strdup (view->priv->sorting_column_name);
990 if (sort_order != NULL) {
991 *sort_order = view->priv->sorting_order;
995 void
996 rb_entry_view_set_sorting_order (RBEntryView *view,
997 const char *column_name,
998 gint sort_order)
1000 if (column_name == NULL)
1001 return;
1003 g_free (view->priv->sorting_column_name);
1004 view->priv->sorting_column_name = g_strdup (column_name);
1005 view->priv->sorting_order = sort_order;
1007 rb_entry_view_sync_sorting (view);
1010 static void
1011 rb_entry_view_column_clicked_cb (GtkTreeViewColumn *column, RBEntryView *view)
1013 gint sort_order;
1014 char *clicked_column;
1016 rb_debug ("sorting on column %p", column);
1018 /* identify the clicked column, and then update the sorting order */
1019 clicked_column = (char*) g_object_get_data (G_OBJECT (column), "rb-entry-view-key");
1020 sort_order = view->priv->sorting_order;
1022 if (view->priv->sorting_column_name
1023 && !strcmp(clicked_column, view->priv->sorting_column_name)
1024 && (sort_order == GTK_SORT_ASCENDING))
1025 sort_order = GTK_SORT_DESCENDING;
1026 else
1027 sort_order = GTK_SORT_ASCENDING;
1029 rb_entry_view_set_sorting_order (view, clicked_column, sort_order);
1031 /* update the sort order in GConf */
1032 if (view->priv->sorting_key)
1033 eel_gconf_set_string (view->priv->sorting_key, rb_entry_view_get_sorting_type(view));
1036 void
1037 rb_entry_view_append_column (RBEntryView *view,
1038 RBEntryViewColumn coltype,
1039 gboolean always_visible)
1041 GtkTreeViewColumn *column;
1042 GtkCellRenderer *renderer = NULL;
1043 struct RBEntryViewCellDataFuncData *cell_data;
1044 const char *title = NULL;
1045 const char *key = NULL;
1046 const char *strings[4] = {0};
1047 GtkTreeCellDataFunc cell_data_func = NULL;
1048 GCompareDataFunc sort_func = NULL;
1049 RhythmDBPropType propid;
1050 RhythmDBPropType sort_propid = RHYTHMDB_NUM_PROPERTIES;
1051 gboolean ellipsize = FALSE;
1052 gboolean resizable = TRUE;
1053 gint column_width = -1;
1055 column = gtk_tree_view_column_new ();
1057 cell_data = g_new0 (struct RBEntryViewCellDataFuncData, 1);
1058 cell_data->view = view;
1060 switch (coltype) {
1061 case RB_ENTRY_VIEW_COL_TRACK_NUMBER:
1062 propid = RHYTHMDB_PROP_TRACK_NUMBER;
1063 cell_data->propid = propid;
1064 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_long_cell_data_func;
1065 sort_func = (GCompareDataFunc) rhythmdb_query_model_track_sort_func;
1066 title = _("Track");
1067 key = "Track";
1068 strings[0] = title;
1069 strings[1] = "9999";
1070 break;
1071 case RB_ENTRY_VIEW_COL_TITLE:
1072 propid = RHYTHMDB_PROP_TITLE;
1073 cell_data->propid = propid;
1074 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1075 sort_propid = RHYTHMDB_PROP_TITLE_SORT_KEY;
1076 sort_func = (GCompareDataFunc) rhythmdb_query_model_string_sort_func;
1077 title = _("Title");
1078 key = "Title";
1079 ellipsize = TRUE;
1080 break;
1081 case RB_ENTRY_VIEW_COL_ARTIST:
1082 propid = RHYTHMDB_PROP_ARTIST;
1083 cell_data->propid = propid;
1084 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1085 sort_propid = RHYTHMDB_PROP_ARTIST_SORT_KEY;
1086 sort_func = (GCompareDataFunc) rhythmdb_query_model_artist_sort_func;
1087 title = _("Artist");
1088 key = "Artist";
1089 ellipsize = TRUE;
1090 break;
1091 case RB_ENTRY_VIEW_COL_ALBUM:
1092 propid = RHYTHMDB_PROP_ALBUM;
1093 cell_data->propid = propid;
1094 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1095 sort_propid = RHYTHMDB_PROP_ALBUM_SORT_KEY;
1096 sort_func = (GCompareDataFunc) rhythmdb_query_model_album_sort_func;
1097 title = _("Album");
1098 key = "Album";
1099 ellipsize = TRUE;
1100 break;
1101 case RB_ENTRY_VIEW_COL_GENRE:
1102 propid = RHYTHMDB_PROP_GENRE;
1103 cell_data->propid = propid;
1104 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1105 sort_propid = RHYTHMDB_PROP_GENRE_SORT_KEY;
1106 sort_func = (GCompareDataFunc) rhythmdb_query_model_genre_sort_func;
1107 title = _("Genre");
1108 key = "Genre";
1109 ellipsize = TRUE;
1110 break;
1111 case RB_ENTRY_VIEW_COL_DURATION:
1112 propid = RHYTHMDB_PROP_DURATION;
1113 cell_data->propid = propid;
1114 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_duration_cell_data_func;
1115 sort_propid = cell_data->propid;
1116 sort_func = (GCompareDataFunc) rhythmdb_query_model_ulong_sort_func;
1117 title = _("Time");
1118 key = "Time";
1119 strings[0] = title;
1120 strings[1] = "000:00";
1121 strings[2] = _("Unknown");
1122 break;
1123 case RB_ENTRY_VIEW_COL_YEAR:
1124 propid = RHYTHMDB_PROP_DATE;
1125 cell_data->propid = propid;
1126 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_year_cell_data_func;
1127 sort_propid = cell_data->propid;
1128 sort_func = (GCompareDataFunc) rhythmdb_query_model_date_sort_func;
1129 title = _("Year");
1130 key = "Year";
1131 strings[0] = title;
1132 strings[1] = "0000";
1133 strings[2] = _("Unknown");
1134 break;
1135 case RB_ENTRY_VIEW_COL_QUALITY:
1136 propid = RHYTHMDB_PROP_BITRATE;
1137 cell_data->propid = propid;
1138 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_quality_cell_data_func;
1139 sort_propid = cell_data->propid;
1140 sort_func = (GCompareDataFunc) rhythmdb_query_model_ulong_sort_func;
1141 title = _("Quality");
1142 key = "Quality";
1143 strings[0] = title;
1144 strings[1] = _("000 kbps");
1145 strings[2] = _("Unknown");
1146 break;
1147 case RB_ENTRY_VIEW_COL_RATING:
1148 propid = RHYTHMDB_PROP_RATING;
1149 sort_func = (GCompareDataFunc) rhythmdb_query_model_double_ceiling_sort_func;
1151 gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &column_width, NULL);
1152 column_width = column_width * 5 + 5;
1153 resizable = FALSE;
1154 title = _("Rating");
1155 key = "Rating";
1157 renderer = rb_cell_renderer_rating_new ();
1158 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1159 gtk_tree_view_column_set_cell_data_func (column, renderer,
1160 (GtkTreeCellDataFunc)
1161 rb_entry_view_rating_cell_data_func,
1162 view,
1163 NULL);
1164 g_signal_connect_object (renderer,
1165 "rated",
1166 G_CALLBACK (rb_entry_view_rated_cb),
1167 view,
1169 break;
1170 case RB_ENTRY_VIEW_COL_PLAY_COUNT:
1171 propid = RHYTHMDB_PROP_PLAY_COUNT;
1172 cell_data->propid = propid;
1173 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_play_count_cell_data_func;
1174 sort_propid = cell_data->propid;
1175 sort_func = (GCompareDataFunc) rhythmdb_query_model_ulong_sort_func;
1176 title = _("Play Count");
1177 key = "PlayCount";
1178 strings[0] = title;
1179 strings[1] = _("Never");
1180 strings[2] = "9999";
1181 break;
1182 case RB_ENTRY_VIEW_COL_LAST_PLAYED:
1183 propid = RHYTHMDB_PROP_LAST_PLAYED;
1184 cell_data->propid = RHYTHMDB_PROP_LAST_PLAYED_STR;
1185 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1186 sort_propid = RHYTHMDB_PROP_LAST_PLAYED;
1187 sort_func = (GCompareDataFunc) rhythmdb_query_model_ulong_sort_func;
1188 title = _("Last Played");
1189 key = "LastPlayed";
1190 strings[0] = title;
1191 strings[1] = rb_entry_view_get_time_date_column_sample ();
1192 strings[2] = _("Never");
1193 break;
1194 case RB_ENTRY_VIEW_COL_FIRST_SEEN:
1195 propid = RHYTHMDB_PROP_FIRST_SEEN;
1196 cell_data->propid = RHYTHMDB_PROP_FIRST_SEEN_STR;
1197 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1198 sort_propid = RHYTHMDB_PROP_FIRST_SEEN;
1199 sort_func = (GCompareDataFunc) rhythmdb_query_model_ulong_sort_func;
1200 title = _("Date Added");
1201 key = "FirstSeen";
1202 strings[0] = title;
1203 strings[1] = rb_entry_view_get_time_date_column_sample ();
1204 break;
1205 case RB_ENTRY_VIEW_COL_LAST_SEEN:
1206 propid = RHYTHMDB_PROP_LAST_SEEN;
1207 cell_data->propid = RHYTHMDB_PROP_LAST_SEEN_STR;
1208 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1209 sort_propid = RHYTHMDB_PROP_LAST_SEEN;
1210 sort_func = (GCompareDataFunc) rhythmdb_query_model_ulong_sort_func;
1211 title = _("Last Seen");
1212 key = "LastSeen";
1213 strings[0] = title;
1214 strings[1] = rb_entry_view_get_time_date_column_sample ();
1215 break;
1216 case RB_ENTRY_VIEW_COL_LOCATION:
1217 propid = RHYTHMDB_PROP_LOCATION;
1218 cell_data->propid = RHYTHMDB_PROP_LOCATION;
1219 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_location_cell_data_func;
1220 sort_propid = RHYTHMDB_PROP_LOCATION;
1221 sort_func = (GCompareDataFunc) rhythmdb_query_model_location_sort_func;
1222 title = _("Location");
1223 key = "Location";
1224 ellipsize = TRUE;
1225 break;
1226 case RB_ENTRY_VIEW_COL_ERROR:
1227 propid = RHYTHMDB_PROP_PLAYBACK_ERROR;
1228 cell_data->propid = RHYTHMDB_PROP_PLAYBACK_ERROR;
1229 cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
1230 title = _("Error");
1231 key = "Error";
1232 ellipsize = TRUE;
1233 break;
1234 default:
1235 g_assert_not_reached ();
1236 propid = -1;
1237 break;
1240 if (sort_propid == RHYTHMDB_NUM_PROPERTIES)
1241 sort_propid = propid;
1243 if (renderer == NULL) {
1244 renderer = gtk_cell_renderer_text_new ();
1245 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1246 gtk_tree_view_column_set_cell_data_func (column, renderer,
1247 cell_data_func, cell_data, g_free);
1248 } else {
1249 g_free (cell_data);
1253 * Columns must either be expanding (ellipsized) or have a
1254 * fixed minimum width specified. Otherwise, gtk+ gives them a
1255 * width of 0.
1257 if (ellipsize) {
1258 g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
1259 gtk_tree_view_column_set_expand (GTK_TREE_VIEW_COLUMN (column), TRUE);
1260 } else if (column_width != -1) {
1261 gtk_tree_view_column_set_fixed_width (column, column_width);
1262 } else {
1263 rb_entry_view_set_fixed_column_width (view, column, renderer, strings);
1266 if (resizable)
1267 gtk_tree_view_column_set_resizable (column, TRUE);
1269 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1270 gtk_tree_view_column_set_clickable (column, TRUE);
1272 if (always_visible)
1273 g_object_set_qdata (G_OBJECT (column),
1274 rb_entry_view_column_always_visible,
1275 GINT_TO_POINTER (1));
1277 g_hash_table_insert (view->priv->propid_column_map, GINT_TO_POINTER (propid), column);
1279 rb_entry_view_append_column_custom (view, column, title, key, sort_func, GINT_TO_POINTER (sort_propid));
1282 void
1283 rb_entry_view_append_column_custom (RBEntryView *view,
1284 GtkTreeViewColumn *column,
1285 const char *title,
1286 const char *key,
1287 GCompareDataFunc sort_func,
1288 gpointer data)
1290 rb_entry_view_insert_column_custom (view, column, title, key, sort_func, data, -1);
1293 void
1294 rb_entry_view_insert_column_custom (RBEntryView *view,
1295 GtkTreeViewColumn *column,
1296 const char *title,
1297 const char *key,
1298 GCompareDataFunc sort_func,
1299 gpointer data,
1300 gint position)
1302 struct RBEntryViewColumnSortData *sortdata;
1304 gtk_tree_view_column_set_title (column, title);
1305 gtk_tree_view_column_set_reorderable (column, FALSE);
1307 g_signal_connect_object (column, "clicked",
1308 G_CALLBACK (rb_entry_view_column_clicked_cb),
1309 view, 0);
1311 g_object_set_data_full (G_OBJECT (column), "rb-entry-view-key",
1312 g_strdup (key), g_free);
1314 rb_debug ("appending column: %p (%s)", column, title);
1316 gtk_tree_view_insert_column (GTK_TREE_VIEW (view->priv->treeview), column, position);
1318 if (sort_func != NULL) {
1319 sortdata = g_new (struct RBEntryViewColumnSortData, 1);
1320 sortdata->func = (GCompareDataFunc) sort_func;
1321 sortdata->data = data;
1322 g_hash_table_insert (view->priv->column_sort_data_map, column, sortdata);
1324 g_hash_table_insert (view->priv->column_key_map, g_strdup (key), column);
1326 rb_entry_view_sync_columns_visible (view);
1327 rb_entry_view_sync_sorting (view);
1330 void
1331 rb_entry_view_set_columns_clickable (RBEntryView *view,
1332 gboolean clickable)
1334 GList *columns, *tem;
1336 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (view->priv->treeview));
1337 for (tem = columns; tem; tem = tem->next) {
1338 /* only columns we can sort on should be clickable */
1339 GtkTreeViewColumn *column = (GtkTreeViewColumn *) tem->data;
1340 if (g_hash_table_lookup (view->priv->column_sort_data_map, column) != NULL)
1341 gtk_tree_view_column_set_clickable (tem->data, clickable);
1343 g_list_free (columns);
1346 static GObject *
1347 rb_entry_view_constructor (GType type,
1348 guint n_construct_properties,
1349 GObjectConstructParam *construct_properties)
1351 RBEntryView *view;
1352 RBEntryViewClass *klass;
1353 klass = RB_ENTRY_VIEW_CLASS (g_type_class_peek (RB_TYPE_ENTRY_VIEW));
1355 view = RB_ENTRY_VIEW (G_OBJECT_CLASS (rb_entry_view_parent_class)
1356 ->constructor (type, n_construct_properties, construct_properties));
1358 view->priv->treeview = GTK_WIDGET (gtk_tree_view_new ());
1359 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (view->priv->treeview), TRUE);
1361 gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (view->priv->treeview),
1362 type_ahead_search_func,
1363 NULL, NULL);
1365 g_signal_connect_object (view->priv->treeview,
1366 "button_press_event",
1367 G_CALLBACK (rb_entry_view_button_press_cb),
1368 view,
1370 g_signal_connect_object (view->priv->treeview,
1371 "row_activated",
1372 G_CALLBACK (rb_entry_view_row_activated_cb),
1373 view,
1375 g_signal_connect_object (view->priv->treeview,
1376 "popup_menu",
1377 G_CALLBACK (rb_entry_view_popup_menu_cb),
1378 view,
1380 view->priv->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view->priv->treeview));
1381 g_signal_connect_object (view->priv->selection,
1382 "changed",
1383 G_CALLBACK (rb_entry_view_selection_changed_cb),
1384 view,
1387 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view->priv->treeview), TRUE);
1388 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view->priv->treeview), TRUE);
1389 gtk_tree_selection_set_mode (view->priv->selection, GTK_SELECTION_MULTIPLE);
1391 if (view->priv->is_drag_source) {
1392 rb_tree_dnd_add_drag_source_support (GTK_TREE_VIEW (view->priv->treeview),
1393 GDK_BUTTON1_MASK,
1394 rb_entry_view_drag_types,
1395 G_N_ELEMENTS (rb_entry_view_drag_types),
1396 GDK_ACTION_COPY);
1399 if (view->priv->is_drag_dest) {
1400 rb_tree_dnd_add_drag_dest_support (GTK_TREE_VIEW (view->priv->treeview),
1401 RB_TREE_DEST_CAN_DROP_BETWEEN | RB_TREE_DEST_EMPTY_VIEW_DROP,
1402 rb_entry_view_drag_types,
1403 G_N_ELEMENTS (rb_entry_view_drag_types),
1404 GDK_ACTION_COPY | GDK_ACTION_MOVE);
1407 gtk_container_add (GTK_CONTAINER (view), view->priv->treeview);
1410 GtkTreeViewColumn *column;
1411 GtkTooltips *tooltip;
1412 GtkCellRenderer *renderer;
1413 GtkWidget *image_widget;
1414 gint width;
1416 tooltip = gtk_tooltips_new ();
1418 /* Playing icon column */
1419 column = GTK_TREE_VIEW_COLUMN (gtk_tree_view_column_new ());
1420 renderer = rb_cell_renderer_pixbuf_new ();
1421 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1422 gtk_tree_view_column_set_cell_data_func (column, renderer,
1423 (GtkTreeCellDataFunc)
1424 rb_entry_view_playing_cell_data_func,
1425 view,
1426 NULL);
1428 image_widget = gtk_image_new_from_icon_name ("stock_volume-max", GTK_ICON_SIZE_MENU);
1429 gtk_tree_view_column_set_widget (column, image_widget);
1430 gtk_widget_show (image_widget);
1432 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1433 gtk_tree_view_column_set_clickable (column, FALSE);
1434 gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, NULL);
1435 gtk_tree_view_column_set_fixed_width (column, width + 5);
1436 gtk_tree_view_append_column (GTK_TREE_VIEW (view->priv->treeview), column);
1437 g_signal_connect_swapped (renderer,
1438 "pixbuf-clicked",
1439 G_CALLBACK (rb_entry_view_pixbuf_clicked_cb),
1440 view);
1442 gtk_tooltips_set_tip (GTK_TOOLTIPS (tooltip), GTK_WIDGET (column->button),
1443 _("Now Playing"), NULL);
1446 view->priv->gconf_notification_id =
1447 eel_gconf_notification_add (CONF_UI_COLUMNS_SETUP,
1448 rb_entry_view_columns_config_changed_cb,
1449 view);
1450 if (view->priv->sorting_key) {
1451 view->priv->sorting_gconf_notification_id =
1452 eel_gconf_notification_add (view->priv->sorting_key,
1453 rb_entry_view_sort_key_changed_cb,
1454 view);
1457 if (view->priv->sorting_key) {
1458 char *s = eel_gconf_get_string (view->priv->sorting_key);
1459 rb_entry_view_set_sorting_type (view, s);
1460 g_free (s);
1464 RhythmDBQueryModel *query_model;
1465 query_model = rhythmdb_query_model_new_empty (view->priv->db);
1466 rb_entry_view_set_model (view, RHYTHMDB_QUERY_MODEL (query_model));
1467 g_object_unref (query_model);
1470 return G_OBJECT (view);
1473 static void
1474 rb_entry_view_rated_cb (RBCellRendererRating *cellrating,
1475 const char *path_string,
1476 double rating,
1477 RBEntryView *view)
1479 GtkTreePath *path;
1480 RhythmDBEntry *entry;
1481 GValue value = { 0, };
1483 g_return_if_fail (rating >= 0 && rating <= 5 );
1484 g_return_if_fail (path_string != NULL);
1486 path = gtk_tree_path_new_from_string (path_string);
1487 entry = rhythmdb_query_model_tree_path_to_entry (view->priv->model, path);
1488 gtk_tree_path_free (path);
1490 g_value_init (&value, G_TYPE_DOUBLE);
1491 g_value_set_double (&value, rating);
1492 rhythmdb_entry_set (view->priv->db, entry, RHYTHMDB_PROP_RATING, &value);
1493 g_value_unset (&value);
1495 rhythmdb_commit (view->priv->db);
1497 rhythmdb_entry_unref (entry);
1500 static void
1501 rb_entry_view_pixbuf_clicked_cb (RBEntryView *view,
1502 const char *path_string,
1503 RBCellRendererPixbuf *cellpixbuf)
1505 GtkTreePath *path;
1506 RhythmDBEntry *entry;
1507 const gchar *error;
1509 g_return_if_fail (path_string != NULL);
1511 path = gtk_tree_path_new_from_string (path_string);
1512 entry = rhythmdb_query_model_tree_path_to_entry (view->priv->model, path);
1514 gtk_tree_path_free (path);
1516 error = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_PLAYBACK_ERROR);
1517 if (error) {
1518 rb_error_dialog (NULL, _("Playback Error"), "%s", error);
1521 rhythmdb_entry_unref (entry);
1524 static void
1525 rb_entry_view_playing_song_changed (RBShellPlayer *player,
1526 RhythmDBEntry *entry,
1527 RBEntryView *view)
1529 gboolean realized, visible;
1530 GtkTreeIter iter;
1532 g_return_if_fail (RB_IS_ENTRY_VIEW (view));
1534 if (view->priv->playing_entry != NULL) {
1535 if (view->priv->playing_state != RB_ENTRY_VIEW_NOT_PLAYING)
1536 rb_entry_view_emit_row_changed (view, view->priv->playing_entry);
1537 g_object_unref (view->priv->playing_model);
1540 view->priv->playing_entry = entry;
1541 view->priv->playing_model = view->priv->model;
1542 g_object_ref (view->priv->playing_model);
1544 if (view->priv->playing_state != RB_ENTRY_VIEW_NOT_PLAYING) {
1545 if (view->priv->playing_entry != NULL) {
1546 view->priv->playing_entry_in_view =
1547 rb_entry_view_emit_row_changed (view, view->priv->playing_entry);
1550 if (view->priv->playing_entry
1551 && view->priv->playing_entry_in_view) {
1552 rb_entry_view_entry_is_visible (view, view->priv->playing_entry,
1553 &realized, &visible, &iter);
1554 if (realized && !visible)
1555 rb_entry_view_scroll_to_iter (view, &iter);
1560 static gboolean
1561 harvest_entries (GtkTreeModel *model,
1562 GtkTreePath *path,
1563 GtkTreeIter *iter,
1564 GList **list)
1566 RhythmDBEntry *entry;
1568 gtk_tree_model_get (model, iter, 0, &entry, -1);
1570 *list = g_list_prepend (*list, entry);
1572 return FALSE;
1575 GList *
1576 rb_entry_view_get_selected_entries (RBEntryView *view)
1578 GList *list = NULL;
1580 gtk_tree_selection_selected_foreach (view->priv->selection,
1581 (GtkTreeSelectionForeachFunc) harvest_entries,
1582 (gpointer) &list);
1584 list = g_list_reverse (list);
1585 return list;
1588 static gboolean
1589 rb_entry_view_button_press_cb (GtkTreeView *treeview,
1590 GdkEventButton *event,
1591 RBEntryView *view)
1593 if (event->button == 3) {
1594 GtkTreePath *path;
1595 RhythmDBEntry *entry;
1597 gtk_tree_view_get_path_at_pos (treeview, event->x, event->y, &path, NULL, NULL, NULL);
1598 if (path != NULL) {
1599 GList *selected;
1600 entry = rhythmdb_query_model_tree_path_to_entry (view->priv->model, path);
1602 selected = rb_entry_view_get_selected_entries (view);
1604 if (!g_list_find (selected, entry))
1605 rb_entry_view_select_entry (view, entry);
1607 g_list_free (selected);
1609 rhythmdb_entry_unref (entry);
1611 g_signal_emit (G_OBJECT (view), rb_entry_view_signals[SHOW_POPUP], 0, (path != NULL));
1612 return TRUE;
1615 return FALSE;
1618 static gboolean
1619 rb_entry_view_popup_menu_cb (GtkTreeView *treeview,
1620 RBEntryView *view)
1622 if (gtk_tree_selection_count_selected_rows (gtk_tree_view_get_selection (treeview)) == 0)
1623 return FALSE;
1625 g_signal_emit (G_OBJECT (view), rb_entry_view_signals[SHOW_POPUP], 0);
1626 return TRUE;
1629 static gboolean
1630 rb_entry_view_emit_selection_changed (RBEntryView *view)
1632 gboolean available;
1633 gint sel_count;
1635 GDK_THREADS_ENTER ();
1636 sel_count = gtk_tree_selection_count_selected_rows (view->priv->selection);
1637 available = (sel_count > 0);
1639 if (available != view->priv->have_selection) {
1640 gint entry_count;
1642 entry_count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (view->priv->model), NULL);
1643 view->priv->have_complete_selection = (sel_count == entry_count);
1645 view->priv->have_selection = available;
1647 g_signal_emit (G_OBJECT (view), rb_entry_view_signals[HAVE_SEL_CHANGED], 0, available);
1650 view->priv->selection_changed_id = 0;
1651 g_signal_emit (G_OBJECT (view), rb_entry_view_signals[SELECTION_CHANGED], 0);
1653 GDK_THREADS_LEAVE ();
1654 return FALSE;
1657 static void
1658 rb_entry_view_selection_changed_cb (GtkTreeSelection *selection,
1659 RBEntryView *view)
1661 if (view->priv->selection_changed_id == 0)
1662 view->priv->selection_changed_id = g_idle_add ((GSourceFunc)rb_entry_view_emit_selection_changed, view);
1665 gboolean
1666 rb_entry_view_have_selection (RBEntryView *view)
1668 return view->priv->have_selection;
1671 gboolean
1672 rb_entry_view_have_complete_selection (RBEntryView *view)
1674 return view->priv->have_complete_selection;
1677 static void
1678 rb_entry_view_row_activated_cb (GtkTreeView *treeview,
1679 GtkTreePath *path,
1680 GtkTreeViewColumn *column,
1681 RBEntryView *view)
1683 RhythmDBEntry *entry;
1685 rb_debug ("row activated");
1687 entry = rhythmdb_query_model_tree_path_to_entry (view->priv->model, path);
1689 rb_debug ("emitting entry activated");
1690 g_signal_emit (G_OBJECT (view), rb_entry_view_signals[ENTRY_ACTIVATED], 0, entry);
1692 rhythmdb_entry_unref (entry);
1695 static void
1696 rb_entry_view_row_inserted_cb (GtkTreeModel *model,
1697 GtkTreePath *path,
1698 GtkTreeIter *iter,
1699 RBEntryView *view)
1701 RhythmDBEntry *entry = rhythmdb_query_model_tree_path_to_entry (RHYTHMDB_QUERY_MODEL (model), path);
1703 rb_debug ("row added");
1704 g_signal_emit (G_OBJECT (view), rb_entry_view_signals[ENTRY_ADDED], 0, entry);
1705 rhythmdb_entry_unref (entry);
1708 static void
1709 rb_entry_view_row_deleted_cb (GtkTreeModel *model,
1710 GtkTreePath *path,
1711 RBEntryView *view)
1713 RhythmDBEntry *entry = rhythmdb_query_model_tree_path_to_entry (RHYTHMDB_QUERY_MODEL (model), path);
1715 rb_debug ("row deleted");
1716 g_signal_emit (G_OBJECT (view), rb_entry_view_signals[ENTRY_DELETED], 0, entry);
1717 rhythmdb_entry_unref (entry);
1720 static void
1721 rb_entry_view_rows_reordered_cb (GtkTreeModel *model,
1722 GtkTreePath *path,
1723 GtkTreeIter *iter,
1724 gint *order,
1725 RBEntryView *view)
1727 GList *selected_rows;
1728 GList *i;
1729 gint model_size;
1730 gboolean scrolled = FALSE;
1732 rb_debug ("rows reordered");
1734 model_size = gtk_tree_model_iter_n_children (model, NULL);
1736 /* check if a selected row was moved; if so, we'll
1737 * need to move the selection too.
1739 selected_rows = gtk_tree_selection_get_selected_rows (view->priv->selection,
1740 NULL);
1741 for (i = selected_rows; i != NULL; i = i->next) {
1742 GtkTreePath *path = (GtkTreePath *)i->data;
1743 gint index = gtk_tree_path_get_indices (path)[0];
1744 gint newindex;
1745 if (order[index] != index) {
1746 GtkTreePath *newpath;
1747 gtk_tree_selection_unselect_path (view->priv->selection, path);
1749 for (newindex = 0; newindex < model_size; newindex++) {
1750 if (order[newindex] == index) {
1751 newpath = gtk_tree_path_new_from_indices (newindex, -1);
1752 gtk_tree_selection_select_path (view->priv->selection, newpath);
1753 if (!scrolled) {
1754 GtkTreeViewColumn *col;
1755 GtkTreeView *treeview = GTK_TREE_VIEW (view->priv->treeview);
1757 col = gtk_tree_view_get_column (treeview, 0);
1758 gtk_tree_view_scroll_to_cell (treeview, newpath, col, TRUE, 0.5, 0.0);
1759 scrolled = TRUE;
1761 gtk_tree_path_free (newpath);
1762 break;
1769 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1770 g_list_free (selected_rows);
1772 gtk_widget_queue_draw (GTK_WIDGET (view));
1775 void
1776 rb_entry_view_select_all (RBEntryView *view)
1778 gtk_tree_selection_select_all (view->priv->selection);
1781 void
1782 rb_entry_view_select_none (RBEntryView *view)
1784 gtk_tree_selection_unselect_all (view->priv->selection);
1787 void
1788 rb_entry_view_select_entry (RBEntryView *view,
1789 RhythmDBEntry *entry)
1791 GtkTreeIter iter;
1793 if (entry == NULL)
1794 return;
1796 rb_entry_view_select_none (view);
1798 if (rhythmdb_query_model_entry_to_iter (view->priv->model,
1799 entry, &iter)) {
1800 gtk_tree_selection_select_iter (view->priv->selection, &iter);
1804 void
1805 rb_entry_view_scroll_to_entry (RBEntryView *view,
1806 RhythmDBEntry *entry)
1808 GtkTreeIter iter;
1810 if (rhythmdb_query_model_entry_to_iter (view->priv->model,
1811 entry, &iter)) {
1812 rb_entry_view_scroll_to_iter (view, &iter);
1816 static void
1817 rb_entry_view_scroll_to_iter (RBEntryView *view,
1818 GtkTreeIter *iter)
1820 GtkTreePath *path;
1822 /* It's possible to we can be asked to scroll the play queue's entry
1823 * view to the playing entry before the view has ever been displayed.
1824 * This will result in gtk+ warnings, so we avoid it in this case.
1826 if (!GTK_WIDGET_REALIZED (view))
1827 return;
1829 path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->priv->model), iter);
1830 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (view->priv->treeview), path,
1831 gtk_tree_view_get_column (GTK_TREE_VIEW (view->priv->treeview), 0),
1832 TRUE, 0.5, 0.0);
1833 gtk_tree_view_set_cursor (GTK_TREE_VIEW (view->priv->treeview), path,
1834 gtk_tree_view_get_column (GTK_TREE_VIEW (view->priv->treeview), 0), FALSE);
1836 gtk_tree_path_free (path);
1839 gboolean
1840 rb_entry_view_get_entry_visible (RBEntryView *view,
1841 RhythmDBEntry *entry)
1843 GtkTreeIter unused;
1844 gboolean realized, visible;
1846 if (view->priv->playing_model != view->priv->model)
1847 return FALSE;
1849 rb_entry_view_entry_is_visible (view, entry, &realized, &visible,
1850 &unused);
1851 return realized && visible;
1854 gboolean
1855 rb_entry_view_get_entry_contained (RBEntryView *view,
1856 RhythmDBEntry *entry)
1858 GtkTreeIter unused;
1860 return rhythmdb_query_model_entry_to_iter (view->priv->model,
1861 entry, &unused);
1864 static void
1865 rb_entry_view_entry_is_visible (RBEntryView *view,
1866 RhythmDBEntry *entry,
1867 gboolean *realized,
1868 gboolean *visible,
1869 GtkTreeIter *iter)
1871 GtkTreePath *path;
1872 GdkRectangle rect;
1874 *realized = FALSE;
1875 *visible = FALSE;
1877 g_return_if_fail (entry != NULL);
1879 if (!GTK_WIDGET_REALIZED (view))
1880 return;
1882 *realized = TRUE;
1884 if (!rhythmdb_query_model_entry_to_iter (view->priv->model,
1885 entry, iter))
1886 return;
1888 path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->priv->model), iter);
1889 gtk_tree_view_get_cell_area (GTK_TREE_VIEW (view->priv->treeview),
1890 path,
1891 gtk_tree_view_get_column (GTK_TREE_VIEW (view->priv->treeview), 0),
1892 &rect);
1894 gtk_tree_path_free (path);
1896 *visible = (rect.y != 0 && rect.height != 0);
1899 void
1900 rb_entry_view_enable_drag_source (RBEntryView *view,
1901 const GtkTargetEntry *targets,
1902 int n_targets)
1904 g_return_if_fail (view != NULL);
1906 rb_tree_dnd_add_drag_source_support (GTK_TREE_VIEW (view->priv->treeview),
1907 GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
1908 targets, n_targets, GDK_ACTION_COPY);
1911 static void
1912 rb_entry_view_sort_key_changed_cb (GConfClient* client,
1913 guint cnxn_id,
1914 GConfEntry *entry,
1915 gpointer user_data)
1917 RBEntryView *view = user_data;
1919 g_return_if_fail (RB_IS_ENTRY_VIEW (view));
1921 rb_entry_view_set_sorting_type (view, eel_gconf_get_string (view->priv->sorting_key));
1924 static void
1925 rb_entry_view_columns_config_changed_cb (GConfClient* client,
1926 guint cnxn_id,
1927 GConfEntry *entry,
1928 gpointer user_data)
1930 RBEntryView *view = user_data;
1932 g_return_if_fail (RB_IS_ENTRY_VIEW (view));
1934 rb_entry_view_sync_columns_visible (view);
1937 static gint
1938 propid_from_name (const char *name)
1940 GEnumClass *prop_class = g_type_class_ref (RHYTHMDB_TYPE_PROP_TYPE);
1941 GEnumValue *ev;
1942 int ret;
1944 ev = g_enum_get_value_by_name (prop_class, name);
1945 if (ev)
1946 ret = ev->value;
1947 else
1948 ret = -1;
1949 return ret;
1952 static void
1953 set_column_visibility (guint propid,
1954 GtkTreeViewColumn *column,
1955 GList *visible_props)
1957 gboolean visible;
1959 if (g_object_get_qdata (G_OBJECT (column),
1960 rb_entry_view_column_always_visible) == GINT_TO_POINTER (1))
1961 visible = TRUE;
1962 else
1963 visible = (g_list_find (visible_props, GINT_TO_POINTER (propid)) != NULL);
1965 gtk_tree_view_column_set_visible (column, visible);
1968 static void
1969 rb_entry_view_sync_columns_visible (RBEntryView *view)
1971 char **items;
1972 GList *visible_properties = NULL;
1973 char *config = eel_gconf_get_string (CONF_UI_COLUMNS_SETUP);
1975 g_return_if_fail (view != NULL);
1976 g_return_if_fail (config != NULL);
1978 items = g_strsplit (config, ",", 0);
1979 if (items != NULL) {
1980 int i;
1981 for (i = 0; items[i] != NULL && *(items[i]); i++) {
1982 int value = propid_from_name (items[i]);
1984 if ((value >= 0) && (value < RHYTHMDB_NUM_PROPERTIES))
1985 visible_properties = g_list_prepend (visible_properties, GINT_TO_POINTER (value));
1987 g_strfreev (items);
1990 g_hash_table_foreach (view->priv->propid_column_map, (GHFunc) set_column_visibility, visible_properties);
1992 g_list_free (visible_properties);
1993 g_free (config);
1996 void
1997 rb_entry_view_set_state (RBEntryView *view,
1998 RBEntryViewState state)
2000 g_return_if_fail (RB_IS_ENTRY_VIEW (view));
2001 g_object_set (view, "playing-state", state, NULL);
2004 static void
2005 rb_entry_view_grab_focus (GtkWidget *widget)
2007 RBEntryView *view = RB_ENTRY_VIEW (widget);
2009 gtk_widget_grab_focus (GTK_WIDGET (view->priv->treeview));
2012 static gboolean
2013 rb_entry_view_emit_row_changed (RBEntryView *view,
2014 RhythmDBEntry *entry)
2016 GtkTreeIter iter;
2017 GtkTreePath *path;
2019 if (!rhythmdb_query_model_entry_to_iter (view->priv->model, entry, &iter))
2020 return FALSE;
2022 path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->priv->model),
2023 &iter);
2024 gtk_tree_model_row_changed (GTK_TREE_MODEL (view->priv->model),
2025 path, &iter);
2026 gtk_tree_path_free (path);
2027 return TRUE;
2030 void
2031 rb_entry_view_set_fixed_column_width (RBEntryView *view,
2032 GtkTreeViewColumn *column,
2033 GtkCellRenderer *renderer,
2034 const gchar **strings)
2036 gint max_width = 0;
2037 int i = 0;
2039 while (strings[i] != NULL) {
2040 gint width;
2041 g_object_set (renderer, "text", strings[i], NULL);
2042 gtk_cell_renderer_get_size (renderer,
2043 view->priv->treeview,
2044 NULL,
2045 NULL, NULL,
2046 &width, NULL);
2048 if (width > max_width)
2049 max_width = width;
2051 i++;
2054 /* include some arbitrary amount of padding, just to be safeish */
2055 gtk_tree_view_column_set_fixed_width (column, max_width + 5);
2058 const char *
2059 rb_entry_view_get_time_date_column_sample ()
2061 static const char *sample = NULL;
2063 * Currently, Japanese is the only translation that uses
2064 * anything other than %Y, %m ,%d, %H, and %M to format dates.
2065 * It uses %B (month name) and %e (days), and the values for
2066 * the month name appear to all consist of the month number
2067 * followed by a single character, so they're of consistent
2068 * width. So, this approach should work for every locale.
2070 * Midnight on September 30th, 2000 is the widest date/time I
2071 * can think of. 2000-09-30 00:00.
2074 if (sample == NULL) {
2075 /* s m h d M Y dw dY x */
2076 struct tm someday = { 0, 0, 0, 30, 9, 100, 6, 274, 0};
2078 /* Translators: Please keep the translated date format
2079 * compact, and avoid variable-width items such as month and
2080 * day names wherever possible. This allows us to disable
2081 * column autosizing, which makes the Rhythmbox UI much faster.
2083 sample = eel_strdup_strftime (_("%Y-%m-%d %H:%M"), &someday);
2085 return sample;
2088 void
2089 rb_entry_view_resort_model (RBEntryView *view)
2091 struct RBEntryViewColumnSortData *sort_data;
2093 g_assert (view->priv->sorting_column);
2094 sort_data = g_hash_table_lookup (view->priv->column_sort_data_map,
2095 view->priv->sorting_column);
2096 g_assert (sort_data);
2098 rhythmdb_query_model_set_sort_order (view->priv->model,
2099 sort_data->func,
2100 sort_data->data,
2101 NULL,
2102 (view->priv->sorting_order == GTK_SORT_DESCENDING));
2105 /* This should really be standard. */
2106 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
2108 GType
2109 rb_entry_view_column_get_type (void)
2111 static GType etype = 0;
2113 if (etype == 0) {
2114 static const GEnumValue values[] = {
2115 ENUM_ENTRY (RB_ENTRY_VIEW_COL_TRACK_NUMBER, "Track Number"),
2116 ENUM_ENTRY (RB_ENTRY_VIEW_COL_TITLE, "Title"),
2117 ENUM_ENTRY (RB_ENTRY_VIEW_COL_ARTIST, "Artist"),
2118 ENUM_ENTRY (RB_ENTRY_VIEW_COL_ALBUM, "Album"),
2119 ENUM_ENTRY (RB_ENTRY_VIEW_COL_GENRE, "Genre"),
2120 ENUM_ENTRY (RB_ENTRY_VIEW_COL_DURATION, "Duration"),
2121 ENUM_ENTRY (RB_ENTRY_VIEW_COL_QUALITY, "Quality"),
2122 ENUM_ENTRY (RB_ENTRY_VIEW_COL_RATING, "Rating"),
2123 ENUM_ENTRY (RB_ENTRY_VIEW_COL_PLAY_COUNT, "Play Count"),
2124 ENUM_ENTRY (RB_ENTRY_VIEW_COL_YEAR, "Year"),
2125 ENUM_ENTRY (RB_ENTRY_VIEW_COL_LAST_PLAYED, "Last Played"),
2126 ENUM_ENTRY (RB_ENTRY_VIEW_COL_FIRST_SEEN, "First Seen"),
2127 ENUM_ENTRY (RB_ENTRY_VIEW_COL_LAST_SEEN, "Last Seen"),
2128 ENUM_ENTRY (RB_ENTRY_VIEW_COL_LOCATION, "Location"),
2129 ENUM_ENTRY (RB_ENTRY_VIEW_COL_ERROR, "Error"),
2130 { 0, 0, 0 }
2133 etype = g_enum_register_static ("RBEntryViewColumn", values);
2136 return etype;
2139 GType
2140 rb_entry_view_state_get_type (void)
2142 static GType etype = 0;
2144 if (etype == 0) {
2145 static const GEnumValue values[] = {
2146 ENUM_ENTRY (RB_ENTRY_VIEW_NOT_PLAYING, "Not Playing"),
2147 ENUM_ENTRY (RB_ENTRY_VIEW_PLAYING, "Playing"),
2148 ENUM_ENTRY (RB_ENTRY_VIEW_PAUSED, "Paused"),
2149 { 0, 0, 0 }
2152 etype = g_enum_register_static ("RBEntryViewState", values);
2155 return etype;