3 * $ If disconnected, don't destroy all tag browsers, just clear the first one, if connected refill the first one
7 * Released under the GPL.
13 #include <gdk/gdkkeysyms.h>
14 #include <libmpd/debug_printf.h>
18 #include "gmpc-mpddata-treeview.h"
19 #include "gmpc-mpddata-model.h"
20 #include "sexy-icon-entry.h"
21 #include <libmpd/libmpd-internal.h>
24 * dirty hack to workaround single parameter for now
28 static void tag2_init(void);
29 static void tag2_destroy(void);
30 static void tag2_set_enabled(int enabled
);
31 static int tag2_get_enabled(void);
33 static void tag2_save_myself(void);
34 static void tag2_browser_add(GtkWidget
*cat_tree
);
36 /* connection changed signal handling */
37 static void tag2_connection_changed(MpdObj
*mi
, int connect
, gpointer data
);
38 static void tag2_status_changed(MpdObj
*mi
, ChangedStatusType what
, gpointer data
);
39 /* intergration in gmpc */
40 static void tag2_browser_selected(GtkWidget
*container
);
41 static void tag2_browser_unselected(GtkWidget
*container
);
42 static int tag2_browser_add_go_menu(GtkWidget
*menu
);
44 /* One treemodel to store all possible tags, use this multiple times. */
45 static GtkTreeModel
*tags_store
= NULL
;
50 static void tag2_pref_construct(GtkWidget
*container
);
51 static void tag2_pref_destroy(GtkWidget
*container
);
52 static GtkWidget
*pref_vbox
= NULL
;
53 static GtkWidget
*pref_entry
= NULL
;
56 * Preferences structure
58 gmpcPrefPlugin tag2_prefs
= {
59 .construct
= tag2_pref_construct
,
60 .destroy
= tag2_pref_destroy
66 * Consists of add, selected, unselected.
68 gmpcPlBrowserPlugin tag2_browser_plugin
={
69 .add
= tag2_browser_add
,
70 .selected
= tag2_browser_selected
,
71 .unselected
= tag2_browser_unselected
,
72 .add_go_menu
= tag2_browser_add_go_menu
75 gmpcPlugin tag2_plug
= {
76 .name
= N_("Tag based browser"),
78 .plugin_type
= GMPC_PLUGIN_PL_BROWSER
,
80 .destroy
= tag2_destroy
,
81 .save_yourself
= tag2_save_myself
,
82 .get_enabled
= tag2_get_enabled
,
83 .set_enabled
= tag2_set_enabled
,
84 .browser
= &tag2_browser_plugin
,
86 .mpd_connection_changed
= tag2_connection_changed
,
87 .mpd_status_changed
= tag2_status_changed
,
89 /** Little hack to work around gmpc's limitations */
90 static GList
*tag2_ht
= NULL
;
91 /** This stucture contains all the needed data for a browser
93 typedef struct _tag_browser
{
95 /* The key used as ID in gmpc, and as config id and name */
97 /* The paned window that is packed into gmpc */
99 /* The hbox that is used to pack the tag browsers in to the browser window */
101 /* The GmpcMpdDataTreeView that shows the songs */
102 GtkTreeView
*tag_songlist
;
103 /* List with tag_element's (the tag browsers that are in tag_hbox) */
105 /* GtkTreeRowReference */
106 GtkTreeRowReference
*ref_iter
;
108 static void tag2_save_browser(tag_browser
*browser
);
110 /* The current visible browser, this is needed to workaround gmpc's limitation */
111 static GtkWidget
*tag2_current
= NULL
;
112 static void tag2_destroy_browser(tag_browser
*browser
, gpointer user_data
);
113 static void tag2_connection_changed_foreach(tag_browser
*browser
, gpointer data
);
114 static void tag2_init_browser(tag_browser
*browser
);
117 * Adds a browser to gmpc's category browser
119 static void tag2_browser_add_browser(GtkWidget
*cat_tree
, char *key
)
124 gchar
*group
= g_strdup_printf("tag2-plugin:%s",key
);
125 gchar
*name
= cfg_get_single_value_as_string_with_default(config
, group
, "name", "default");
126 GtkListStore
*tree
= (GtkListStore
*)gtk_tree_view_get_model(GTK_TREE_VIEW(cat_tree
));
127 gint pos
= cfg_get_single_value_as_int_with_default(config
, group
,"position",50+g_list_length(tag2_ht
));
129 playlist3_insert_browser(&iter
, pos
);
130 gtk_list_store_set(tree
, &iter
,
131 PL3_CAT_TYPE
, tag2_plug
.id
,
134 PL3_CAT_ICON_ID
, "tag-browser",
136 PL3_CAT_ICON_SIZE
,GTK_ICON_SIZE_DND
,-1);
137 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(tree
), &iter
);
139 tb
= g_malloc0(sizeof(*tb
));
141 tb
->key
= g_strdup(key
);
142 /* get a reference to the key */
143 tb
->ref_iter
= gtk_tree_row_reference_new(GTK_TREE_MODEL(tree
),path
);
144 tag2_ht
= g_list_append(tag2_ht
,tb
);
145 tag2_init_browser(tb
);
146 gtk_tree_path_free(path
);
147 pl3_update_go_menu();
150 static void tag2_browser_add(GtkWidget
*cat_tree
)
152 conf_mult_obj
*cmo
,*iter
;
153 if(!tag2_get_enabled())
156 * Init hash table if it does not extists
158 cmo
= cfg_get_key_list(config
, "tag2-browsers");
159 for(iter
=cmo
;iter
;iter
= iter
->next
)
161 tag2_browser_add_browser(cat_tree
, iter
->key
);
164 cfg_free_multiple(cmo
);
169 typedef struct _tag_element
{
175 GtkCellRenderer
*image_renderer
;
176 GtkTreeViewColumn
*column
;
181 tag_browser
*browser
;
189 int tag2_get_enabled()
191 return cfg_get_single_value_as_int_with_default(config
, "tag2-plugin", "enable", TRUE
);
193 static void tag2_set_enabled(int enabled
)
195 cfg_set_single_value_as_int(config
, "tag2-plugin", "enable", enabled
);
196 if(enabled
&& !tag2_ht
)
198 GtkTreeView
*tree
= playlist3_get_category_tree_view();
199 tag2_browser_add((GtkWidget
*)tree
);
200 } else if (!enabled
&& tag2_ht
) {
205 * Destroy the browser
207 static void tag2_destroy()
209 /* clear all the browsers */
210 g_list_foreach(tag2_ht
, (GFunc
)tag2_destroy_browser
, NULL
);
211 g_list_free(tag2_ht
);
215 static void tag2_destroy_tag(tag_element
*te
)
218 g_source_remove(te
->timeout
);
219 gtk_widget_destroy(te
->vbox
);
222 g_object_unref(te
->model
);
231 * Handles right mouse press event
232 * this function fixes the behauviour of gtk to something more logic,
233 * the actually handling of the press happens in the release event
235 static gboolean
tag2_song_list_button_press_event(GtkWidget
*but
, GdkEventButton
*event
)
237 GtkTreePath
*path
= NULL
;
238 if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(but
), event
->x
, event
->y
,&path
,NULL
,NULL
,NULL
))
240 GtkTreeSelection
*sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(but
));
241 if(gtk_tree_selection_path_is_selected(sel
, path
))
243 if(event
->button
== 3)
245 gtk_tree_path_free(path
);
248 gtk_tree_selection_unselect_path(sel
, path
);
256 gtk_tree_path_free(path
);
262 * Add songs from the selected browser entry of te
264 static void tag2_browser_add_selected(GtkWidget
*item
, tag_element
*te
)
267 GtkTreeSelection
*sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(te
->tree
));
268 if(gtk_tree_selection_get_selected(sel
, &(te
->model
), &iter
))
272 /* now get the song content, this needs all the fields of the previous
274 mpd_database_search_start(connection
, TRUE
);
275 /* add constraints based on previous browsers */
276 list
= g_list_first(te
->browser
->tag_lists
);
279 tag_element
*te3
= list
->data
;//g_list_nth_data(te->browser->tag_lists, i);
280 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(te3
->tree
));
281 if(gtk_tree_selection_get_selected(sel
, &(te3
->model
), &iter
))
284 gtk_tree_model_get(te3
->model
, &iter
, MPDDATA_MODEL_COL_SONG_TITLE
, &value
, -1);
285 mpd_database_search_add_constraint(connection
, te3
->type
, value
);
290 data
= mpd_database_search_commit(connection
);
291 /* if there is a result queue them and add them */
294 data
= misc_sort_mpddata_by_album_disc_track(data
);
295 for(;data
;data
= mpd_data_get_next(data
))
297 mpd_playlist_queue_add(connection
,data
->song
->file
);
299 mpd_playlist_queue_commit(connection
);
304 * Redirect to metadata browser
306 static void tag2_browser_header_information(GtkWidget
*item
, tag_element
*te
)
309 GtkTreeSelection
*sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(te
->tree
));
310 if(gtk_tree_selection_get_selected(sel
, &(te
->model
), &iter
))
316 if(gtk_tree_selection_get_selected(sel
, &(te
->model
), &iter
))
319 gtk_tree_model_get(te
->model
, &iter
, MPDDATA_MODEL_COL_SONG_TITLE
, &value
, -1);
320 if(te
->type
== MPD_TAG_ITEM_ARTIST
)
323 info2_fill_artist_view(value
);
325 }else if (te
->type
== MPD_TAG_ITEM_ALBUM
){
326 gchar
*artist
= gmpc_mpddata_model_get_request_artist(GMPC_MPDDATA_MODEL(te
->model
));
329 info2_fill_album_view(artist
,value
);
338 * Replace playlist with content of selected browser
340 static void tag2_browser_replace_selected(GtkWidget
*item
, tag_element
*te
)
342 mpd_playlist_clear(connection
);
343 if(mpd_check_connected(connection
))
345 tag2_browser_add_selected(item
, te
);
346 mpd_player_play(connection
);
351 * Handles right mouse release on song list
353 static gboolean
tag2_browser_button_release_event(GtkTreeView
*tree
, GdkEventButton
*event
, tag_element
*te
)
355 /* only on right mouse click */
356 if(event
->button
== 3)
358 GtkWidget
*menu
, *item
;
359 GtkTreeSelection
*selection
= gtk_tree_view_get_selection(tree
);
360 int count
= gtk_tree_selection_count_selected_rows(selection
);
361 /* if nothing is selected return */
365 /* create menu to popup */
366 menu
= gtk_menu_new();
367 /* add the add widget */
368 item
= gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD
,NULL
);
369 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
370 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(tag2_browser_add_selected
), te
);
371 /* add the replace widget */
372 item
= gtk_image_menu_item_new_with_label("Replace");
373 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
374 gtk_image_new_from_stock(GTK_STOCK_REDO
, GTK_ICON_SIZE_MENU
));
375 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
376 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(tag2_browser_replace_selected
), te
);
378 if(te
->type
== MPD_TAG_ITEM_ARTIST
|| (te
->type
== MPD_TAG_ITEM_ALBUM
&& gmpc_mpddata_model_get_request_artist(GMPC_MPDDATA_MODEL(te
->model
)) != NULL
))
380 item
= gtk_image_menu_item_new_from_stock(GTK_STOCK_INFO
,NULL
);
381 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
382 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(tag2_browser_header_information
), te
);
387 gtk_widget_show_all(GTK_WIDGET(menu
));
388 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
,NULL
, NULL
, 0, event
->time
);
395 static int tag2_key_release_event(GtkTreeView
*tree
, GdkEventKey
*event
,tag_element
*te
)
397 if((event
->state
&(GDK_CONTROL_MASK
|GDK_MOD1_MASK
)) == 0 &&
398 ((event
->keyval
>= GDK_space
&& event
->keyval
<= GDK_z
)))
401 data
[0] = (char)gdk_keyval_to_unicode(event
->keyval
);
403 gtk_widget_grab_focus(te
->sentry
);
404 gtk_entry_set_text(GTK_ENTRY(te
->sentry
),data
);
405 gtk_editable_set_position(GTK_EDITABLE(te
->sentry
),1);
413 static void tag2_changed(GtkTreeSelection
*sel2
, tag_element
*te
)
416 MpdData
*data
= NULL
;
417 tag_browser
*browser
= te
->browser
;
418 int not_to_update
= te
->index
;
419 int artist_set
= FALSE
;
420 GList
*tel
= g_list_first(browser
->tag_lists
);
421 /* Clear songs list */
422 /* clear the depending browsers */
425 tag_element
*te2
=tel
->data
;
428 gmpc_mpddata_model_set_request_artist(GMPC_MPDDATA_MODEL(te2
->model
), NULL
);
432 tel
= g_list_first(browser
->tag_lists
);
433 /* check if the user selected a row, if not do nothing */
437 if(te
->index
!= not_to_update
)
439 GtkTreeSelection
*sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(te
->tree
));
441 gchar
*artist
= NULL
;
442 GList
*first
= g_list_first(browser
->tag_lists
);
443 /* only do the following query if it isn't the last one */
446 /* Search for the fields of the next tag, this needs the value/type of all the previous,
447 * Parsed from left to right
449 mpd_database_search_field_start(connection
, te
->type
);
450 /* fil in the next */
452 tag_element
*te3
= first
->data
;
453 if(te3
->index
!= te
->index
)
455 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(te3
->tree
));
456 if(gtk_tree_selection_get_selected(sel
, &(te3
->model
), &iter
))
459 gtk_tree_model_get(te3
->model
, &iter
, MPDDATA_MODEL_COL_SONG_TITLE
, &value
, -1);
460 mpd_database_search_add_constraint(connection
, te3
->type
, value
);
461 if(te3
->index
< (te
->index
) && !artist_seen
)
463 if(te3
->type
== MPD_TAG_ITEM_ARTIST
)
465 artist
= g_strdup(value
);
476 /* Set on all tags the request artist, if available. */
477 /* TODO: This sets it many, uneeded times, fix that.. */
480 GList
*first
= g_list_first(browser
->tag_lists
);
481 for(;first
;first
= first
->next
) {
482 tag_element
*te3
= first
->data
;
483 gmpc_mpddata_model_set_request_artist(GMPC_MPDDATA_MODEL(te3
->model
), artist
);
490 data
= mpd_database_search_commit(connection
);
491 /* Delete items that match */
492 if(strlen(gtk_entry_get_text(GTK_ENTRY(te
->sentry
))) > 0)
494 MpdData_real
*d
= (MpdData_real
*)data
;
495 /* Get the entry from the text view */
496 const char *str
= gtk_entry_get_text(GTK_ENTRY(te
->sentry
));
498 gchar
*sb1
= g_utf8_casefold(str
, -1);
499 /* Normalize it, this to ensure they use the same way of representing the character */
500 gchar
*sb
= g_utf8_normalize(sb1
,-1,G_NORMALIZE_ALL_COMPOSE
);
505 gchar
*sa1
= g_utf8_casefold(d
->tag
, -1);
506 /* Normalize it, this to ensure they use the same way of representing the character */
507 gchar
*sa
= g_utf8_normalize(sa1
,-1,G_NORMALIZE_ALL_COMPOSE
);
509 /* compare the utf-8 strings, this should be a fairly good compare
510 * because I made sure the utf-8 is represented in a equal way
512 if(strstr(sa
,sb
) == NULL
)
514 /* If it does not match, remove the item from the list */
515 data
= mpd_data_delete_item((MpdData
*)d
);
516 d
= (MpdData_real
*)data
;
519 /* if it does match, we go to the next and check that */
525 /* if there are items in the lest, make sure we get the first one */
527 data
= mpd_data_get_first(data
);
531 * Update the TreeModel using the special incremental replace function.
532 * This will make sure, selected rows that are matched, don't get de-selected
534 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(te
->tree
));
535 gmpc_mpddata_model_set_mpd_data_slow(GMPC_MPDDATA_MODEL(te
->model
), data
);
536 /* this make sure the selected row is centered in the middle of the treeview.
537 * Otherwise the user could have the tedious job of finding it again
539 /* get the selected row, if any */
540 if(gtk_tree_selection_get_selected(sel
, &(te
->model
), &iter
))
542 /* get the path to the selected row */
543 GtkTreePath
*path
= gtk_tree_model_get_path(te
->model
, &iter
);
544 /* scroll to the path, and center it in the middle of the treeview, at the left of the column */
545 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(te
->tree
), path
, NULL
, TRUE
, 0.5,0);
547 gtk_tree_path_free(path
);
553 tel
= g_list_first(browser
->tag_lists
);
557 GtkTreeSelection
*sel
;
560 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(te
->tree
));
561 if(gtk_tree_selection_get_selected(sel
, &(te
->model
), &iter
))
566 mpd_database_search_start(connection
, TRUE
);
569 gtk_tree_model_get(te
->model
, &iter
, MPDDATA_MODEL_COL_SONG_TITLE
, &value
, -1);
570 mpd_database_search_add_constraint(connection
, te
->type
, value
);
576 data
= mpd_database_search_commit(connection
);
577 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(gtk_tree_view_get_model(browser
->tag_songlist
)), data
);
581 static void tag2_destroy_browser(tag_browser
*browser
, gpointer user_data
)
592 d
= g_strdup_printf("tag2-plugin:%s", browser
->key
);
593 cfg_set_single_value_as_int(config
, d
, "pane-pos", gtk_paned_get_position(GTK_PANED(browser
->tag2_vbox
)));
596 /* remove it from the left hand view */
597 model
= gtk_tree_row_reference_get_model(browser
->ref_iter
);
598 path
= gtk_tree_row_reference_get_path(browser
->ref_iter
);
601 if(gtk_tree_model_get_iter(model
,&iter
, path
))
603 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
605 gtk_tree_path_free(path
);
607 gtk_tree_row_reference_free(browser
->ref_iter
);
610 g_free(browser
->name
);
611 g_free(browser
->key
);
612 /* free the tag browsers */
613 g_list_foreach(browser
->tag_lists
, (GFunc
)tag2_destroy_tag
, NULL
);
614 g_list_free(browser
->tag_lists
);
615 /* destroy the container */
616 if(browser
->tag2_vbox
) {
617 g_object_unref(browser
->tag2_vbox
);
619 /* clear structure */
622 static void tag2_songlist_combo_box_changed(GtkComboBox
*box
, tag_element
*te
)
625 te
->type
= gtk_combo_box_get_active(box
);
626 /* if the first is changed, refill the first.
627 * if any other is changed, make the edited refill by triggering changed signal on the previous.
629 /* clear the list, makes changing the tree layout better.*/
630 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(te
->model
), NULL
);
631 /* clear all settings */
632 gtk_cell_layout_clear_attributes(GTK_CELL_LAYOUT(te
->column
), te
->image_renderer
);
633 if(te
->type
== MPD_TAG_ITEM_ARTIST
||te
->type
== MPD_TAG_ITEM_ALBUM
)
635 int size
= cfg_get_single_value_as_int_with_default(config
, "gmpc-mpddata-model", "icon-size", 32);
636 gtk_tree_view_column_add_attribute(te
->column
,te
->image_renderer
, "pixbuf", MPDDATA_MODEL_META_DATA
);
637 gtk_cell_renderer_set_fixed_size(te
->image_renderer
, size
,size
);
642 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU
, &width
, &height
);
643 gtk_cell_renderer_set_fixed_size(te
->image_renderer
, width
,height
);
644 gtk_tree_view_column_add_attribute(te
->column
,te
->image_renderer
, "icon-name", MPDDATA_MODEL_COL_ICON_ID
);
647 /* index starts at 1 */
650 list
= g_list_nth(te
->browser
->tag_lists
,te
->index
-2);
653 tag_element
*te2
= list
->data
;
654 /* update the content */
655 tag2_changed(gtk_tree_view_get_selection(GTK_TREE_VIEW(te2
->tree
)),te2
);
661 list
= g_list_first(te
->browser
->tag_lists
);
664 tag_element
*te2
= list
->next
->data
;
665 tag2_changed(gtk_tree_view_get_selection(GTK_TREE_VIEW(te2
->tree
)),te2
);
669 mpd_database_search_field_start(connection
, te
->type
);
670 data
= mpd_database_search_commit(connection
);
671 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(te
->model
), data
);
675 tag2_save_browser(te
->browser
);
678 static gboolean
tag2_sentry_changed_real(tag_element
*te
)
680 tag_element
*te2
= g_malloc0(sizeof(*te2
));
682 te2
->browser
= te
->browser
;
683 tag2_changed(NULL
, te2
);
687 if(strlen(gtk_entry_get_text(GTK_ENTRY(te
->sentry
)))==0)
689 gtk_widget_hide(te
->sentry
);
694 static void tag2_sentry_changed(SexyIconEntry
*entry
, tag_element
*te
)
696 gtk_widget_show(te
->sentry
);
698 g_source_remove(te
->timeout
);
699 te
->timeout
= g_timeout_add_seconds(1, (GSourceFunc
)tag2_sentry_changed_real
, te
);
703 static void playtime_changed(GmpcMpdDataModel
*model
, gulong playtime
)
705 if(pl3_cat_get_selected_browser() == tag2_plug
.id
)
707 playlist3_show_playtime(playtime
);
711 static void tag2_songlist_add_tag(tag_browser
*browser
,const gchar
*name
, int type
)
713 GtkCellRenderer
*renderer
;
714 GtkTreeViewColumn
*column
;
715 tag_element
*te
= g_malloc0(sizeof(*te
));
717 browser
->tag_lists
= g_list_append(browser
->tag_lists
, te
);
720 te
->index
= g_list_length(browser
->tag_lists
);
721 te
->model
= (GtkTreeModel
*) gmpc_mpddata_model_new();
722 te
->combo
= gtk_combo_box_new_with_model(tags_store
);
723 te
->sentry
= sexy_icon_entry_new();
724 te
->sw
= gtk_scrolled_window_new(NULL
,NULL
);
725 te
->vbox
= gtk_vbox_new(FALSE
, 6);
726 te
->tree
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(te
->model
));
727 te
->browser
= browser
;
730 /* setup combo box */
731 renderer
= gtk_cell_renderer_text_new();
732 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(te
->combo
), renderer
, TRUE
);
733 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(te
->combo
), renderer
, "text", 2,NULL
);
734 gtk_box_pack_start(GTK_BOX(te
->vbox
), te
->combo
, FALSE
, TRUE
, 0);
735 gtk_combo_box_set_active(GTK_COMBO_BOX(te
->combo
), te
->type
);
736 g_signal_connect(G_OBJECT(te
->combo
), "changed", G_CALLBACK(tag2_songlist_combo_box_changed
), te
);
739 gtk_widget_set_no_show_all(te
->sentry
, TRUE
);
740 gtk_widget_hide(te
->sentry
);
741 sexy_icon_entry_add_clear_button(SEXY_ICON_ENTRY(te
->sentry
));
742 g_signal_connect(G_OBJECT(te
->sentry
), "changed", G_CALLBACK(tag2_sentry_changed
), te
);
743 gtk_box_pack_start(GTK_BOX(te
->vbox
), te
->sentry
,FALSE
, TRUE
, 0);
745 g_signal_connect(G_OBJECT(te
->tree
), "key-press-event", G_CALLBACK(tag2_key_release_event
), te
);
747 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(te
->sw
), GTK_SHADOW_ETCHED_IN
);
748 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(te
->sw
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
750 gtk_container_add(GTK_CONTAINER(te
->sw
), te
->tree
);
751 gtk_box_pack_start(GTK_BOX(te
->vbox
), te
->sw
, TRUE
, TRUE
, 0);
752 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(te
->tree
), FALSE
);
753 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(te
->tree
), FALSE
);
757 /* Add the column, and set it up */
758 te
->column
= column
= gtk_tree_view_column_new();
760 gtk_tree_view_column_set_title(column
, name
);
762 te
->image_renderer
= renderer
= gtk_cell_renderer_pixbuf_new();
764 gtk_tree_view_column_pack_start(column
, renderer
, FALSE
);
765 if(te
->type
== MPD_TAG_ITEM_ARTIST
||te
->type
== MPD_TAG_ITEM_ALBUM
)
767 int size
= cfg_get_single_value_as_int_with_default(config
, "gmpc-mpddata-model", "icon-size", 32);
769 gtk_tree_view_column_add_attribute(column
, renderer
, "pixbuf", MPDDATA_MODEL_META_DATA
);
770 gtk_cell_renderer_set_fixed_size(renderer
, size
,size
);
771 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
776 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU
, &width
, &height
);
777 gtk_cell_renderer_set_fixed_size(te
->image_renderer
, width
,height
);
778 gtk_tree_view_column_add_attribute(column
, renderer
, "icon-name", MPDDATA_MODEL_COL_ICON_ID
);
779 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
781 gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(te
->tree
), TRUE
);
782 renderer
= gtk_cell_renderer_text_new();
783 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
784 gtk_tree_view_column_add_attribute(column
, renderer
, "text", MPDDATA_MODEL_COL_SONG_TITLE
);
785 gtk_tree_view_insert_column(GTK_TREE_VIEW(te
->tree
),column
, -1);
786 /* set the search column */
787 gtk_tree_view_set_search_column(GTK_TREE_VIEW(te
->tree
), MPDDATA_MODEL_COL_SONG_TITLE
);
789 gtk_box_pack_start(GTK_BOX(browser
->tag_hbox
), te
->vbox
, TRUE
, TRUE
, 0);
790 g_signal_connect(G_OBJECT(te
->tree
), "button-press-event", G_CALLBACK(tag2_song_list_button_press_event
), te
);
791 g_signal_connect(G_OBJECT(te
->tree
), "button-release-event", G_CALLBACK(tag2_browser_button_release_event
), te
);
796 g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(te
->tree
))), "changed", G_CALLBACK(tag2_changed
), te
);
800 static void tag2_create_tags(tag_browser
*browser
)
802 gchar
*str
= cfg_get_single_value_as_string_with_default(config
, "tag2-browsers",browser
->key
, "genre|artist|album");
805 char **strv
= g_strsplit(str
, "|",0);
807 for(i
=0;strv
&& strv
[i
];i
++)
809 int tag
= mpd_misc_get_tag_by_name(strv
[i
]);
812 /* This needs to be done based on config */
813 tag2_songlist_add_tag(browser
,strv
[i
],tag
);
819 gtk_widget_show_all(browser
->tag2_vbox
);
822 * Add all selected songs
825 static void tag2_songlist_add_selected_songs(GtkWidget
*bug
, tag_browser
*browser
)
827 GtkTreeSelection
*sel
= gtk_tree_view_get_selection(browser
->tag_songlist
);
828 GtkTreeModel
*model
= gtk_tree_view_get_model(browser
->tag_songlist
);
829 /* get a list of selected paths */
830 GList
*list
= gtk_tree_selection_get_selected_rows(sel
, &model
);
834 GList
*iter
= g_list_first(list
);
835 /* iterate over all the selected rows */
836 for(;iter
;iter
= g_list_next(iter
))
838 /* get iter from path */
839 if(gtk_tree_model_get_iter(model
, &piter
, iter
->data
))
842 gtk_tree_model_get(model
, &piter
, MPDDATA_MODEL_COL_PATH
, &path
, -1);
843 mpd_playlist_queue_add(connection
, path
);
847 mpd_playlist_queue_commit(connection
);
849 g_list_foreach(list
, (GFunc
)gtk_tree_path_free
, NULL
);
855 * Replace with selected songs
857 static void tag2_songlist_replace_selected_songs(GtkWidget
*bug
, tag_browser
*browser
)
859 GtkTreeSelection
*sel
= gtk_tree_view_get_selection(browser
->tag_songlist
);
860 /* if no song is selected don't do anything */
861 if(gtk_tree_selection_count_selected_rows(sel
) > 0)
863 /* clear the current playlist */
864 mpd_playlist_clear(connection
);
865 /* Add selected songs */
866 tag2_songlist_add_selected_songs(bug
, browser
);
867 /* start playing, as is standard in gmpc */
868 mpd_player_play(connection
);
873 * Calls the column editing function of the GmpcTreeview
875 static void tag2_song_list_edit_columns(GtkMenuItem
*item
, tag_browser
*browser
)
877 gmpc_mpddata_treeview_edit_columns(GMPC_MPDDATA_TREEVIEW(browser
->tag_songlist
));
882 * Handles right mouse release on song list
884 static gboolean
tag2_song_list_button_release_event(GtkTreeView
*tree
,
885 GdkEventButton
*event
,
886 tag_browser
*browser
)
888 /* only on right mouse click */
889 if(event
->button
== 3)
891 GtkWidget
*menu
, *item
;
892 GtkTreeSelection
*selection
= gtk_tree_view_get_selection(tree
);
893 int count
= gtk_tree_selection_count_selected_rows(selection
);
895 /* create menu to popup */
896 menu
= gtk_menu_new();
897 /* only show when soething is selected */
900 /* add the add widget */
901 item
= gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD
,NULL
);
902 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
903 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(tag2_songlist_add_selected_songs
), browser
);
904 /* add the replace widget */
905 item
= gtk_image_menu_item_new_with_label("Replace");
906 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
907 gtk_image_new_from_stock(GTK_STOCK_REDO
, GTK_ICON_SIZE_MENU
));
908 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
909 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(tag2_songlist_replace_selected_songs
), browser
);
912 item
= gtk_separator_menu_item_new();
913 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
916 item
= gtk_image_menu_item_new_with_label(("Edit Columns"));
917 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
918 gtk_image_new_from_stock(GTK_STOCK_EDIT
, GTK_ICON_SIZE_MENU
));
919 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
920 g_signal_connect(G_OBJECT(item
), "activate",
921 G_CALLBACK(tag2_song_list_edit_columns
), browser
);
925 gtk_widget_show_all(GTK_WIDGET(menu
));
926 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
,NULL
, NULL
, 0, event
->time
);
933 * Handles a double click/activate on a row of the song list
935 static void tag2_row_activate(GtkTreeView
*tree
,
937 GtkTreeViewColumn
*column
,
938 tag_browser
*browser
)
941 if(gtk_tree_model_get_iter(gtk_tree_view_get_model(tree
), &iter
, path
))
944 gtk_tree_model_get(gtk_tree_view_get_model(tree
), &iter
, MPDDATA_MODEL_COL_PATH
, &song_path
, -1);
947 /* gcc complains that this function is declared implicit.
948 * This is correct, but it exists in the gmpc program space
950 play_path(song_path
);
957 static void tag2_init(void)
961 * if first time used, add 2 example browsers
963 if(cfg_get_single_value_as_int_with_default(config
, "tag2-plugin", "first-use", 1))
965 cfg_set_single_value_as_int(config
,"tag2-plugin", "first-use",0);
967 cfg_set_single_value_as_string(config
, "tag2-browsers", "Artist Browser", "artist|album|disc");
968 cfg_set_single_value_as_string(config
, "tag2-plugin:Artist Browser", "name", "Artist Browser");
969 cfg_set_single_value_as_string(config
, "tag2-browsers", "Genre Browser", "genre|artist|album|disc");
970 cfg_set_single_value_as_string(config
, "tag2-plugin:Genre Browser", "name", "Genre Browser");
973 tags_store
= (GtkTreeModel
*)gtk_list_store_new(3, G_TYPE_STRING
,G_TYPE_INT
,G_TYPE_STRING
);
974 for(i
=0;i
<MPD_TAG_ITEM_ANY
;i
++)
977 gtk_list_store_append(GTK_LIST_STORE(tags_store
), &titer
);
978 gtk_list_store_set(GTK_LIST_STORE(tags_store
), &titer
, 0, mpdTagItemKeys
[i
],1, i
,2,_(mpdTagItemKeys
[i
]),-1);
983 static void tag2_init_browser(tag_browser
*browser
) {
986 GmpcMpdDataModel
*model
= NULL
;
989 /* create the pane that separates the song list from the browsers */
990 key
= g_strdup_printf("tag2-plugin:%s", browser
->key
);
991 pp
= cfg_get_single_value_as_int_with_default(config
, key
, "pane-pos",150);
992 browser
->tag2_vbox
= gtk_vpaned_new();
993 /* set the previous pane position */
994 gtk_paned_set_position(GTK_PANED(browser
->tag2_vbox
), pp
);
997 /* box with tag treeviews (browsers) */
998 browser
->tag_hbox
= gtk_hbox_new(TRUE
, 6);
1000 /* Add this to the 1st pane*/
1001 gtk_paned_add1(GTK_PANED(browser
->tag2_vbox
), browser
->tag_hbox
);
1004 /** Create Songs list view */
1005 /* create the treeview model, this is a GmpcMpdData model */
1006 model
= gmpc_mpddata_model_new();
1007 g_signal_connect(G_OBJECT(model
), "playtime_changed", G_CALLBACK(playtime_changed
), NULL
);
1008 /* create scrolled window to make the treeview scrollable */
1009 sw
= gtk_scrolled_window_new(NULL
,NULL
);
1010 /* setup the scrolled window */
1011 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw
), GTK_SHADOW_ETCHED_IN
);
1012 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
1013 /* create the actual treeview, use the GmpcTreeview type, so all the handling is done automatic */
1015 browser
->tag_songlist
= (GtkTreeView
*)gmpc_mpddata_treeview_new(key
,TRUE
,GTK_TREE_MODEL(model
));
1017 /* add the treeview to the scrolled window */
1018 gtk_container_add(GTK_CONTAINER(sw
), GTK_WIDGET(browser
->tag_songlist
));
1019 /* add the scrolled window to the 2nd pane */
1020 gtk_paned_add2(GTK_PANED(browser
->tag2_vbox
),sw
);
1021 /* connect some of the signals */
1022 g_signal_connect(G_OBJECT(browser
->tag_songlist
), "row-activated", G_CALLBACK(tag2_row_activate
), browser
);
1023 // g_signal_connect(G_OBJECT(browser->tag_songlist), "button-press-event", G_CALLBACK(tag2_song_list_button_press_event), browser);
1024 g_signal_connect(G_OBJECT(browser
->tag_songlist
), "button-release-event", G_CALLBACK(tag2_song_list_button_release_event
), browser
);
1025 /* Create an extra reference to the paned window containing everything, this way
1026 * I can add/remove it from gmpc's container withouth it being destroyed by gtk
1028 g_object_ref(browser
->tag2_vbox
);
1029 /* show everything */
1030 gtk_widget_show_all(browser
->tag2_vbox
);
1032 tag2_connection_changed_foreach(browser
, NULL
);
1034 static gboolean
tag2_custom_find(tag_browser
*a
,gchar
*key
)
1036 return strcmp(a
->key
, key
);
1040 static void tag2_browser_selected(GtkWidget
*container
)
1042 GtkTreeView
*tree
= playlist3_get_category_tree_view();
1043 GtkTreeModel
*model
= gtk_tree_view_get_model(tree
);
1044 GtkTreeSelection
*sel
= gtk_tree_view_get_selection(tree
);
1046 if(gtk_tree_selection_get_selected(sel
, &model
, &iter
))
1049 gtk_tree_model_get(model
, &iter
, PL3_CAT_INT_ID
, &key
, -1);
1052 GList
*node
= g_list_find_custom(tag2_ht
, key
, (GCompareFunc
)tag2_custom_find
);
1055 tag_browser
*tb
= node
->data
;
1059 if(tb
->tag2_vbox
==NULL
)
1060 tag2_init_browser(tb
);
1061 gtk_container_add(GTK_CONTAINER(container
), tb
->tag2_vbox
);
1062 gtk_widget_show_all(container
);
1063 tag2_current
= tb
->tag2_vbox
;
1065 list = g_list_first(tb->tag_lists);
1068 tag_element *te= list->data;
1069 gtk_widget_grab_focus(te->tree);
1073 playlist3_show_playtime(gmpc_mpddata_model_get_playtime(GMPC_MPDDATA_MODEL(
1074 gtk_tree_view_get_model(tb
->tag_songlist
))));
1085 static void tag2_browser_unselected(GtkWidget
*container
)
1088 gtk_container_remove(GTK_CONTAINER(container
), tag2_current
);
1089 tag2_current
= NULL
;
1092 static void tag2_clear(tag_browser
*browser
)
1094 if(browser
->tag2_vbox
)
1096 GtkTreeModel
*model
= gtk_tree_view_get_model(GTK_TREE_VIEW(browser
->tag_songlist
));
1097 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(model
), NULL
);
1098 g_list_foreach(browser
->tag_lists
, (GFunc
)tag2_destroy_tag
, NULL
);
1099 g_list_free(browser
->tag_lists
);
1100 browser
->tag_lists
= NULL
;
1103 static void tag2_save_browser(tag_browser
*browser
)
1105 GString
*str
= g_string_new("");
1106 GList
*list
= browser
->tag_lists
;
1107 for(;list
;list
= g_list_next(list
))
1109 tag_element
*te
= list
->data
;
1110 str
= g_string_append(str
, mpdTagItemKeys
[te
->type
]);
1112 str
= g_string_append(str
, "|");
1115 cfg_set_single_value_as_string(config
, "tag2-browsers",browser
->key
,str
->str
);
1116 g_string_free(str
, TRUE
);
1118 static void tag2_connection_changed_foreach(tag_browser
*browser
, gpointer userdata
)
1120 if(browser
->tag2_vbox
)
1122 tag_element
*te
= NULL
;
1123 if(browser
->tag_lists
== NULL
)
1124 tag2_create_tags(browser
);
1125 te
= g_list_nth_data(browser
->tag_lists
, 0);
1126 if(te
!= NULL
&& mpd_check_connected(connection
) )
1131 te = g_list_next(te);
1136 mpd_database_search_field_start(connection
, te
->type
);
1137 data
= mpd_database_search_commit(connection
);
1138 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(te
->model
), data
);
1140 tag_element
*te2
= g_malloc0(sizeof(*te2
));
1142 te2
->browser
= te
->browser
;
1143 tag2_changed(NULL
, te2
);
1147 tag2_changed(gtk_tree_view_get_selection(GTK_TREE_VIEW(te
->tree
)),te
);
1153 static void tag2_connection_changed(MpdObj
*mi
, int connect
, gpointer data
)
1156 // g_list_foreach(tag2_ht,(GFunc)tag2_clean, NULL);
1158 if(connect
&& tag2_ht
)
1161 g_list_foreach(tag2_ht
,(GFunc
)tag2_connection_changed_foreach
, NULL
);
1167 static void tag2_status_changed(MpdObj
*mi
, ChangedStatusType what
, gpointer data
)
1169 if(what
&MPD_CST_DATABASE
)
1173 /* g_list_foreach(tag2_ht,(GFunc)tag2_clear, NULL);
1174 g_list_foreach(tag2_ht,(GFunc)tag2_connection_changed_foreach, NULL);
1180 * Preferences window
1182 void tag2_pref_destroy(GtkWidget
*container
)
1184 gtk_container_remove(GTK_CONTAINER(container
), pref_vbox
);
1187 * Browser selector changed
1189 static void tag2_pref_combo_changed(GtkComboBox
*box
, GtkTreeModel
*model2
)
1194 /* clear old data */
1195 gtk_list_store_clear(GTK_LIST_STORE(model2
));
1197 if(gtk_combo_box_get_active_iter(box
, &iter
))
1199 GtkTreeModel
*model
= gtk_combo_box_get_model(box
);
1203 /* get the active tag browser */
1204 gtk_tree_model_get(model
, &iter
, 2,&tb
,-1);
1205 /* iterate over all the tag elements and add them to the list */
1208 GList
*liter
= g_list_first(tb
->tag_lists
);
1209 for(;liter
;liter
= g_list_next(liter
))
1212 tag_element
*te
= liter
->data
;
1213 gtk_list_store_append(GTK_LIST_STORE(model2
),&titer
);
1214 gtk_list_store_set(GTK_LIST_STORE(model2
), &titer
, 0,mpdTagItemKeys
[te
->type
],1,te
->type
,2,te
,-1);
1218 /* Get browser name and set entry */
1219 group
= g_strdup_printf("tag2-plugin:%s", tb
->key
);
1220 str
= cfg_get_single_value_as_string_with_default(config
,group
,"name", "default");
1221 gtk_entry_set_text(GTK_ENTRY(pref_entry
), str
);
1227 /* only clear the text when nothing is selected, else the currently selected is cleared..
1228 * yeah, longlive signals
1230 gtk_entry_set_text(GTK_ENTRY(pref_entry
), "");
1234 static void tag2_pref_add_browser_clicked(GtkWidget
*but
, GtkComboBox
*combo
)
1236 GtkTreeView
*tree
= playlist3_get_category_tree_view();
1239 GtkListStore
*model
= (GtkListStore
*)gtk_combo_box_get_model(combo
);
1240 gchar
*name
= g_strdup_printf("%u", g_random_int());
1241 cfg_set_single_value_as_string(config
, "tag2-browsers",name
, "");
1245 tag2_browser_add_browser(GTK_WIDGET(tree
),name
);
1247 node
= g_list_find_custom(tag2_ht
, name
, (GCompareFunc
)tag2_custom_find
);
1250 tag_browser
*tb
= node
->data
;
1251 gtk_list_store_append(model
, &titer
);
1252 gtk_list_store_set(model
, &titer
, 0,name
, 1,"default",2,tb
,-1);
1253 gtk_combo_box_set_active_iter(combo
, &titer
);
1254 /* change this to store TB in list store) */
1260 * Update the title of the browser
1262 static void tag2_pref_entry_changed(GtkWidget
*entry
, GtkComboBox
*combo
)
1265 GtkListStore
*model
= (GtkListStore
*)gtk_combo_box_get_model(combo
);
1266 if(gtk_combo_box_get_active_iter(combo
, &iter
))
1271 const gchar
*name
= gtk_entry_get_text(GTK_ENTRY(pref_entry
));
1272 gtk_tree_model_get(GTK_TREE_MODEL(model
), &iter
,0,&key
, 2, &tb
, -1);
1273 gtk_list_store_set(model
, &iter
,1,name
,-1);
1276 tb
->name
= g_strdup(name
);
1277 if((path
= gtk_tree_row_reference_get_path(tb
->ref_iter
)))
1280 GtkTreeModel
*model2
= gtk_tree_row_reference_get_model(tb
->ref_iter
);
1281 gtk_tree_model_get_iter(model2
, &iter2
, path
);
1282 gtk_list_store_set(GTK_LIST_STORE(model2
), &iter2
, PL3_CAT_TITLE
, name
, -1);
1283 gtk_tree_path_free(path
);
1286 group
= g_strdup_printf("tag2-plugin:%s", key
);
1287 cfg_set_single_value_as_string(config
, group
, "name", (char *)name
);
1292 * Handles editing of the columns type
1294 static void tag2_pref_column_type_edited(GtkCellRendererText
*text
, gchar
*path
, char *new_data
, GtkTreeModel
*model
)
1297 if(gtk_tree_model_get_iter_from_string(model
, &iter
, path
))
1299 int tag
= mpd_misc_get_tag_by_name(new_data
);
1301 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
, 0, new_data
,1,tag
, -1);
1302 gtk_tree_model_get(model
, &iter
, 2, &te
, -1);
1303 gtk_combo_box_set_active(GTK_COMBO_BOX(te
->combo
), tag
);
1307 * Add's a column to the selected tag browser
1309 static void tag2_pref_browser_remove(GtkWidget
*but
, GtkComboBox
*box
)
1312 if(gtk_combo_box_get_active_iter(box
, &iter
))
1314 GtkTreeModel
*model
= gtk_combo_box_get_model(box
);
1318 gtk_tree_model_get(model
, &iter
, 2,&tb
,-1);
1319 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
1321 key
= g_strdup(tb
->key
);
1322 /* remove from browser list */
1323 tag2_ht
= g_list_remove(tag2_ht
, tb
);
1324 /* destroy remaining */
1325 tag2_destroy_browser(tb
,NULL
);
1329 cfg_del_single_value(config
, "tag2-browsers", key
);
1331 name
= g_strdup_printf("tag2-plugin:%s", key
);
1332 cfg_remove_class(config
, name
);
1334 name
= g_strdup_printf("tag2-plugin:%s-colsize", key
);
1335 cfg_remove_class(config
, name
);
1337 name
= g_strdup_printf("tag2-plugin:%s-colpos", key
);
1338 cfg_remove_class(config
, name
);
1340 name
= g_strdup_printf("tag2-plugin:%s-colshow", key
);
1341 cfg_remove_class(config
, name
);
1344 pl3_update_go_menu();
1348 * Add's a column to the selected tag browser
1350 static void tag2_pref_column_add(GtkWidget
*but
, GtkComboBox
*box
)
1353 GtkTreeModel
*model2
= g_object_get_data(G_OBJECT(but
),"model");
1354 if(gtk_combo_box_get_active_iter(box
, &iter
))
1356 GtkTreeModel
*model
= gtk_combo_box_get_model(box
);
1358 /* TODO: Change to the browser tag editing stuff */
1359 gtk_tree_model_get(model
, &iter
, 2,&tb
,-1);
1360 tag2_songlist_add_tag(tb
,mpdTagItemKeys
[MPD_TAG_ITEM_ARTIST
], MPD_TAG_ITEM_ARTIST
);
1361 tag2_pref_combo_changed(box
, model2
);
1362 gtk_widget_show_all(tb
->tag_hbox
);
1363 tag2_save_browser(tb
);
1364 /* if it's the first, update */
1365 if(g_list_length(tb
->tag_lists
) == 1)
1367 GList
*giter
= g_list_first(tb
->tag_lists
);
1371 tag_element
*te2
= giter
->data
;
1372 /* update the content */
1373 mpd_database_search_field_start(connection
, te2
->type
);
1374 data
= mpd_database_search_commit(connection
);
1375 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(te2
->model
), data
);
1381 * Removes the selected column from the tag browser
1383 static void tag2_pref_column_remove(GtkWidget
*but
, GtkComboBox
*box
)
1386 GtkTreeView
*tree
= g_object_get_data(G_OBJECT(but
),"tree");
1387 if(gtk_combo_box_get_active_iter(box
, &iter
))
1389 GtkTreeModel
*model
= gtk_combo_box_get_model(box
);
1390 GtkTreeSelection
*sel
= gtk_tree_view_get_selection(tree
);
1392 /* get the selected tag browser */
1393 gtk_tree_model_get(model
, &iter
, 2,&tb
,-1);
1394 model
= gtk_tree_view_get_model(tree
);
1396 /* Get the selected column */
1397 if(gtk_tree_selection_get_selected(sel
, &model
,&iter
))
1402 /* get tag element */
1403 gtk_tree_model_get(model
, &iter
,2, &te
, -1);
1404 /* remove from pref editor */
1405 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
1406 /* remove from browsers list */
1407 tb
->tag_lists
= g_list_remove(tb
->tag_lists
, te
);
1410 * renumber, this is needed to keep the filling consistent
1411 * Beware, the numbering starts at 1. not 0. (yes shoot me for this)
1414 for(giter
= g_list_first(tb
->tag_lists
);giter
;giter
= g_list_next(giter
))
1416 tag_element
*te2
= giter
->data
;
1421 /* destroy from interface */
1422 tag2_destroy_tag(te
);
1424 * Refill the complete browser.
1425 * TODO: Only refill the part that is needed
1427 giter
= g_list_first(te
->browser
->tag_lists
);
1431 tag_element
*te2
= giter
->data
;
1432 /* update the content */
1433 mpd_database_search_field_start(connection
, te2
->type
);
1434 data
= mpd_database_search_commit(connection
);
1435 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(te2
->model
), data
);
1440 /* save the browsers content */
1441 tag2_save_browser(tb
);
1447 void tag2_pref_construct(GtkWidget
*container
)
1449 GtkWidget
*sw
, *tree
, *but
;
1450 GtkCellRenderer
*renderer
;
1451 GtkTreeModel
*model
;
1452 GtkWidget
*hbox
,*label
,*vbox
;
1453 GtkWidget
*combo
= NULL
;
1454 conf_mult_obj
*cmo
,*iter
;
1456 * Create the parent widget where the preferences window is packed in
1458 pref_vbox
= gtk_vbox_new(FALSE
,6);
1460 /* select browser to edit */
1461 hbox
= gtk_hbox_new(FALSE
, 6);
1462 model
= (GtkTreeModel
*)gtk_list_store_new(3, G_TYPE_STRING
,G_TYPE_STRING
, G_TYPE_POINTER
);
1463 combo
= gtk_combo_box_new_with_model(model
);
1464 renderer
= gtk_cell_renderer_text_new();
1465 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), renderer
, TRUE
);
1466 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo
), renderer
, "text", 1,NULL
);
1468 gtk_box_pack_start(GTK_BOX(hbox
), combo
, TRUE
, TRUE
, 0);
1470 but
= gtk_button_new_from_stock(GTK_STOCK_ADD
);
1471 gtk_box_pack_start(GTK_BOX(hbox
),but
, FALSE
, TRUE
, 0);
1472 g_signal_connect(G_OBJECT(but
), "clicked", G_CALLBACK(tag2_pref_add_browser_clicked
), combo
);
1473 but
= gtk_button_new_from_stock(GTK_STOCK_REMOVE
);
1474 g_signal_connect(G_OBJECT(but
), "clicked", G_CALLBACK(tag2_pref_browser_remove
), combo
);
1475 gtk_box_pack_start(GTK_BOX(hbox
), but
, FALSE
, TRUE
, 0);
1476 gtk_box_pack_start(GTK_BOX(pref_vbox
), hbox
, FALSE
, TRUE
, 0);
1479 hbox
= gtk_hbox_new(FALSE
, 6);
1480 label
= gtk_label_new("Name:");
1481 gtk_misc_set_alignment(GTK_MISC(label
), 1,0.5);
1482 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, TRUE
, 0);
1484 pref_entry
= gtk_entry_new();
1485 g_signal_connect(GTK_ENTRY(pref_entry
),"changed", G_CALLBACK(tag2_pref_entry_changed
), combo
);
1486 gtk_box_pack_start(GTK_BOX(hbox
), pref_entry
,TRUE
, TRUE
, 0);
1488 gtk_box_pack_start(GTK_BOX(pref_vbox
), hbox
, FALSE
, TRUE
, 0);
1493 /* change this to store TB in list store) */
1494 cmo
= cfg_get_key_list(config
, "tag2-browsers");
1495 for(iter
=cmo
;iter
;iter
= iter
->next
)
1498 gchar
*group
= g_strdup_printf("tag2-plugin:%s", iter
->key
);
1499 gchar
*name
= cfg_get_single_value_as_string_with_default(config
, group
, "name", "default");
1500 GList
*node
= g_list_find_custom(tag2_ht
, iter
->key
, (GCompareFunc
)tag2_custom_find
);
1503 tag_browser
*tb
= node
->data
;
1505 gtk_list_store_append(GTK_LIST_STORE(model
), &titer
);
1506 gtk_list_store_set(GTK_LIST_STORE(model
), &titer
, 0, iter
->key
,1,name
,2,tb
,-1);
1514 cfg_free_multiple(cmo
);
1518 /* scrolled window used to pack the treeview */
1520 hbox
= gtk_hbox_new(FALSE
, 6);
1521 sw
= gtk_scrolled_window_new(NULL
, NULL
);
1522 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
1523 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw
),GTK_SHADOW_ETCHED_IN
);
1525 tree
= gtk_tree_view_new();
1526 /* the model for the treeview */
1527 model
= (GtkTreeModel
*) gtk_list_store_new(3, G_TYPE_STRING
,G_TYPE_INT
, G_TYPE_POINTER
);
1528 gtk_tree_view_set_model(GTK_TREE_VIEW(tree
), model
);
1530 renderer
= gtk_cell_renderer_combo_new();
1531 g_object_set(G_OBJECT(renderer
), "editable", TRUE
,NULL
);
1532 g_object_set(G_OBJECT(renderer
), "model", tags_store
,"text-column", 0,"has-entry", FALSE
,NULL
);
1533 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree
), -1, "text",renderer
,"text",0,NULL
);
1534 g_signal_connect(G_OBJECT(renderer
), "edited", G_CALLBACK(tag2_pref_column_type_edited
),model
);
1536 g_signal_connect(G_OBJECT(combo
), "changed", G_CALLBACK(tag2_pref_combo_changed
), model
);
1537 /* tree to scrolled window */
1538 gtk_container_add(GTK_CONTAINER(sw
), tree
);
1541 vbox
= gtk_vbox_new(FALSE
,6);
1543 but
= gtk_button_new_from_stock(GTK_STOCK_ADD
);
1544 gtk_box_pack_start(GTK_BOX(vbox
), but
, FALSE
, TRUE
, 0);
1545 g_object_set_data(G_OBJECT(but
), "model", model
);
1546 g_signal_connect(G_OBJECT(but
), "clicked", G_CALLBACK(tag2_pref_column_add
), combo
);
1547 but
= gtk_button_new_from_stock(GTK_STOCK_REMOVE
);
1548 g_object_set_data(G_OBJECT(but
), "tree", tree
);
1549 g_signal_connect(G_OBJECT(but
), "clicked", G_CALLBACK(tag2_pref_column_remove
), combo
);
1552 gtk_box_pack_start(GTK_BOX(vbox
), but
, FALSE
, TRUE
, 0);
1554 gtk_box_pack_start(GTK_BOX(hbox
), sw
, TRUE
, TRUE
, 0);
1555 gtk_box_pack_start(GTK_BOX(hbox
), vbox
, FALSE
, TRUE
, 0);
1556 gtk_box_pack_start(GTK_BOX(pref_vbox
), hbox
, TRUE
, TRUE
, 0);
1558 gtk_container_add(GTK_CONTAINER(container
), pref_vbox
);
1559 gtk_widget_show_all(container
);
1561 static void tag2_browser_activate(GtkWidget
*item
, tag_browser
*browser
)
1563 GtkTreeSelection
*selec
= gtk_tree_view_get_selection((GtkTreeView
*)
1564 playlist3_get_category_tree_view());
1566 GtkTreePath
*path
= gtk_tree_row_reference_get_path(browser
->ref_iter
);
1569 gtk_tree_selection_select_path(selec
, path
);
1570 gtk_tree_path_free(path
);
1576 static void tag2_browser_add_go_menu_foreach(tag_browser
*browser
, GtkWidget
*menu
)
1578 GtkWidget
*item
= gtk_image_menu_item_new_with_label(browser
->name
);
1579 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
1580 gtk_image_new_from_icon_name("media-tag", GTK_ICON_SIZE_MENU
));
1581 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
1583 gtk_widget_add_accelerator(GTK_WIDGET(item
), "activate", gtk_menu_get_accel_group(GTK_MENU(menu
)), GDK_F1
+counter
,GDK_SHIFT_MASK
, GTK_ACCEL_VISIBLE
);
1585 g_signal_connect(G_OBJECT(item
), "activate",
1586 G_CALLBACK(tag2_browser_activate
), browser
);
1589 static int tag2_browser_add_go_menu(GtkWidget
*menu
)
1591 if(tag2_get_enabled() == FALSE
)
1596 g_list_foreach(tag2_ht
,(GFunc
)tag2_browser_add_go_menu_foreach
, menu
);
1603 static void tag2_save_myself(void)
1607 GList
*iter
= g_list_first(tag2_ht
);
1609 tag_browser
*tb
= iter
->data
;
1610 GtkTreePath
*path
= gtk_tree_row_reference_get_path(tb
->ref_iter
);
1613 gint
*indices
= gtk_tree_path_get_indices(path
);
1614 gchar
*group
= g_strdup_printf("tag2-plugin:%s",tb
->key
);
1615 debug_printf(DEBUG_INFO
,"Saving myself to position: %i\n", indices
[0]);
1616 cfg_set_single_value_as_int(config
, group
,"position",indices
[0]);
1617 gtk_tree_path_free(path
);
1620 iter
= g_list_next(iter
);