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.
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
27 * * More. (expands, collapses a sub widget
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()
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(
72 _("Switch to Metadata view"),
73 (Gmpc
.Easy
.Command
.Callback
*)show_metadata_search
,
77 private const int[] version
= {0,0,0};
78 public override unowned
int[] get_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);
101 var path
= rref
.get_path();
104 unowned
int[] indices
= path
.get_indices();
105 config
.set_int(this
.get_name(), "position", indices
[0]);
109 /* Now playing browser */
116 private Gtk
.Paned paned
= null;
117 /* holding the 3 browsers */
118 private Gtk
.Box browser_box
= null;
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;
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;
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
153 private void select_metadata_browser_search(Gtk
.Widget widget
)
155 select_browser(null);
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
);
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
);
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
);
204 * Artist tree view functions */
205 private void browser_artist_entry_changed(Gtk
.Editable entry
)
207 string text
= (entry as Gtk
.Entry
).get_text();
210 (entry as Gtk
.Widget
).show();
211 (entry as Gtk
.Widget
).grab_focus();
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
) =>
234 if(tree
.get_selection().get_selected(out model
, out iter
))
237 model
.get(iter
,7, out artist
);
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();
249 MPD
.PlayQueue
.queue_add_song(server
, data
.song
.file
);
253 MPD
.PlayQueue
.queue_commit(server
);
260 item
= new Gtk
.ImageMenuItem
.from_stock("gtk-media-play",null);
261 item
.activate
.connect((source
) =>
265 if(tree
.get_selection().get_selected(out model
, out iter
))
268 model
.get(iter
,7, out artist
);
271 MPD
.PlayQueue
.clear(server
);
272 MPD
.PlayQueue
.add_artist(server
, artist
);
273 MPD
.Player
.play(server
);
279 menu
.popup(null, null, null, event
.button
, event
.time
);
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" */
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)
303 private bool browser_artist_key_press_event(Gtk
.Widget widget
, Gdk
.EventKey event
)
305 unichar uc
= Gdk
.keyval_to_unicode(event
.keyval
);
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);
322 private void album_add_clicked(Gtk
.Widget item
)
324 string artist
= browser_get_selected_artist();
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
);
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
);
346 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ARTIST
, artist
);
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();
357 MPD
.PlayQueue
.queue_add_song(server
, data
.song
.file
);
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
);
383 item
= new Gtk
.ImageMenuItem
.from_stock("gtk-media-play",null);
384 item
.activate
.connect(album_replace_clicked
);
387 menu
.popup(null, null, null, event
.button
, event
.time
);
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" */
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)
412 private bool browser_album_key_press_event(Gtk
.Widget widget
, Gdk
.EventKey event
)
414 unichar uc
= Gdk
.keyval_to_unicode(event
.keyval
);
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);
429 private void browser_album_entry_changed(Gtk
.Editable pentry
)
431 Gtk
.Entry entry
= (pentry as Gtk
.Entry
);
432 string text
= entry
.get_text();
441 this
.tree_album
.grab_focus();
443 this
.model_filter_album
.refilter();
448 private void song_add_clicked(Gtk
.Widget item
)
450 unowned MPD
.Song? song
= browser_get_selected_song();
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
);
475 item
= new Gtk
.ImageMenuItem
.from_stock("gtk-media-play",null);
476 item
.activate
.connect(song_replace_clicked
);
479 menu
.popup(null, null, null, event
.button
, event
.time
);
487 private bool browser_button_release_event(Gtk
.Widget widget
, Gdk
.EventButton event
)
489 if(event
.button
== 8)
494 else if (event
.button
== 9)
501 public void set_orientation()
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
);
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
);
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
);
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);
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
);
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
);
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
);
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
);
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());
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);
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()
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);
760 private string?
browser_get_selected_album()
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
))
768 model
.get(iter
, 6,out album
, -1);
773 private unowned MPD
.Song?
browser_get_selected_song()
776 var sel
= this
.tree_songs
.get_selection();
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);
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();
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();
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
);
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
;
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();
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
);
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
);
874 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ARTIST
, artist
);
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();
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();
900 MPD
.PlayQueue
.add_song(server
,song
.file
);
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
);
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
);
924 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ARTIST
, artist
);
926 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ALBUM
, album
);
927 var data
= MPD
.Database
.search_commit(server
);
930 data
.sort_album_disc_track();
933 MPD
.PlayQueue
.queue_add_song(server
, data
.song
.file
);
936 MPD
.PlayQueue
.queue_commit(server
);
941 private void play_selected_song(Gtk
.Button button
)
943 unowned MPD
.Song? song
= browser_get_selected_song();
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
)
964 * Add a row to a gtk table
965 * <b>$label:</b> $value
969 private void add_entry(Gtk
.Grid table
, string entry_label
, string? value
,Gtk
.Widget? extra
, ref int i
, string ? image
= null)
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
));
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
);
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);
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);
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);
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 */
1022 var hbox
= new Gtk
.HBox(false, 6);
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);
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);
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);
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);
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);
1125 if(song
.artist
!= null)
1127 this
.add_entry(info_box
, _("Artist"), song
.artist
, null,ref i
, "media-artist");
1130 if(song
.album
!= null)
1132 this
.add_entry(info_box
, _("Album"), song
.album
,null, ref i
, "media-album");
1135 if(song
.genre
!= null)
1137 this
.add_entry(info_box
, _("Genre"), song
.genre
,null, ref i
, "media-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");
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",
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);
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);
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);
1238 button
.clicked
.connect((source
) =>
1240 debug("notebook page %i clicked", j
);
1241 notebook
.set_current_page(j
);
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);
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;
1276 text_view
.query_from_song(song
);
1277 text_view_queried
= true;
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);
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();
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();
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);
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();
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 */
1352 var page
= config
.get_int_with_default("MetaData", "song-last-page", 0);
1355 notebook
.set_current_page(0);
1359 /* The list is in reversed order, compensate for that. */
1360 var w
= group
.nth_data(i
-page
-1);
1362 notebook
.set_current_page(page
);
1366 ali
= new Gtk
.Alignment(0.0f
, 0.5f
,0f
,0f
);
1369 /* Create pane in 2. */
1370 var bottom_hbox
= new Gtk
.HBox(false, 6);
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);
1390 master_box
.pack_start(vbox
, false, false, 0);
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(),
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
);
1467 stdout
.printf("song context menu show\n");
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();
1481 vbox
.pack_start(result_hbox
, false, false);
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
;
1507 this
.browser_box
.hide();
1508 show_button
.label
= _("Show sidebar");
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);
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
;
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);
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);
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);
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);
1650 rbutton
.clicked
.connect((source
) =>
1652 debug("notebook page %i clicked", j
);
1653 notebook
.set_current_page(j
);
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
);
1696 stdout
.printf("song context menu show\n");
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);
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
);
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
);
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
);
1734 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ARTIST
, artist
);
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);
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 */
1759 var page
= config
.get_int_with_default("MetaData", "album-last-page", 0);
1762 notebook
.set_current_page(0);
1766 /* The list is in reversed order, compensate for that. */
1767 var w
= group
.nth_data(i
-page
-1);
1769 notebook
.set_current_page(page
);
1773 ali
= new Gtk
.Alignment(0.0f
, 0.5f
,0f
,0f
);
1776 /* Create pane in 2. */
1777 var bottom_hbox
= new Gtk
.HBox(false, 6);
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
1799 * <image> | <array with info>
1802 * <artist info text>
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);
1812 vbox
.border_width
= 8;
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
;
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);
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);
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);
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);
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
);
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);
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();
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
);
1997 stdout
.printf("song context menu show\n");
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);
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
);
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);
2033 if( song
.artist
!= null)
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.
2045 if(this
.model_albums
.get_iter_first(out titer
))
2049 unowned MPD
.Song? asong
= null;
2050 this
.model_albums
.get(titer
,0, &asong
);
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();
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
);
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");
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
);
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
);
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
);
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
,
2121 MPD
.Player
.play(server
);
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
,
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);
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
);
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 */
2183 var page
= config
.get_int_with_default("MetaData", "artist-last-page", 0);
2186 notebook
.set_current_page(0);
2190 /* The list is in reversed order, compensate for that. */
2191 var w
= group
.nth_data(i
-page
-1);
2193 notebook
.set_current_page(page
);
2198 ali
= new Gtk
.Alignment(0.0f
, 0.5f
,0f
,0f
);
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;
2240 string artist
= browser_get_selected_artist();
2241 string album
= browser_get_selected_album();
2242 unowned MPD
.Song? song
= browser_get_selected_song();
2246 /** Add item to history */
2247 var item
= new
Hitem();
2248 item
.song
= song
.copy();
2249 item
.type
= HitemType
.SONG
;
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
;
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
;
2277 metadata_box_show_artist(artist
);
2281 var item
= new
Hitem();
2282 item
.search_string
= null;
2283 item
.type
= HitemType
.BASE
;
2285 metadata_box_show_base();
2288 this
.update_timeout
= 0;
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();
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
)
2309 this
.selected
= true;
2310 this
.browser_init();
2311 container
.add(this
.paned
);
2313 /* update if non selected */
2314 artist
= browser_get_selected_artist();
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
);
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();
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
);
2358 private enum HitemType
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
)
2377 case HitemType
.BASE
:
2380 case HitemType
.ARTIST
:
2381 this
.set_artist(hi
.song
.artist
);
2383 case HitemType
.ALBUM
:
2384 this
.set_album(hi
.song
.artist
, hi
.song
.album
);
2386 case HitemType
.SONG
:
2387 this
.set_song(hi
.song
);
2390 metadata_box_clear();
2394 private void history_previous()
2396 if(history
== null || current
== null)
2400 if(current
.next
== null)
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)
2415 if(current
.prev
== null)
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");
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();
2444 unowned Hitem i
= iter
.data
;
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
;
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
);
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())
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);
2499 hist_box
.attach(ali
, 0, 0, 1, 1);//pack_start(ali, true, true, 0);
2500 //hist_box.attach(ali, 1, 0, 1,1);
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 */
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
)=>
2523 hist_box
.attach(button
, 1, 0, 1,1);//pack_start(button, true, true, 0);
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);
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);
2549 var button
= new Gtk
.Button();//.with_label(album);
2551 var ali
= new Gtk
.Alignment(0.0f
, 0.5f
, 0.0f
, 0.0f
);
2554 var gr
= new Gtk
.Grid();
2555 gr
.set_column_spacing(6);
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
);
2571 hist_box
.attach(button
, 4,0,1,1);//pack_start(button, true, true, 0);
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
);
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);
2626 private void history_add(Hitem hi
)
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
))
2639 /* clean items in the future */
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
);
2658 private void history_clear()
2660 this
.current
= null;
2661 this
.history
= null;
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();
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();
2690 this
.artist_filter_entry
.set_text("");
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();
2709 while((this
.model_filter_artist
).iter_next(ref iter
));
2711 this
.block_update
--;
2714 this
.metadata_box_clear();
2715 this
.metadata_box_update();
2720 set_album(string artist
, string album
)
2722 if(!this
.get_enabled()) return;
2723 this
.block_update
++;
2724 this
.set_artist(artist
);
2726 this
.album_filter_entry
.set_text("");
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();
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();
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
);
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();
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
;
2804 var view
= metadata_box_show_song(song
,true);
2805 this
.metadata_box
.add(view
);
2806 this
.metadata_box
.show_all();
2810 select_browser(Gtk
.TreeView? tree
)
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();
2819 sel
.select_path(path
);
2824 private string get_extension(string path
)
2826 long length
= path
.length
;
2829 for(; i
>0 && (length
-i
) <8; i
--)
2833 retv
= path
.substring(i
+1);
2840 public override void set_enabled(bool state
)
2846 browser_add( Gmpc
.Playlist3
.get_category_tree_view());
2852 if(this
.rref
!= null)
2854 var path
= rref
.get_path();
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();
2861 if(model
.get_iter(out iter
, path
))
2870 if(this
.get_name() != null)
2871 Gmpc
.config
.set_int(this
.get_name(), "enabled", (int)state
);