[0003838] Show popup when pressing dropdown button.
[gmpc.git] / src / browsers / gmpc-metadata-browser2.vala
blobdcbb6ceacade214d6c36b0839f2b885bf896c2fd
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 * This plugin consists of 3 parts
22 * Metadata2 plugin: Implements metadata 2 browser.
23 * Now Playing plugin: Reusing the metadata 2 browser it implements a now playing browser.
24 * Custom widget, there are some custom widgets used by the metadata 2 browser
25 * * Similar songs.
26 * * Similar artist.
27 * * More. (expands, collapses a sub widget
30 using Config;
31 using Gtk;
32 using Gmpc;
34 private const bool use_transition_mdb = Gmpc.use_transition;
35 private const string some_unique_name_mdb = Config.VERSION;
37 public class Gmpc.Browsers.Metadata : Gmpc.Plugin.Base, Gmpc.Plugin.BrowserIface
39 private int block_update = 0;
40 /* Stores the location in the cat_tree */
41 private Gtk.TreeRowReference rref = null;
43 private string title_color = config.get_string_with_default("Now Playing", "title-color", "#4d90dd");
44 private string item_color = config.get_string_with_default("Now Playing", "item-color", "#304ab8");
45 private bool theme_colors = (bool) config.get_int_with_default("Now Playing", "use-theme-color",1);
46 private Gdk.Color background;
47 private Gdk.Color foreground;
50 private void show_metadata_search()
52 select_browser(null);
55 construct
57 /* Set the plugin as an internal one and of type pl_browser */
58 this.plugin_type = 2|8;
60 gmpcconn.connection_changed.connect(con_changed);
61 gmpcconn.status_changed.connect(status_changed);
64 var background = config.get_string_with_default("Now Playing", "background-color", "#000");
65 var foreground = config.get_string_with_default("Now Playing", "foreground-color", "#FFF");
66 Gdk.Color.parse(background,out this.background);
67 Gdk.Color.parse(foreground,out this.foreground);
69 easy_command.add_entry(
70 _("switch metadata"),
71 "",
72 _("Switch to Metadata view"),
73 (Gmpc.Easy.Command.Callback *)show_metadata_search,
74 this);
77 private const int[] version = {0,0,0};
78 public override unowned int[] get_version()
80 return version;
83 public override unowned string get_name()
85 return N_("Metadata Browser");
88 public override void save_yourself()
90 if(this.paned != null)
92 int pos = this.paned.get_position();
93 config.set_int(this.get_name(), "pane-pos", pos);
96 if(this.model_artist != null) this.model_artist.set_mpd_data(null);
97 if(this.model_albums != null)this.model_albums.set_mpd_data(null);
99 if(this.rref != null)
101 var path = rref.get_path();
102 if(path != null)
104 unowned int[] indices = path.get_indices();
105 config.set_int(this.get_name(), "position", indices[0]);
109 /* Now playing browser */
113 * Browser part
115 /* 'base' widget */
116 private Gtk.Paned paned = null;
117 /* holding the 3 browsers */
118 private Gtk.Box browser_box = null;
119 /* The 3 browsers */
120 /* artist */
121 private Gtk.TreeView tree_artist = null;
122 private Gmpc.MpdData.Model model_artist = null;
123 private Gtk.TreeModelFilter model_filter_artist = null;
124 private Gtk.Entry artist_filter_entry = null;
126 /* album */
127 private Gtk.TreeView tree_album = null;
128 private Gmpc.MpdData.Model model_albums = null;
129 private Gtk.TreeModelFilter model_filter_album = null;
130 private Gtk.Entry album_filter_entry = null;
132 /* song */
133 private Gtk.TreeView tree_songs = null;
134 private Gmpc.MpdData.Model model_songs = null;
136 /* The right hand "browser" box */
137 private Gtk.ScrolledWindow metadata_sw = null;
138 private Gtk.EventBox metadata_box = null;
140 private Gtk.EventBox header = null;
143 * Makes gmpc jump to the metadata browser
145 private void select_metadata_browser(Gtk.Widget widget)
147 this.select_browser(null);
150 * Makes gmpc jump to the metadata browser
151 * and search
153 private void select_metadata_browser_search(Gtk.Widget widget)
155 select_browser(null);
156 set_base();
159 * Gmpc.Plugin.BrowserIface.add_go_menu
161 private int browser_add_go_menu(Gtk.Menu menu)
163 if(this.get_enabled())
165 var item = new Gtk.ImageMenuItem.with_mnemonic(_(this.get_name()));
166 item.set_image(new Gtk.Image.from_icon_name("gmpc-metabrowser", Gtk.IconSize.MENU));
167 item.activate.connect(select_metadata_browser);
168 item.add_accelerator("activate", menu.get_accel_group(),0xffc1,0, Gtk.AccelFlags.VISIBLE);
169 menu.append(item);
171 item = new Gtk.ImageMenuItem.with_mnemonic(_("Search metadata"));
172 item.set_image(new Gtk.Image.from_stock("gtk-find", Gtk.IconSize.MENU));
173 item.activate.connect(select_metadata_browser_search);
174 item.add_accelerator("activate", menu.get_accel_group(),
175 0x04d,Gdk.ModifierType.CONTROL_MASK,
176 Gtk.AccelFlags.VISIBLE);
177 menu.append(item);
179 return 2;
181 return 0;
185 * This builds the browser
187 /* This hack makes clicking a selected row again, unselect it */
188 private bool browser_button_press_event(Gtk.Widget treel, Gdk.EventButton event)
190 var tree = (treel as Gtk.TreeView);
191 Gtk.TreePath path= null;
192 if(event.button != 1) return false;
193 if(tree.get_path_at_pos((int)event.x,(int)event.y,out path, null, null, null))
195 if(tree.get_selection().path_is_selected(path))
197 tree.get_selection().unselect_path(path);
198 return true;
201 return false;
204 * Artist tree view functions */
205 private void browser_artist_entry_changed(Gtk.Editable entry)
207 string text = (entry as Gtk.Entry).get_text();
208 if(text.length > 0)
210 (entry as Gtk.Widget).show();
211 (entry as Gtk.Widget).grab_focus();
213 else
215 (entry as Gtk.Widget).hide();
216 this.tree_artist.grab_focus();
218 this.model_filter_artist.refilter();
220 /* Handle right mouse click */
221 private bool artist_browser_button_release_event(Gtk.Widget treel, Gdk.EventButton event)
223 var tree = (treel as Gtk.TreeView);
224 if(event.button == 3)
226 if(tree.get_selection().count_selected_rows()>0)
228 var menu = new Gtk.Menu();
229 var item = new Gtk.ImageMenuItem.from_stock("gtk-add",null);
230 item.activate.connect((source) =>
232 Gtk.TreeModel model;
233 Gtk.TreeIter iter;
234 if(tree.get_selection().get_selected(out model, out iter))
236 string artist;
237 model.get(iter,7, out artist);
238 if(artist != null)
240 MPD.Database.search_start(server,true);
241 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
242 MPD.Data.Item data = MPD.Database.search_commit(server);
243 data.sort_album_disc_track();
244 if(data != null)
246 data.first();
249 MPD.PlayQueue.queue_add_song(server, data.song.file);
250 data.next_free();
252 while(data != null);
253 MPD.PlayQueue.queue_commit(server);
258 menu.append(item);
260 item = new Gtk.ImageMenuItem.from_stock("gtk-media-play",null);
261 item.activate.connect((source) =>
263 Gtk.TreeModel model;
264 Gtk.TreeIter iter;
265 if(tree.get_selection().get_selected(out model, out iter))
267 string artist;
268 model.get(iter,7, out artist);
269 if(artist != null)
271 MPD.PlayQueue.clear(server);
272 MPD.PlayQueue.add_artist(server, artist);
273 MPD.Player.play(server);
277 menu.append(item);
279 menu.popup(null, null, null, event.button, event.time);
280 menu.show_all();
281 return true;
285 return false;
288 private bool visible_func_artist (Gtk.TreeModel model, Gtk.TreeIter iter)
290 string text = this.artist_filter_entry.get_text();
291 /* Visible if row is non-empty and first column is "HI" */
292 string str = null;
293 bool visible = false;
295 if(text[0] == '\0') return true;
297 model.get (iter, 7, out str, -1);
298 if (str != null && str.casefold().normalize().index_of(text.casefold().normalize()) >= 0)
299 visible = true;
301 return visible;
303 private bool browser_artist_key_press_event(Gtk.Widget widget, Gdk.EventKey event)
305 unichar uc = Gdk.keyval_to_unicode(event.keyval);
306 if(uc > 0)
308 string outbuf = " ";
309 int i = uc.to_utf8(outbuf);
310 ((char[])outbuf)[i] = '\0';
311 this.artist_filter_entry.set_text(outbuf);
312 this.artist_filter_entry.grab_focus();
313 this.artist_filter_entry.set_position(1);
315 return true;
317 return false;
320 * Album tree view
322 private void album_add_clicked(Gtk.Widget item )
324 string artist = browser_get_selected_artist();
325 if(artist != null)
327 string albumartist = null;
328 string album = browser_get_selected_album();
329 if(album != null && Gmpc.server.tag_supported(MPD.Tag.Type.ALBUM_ARTIST))
331 MPD.Database.search_field_start(server, MPD.Tag.Type.ALBUM_ARTIST);
332 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, album);
333 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
334 var ydata = MPD.Database.search_commit(server);
335 if(ydata != null)
337 if(ydata.tag.length > 0)
338 albumartist = ydata.tag;
341 /* Fill in the first browser */
342 MPD.Database.search_start(server,true);
343 if(albumartist != null && albumartist.length > 0)
344 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM_ARTIST, albumartist);
345 else
346 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
348 if(album != null)
349 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, album);
350 var data = MPD.Database.search_commit(server);
352 data.sort_album_disc_track();
353 if(data != null)
357 MPD.PlayQueue.queue_add_song(server, data.song.file);
358 data.next_free();
360 while(data != null);
361 MPD.PlayQueue.queue_commit(server);
365 private void album_replace_clicked(Gtk.Widget item)
367 MPD.PlayQueue.clear(server);
368 album_add_clicked(item);
369 MPD.Player.play(server);
371 /* Handle right mouse click */
372 private bool album_browser_button_release_event(Gtk.Widget tree, Gdk.EventButton event)
374 if(event.button == 3)
376 if((tree as Gtk.TreeView).get_selection().count_selected_rows()>0)
378 var menu = new Gtk.Menu();
379 var item = new Gtk.ImageMenuItem.from_stock("gtk-add",null);
380 item.activate.connect(album_add_clicked);
381 menu.append(item);
383 item = new Gtk.ImageMenuItem.from_stock("gtk-media-play",null);
384 item.activate.connect(album_replace_clicked);
385 menu.append(item);
387 menu.popup(null, null, null, event.button, event.time);
388 menu.show_all();
389 return true;
393 return false;
395 private bool visible_func_album (Gtk.TreeModel model, Gtk.TreeIter iter)
397 string text = this.album_filter_entry.get_text();
398 /* Visible if row is non-empty and first column is "HI" */
399 string str = null;
400 bool visible = false;
402 if(text[0] == '\0') return true;
404 model.get (iter, 6, out str, -1);
405 if (str != null && str.casefold().normalize().index_of(text.casefold().normalize()) >= 0)
406 visible = true;
408 return visible;
412 private bool browser_album_key_press_event(Gtk.Widget widget, Gdk.EventKey event)
414 unichar uc = Gdk.keyval_to_unicode(event.keyval);
415 if(uc > 0)
417 string outbuf = " ";
418 int i = uc.to_utf8(outbuf);
419 ((char[])outbuf)[i] = '\0';
420 this.album_filter_entry.set_text(outbuf);
421 this.album_filter_entry.grab_focus();
422 this.album_filter_entry.set_position(1);
424 return true;
426 return false;
429 private void browser_album_entry_changed(Gtk.Editable pentry)
431 Gtk.Entry entry = (pentry as Gtk.Entry);
432 string text = entry.get_text();
433 if(text.length > 0)
435 entry.show();
436 entry.grab_focus();
438 else
440 entry.hide();
441 this.tree_album.grab_focus();
443 this.model_filter_album.refilter();
446 * Songs
448 private void song_add_clicked(Gtk.Widget item )
450 unowned MPD.Song? song = browser_get_selected_song();
451 if(song != null)
453 MPD.PlayQueue.add_song(server,song.file);
456 private void song_replace_clicked(Gtk.Widget item)
458 MPD.PlayQueue.clear(server);
459 song_add_clicked(item);
460 MPD.Player.play(server);
462 /* Handle right mouse click */
463 private bool song_browser_button_release_event(Gtk.Widget ptree, Gdk.EventButton event)
465 Gtk.TreeView tree = (ptree as Gtk.TreeView);
466 if(event.button == 3)
468 if(tree.get_selection().count_selected_rows()>0)
470 var menu = new Gtk.Menu();
471 var item = new Gtk.ImageMenuItem.from_stock("gtk-add",null);
472 item.activate.connect(song_add_clicked);
473 menu.append(item);
475 item = new Gtk.ImageMenuItem.from_stock("gtk-media-play",null);
476 item.activate.connect(song_replace_clicked);
477 menu.append(item);
479 menu.popup(null, null, null, event.button, event.time);
480 menu.show_all();
481 return true;
484 return false;
487 private bool browser_button_release_event(Gtk.Widget widget, Gdk.EventButton event)
489 if(event.button == 8)
491 history_previous();
492 return true;
494 else if (event.button == 9)
496 history_next();
497 return true;
499 return false;
501 public void set_orientation()
503 if(paned != null) {
504 if(config.get_int_with_default("tag2-plugin", "orientation", 0) == 1)
506 (paned as Gtk.Orientable).set_orientation(Gtk.Orientation.VERTICAL);
507 (browser_box as Gtk.Orientable).set_orientation(Gtk.Orientation.HORIZONTAL);
508 }else{
509 (paned as Gtk.Orientable).set_orientation(Gtk.Orientation.HORIZONTAL);
510 (browser_box as Gtk.Orientable).set_orientation(Gtk.Orientation.VERTICAL);
514 private void browser_init()
516 if(this.paned == null)
518 this.paned = new Gtk.HPaned();
519 paned_size_group.add_paned(this.paned);
520 /* Bow with browsers */
521 this.browser_box = new Gtk.VBox(true, 6);
522 this.paned.add1(this.browser_box);
525 /* Artist list */
526 var box = new Gtk.VBox(false, 6);
527 this.browser_box.pack_start(box, true, true, 0);
529 this.artist_filter_entry = new Gtk.Entry();
531 this.artist_filter_entry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, "gtk-clear");
532 this.artist_filter_entry.icon_press.connect((source, pos, event)=>
534 if(pos == Gtk.EntryIconPosition.SECONDARY) source.set_text("");
537 this.artist_filter_entry.set_no_show_all(true);
538 this.artist_filter_entry.changed.connect(browser_artist_entry_changed);
540 box.pack_start(this.artist_filter_entry, false, false, 0);
542 var sw = new Gtk.ScrolledWindow(null, null);
543 sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
544 sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN);
545 box.pack_start(sw, true, true, 0);
547 this.model_artist = new Gmpc.MpdData.Model();
548 this.model_filter_artist = new Gtk.TreeModelFilter(this.model_artist, null);
549 this.model_filter_artist.set_visible_func(visible_func_artist);
550 this.tree_artist = new Gtk.TreeView.with_model(this.model_filter_artist);
551 this.tree_artist.rules_hint = true;
552 new Gmpc.MpdData.Treeview.Tooltip(this.tree_artist, Gmpc.MetaData.Type.ARTIST_ART);
554 this.tree_artist.set_enable_search(false);
555 this.tree_artist.button_press_event.connect(browser_button_press_event);
556 this.tree_artist.button_release_event.connect(artist_browser_button_release_event);
557 this.tree_artist.key_press_event.connect(browser_artist_key_press_event);
558 sw.add(tree_artist);
559 /* setup the columns */
560 var column = new Gtk.TreeViewColumn();
561 if(config.get_int_with_default("tag2-plugin", "show-image-column", 1) == 1)
563 var prenderer = new Gtk.CellRendererPixbuf();
564 prenderer.set("height", this.model_artist.icon_size);
565 column.pack_start(prenderer, false);
566 column.add_attribute(prenderer, "pixbuf",27);
568 var trenderer = new Gtk.CellRendererText();
569 column.pack_start(trenderer, true);
570 column.add_attribute(trenderer, "text", 7);
571 this.tree_artist.append_column(column);
572 column.set_title(_("Artist"));
573 this.tree_artist.get_selection().changed.connect(browser_artist_changed);
575 /* set fixed height mode */
576 column.sizing = Gtk.TreeViewColumnSizing.FIXED;
577 this.tree_artist.set_fixed_height_mode(true);
580 /* Album list */
582 box = new Gtk.VBox(false, 6);
583 this.browser_box.pack_start(box, true, true, 0);
586 this.album_filter_entry = new Gtk.Entry();
588 this.album_filter_entry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, "gtk-clear");
589 this.album_filter_entry.icon_press.connect((source, pos, event)=>
591 if(pos == Gtk.EntryIconPosition.SECONDARY) source.set_text("");
593 this.album_filter_entry.set_no_show_all(true);
594 this.album_filter_entry.changed.connect(browser_album_entry_changed);
595 box.pack_start(this.album_filter_entry, false, false, 0);
597 sw = new Gtk.ScrolledWindow(null, null);
598 sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
599 sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN);
600 box.pack_start(sw, true, true, 0);
601 this.model_albums = new Gmpc.MpdData.Model();
602 this.model_filter_album = new Gtk.TreeModelFilter(this.model_albums, null);
603 this.model_filter_album.set_visible_func(visible_func_album);
604 this.tree_album = new Gtk.TreeView.with_model(this.model_filter_album);
605 this.tree_album.rules_hint = true;
606 this.tree_album.set_enable_search(false);
607 new Gmpc.MpdData.Treeview.Tooltip(this.tree_album, Gmpc.MetaData.Type.ALBUM_ART);
609 this.tree_album.button_press_event.connect(browser_button_press_event);
610 this.tree_album.button_release_event.connect(album_browser_button_release_event);
611 this.tree_album.key_press_event.connect(browser_album_key_press_event);
612 sw.add(tree_album);
613 /* setup the columns */
614 column = new Gtk.TreeViewColumn();
615 if(config.get_int_with_default("tag2-plugin", "show-image-column", 1) == 1)
617 var prenderer = new Gtk.CellRendererPixbuf();
618 prenderer.set("height", this.model_albums.icon_size);
619 column.pack_start(prenderer, false);
620 column.add_attribute(prenderer, "pixbuf",27);
622 this.tree_album.append_column(column);
624 column = new Gtk.TreeViewColumn();
625 trenderer = new Gtk.CellRendererText();
626 column.pack_start(trenderer, true);
627 column.add_attribute(trenderer, "text", 14);
628 this.tree_album.append_column(column);
629 column.set_title(_("Year"));
631 column = new Gtk.TreeViewColumn();
632 trenderer = new Gtk.CellRendererText();
633 column.pack_start(trenderer, true);
634 column.add_attribute(trenderer, "text", 6);
635 this.tree_album.append_column(column);
636 column.set_title(_("Album"));
639 this.tree_album.get_selection().changed.connect(browser_album_changed);
641 /* Song list */
642 sw = new Gtk.ScrolledWindow(null, null);
643 sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
644 sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN);
645 this.browser_box.pack_start(sw, true, true, 0);
646 this.model_songs = new Gmpc.MpdData.Model();
647 this.tree_songs = new Gtk.TreeView.with_model(this.model_songs);
648 this.tree_songs.rules_hint = true;
649 this.tree_songs.button_press_event.connect(browser_button_press_event);
650 this.tree_songs.button_release_event.connect(song_browser_button_release_event);
651 sw.add(tree_songs);
652 /* setup the columns */
653 column = new Gtk.TreeViewColumn();
655 if(config.get_int_with_default("tag2-plugin", "show-image-column", 1) == 1)
657 var prenderer = new Gtk.CellRendererPixbuf();
658 column.pack_start(prenderer, false);
659 column.add_attribute(prenderer, "icon-name",23);
661 trenderer = new Gtk.CellRendererText();
662 column.pack_start(trenderer, false);
663 column.add_attribute(trenderer, "text", 10);
665 column.set_title(_("Track"));
666 this.tree_songs.append_column(column);
668 column = new Gtk.TreeViewColumn();
669 trenderer = new Gtk.CellRendererText();
670 column.pack_start(trenderer, true);
671 column.add_attribute(trenderer, "text", 7);
674 this.tree_songs.append_column(column);
675 this.tree_songs.set_search_column(7);
676 column.set_title(_("Songs"));
678 this.tree_songs.get_selection().changed.connect(browser_songs_changed);
680 /* The right view */
681 this.metadata_sw = new Gtk.ScrolledWindow(null, null);
682 this.metadata_sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
683 this.metadata_sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN);
685 this.metadata_box = new Gtk.EventBox();
686 this.metadata_box.set_visible_window(true);
687 this.metadata_sw.add_with_viewport(this.metadata_box);
688 (this.metadata_box as Gtk.Container).set_focus_hadjustment(
689 this.metadata_sw.get_hadjustment());
690 (this.metadata_box as Gtk.Container).set_focus_vadjustment(
691 this.metadata_sw.get_vadjustment());
694 /* header */
695 var vb = new Gtk.VBox(false, 0);
696 vb.pack_end(this.metadata_sw, true,true,0);
698 this.metadata_box.get_style_context().add_class(Gtk.STYLE_CLASS_VIEW);
700 this.header = new Gtk.EventBox();
702 this.header.get_style_context().add_class(Gtk.STYLE_CLASS_HIGHLIGHT);
705 var sp = new Gtk.Separator(Gtk.Orientation.HORIZONTAL);
706 sp.set_size_request(-1, 1);
707 vb.pack_start(sp, false, false, 0);
708 vb.pack_start(header, false, false,0);
709 header.show();
711 this.paned.add2(vb);
715 this.paned.button_release_event.connect(browser_button_release_event);
717 this.reload_browsers();
719 this.paned.show_all();
721 if(config.get_int_with_default("metadata browser", "show-browsers", 1) == 0)
723 this.browser_box.hide();
725 this.set_orientation();
728 private void reload_browsers()
730 if(this.paned == null) return;
732 this.model_songs.set_mpd_data(null);
733 this.model_albums.set_mpd_data(null);
734 this.model_artist.set_mpd_data(null);
736 this.artist_filter_entry.set_text("");
737 this.album_filter_entry.set_text("");
739 /* Fill in the first browser */
740 MPD.Database.search_field_start(server,MPD.Tag.Type.ARTIST);
741 var data = MPD.Database.search_commit(server);
742 data.sort_album_disc_track();
743 this.model_artist.set_mpd_data((owned)data);
746 private string? browser_get_selected_artist()
748 Gtk.TreeIter iter;
749 var sel = this.tree_artist.get_selection();
750 Gtk.TreeModel model = null;//this.model_artist;
751 if(sel.get_selected(out model, out iter))
753 string artist = null;
754 model.get(iter, 7,out artist, -1);
755 return artist;
757 return null;
760 private string? browser_get_selected_album()
762 Gtk.TreeIter iter;
763 var sel = this.tree_album.get_selection();
764 Gtk.TreeModel model = null;//this.model_albums;
765 if(sel.get_selected(out model, out iter))
767 string album = null;
768 model.get(iter, 6,out album, -1);
769 return album;
771 return null;
773 private unowned MPD.Song? browser_get_selected_song()
775 Gtk.TreeIter iter;
776 var sel = this.tree_songs.get_selection();
777 Gtk.TreeModel model;
778 if(sel.get_selected(out model, out iter))
780 unowned MPD.Song? songs = null;
781 this.model_songs .get(iter, 0,out songs, -1);
782 return songs;
784 return null;
786 private void browser_artist_changed(Gtk.TreeSelection sel)
788 this.model_albums.set_mpd_data(null);
789 this.model_songs.set_mpd_data(null);
790 this.metadata_box_clear();
792 string artist = browser_get_selected_artist();
793 if(artist != null)
795 /* Fill in the first browser */
796 MPD.Database.search_field_start(server,MPD.Tag.Type.ALBUM);
797 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
798 var data = MPD.Database.search_commit(server);
799 data.sort_album_disc_track();
801 this.model_albums.set_request_artist(artist);
802 MPD.Data.Item list = null;
803 unowned MPD.Data.Item iter = data.get_first();
804 if(iter!= null)
808 list.append_new();
809 list.type = MPD.Data.Type.SONG;
810 list.song = new MPD.Song();
811 list.song.artist = artist;
812 list.song.album = iter.tag;
813 MPD.Database.search_field_start(server,MPD.Tag.Type.DATE);
814 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
815 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, iter.tag);
816 var ydata = MPD.Database.search_commit(server);
817 if(ydata != null)
819 unowned MPD.Data.Item yi = ydata.get_first();
820 while(list.song.date == null && yi != null)
822 if(yi.tag != null && yi.tag.length > 0)
824 list.song.date = yi.tag;
826 yi.next(false);
829 iter.next(false);
831 while(iter!= null);
834 list.sort_album_disc_track();
835 this.model_albums.set_mpd_data((owned)list);
837 MPD.Database.search_start(server,true);
838 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
839 data = MPD.Database.search_commit(server);
840 data.sort_album_disc_track();
841 this.model_songs.set_mpd_data((owned)data);
844 this.metadata_box_update();
846 private void browser_album_changed(Gtk.TreeSelection album_sel)
848 this.model_songs.set_mpd_data(null);
849 this.metadata_box_clear();
851 string artist = browser_get_selected_artist();
852 if(artist != null)
854 string album = browser_get_selected_album();
855 string albumartist = null;
857 if(album != null && Gmpc.server.tag_supported(MPD.Tag.Type.ALBUM_ARTIST))
859 MPD.Database.search_field_start(server, MPD.Tag.Type.ALBUM_ARTIST);
860 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, album);
861 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
862 var ydata = MPD.Database.search_commit(server);
863 if(ydata != null)
865 if(ydata.tag.length > 0)
866 albumartist = ydata.tag;
869 /* Fill in the first browser */
870 MPD.Database.search_start(server,true);
871 if(albumartist != null&& albumartist.length > 0)
872 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM_ARTIST, albumartist);
873 else
874 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
876 if(album != null)
877 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, album);
878 var data = MPD.Database.search_commit(server);
879 data.sort_album_disc_track();
880 this.model_songs.set_mpd_data((owned)data);
882 this.metadata_box_update();
884 private void browser_songs_changed (Gtk.TreeSelection song_sel)
886 this.metadata_box_clear();
887 this.metadata_box_update();
890 * Metadata box
893 private void add_selected_song(Gtk.Button button)
895 string artist = browser_get_selected_artist();
896 string album = browser_get_selected_album();
897 unowned MPD.Song? song = browser_get_selected_song();
898 if(song != null)
900 MPD.PlayQueue.add_song(server,song.file);
901 return;
903 if(artist != null )
905 /* Hunt albumartist */
906 string albumartist = null;
907 if(album != null&& Gmpc.server.tag_supported(MPD.Tag.Type.ALBUM_ARTIST))
909 MPD.Database.search_field_start(server, MPD.Tag.Type.ALBUM_ARTIST);
910 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, album);
911 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
912 var ydata = MPD.Database.search_commit(server);
913 if(ydata != null)
915 if(ydata.tag.length > 0)
916 albumartist = ydata.tag;
920 MPD.Database.search_start(server,true);//server,MPD.Tag.Type.FILENAME);
921 if(albumartist != null&& albumartist.length > 0)
922 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM_ARTIST, albumartist);
923 else
924 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
925 if(album != null)
926 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, album);
927 var data = MPD.Database.search_commit(server);
928 if(data != null)
930 data.sort_album_disc_track();
931 while(data != null)
933 MPD.PlayQueue.queue_add_song(server, data.song.file);
934 data.next_free();
936 MPD.PlayQueue.queue_commit(server);
941 private void play_selected_song(Gtk.Button button)
943 unowned MPD.Song? song = browser_get_selected_song();
944 if(song != null)
946 Gmpc.MpdInteraction.play_path(song.file);
949 private void replace_selected_song(Gtk.Button button)
951 MPD.PlayQueue.clear(server);
952 this.add_selected_song(button);
953 MPD.Player.play(server);
955 private void metadata_box_clear()
957 var list = this.metadata_box.get_children();
958 foreach(Gtk.Widget child in list)
960 child.destroy();
964 * Add a row to a gtk table
965 * <b>$label:</b> $value
966 * then increments i
969 private void add_entry(Gtk.Grid table, string entry_label, string? value,Gtk.Widget? extra, ref int i, string ? image = null)
971 int j=0;
972 if(value == null && extra == null) return;
973 var box = new Gtk.Grid();
974 var label = new Gtk.Label("");
975 box.column_spacing = 6;
976 label.set_alignment(0.0f, 0.0f);
977 label.set_markup(Markup.printf_escaped("<b>%s:</b>",entry_label));
978 if(image != null)
980 /* Make sure the icon always lines out to the top */
981 var ali = new Gtk.Alignment (0f, 0f, 0f, 0f);
982 var wimage = new Gtk.Image.from_icon_name(image, Gtk.IconSize.MENU);
983 ali.add(wimage);
984 box.attach(ali, 0,0,1,1);
986 box.attach(label, 1,0,1,1);
987 // table.attach(box, j,(j+1),i,i+1,Gtk.AttachOptions.SHRINK|Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK|Gtk.AttachOptions.FILL,0,0);
988 table.attach(box,j,i, 1, 1);
989 j++;
991 if(value != null)
993 var pt_label = new Gtk.Label(value);
994 pt_label.set_selectable(true);
995 pt_label.set_alignment(0.0f, 0.0f);
996 pt_label.set_line_wrap(true);
997 // table.attach(pt_label, j,(j+1),i,i+1,Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK|Gtk.AttachOptions.FILL,0,0);
998 table.attach(pt_label, j, i, 1, 1);
999 j++;
1001 if(extra != null)
1003 //table.attach(extra, j,(j+1),i,i+1,Gtk.AttachOptions.SHRINK|Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK|Gtk.AttachOptions.FILL,0,0);
1004 table.attach(extra,j, i, 1, 1);
1005 j++;
1007 i++;
1010 public Gtk.Widget metadata_box_show_song(MPD.Song tmp_song, bool show_controls)
1012 var master_box = new Gtk.VBox (false, 6);
1013 var vbox = new Gtk.VBox (false,6);
1014 var song = tmp_song.copy();
1015 vbox.border_width = 8;
1017 history_bc_header(HitemType.SONG, song.artist, song.album, song.title);
1020 /* Start building the gui */
1021 /* Artist image */
1022 var hbox = new Gtk.HBox(false, 6);
1023 /* Album image */
1024 var ali = new Gtk.Alignment(0f,0f,0f,0f);
1025 int meta_size = (int)(this.metadata_box.get_allocated_width()*0.20);
1026 meta_size = int.min(meta_size, (int)(this.metadata_sw.get_allocated_height()*0.30));
1027 meta_size = int.min(int.max(100, meta_size), 250);
1028 var album_image = new Gmpc.MetaData.Image(Gmpc.MetaData.Type.ALBUM_ART, meta_size);
1029 album_image.set_scale_up(true);
1030 album_image.set_squared(false);
1031 ali.add(album_image);
1032 album_image.update_from_song(song);
1033 hbox.pack_start(ali, false, false, 0);
1035 /* Artist image */
1036 ali = new Gtk.Alignment(1f,0f,0f,0f);
1037 var artist_image = new Gmpc.MetaData.Image(Gmpc.MetaData.Type.ARTIST_ART, meta_size);
1039 artist_image.set_no_cover_icon("no-artist");
1040 artist_image.set_loading_cover_icon("fetching-artist");
1041 artist_image.set_scale_up(true);
1042 artist_image.set_hide_on_na(true);
1043 artist_image.set_squared(false);
1044 artist_image.update_from_song(song);
1045 ali.add(artist_image);
1046 hbox.pack_end(ali, false, false, 0);
1049 /* Information box */
1050 var info_box = new Gtk.Grid();
1051 info_box.set_row_spacing(3);
1052 info_box.set_column_spacing(8);
1053 int i=0;
1054 /* Title */
1055 if(song.title != null)
1058 var box = new Gtk.HBox(false, 6);
1060 if(config.get_int_with_default("Interface", "hide-favorites-icon",0) == 0)
1062 var fav_button = new Gmpc.Favorites.Button();
1063 fav_button.set_song(song);
1064 ali = new Gtk.Alignment(0.0f, 0.5f,0f,0f);
1065 ali.add(fav_button);
1066 box.pack_start(ali, false, false, 0);
1068 var label = new Gtk.Label(song.title);
1069 label.selectable = true;
1070 label.set_markup(GLib.Markup.printf_escaped("<span color='%s' size='%i' weight='bold'>%s</span>",
1071 this.title_color,Pango.SCALE*20 ,song.title));
1072 label.set_ellipsize(Pango.EllipsizeMode.END);
1073 label.set_alignment(0.0f, 0.5f);
1074 box.pack_start(label, true, true, 0);
1075 //hist_box.pack_start(box, true, true, 0);
1076 //vbox.pack_start(box, true, true, 0);
1077 //info_box.attach(box, 0,2, i, i+1,Gtk.AttachOptions.FILL,Gtk.AttachOptions.FILL,0,0);
1078 info_box.attach(box, i,0, 2,1);
1079 i++;
1081 else if (song.name!= null)
1083 var label = new Gtk.Label(song.name);
1084 label.selectable = true;
1085 label.set_markup(GLib.Markup.printf_escaped("<span color='%s' size='%i' weight='bold'>%s</span>",this.
1086 title_color, Pango.SCALE*20, song.name));
1087 label.set_ellipsize(Pango.EllipsizeMode.END);
1088 label.set_alignment(0.0f, 0.5f);
1089 //info_box.attach(label, 0,2, i, i+1,Gtk.AttachOptions.FILL,Gtk.AttachOptions.FILL,0,0);
1090 info_box.attach(label,i, 0, 2, 1);
1091 i++;
1093 else if (song.file != null)
1095 var filename = GLib.Path.get_basename(song.file);
1096 var label = new Gtk.Label(song.name);
1097 label.selectable = true;
1100 var regex_a = new GLib.Regex ("\\.[0-9a-zA-Z]*$");
1101 filename = regex_a.replace_literal (filename, -1, 0, "");
1103 catch (GLib.RegexError e)
1105 GLib.error("%s", e.message);
1109 var regex_b = new GLib.Regex ("_");
1110 filename = regex_b.replace_literal (filename, -1, 0, " ");
1112 catch (GLib.RegexError e)
1114 GLib.error("%s", e.message);
1116 label.set_markup(GLib.Markup.printf_escaped("<span color='%s' size='%i' weight='bold'>%s</span>",this.
1117 title_color, Pango.SCALE*20, filename));
1118 label.set_ellipsize(Pango.EllipsizeMode.END);
1119 label.set_alignment(0.0f, 0.5f);
1120 //info_box.attach(label, 0,2, i, i+1,Gtk.AttachOptions.FILL,Gtk.AttachOptions.FILL,0,0);
1121 info_box.attach(label, 0,2, i, i+1);
1122 i++;
1124 /* Artist */
1125 if(song.artist != null)
1127 this.add_entry(info_box, _("Artist"), song.artist, null,ref i, "media-artist");
1129 /* Album */
1130 if(song.album != null)
1132 this.add_entry(info_box, _("Album"), song.album,null, ref i, "media-album");
1134 /* Genre */
1135 if(song.genre != null)
1137 this.add_entry(info_box, _("Genre"), song.genre,null, ref i, "media-genre");
1139 /* Genre */
1140 if(song.date != null)
1142 this.add_entry(info_box, _("Date"), song.date,null, ref i, "media-date");
1144 if(song.file != null)
1147 string extension = null;
1148 extension = get_extension(song.file);
1149 if(extension != null)
1151 this.add_entry(info_box, _("Codec"), extension,null, ref i, "media-codec");
1154 string directory = GLib.Path.get_dirname(song.file);
1155 if(directory != null)
1157 this.add_entry(info_box, _("Directory"), directory, null, ref i, "gtk-open");
1161 /* Time*/
1162 if(song.time > 0)
1164 this.add_entry(info_box, _("Length"),Gmpc.Misc.format_time((ulong) song.time, ""),null, ref i, "media-track-length");
1167 if(song.track != null)
1169 var label = new Gtk.Label("");
1170 label.selectable = true;
1171 label.set_ellipsize(Pango.EllipsizeMode.END);
1172 label.set_markup(GLib.Markup.printf_escaped("%s %s",
1173 song.track,
1174 (song.disc != null)? "[%s]".printf(song.disc):""
1176 label.set_alignment(0.0f, 0.5f);
1177 this.add_entry(info_box, _("Track"),null,label, ref i, "media-num-tracks");
1181 /* Player controls */
1182 var control_hbox = new Gtk.VBox (false, 6);
1184 var abutton = new Gtk.Button.from_stock("gtk-add");
1185 abutton.set_tooltip_text(_("Add the item to the play queue"));
1186 abutton.set_property("xalign", 0.0f);
1187 abutton.set_relief(Gtk.ReliefStyle.NONE);
1188 abutton.clicked.connect(add_selected_song);
1189 control_hbox.pack_start(abutton, false, false,0);
1191 abutton = new Gtk.Button.with_mnemonic(_("_Clear and play"));
1192 abutton.set_tooltip_text(_("Replace the current play queue with this item"));
1193 abutton.set_property("xalign", 0.0f);
1194 abutton.set_image(new Gtk.Image.from_stock("gtk-redo", Gtk.IconSize.BUTTON));
1195 abutton.set_relief(Gtk.ReliefStyle.NONE);
1196 abutton.clicked.connect(replace_selected_song);
1197 control_hbox.pack_start(abutton, false, false,0);
1199 abutton = new Gtk.Button.from_stock("gtk-media-play");
1200 abutton.set_tooltip_text(_("Play this song"));
1201 abutton.set_property("xalign", 0.0f);
1202 abutton.set_relief(Gtk.ReliefStyle.NONE);
1203 abutton.clicked.connect(play_selected_song);
1204 control_hbox.pack_start(abutton, false, false,0);
1206 // info_box.attach(control_hbox, 0,2,i,i+1,Gtk.AttachOptions.SHRINK|Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK|Gtk.AttachOptions.FILL,0,0);
1207 i++;
1210 hbox.pack_start(info_box, true, true, 0);
1211 hbox.pack_start(control_hbox, false, false, 0);
1212 vbox.pack_start(hbox, false, false, 0);
1215 var hboxje = new Gtk.HBox(false, 6);
1217 /* Notebook where all the metadata items are kept, Override the tabs by a radiobutton list. */
1218 var notebook = new Gtk.Notebook();
1219 notebook.set_show_border(false);
1220 notebook.set_show_tabs(false);
1222 /* Lyrics */
1223 i = 0;
1224 unowned SList<unowned Gtk.RadioButton> group = null;
1225 if(config.get_int_with_default("MetaData", "show-lyrics",1) == 1)
1227 var alib = new Gtk.Alignment(0f,0f,1f,0f);
1228 var text_view = new Gmpc.MetaData.TextView(Gmpc.MetaData.Type.SONG_TXT);
1229 text_view.set_left_margin(8);
1230 text_view.query_from_song(song);
1232 alib.add(text_view);
1233 notebook.append_page(alib, new Gtk.Label("Lyrics"));
1234 var button = new Gtk.RadioButton.with_label(group,("Lyrics"));
1235 group = button.get_group();
1236 hboxje.pack_start(button, false, false, 0);
1237 var j = i;
1238 button.clicked.connect((source) =>
1240 debug("notebook page %i clicked", j);
1241 notebook.set_current_page(j);
1243 i++;
1245 alib.show();
1248 /* Guitar Tabs */
1249 if(config.get_int_with_default("MetaData", "show-guitar-tabs",1) == 1)
1251 var alib = new Gtk.Alignment(0f,0f,1f,0f);
1252 var text_view = new Gmpc.MetaData.TextView(Gmpc.MetaData.Type.SONG_GUITAR_TAB);
1253 text_view.use_monospace = true;
1254 text_view.set_left_margin(8);
1255 var text_view_queried = false;
1257 alib.add(text_view);
1258 notebook.append_page(alib, new Gtk.Label(_("Guitar Tabs")));
1259 var button = new Gtk.RadioButton.with_label(group,_("Guitar Tabs"));
1260 group = button.get_group();
1261 hboxje.pack_start(button, false, false, 0);
1262 var j = i;
1263 /* Only query the guitar-tab when opened or first notebook page*/
1264 button.clicked.connect((source) =>
1266 debug("notebook page %i clicked", j);
1267 notebook.set_current_page(j);
1268 if(!text_view_queried)
1270 text_view.query_from_song(song);
1271 text_view_queried = true;
1274 if(i == 0)
1276 text_view.query_from_song(song);
1277 text_view_queried = true;
1279 alib.show();
1280 i++;
1282 /* Similar songs */
1284 if(config.get_int_with_default("MetaData", "show-similar-songs",1) == 1)
1286 var similar_songs_queried = false;
1287 var similar_songs_box = new Gtk.Alignment(0f,0f,0f,0f);
1289 notebook.append_page(similar_songs_box, new Gtk.Label(_("Similar Songs")));
1291 var button = new Gtk.RadioButton.with_label(group,_("Similar Songs"));
1292 group = button.get_group();
1293 hboxje.pack_start(button, false, false, 0);
1295 var j = i;
1296 /* Only query when opened or first notebook page*/
1297 button.clicked.connect((source) =>
1299 debug("notebook page %i clicked", j);
1300 notebook.set_current_page(j);
1301 if(!similar_songs_queried)
1303 var similar_songs = new Gmpc.MetaData.Widgets.SimilarSongs(song);
1304 similar_songs.update();
1305 similar_songs_queried = true;
1306 similar_songs_box.add(similar_songs);
1307 similar_songs_box.show_all();
1310 if(i == 0)
1312 var similar_songs = new Gmpc.MetaData.Widgets.SimilarSongs(song);
1313 similar_songs.update();
1314 similar_songs_queried = true;
1315 similar_songs_box.add(similar_songs);
1316 similar_songs_box.show_all();
1318 similar_songs_box.show();
1319 i++;
1322 if(config.get_int_with_default("MetaData", "show-similar-artist",1) == 1 && song.artist != null)
1324 var similar_artist = new Gmpc.MetaData.Widgets.SimilarArtists(Gmpc.server,song);
1326 notebook.append_page(similar_artist, new Gtk.Label(_("Similar Artists")));
1328 var button = new Gtk.RadioButton.with_label(group,_("Similar Artists"));
1329 group = button.get_group();
1330 hboxje.pack_start(button, false, false, 0);
1332 var j = i;
1333 button.clicked.connect((source) =>
1335 debug("notebook page %i clicked", j);
1336 notebook.set_current_page(j);
1337 similar_artist.first_show();
1339 similar_artist.show();
1340 i++;
1342 /* Track changed pages */
1343 notebook.notify["page"].connect((source,spec) =>
1345 var page = notebook.get_current_page();
1346 config.set_int("MetaData", "song-last-page", (int)page);
1349 /* Restore right page */
1350 if(i > 0)
1352 var page = config.get_int_with_default("MetaData", "song-last-page", 0);
1353 if(page > i)
1355 notebook.set_current_page(0);
1357 else
1359 /* The list is in reversed order, compensate for that. */
1360 var w = group.nth_data(i-page-1);
1361 w.set_active(true);
1362 notebook.set_current_page(page);
1366 ali = new Gtk.Alignment(0.0f, 0.5f,0f,0f);
1367 ali.add(hboxje);
1369 /* Create pane in 2. */
1370 var bottom_hbox = new Gtk.HBox(false, 6);
1371 /* left pane */
1373 var metadata_vbox = new Gtk.VBox(false, 6);
1375 var sep = new Gtk.HSeparator();
1376 sep.set_size_request(-1, 1);
1377 metadata_vbox.pack_start(sep, false, false, 0);
1379 metadata_vbox.pack_start(ali, false, false, 0);
1380 sep = new Gtk.HSeparator();
1381 sep.set_size_request(-1, 1);
1382 metadata_vbox.pack_start(sep, false, false, 0);
1383 metadata_vbox.pack_start(notebook, false, false, 0);
1385 bottom_hbox.pack_start(metadata_vbox, true, true, 0);
1387 vbox.pack_start(bottom_hbox, true, true, 0);
1389 /* show it */
1390 master_box.pack_start(vbox, false, false, 0);
1391 return master_box;
1394 private void metadata_box_show_base()
1396 Gtk.Entry MetadataBoxShowBaseEntry = null;
1397 var master_box = new Gtk.VBox (false, 6);
1398 var vbox = new Gtk.VBox (false,6);
1399 vbox.border_width = 8;
1402 history_bc_header(HitemType.BASE, null, null, null);
1404 var hbox = new Gtk.HBox(false, 6);
1405 var label = new Gtk.Label("");
1406 label.set_markup(_("<b>Search:</b>"));
1407 hbox.pack_start(label, false, false, 0);
1408 MetadataBoxShowBaseEntry = new Gtk.Entry();
1410 MetadataBoxShowBaseEntry.add_accelerator("grab-focus",
1411 Gmpc.Playlist.get_accel_group(),
1412 0x046,
1413 Gdk.ModifierType.CONTROL_MASK,
1416 /* Add clear icon */
1417 MetadataBoxShowBaseEntry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, "gtk-clear");
1418 MetadataBoxShowBaseEntry.icon_press.connect((source, pos, event) =>
1420 source.set_text("");
1424 hbox.pack_start(MetadataBoxShowBaseEntry, true, true, 0);
1426 vbox.pack_start(hbox, false, false, 0);
1429 var result_hbox = new Gmpc.Widgets.Songlist();
1430 result_hbox.artist_song_clicked.connect((source, song) =>
1432 if(song.artist != null)
1434 this.set_artist(song.artist);
1437 result_hbox.album_song_clicked.connect((source, song) =>
1439 if(song.artist != null && song.album != null)
1441 this.set_album(song.artist, song.album);
1444 result_hbox.song_clicked.connect((source, song) =>
1446 this.set_song(song);
1449 result_hbox.play_song_clicked.connect((source, song) =>
1451 if(song.file != null)
1453 Gmpc.MpdInteraction.play_path(song.file);
1456 result_hbox.song_context_menu.connect((source, song) =>
1458 var menu = new Gtk.Menu();
1460 var item = new Gtk.ImageMenuItem.from_stock("gtk-open",null);
1461 MPD.Song song_pass = song.copy();
1462 item.activate.connect((source)=>{
1463 this.set_song(song_pass);
1465 menu.append(item);
1467 stdout.printf("song context menu show\n");
1468 menu.show_all();
1469 menu.popup(null, null, null, 3, Gtk.get_current_event_time());
1472 MetadataBoxShowBaseEntry.activate.connect((source) =>
1474 string value = source.get_text();
1475 current.data.search_string = value;
1476 MPD.Data.Item? list = Gmpc.Query.search(value, false);
1477 result_hbox.set_from_data((owned)list,true,true);
1478 result_hbox.show_all();
1480 result_hbox.show();
1481 vbox.pack_start(result_hbox, false, false);
1483 if(current != null)
1485 if(current.data.type == HitemType.BASE && current.data.search_string != null)
1487 MetadataBoxShowBaseEntry.set_text(current.data.search_string);
1488 MetadataBoxShowBaseEntry.activate();
1493 * Button that allows you to enable/disable the side bars
1495 var show_button = new Gtk.ToggleButton.with_label(_("Hide sidebar"));
1497 if(config.get_int_with_default("metadata browser", "show-browsers", 1) == 0)
1499 show_button.label = _("Show sidebar");
1500 show_button.set_active(true);
1502 show_button.toggled.connect((source) =>
1504 var state = show_button.active;
1505 if(state == true)
1507 this.browser_box.hide();
1508 show_button.label = _("Show sidebar");
1510 else {
1511 this.browser_box.show();
1512 show_button.label = _("Hide sidebar");
1514 config.set_int("metadata browser", "show-browsers", (int)!state);
1518 var ali = new Gtk.Alignment(0.0f, 0.5f, 0f,0f);
1519 ali.add(show_button);
1520 vbox.pack_start(ali, false, false);
1522 * Add it to the view
1524 master_box.pack_start(vbox, false, false, 0);
1525 this.metadata_box.add(master_box);
1526 this.metadata_sw.show_all();
1527 // MetadataBoxShowBaseEntry.grab_focus();
1532 private void metadata_box_show_album(string artist, string album)
1534 var master_box = new Gtk.VBox (false, 6);
1535 var vbox = new Gtk.VBox (false,6);
1536 vbox.border_width = 8;
1539 history_bc_header(HitemType.ALBUM, artist, album, null);
1541 /* Artist image */
1542 var hbox = new Gtk.HBox(false, 6);
1543 var ali = new Gtk.Alignment(0f,0f,0f,0f);
1545 int meta_size = (int)(this.metadata_box.get_allocated_width()*0.20);
1546 meta_size = int.min(meta_size, (int)(this.metadata_sw.get_allocated_height()*0.30));
1547 meta_size = int.min(int.max(100, meta_size), 250);
1548 var artist_image = new Gmpc.MetaData.Image(Gmpc.MetaData.Type.ALBUM_ART, meta_size);
1549 artist_image.set_squared(false);
1550 MPD.Song song = new MPD.Song();
1551 song.artist = artist;
1552 song.album = album;
1553 artist_image.update_from_song(song);
1554 ali.add(artist_image);
1555 hbox.pack_start(ali, false, false, 0);
1556 /* Artist information */
1557 var info_box = new Gtk.Grid();
1558 info_box.set_row_spacing(3);
1559 info_box.set_column_spacing(8);
1560 hbox.pack_start(info_box, true, true, 0);
1561 int i=0;
1564 var label = new Gtk.Label(artist != null?artist:_("Unknown"));
1565 label.set_alignment(0.0f, 0.5f);
1566 label.set_line_wrap(true);
1567 this.add_entry(info_box, _("Artist"), null, label, ref i, "media-artist");
1569 label = new Gtk.Label(album != null?album:_("Unknown"));
1570 label.set_alignment(0.0f, 0.5f);
1571 label.set_line_wrap(true);
1572 this.add_entry(info_box, _("Album"), null, label, ref i, "media-album");
1574 /* Genres of songs */
1575 var pt_label = new Gmpc.MetaData.StatsLabel(Gmpc.MetaData.StatsLabel.Type.ALBUM_GENRES_SONGS, song);
1576 pt_label.set_selectable(true);
1577 pt_label.set_alignment(0.0f, 0.5f);
1578 pt_label.set_line_wrap(true);
1579 this.add_entry(info_box, _("Genres"), null, pt_label, ref i, "media-genre");
1581 /* Dates of songs */
1582 pt_label = new Gmpc.MetaData.StatsLabel(Gmpc.MetaData.StatsLabel.Type.ALBUM_DATES_SONGS, song);
1583 pt_label.set_selectable(true);
1584 pt_label.set_line_wrap(true);
1585 pt_label.set_alignment(0.0f, 0.5f);
1586 this.add_entry(info_box, _("Dates"), null, pt_label, ref i,"media-date");
1587 /* Total number of songs */
1588 pt_label = new Gmpc.MetaData.StatsLabel(Gmpc.MetaData.StatsLabel.Type.ALBUM_NUM_SONGS, song);
1589 pt_label.set_selectable(true);
1590 pt_label.set_line_wrap(true);
1591 pt_label.set_alignment(0.0f, 0.5f);
1592 this.add_entry(info_box, _("Songs"), null, pt_label, ref i, "media-num-tracks");
1593 /* Total playtime */
1594 pt_label = new Gmpc.MetaData.StatsLabel(Gmpc.MetaData.StatsLabel.Type.ALBUM_PLAYTIME_SONGS, song);
1595 pt_label.set_selectable(true);
1596 pt_label.set_line_wrap(true);
1597 pt_label.set_alignment(0.0f, 0.5f);
1598 this.add_entry(info_box, _("Playtime"), null, pt_label, ref i, "media-track-length");
1600 vbox.pack_start(hbox , false, false, 0);
1602 /* Player controls */
1603 var control_hbox = new Gtk.VBox (false, 6);
1605 var button = new Gtk.Button.from_stock("gtk-add");
1606 button.set_tooltip_text(_("Add the item to the play queue"));
1607 button.set_property("xalign", 0.0f);
1608 button.set_relief(Gtk.ReliefStyle.NONE);
1609 button.clicked.connect(add_selected_song);
1610 control_hbox.pack_start(button, false, false,0);
1612 button = new Gtk.Button.from_stock("gtk-media-play");
1613 button.set_tooltip_text(_("Replace the current play queue with this item and play"));
1614 button.set_property("xalign", 0.0f);
1615 button.set_relief(Gtk.ReliefStyle.NONE);
1616 button.clicked.connect(replace_selected_song);
1617 control_hbox.pack_start(button, false, false,0);
1619 hbox.pack_start(control_hbox, false, false, 0);
1621 /* Separator */
1622 var sep = new Gtk.HSeparator();
1623 sep.set_size_request(-1, 4);
1624 vbox.pack_start(sep, false, false, 0);
1626 var hboxje = new Gtk.HBox(false, 6);
1628 /* Notebook where all the metadata items are kept, Override the tabs by a radiobutton list. */
1629 var notebook = new Gtk.Notebook();
1630 notebook.set_show_border(false);
1631 notebook.set_show_tabs(false);
1633 /* Lyrics */
1634 i = 0;
1635 unowned SList<unowned Gtk.RadioButton> group = null;
1636 /* Album information */
1637 if(config.get_int_with_default("MetaData", "show-album-information",1) == 1)
1639 var alib = new Gtk.Alignment(0f,0f,1f,0f);
1640 var text_view = new Gmpc.MetaData.TextView(Gmpc.MetaData.Type.ALBUM_TXT);
1641 text_view.set_left_margin(8);
1642 text_view.query_from_song(song);
1644 alib.add(text_view);
1645 notebook.append_page(alib, new Gtk.Label(_("Album information")));
1646 var rbutton = new Gtk.RadioButton.with_label(group,_("Album information"));
1647 group = rbutton.get_group();
1648 hboxje.pack_start(rbutton, false, false, 0);
1649 var j = i;
1650 rbutton.clicked.connect((source) =>
1652 debug("notebook page %i clicked", j);
1653 notebook.set_current_page(j);
1655 i++;
1657 alib.show();
1660 var slhbox = new Gtk.VBox(false, 6);
1662 var sl = new Gmpc.Widgets.Songlist();
1663 slhbox.pack_start(sl, false, false, 0);
1665 sl.album_song_clicked.connect((source, song) =>
1667 if(song.artist != null && song.album != null)
1669 this.set_album(song.artist, song.album);
1672 sl.song_clicked.connect((source, song) =>
1674 this.set_song(song);
1677 sl.play_song_clicked.connect((source, song) =>
1679 if(song.file != null)
1681 Gmpc.MpdInteraction.play_path(song.file);
1685 sl.song_context_menu.connect((source,song)=>
1687 var menu = new Gtk.Menu();
1689 var item = new Gtk.ImageMenuItem.from_stock("gtk-open",null);
1690 MPD.Song song_pass = song.copy();
1691 item.activate.connect((source)=>{
1692 this.set_song(song_pass);
1694 menu.append(item);
1696 stdout.printf("song context menu show\n");
1697 menu.show_all();
1698 menu.popup(null, null, null, 3, Gtk.get_current_event_time());
1701 notebook.append_page(slhbox, new Gtk.Label(_("Song list")));
1702 var rbutton = new Gtk.RadioButton.with_label(group,_("Song list"));
1703 group = rbutton.get_group();
1704 hboxje.pack_start(rbutton, false, false, 0);
1705 var j = i;
1706 bool seen = false;
1707 rbutton.clicked.connect((source) =>
1709 notebook.set_current_page(j);
1711 notebook.notify["page"].connect((source)=>
1713 if(notebook.page != j) return;
1714 debug("notebook page %i clicked", j);
1715 if(!seen)
1717 string albumartist = null;
1718 if(Gmpc.server.tag_supported(MPD.Tag.Type.ALBUM_ARTIST))
1720 MPD.Database.search_field_start(server, MPD.Tag.Type.ALBUM_ARTIST);
1721 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, album);
1722 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
1723 var ydata = MPD.Database.search_commit(server);
1724 if(ydata != null)
1726 if(ydata.tag.length > 0)
1727 albumartist = ydata.tag;
1730 MPD.Database.search_start(server,true);
1731 if(albumartist != null&& albumartist.length > 0)
1732 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM_ARTIST, albumartist);
1733 else
1734 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
1736 if(album != null)
1737 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, album);
1738 var data = MPD.Database.search_commit(server);
1739 data.sort_album_disc_track();
1741 sl.set_from_data((owned)data,false);
1743 seen = true;
1746 slhbox.show();
1747 i++;
1749 /* Track changed pages */
1750 notebook.notify["page"].connect((source,spec) =>
1752 var page = notebook.get_current_page();
1753 config.set_int("MetaData", "album-last-page", (int)page);
1756 /* Restore right page */
1757 if(i > 0)
1759 var page = config.get_int_with_default("MetaData", "album-last-page", 0);
1760 if(page > i)
1762 notebook.set_current_page(0);
1764 else
1766 /* The list is in reversed order, compensate for that. */
1767 var w = group.nth_data(i-page-1);
1768 w.set_active(true);
1769 notebook.set_current_page(page);
1773 ali = new Gtk.Alignment(0.0f, 0.5f,0f,0f);
1774 ali.add(hboxje);
1776 /* Create pane in 2. */
1777 var bottom_hbox = new Gtk.HBox(false, 6);
1778 /* left pane */
1779 var metadata_vbox = new Gtk.VBox(false, 6);
1780 metadata_vbox.pack_start(ali, false, false, 0);
1781 sep = new Gtk.HSeparator();
1782 sep.set_size_request(-1, 1);
1783 metadata_vbox.pack_start(sep, false, false, 0);
1784 metadata_vbox.pack_start(notebook, false, false, 0);
1785 bottom_hbox.pack_start(metadata_vbox, true, true, 0);
1788 vbox.pack_start(bottom_hbox, true, true, 0);
1790 * Add it to the view
1792 master_box.pack_start(vbox, false, false, 0);
1793 this.metadata_box.add(master_box);
1794 this.metadata_sw.show_all();
1797 * This fills the view for artist
1798 * <artist name>
1799 * <image> | <array with info>
1800 * < buttonss>
1802 * <artist info text>
1804 * <similar artists>
1805 * <links>
1807 private void metadata_box_show_artist(string artist)
1809 var master_box = new Gtk.VBox (false, 6);
1810 var vbox = new Gtk.VBox (false,6);
1811 var i = 0;
1812 vbox.border_width = 8;
1814 /* Set up header */
1815 history_bc_header(HitemType.ARTIST, artist, null, null);
1817 /* Create an MPD.Song with the info for this type set */
1818 MPD.Song song = new MPD.Song();
1819 song.artist = artist;
1821 /* Artist image */
1822 var hbox = new Gtk.HBox(false, 6);
1823 var ali = new Gtk.Alignment(0f,0f,0f,0f);
1824 int meta_size = (int)(this.metadata_box.get_allocated_width()*0.20);
1825 meta_size = int.min(meta_size, (int)(this.metadata_sw.get_allocated_height()*0.30));
1826 meta_size = int.min(int.max(100, meta_size), 250);
1827 var artist_image = new Gmpc.MetaData.Image(Gmpc.MetaData.Type.ARTIST_ART, meta_size);
1829 artist_image.set_no_cover_icon("no-artist");
1830 artist_image.set_loading_cover_icon("fetching-artist");
1831 artist_image.set_squared(false);
1832 artist_image.update_from_song(song);
1833 ali.add(artist_image);
1834 hbox.pack_start(ali, false, false, 0);
1835 /* Artist information */
1836 var info_box = new Gtk.Grid();
1837 info_box.set_row_spacing(3);
1838 info_box.set_column_spacing(8);
1839 hbox.pack_start(info_box, true, true, 0);
1841 /* Artist */
1842 var label = new Gtk.Label((artist != null)?artist:_("Unknown"));
1843 label.set_selectable(true);
1844 label.set_alignment(0.0f, 0.5f);
1845 this.add_entry(info_box, _("Artist"), null, label, ref i, "media-artist");
1847 /* Genres of songs */
1848 var pt_label = new Gmpc.MetaData.StatsLabel(Gmpc.MetaData.StatsLabel.Type.ARTIST_GENRES_SONGS, song);
1849 pt_label.set_selectable(true);
1850 pt_label.set_alignment(0.0f, 0.5f);
1851 pt_label.set_line_wrap(true);
1852 this.add_entry(info_box, _("Genres"), null, pt_label, ref i, "media-genre");
1853 /* Dates of songs */
1854 pt_label = new Gmpc.MetaData.StatsLabel(Gmpc.MetaData.StatsLabel.Type.ARTIST_DATES_SONGS, song);
1855 pt_label.set_selectable(true);
1856 pt_label.set_line_wrap(true);
1857 pt_label.set_alignment(0.0f, 0.5f);
1858 this.add_entry(info_box, _("Dates"), null, pt_label, ref i,"media-date");
1859 /* Total number of songs */
1860 pt_label = new Gmpc.MetaData.StatsLabel(Gmpc.MetaData.StatsLabel.Type.ARTIST_NUM_SONGS, song);
1861 pt_label.set_selectable(true);
1862 pt_label.set_line_wrap(true);
1863 pt_label.set_alignment(0.0f, 0.5f);
1864 this.add_entry(info_box, _("Songs"), null, pt_label, ref i, "media-num-tracks");
1865 /* Total playtime */
1866 pt_label = new Gmpc.MetaData.StatsLabel(Gmpc.MetaData.StatsLabel.Type.ARTIST_PLAYTIME_SONGS, song);
1867 pt_label.set_selectable(true);
1868 pt_label.set_line_wrap(true);
1869 pt_label.set_alignment(0.0f, 0.5f);
1870 this.add_entry(info_box, _("Playtime"), null, pt_label, ref i, "media-track-length");
1872 vbox.pack_start(hbox , true, true, 0);
1874 /* Player controls */
1875 var hbox_2 = new Gtk.VBox (false, 6);
1877 var button = new Gtk.Button.from_stock("gtk-add");
1878 button.set_tooltip_text(_("Add the item to the play queue"));
1879 button.set_property("xalign", 0.0f);
1880 button.set_relief(Gtk.ReliefStyle.NONE);
1881 button.clicked.connect(add_selected_song);
1882 hbox_2.pack_start(button, false, false,0);
1884 button = new Gtk.Button.from_stock("gtk-media-play");
1885 button.set_tooltip_text(_("Replace the current play queue with this item and play"));
1886 button.set_property("xalign", 0.0f);
1887 button.set_relief(Gtk.ReliefStyle.NONE);
1888 button.clicked.connect(replace_selected_song);
1889 hbox_2.pack_start(button, false, false,0);
1891 hbox.pack_start(hbox_2, false, false, 0);
1894 var hboxje = new Gtk.HBox(false, 6);
1895 /* Separator */
1896 var sep = new Gtk.HSeparator();
1897 sep.set_size_request(-1, 4);
1898 vbox.pack_start(sep, false, false, 0);
1901 /* Notebook where all the metadata items are kept, Override the tabs by a radiobutton list. */
1902 var notebook = new Gtk.Notebook();
1903 notebook.set_show_border(false);
1904 notebook.set_show_tabs(false);
1906 i = 0;
1907 unowned SList<unowned Gtk.RadioButton> group = null;
1908 /* Artist information */
1909 if(config.get_int_with_default("MetaData", "show-artist-information",1) == 1)
1911 var alib = new Gtk.Alignment(0f,0f,1f,0f);
1912 var text_view = new Gmpc.MetaData.TextView(Gmpc.MetaData.Type.ARTIST_TXT);
1913 text_view.set_left_margin(8);
1914 text_view.query_from_song(song);
1916 alib.add(text_view);
1917 notebook.append_page(alib, new Gtk.Label(_("Artist information")));
1918 var button_sai = new Gtk.RadioButton.with_label(group,_("Artist information"));
1919 group = button_sai.get_group();
1920 hboxje.pack_start(button_sai, false, false, 0);
1922 var j = i;
1923 button_sai.clicked.connect((source) =>
1925 if((source as Gtk.CheckButton).get_active())
1927 debug("notebook page %i clicked", j);
1928 notebook.set_current_page(j);
1931 i++;
1933 alib.show();
1936 /* Show similar artist */
1937 if(config.get_int_with_default("MetaData", "show-similar-artist",1) == 1)
1939 var similar_artist = new Gmpc.MetaData.Widgets.SimilarArtists(Gmpc.server,song);
1941 notebook.append_page(similar_artist, new Gtk.Label(_("Similar Artists")));
1943 var button_sa = new Gtk.RadioButton.with_label(group,_("Similar Artists"));
1944 group = button_sa.get_group();
1945 hboxje.pack_start(button_sa, false, false, 0);
1947 var j = i;
1948 button_sa.clicked.connect((source) =>
1950 if((source as Gtk.CheckButton).get_active())
1952 debug("notebook page %i clicked", j);
1953 similar_artist.first_show();
1954 notebook.set_current_page(j);
1957 similar_artist.show();
1958 i++;
1962 var slhbox = new Gtk.VBox(false, 6);
1964 var sl = new Gmpc.Widgets.Songlist();
1965 slhbox.pack_start(sl, false, false, 0);
1967 sl.album_song_clicked.connect((source, song) =>
1969 if(song.artist != null && song.album != null)
1971 this.set_album(song.artist, song.album);
1974 sl.song_clicked.connect((source, song) =>
1976 this.set_song(song);
1979 sl.play_song_clicked.connect((source, song) =>
1981 if(song.file != null)
1983 Gmpc.MpdInteraction.play_path(song.file);
1986 sl.song_context_menu.connect((source,song)=>
1988 var menu = new Gtk.Menu();
1990 var item = new Gtk.ImageMenuItem.from_stock("gtk-open",null);
1991 MPD.Song song_pass = song.copy();
1992 item.activate.connect((source)=>{
1993 this.set_song(song_pass);
1995 menu.append(item);
1997 stdout.printf("song context menu show\n");
1998 menu.show_all();
1999 menu.popup(null, null, null, 3, Gtk.get_current_event_time());
2003 notebook.append_page(slhbox, new Gtk.Label(_("Song list")));
2004 var rbutton = new Gtk.RadioButton.with_label(group,_("Song list"));
2005 group = rbutton.get_group();
2006 hboxje.pack_start(rbutton, false, false, 0);
2007 var j = i;
2008 bool seen = false;
2009 rbutton.clicked.connect((source) =>
2011 notebook.set_current_page(j);
2013 notebook.notify["page"].connect((source)=>
2015 if(notebook.page != j) return;
2016 debug("notebook page %i clicked", j);
2017 if(!seen)
2019 MPD.Database.search_start(server,true);
2020 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
2021 var data = MPD.Database.search_commit(server);
2022 data.sort_album_disc_track();
2024 sl.set_from_data((owned)data, true);
2026 seen = true;
2029 slhbox.show();
2030 i++;
2033 if( song.artist != null)
2035 int albums =0;
2036 /* Create album list */
2037 var album_hbox = new Gtk.VBox(false, 6);
2039 var album_view = new Gmpc.Widgets.Qtable();
2040 album_hbox.pack_start(album_view, false, true, 0);
2042 * Reuse the album browser view.
2044 Gtk.TreeIter titer;
2045 if(this.model_albums.get_iter_first(out titer))
2049 unowned MPD.Song? asong = null;
2050 this.model_albums.get(titer,0, &asong);
2051 if(asong != null)
2053 /* Make a copy of the song. Otherwise only a reference is added to the
2054 * button clicked handler.
2055 * This reference can be invalid before click completed.
2057 MPD.Song but_song = asong.copy();
2059 /* Create button */
2060 var but_hbox = new Gtk.VBox(false, 6);
2061 var image = new Gmpc.MetaData.Image(Gmpc.MetaData.Type.ALBUM_ART, 175);
2062 image.set_squared(true);
2063 image.set_scale_up(true);
2064 image.set_has_tooltip(false);
2065 image.update_from_song_delayed(but_song);
2067 but_hbox.pack_start(image, false, false, 0);
2069 var but_label = new Gtk.Label(but_song.album);
2070 but_label.selectable = true;
2071 but_label.set_alignment(0.5f, 0.0f);
2072 /* Create label */
2073 var strlabel = "<b>";
2074 if(but_song.date != null && but_song.date.length > 0) strlabel += GLib.Markup.printf_escaped("%s - ",but_song.date);
2075 if(but_song.album != null) strlabel+=GLib.Markup.escape_text(but_song.album);
2076 else strlabel += _("No Album");
2077 strlabel += "</b>";
2078 if(but_song.artist != null) strlabel += GLib.Markup.printf_escaped("\n%s",but_song.artist);
2079 but_label.set_markup(strlabel);
2080 (but_label as Gtk.Misc).set_alignment(0.0f, 0.0f);
2081 but_label.set_ellipsize(Pango.EllipsizeMode.END);
2082 /* add label */
2083 but_hbox.pack_start(but_label, true, true, 0);
2085 /* If clicked switch to browser */
2086 image.button_release_event.connect((source, event) =>
2088 if(event.button == 2)
2090 set_album(but_song.artist, but_song.album);
2091 return true;
2093 else if (event.button == 1)
2095 var p = new PopupMenu();
2097 hbox = new Gtk.HBox(false, 0);
2098 var open = new Gtk.Button();
2099 open.set_tooltip_text(_("Open"));
2100 open.add(new Image.from_stock("gtk-open", Gtk.IconSize.DND));
2101 hbox.pack_start(open, false, false, 0);
2102 open.clicked.connect( (source) =>
2104 set_album(but_song.artist, but_song.album);
2105 p.destroy();
2109 * Play machine
2111 var play = new Button();
2112 play.add(new Image.from_stock("gtk-media-play", Gtk.IconSize.DND));
2113 play.set_tooltip_text(_("Play album"));
2114 hbox.pack_start(play, false, false, 0);
2115 play.clicked.connect( (source) =>
2117 MPD.PlayQueue.clear(server);
2118 MPD.PlayQueue.add_album(server,
2119 but_song.artist,
2120 but_song.album);
2121 MPD.Player.play(server);
2122 p.destroy();
2126 * Add to play queue
2128 var add = new Button();
2129 add.add(new Image.from_stock("gtk-add", Gtk.IconSize.DND));
2130 add.set_tooltip_text(_("Add album to play-queue"));
2131 hbox.pack_start(add, false, false, 0);
2132 add.clicked.connect( (source) =>
2134 MPD.PlayQueue.add_album(server,
2135 but_song.artist,
2136 but_song.album);
2137 p.destroy();
2140 p.add(hbox);
2141 hbox.show_all();
2142 p.popup(event);
2143 return true;
2145 return false;
2147 albums++;
2150 album_view.add(but_hbox);
2153 while(this.model_albums.iter_next(ref titer));
2156 notebook.append_page(album_hbox, new Gtk.Label(_("Albums")));
2157 var button_sl = new Gtk.RadioButton.with_label(group,_("Albums"));
2158 group = button_sl.get_group();
2159 hboxje.pack_start(button_sl, false, false, 0);
2160 var j = i;
2161 button_sl.clicked.connect((source) =>
2163 if((source as Gtk.CheckButton).get_active())
2165 debug("notebook page %i clicked", j);
2166 notebook.set_current_page(j);
2169 album_hbox.show();
2170 i++;
2173 /* Track changed pages */
2174 notebook.notify["page"].connect((source,spec) =>
2176 var page = notebook.get_current_page();
2177 config.set_int("MetaData", "artist-last-page", (int)page);
2180 /* Restore right page */
2181 if(i > 0)
2183 var page = config.get_int_with_default("MetaData", "artist-last-page", 0);
2184 if(page > i)
2186 notebook.set_current_page(0);
2188 else
2190 /* The list is in reversed order, compensate for that. */
2191 var w = group.nth_data(i-page-1);
2192 w.set_active(true);
2193 notebook.set_current_page(page);
2198 ali = new Gtk.Alignment(0.0f, 0.5f,0f,0f);
2199 ali.add(hboxje);
2201 /* left pane */
2202 var metadata_vbox = new Gtk.VBox(false, 6);
2203 metadata_vbox.pack_start(ali, false, false, 0);
2204 sep = new Gtk.HSeparator();
2205 sep.set_size_request(-1, 1);
2206 metadata_vbox.pack_start(sep, false, false, 0);
2207 metadata_vbox.pack_start(notebook, false, false, 0);
2210 vbox.pack_start(metadata_vbox, true, true, 0);
2214 * Add it to the view
2217 master_box.pack_start(vbox, false, false, 0);
2218 this.metadata_box.add(master_box);
2219 this.metadata_box.show_all();
2223 private uint update_timeout = 0;
2224 /* If we get an update, wait for idle time, then do it.*/
2225 private void metadata_box_update()
2227 if(this.update_timeout > 0)
2229 GLib.Source.remove(this.update_timeout);
2231 update_timeout = GLib.Idle.add(this.metadata_box_update_real);
2233 private bool metadata_box_update_real()
2235 if(this.block_update > 0)
2237 this.update_timeout = 0;
2238 return false;
2240 string artist = browser_get_selected_artist();
2241 string album = browser_get_selected_album();
2242 unowned MPD.Song? song = browser_get_selected_song();
2244 if(song != null)
2246 /** Add item to history */
2247 var item = new Hitem();
2248 item.song = song.copy();
2249 item.type = HitemType.SONG;
2250 history_add(item);
2252 var view = metadata_box_show_song(song,true);
2253 this.metadata_box.add(view);
2254 this.metadata_box.show_all();
2256 else if(album != null && artist != null)
2258 /** Add item to history */
2259 var item = new Hitem();
2260 item.song = new MPD.Song();
2261 item.song.artist =artist;
2262 item.song.album = album;
2263 item.type = HitemType.ALBUM;
2264 history_add(item);
2266 metadata_box_show_album(artist,album);
2268 else if (artist != null)
2270 /** Add item to history */
2271 var item = new Hitem();
2272 item.song = new MPD.Song();
2273 item.song.artist =artist;
2274 item.type = HitemType.ARTIST;
2275 history_add(item);
2277 metadata_box_show_artist(artist);
2279 else
2281 var item = new Hitem();
2282 item.search_string = null;
2283 item.type = HitemType.BASE;
2284 history_add(item);
2285 metadata_box_show_base();
2288 this.update_timeout = 0;
2289 return false;
2292 * Browser Interface bindings
2294 public void browser_add (Gtk.Widget category_tree)
2296 Gtk.TreeView tree = (Gtk.TreeView)category_tree;
2297 Gtk.ListStore store = (Gtk.ListStore)tree.get_model();
2298 Gtk.TreeModel model = tree.get_model();
2299 Gtk.TreeIter iter;
2300 Gmpc.Browser.insert(out iter,
2301 Playlist.BrowserType.LIBRARY+config.get_int_with_default(this.get_name(), "position", 100)%Playlist.BrowserType.LIBRARY);
2302 store.set(iter, 0, this.id, 1, _(this.get_name()), 3, "gmpc-metabrowser");
2303 /* Create a row reference */
2304 this.rref = new Gtk.TreeRowReference(model, model.get_path(iter));
2306 public void browser_selected (Gtk.Container container)
2308 string artist;
2309 this.selected = true;
2310 this.browser_init();
2311 container.add(this.paned);
2313 /* update if non selected */
2314 artist = browser_get_selected_artist();
2315 if(artist == null)
2317 metadata_box_clear();
2318 metadata_box_update();
2322 private bool selected = false;
2323 public void browser_unselected(Gtk.Container container)
2325 this.selected = false;
2326 container.remove(this.paned);
2328 private
2329 void
2330 con_changed(Gmpc.Connection conn, MPD.Server server, int connect)
2332 if(this.paned == null) return;
2333 this.history_clear();
2335 this.reload_browsers();
2336 metadata_box_clear();
2337 metadata_box_update();
2339 private
2340 void
2341 status_changed(Gmpc.Connection conn, MPD.Server server, MPD.Status.Changed what)
2343 if(this.paned == null) return;
2344 if((what&MPD.Status.Changed.DATABASE) != 0)
2346 this.reload_browsers();
2347 /* Restore old selection */
2348 if(this.current != null)
2350 this.show_hitem(this.current.data);
2356 * History
2358 private enum HitemType
2360 BASE,
2361 ARTIST,
2362 ALBUM,
2363 SONG
2365 private class Hitem
2367 public HitemType type;
2368 public MPD.Song? song;
2369 public string search_string ;
2371 private List<Hitem?> history = null;
2372 private unowned List<Hitem?> current = null;
2373 private void show_hitem(Hitem hi)
2375 switch(hi.type)
2377 case HitemType.BASE:
2378 this.set_base();
2379 break;
2380 case HitemType.ARTIST:
2381 this.set_artist(hi.song.artist);
2382 break;
2383 case HitemType.ALBUM:
2384 this.set_album(hi.song.artist, hi.song.album);
2385 break;
2386 case HitemType.SONG:
2387 this.set_song(hi.song);
2388 break;
2389 default:
2390 metadata_box_clear();
2391 break;
2394 private void history_previous()
2396 if(history == null || current == null)
2398 return;
2400 if(current.next == null)
2402 return;
2404 current = current.next;
2405 if(current != null) show_hitem(current.data);
2406 else metadata_box_clear();
2409 private void history_next()
2411 if(history == null || current == null)
2413 return;
2415 if(current.prev == null)
2417 return;
2419 current = current.prev;
2420 if(current != null) show_hitem(current.data);
2421 else metadata_box_clear();
2424 private void history_show_list_clicked(Gtk.MenuItem item)
2426 unowned List<Hitem?> a = (List<Hitem?>) item.get_data<List<Hitem?>>("current");
2427 if(a != null)
2429 current = a;
2430 show_hitem(current.data);
2434 // Keep the 'menu' around so the pop-up still works.
2435 private Gtk.Menu menu = null;
2438 private void history_show_list()
2440 menu = new Gtk.Menu();
2441 unowned List<Hitem?> iter = history.last();
2442 while(iter!= null)
2444 unowned Hitem i = iter.data;
2445 string label = "";
2446 if(i.type == HitemType.ARTIST)
2447 label = i.song.artist;
2448 else if (i.type == HitemType.ALBUM)
2449 label = "%s - %s".printf(i.song.artist, i.song.album);
2450 else if (i.type == HitemType.SONG)
2452 if(i.song.title != null)
2453 label = i.song.title;
2454 else
2455 label = _("Unknown");
2457 else if (i.type == HitemType.BASE)
2459 label = _("Metadata Browser");
2460 if(i.search_string != null)
2462 label += " ("+i.search_string+")";
2466 var item = new Gtk.CheckMenuItem.with_label(label);
2467 item.draw_as_radio = true;
2468 if(current != null && current == iter)
2470 item.set_active(true);
2472 item.activate.connect(history_show_list_clicked);
2473 item.set_data("current", (void*)iter);
2474 menu.append(item);
2475 iter = iter.prev;
2477 menu.show_all();
2478 menu.popup(null, null, null, 0, Gtk.get_current_event_time());
2481 private void history_bc_header(HitemType type, string? artist, string? album, string? title)
2483 foreach(var child in this.header.get_children())
2485 child.destroy();
2487 /* Add the history buttons.
2488 * we use the history buttons hbox for adding our buttons */
2489 var hist_box = history_buttons();
2490 this.header.add(hist_box);
2492 /* The 'BASE' button */
2493 if(artist == null && album == null && title==null)
2495 var image = new Gtk.Image.from_icon_name("gmpc-metabrowser", Gtk.IconSize.MENU);
2496 var ali = new Gtk.Alignment(0.5f,0.5f, 0f, 0f);
2497 ali.set_padding(6,6,6,6);
2498 ali.add(image);
2499 hist_box.attach(ali, 0, 0, 1, 1);//pack_start(ali, true, true, 0);
2500 //hist_box.attach(ali, 1, 0, 1,1);
2502 else
2504 var image = new Gtk.Image.from_icon_name("gmpc-metabrowser", Gtk.IconSize.MENU);
2505 var button = new Gtk.Button();
2506 button.set_relief(Gtk.ReliefStyle.NONE);
2507 button.set_image(image);
2508 button.clicked.connect((source)=> { set_base();});
2509 hist_box.attach(button, 0, 0, 1, 1);//pack_start(button, true, true, 0);
2511 /* If there is an artist, we can show more */
2512 if(artist != null)
2514 if(type != HitemType.ARTIST)
2516 var button = new Gtk.Button.with_label(artist);
2517 button.set_relief(Gtk.ReliefStyle.NONE);
2518 button.set_image(new Gtk.Image.from_icon_name("media-artist", Gtk.IconSize.MENU));
2519 button.clicked.connect((source)=>
2521 set_artist(artist);
2523 hist_box.attach(button, 1, 0, 1,1);//pack_start(button, true, true, 0);
2526 else
2528 var label = new Gtk.Label(artist);
2529 label.set_ellipsize(Pango.EllipsizeMode.END);
2530 var image = new Gtk.Image.from_icon_name("media-artist", Gtk.IconSize.MENU);
2531 label.set_alignment(0.0f, 0.5f);
2532 hist_box.attach(image, 2, 0, 1,1);
2533 hist_box.attach(label, 3, 0, 1,1);
2535 /* Album */
2536 if(album != null)
2538 if(type == HitemType.ALBUM)
2540 var label = new Gtk.Label(album);
2541 label.set_ellipsize(Pango.EllipsizeMode.END);
2542 var image = new Gtk.Image.from_icon_name("media-album", Gtk.IconSize.MENU);
2543 label.set_alignment(0.0f, 0.5f);
2544 hist_box.attach(image, 4, 0, 1,1);
2545 hist_box.attach(label, 5, 0, 1,1);
2547 else
2549 var button = new Gtk.Button();//.with_label(album);
2551 var ali = new Gtk.Alignment(0.0f, 0.5f, 0.0f, 0.0f);
2552 button.add(ali);
2554 var gr = new Gtk.Grid();
2555 gr.set_column_spacing(6);
2556 ali.add(gr);
2558 var im = new Gtk.Image.from_icon_name("media-album", Gtk.IconSize.MENU);
2559 // button.set_image(im);
2560 gr.attach(im,0,0,1,1);
2561 var label = new Gtk.Label(album);
2562 label.set_ellipsize(Pango.EllipsizeMode.END);
2563 gr.attach(label, 1,0,1,1);
2565 button.set_relief(Gtk.ReliefStyle.NONE);
2566 button.clicked.connect((source)=>
2568 set_album(artist, album);
2570 button.show_all();
2571 hist_box.attach(button, 4,0,1,1);//pack_start(button, true, true, 0);
2575 if(title != null)
2577 var image = new Gtk.Image.from_icon_name("media-audiofile", Gtk.IconSize.MENU);
2578 hist_box.attach(image, 6, 0, 1,1);
2580 var label = new Gtk.Label(title);
2581 label.set_alignment(0.0f, 0.5f);
2582 label.set_ellipsize(Pango.EllipsizeMode.END);
2583 label.set_line_wrap(false);
2585 hist_box.attach(label, 7, 0, 1,1);
2587 this.header.show_all();
2590 private Gtk.Grid history_buttons()
2592 var box = new Gtk.Grid();
2593 box.set_column_spacing(6);
2594 if(history == null && current == null) return box;
2596 var back_but = new Gtk.Button.from_stock("gtk-go-back");
2597 back_but.set_relief(Gtk.ReliefStyle.NONE);
2598 if(current == null || current.next == null) back_but.sensitive = false;
2599 back_but.clicked.connect(history_previous);
2602 back_but.set_hexpand(true);
2603 back_but.set_halign(Gtk.Align.END);
2604 box.attach(back_but, 10, 0, 1, 1);
2607 if(current != null && (current.next != null || current.prev != null))
2609 var dd_but = new Gtk.Button();
2610 var arrow = new Gtk.Arrow(Gtk.ArrowType.DOWN, Gtk.ShadowType.ETCHED_IN);
2611 dd_but.add(arrow);
2612 dd_but.set_relief(Gtk.ReliefStyle.NONE);
2613 dd_but.clicked.connect(history_show_list);
2615 box.attach(dd_but, 11, 0, 1, 1);
2617 var next_but = new Gtk.Button.from_stock("gtk-go-forward");
2618 next_but.set_relief(Gtk.ReliefStyle.NONE);
2619 if(current == null || current.prev == null) next_but.sensitive = false;
2620 next_but.clicked.connect(history_next);
2622 box.attach(next_but, 12, 0, 1, 1);
2624 return box;
2626 private void history_add(Hitem hi)
2628 if(history != null)
2630 unowned Hitem a = current.data;
2631 if(a.type == hi.type)
2633 if(Gmpc.Misc.song_checksum(a.song) == Gmpc.Misc.song_checksum(hi.song))
2635 return;
2639 /* clean items in the future */
2640 if(history != null)
2642 unowned List<Hitem?>? iter = current.prev;
2643 while(iter != null && iter != current)
2645 history.remove(iter.data);
2646 iter = history.first();
2650 history.prepend(hi);
2651 if(history.length() > 25)
2653 unowned List<Hitem?> a = history.last();
2654 history.remove(a.data);
2656 current = history;
2658 private void history_clear()
2660 this.current = null;
2661 this.history = null;
2666 * Public api
2668 public
2669 void
2670 set_base()
2673 this.tree_artist.get_selection().unselect_all();
2674 this.artist_filter_entry.set_text("");
2676 this.metadata_box_clear();
2677 this.metadata_box_update();
2679 public
2680 void
2681 set_artist(string artist)
2683 if(!this.get_enabled()) return;
2684 this.block_update++;
2687 this.tree_artist.get_selection().unselect_all();
2688 this.tree_album.get_selection().unselect_all();
2689 /* clear */
2690 this.artist_filter_entry.set_text("");
2691 Gtk.TreeIter iter;
2692 if(this.model_filter_artist.get_iter_first(out iter))
2696 string lartist= null;
2697 this.model_filter_artist.get(iter, 7, out lartist, -1);
2698 if( lartist != null && lartist.collate(artist) == 0)
2700 this.tree_artist.get_selection().select_iter(iter);
2701 this.tree_artist.scroll_to_cell(this.model_filter_artist.get_path(iter), null, true, 0.5f,0f);
2702 this.block_update--;
2704 this.metadata_box_clear();
2705 this.metadata_box_update();
2706 return;
2709 while((this.model_filter_artist).iter_next(ref iter));
2711 this.block_update--;
2714 this.metadata_box_clear();
2715 this.metadata_box_update();
2718 public
2719 void
2720 set_album(string artist, string album)
2722 if(!this.get_enabled()) return;
2723 this.block_update++;
2724 this.set_artist(artist);
2725 /* clear */
2726 this.album_filter_entry.set_text("");
2727 Gtk.TreeIter iter;
2728 if(this.model_filter_album.get_iter_first(out iter))
2732 string lalbum= null;
2733 this.model_filter_album.get(iter, 6, out lalbum, -1);
2734 if( lalbum != null && lalbum.collate(album) == 0)
2736 this.tree_album.get_selection().select_iter(iter);
2737 this.tree_album.scroll_to_cell(this.model_filter_album.get_path(iter), null, true, 0.5f,0f);
2738 this.tree_songs.get_selection().unselect_all();
2739 this.block_update--;
2740 this.metadata_box_update();
2741 return;
2744 while((this.model_filter_album).iter_next(ref iter));
2747 this.tree_songs.get_selection().unselect_all();
2749 this.block_update--;
2751 this.metadata_box_clear();
2752 this.metadata_box_update();
2755 public
2756 void
2757 set_song(MPD.Song song)
2759 if(!this.get_enabled()) return;
2760 this.block_update++;
2761 if(song.artist != null)
2763 this.set_artist(song.artist);
2764 if(song.album != null)
2766 this.set_album(song.artist,song.album);
2770 Gtk.TreeIter iter;
2771 if(this.model_songs.get_iter_first(out iter))
2775 string ltitle = null;
2776 this.model_songs.get(iter, 7, out ltitle, -1);
2777 if( ltitle != null && ltitle.collate(song.title) == 0)
2779 this.tree_songs.get_selection().select_iter(iter);
2780 this.tree_songs.scroll_to_cell(this.model_songs.get_path(iter), null, true, 0.5f,0f);
2781 this.block_update--;
2782 this.metadata_box_update();
2783 return;
2786 while((this.model_songs).iter_next(ref iter));
2790 this.block_update--;
2791 this.metadata_box_clear();
2792 if(this.update_timeout > 0)
2794 GLib.Source.remove(this.update_timeout);
2795 this.update_timeout = 0;
2798 /** Add item to history */
2799 var item = new Hitem();
2800 item.song = song.copy();
2801 item.type = HitemType.SONG;
2802 history_add(item);
2804 var view = metadata_box_show_song(song,true);
2805 this.metadata_box.add(view);
2806 this.metadata_box.show_all();
2808 public
2809 void
2810 select_browser(Gtk.TreeView? tree)
2812 if(rref != null)
2814 unowned Gtk.TreeView category_tree = Gmpc.Playlist3.get_category_tree_view();
2815 var sel = category_tree.get_selection();
2816 var path = rref.get_path();
2817 if(path != null)
2819 sel.select_path(path);
2824 private string get_extension(string path)
2826 long length = path.length;
2827 long i=length;
2828 string retv = null;
2829 for(; i>0 && (length-i) <8; i--)
2831 if(path[i] == '.')
2833 retv = path.substring(i+1);
2834 return retv;
2837 return retv;
2840 public override void set_enabled(bool state)
2842 if(state)
2844 if(rref == null)
2846 browser_add( Gmpc.Playlist3.get_category_tree_view());
2847 browser_init();
2850 else
2852 if(this.rref != null)
2854 var path = rref.get_path();
2855 if(path != null)
2857 unowned int[] indices = path.get_indices();
2858 config.set_int(this.get_name(), "position", indices[0]);
2859 Gtk.ListStore model = (Gtk.ListStore)rref.get_model();
2860 Gtk.TreeIter iter;
2861 if(model.get_iter(out iter, path))
2863 model.remove(iter);
2866 rref = null;
2870 if(this.get_name() != null)
2871 Gmpc.config.set_int(this.get_name(), "enabled", (int)state);