Try to fix selecting issue.
[gmpc.git] / src / browsers / playlist3-tag2-browser.c
blob6c28dd2fb5a94db688821af3e7a0726e01966523
1 /* Gnome Music Player Client (GMPC)
2 * Copyright (C) 2004-2012 Qball Cow <qball@gmpclient.org>
3 * Project homepage: http://gmpclient.org/
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 /**
21 * TODO:
22 * $ If disconnected, don't destroy all tag browsers, just clear the first one, if connected refill the first one
25 /**
26 * Released under the GPL.
29 #include <stdio.h>
30 #include <string.h>
31 #include <gtk/gtk.h>
32 #include <gdk/gdkkeysyms.h>
33 #include "main.h"
34 #include "misc.h"
35 #include "gmpc-mpddata-model.h"
36 #include <libmpd/libmpd-internal.h>
37 #include "playlist3.h"
38 #include "playlist3-playlist-editor.h"
39 #include "gmpc-extras.h"
41 #define LOG_DOMAIN "TagBrowser"
42 /**
43 * dirty hack to workaround single parameter for now
45 static int counter;
47 static void tag2_init(void);
48 static void tag2_destroy(void);
49 static void tag2_set_enabled(int enabled);
50 static int tag2_get_enabled(void);
52 static void tag2_save_myself(void);
53 static void tag2_browser_add(GtkWidget * cat_tree);
55 /* connection changed signal handling */
56 static void tag2_connection_changed(MpdObj * mi, int connect, gpointer data);
57 static void tag2_status_changed(MpdObj * mi, ChangedStatusType what, gpointer data);
58 /* intergration in gmpc */
59 static void tag2_browser_selected(GtkWidget * container);
60 static void tag2_browser_unselected(GtkWidget * container);
61 static int tag2_browser_add_go_menu(GtkWidget * menu);
62 static void tag2_pref_combo_changed(GtkComboBox * box, GtkTreeModel * model2);
63 static int tag2_browser_right_mouse_menu(GtkWidget * menu, int type, GtkWidget * tree, GdkEventButton * event);
64 static int tag2_browser_key_press_event(GtkWidget * mw, GdkEventKey * event, int type);
65 /* One treemodel to store all possible tags, use this multiple times. */
66 static GtkTreeModel *tags_store = NULL;
68 /**
69 * preferences
71 static void tag2_pref_construct(GtkWidget * container);
72 static void tag2_pref_destroy(GtkWidget * container);
73 static GtkWidget *pref_vbox = NULL;
74 static GtkWidget *pref_entry = NULL;
75 static GtkWidget *pref_combo = NULL;
79 /**
80 * Preferences structure
82 gmpcPrefPlugin tag2_prefs = {
83 .construct = tag2_pref_construct,
84 .destroy = tag2_pref_destroy
87 /**
88 * Browser plugin
89 * Consists of add, selected, unselected.
91 gmpcPlBrowserPlugin tag2_browser_plugin = {
92 .add = tag2_browser_add,
93 .selected = tag2_browser_selected,
94 .unselected = tag2_browser_unselected,
95 .add_go_menu = tag2_browser_add_go_menu,
96 .cat_right_mouse_menu = tag2_browser_right_mouse_menu,
97 .key_press_event = tag2_browser_key_press_event
100 gmpcPlugin tag2_plug = {
101 .name = N_("Tag based browser"),
102 .version = {0, 15, 0},
103 .plugin_type = GMPC_PLUGIN_PL_BROWSER | GMPC_INTERNALL,
104 .init = tag2_init,
105 .destroy = tag2_destroy,
106 .save_yourself = tag2_save_myself,
107 .get_enabled = tag2_get_enabled,
108 .set_enabled = tag2_set_enabled,
109 .browser = &tag2_browser_plugin,
110 .pref = &tag2_prefs,
111 .mpd_connection_changed = tag2_connection_changed,
112 .mpd_status_changed = tag2_status_changed,
115 /** Little hack to work around gmpc's limitations */
116 static GList *tag2_ht = NULL;
117 /** This stucture contains all the needed data for a browser
119 typedef struct _tag_browser
121 gchar *name;
122 /* The key used as ID in gmpc, and as config id and name */
123 gchar *key;
124 /* The paned window that is packed into gmpc */
125 GtkWidget *tag2_vbox;
126 /* The hbox that is used to pack the tag browsers in to the browser window */
127 GtkWidget *tag_hbox;
128 /* The GmpcMpdDataTreeView that shows the songs */
129 GtkTreeView *tag_songlist;
130 /* List with tag_element's (the tag browsers that are in tag_hbox) */
131 GList *tag_lists;
132 /* GtkTreeRowReference */
133 GtkTreeRowReference *ref_iter;
135 } tag_browser;
138 static void tag2_save_browser(tag_browser * browser);
140 /* The current visible browser, this is needed to workaround gmpc's limitation */
141 static GtkWidget *tag2_current = NULL;
142 static void tag2_destroy_browser(tag_browser * browser, gpointer user_data);
143 static void tag2_connection_changed_foreach(tag_browser * browser, gpointer data);
144 static void tag2_init_browser(tag_browser * browser);
147 * Adds a browser to gmpc's category browser
149 static void tag2_browser_add_browser(GtkWidget * cat_tree, char *key)
151 GtkTreePath *path;
152 GtkTreeIter iter;
153 tag_browser *tb;
154 gchar *group = g_strdup_printf("tag2-plugin:%s", key);
155 gchar *name = cfg_get_single_value_as_string_with_default(config, group, "name", "default");
156 GtkListStore *tree = (GtkListStore *) gtk_tree_view_get_model(GTK_TREE_VIEW(cat_tree));
157 gint pos = cfg_get_single_value_as_int_with_default(config, group, "position", 50 + g_list_length(tag2_ht));
158 g_free(group);
159 playlist3_insert_browser(&iter, PL3_CAT_BROWSER_LIBRARY+pos%PL3_CAT_BROWSER_LIBRARY);
160 gtk_list_store_set(tree, &iter,
161 PL3_CAT_TYPE, tag2_plug.id,
162 PL3_CAT_TITLE, name, PL3_CAT_INT_ID, key, PL3_CAT_ICON_ID, "tag-browser", -1);
163 path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree), &iter);
165 tb = g_malloc0(sizeof(*tb));
166 tb->name = name;
167 tb->key = g_strdup(key);
168 /* get a reference to the key */
169 tb->ref_iter = gtk_tree_row_reference_new(GTK_TREE_MODEL(tree), path);
170 tag2_ht = g_list_append(tag2_ht, tb);
171 // tag2_init_browser(tb);
172 gtk_tree_path_free(path);
173 pl3_update_go_menu();
177 static void tag2_browser_add(GtkWidget * cat_tree)
179 conf_mult_obj *cmo, *iter;
180 if (!tag2_get_enabled())
181 return;
183 * Init hash table if it does not extists
185 cmo = cfg_get_key_list(config, "tag2-browsers");
186 for (iter = cmo; iter; iter = iter->next)
188 tag2_browser_add_browser(cat_tree, iter->key);
190 if (cmo)
191 cfg_free_multiple(cmo);
195 typedef struct _tag_element
197 GtkWidget *tree;
198 GmpcMpdDataTreeviewTooltip *tool;
199 GtkTreeModel *model;
200 GtkWidget *sw;
201 GtkWidget *vbox;
202 GtkCellRenderer *image_renderer;
203 GtkTreeViewColumn *column;
204 GtkWidget *column_label;
206 GtkWidget *sentry;
207 int type;
208 int index;
209 tag_browser *browser;
210 guint timeout;
212 /* */
213 unsigned int signal_id;
214 } tag_element;
216 static void tag2_changed(GtkTreeSelection * sel2, tag_element * te);
218 static gboolean tag2_changed_delayed(gpointer data);
220 * Get/Set enabled
222 int tag2_get_enabled(void)
224 return cfg_get_single_value_as_int_with_default(config, "tag2-plugin", "enable", TRUE);
227 static void tag2_set_enabled(int enabled)
229 cfg_set_single_value_as_int(config, "tag2-plugin", "enable", enabled);
230 if (enabled && !tag2_ht)
232 GtkTreeView *tree = playlist3_get_category_tree_view();
233 tag2_browser_add((GtkWidget *) tree);
234 } else if (!enabled && tag2_ht)
236 tag2_destroy();
241 * Destroy the browser
243 static void tag2_destroy(void)
245 /* clear all the browsers */
246 g_list_foreach(tag2_ht, (GFunc) tag2_destroy_browser, NULL);
247 g_list_free(tag2_ht);
248 tag2_ht = NULL;
251 static void tag2_destroy_tag(tag_element * te)
253 /* Part of inconsistent update hack */
254 g_idle_remove_by_data(te);
255 if (te->timeout)
256 g_source_remove(te->timeout);
257 gtk_widget_destroy(te->vbox);
258 gtk_widget_destroy(GTK_WIDGET(te->tool));
260 if (te->model)
261 g_object_unref(te->model);
262 g_free(te);
266 * Handles right mouse press event
267 * this function fixes the behauviour of gtk to something more logic,
268 * the actually handling of the press happens in the release event
270 static gboolean tag2_song_list_button_press_event(GtkWidget * but, GdkEventButton * event,
271 tag_element *te)
273 GtkTreePath *path = NULL;
274 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(but), event->x, event->y, &path, NULL, NULL, NULL))
276 GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(but));
277 if (gtk_tree_selection_path_is_selected(sel, path))
279 if (event->button == 3)
281 gtk_tree_path_free(path);
282 return TRUE;
283 } else
285 gtk_tree_selection_unselect_all(sel);
286 gtk_tree_path_free(path);
287 return TRUE;
291 if (path)
293 gtk_tree_path_free(path);
295 return FALSE;
299 * Add songs from the selected browser entry of te
301 static void tag2_browser_add_selected(GtkWidget * item, tag_element * te)
303 GtkTreeIter iter;
304 GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(te->tree));
305 if (gtk_tree_selection_get_selected(sel, &(te->model), &iter))
307 MpdData *data;
308 GList *list = NULL;
309 /* now get the song content, this needs all the fields of the previous
311 mpd_database_search_start(connection, TRUE);
312 /* add constraints based on previous browsers */
313 list = g_list_first(te->browser->tag_lists);
314 while (list)
316 tag_element *te3 = list->data; //g_list_nth_data(te->browser->tag_lists, i);
317 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(te3->tree));
318 if (gtk_tree_selection_get_selected(sel, &(te3->model), &iter))
320 gchar *value;
321 gtk_tree_model_get(te3->model, &iter, MPDDATA_MODEL_COL_SONG_TITLE, &value, -1);
322 mpd_database_search_add_constraint(connection, te3->type, (value) ? value : "");
323 g_free(value);
325 list = list->next;
327 data = mpd_database_search_commit(connection);
328 /* if there is a result queue them and add them */
329 if (data)
331 data = misc_sort_mpddata_by_album_disc_track(data);
332 for (; data; data = mpd_data_get_next(data))
334 mpd_playlist_queue_add(connection, data->song->file);
336 mpd_playlist_queue_commit(connection);
342 * Redirect to metadata browser
345 extern GmpcBrowsersMetadata *browsers_metadata;
346 static void tag2_browser_header_information(GtkWidget * item, tag_element * te)
348 GtkTreeIter iter;
349 GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(te->tree));
350 if (gtk_tree_selection_get_selected(sel, &(te->model), &iter))
352 if (gtk_tree_selection_get_selected(sel, &(te->model), &iter))
354 gchar *value = NULL;
355 gtk_tree_model_get(te->model, &iter, MPDDATA_MODEL_COL_SONG_TITLE, &value, -1);
356 if (te->type == MPD_TAG_ITEM_ARTIST || te->type == MPD_TAG_ITEM_ALBUM_ARTIST)
358 info2_activate();
359 gmpc_browsers_metadata_set_artist(browsers_metadata, value);
361 } else if (te->type == MPD_TAG_ITEM_ALBUM)
363 const gchar *artist = gmpc_mpddata_model_get_request_artist(GMPC_MPDDATA_MODEL(te->model));
364 if (artist)
366 info2_activate();
367 gmpc_browsers_metadata_set_album(browsers_metadata, artist, value);
369 info2_activate();
370 info2_fill_album_view(artist,value);
376 g_free(value);
382 * Replace playlist with content of selected browser
384 static void tag2_browser_replace_selected(GtkWidget * item, tag_element * te)
386 mpd_playlist_clear(connection);
387 if (mpd_check_connected(connection))
389 tag2_browser_add_selected(item, te);
390 mpd_player_play(connection);
395 * Handles right mouse release on song list
397 static gboolean tag2_browser_button_release_event(GtkTreeView * tree, GdkEventButton * event, tag_element * te)
399 /* only on right mouse click */
400 if (event->button == 3)
402 GtkWidget *menu, *item;
403 GtkTreeSelection *selection = gtk_tree_view_get_selection(tree);
404 int count = gtk_tree_selection_count_selected_rows(selection);
405 /* if nothing is selected return */
406 if (count == 0)
407 return FALSE;
409 /* create menu to popup */
410 menu = gtk_menu_new();
411 /* add the add widget */
412 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD, NULL);
413 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
414 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(tag2_browser_add_selected), te);
415 /* add the replace widget */
416 item = gtk_image_menu_item_new_with_label(_("Replace"));
417 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
418 gtk_image_new_from_stock(GTK_STOCK_REDO, GTK_ICON_SIZE_MENU));
419 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
420 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(tag2_browser_replace_selected), te);
422 if (te->type == MPD_TAG_ITEM_ARTIST || te->type == MPD_TAG_ITEM_ALBUM_ARTIST
423 || (te->type == MPD_TAG_ITEM_ALBUM
424 && gmpc_mpddata_model_get_request_artist(GMPC_MPDDATA_MODEL(te->model)) != NULL))
426 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_INFO, NULL);
427 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
428 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(tag2_browser_header_information), te);
431 /* popup */
432 gtk_widget_show_all(GTK_WIDGET(menu));
433 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, event->time);
434 return TRUE;
437 return FALSE;
440 static gboolean tag2_key_entry_key_press_event(GtkWidget * entry, GdkEventKey * event, tag_element * te)
442 const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry));
444 if (strlen(text) == 0)
446 if (event->keyval == GDK_KEY_BackSpace || event->keyval == GDK_KEY_Escape)
448 gtk_widget_hide(te->sentry);
449 gtk_widget_grab_focus(te->tree);
450 return TRUE;
453 return FALSE;
456 static int tag2_key_release_event(GtkTreeView * tree, GdkEventKey * event, tag_element * te)
458 guint32 uc;
459 if ((event->state & GDK_CONTROL_MASK) != 0 && event->keyval == GDK_KEY_f)
461 gtk_widget_grab_focus(te->sentry);
462 gtk_widget_show(te->sentry);
463 return FALSE;
464 } /*
465 else if(event->keyval == GDK_KEY_Escape){
466 gtk_entry_set_text(GTK_ENTRY(te->sentry),"");
467 gtk_widget_hide(te->sentry);
468 return FALSE;
469 } */
470 else if ((event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) == 0 &&
471 /* ((event->keyval >= GDK_space && event->keyval <= GDK_z)))*/
472 (uc = gdk_keyval_to_unicode(event->keyval)))
474 char data[2];
475 data[0] = (char)gdk_keyval_to_unicode(event->keyval);
476 data[1] = '\0';
477 gtk_widget_grab_focus(te->sentry);
478 gtk_entry_set_text(GTK_ENTRY(te->sentry), data);
479 gtk_editable_set_position(GTK_EDITABLE(te->sentry), 1);
481 return TRUE;
483 return FALSE;
488 * This is a hack here, for one purpose.
489 * When mpd database changes, it could hapening that when updating a sel change on column A
490 * that during the update, the selection of column B changes, what causes another change in column A
491 * So you get recursive updates.
492 * To avoid this, we "delay" the update callbacks to after the update on column A is completely handled.
493 * TODO: This needs to be handled nicer.
495 static gboolean tag2_changed_delayed(gpointer data)
497 tag2_changed(NULL, (tag_element *) data);
498 return FALSE;
501 void tag2_changed(GtkTreeSelection * sel2, tag_element * te)
503 int nfilter = cfg_get_single_value_as_int_with_default(config, "tag2-plugin", "don't filter", 0);
504 int found = 0;
505 MpdData *data = NULL;
506 tag_browser *browser = te->browser;
507 int not_to_update = te->index;
508 GList *tel = g_list_first(browser->tag_lists);
509 static int working = 0;
510 if (working)
512 // Only call the idle thing once.
513 if(working == 1)
514 g_idle_add(tag2_changed_delayed, te);
515 working++;
516 return;
518 working = 1;
519 /* Clear songs list */
520 tel = g_list_first(browser->tag_lists);
521 /* check if the user selected a row, if not do nothing */
523 /* Set on all tags the request artist, if available. */
525 gchar *artist = NULL;
526 GList *first_te = NULL;
527 for (first_te = tel; first_te && !artist; first_te = first_te->next)
529 tag_element *te3 = first_te->data;
530 GtkTreeIter iter;
531 GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(te3->tree));
532 if (gtk_tree_selection_get_selected(sel, &(te3->model), &iter))
534 if (mpd_server_tag_supported(connection, te3->type))
536 gchar *value;
537 gtk_tree_model_get(te3->model, &iter, MPDDATA_MODEL_COL_SONG_TITLE, &value, -1);
538 if (te3->type == MPD_TAG_ITEM_ARTIST || te3->type == MPD_TAG_ITEM_ALBUM_ARTIST)
540 artist = g_strdup(value);
542 g_free(value);
547 for (first_te = tel; first_te; first_te = first_te->next)
549 tag_element *te3 = first_te->data;
550 if (te3->index != not_to_update)
552 gmpc_mpddata_model_set_request_artist(GMPC_MPDDATA_MODEL(te3->model), artist);
553 if (te3->tool->request_artist)
555 g_free(te3->tool->request_artist);
556 te3->tool->request_artist = NULL;
558 te3->tool->request_artist = g_strdup(artist);
561 q_free(artist);
564 while (tel)
566 tag_element *te_i = tel->data;
567 if (te_i->index != not_to_update && mpd_server_tag_supported(connection, te_i->type))
569 GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(te_i->tree));
570 GtkTreeIter iter;
571 GList *first = g_list_first(browser->tag_lists);
572 /* only do the following query if it isn't the last one */
574 /* Search for the fields of the next tag, this needs the value/type of all the previous,
575 * Parsed from left to right
577 mpd_database_search_field_start(connection, te_i->type);
578 /* fil in the next */
579 while (first)
581 tag_element *te3 = first->data;
582 if (te3->index != te_i->index)
584 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(te3->tree));
585 if (gtk_tree_selection_get_selected(sel, &(te3->model), &iter))
587 if (mpd_server_tag_supported(connection, te3->type))
589 gchar *value;
590 gtk_tree_model_get(te3->model, &iter, MPDDATA_MODEL_COL_SONG_TITLE, &value, -1);
591 if (!nfilter || te3->index < te_i->index)
592 mpd_database_search_add_constraint(connection, te3->type, (value) ? value : "");
593 g_free(value);
597 first = first->next;
599 data = mpd_database_search_commit(connection);
600 /* Delete items that match */
601 if (strlen(gtk_entry_get_text(GTK_ENTRY(te_i->sentry))) > 0)
603 MpdData_real *d = (MpdData_real *) data;
604 /* Get the entry from the text view */
605 const char *str = gtk_entry_get_text(GTK_ENTRY(te_i->sentry));
606 /* Lowercase it. */
607 gchar *sb1 = g_utf8_casefold(str, -1);
608 /* Normalize it, this to ensure they use the same way of representing the character */
609 gchar *sb = g_utf8_normalize(sb1, -1, G_NORMALIZE_ALL_COMPOSE);
610 g_free(sb1);
611 while (d)
613 /* Lowercase it. */
614 gchar *sa1 = g_utf8_casefold(d->tag, -1);
615 /* Normalize it, this to ensure they use the same way of representing the character */
616 gchar *sa = g_utf8_normalize(sa1, -1, G_NORMALIZE_ALL_COMPOSE);
617 g_free(sa1);
618 /* compare the utf-8 strings, this should be a fairly good compare
619 * because I made sure the utf-8 is represented in a equal way
621 if (strstr(sa, sb) == NULL)
623 /* If it does not match, remove the item from the list */
624 data = mpd_data_delete_item((MpdData *) d);
625 d = (MpdData_real *) data;
626 } else
627 /* if it does match, we go to the next and check that */
628 d = d->next;
629 /* cleanup */
630 g_free(sa);
633 /* if there are items in the lest, make sure we get the first one */
634 if (data)
635 data = mpd_data_get_first(data);
636 g_free(sb);
639 * Update the TreeModel using the special incremental replace function.
640 * This will make sure, selected rows that are matched, don't get de-selected
642 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(te_i->tree));
645 /* get the selected row, if any */
646 if (gtk_tree_selection_get_selected(sel, NULL, NULL))
648 gmpc_mpddata_model_set_mpd_data_slow(GMPC_MPDDATA_MODEL(te_i->model), data);
649 /* this make sure the selected row is centered in the middle of the treeview.
650 * Otherwise the user could have the tedious job of finding it again
653 if (gtk_tree_selection_get_selected(sel, NULL, &iter))
655 /* get the path to the selected row */
656 GtkTreePath *path = gtk_tree_model_get_path(te_i->model, &iter);
657 /* scroll to the path, and center it in the middle of the treeview, at the left of the column */
658 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(te_i->tree), path, NULL, TRUE, 0.5, 0);
659 /* free the path */
660 gtk_tree_path_free(path);
662 }else {
664 data = misc_sort_mpddata(data,
665 (GCompareDataFunc)gmpc_mpddata_model_test_sort_func,te_i->model);
666 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(te_i->model), data);
669 tel = tel->next;
672 tel = g_list_first(browser->tag_lists);
673 data = NULL;
674 while (tel)
676 GtkTreeSelection *sel;
677 GtkTreeIter iter;
678 te = tel->data;
679 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(te->tree));
680 if (gtk_tree_selection_get_selected(sel, &(te->model), &iter))
682 gchar *value;
683 if (!found)
685 mpd_database_search_start(connection, TRUE);
686 found = 1;
688 gtk_tree_model_get(te->model, &iter, MPDDATA_MODEL_COL_SONG_TITLE, &value, -1);
689 mpd_database_search_add_constraint(connection, te->type, (value) ? value : "");
690 g_free(value);
692 tel = tel->next;
694 if (found)
695 data = mpd_database_search_commit(connection);
696 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(gtk_tree_view_get_model(browser->tag_songlist)), data);
697 working = 0;
700 static void tag2_destroy_browser(tag_browser * browser, gpointer user_data)
702 GtkTreeIter iter;
703 GtkTreeModel *model;
704 GtkTreePath *path;
705 if (!browser)
707 return;
710 /* remove it from the left hand view */
711 model = gtk_tree_row_reference_get_model(browser->ref_iter);
712 path = gtk_tree_row_reference_get_path(browser->ref_iter);
713 if (path)
715 if (gtk_tree_model_get_iter(model, &iter, path))
717 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
719 gtk_tree_path_free(path);
721 gtk_tree_row_reference_free(browser->ref_iter);
723 /* free browser */
724 g_free(browser->name);
725 g_free(browser->key);
726 /* free the tag browsers */
727 g_list_foreach(browser->tag_lists, (GFunc) tag2_destroy_tag, NULL);
728 g_list_free(browser->tag_lists);
729 /* destroy the container */
730 if (browser->tag2_vbox)
732 g_object_unref(browser->tag2_vbox);
734 /* clear structure */
735 g_free(browser);
738 static void tag2_column_header_menu_item_search_clicked(GtkCheckMenuItem * item, tag_element * te)
740 gtk_widget_grab_focus(te->sentry);
741 gtk_widget_show(te->sentry);
743 static void tag2_column_header_menu_item_clicked(GtkCheckMenuItem * item, tag_element * te)
745 GList *list;
746 gpointer userdata = g_object_get_data(G_OBJECT(item), "tag-id");
747 te->type = GPOINTER_TO_INT(userdata);
748 if (te->type == MPD_TAG_ITEM_ARTIST || te->type == MPD_TAG_ITEM_ALBUM_ARTIST)
750 te->tool->mtype = META_ARTIST_ART;
751 } else if (te->type == MPD_TAG_ITEM_ALBUM)
753 te->tool->mtype = META_ALBUM_ART;
754 } else
755 te->tool->mtype = 0;
757 // gtk_tree_view_column_set_title(te->column, _(mpdTagItemKeys[te->type]));
758 gtk_label_set_text(GTK_LABEL(te->column_label), _(mpdTagItemKeys[te->type]));
759 /* if the first is changed, refill the first.
760 * if any other is changed, make the edited refill by triggering changed signal on the previous.
762 /* clear the list, makes changing the tree layout better. */
763 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(te->model), NULL);
764 /* clear all settings */
765 if (te->image_renderer)
767 /* Disable fixed height mode, otherwise GTK won't propperly resize the
768 * height of the row */
769 gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(te->tree), FALSE);
770 gtk_cell_layout_clear_attributes(GTK_CELL_LAYOUT(te->column), te->image_renderer);
771 if (te->type == MPD_TAG_ITEM_ARTIST || te->type == MPD_TAG_ITEM_ALBUM_ARTIST || te->type == MPD_TAG_ITEM_ALBUM)
773 int size = cfg_get_single_value_as_int_with_default(config, "gmpc-mpddata-model", "icon-size", 32);
774 gtk_tree_view_column_add_attribute(te->column, te->image_renderer, "pixbuf", MPDDATA_MODEL_META_DATA);
775 gtk_cell_renderer_set_fixed_size(te->image_renderer, size, size);
776 } else
778 int width, height;
779 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
780 gtk_cell_renderer_set_fixed_size(te->image_renderer, width, height);
781 gtk_tree_view_column_add_attribute(te->column, te->image_renderer, "icon-name", MPDDATA_MODEL_COL_ICON_ID);
783 gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(te->tree), TRUE);
786 /* index starts at 1 */
787 if (te->index != 1)
789 list = g_list_nth(te->browser->tag_lists, te->index - 2);
790 if (list)
792 tag_element *te2 = list->data;
793 /* update the content */
794 tag2_changed(gtk_tree_view_get_selection(GTK_TREE_VIEW(te2->tree)), te2);
796 } else
798 MpdData *data;
799 list = g_list_first(te->browser->tag_lists);
800 if (list->next)
802 tag_element *te2 = list->next->data;
803 tag2_changed(gtk_tree_view_get_selection(GTK_TREE_VIEW(te2->tree)), te2);
804 } else
806 if (mpd_server_tag_supported(connection, te->type))
808 mpd_database_search_field_start(connection, te->type);
809 data = mpd_database_search_commit(connection);
810 gmpc_mpddata_model_set_mpd_data_slow(GMPC_MPDDATA_MODEL(te->model), data);
811 } else
813 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(te->model), NULL);
818 tag2_save_browser(te->browser);
819 /* Hack to make pref window update */
820 if (pref_combo)
822 GtkTreeIter iter;
823 if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(pref_combo), &iter))
825 gtk_combo_box_set_active(GTK_COMBO_BOX(pref_combo), -1);
826 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(pref_combo), &iter);
831 static void tag2_column_header_clicked(GtkTreeViewColumn * column, tag_element * te)
833 int i = 0;
834 GtkWidget *menu = gtk_menu_new();
837 GtkWidget *item = gtk_image_menu_item_new_from_stock(GTK_STOCK_FIND, NULL);
838 g_signal_connect(G_OBJECT(item),"activate", G_CALLBACK(tag2_column_header_menu_item_search_clicked), te);
839 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(item));
841 for (i = 0; i < MPD_TAG_ITEM_ANY; i++)
843 if (mpd_server_tag_supported(connection, i))
845 GtkWidget *item = gtk_check_menu_item_new_with_label(_(mpdTagItemKeys[i]));
846 g_object_set_data(G_OBJECT(item), "tag-id", GINT_TO_POINTER(i));
847 if (te->type == i)
849 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
850 } else
851 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(tag2_column_header_menu_item_clicked), te);
852 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(item));
855 gtk_widget_show_all(GTK_WIDGET(menu));
856 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
860 static gboolean tag2_sentry_changed_real(tag_element * te)
862 tag_element *te2 = g_malloc0(sizeof(*te2));
863 te2->index = -1;
864 te2->browser = te->browser;
865 tag2_changed(NULL, te2);
867 g_free(te2);
868 te->timeout = 0;
869 /* Disable: #3480
870 if (strlen(gtk_entry_get_text(GTK_ENTRY(te->sentry))) == 0)
872 gtk_widget_hide(te->sentry);
875 return FALSE;
878 static void tag2_sentry_changed(GtkWidget * entry, tag_element * te)
880 gtk_widget_show(te->sentry);
881 if (te->timeout)
882 g_source_remove(te->timeout);
883 te->timeout = g_timeout_add(250, (GSourceFunc) tag2_sentry_changed_real, te);
884 // tag2_sentry_changed_real(te);
887 static void playtime_changed(GmpcMpdDataModel * model, gulong playtime)
891 static void tag2_songlist_clear_selection(GtkWidget * button, tag_browser * browser)
893 GList *iter = g_list_first(browser->tag_lists);
894 for (; iter; iter = g_list_next(iter))
896 tag_element *te = iter->data;
897 g_signal_handlers_block_by_func(G_OBJECT(te->sentry), tag2_sentry_changed, te);
898 g_signal_handlers_block_by_func(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(te->tree))), tag2_changed,
899 te);
900 gtk_entry_set_text(GTK_ENTRY(te->sentry), "");
901 gtk_widget_hide(te->sentry);
903 for (iter = g_list_first(browser->tag_lists); iter; iter = g_list_next(iter))
905 MpdData *data;
906 tag_element *te = iter->data;
908 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(te->model), NULL);
909 if (mpd_server_tag_supported(connection, te->type))
911 mpd_database_search_field_start(connection, te->type);
913 data = mpd_database_search_commit(connection);
914 gmpc_mpddata_model_set_mpd_data_slow(GMPC_MPDDATA_MODEL(te->model), data);
915 } else
916 gmpc_mpddata_model_set_mpd_data_slow(GMPC_MPDDATA_MODEL(te->model), NULL);
917 gmpc_mpddata_model_set_request_artist(GMPC_MPDDATA_MODEL(te->model), NULL);
918 if (te->tool->request_artist)
920 g_free(te->tool->request_artist);
921 te->tool->request_artist = NULL;
925 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(gtk_tree_view_get_model(browser->tag_songlist)), NULL);
927 for (iter = g_list_first(browser->tag_lists); iter; iter = g_list_next(iter))
929 tag_element *te = iter->data;
930 g_signal_handlers_unblock_by_func(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(te->tree))), tag2_changed,
931 te);
932 g_signal_handlers_unblock_by_func(G_OBJECT(te->sentry), tag2_sentry_changed, te);
935 static void tag_browser_clear_search_entry(GtkEntry * entry, GtkEntryIconPosition icon_pos, GdkEvent * event,
936 gpointer user_data)
938 if (icon_pos == GTK_ENTRY_ICON_SECONDARY)
940 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)
942 gtk_widget_hide(GTK_WIDGET(entry));
944 else
946 gtk_entry_set_text(GTK_ENTRY(entry), "");
952 static void tag2_songlist_add_tag(tag_browser * browser, const gchar * name, int type)
954 GtkCellRenderer *renderer;
955 GtkTreeViewColumn *column;
956 GtkWidget *hbox;
957 tag_element *te = g_malloc0(sizeof(*te));
959 browser->tag_lists = g_list_append(browser->tag_lists, te);
960 /* Tag Element */
961 te->type = type;
962 te->index = g_list_length(browser->tag_lists);
963 te->model = (GtkTreeModel *) gmpc_mpddata_model_new();
965 te->sentry = gtk_entry_new();
966 gtk_entry_set_icon_from_stock(GTK_ENTRY(te->sentry), GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
967 g_signal_connect(GTK_ENTRY(te->sentry), "icon-press", G_CALLBACK(tag_browser_clear_search_entry), NULL);
969 gtk_entry_set_icon_from_stock(GTK_ENTRY(te->sentry), GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND);
970 gtk_entry_set_icon_activatable(GTK_ENTRY(te->sentry), GTK_ENTRY_ICON_PRIMARY, FALSE);
971 g_signal_connect_swapped(G_OBJECT(te->sentry), "activate", G_CALLBACK(tag2_sentry_changed_real), te);
972 te->sw = gtk_scrolled_window_new(NULL, NULL);
973 te->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
974 te->tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(te->model));
975 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(te->tree)),
976 GTK_SELECTION_SINGLE);
977 te->tool = gmpc_mpd_data_treeview_tooltip_new(GTK_TREE_VIEW(te->tree), 0);
978 te->browser = browser;
980 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(te->tree), TRUE);
981 if (te->type == MPD_TAG_ITEM_ARTIST || te->type == MPD_TAG_ITEM_ALBUM_ARTIST)
983 te->tool->mtype = META_ARTIST_ART;
984 } else if (te->type == MPD_TAG_ITEM_ALBUM)
986 te->tool->mtype = META_ALBUM_ART;
987 } else
988 te->tool->mtype = 0;
990 /* entry */
991 gtk_widget_set_no_show_all(te->sentry, TRUE);
992 gtk_widget_hide(te->sentry);
995 g_signal_connect(G_OBJECT(te->sentry), "changed", G_CALLBACK(tag2_sentry_changed), te);
996 g_signal_connect(G_OBJECT(te->sentry), "key-press-event", G_CALLBACK(tag2_key_entry_key_press_event), te);
998 gtk_box_pack_start(GTK_BOX(te->vbox), te->sentry, FALSE, TRUE, 0);
1000 g_signal_connect(G_OBJECT(te->tree), "key-press-event", G_CALLBACK(tag2_key_release_event), te);
1001 /* setup sw */
1002 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(te->sw), GTK_SHADOW_ETCHED_IN);
1003 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(te->sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1004 /* add tree */
1005 gtk_container_add(GTK_CONTAINER(te->sw), te->tree);
1006 gtk_box_pack_start(GTK_BOX(te->vbox), te->sw, TRUE, TRUE, 0);
1007 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(te->tree), FALSE);
1009 /* Add the column, and set it up */
1010 te->column = column = gtk_tree_view_column_new();
1013 /* Set search icon next to the label */
1014 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1015 gtk_box_pack_start(GTK_BOX(hbox),
1016 gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU),
1017 FALSE, FALSE, 0);
1019 te->column_label = gtk_label_new(name);
1020 gtk_box_pack_start(GTK_BOX(hbox),
1021 te->column_label,
1022 TRUE, TRUE, 0);
1023 gtk_tree_view_column_set_widget(GTK_TREE_VIEW_COLUMN(te->column), hbox);
1024 gtk_widget_show_all(hbox);
1026 g_signal_connect(G_OBJECT(te->column), "clicked", G_CALLBACK(tag2_column_header_clicked), te);
1027 gtk_tree_view_column_set_clickable(te->column, TRUE);
1028 te->image_renderer = NULL;
1029 if (cfg_get_single_value_as_int_with_default(config, "tag2-plugin", "show-image-column", 1))
1031 te->image_renderer = renderer = gtk_cell_renderer_pixbuf_new();
1033 gtk_tree_view_column_pack_start(column, renderer, FALSE);
1034 if (te->type == MPD_TAG_ITEM_ARTIST || te->type == MPD_TAG_ITEM_ALBUM_ARTIST || te->type == MPD_TAG_ITEM_ALBUM)
1036 int size = cfg_get_single_value_as_int_with_default(config, "gmpc-mpddata-model", "icon-size", 32);
1038 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", MPDDATA_MODEL_META_DATA);
1039 gtk_cell_renderer_set_fixed_size(renderer, size, size);
1040 } else
1042 int width, height;
1043 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
1044 gtk_cell_renderer_set_fixed_size(te->image_renderer, width, height);
1045 gtk_tree_view_column_add_attribute(column, renderer, "icon-name", MPDDATA_MODEL_COL_ICON_ID);
1049 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1050 gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(te->tree), TRUE);
1051 renderer = gtk_cell_renderer_text_new();
1052 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1053 gtk_tree_view_column_add_attribute(column, renderer, "text", MPDDATA_MODEL_COL_SONG_TITLE);
1054 gtk_tree_view_insert_column(GTK_TREE_VIEW(te->tree), column, -1);
1055 /* set the search column */
1056 gtk_tree_view_set_search_column(GTK_TREE_VIEW(te->tree), MPDDATA_MODEL_COL_SONG_TITLE);
1058 gtk_box_pack_start(GTK_BOX(browser->tag_hbox), te->vbox, TRUE, TRUE, 0);
1059 g_signal_connect(G_OBJECT(te->tree), "button-press-event", G_CALLBACK(tag2_song_list_button_press_event), te);
1060 g_signal_connect(G_OBJECT(te->tree), "button-release-event", G_CALLBACK(tag2_browser_button_release_event), te);
1062 /* Signal */
1063 te->signal_id = g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(te->tree))),
1064 "changed", G_CALLBACK(tag2_changed), te);
1068 static void tag2_create_tags(tag_browser * browser)
1070 gchar *str =
1071 cfg_get_single_value_as_string_with_default(config, "tag2-browsers", browser->key, "genre|artist|album");
1072 if (str)
1074 char **strv = g_strsplit(str, "|", 0);
1075 int i;
1076 for (i = 0; strv && strv[i]; i++)
1078 int tag = mpd_misc_get_tag_by_name(strv[i]);
1079 if (tag >= 0)
1081 /* This needs to be done based on config */
1082 tag2_songlist_add_tag(browser, strv[i], tag);
1085 g_strfreev(strv);
1086 g_free(str);
1088 gtk_widget_show_all(browser->tag2_vbox);
1091 static void tag2_init(void)
1093 int i;
1095 * if first time used, add 2 example browsers
1097 if (cfg_get_single_value_as_int_with_default(config, "tag2-plugin", "first-use", 1))
1099 cfg_set_single_value_as_int(config, "tag2-plugin", "first-use", 0);
1101 cfg_set_single_value_as_string(config, "tag2-browsers", "Artist Browser", "artist|album|disc");
1102 cfg_set_single_value_as_string(config, "tag2-plugin:Artist Browser", "name", "Artist Browser");
1103 cfg_set_single_value_as_string(config, "tag2-browsers", "Genre Browser", "genre|artist|album|disc");
1104 cfg_set_single_value_as_string(config, "tag2-plugin:Genre Browser", "name", "Genre Browser");
1107 tags_store = (GtkTreeModel *) gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
1108 for (i = 0; i < MPD_TAG_ITEM_ANY; i++)
1110 GtkTreeIter titer;
1111 gtk_list_store_append(GTK_LIST_STORE(tags_store), &titer);
1112 gtk_list_store_set(GTK_LIST_STORE(tags_store), &titer, 0, mpdTagItemKeys[i], 1, i, 2, _(mpdTagItemKeys[i]), -1);
1117 static void tag2_init_browser(tag_browser * browser)
1119 gchar *key;
1120 GtkWidget *sw;
1121 GmpcMpdDataModel *model = NULL;
1122 /* create the pane that separates the song list from the browsers */
1123 key = g_strdup_printf("tag2-plugin:%s", browser->key);
1124 browser->tag2_vbox = gtk_hpaned_new();
1125 gmpc_paned_size_group_add_paned(GMPC_PANED_SIZE_GROUP(paned_size_group), GTK_PANED(browser->tag2_vbox));
1127 /* box with tag treeviews (browsers) */
1128 browser->tag_hbox = gtk_vbox_new(TRUE, 6);
1130 /* Add this to the 1st pane */
1131 gtk_paned_add1(GTK_PANED(browser->tag2_vbox), browser->tag_hbox);
1133 /** Create Songs list view */
1134 /* create the treeview model, this is a GmpcMpdData model */
1135 model = gmpc_mpddata_model_new();
1136 gmpc_mpddata_model_disable_image(GMPC_MPDDATA_MODEL(model));
1137 g_signal_connect(G_OBJECT(model), "playtime_changed", G_CALLBACK(playtime_changed), NULL);
1138 /* create scrolled window to make the treeview scrollable */
1139 sw = gtk_scrolled_window_new(NULL, NULL);
1140 /* setup the scrolled window */
1141 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
1142 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1143 /* create the actual treeview, use the GmpcTreeview type, so all the handling is done automatic */
1145 browser->tag_songlist = (GtkTreeView *) gmpc_data_view_new(key, FALSE);
1146 gtk_tree_view_set_model(GTK_TREE_VIEW(browser->tag_songlist), model);
1147 g_free(key);
1148 /* add the treeview to the scrolled window */
1149 gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(browser->tag_songlist));
1150 /* add the scrolled window to the 2nd pane */
1151 gtk_paned_add2(GTK_PANED(browser->tag2_vbox), sw);
1152 /* Create an extra reference to the paned window containing everything, this way
1153 * I can add/remove it from gmpc's container withouth it being destroyed by gtk
1155 g_object_ref_sink(browser->tag2_vbox);
1156 /* show everything */
1157 gtk_widget_show_all(browser->tag2_vbox);
1159 tag2_connection_changed_foreach(browser, NULL);
1162 static gboolean tag2_custom_find(tag_browser * a, gchar * key)
1164 return strcmp(a->key, key);
1167 static void tag2_browser_selected(GtkWidget * container)
1169 GtkTreeView *tree = playlist3_get_category_tree_view();
1170 GtkTreeModel *model = gtk_tree_view_get_model(tree);
1171 GtkTreeSelection *sel = gtk_tree_view_get_selection(tree);
1172 GtkTreeIter iter;
1173 if (gtk_tree_selection_get_selected(sel, &model, &iter))
1175 gchar *key;
1176 gtk_tree_model_get(model, &iter, PL3_CAT_INT_ID, &key, -1);
1177 if (key)
1179 GList *node = g_list_find_custom(tag2_ht, key, (GCompareFunc) tag2_custom_find);
1180 if (node)
1182 tag_browser *tb = node->data;
1183 if (tb)
1186 if (tb->tag2_vbox == NULL)
1187 tag2_init_browser(tb);
1188 gtk_container_add(GTK_CONTAINER(container), tb->tag2_vbox);
1189 gtk_widget_show_all(container);
1190 tag2_current = tb->tag2_vbox;
1193 g_free(key);
1199 static void tag2_browser_unselected(GtkWidget * container)
1201 if (tag2_current)
1202 gtk_container_remove(GTK_CONTAINER(container), tag2_current);
1203 tag2_current = NULL;
1206 static void tag2_save_browser(tag_browser * browser)
1208 GString *str = g_string_new("");
1209 GList *list = browser->tag_lists;
1210 for (; list; list = g_list_next(list))
1212 tag_element *te = list->data;
1213 str = g_string_append(str, mpdTagItemKeys[te->type]);
1214 if (list->next)
1215 str = g_string_append(str, "|");
1218 cfg_set_single_value_as_string(config, "tag2-browsers", browser->key, str->str);
1219 g_string_free(str, TRUE);
1222 static void tag2_connection_changed_foreach(tag_browser * browser, gpointer userdata)
1224 if (browser->tag2_vbox)
1226 tag_element *te = NULL;
1227 if (browser->tag_lists == NULL)
1228 tag2_create_tags(browser);
1229 te = g_list_nth_data(browser->tag_lists, 0);
1230 if (te != NULL && mpd_check_connected(connection))
1232 MpdData *data;
1233 tag_element *te2 = NULL;
1234 if (mpd_server_tag_supported(connection, te->type))
1236 mpd_database_search_field_start(connection, te->type);
1237 data = mpd_database_search_commit(connection);
1238 gmpc_mpddata_model_set_mpd_data_slow(GMPC_MPDDATA_MODEL(te->model), data);
1239 } else
1241 gmpc_mpddata_model_set_mpd_data_slow(GMPC_MPDDATA_MODEL(te->model), NULL);
1244 te2 = g_malloc0(sizeof(*te2));
1245 te2->index = -1;
1246 te2->browser = te->browser;
1247 tag2_changed(NULL, te2);
1249 g_free(te2);
1251 tag2_changed(gtk_tree_view_get_selection(GTK_TREE_VIEW(te->tree)), te);
1256 static void tag2_connection_changed(MpdObj * mi, int connect, gpointer data)
1258 if (connect && tag2_ht)
1260 /* create tags */
1261 g_list_foreach(tag2_ht, (GFunc) tag2_connection_changed_foreach, NULL);
1265 static void tag2_status_changed(MpdObj * mi, ChangedStatusType what, gpointer data)
1267 if (what & MPD_CST_DATABASE)
1269 if (tag2_ht)
1271 g_list_foreach(tag2_ht, (GFunc) tag2_connection_changed_foreach, NULL);
1272 /* GList *list = g_list_first(tag2_ht);
1273 for(;list;list = g_list_next(list))
1275 tag2_songlist_clear_selection(NULL, list->data);
1283 * Preferences window
1285 void tag2_pref_destroy(GtkWidget * container)
1287 gtk_container_remove(GTK_CONTAINER(container), pref_vbox);
1288 pref_vbox = NULL;
1289 pref_combo = NULL;
1290 pref_entry = NULL;
1294 * Browser selector changed
1296 static void tag2_pref_combo_changed(GtkComboBox * box, GtkTreeModel * model2)
1298 GtkTreeIter iter;
1300 /* clear old data */
1301 gtk_list_store_clear(GTK_LIST_STORE(model2));
1303 if (gtk_combo_box_get_active_iter(box, &iter))
1305 GtkTreeModel *model = gtk_combo_box_get_model(box);
1306 gchar *group;
1307 gchar *str;
1308 tag_browser *tb;
1309 /* get the active tag browser */
1310 gtk_tree_model_get(model, &iter, 2, &tb, -1);
1311 if (tb->tag2_vbox == NULL)
1312 tag2_init_browser(tb);
1313 /* iterate over all the tag elements and add them to the list */
1314 if (tb->tag_lists)
1316 GList *liter = g_list_first(tb->tag_lists);
1317 for (; liter; liter = g_list_next(liter))
1319 GtkTreeIter titer;
1320 tag_element *te = liter->data;
1321 gtk_list_store_append(GTK_LIST_STORE(model2), &titer);
1322 gtk_list_store_set(GTK_LIST_STORE(model2), &titer, 0, mpdTagItemKeys[te->type], 1, te->type, 2, te, -1);
1326 /* Get browser name and set entry */
1327 group = g_strdup_printf("tag2-plugin:%s", tb->key);
1328 str = cfg_get_single_value_as_string_with_default(config, group, "name", "default");
1329 gtk_entry_set_text(GTK_ENTRY(pref_entry), str);
1330 g_free(str);
1331 g_free(group);
1332 } else
1334 /* only clear the text when nothing is selected, else the currently selected is cleared..
1335 * yeah, longlive signals
1337 gtk_entry_set_text(GTK_ENTRY(pref_entry), "");
1341 static void tag2_pref_add_browser_clicked(GtkWidget * but, GtkComboBox * combo)
1343 GtkTreeView *tree = playlist3_get_category_tree_view();
1344 GtkTreeIter titer;
1345 GList *node;
1346 GtkListStore *model = (GtkListStore *) gtk_combo_box_get_model(combo);
1347 gchar *name = g_strdup_printf("%u", g_random_int());
1348 cfg_set_single_value_as_string(config, "tag2-browsers", name, "");
1350 tag2_browser_add_browser(GTK_WIDGET(tree), name);
1352 node = g_list_find_custom(tag2_ht, name, (GCompareFunc) tag2_custom_find);
1353 if (node)
1355 tag_browser *tb = node->data;
1356 gtk_list_store_append(model, &titer);
1357 gtk_list_store_set(model, &titer, 0, name, 1, "default", 2, tb, -1);
1358 gtk_combo_box_set_active_iter(combo, &titer);
1359 /* change this to store TB in list store) */
1361 g_free(name);
1366 * Update the title of the browser
1368 static void tag2_pref_entry_changed(GtkWidget * entry, GtkComboBox * combo)
1370 GtkTreeIter iter;
1371 GtkListStore *model = (GtkListStore *) gtk_combo_box_get_model(combo);
1372 if (gtk_combo_box_get_active_iter(combo, &iter))
1374 gchar *group, *key;
1375 GtkTreePath *path;
1376 tag_browser *tb;
1377 const gchar *name = gtk_entry_get_text(GTK_ENTRY(pref_entry));
1378 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &key, 2, &tb, -1);
1379 gtk_list_store_set(model, &iter, 1, name, -1);
1381 g_free(tb->name);
1382 tb->name = g_strdup(name);
1383 if ((path = gtk_tree_row_reference_get_path(tb->ref_iter)))
1385 GtkTreeIter iter2;
1386 GtkTreeModel *model2 = gtk_tree_row_reference_get_model(tb->ref_iter);
1387 gtk_tree_model_get_iter(model2, &iter2, path);
1388 gtk_list_store_set(GTK_LIST_STORE(model2), &iter2, PL3_CAT_TITLE, name, -1);
1389 gtk_tree_path_free(path);
1392 group = g_strdup_printf("tag2-plugin:%s", key);
1393 cfg_set_single_value_as_string(config, group, "name", (char *)name);
1394 g_free(group);
1399 * Handles editing of the columns type
1401 static void tag2_pref_column_type_edited(GtkCellRendererText * text, gchar * path, char *new_data, GtkTreeModel * model)
1403 GtkTreeIter iter;
1404 if (gtk_tree_model_get_iter_from_string(model, &iter, path))
1406 int tag = mpd_misc_get_tag_by_name(new_data);
1407 tag_element *te;
1408 gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, new_data, 1, tag, -1);
1409 gtk_tree_model_get(model, &iter, 2, &te, -1);
1410 te->type = tag;
1412 if (te->type == MPD_TAG_ITEM_ARTIST || te->type == MPD_TAG_ITEM_ALBUM_ARTIST)
1414 te->tool->mtype = META_ARTIST_ART;
1415 } else if (te->type == MPD_TAG_ITEM_ALBUM)
1417 te->tool->mtype = META_ALBUM_ART;
1418 } else
1419 te->tool->mtype = 0;
1420 /* clear all settings */
1421 if (te->image_renderer)
1423 /* Disable fixed height mode, otherwise GTK won't propperly resize the
1424 * height of the row */
1425 gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(te->tree), FALSE);
1426 gtk_cell_layout_clear_attributes(GTK_CELL_LAYOUT(te->column), te->image_renderer);
1427 if (te->type == MPD_TAG_ITEM_ARTIST || te->type == MPD_TAG_ITEM_ALBUM_ARTIST
1428 || te->type == MPD_TAG_ITEM_ALBUM)
1430 int size = cfg_get_single_value_as_int_with_default(config, "gmpc-mpddata-model", "icon-size", 32);
1431 gtk_tree_view_column_add_attribute(te->column, te->image_renderer, "pixbuf", MPDDATA_MODEL_META_DATA);
1432 gtk_cell_renderer_set_fixed_size(te->image_renderer, size, size);
1433 } else
1435 int width, height;
1436 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
1437 gtk_cell_renderer_set_fixed_size(te->image_renderer, width, height);
1438 gtk_tree_view_column_add_attribute(te->column, te->image_renderer, "icon-name",
1439 MPDDATA_MODEL_COL_ICON_ID);
1441 gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(te->tree), TRUE);
1444 gtk_label_set_text(GTK_LABEL(te->column_label), _(mpdTagItemKeys[te->type]));
1446 //gtk_tree_view_column_set_title(te->column, _(mpdTagItemKeys[te->type]));
1447 tag2_songlist_clear_selection(NULL, te->browser);
1448 tag2_save_browser(te->browser);
1453 * Add's a column to the selected tag browser
1455 static void tag2_pref_browser_remove(GtkWidget * but, GtkComboBox * box)
1457 GtkTreeIter iter;
1458 if (gtk_combo_box_get_active_iter(box, &iter))
1460 GtkTreeModel *model = gtk_combo_box_get_model(box);
1461 gchar *name;
1462 gchar *key;
1463 tag_browser *tb;
1464 gtk_tree_model_get(model, &iter, 2, &tb, -1);
1465 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
1467 key = g_strdup(tb->key);
1468 /* remove from browser list */
1469 tag2_ht = g_list_remove(tag2_ht, tb);
1470 /* destroy remaining */
1471 tag2_destroy_browser(tb, NULL);
1473 /* TODO delete */
1474 cfg_del_single_value(config, "tag2-browsers", key);
1476 name = g_strdup_printf("tag2-plugin:%s", key);
1477 cfg_remove_class(config, name);
1478 g_free(name);
1479 name = g_strdup_printf("tag2-plugin:%s-colsize", key);
1480 cfg_remove_class(config, name);
1481 g_free(name);
1482 name = g_strdup_printf("tag2-plugin:%s-colpos", key);
1483 cfg_remove_class(config, name);
1484 g_free(name);
1485 name = g_strdup_printf("tag2-plugin:%s-colshow", key);
1486 cfg_remove_class(config, name);
1487 g_free(name);
1488 g_free(key);
1489 pl3_update_go_menu();
1494 * Add's a column to the selected tag browser
1496 static void tag2_pref_column_add(GtkWidget * but, GtkComboBox * box)
1498 GtkTreeIter iter;
1499 GtkTreeModel *model2 = g_object_get_data(G_OBJECT(but), "model");
1500 if (gtk_combo_box_get_active_iter(box, &iter))
1502 GtkTreeModel *model = gtk_combo_box_get_model(box);
1503 tag_browser *tb;
1504 /* TODO: Change to the browser tag editing stuff */
1505 gtk_tree_model_get(model, &iter, 2, &tb, -1);
1506 tag2_songlist_add_tag(tb, mpdTagItemKeys[MPD_TAG_ITEM_ARTIST], MPD_TAG_ITEM_ARTIST);
1507 tag2_pref_combo_changed(box, model2);
1508 gtk_widget_show_all(tb->tag_hbox);
1509 tag2_save_browser(tb);
1510 /* if it's the first, update */
1511 /*if(g_list_length(tb->tag_lists) == 1)
1513 GList *giter;
1515 giter = g_list_first(tb->tag_lists);
1516 if(giter)
1518 MpdData *data;
1519 tag_element *te2 = giter->data;
1520 *//* update the content */
1521 /* if(mpd_server_tag_supported(connection,te2->type))
1523 mpd_database_search_field_start(connection, te2->type);
1524 data = mpd_database_search_commit(connection);
1525 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(te2->model), data);
1526 }else{
1527 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(te2->model), NULL);
1532 /* just reset the whole thing */
1533 tag2_songlist_clear_selection(NULL, tb);
1538 * Removes the selected column from the tag browser
1540 static void tag2_pref_column_remove(GtkWidget * but, GtkComboBox * box)
1542 GtkTreeIter iter;
1543 GtkTreeView *tree = g_object_get_data(G_OBJECT(but), "tree");
1544 if (gtk_combo_box_get_active_iter(box, &iter))
1546 GtkTreeModel *model = gtk_combo_box_get_model(box);
1547 GtkTreeSelection *sel = gtk_tree_view_get_selection(tree);
1548 tag_browser *tb;
1549 /* get the selected tag browser */
1550 gtk_tree_model_get(model, &iter, 2, &tb, -1);
1551 model = gtk_tree_view_get_model(tree);
1553 /* Get the selected column */
1554 if (gtk_tree_selection_get_selected(sel, &model, &iter))
1556 tag_element *te;
1557 GList *giter;
1558 int i;
1559 /* get tag element */
1560 gtk_tree_model_get(model, &iter, 2, &te, -1);
1561 /* remove from pref editor */
1562 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
1563 /* remove from browsers list */
1564 tb->tag_lists = g_list_remove(tb->tag_lists, te);
1567 * renumber, this is needed to keep the filling consistent
1568 * Beware, the numbering starts at 1. not 0. (yes shoot me for this)
1570 i = 1;
1571 for (giter = g_list_first(tb->tag_lists); giter; giter = g_list_next(giter))
1573 tag_element *te2 = giter->data;
1574 te2->index = i;
1575 i++;
1578 /* destroy from interface */
1579 tag2_destroy_tag(te);
1581 * Refill the complete browser.
1582 * TODO: Only refill the part that is needed
1585 giter = g_list_first(te->browser->tag_lists);
1586 if(giter)
1588 MpdData *data;
1589 tag_element *te2 = giter->data;
1591 /* update the content *//*
1592 mpd_database_search_field_start(connection, te2->type);
1593 data = mpd_database_search_commit(connection);
1594 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(te2->model), data);
1598 tag2_connection_changed_foreach(te->browser, NULL);
1600 /* save the browsers content */
1601 tag2_save_browser(tb);
1606 void tag2_pref_construct(GtkWidget * container)
1608 GtkWidget *sw, *tree, *but;
1609 GtkCellRenderer *renderer;
1610 GtkTreeModel *model;
1611 GtkWidget *hbox, *label, *vbox;
1612 GtkWidget *combo = NULL;
1613 conf_mult_obj *cmo, *iter;
1615 * Create the parent widget where the preferences window is packed in
1617 pref_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
1619 /* select browser to edit */
1620 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1621 model = (GtkTreeModel *) gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
1622 pref_combo = combo = gtk_combo_box_new_with_model(model);
1623 renderer = gtk_cell_renderer_text_new();
1624 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
1625 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer, "text", 1, NULL);
1627 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
1629 but = gtk_button_new_from_stock(GTK_STOCK_ADD);
1630 gtk_box_pack_start(GTK_BOX(hbox), but, FALSE, TRUE, 0);
1631 g_signal_connect(G_OBJECT(but), "clicked", G_CALLBACK(tag2_pref_add_browser_clicked), combo);
1632 but = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
1633 g_signal_connect(G_OBJECT(but), "clicked", G_CALLBACK(tag2_pref_browser_remove), combo);
1634 gtk_box_pack_start(GTK_BOX(hbox), but, FALSE, TRUE, 0);
1635 gtk_box_pack_start(GTK_BOX(pref_vbox), hbox, FALSE, TRUE, 0);
1637 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1638 label = gtk_label_new("Name:");
1639 gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
1640 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
1642 pref_entry = gtk_entry_new();
1643 g_signal_connect(GTK_ENTRY(pref_entry), "changed", G_CALLBACK(tag2_pref_entry_changed), combo);
1644 gtk_box_pack_start(GTK_BOX(hbox), pref_entry, TRUE, TRUE, 0);
1646 gtk_box_pack_start(GTK_BOX(pref_vbox), hbox, FALSE, TRUE, 0);
1648 /* change this to store TB in list store) */
1649 cmo = cfg_get_key_list(config, "tag2-browsers");
1650 for (iter = cmo; iter; iter = iter->next)
1652 GtkTreeIter titer;
1653 gchar *group = g_strdup_printf("tag2-plugin:%s", iter->key);
1654 gchar *name = cfg_get_single_value_as_string_with_default(config, group, "name", "default");
1655 GList *node = g_list_find_custom(tag2_ht, iter->key, (GCompareFunc) tag2_custom_find);
1656 if (node)
1658 tag_browser *tb = node->data;
1659 /* add to list */
1660 gtk_list_store_append(GTK_LIST_STORE(model), &titer);
1661 gtk_list_store_set(GTK_LIST_STORE(model), &titer, 0, iter->key, 1, name, 2, tb, -1);
1663 /* cleanup */
1664 g_free(group);
1666 g_free(name);
1668 if (cmo)
1669 cfg_free_multiple(cmo);
1671 /* scrolled window used to pack the treeview */
1673 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1674 sw = gtk_scrolled_window_new(NULL, NULL);
1675 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1676 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
1677 /* the treeview */
1678 tree = gtk_tree_view_new();
1679 /* the model for the treeview */
1680 model = (GtkTreeModel *) gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_POINTER);
1681 gtk_tree_view_set_model(GTK_TREE_VIEW(tree), model);
1683 renderer = gtk_cell_renderer_combo_new();
1684 g_object_set(G_OBJECT(renderer), "editable", TRUE, NULL);
1685 g_object_set(G_OBJECT(renderer), "model", tags_store, "text-column", 0, "has-entry", FALSE, NULL);
1686 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree), -1, "text", renderer, "text", 0, NULL);
1687 g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(tag2_pref_column_type_edited), model);
1689 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(tag2_pref_combo_changed), model);
1690 /* tree to scrolled window */
1691 gtk_container_add(GTK_CONTAINER(sw), tree);
1693 /* vbox */
1694 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
1696 but = gtk_button_new_from_stock(GTK_STOCK_ADD);
1697 gtk_box_pack_start(GTK_BOX(vbox), but, FALSE, TRUE, 0);
1698 g_object_set_data(G_OBJECT(but), "model", model);
1699 g_signal_connect(G_OBJECT(but), "clicked", G_CALLBACK(tag2_pref_column_add), combo);
1700 but = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
1701 g_object_set_data(G_OBJECT(but), "tree", tree);
1702 g_signal_connect(G_OBJECT(but), "clicked", G_CALLBACK(tag2_pref_column_remove), combo);
1704 gtk_box_pack_start(GTK_BOX(vbox), but, FALSE, TRUE, 0);
1706 gtk_box_pack_start(GTK_BOX(hbox), sw, TRUE, TRUE, 0);
1707 gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, TRUE, 0);
1708 gtk_box_pack_start(GTK_BOX(pref_vbox), hbox, TRUE, TRUE, 0);
1710 gtk_container_add(GTK_CONTAINER(container), pref_vbox);
1711 gtk_widget_show_all(container);
1714 static void tag2_browser_activate(GtkWidget * item, tag_browser * browser)
1716 GtkTreeSelection *selec = gtk_tree_view_get_selection((GtkTreeView *) playlist3_get_category_tree_view());
1718 GtkTreePath *path = gtk_tree_row_reference_get_path(browser->ref_iter);
1719 if (path)
1721 gtk_tree_selection_select_path(selec, path);
1722 gtk_tree_path_free(path);
1727 static void tag2_browser_add_go_menu_foreach(tag_browser * browser, GtkWidget * menu)
1729 GtkWidget *item = gtk_image_menu_item_new_with_label(browser->name);
1730 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
1731 gtk_image_new_from_icon_name("media-tag", GTK_ICON_SIZE_MENU));
1732 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1734 gtk_widget_add_accelerator(GTK_WIDGET(item), "activate", gtk_menu_get_accel_group(GTK_MENU(menu)), GDK_KEY_F1 + counter,
1735 GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
1736 counter++;
1737 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(tag2_browser_activate), browser);
1740 static int tag2_browser_add_go_menu(GtkWidget * menu)
1742 if (tag2_get_enabled() == FALSE)
1743 return 0;
1744 if (tag2_ht)
1746 counter = 0;
1747 g_list_foreach(tag2_ht, (GFunc) tag2_browser_add_go_menu_foreach, menu);
1748 return 1;
1751 return 0;
1754 static void tag2_save_myself(void)
1756 if (tag2_ht)
1758 GList *iter = g_list_first(tag2_ht);
1759 while (iter)
1761 tag_browser *tb = iter->data;
1762 GtkTreePath *path = gtk_tree_row_reference_get_path(tb->ref_iter);
1763 if (path)
1765 gint *indices = gtk_tree_path_get_indices(path);
1766 gchar *group = g_strdup_printf("tag2-plugin:%s", tb->key);
1767 g_log(LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Saving myself to position: %i\n", indices[0]);
1768 cfg_set_single_value_as_int(config, group, "position", indices[0]);
1769 gtk_tree_path_free(path);
1770 g_free(group);
1772 iter = g_list_next(iter);
1777 static int tag2_browser_right_mouse_menu(GtkWidget * menu, int type, GtkWidget * tree, GdkEventButton * event)
1779 int retv = 0;
1780 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(tree));
1781 GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1782 GtkTreeIter iter;
1783 if (type != tag2_plug.id)
1784 return 0;
1785 if (gtk_tree_selection_get_selected(sel, &model, &iter))
1788 gchar *key;
1789 gtk_tree_model_get(model, &iter, PL3_CAT_INT_ID, &key, -1);
1790 if (key)
1792 GList *node = g_list_find_custom(tag2_ht, key, (GCompareFunc) tag2_custom_find);
1793 if (node)
1795 tag_browser *tb = node->data;
1796 if (tb)
1798 GtkWidget *item = gtk_image_menu_item_new_with_label(_("Reset browser"));
1799 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
1800 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
1801 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1802 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(tag2_songlist_clear_selection), tb);
1803 retv++;
1805 g_free(key);
1810 return retv;
1813 static int tag2_browser_key_press_event(GtkWidget * mw, GdkEventKey * event, int type)
1815 if (type != tag2_plug.id)
1816 return 0;
1817 if (event->keyval == GDK_KEY_r && event->state & GDK_MOD1_MASK)
1819 GtkTreeView *tree = playlist3_get_category_tree_view();
1820 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(tree));
1821 GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1822 GtkTreeIter iter;
1823 if (type != tag2_plug.id)
1824 return 0;
1825 if (gtk_tree_selection_get_selected(sel, &model, &iter))
1828 gchar *key;
1829 gtk_tree_model_get(model, &iter, PL3_CAT_INT_ID, &key, -1);
1830 if (key)
1832 GList *node = g_list_find_custom(tag2_ht, key, (GCompareFunc) tag2_custom_find);
1833 if (node)
1835 tag_browser *tb = node->data;
1836 if (tb)
1838 tag2_songlist_clear_selection(NULL, tb);
1839 g_free(key);
1840 return 1;
1842 g_free(key);
1848 return 0;
1851 /* vim: set noexpandtab ts=4 sw=4 sts=4 tw=120*/