Remove weblinks.
[gmpc.git] / src / browsers / gmpc-metadata-browser2.vala
blob6b2f0036d1274bcad0f4ff824f794625b631c46a
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 {
56 /* Set the plugin as an internal one and of type pl_browser */
57 this.plugin_type = 2|8;
59 gmpcconn.connection_changed.connect(con_changed);
60 gmpcconn.status_changed.connect(status_changed);
63 var background = config.get_string_with_default("Now Playing", "background-color", "#000");
64 var foreground = config.get_string_with_default("Now Playing", "foreground-color", "#FFF");
65 Gdk.Color.parse(background,out this.background);
66 Gdk.Color.parse(foreground,out this.foreground);
68 easy_command.add_entry(
69 _("switch metadata"),
70 "",
71 _("Switch to Metadata view"),
72 (Gmpc.Easy.Command.Callback *)show_metadata_search,
73 this);
76 private const int[] version = {0,0,0};
77 public override unowned int[] get_version() {
78 return version;
81 public override unowned string get_name() {
82 return N_("Metadata Browser");
85 public override void save_yourself() {
86 if(this.paned != null) {
87 int pos = this.paned.get_position();
88 config.set_int(this.get_name(), "pane-pos", pos);
91 if(this.model_artist != null) this.model_artist.set_mpd_data(null);
92 if(this.model_albums != null)this.model_albums.set_mpd_data(null);
94 if(this.rref != null) {
95 var path = rref.get_path();
96 if(path != null) {
97 unowned int[] indices = path.get_indices();
98 config.set_int(this.get_name(), "position", indices[0]);
102 /* Now playing browser */
106 * Browser part
108 /* 'base' widget */
109 private Gtk.Paned paned = null;
110 /* holding the 3 browsers */
111 private Gtk.Box browser_box = null;
112 /* The 3 browsers */
113 /* artist */
114 private Gtk.TreeView tree_artist = null;
115 private Gmpc.MpdData.Model model_artist = null;
116 private Gtk.TreeModelFilter model_filter_artist = null;
117 private Gtk.Entry artist_filter_entry = null;
119 /* album */
120 private Gtk.TreeView tree_album = null;
121 private Gmpc.MpdData.Model model_albums = null;
122 private Gtk.TreeModelFilter model_filter_album = null;
123 private Gtk.Entry album_filter_entry = null;
125 /* song */
126 private Gtk.TreeView tree_songs = null;
127 private Gmpc.MpdData.Model model_songs = null;
129 /* The right hand "browser" box */
130 private Gtk.ScrolledWindow metadata_sw = null;
131 private Gtk.EventBox metadata_box = null;
133 private Gtk.EventBox header = null;
136 * Makes gmpc jump to the metadata browser
138 private void select_metadata_browser(Gtk.Widget widget)
140 this.select_browser(null);
143 * Makes gmpc jump to the metadata browser
144 * and search
146 private void select_metadata_browser_search(Gtk.Widget widget)
148 select_browser(null);
149 set_base();
152 * Gmpc.Plugin.BrowserIface.add_go_menu
154 private int browser_add_go_menu(Gtk.Menu menu)
156 if(this.get_enabled())
158 var item = new Gtk.ImageMenuItem.with_mnemonic(_(this.get_name()));
159 item.set_image(new Gtk.Image.from_icon_name("gmpc-metabrowser", Gtk.IconSize.MENU));
160 item.activate.connect(select_metadata_browser);
161 item.add_accelerator("activate", menu.get_accel_group(),0xffc1,0, Gtk.AccelFlags.VISIBLE);
162 menu.append(item);
164 item = new Gtk.ImageMenuItem.with_mnemonic(_("Search metadata"));
165 item.set_image(new Gtk.Image.from_stock("gtk-find", Gtk.IconSize.MENU));
166 item.activate.connect(select_metadata_browser_search);
167 item.add_accelerator("activate", menu.get_accel_group(),
168 0x04d,Gdk.ModifierType.CONTROL_MASK,
169 Gtk.AccelFlags.VISIBLE);
170 menu.append(item);
172 return 2;
174 return 0;
178 * This builds the browser
180 private void browser_bg_style_changed(Gtk.Widget bg,Gtk.Style? style)
182 this.metadata_box.modify_bg(Gtk.StateType.NORMAL,this.metadata_sw.style.base[Gtk.StateType.NORMAL]);
184 debug("Change style signal");
185 if(this.theme_colors) {
186 this.title_color = this.paned.style.text[Gtk.StateType.PRELIGHT].to_string();
187 this.item_color = this.paned.style.text[Gtk.StateType.PRELIGHT].to_string();
189 this.change_color_style(this.metadata_sw);
191 /* This hack makes clicking a selected row again, unselect it */
192 private bool browser_button_press_event(Gtk.Widget treel, Gdk.EventButton event)
194 var tree = (treel as Gtk.TreeView);
195 Gtk.TreePath path= null;
196 if(event.button != 1) return false;
197 if(tree.get_path_at_pos((int)event.x,(int)event.y,out path, null, null, null))
199 if(tree.get_selection().path_is_selected(path)){
200 tree.get_selection().unselect_path(path);
201 return true;
204 return false;
207 * Artist tree view functions */
208 private void browser_artist_entry_changed(Gtk.Editable entry)
210 string text = (entry as Gtk.Entry).get_text();
211 if(text.length > 0) {
212 (entry as Gtk.Widget).show();
213 (entry as Gtk.Widget).grab_focus();
214 }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) {
225 if(tree.get_selection().count_selected_rows()>0)
227 var menu = new Gtk.Menu();
228 var item = new Gtk.ImageMenuItem.from_stock("gtk-add",null);
229 item.activate.connect((source) => {
230 Gtk.TreeModel model;
231 Gtk.TreeIter iter;
232 if(tree.get_selection().get_selected(out model, out iter)) {
233 string artist;
234 model.get(iter,7, out artist);
235 if(artist != null) {
236 MPD.Database.search_start(server,true);
237 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
238 MPD.Data.Item data = MPD.Database.search_commit(server);
239 data.sort_album_disc_track();
240 if(data != null)
242 data.first();
244 MPD.PlayQueue.queue_add_song(server, data.song.file);
245 data.next_free();
246 }while(data != null);
247 MPD.PlayQueue.queue_commit(server);
252 menu.append(item);
254 item = new Gtk.ImageMenuItem.from_stock("gtk-media-play",null);
255 item.activate.connect((source) => {
256 Gtk.TreeModel model;
257 Gtk.TreeIter iter;
258 if(tree.get_selection().get_selected(out model, out iter)) {
259 string artist;
260 model.get(iter,7, out artist);
261 if(artist != null) {
262 MPD.PlayQueue.clear(server);
263 MPD.PlayQueue.add_artist(server, artist);
264 MPD.Player.play(server);
268 menu.append(item);
270 menu.popup(null, null, null, event.button, event.time);
271 menu.show_all();
272 return true;
276 return false;
279 private bool visible_func_artist (Gtk.TreeModel model, Gtk.TreeIter iter)
281 string text = this.artist_filter_entry.get_text();
282 /* Visible if row is non-empty and first column is "HI" */
283 string str = null;
284 bool visible = false;
286 if(text[0] == '\0') return true;
288 model.get (iter, 7, out str, -1);
289 if (str != null && str.casefold().normalize().index_of(text.casefold().normalize()) >= 0)
290 visible = true;
292 return visible;
294 private bool browser_artist_key_press_event(Gtk.Widget widget, Gdk.EventKey event)
296 unichar uc = Gdk.keyval_to_unicode(event.keyval);
297 if(uc > 0)
299 string outbuf = " ";
300 int i = uc.to_utf8(outbuf);
301 ((char[])outbuf)[i] = '\0';
302 this.artist_filter_entry.set_text(outbuf);
303 this.artist_filter_entry.grab_focus();
304 this.artist_filter_entry.set_position(1);
306 return true;
308 return false;
311 * Album tree view
313 private void album_add_clicked(Gtk.Widget item )
315 string artist = browser_get_selected_artist();
316 if(artist != null)
318 string albumartist = null;
319 string album = browser_get_selected_album();
320 if(album != null && Gmpc.server.tag_supported(MPD.Tag.Type.ALBUM_ARTIST))
322 MPD.Database.search_field_start(server, MPD.Tag.Type.ALBUM_ARTIST);
323 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, album);
324 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
325 var ydata = MPD.Database.search_commit(server);
326 if(ydata != null)
328 if(ydata.tag.length > 0)
329 albumartist = ydata.tag;
332 /* Fill in the first browser */
333 MPD.Database.search_start(server,true);
334 if(albumartist != null && albumartist.length > 0)
335 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM_ARTIST, albumartist);
336 else
337 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
339 if(album != null)
340 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, album);
341 var data = MPD.Database.search_commit(server);
343 data.sort_album_disc_track();
344 if(data != null)
347 MPD.PlayQueue.queue_add_song(server, data.song.file);
348 data.next_free();
349 }while(data != null);
350 MPD.PlayQueue.queue_commit(server);
354 private void album_replace_clicked(Gtk.Widget item)
356 MPD.PlayQueue.clear(server);
357 album_add_clicked(item);
358 MPD.Player.play(server);
360 /* Handle right mouse click */
361 private bool album_browser_button_release_event(Gtk.Widget tree, Gdk.EventButton event)
363 if(event.button == 3) {
364 if((tree as Gtk.TreeView).get_selection().count_selected_rows()>0)
366 var menu = new Gtk.Menu();
367 var item = new Gtk.ImageMenuItem.from_stock("gtk-add",null);
368 item.activate.connect(album_add_clicked);
369 menu.append(item);
371 item = new Gtk.ImageMenuItem.from_stock("gtk-media-play",null);
372 item.activate.connect(album_replace_clicked);
373 menu.append(item);
375 menu.popup(null, null, null, event.button, event.time);
376 menu.show_all();
377 return true;
381 return false;
383 private bool visible_func_album (Gtk.TreeModel model, Gtk.TreeIter iter)
385 string text = this.album_filter_entry.get_text();
386 /* Visible if row is non-empty and first column is "HI" */
387 string str = null;
388 bool visible = false;
390 if(text[0] == '\0') return true;
392 model.get (iter, 6, out str, -1);
393 if (str != null && str.casefold().normalize().index_of(text.casefold().normalize()) >= 0)
394 visible = true;
396 return visible;
400 private bool browser_album_key_press_event(Gtk.Widget widget, Gdk.EventKey event)
402 unichar uc = Gdk.keyval_to_unicode(event.keyval);
403 if(uc > 0)
405 string outbuf = " ";
406 int i = uc.to_utf8(outbuf);
407 ((char[])outbuf)[i] = '\0';
408 this.album_filter_entry.set_text(outbuf);
409 this.album_filter_entry.grab_focus();
410 this.album_filter_entry.set_position(1);
412 return true;
414 return false;
417 private void browser_album_entry_changed(Gtk.Editable pentry)
419 Gtk.Entry entry = (pentry as Gtk.Entry);
420 string text = entry.get_text();
421 if(text.length > 0) {
422 entry.show();
423 entry.grab_focus();
424 }else{
425 entry.hide();
426 this.tree_album.grab_focus();
428 this.model_filter_album.refilter();
431 * Songs
433 private void song_add_clicked(Gtk.Widget item )
435 unowned MPD.Song? song = browser_get_selected_song();
436 if(song != null)
438 MPD.PlayQueue.add_song(server,song.file);
441 private void song_replace_clicked(Gtk.Widget item)
443 MPD.PlayQueue.clear(server);
444 song_add_clicked(item);
445 MPD.Player.play(server);
447 /* Handle right mouse click */
448 private bool song_browser_button_release_event(Gtk.Widget ptree, Gdk.EventButton event)
450 Gtk.TreeView tree = (ptree as Gtk.TreeView);
451 if(event.button == 3) {
452 if(tree.get_selection().count_selected_rows()>0)
454 var menu = new Gtk.Menu();
455 var item = new Gtk.ImageMenuItem.from_stock("gtk-add",null);
456 item.activate.connect(song_add_clicked);
457 menu.append(item);
459 item = new Gtk.ImageMenuItem.from_stock("gtk-media-play",null);
460 item.activate.connect(song_replace_clicked);
461 menu.append(item);
463 menu.popup(null, null, null, event.button, event.time);
464 menu.show_all();
465 return true;
468 return false;
471 private bool browser_button_release_event(Gtk.Widget widget, Gdk.EventButton event)
473 if(event.button == 8) {
474 history_previous();
475 return true;
477 else if (event.button == 9) {
478 history_next();
479 return true;
481 return false;
483 private void browser_init()
485 if(this.paned == null)
487 this.paned = new Gtk.HPaned();
488 paned_size_group.add_paned(this.paned);
489 this.paned.style_set.connect(browser_bg_style_changed);
490 /* Bow with browsers */
491 this.browser_box = new Gtk.VBox(true, 6);
492 this.paned.add1(this.browser_box);
495 /* Artist list */
496 var box = new Gtk.VBox(false, 6);
497 this.browser_box.pack_start(box, true, true, 0);
499 this.artist_filter_entry = new Gtk.Entry();
501 this.artist_filter_entry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, "gtk-clear");
502 this.artist_filter_entry.icon_press.connect((source, pos, event)=>{
503 if(pos == Gtk.EntryIconPosition.SECONDARY) source.set_text("");
506 this.artist_filter_entry.set_no_show_all(true);
507 this.artist_filter_entry.changed.connect(browser_artist_entry_changed);
509 box.pack_start(this.artist_filter_entry, false, false, 0);
511 var sw = new Gtk.ScrolledWindow(null, null);
512 sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
513 sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN);
514 box.pack_start(sw, true, true, 0);
516 this.model_artist = new Gmpc.MpdData.Model();
517 this.model_filter_artist = new Gtk.TreeModelFilter(this.model_artist, null);
518 this.model_filter_artist.set_visible_func(visible_func_artist);
519 this.tree_artist = new Gtk.TreeView.with_model(this.model_filter_artist);
520 this.tree_artist.rules_hint = true;
521 new Gmpc.MpdData.Treeview.Tooltip(this.tree_artist, Gmpc.MetaData.Type.ARTIST_ART);
523 this.tree_artist.set_enable_search(false);
524 this.tree_artist.button_press_event.connect(browser_button_press_event);
525 this.tree_artist.button_release_event.connect(artist_browser_button_release_event);
526 this.tree_artist.key_press_event.connect(browser_artist_key_press_event);
527 sw.add(tree_artist);
528 /* setup the columns */
529 var column = new Gtk.TreeViewColumn();
530 if(config.get_int_with_default("tag2-plugin", "show-image-column", 1) == 1)
532 var prenderer = new Gtk.CellRendererPixbuf();
533 prenderer.set("height", this.model_artist.icon_size);
534 column.pack_start(prenderer, false);
535 column.add_attribute(prenderer, "pixbuf",27);
537 var trenderer = new Gtk.CellRendererText();
538 column.pack_start(trenderer, true);
539 column.add_attribute(trenderer, "text", 7);
540 this.tree_artist.append_column(column);
541 column.set_title(_("Artist"));
542 this.tree_artist.get_selection().changed.connect(browser_artist_changed);
544 /* set fixed height mode */
545 column.sizing = Gtk.TreeViewColumnSizing.FIXED;
546 this.tree_artist.set_fixed_height_mode(true);
549 /* Album list */
551 box = new Gtk.VBox(false, 6);
552 this.browser_box.pack_start(box, true, true, 0);
555 this.album_filter_entry = new Gtk.Entry();
557 this.album_filter_entry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, "gtk-clear");
558 this.album_filter_entry.icon_press.connect((source, pos, event)=>{
559 if(pos == Gtk.EntryIconPosition.SECONDARY) source.set_text("");
561 this.album_filter_entry.set_no_show_all(true);
562 this.album_filter_entry.changed.connect(browser_album_entry_changed);
563 box.pack_start(this.album_filter_entry, false, false, 0);
565 sw = new Gtk.ScrolledWindow(null, null);
566 sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
567 sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN);
568 box.pack_start(sw, true, true, 0);
569 this.model_albums = new Gmpc.MpdData.Model();
570 this.model_filter_album = new Gtk.TreeModelFilter(this.model_albums, null);
571 this.model_filter_album.set_visible_func(visible_func_album);
572 this.tree_album = new Gtk.TreeView.with_model(this.model_filter_album);
573 this.tree_album.rules_hint = true;
574 this.tree_album.set_enable_search(false);
575 new Gmpc.MpdData.Treeview.Tooltip(this.tree_album, Gmpc.MetaData.Type.ALBUM_ART);
577 this.tree_album.button_press_event.connect(browser_button_press_event);
578 this.tree_album.button_release_event.connect(album_browser_button_release_event);
579 this.tree_album.key_press_event.connect(browser_album_key_press_event);
580 sw.add(tree_album);
581 /* setup the columns */
582 column = new Gtk.TreeViewColumn();
583 if(config.get_int_with_default("tag2-plugin", "show-image-column", 1) == 1)
585 var prenderer = new Gtk.CellRendererPixbuf();
586 prenderer.set("height", this.model_albums.icon_size);
587 column.pack_start(prenderer, false);
588 column.add_attribute(prenderer, "pixbuf",27);
590 this.tree_album.append_column(column);
592 column = new Gtk.TreeViewColumn();
593 trenderer = new Gtk.CellRendererText();
594 column.pack_start(trenderer, true);
595 column.add_attribute(trenderer, "text", 14);
596 this.tree_album.append_column(column);
597 column.set_title(_("Year"));
599 column = new Gtk.TreeViewColumn();
600 trenderer = new Gtk.CellRendererText();
601 column.pack_start(trenderer, true);
602 column.add_attribute(trenderer, "text", 6);
603 this.tree_album.append_column(column);
604 column.set_title(_("Album"));
607 this.tree_album.get_selection().changed.connect(browser_album_changed);
609 /* Song list */
610 sw = new Gtk.ScrolledWindow(null, null);
611 sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
612 sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN);
613 this.browser_box.pack_start(sw, true, true, 0);
614 this.model_songs = new Gmpc.MpdData.Model();
615 this.tree_songs = new Gtk.TreeView.with_model(this.model_songs);
616 this.tree_songs.rules_hint = true;
617 this.tree_songs.button_press_event.connect(browser_button_press_event);
618 this.tree_songs.button_release_event.connect(song_browser_button_release_event);
619 sw.add(tree_songs);
620 /* setup the columns */
621 column = new Gtk.TreeViewColumn();
623 if(config.get_int_with_default("tag2-plugin", "show-image-column", 1) == 1)
625 var prenderer = new Gtk.CellRendererPixbuf();
626 column.pack_start(prenderer, false);
627 column.add_attribute(prenderer, "icon-name",23);
629 trenderer = new Gtk.CellRendererText();
630 column.pack_start(trenderer, false);
631 column.add_attribute(trenderer, "text", 10);
633 column.set_title(_("Track"));
634 this.tree_songs.append_column(column);
636 column = new Gtk.TreeViewColumn();
637 trenderer = new Gtk.CellRendererText();
638 column.pack_start(trenderer, true);
639 column.add_attribute(trenderer, "text", 7);
642 this.tree_songs.append_column(column);
643 this.tree_songs.set_search_column(7);
644 column.set_title(_("Songs"));
646 this.tree_songs.get_selection().changed.connect(browser_songs_changed);
648 /* The right view */
649 this.metadata_sw = new Gtk.ScrolledWindow(null, null);
650 this.metadata_sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
651 this.metadata_sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN);
652 // this.metadata_sw.style_set += browser_bg_style_changed;
653 this.metadata_box = new Gtk.EventBox();
654 this.metadata_box.set_visible_window(true);
655 this.metadata_sw.add_with_viewport(this.metadata_box);
656 (this.metadata_box as Gtk.Container).set_focus_hadjustment(
657 this.metadata_sw.get_hadjustment());
658 (this.metadata_box as Gtk.Container).set_focus_vadjustment(
659 this.metadata_sw.get_vadjustment());
661 /* header */
662 var vb = new Gtk.VBox(false, 0);
663 vb.pack_end(this.metadata_sw, true,true,0);
665 this.header = new Gtk.EventBox();
666 header.app_paintable = true;
667 header.set_visible_window(true);
668 header.expose_event.connect(Gmpc.Misc.misc_header_expose_event);
670 vb.pack_start(header, false, false,0);
671 header.show();
673 this.paned.add2(vb);
677 this.paned.button_release_event.connect(browser_button_release_event);
679 this.reload_browsers();
681 this.paned.show_all();
683 if(config.get_int_with_default("metadata browser", "show-browsers", 1) == 0)
685 this.browser_box.hide();
689 private void reload_browsers()
691 if(this.paned == null) return;
693 this.model_songs.set_mpd_data(null);
694 this.model_albums.set_mpd_data(null);
695 this.model_artist.set_mpd_data(null);
697 this.artist_filter_entry.set_text("");
698 this.album_filter_entry.set_text("");
700 /* Fill in the first browser */
701 MPD.Database.search_field_start(server,MPD.Tag.Type.ARTIST);
702 var data = MPD.Database.search_commit(server);
703 data.sort_album_disc_track();
704 this.model_artist.set_mpd_data((owned)data);
707 private string? browser_get_selected_artist()
709 Gtk.TreeIter iter;
710 var sel = this.tree_artist.get_selection();
711 Gtk.TreeModel model = null;//this.model_artist;
712 if(sel.get_selected(out model, out iter))
714 string artist = null;
715 model.get(iter, 7,out artist, -1);
716 return artist;
718 return null;
721 private string? browser_get_selected_album()
723 Gtk.TreeIter iter;
724 var sel = this.tree_album.get_selection();
725 Gtk.TreeModel model = null;//this.model_albums;
726 if(sel.get_selected(out model, out iter))
728 string album = null;
729 model.get(iter, 6,out album, -1);
730 return album;
732 return null;
734 private unowned MPD.Song? browser_get_selected_song()
736 Gtk.TreeIter iter;
737 var sel = this.tree_songs.get_selection();
738 Gtk.TreeModel model;
739 if(sel.get_selected(out model, out iter))
741 unowned MPD.Song? songs = null;
742 this.model_songs .get(iter, 0,out songs, -1);
743 return songs;
745 return null;
747 private void browser_artist_changed(Gtk.TreeSelection sel)
749 this.model_albums.set_mpd_data(null);
750 this.model_songs.set_mpd_data(null);
751 this.metadata_box_clear();
753 string artist = browser_get_selected_artist();
754 if(artist != null)
756 /* Fill in the first browser */
757 MPD.Database.search_field_start(server,MPD.Tag.Type.ALBUM);
758 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
759 var data = MPD.Database.search_commit(server);
760 data.sort_album_disc_track();
762 this.model_albums.set_request_artist(artist);
763 MPD.Data.Item list = null;
764 unowned MPD.Data.Item iter = data.get_first();
765 if(iter!= null)
768 list.append_new();
769 list.type = MPD.Data.Type.SONG;
770 list.song = new MPD.Song();
771 list.song.artist = artist;
772 list.song.album = iter.tag;
773 MPD.Database.search_field_start(server,MPD.Tag.Type.DATE);
774 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
775 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, iter.tag);
776 var ydata = MPD.Database.search_commit(server);
777 if(ydata != null) {
778 unowned MPD.Data.Item yi = ydata.get_first();
779 while(list.song.date == null && yi != null)
781 if(yi.tag != null && yi.tag.length > 0) {
782 list.song.date = yi.tag;
784 yi.next(false);
787 iter.next(false);
788 }while(iter!= null);
791 list.sort_album_disc_track();
792 this.model_albums.set_mpd_data((owned)list);
794 MPD.Database.search_start(server,true);
795 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
796 data = MPD.Database.search_commit(server);
797 data.sort_album_disc_track();
798 this.model_songs.set_mpd_data((owned)data);
801 this.metadata_box_update();
803 private void browser_album_changed(Gtk.TreeSelection album_sel)
805 this.model_songs.set_mpd_data(null);
806 this.metadata_box_clear();
808 string artist = browser_get_selected_artist();
809 if(artist != null)
811 string album = browser_get_selected_album();
812 string albumartist = null;
814 if(album != null && Gmpc.server.tag_supported(MPD.Tag.Type.ALBUM_ARTIST))
816 MPD.Database.search_field_start(server, MPD.Tag.Type.ALBUM_ARTIST);
817 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, album);
818 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
819 var ydata = MPD.Database.search_commit(server);
820 if(ydata != null)
822 if(ydata.tag.length > 0)
823 albumartist = ydata.tag;
826 /* Fill in the first browser */
827 MPD.Database.search_start(server,true);
828 if(albumartist != null&& albumartist.length > 0)
829 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM_ARTIST, albumartist);
830 else
831 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
833 if(album != null)
834 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, album);
835 var data = MPD.Database.search_commit(server);
836 data.sort_album_disc_track();
837 this.model_songs.set_mpd_data((owned)data);
839 this.metadata_box_update();
841 private void browser_songs_changed (Gtk.TreeSelection song_sel)
843 this.metadata_box_clear();
844 this.metadata_box_update();
847 * Metadata box
850 private void add_selected_song(Gtk.Button button)
852 string artist = browser_get_selected_artist();
853 string album = browser_get_selected_album();
854 unowned MPD.Song? song = browser_get_selected_song();
855 if(song != null){
856 MPD.PlayQueue.add_song(server,song.file);
857 return;
859 if(artist != null ) {
860 /* Hunt albumartist */
861 string albumartist = null;
862 if(album != null&& Gmpc.server.tag_supported(MPD.Tag.Type.ALBUM_ARTIST))
864 MPD.Database.search_field_start(server, MPD.Tag.Type.ALBUM_ARTIST);
865 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, album);
866 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
867 var ydata = MPD.Database.search_commit(server);
868 if(ydata != null)
870 if(ydata.tag.length > 0)
871 albumartist = ydata.tag;
875 MPD.Database.search_start(server,true);//server,MPD.Tag.Type.FILENAME);
876 if(albumartist != null&& albumartist.length > 0)
877 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM_ARTIST, albumartist);
878 else
879 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
880 if(album != null)
881 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, album);
882 var data = MPD.Database.search_commit(server);
883 if(data != null) {
884 data.sort_album_disc_track();
885 while(data != null){
886 MPD.PlayQueue.queue_add_song(server, data.song.file);
887 data.next_free();
889 MPD.PlayQueue.queue_commit(server);
894 private void play_selected_song(Gtk.Button button)
896 unowned MPD.Song? song = browser_get_selected_song();
897 if(song != null) {
898 Gmpc.MpdInteraction.play_path(song.file);
901 private void replace_selected_song(Gtk.Button button)
903 MPD.PlayQueue.clear(server);
904 this.add_selected_song(button);
905 MPD.Player.play(server);
907 private void metadata_box_clear()
909 var list = this.metadata_box.get_children();
910 foreach(Gtk.Widget child in list){
911 child.destroy();
915 * Add a row to a gtk table
916 * <b>$label:</b> $value
917 * then increments i
920 private void add_entry(Gtk.Table table, string entry_label, string? value,Gtk.Widget? extra, ref int i, string ? image = null)
922 int j=0;
923 if(value == null && extra == null) return;
924 var box = new Gtk.HBox(false, 6);
925 var label = new Gtk.Label("");
926 label.set_alignment(0.0f, 0.0f);
927 label.set_markup(Markup.printf_escaped("<b>%s:</b>",entry_label));
928 if(image != null)
930 /* Make sure the icon always lines out to the top */
931 var ali = new Gtk.Alignment (0f, 0f, 0f, 0f);
932 var wimage = new Gtk.Image.from_icon_name(image, Gtk.IconSize.MENU);
933 ali.add(wimage);
934 box.pack_start(ali, false, false, 0);
936 box.pack_start(label, true, true, 0);
937 table.attach(box, j,(j+1),i,i+1,Gtk.AttachOptions.SHRINK|Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK|Gtk.AttachOptions.FILL,0,0);
938 j++;
940 if(value != null)
942 var pt_label = new Gtk.Label(value);
943 pt_label.set_selectable(true);
944 pt_label.set_alignment(0.0f, 0.0f);
945 pt_label.set_line_wrap(true);
946 table.attach(pt_label, j,(j+1),i,i+1,Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK|Gtk.AttachOptions.FILL,0,0);
947 j++;
949 if(extra != null)
951 table.attach(extra, j,(j+1),i,i+1,Gtk.AttachOptions.SHRINK|Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK|Gtk.AttachOptions.FILL,0,0);
952 j++;
954 i++;
957 public Gtk.Widget metadata_box_show_song(MPD.Song tmp_song, bool show_controls)
959 var master_box = new Gtk.VBox (false, 6);
960 var vbox = new Gtk.VBox (false,6);
961 var song = tmp_song.copy();
962 vbox.border_width = 8;
964 history_bc_header(HitemType.SONG, song.artist, song.album, song.title);
967 /* Start building the gui */
968 /* Artist image */
969 var hbox = new Gtk.HBox(false, 6);
970 /* Album image */
971 var ali = new Gtk.Alignment(0f,0f,0f,0f);
972 int meta_size = (int)(this.metadata_box.allocation.width*0.20);
973 meta_size = int.min(meta_size, (int)(this.metadata_sw.allocation.height*0.30));
974 meta_size = int.min(int.max(100, meta_size), 250);
975 var album_image = new Gmpc.MetaData.Image(Gmpc.MetaData.Type.ALBUM_ART, meta_size);
976 album_image.set_scale_up(true);
977 album_image.set_squared(false);
978 ali.add(album_image);
979 album_image.update_from_song(song);
980 hbox.pack_start(ali, false, false, 0);
982 /* Artist image */
983 ali = new Gtk.Alignment(1f,0f,0f,0f);
984 var artist_image = new Gmpc.MetaData.Image(Gmpc.MetaData.Type.ARTIST_ART, meta_size);
986 artist_image.set_no_cover_icon("no-artist");
987 artist_image.set_loading_cover_icon("fetching-artist");
988 artist_image.set_scale_up(true);
989 artist_image.set_hide_on_na(true);
990 artist_image.set_squared(false);
991 artist_image.update_from_song(song);
992 ali.add(artist_image);
993 hbox.pack_end(ali, false, false, 0);
996 /* Information box */
997 var info_box = new Gtk.Table (4,2,false);
998 info_box.set_row_spacings(3);
999 info_box.set_col_spacings(8);
1000 int i=0;
1001 /* Title */
1002 if(song.title != null) {
1004 var box = new Gtk.HBox(false, 6);
1006 if(config.get_int_with_default("Interface", "hide-favorites-icon",0) == 0){
1007 var fav_button = new Gmpc.Favorites.Button();
1008 fav_button.set_song(song);
1009 ali = new Gtk.Alignment(0.0f, 0.5f,0f,0f);
1010 ali.add(fav_button);
1011 box.pack_start(ali, false, false, 0);
1013 var label = new Gtk.Label(song.title);
1014 label.selectable = true;
1015 label.set_markup(GLib.Markup.printf_escaped("<span color='%s' size='%i' weight='bold'>%s</span>",
1016 this.title_color,Pango.SCALE*20 ,song.title));
1017 label.set_ellipsize(Pango.EllipsizeMode.END);
1018 label.set_alignment(0.0f, 0.5f);
1019 box.pack_start(label, true, true, 0);
1020 //hist_box.pack_start(box, true, true, 0);
1021 //vbox.pack_start(box, true, true, 0);
1022 info_box.attach(box, 0,2, i, i+1,Gtk.AttachOptions.FILL,Gtk.AttachOptions.FILL,0,0);
1023 i++;
1024 }else if (song.name!= null) {
1025 var label = new Gtk.Label(song.name);
1026 label.selectable = true;
1027 label.set_markup(GLib.Markup.printf_escaped("<span color='%s' size='%i' weight='bold'>%s</span>",this.
1028 title_color, Pango.SCALE*20, song.name));
1029 label.set_ellipsize(Pango.EllipsizeMode.END);
1030 label.set_alignment(0.0f, 0.5f);
1031 info_box.attach(label, 0,2, i, i+1,Gtk.AttachOptions.FILL,Gtk.AttachOptions.FILL,0,0);
1032 i++;
1034 else if (song.file != null)
1036 var filename = GLib.Path.get_basename(song.file);
1037 var label = new Gtk.Label(song.name);
1038 label.selectable = true;
1039 try {
1040 var regex_a = new GLib.Regex ("\\.[0-9a-zA-Z]*$");
1041 filename = regex_a.replace_literal (filename, -1, 0, "");
1042 } catch (GLib.RegexError e) {
1043 GLib.error("%s", e.message);
1045 try {
1046 var regex_b = new GLib.Regex ("_");
1047 filename = regex_b.replace_literal (filename, -1, 0, " ");
1048 } catch (GLib.RegexError e) {
1049 GLib.error("%s", e.message);
1051 label.set_markup(GLib.Markup.printf_escaped("<span color='%s' size='%i' weight='bold'>%s</span>",this.
1052 title_color, Pango.SCALE*20, filename));
1053 label.set_ellipsize(Pango.EllipsizeMode.END);
1054 label.set_alignment(0.0f, 0.5f);
1055 info_box.attach(label, 0,2, i, i+1,Gtk.AttachOptions.FILL,Gtk.AttachOptions.FILL,0,0);
1056 i++;
1058 /* Artist */
1059 if(song.artist != null) {
1060 this.add_entry(info_box, _("Artist"), song.artist, null,ref i, "media-artist");
1062 /* Album */
1063 if(song.album != null) {
1064 this.add_entry(info_box, _("Album"), song.album,null, ref i, "media-album");
1066 /* Genre */
1067 if(song.genre != null) {
1068 this.add_entry(info_box, _("Genre"), song.genre,null, ref i, "media-genre");
1070 /* Genre */
1071 if(song.date != null) {
1072 this.add_entry(info_box, _("Date"), song.date,null, ref i, "media-date");
1074 if(song.file != null)
1077 string extension = null;
1078 extension = get_extension(song.file);
1079 if(extension != null)
1081 this.add_entry(info_box, _("Codec"), extension,null, ref i, "media-codec");
1084 string directory = GLib.Path.get_dirname(song.file);
1085 if(directory != null)
1087 this.add_entry(info_box, _("Directory"), directory, null, ref i, "gtk-open");
1091 /* Time*/
1092 if(song.time > 0) {
1093 this.add_entry(info_box, _("Length"),Gmpc.Misc.format_time((ulong) song.time, ""),null, ref i, "media-track-length");
1096 if(song.track != null) {
1097 var label = new Gtk.Label("");
1098 label.selectable = true;
1099 label.set_ellipsize(Pango.EllipsizeMode.END);
1100 label.set_markup(GLib.Markup.printf_escaped("%s %s",
1101 song.track,
1102 (song.disc != null)? "[%s]".printf(song.disc):""
1104 label.set_alignment(0.0f, 0.5f);
1105 this.add_entry(info_box, _("Track"),null,label, ref i, "media-num-tracks");
1109 /* Player controls */
1110 var control_hbox = new Gtk.VBox (false, 6);
1112 var abutton = new Gtk.Button.from_stock("gtk-add");
1113 abutton.set_tooltip_text(_("Add the item to the play queue"));
1114 abutton.set_property("xalign", 0.0f);
1115 abutton.set_relief(Gtk.ReliefStyle.NONE);
1116 abutton.clicked.connect(add_selected_song);
1117 control_hbox.pack_start(abutton, false, false,0);
1119 abutton = new Gtk.Button.with_mnemonic(_("_Clear and play"));
1120 abutton.set_tooltip_text(_("Replace the current play queue with this item"));
1121 abutton.set_property("xalign", 0.0f);
1122 abutton.set_image(new Gtk.Image.from_stock("gtk-redo", Gtk.IconSize.BUTTON));
1123 abutton.set_relief(Gtk.ReliefStyle.NONE);
1124 abutton.clicked.connect(replace_selected_song);
1125 control_hbox.pack_start(abutton, false, false,0);
1127 abutton = new Gtk.Button.from_stock("gtk-media-play");
1128 abutton.set_tooltip_text(_("Play this song"));
1129 abutton.set_property("xalign", 0.0f);
1130 abutton.set_relief(Gtk.ReliefStyle.NONE);
1131 abutton.clicked.connect(play_selected_song);
1132 control_hbox.pack_start(abutton, false, false,0);
1134 // info_box.attach(control_hbox, 0,2,i,i+1,Gtk.AttachOptions.SHRINK|Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK|Gtk.AttachOptions.FILL,0,0);
1135 i++;
1138 hbox.pack_start(info_box, true, true, 0);
1139 hbox.pack_start(control_hbox, false, false, 0);
1140 vbox.pack_start(hbox, false, false, 0);
1143 var hboxje = new Gtk.HBox(false, 6);
1145 /* Notebook where all the metadata items are kept, Override the tabs by a radiobutton list. */
1146 var notebook = new Gtk.Notebook();
1147 notebook.set_show_border(false);
1148 notebook.set_show_tabs(false);
1150 /* Lyrics */
1151 i = 0;
1152 unowned SList<unowned Gtk.RadioButton> group = null;
1153 if(config.get_int_with_default("MetaData", "show-lyrics",1) == 1)
1155 var alib = new Gtk.Alignment(0f,0f,1f,0f);
1156 var text_view = new Gmpc.MetaData.TextView(Gmpc.MetaData.Type.SONG_TXT);
1157 text_view.set_left_margin(8);
1158 text_view.query_from_song(song);
1160 alib.add(text_view);
1161 notebook.append_page(alib, new Gtk.Label("Lyrics"));
1162 var button = new Gtk.RadioButton.with_label(group,("Lyrics"));
1163 group = button.get_group();
1164 hboxje.pack_start(button, false, false, 0);
1165 var j = i;
1166 button.clicked.connect((source) => {
1167 debug("notebook page %i clicked", j);
1168 notebook.set_current_page(j);
1170 i++;
1172 alib.show();
1175 /* Guitar Tabs */
1176 if(config.get_int_with_default("MetaData", "show-guitar-tabs",1) == 1)
1178 var alib = new Gtk.Alignment(0f,0f,1f,0f);
1179 var text_view = new Gmpc.MetaData.TextView(Gmpc.MetaData.Type.SONG_GUITAR_TAB);
1180 text_view.use_monospace = true;
1181 text_view.set_left_margin(8);
1182 var text_view_queried = false;
1184 alib.add(text_view);
1185 notebook.append_page(alib, new Gtk.Label(_("Guitar Tabs")));
1186 var button = new Gtk.RadioButton.with_label(group,_("Guitar Tabs"));
1187 group = button.get_group();
1188 hboxje.pack_start(button, false, false, 0);
1189 var j = i;
1190 /* Only query the guitar-tab when opened or first notebook page*/
1191 button.clicked.connect((source) => {
1192 debug("notebook page %i clicked", j);
1193 notebook.set_current_page(j);
1194 if(!text_view_queried){
1195 text_view.query_from_song(song);
1196 text_view_queried = true;
1197 this.change_color_style(text_view);
1200 if(i == 0){
1201 text_view.query_from_song(song);
1202 text_view_queried = true;
1204 alib.show();
1205 i++;
1207 /* Similar songs */
1209 if(config.get_int_with_default("MetaData", "show-similar-songs",1) == 1)
1211 var similar_songs_queried = false;
1212 var similar_songs_box = new Gtk.Alignment(0f,0f,0f,0f);
1214 notebook.append_page(similar_songs_box, new Gtk.Label(_("Similar Songs")));
1216 var button = new Gtk.RadioButton.with_label(group,_("Similar Songs"));
1217 group = button.get_group();
1218 hboxje.pack_start(button, false, false, 0);
1220 var j = i;
1221 /* Only query when opened or first notebook page*/
1222 button.clicked.connect((source) => {
1223 debug("notebook page %i clicked", j);
1224 notebook.set_current_page(j);
1225 if(!similar_songs_queried){
1226 var similar_songs = new Gmpc.MetaData.Widgets.SimilarSongs(song);
1227 similar_songs.update();
1228 similar_songs_queried = true;
1229 similar_songs_box.add(similar_songs);
1230 this.change_color_style(similar_songs_box);
1231 similar_songs_box.show_all();
1234 if(i == 0){
1235 var similar_songs = new Gmpc.MetaData.Widgets.SimilarSongs(song);
1236 similar_songs.update();
1237 similar_songs_queried = true;
1238 similar_songs_box.add(similar_songs);
1239 similar_songs_box.show_all();
1241 similar_songs_box.show();
1242 i++;
1245 if(config.get_int_with_default("MetaData", "show-similar-artist",1) == 1 && song.artist != null)
1247 var similar_artist = new Gmpc.MetaData.Widgets.SimilarArtists(Gmpc.server,song);
1249 notebook.append_page(similar_artist, new Gtk.Label(_("Similar Artist")));
1251 var button = new Gtk.RadioButton.with_label(group,_("Similar Artist"));
1252 group = button.get_group();
1253 hboxje.pack_start(button, false, false, 0);
1255 var j = i;
1256 button.clicked.connect((source) => {
1257 debug("notebook page %i clicked", j);
1258 notebook.set_current_page(j);
1259 similar_artist.first_show();
1261 similar_artist.show();
1262 i++;
1264 /* Track changed pages */
1265 notebook.notify["page"].connect((source,spec) => {
1266 var page = notebook.get_current_page();
1267 config.set_int("MetaData", "song-last-page", (int)page);
1270 /* Restore right page */
1271 if(i > 0){
1272 var page = config.get_int_with_default("MetaData", "song-last-page", 0);
1273 if(page > i)
1275 notebook.set_current_page(0);
1276 }else{
1277 /* The list is in reversed order, compensate for that. */
1278 var w = group.nth_data(i-page-1);
1279 w.set_active(true);
1280 notebook.set_current_page(page);
1284 ali = new Gtk.Alignment(0.0f, 0.5f,0f,0f);
1285 ali.add(hboxje);
1287 /* Create pane in 2. */
1288 var bottom_hbox = new Gtk.HBox(false, 6);
1289 /* left pane */
1291 var metadata_vbox = new Gtk.VBox(false, 6);
1293 var sep = new Gtk.HSeparator();
1294 sep.set_size_request(-1, 1);
1295 metadata_vbox.pack_start(sep, false, false, 0);
1297 metadata_vbox.pack_start(ali, false, false, 0);
1298 sep = new Gtk.HSeparator();
1299 sep.set_size_request(-1, 1);
1300 metadata_vbox.pack_start(sep, false, false, 0);
1301 metadata_vbox.pack_start(notebook, false, false, 0);
1303 bottom_hbox.pack_start(metadata_vbox, true, true, 0);
1305 vbox.pack_start(bottom_hbox, true, true, 0);
1307 /* show it */
1308 master_box.pack_start(vbox, false, false, 0);
1309 return master_box;
1312 private void metadata_box_show_base()
1314 Gtk.Entry MetadataBoxShowBaseEntry = null;
1315 var master_box = new Gtk.VBox (false, 6);
1316 var vbox = new Gtk.VBox (false,6);
1317 vbox.border_width = 8;
1320 history_bc_header(HitemType.BASE, null, null, null);
1322 var hbox = new Gtk.HBox(false, 6);
1323 var label = new Gtk.Label("");
1324 label.set_markup(_("<b>Search:</b>"));
1325 hbox.pack_start(label, false, false, 0);
1326 MetadataBoxShowBaseEntry = new Gtk.Entry();
1328 MetadataBoxShowBaseEntry.add_accelerator("grab-focus",
1329 Gmpc.Playlist.get_accel_group(),
1330 0x046,
1331 Gdk.ModifierType.CONTROL_MASK,
1334 /* Add clear icon */
1335 MetadataBoxShowBaseEntry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, "gtk-clear");
1336 MetadataBoxShowBaseEntry.icon_press.connect((source, pos, event) => {
1337 source.set_text("");
1341 hbox.pack_start(MetadataBoxShowBaseEntry, true, true, 0);
1343 vbox.pack_start(hbox, false, false, 0);
1346 var result_hbox = new Gmpc.Widgets.Songlist();
1347 result_hbox.artist_song_clicked.connect((source, song) => {
1348 if(song.artist != null) {
1349 this.set_artist(song.artist);
1352 result_hbox.album_song_clicked.connect((source, song) => {
1353 if(song.artist != null && song.album != null) {
1354 this.set_album(song.artist, song.album);
1357 result_hbox.song_clicked.connect((source, song) => {
1358 this.set_song(song);
1361 result_hbox.play_song_clicked.connect((source, song) => {
1362 if(song.file != null) {
1363 Gmpc.MpdInteraction.play_path(song.file);
1366 result_hbox.song_context_menu.connect((source, song) => {
1367 var menu = new Gtk.Menu();
1369 var item = new Gtk.ImageMenuItem.from_stock("gtk-open",null);
1370 MPD.Song song_pass = song.copy();
1371 item.activate.connect((source)=>{
1372 this.set_song(song_pass);
1374 menu.append(item);
1376 stdout.printf("song context menu show\n");
1377 menu.show_all();
1378 menu.popup(null, null, null, 3, Gtk.get_current_event_time());
1381 MetadataBoxShowBaseEntry.activate.connect((source) => {
1382 string value = source.get_text();
1383 current.data.search_string = value;
1384 MPD.Data.Item? list = Gmpc.Query.search(value, false);
1385 result_hbox.set_from_data((owned)list,true,true);
1386 result_hbox.show_all();
1388 result_hbox.show();
1389 vbox.pack_start(result_hbox, false, false);
1391 if(current != null) {
1392 if(current.data.type == HitemType.BASE && current.data.search_string != null)
1394 MetadataBoxShowBaseEntry.set_text(current.data.search_string);
1395 MetadataBoxShowBaseEntry.activate();
1400 * Button that allows you to enable/disable the side bars
1402 var show_button = new Gtk.ToggleButton.with_label(_("Hide sidebar"));
1404 if(config.get_int_with_default("metadata browser", "show-browsers", 1) == 0)
1406 show_button.label = _("Show sidebar");
1407 show_button.set_active(true);
1409 show_button.toggled.connect((source) => {
1410 var state = show_button.active;
1411 if(state == true) {
1412 this.browser_box.hide();
1413 show_button.label = _("Show sidebar");
1414 }else {
1415 this.browser_box.show();
1416 show_button.label = _("Hide sidebar");
1418 config.set_int("metadata browser", "show-browsers", (int)!state);
1422 var ali = new Gtk.Alignment(0.0f, 0.5f, 0f,0f);
1423 ali.add(show_button);
1424 vbox.pack_start(ali, false, false);
1426 * Add it to the view
1428 master_box.pack_start(vbox, false, false, 0);
1429 this.metadata_box.add(master_box);
1430 this.change_color_style(this.metadata_sw);
1431 this.metadata_sw.show_all();
1432 // MetadataBoxShowBaseEntry.grab_focus();
1437 private void metadata_box_show_album(string artist, string album)
1439 var master_box = new Gtk.VBox (false, 6);
1440 var vbox = new Gtk.VBox (false,6);
1441 vbox.border_width = 8;
1444 history_bc_header(HitemType.ALBUM, artist, album, null);
1446 /* Artist image */
1447 var hbox = new Gtk.HBox(false, 6);
1448 var ali = new Gtk.Alignment(0f,0f,0f,0f);
1450 int meta_size = (int)(this.metadata_box.allocation.width*0.20);
1451 meta_size = int.min(meta_size, (int)(this.metadata_sw.allocation.height*0.30));
1452 meta_size = int.min(int.max(100, meta_size), 250);
1453 var artist_image = new Gmpc.MetaData.Image(Gmpc.MetaData.Type.ALBUM_ART, meta_size);
1454 artist_image.set_squared(false);
1455 MPD.Song song = new MPD.Song();
1456 song.artist = artist;
1457 song.album = album;
1458 artist_image.update_from_song(song);
1459 ali.add(artist_image);
1460 hbox.pack_start(ali, false, false, 0);
1461 /* Artist information */
1462 var info_box = new Gtk.Table (4,2,false);
1463 info_box.set_row_spacings(3);
1464 info_box.set_col_spacings(8);
1465 hbox.pack_start(info_box, true, true, 0);
1466 int i=0;
1469 var label = new Gtk.Label(artist != null?artist:_("Unknown"));
1470 label.set_alignment(0.0f, 0.5f);
1471 label.set_line_wrap(true);
1472 this.add_entry(info_box, _("Artist"), null, label, ref i, "media-artist");
1474 label = new Gtk.Label(album != null?album:_("Unknown"));
1475 label.set_alignment(0.0f, 0.5f);
1476 label.set_line_wrap(true);
1477 this.add_entry(info_box, _("Album"), null, label, ref i, "media-album");
1479 /* Genres of songs */
1480 var pt_label = new Gmpc.MetaData.StatsLabel(Gmpc.MetaData.StatsLabel.Type.ALBUM_GENRES_SONGS, song);
1481 pt_label.set_selectable(true);
1482 pt_label.set_alignment(0.0f, 0.5f);
1483 pt_label.set_line_wrap(true);
1484 this.add_entry(info_box, _("Genres"), null, pt_label, ref i, "media-genre");
1486 /* Dates of songs */
1487 pt_label = new Gmpc.MetaData.StatsLabel(Gmpc.MetaData.StatsLabel.Type.ALBUM_DATES_SONGS, song);
1488 pt_label.set_selectable(true);
1489 pt_label.set_line_wrap(true);
1490 pt_label.set_alignment(0.0f, 0.5f);
1491 this.add_entry(info_box, _("Dates"), null, pt_label, ref i,"media-date");
1492 /* Total number of songs */
1493 pt_label = new Gmpc.MetaData.StatsLabel(Gmpc.MetaData.StatsLabel.Type.ALBUM_NUM_SONGS, song);
1494 pt_label.set_selectable(true);
1495 pt_label.set_line_wrap(true);
1496 pt_label.set_alignment(0.0f, 0.5f);
1497 this.add_entry(info_box, _("Songs"), null, pt_label, ref i, "media-num-tracks");
1498 /* Total playtime */
1499 pt_label = new Gmpc.MetaData.StatsLabel(Gmpc.MetaData.StatsLabel.Type.ALBUM_PLAYTIME_SONGS, song);
1500 pt_label.set_selectable(true);
1501 pt_label.set_line_wrap(true);
1502 pt_label.set_alignment(0.0f, 0.5f);
1503 this.add_entry(info_box, _("Playtime"), null, pt_label, ref i, "media-track-length");
1505 vbox.pack_start(hbox , false, false, 0);
1507 /* Player controls */
1508 var control_hbox = new Gtk.VBox (false, 6);
1510 var button = new Gtk.Button.from_stock("gtk-add");
1511 button.set_tooltip_text(_("Add the item to the play queue"));
1512 button.set_property("xalign", 0.0f);
1513 button.set_relief(Gtk.ReliefStyle.NONE);
1514 button.clicked.connect(add_selected_song);
1515 control_hbox.pack_start(button, false, false,0);
1517 button = new Gtk.Button.from_stock("gtk-media-play");
1518 button.set_tooltip_text(_("Replace the current play queue with this item and play"));
1519 button.set_property("xalign", 0.0f);
1520 button.set_relief(Gtk.ReliefStyle.NONE);
1521 button.clicked.connect(replace_selected_song);
1522 control_hbox.pack_start(button, false, false,0);
1524 hbox.pack_start(control_hbox, false, false, 0);
1526 /* Separator */
1527 var sep = new Gtk.HSeparator();
1528 sep.set_size_request(-1, 4);
1529 vbox.pack_start(sep, false, false, 0);
1531 var hboxje = new Gtk.HBox(false, 6);
1533 /* Notebook where all the metadata items are kept, Override the tabs by a radiobutton list. */
1534 var notebook = new Gtk.Notebook();
1535 notebook.set_show_border(false);
1536 notebook.set_show_tabs(false);
1538 /* Lyrics */
1539 i = 0;
1540 unowned SList<unowned Gtk.RadioButton> group = null;
1541 /* Album information */
1542 if(config.get_int_with_default("MetaData", "show-album-information",1) == 1)
1544 var alib = new Gtk.Alignment(0f,0f,1f,0f);
1545 var text_view = new Gmpc.MetaData.TextView(Gmpc.MetaData.Type.ALBUM_TXT);
1546 text_view.set_left_margin(8);
1547 text_view.query_from_song(song);
1549 alib.add(text_view);
1550 notebook.append_page(alib, new Gtk.Label(_("Album information")));
1551 var rbutton = new Gtk.RadioButton.with_label(group,_("Album information"));
1552 group = rbutton.get_group();
1553 hboxje.pack_start(rbutton, false, false, 0);
1554 var j = i;
1555 rbutton.clicked.connect((source) => {
1556 debug("notebook page %i clicked", j);
1557 notebook.set_current_page(j);
1559 i++;
1561 alib.show();
1564 var slhbox = new Gtk.VBox(false, 6);
1566 var sl = new Gmpc.Widgets.Songlist();
1567 slhbox.pack_start(sl, false, false, 0);
1569 sl.album_song_clicked.connect((source, song) => {
1570 if(song.artist != null && song.album != null) {
1571 this.set_album(song.artist, song.album);
1574 sl.song_clicked.connect((source, song) => {
1575 this.set_song(song);
1578 sl.play_song_clicked.connect((source, song) => {
1579 if(song.file != null) {
1580 Gmpc.MpdInteraction.play_path(song.file);
1584 sl.song_context_menu.connect((source,song)=>{
1585 var menu = new Gtk.Menu();
1587 var item = new Gtk.ImageMenuItem.from_stock("gtk-open",null);
1588 MPD.Song song_pass = song.copy();
1589 item.activate.connect((source)=>{
1590 this.set_song(song_pass);
1592 menu.append(item);
1594 stdout.printf("song context menu show\n");
1595 menu.show_all();
1596 menu.popup(null, null, null, 3, Gtk.get_current_event_time());
1599 notebook.append_page(slhbox, new Gtk.Label(_("Song list")));
1600 var rbutton = new Gtk.RadioButton.with_label(group,_("Song list"));
1601 group = rbutton.get_group();
1602 hboxje.pack_start(rbutton, false, false, 0);
1603 var j = i;
1604 bool seen = false;
1605 rbutton.clicked.connect((source) => {
1606 notebook.set_current_page(j);
1608 notebook.notify["page"].connect((source)=>
1610 if(notebook.page != j) return;
1611 debug("notebook page %i clicked", j);
1612 if(!seen)
1614 string albumartist = null;
1615 if(Gmpc.server.tag_supported(MPD.Tag.Type.ALBUM_ARTIST))
1617 MPD.Database.search_field_start(server, MPD.Tag.Type.ALBUM_ARTIST);
1618 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, album);
1619 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
1620 var ydata = MPD.Database.search_commit(server);
1621 if(ydata != null)
1623 if(ydata.tag.length > 0)
1624 albumartist = ydata.tag;
1627 MPD.Database.search_start(server,true);
1628 if(albumartist != null&& albumartist.length > 0)
1629 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM_ARTIST, albumartist);
1630 else
1631 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
1633 if(album != null)
1634 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, album);
1635 var data = MPD.Database.search_commit(server);
1636 data.sort_album_disc_track();
1638 sl.set_from_data((owned)data,false);
1640 seen = true;
1643 slhbox.show();
1644 i++;
1646 /* Track changed pages */
1647 notebook.notify["page"].connect((source,spec) => {
1648 var page = notebook.get_current_page();
1649 config.set_int("MetaData", "album-last-page", (int)page);
1652 /* Restore right page */
1653 if(i > 0){
1654 var page = config.get_int_with_default("MetaData", "album-last-page", 0);
1655 if(page > i)
1657 notebook.set_current_page(0);
1658 }else{
1659 /* The list is in reversed order, compensate for that. */
1660 var w = group.nth_data(i-page-1);
1661 w.set_active(true);
1662 notebook.set_current_page(page);
1666 ali = new Gtk.Alignment(0.0f, 0.5f,0f,0f);
1667 ali.add(hboxje);
1669 /* Create pane in 2. */
1670 var bottom_hbox = new Gtk.HBox(false, 6);
1671 /* left pane */
1672 var metadata_vbox = new Gtk.VBox(false, 6);
1673 metadata_vbox.pack_start(ali, false, false, 0);
1674 sep = new Gtk.HSeparator();
1675 sep.set_size_request(-1, 1);
1676 metadata_vbox.pack_start(sep, false, false, 0);
1677 metadata_vbox.pack_start(notebook, false, false, 0);
1678 bottom_hbox.pack_start(metadata_vbox, true, true, 0);
1681 vbox.pack_start(bottom_hbox, true, true, 0);
1683 * Add it to the view
1685 master_box.pack_start(vbox, false, false, 0);
1686 this.metadata_box.add(master_box);
1687 this.change_color_style(this.metadata_sw);
1688 this.metadata_sw.show_all();
1691 * This fills the view for artist
1692 * <artist name>
1693 * <image> | <array with info>
1694 * < buttonss>
1696 * <artist info text>
1698 * <similar artists>
1699 * <links>
1701 private void metadata_box_show_artist(string artist)
1703 var master_box = new Gtk.VBox (false, 6);
1704 var vbox = new Gtk.VBox (false,6);
1705 var i = 0;
1706 vbox.border_width = 8;
1708 /* Set up header */
1709 history_bc_header(HitemType.ARTIST, artist, null, null);
1711 /* Create an MPD.Song with the info for this type set */
1712 MPD.Song song = new MPD.Song();
1713 song.artist = artist;
1715 /* Artist image */
1716 var hbox = new Gtk.HBox(false, 6);
1717 var ali = new Gtk.Alignment(0f,0f,0f,0f);
1718 int meta_size = (int)(this.metadata_box.allocation.width*0.20);
1719 meta_size = int.min(meta_size, (int)(this.metadata_sw.allocation.height*0.30));
1720 meta_size = int.min(int.max(100, meta_size), 250);
1721 var artist_image = new Gmpc.MetaData.Image(Gmpc.MetaData.Type.ARTIST_ART, meta_size);
1723 artist_image.set_no_cover_icon("no-artist");
1724 artist_image.set_loading_cover_icon("fetching-artist");
1725 artist_image.set_squared(false);
1726 artist_image.update_from_song(song);
1727 ali.add(artist_image);
1728 hbox.pack_start(ali, false, false, 0);
1729 /* Artist information */
1730 var info_box = new Gtk.Table (4,2,false);
1731 info_box.set_row_spacings(3);
1732 info_box.set_col_spacings(8);
1733 hbox.pack_start(info_box, true, true, 0);
1735 /* Artist */
1736 var label = new Gtk.Label((artist != null)?artist:_("Unknown"));
1737 label.set_selectable(true);
1738 label.set_alignment(0.0f, 0.5f);
1739 this.add_entry(info_box, _("Artist"), null, label, ref i, "media-artist");
1741 /* Genres of songs */
1742 var pt_label = new Gmpc.MetaData.StatsLabel(Gmpc.MetaData.StatsLabel.Type.ARTIST_GENRES_SONGS, song);
1743 pt_label.set_selectable(true);
1744 pt_label.set_alignment(0.0f, 0.5f);
1745 pt_label.set_line_wrap(true);
1746 this.add_entry(info_box, _("Genres"), null, pt_label, ref i, "media-genre");
1747 /* Dates of songs */
1748 pt_label = new Gmpc.MetaData.StatsLabel(Gmpc.MetaData.StatsLabel.Type.ARTIST_DATES_SONGS, song);
1749 pt_label.set_selectable(true);
1750 pt_label.set_line_wrap(true);
1751 pt_label.set_alignment(0.0f, 0.5f);
1752 this.add_entry(info_box, _("Dates"), null, pt_label, ref i,"media-date");
1753 /* Total number of songs */
1754 pt_label = new Gmpc.MetaData.StatsLabel(Gmpc.MetaData.StatsLabel.Type.ARTIST_NUM_SONGS, song);
1755 pt_label.set_selectable(true);
1756 pt_label.set_line_wrap(true);
1757 pt_label.set_alignment(0.0f, 0.5f);
1758 this.add_entry(info_box, _("Songs"), null, pt_label, ref i, "media-num-tracks");
1759 /* Total playtime */
1760 pt_label = new Gmpc.MetaData.StatsLabel(Gmpc.MetaData.StatsLabel.Type.ARTIST_PLAYTIME_SONGS, song);
1761 pt_label.set_selectable(true);
1762 pt_label.set_line_wrap(true);
1763 pt_label.set_alignment(0.0f, 0.5f);
1764 this.add_entry(info_box, _("Playtime"), null, pt_label, ref i, "media-track-length");
1766 vbox.pack_start(hbox , true, true, 0);
1768 /* Player controls */
1769 var hbox_2 = new Gtk.VBox (false, 6);
1771 var button = new Gtk.Button.from_stock("gtk-add");
1772 button.set_tooltip_text(_("Add the item to the play queue"));
1773 button.set_property("xalign", 0.0f);
1774 button.set_relief(Gtk.ReliefStyle.NONE);
1775 button.clicked.connect(add_selected_song);
1776 hbox_2.pack_start(button, false, false,0);
1778 button = new Gtk.Button.from_stock("gtk-media-play");
1779 button.set_tooltip_text(_("Replace the current play queue with this item and play"));
1780 button.set_property("xalign", 0.0f);
1781 button.set_relief(Gtk.ReliefStyle.NONE);
1782 button.clicked.connect(replace_selected_song);
1783 hbox_2.pack_start(button, false, false,0);
1785 hbox.pack_start(hbox_2, false, false, 0);
1788 var hboxje = new Gtk.HBox(false, 6);
1789 /* Separator */
1790 var sep = new Gtk.HSeparator();
1791 sep.set_size_request(-1, 4);
1792 vbox.pack_start(sep, false, false, 0);
1795 /* Notebook where all the metadata items are kept, Override the tabs by a radiobutton list. */
1796 var notebook = new Gtk.Notebook();
1797 notebook.set_show_border(false);
1798 notebook.set_show_tabs(false);
1800 i = 0;
1801 unowned SList<unowned Gtk.RadioButton> group = null;
1802 /* Artist information */
1803 if(config.get_int_with_default("MetaData", "show-artist-information",1) == 1)
1805 var alib = new Gtk.Alignment(0f,0f,1f,0f);
1806 var text_view = new Gmpc.MetaData.TextView(Gmpc.MetaData.Type.ARTIST_TXT);
1807 text_view.set_left_margin(8);
1808 text_view.query_from_song(song);
1810 alib.add(text_view);
1811 notebook.append_page(alib, new Gtk.Label(_("Artist information")));
1812 var button_sai = new Gtk.RadioButton.with_label(group,_("Artist information"));
1813 group = button_sai.get_group();
1814 hboxje.pack_start(button_sai, false, false, 0);
1816 var j = i;
1817 button_sai.clicked.connect((source) => {
1818 if((source as Gtk.CheckButton).get_active()) {
1819 debug("notebook page %i clicked", j);
1820 notebook.set_current_page(j);
1823 i++;
1825 alib.show();
1828 /* Show similar artist */
1829 if(config.get_int_with_default("MetaData", "show-similar-artist",1) == 1)
1831 var similar_artist = new Gmpc.MetaData.Widgets.SimilarArtists(Gmpc.server,song);
1833 notebook.append_page(similar_artist, new Gtk.Label(_("Similar Artist")));
1835 var button_sa = new Gtk.RadioButton.with_label(group,_("Similar Artist"));
1836 group = button_sa.get_group();
1837 hboxje.pack_start(button_sa, false, false, 0);
1839 var j = i;
1840 button_sa.clicked.connect((source) => {
1841 if((source as Gtk.CheckButton).get_active()) {
1842 debug("notebook page %i clicked", j);
1843 similar_artist.first_show();
1844 notebook.set_current_page(j);
1847 similar_artist.show();
1848 i++;
1852 var slhbox = new Gtk.VBox(false, 6);
1854 var sl = new Gmpc.Widgets.Songlist();
1855 slhbox.pack_start(sl, false, false, 0);
1857 sl.album_song_clicked.connect((source, song) => {
1858 if(song.artist != null && song.album != null) {
1859 this.set_album(song.artist, song.album);
1862 sl.song_clicked.connect((source, song) => {
1863 this.set_song(song);
1866 sl.play_song_clicked.connect((source, song) => {
1867 if(song.file != null) {
1868 Gmpc.MpdInteraction.play_path(song.file);
1871 sl.song_context_menu.connect((source,song)=>{
1872 var menu = new Gtk.Menu();
1874 var item = new Gtk.ImageMenuItem.from_stock("gtk-open",null);
1875 MPD.Song song_pass = song.copy();
1876 item.activate.connect((source)=>{
1877 this.set_song(song_pass);
1879 menu.append(item);
1881 stdout.printf("song context menu show\n");
1882 menu.show_all();
1883 menu.popup(null, null, null, 3, Gtk.get_current_event_time());
1887 notebook.append_page(slhbox, new Gtk.Label(_("Song list")));
1888 var rbutton = new Gtk.RadioButton.with_label(group,_("Song list"));
1889 group = rbutton.get_group();
1890 hboxje.pack_start(rbutton, false, false, 0);
1891 var j = i;
1892 bool seen = false;
1893 rbutton.clicked.connect((source) => {
1894 notebook.set_current_page(j);
1896 notebook.notify["page"].connect((source)=>
1898 if(notebook.page != j) return;
1899 debug("notebook page %i clicked", j);
1900 if(!seen)
1902 MPD.Database.search_start(server,true);
1903 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, artist);
1904 var data = MPD.Database.search_commit(server);
1905 data.sort_album_disc_track();
1907 sl.set_from_data((owned)data, true);
1909 seen = true;
1912 slhbox.show();
1913 i++;
1916 if( song.artist != null)
1918 int albums =0;
1919 /* Create album list */
1920 var album_hbox = new Gtk.VBox(false, 6);
1922 var album_view = new Gmpc.Widgets.Qtable();
1923 album_hbox.pack_start(album_view, false, true, 0);
1925 * Reuse the album browser view.
1927 Gtk.TreeIter titer;
1928 if(this.model_albums.get_iter_first(out titer))
1931 unowned MPD.Song? asong = null;
1932 this.model_albums.get(titer,0, &asong);
1933 if(asong != null)
1935 /* Make a copy of the song. Otherwise only a reference is added to the
1936 * button clicked handler.
1937 * This reference can be invalid before click completed.
1939 MPD.Song but_song = asong.copy();
1941 /* Create button */
1942 var but_hbox = new Gtk.VBox(false, 6);
1943 var image = new Gmpc.MetaData.Image(Gmpc.MetaData.Type.ALBUM_ART, 175);
1944 image.set_squared(true);
1945 image.set_scale_up(true);
1946 image.set_has_tooltip(false);
1947 image.update_from_song_delayed(but_song);
1949 but_hbox.pack_start(image, false, false, 0);
1951 var but_label = new Gtk.Label(but_song.album);
1952 but_label.selectable = true;
1953 but_label.set_alignment(0.5f, 0.0f);
1954 /* Create label */
1955 var strlabel = "<b>";
1956 if(but_song.date != null && but_song.date.length > 0) strlabel += GLib.Markup.printf_escaped("%s - ",but_song.date);
1957 if(but_song.album != null) strlabel+=GLib.Markup.escape_text(but_song.album);
1958 else strlabel += _("No Album");
1959 strlabel += "</b>";
1960 if(but_song.artist != null) strlabel += GLib.Markup.printf_escaped("\n%s",but_song.artist);
1961 but_label.set_markup(strlabel);
1962 (but_label as Gtk.Misc).set_alignment(0.0f, 0.0f);
1963 but_label.set_ellipsize(Pango.EllipsizeMode.END);
1964 /* add label */
1965 but_hbox.pack_start(but_label, true, true, 0);
1967 /* If clicked switch to browser */
1968 image.button_release_event.connect((source, event) => {
1969 if(event.button == 2) {
1970 set_album(but_song.artist, but_song.album);
1971 return true;
1972 }else if (event.button == 1) {
1973 var p = new PopupMenu();
1975 hbox = new Gtk.HBox(false, 0);
1976 var open = new Gtk.Button();
1977 open.set_tooltip_text(_("Open"));
1978 open.add(new Image.from_stock("gtk-open", Gtk.IconSize.DND));
1979 hbox.pack_start(open, false, false, 0);
1980 open.clicked.connect( (source) => {
1981 set_album(but_song.artist, but_song.album);
1982 p.destroy();
1986 * Play machine
1988 var play = new Button();
1989 play.add(new Image.from_stock("gtk-media-play", Gtk.IconSize.DND));
1990 play.set_tooltip_text(_("Play album"));
1991 hbox.pack_start(play, false, false, 0);
1992 play.clicked.connect( (source) => {
1993 MPD.PlayQueue.clear(server);
1994 MPD.PlayQueue.add_album(server,
1995 but_song.artist,
1996 but_song.album);
1997 MPD.Player.play(server);
1998 p.destroy();
2002 * Add to play queue
2004 var add = new Button();
2005 add.add(new Image.from_stock("gtk-add", Gtk.IconSize.DND));
2006 add.set_tooltip_text(_("Add album to play-queue"));
2007 hbox.pack_start(add, false, false, 0);
2008 add.clicked.connect( (source) => {
2009 MPD.PlayQueue.add_album(server,
2010 but_song.artist,
2011 but_song.album);
2012 p.destroy();
2015 p.add(hbox);
2016 hbox.show_all();
2017 p.popup(event);
2018 return true;
2020 return false;
2022 albums++;
2025 album_view.add(but_hbox);
2027 }while(this.model_albums.iter_next(ref titer));
2030 notebook.append_page(album_hbox, new Gtk.Label(_("Albums")));
2031 var button_sl = new Gtk.RadioButton.with_label(group,_("Albums"));
2032 group = button_sl.get_group();
2033 hboxje.pack_start(button_sl, false, false, 0);
2034 var j = i;
2035 button_sl.clicked.connect((source) => {
2036 if((source as Gtk.CheckButton).get_active()) {
2037 debug("notebook page %i clicked", j);
2038 notebook.set_current_page(j);
2041 album_hbox.show();
2042 i++;
2045 /* Track changed pages */
2046 notebook.notify["page"].connect((source,spec) => {
2047 var page = notebook.get_current_page();
2048 config.set_int("MetaData", "artist-last-page", (int)page);
2051 /* Restore right page */
2052 if(i > 0){
2053 var page = config.get_int_with_default("MetaData", "artist-last-page", 0);
2054 if(page > i)
2056 notebook.set_current_page(0);
2057 }else{
2058 /* The list is in reversed order, compensate for that. */
2059 var w = group.nth_data(i-page-1);
2060 w.set_active(true);
2061 notebook.set_current_page(page);
2066 ali = new Gtk.Alignment(0.0f, 0.5f,0f,0f);
2067 ali.add(hboxje);
2069 /* left pane */
2070 var metadata_vbox = new Gtk.VBox(false, 6);
2071 metadata_vbox.pack_start(ali, false, false, 0);
2072 sep = new Gtk.HSeparator();
2073 sep.set_size_request(-1, 1);
2074 metadata_vbox.pack_start(sep, false, false, 0);
2075 metadata_vbox.pack_start(notebook, false, false, 0);
2078 vbox.pack_start(metadata_vbox, true, true, 0);
2082 * Add it to the view
2085 master_box.pack_start(vbox, false, false, 0);
2086 this.metadata_box.add(master_box);
2087 this.change_color_style(this.metadata_sw);
2088 this.metadata_box.show_all();
2092 private uint update_timeout = 0;
2093 /* If we get an update, wait for idle time, then do it.*/
2094 private void metadata_box_update()
2096 if(this.update_timeout > 0) {
2097 GLib.Source.remove(this.update_timeout);
2099 update_timeout = GLib.Idle.add(this.metadata_box_update_real);
2101 private bool metadata_box_update_real()
2103 if(this.block_update > 0){
2104 this.update_timeout = 0;
2105 return false;
2107 string artist = browser_get_selected_artist();
2108 string album = browser_get_selected_album();
2109 unowned MPD.Song? song = browser_get_selected_song();
2111 if(song != null) {
2112 /** Add item to history */
2113 var item = new Hitem();
2114 item.song = song.copy();
2115 item.type = HitemType.SONG;
2116 history_add(item);
2118 var view = metadata_box_show_song(song,true);
2119 this.metadata_box.add(view);
2120 this.change_color_style(this.metadata_sw);
2121 this.metadata_box.show_all();
2122 }else if(album != null && artist != null) {
2123 /** Add item to history */
2124 var item = new Hitem();
2125 item.song = new MPD.Song();
2126 item.song.artist =artist;
2127 item.song.album = album;
2128 item.type = HitemType.ALBUM;
2129 history_add(item);
2131 metadata_box_show_album(artist,album);
2132 }else if (artist != null) {
2133 /** Add item to history */
2134 var item = new Hitem();
2135 item.song = new MPD.Song();
2136 item.song.artist =artist;
2137 item.type = HitemType.ARTIST;
2138 history_add(item);
2140 metadata_box_show_artist(artist);
2141 }else {
2142 var item = new Hitem();
2143 item.search_string = null;
2144 item.type = HitemType.BASE;
2145 history_add(item);
2146 metadata_box_show_base();
2149 this.update_timeout = 0;
2150 return false;
2153 * Browser Interface bindings
2155 public void browser_add (Gtk.Widget category_tree)
2157 Gtk.TreeView tree = (Gtk.TreeView)category_tree;
2158 Gtk.ListStore store = (Gtk.ListStore)tree.get_model();
2159 Gtk.TreeModel model = tree.get_model();
2160 Gtk.TreeIter iter;
2161 Gmpc.Browser.insert(out iter,
2162 Playlist.BrowserType.LIBRARY+config.get_int_with_default(this.get_name(), "position", 100)%Playlist.BrowserType.LIBRARY);
2163 store.set(iter, 0, this.id, 1, _(this.get_name()), 3, "gmpc-metabrowser");
2164 /* Create a row reference */
2165 this.rref = new Gtk.TreeRowReference(model, model.get_path(iter));
2167 public void browser_selected (Gtk.Container container)
2169 string artist;
2170 this.selected = true;
2171 this.browser_init();
2172 container.add(this.paned);
2174 /* update if non selected */
2175 artist = browser_get_selected_artist();
2176 if(artist == null) {
2177 metadata_box_clear();
2178 metadata_box_update();
2182 private bool selected = false;
2183 public void browser_unselected(Gtk.Container container)
2185 this.selected = false;
2186 container.remove(this.paned);
2188 private
2189 void
2190 con_changed(Gmpc.Connection conn, MPD.Server server, int connect)
2192 if(this.paned == null) return;
2193 this.history_clear();
2195 this.reload_browsers();
2196 metadata_box_clear();
2197 metadata_box_update();
2199 private
2200 void
2201 status_changed(Gmpc.Connection conn, MPD.Server server, MPD.Status.Changed what)
2203 if(this.paned == null) return;
2204 if((what&MPD.Status.Changed.DATABASE) != 0)
2206 this.reload_browsers();
2207 /* Restore old selection */
2208 if(this.current != null) {
2209 this.show_hitem(this.current.data);
2215 * History
2217 private enum HitemType {
2218 BASE,
2219 ARTIST,
2220 ALBUM,
2221 SONG
2223 private class Hitem {
2224 public HitemType type;
2225 public MPD.Song? song;
2226 public string search_string ;
2228 private List<Hitem?> history = null;
2229 private unowned List<Hitem?> current = null;
2230 private void show_hitem(Hitem hi)
2232 switch(hi.type)
2234 case HitemType.BASE:
2235 this.set_base();
2236 break;
2237 case HitemType.ARTIST:
2238 this.set_artist(hi.song.artist);
2239 break;
2240 case HitemType.ALBUM:
2241 this.set_album(hi.song.artist, hi.song.album);
2242 break;
2243 case HitemType.SONG:
2244 this.set_song(hi.song);
2245 break;
2246 default:
2247 metadata_box_clear();
2248 break;
2251 private void history_previous()
2253 if(history == null || current == null){
2254 return;
2256 if(current.next == null) {
2257 return;
2259 current = current.next;
2260 if(current != null) show_hitem(current.data);
2261 else metadata_box_clear();
2264 private void history_next()
2266 if(history == null || current == null){
2267 return;
2269 if(current.prev == null) {
2270 return;
2272 current = current.prev;
2273 if(current != null) show_hitem(current.data);
2274 else metadata_box_clear();
2277 private void history_show_list_clicked(Gtk.MenuItem item)
2279 unowned List<Hitem?> a = (List<Hitem?>) item.get_data<List<Hitem?>>("current");
2280 if(a != null)
2282 current = a;
2283 show_hitem(current.data);
2286 private void history_show_list()
2288 var menu = new Gtk.Menu();
2289 unowned List<Hitem?> iter = history.last();
2290 while(iter!= null){
2291 unowned Hitem i = iter.data;
2292 string label = "";
2293 if(i.type == HitemType.ARTIST)
2294 label = i.song.artist;
2295 else if (i.type == HitemType.ALBUM)
2296 label = "%s - %s".printf(i.song.artist, i.song.album);
2297 else if (i.type == HitemType.SONG)
2299 if(i.song.title != null)
2300 label = i.song.title;
2301 else
2302 label = _("Unknown");
2303 }else if (i.type == HitemType.BASE)
2305 label = _("Metadata Browser");
2306 if(i.search_string != null) {
2307 label += " ("+i.search_string+")";
2311 var item = new Gtk.CheckMenuItem.with_label(label);
2312 item.draw_as_radio = true;
2313 if(current != null && current == iter){
2314 item.set_active(true);
2316 item.activate.connect(history_show_list_clicked);
2317 item.set_data("current", (void*)iter);
2318 menu.append(item);
2319 iter = iter.prev;
2321 menu.show_all();
2322 menu.popup(null, null, null, 0, Gtk.get_current_event_time());
2326 private void history_bc_header(HitemType type, string? artist, string? album, string? title)
2328 foreach(var child in this.header.get_children()) {
2329 child.destroy();
2331 /* Add the history buttons.
2332 * we use the history buttons hbox for adding our buttons */
2333 var hist_box = history_buttons();
2334 this.header.add(hist_box);
2336 /* The buttons are in a schrinking hbox.
2337 * The final label will be placed next to it, with ellipsizing on.
2339 var bc_ali = new Gtk.Alignment(0f, 0.5f, 0f, 0f);
2340 var bc_box = new Gtk.HBox(false, 6);
2341 bc_ali.add(bc_box);
2342 hist_box.pack_start(bc_ali, false, false, 0);
2344 /* The 'BASE' button */
2345 if(artist == null && album == null && title==null)
2347 var image = new Gtk.Image.from_icon_name("gmpc-metabrowser", Gtk.IconSize.MENU);
2348 var ali = new Gtk.Alignment(0.5f,0.5f, 0f, 0f);
2349 ali.set_padding(6,6,6,6);
2350 ali.add(image);
2351 bc_box.pack_start(ali, true, true, 0);
2353 else
2355 var image = new Gtk.Image.from_icon_name("gmpc-metabrowser", Gtk.IconSize.MENU);
2356 var button = new Gtk.Button();
2357 button.set_relief(Gtk.ReliefStyle.NONE);
2358 button.set_image(image);
2359 button.clicked.connect((source)=> { set_base();});
2360 bc_box.pack_start(button, true, true, 0);
2362 /* If there is an artist, we can show more */
2363 if(artist != null) {
2364 if(type != HitemType.ARTIST)
2366 var button = new Gtk.Button.with_label(artist);
2367 button.set_relief(Gtk.ReliefStyle.NONE);
2368 button.set_image(new Gtk.Image.from_icon_name("media-artist", Gtk.IconSize.MENU));
2369 button.clicked.connect((source)=>{
2370 set_artist(artist);
2372 bc_box.pack_start(button, true, true, 0);
2374 else
2376 var label = new Gtk.Label(artist);
2377 var image = new Gtk.Image.from_icon_name("media-artist", Gtk.IconSize.MENU);
2378 label.set_ellipsize(Pango.EllipsizeMode.END);
2379 label.set_alignment(0.0f, 0.5f);
2380 hist_box.pack_start(image, false, false, 0);
2381 hist_box.pack_start(label, true, true, 0);
2383 /* Album */
2384 if(album != null) {
2385 if(type == HitemType.ALBUM)
2387 var label = new Gtk.Label(album);
2388 var image = new Gtk.Image.from_icon_name("media-album", Gtk.IconSize.MENU);
2389 label.set_ellipsize(Pango.EllipsizeMode.END);
2390 label.set_alignment(0.0f, 0.5f);
2391 hist_box.pack_start(image, false, false, 0);
2392 hist_box.pack_start(label, true, true, 0);
2394 else
2396 var button = new Gtk.Button.with_label(album);
2397 button.set_relief(Gtk.ReliefStyle.NONE);
2398 button.set_image(new Gtk.Image.from_icon_name("media-album", Gtk.IconSize.MENU));
2399 button.clicked.connect((source)=>{
2400 set_album(artist, album);
2402 bc_box.pack_start(button, true, true, 0);
2406 if(title != null)
2408 var label = new Gtk.Label(title);
2409 var image = new Gtk.Image.from_icon_name("media-audiofile", Gtk.IconSize.MENU);
2410 label.set_ellipsize(Pango.EllipsizeMode.END);
2411 label.set_alignment(0.0f, 0.5f);
2412 hist_box.pack_start(image, false, false, 0);
2413 hist_box.pack_start(label, true, true, 0);
2416 this.header.show_all();
2419 private Gtk.HBox history_buttons()
2421 var box = new HBox(false, 6);
2422 if(history == null && current == null) return box;
2424 var next_but = new Gtk.Button.from_stock("gtk-go-forward");
2425 next_but.set_relief(Gtk.ReliefStyle.NONE);
2426 if(current == null || current.prev == null) next_but.sensitive = false;
2427 next_but.clicked.connect(history_next);
2428 box.pack_end(next_but, false, false, 0);
2429 if(current != null && (current.next != null || current.prev != null))
2431 var dd_but = new Gtk.Button();
2432 var arrow = new Gtk.Arrow(Gtk.ArrowType.DOWN, Gtk.ShadowType.ETCHED_IN);
2433 dd_but.add(arrow);
2434 dd_but.set_relief(Gtk.ReliefStyle.NONE);
2435 dd_but.clicked.connect(history_show_list);
2436 box.pack_end(dd_but, false, false, 0);
2438 var back_but = new Gtk.Button.from_stock("gtk-go-back");
2439 back_but.set_relief(Gtk.ReliefStyle.NONE);
2440 if(current == null || current.next == null) back_but.sensitive = false;
2441 back_but.clicked.connect(history_previous);
2442 box.pack_end(back_but, false, false, 0);
2445 return box;
2447 private void history_add(Hitem hi)
2449 if(history != null)
2451 unowned Hitem a = current.data;
2452 if(a.type == hi.type) {
2453 if(Gmpc.Misc.song_checksum(a.song) == Gmpc.Misc.song_checksum(hi.song)){
2454 return;
2458 /* clean items in the future */
2459 if(history != null) {
2460 unowned List<Hitem?>? iter = current.prev;
2461 while(iter != null && iter != current) {
2462 history.remove(iter.data);
2463 iter = history.first();
2467 history.prepend(hi);
2468 if(history.length() > 25){
2469 unowned List<Hitem?> a = history.last();
2470 history.remove(a.data);
2472 current = history;
2474 private void history_clear()
2476 this.current = null;
2477 this.history = null;
2482 * Public api
2484 public
2485 void
2486 set_base()
2489 this.tree_artist.get_selection().unselect_all();
2490 this.artist_filter_entry.set_text("");
2492 this.metadata_box_clear();
2493 this.metadata_box_update();
2495 public
2496 void
2497 set_artist(string artist)
2499 if(!this.get_enabled()) return;
2500 this.block_update++;
2503 this.tree_artist.get_selection().unselect_all();
2504 this.tree_album.get_selection().unselect_all();
2505 /* clear */
2506 this.artist_filter_entry.set_text("");
2507 Gtk.TreeIter iter;
2508 if(this.model_filter_artist.get_iter_first(out iter))
2511 string lartist= null;
2512 this.model_filter_artist.get(iter, 7, out lartist, -1);
2513 if( lartist != null && lartist.collate(artist) == 0){
2514 this.tree_artist.get_selection().select_iter(iter);
2515 this.tree_artist.scroll_to_cell(this.model_filter_artist.get_path(iter), null, true, 0.5f,0f);
2516 this.block_update--;
2518 this.metadata_box_clear();
2519 this.metadata_box_update();
2520 return;
2522 }while((this.model_filter_artist).iter_next(ref iter));
2524 this.block_update--;
2527 this.metadata_box_clear();
2528 this.metadata_box_update();
2531 public
2532 void
2533 set_album(string artist, string album)
2535 if(!this.get_enabled()) return;
2536 this.block_update++;
2537 this.set_artist(artist);
2538 /* clear */
2539 this.album_filter_entry.set_text("");
2540 Gtk.TreeIter iter;
2541 if(this.model_filter_album.get_iter_first(out iter))
2544 string lalbum= null;
2545 this.model_filter_album.get(iter, 6, out lalbum, -1);
2546 if( lalbum != null && lalbum.collate(album) == 0){
2547 this.tree_album.get_selection().select_iter(iter);
2548 this.tree_album.scroll_to_cell(this.model_filter_album.get_path(iter), null, true, 0.5f,0f);
2549 this.tree_songs.get_selection().unselect_all();
2550 this.block_update--;
2551 this.metadata_box_update();
2552 return;
2554 }while((this.model_filter_album).iter_next(ref iter));
2557 this.tree_songs.get_selection().unselect_all();
2559 this.block_update--;
2561 this.metadata_box_clear();
2562 this.metadata_box_update();
2565 public
2566 void
2567 set_song(MPD.Song song)
2569 if(!this.get_enabled()) return;
2570 this.block_update++;
2571 if(song.artist != null)
2573 this.set_artist(song.artist);
2574 if(song.album != null)
2576 this.set_album(song.artist,song.album);
2580 Gtk.TreeIter iter;
2581 if(this.model_songs.get_iter_first(out iter))
2584 string ltitle = null;
2585 this.model_songs.get(iter, 7, out ltitle, -1);
2586 if( ltitle != null && ltitle.collate(song.title) == 0){
2587 this.tree_songs.get_selection().select_iter(iter);
2588 this.tree_songs.scroll_to_cell(this.model_songs.get_path(iter), null, true, 0.5f,0f);
2589 this.block_update--;
2590 this.metadata_box_update();
2591 return;
2593 }while((this.model_songs).iter_next(ref iter));
2597 this.block_update--;
2598 this.metadata_box_clear();
2599 if(this.update_timeout > 0) {
2600 GLib.Source.remove(this.update_timeout);
2601 this.update_timeout = 0;
2604 /** Add item to history */
2605 var item = new Hitem();
2606 item.song = song.copy();
2607 item.type = HitemType.SONG;
2608 history_add(item);
2610 var view = metadata_box_show_song(song,true);
2611 this.metadata_box.add(view);
2612 this.change_color_style(this.metadata_sw);
2613 this.metadata_box.show_all();
2615 public
2616 void
2617 select_browser(Gtk.TreeView? tree)
2619 if(rref != null)
2621 unowned Gtk.TreeView category_tree = Gmpc.Playlist3.get_category_tree_view();
2622 var sel = category_tree.get_selection();
2623 var path = rref.get_path();
2624 if(path != null){
2625 sel.select_path(path);
2630 private void change_color_style(Gtk.Widget bg)
2632 debug("change style");
2633 if(bg is Gtk.Separator || bg is Gtk.Notebook || bg is Gtk.CheckButton){
2634 /* Do nothing */
2635 }else{
2636 if(theme_colors)
2638 bg.modify_bg(Gtk.StateType.NORMAL,this.paned.style.base[Gtk.StateType.NORMAL]);
2639 }else{
2640 bg.modify_bg(Gtk.StateType.NORMAL,this.background);
2641 bg.modify_base(Gtk.StateType.NORMAL,this.background);
2642 bg.modify_text(Gtk.StateType.NORMAL,this.foreground);
2643 bg.modify_fg(Gtk.StateType.NORMAL,this.foreground);
2644 bg.modify_text(Gtk.StateType.ACTIVE,this.foreground);
2645 bg.modify_fg(Gtk.StateType.ACTIVE,this.foreground);
2646 bg.modify_bg(Gtk.StateType.INSENSITIVE,this.background);
2647 bg.modify_base(Gtk.StateType.INSENSITIVE,this.background);
2650 /* Recurse into children, if the widget can hold children (so is a container */
2651 if(bg is Gtk.Container){
2652 foreach(Gtk.Widget child in ((Gtk.Container)bg).get_children())
2654 change_color_style(child);
2658 private string get_extension(string path)
2660 long length = path.length;
2661 long i=length;
2662 string retv = null;
2663 for(;i>0 && (length-i) <8;i--){
2664 if(path[i] == '.') {
2665 retv = path.substring(i+1);
2666 return retv;
2669 return retv;
2672 public override void set_enabled(bool state) {
2673 if(state) {
2674 if(rref == null) {
2675 browser_add( Gmpc.Playlist3.get_category_tree_view());
2676 browser_init();
2678 }else {
2679 if(this.rref != null) {
2680 var path = rref.get_path();
2681 if(path != null) {
2682 unowned int[] indices = path.get_indices();
2683 config.set_int(this.get_name(), "position", indices[0]);
2684 Gtk.ListStore model = (Gtk.ListStore)rref.get_model();
2685 Gtk.TreeIter iter;
2686 if(model.get_iter(out iter, path))
2688 model.remove(iter);
2691 rref = null;
2695 if(this.get_name() != null)
2696 Gmpc.config.set_int(this.get_name(), "enabled", (int)state);