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()
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(
71 _("Switch to Metadata view"),
72 (Gmpc
.Easy
.Command
.Callback
*)show_metadata_search
,
76 private const int[] version
= {0,0,0};
77 public override unowned
int[] get_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();
97 unowned
int[] indices
= path
.get_indices();
98 config
.set_int(this
.get_name(), "position", indices
[0]);
102 /* Now playing browser */
109 private Gtk
.Paned paned
= null;
110 /* holding the 3 browsers */
111 private Gtk
.Box browser_box
= null;
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;
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;
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
146 private void select_metadata_browser_search(Gtk
.Widget widget
)
148 select_browser(null);
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
);
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
);
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
);
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();
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
) => {
232 if(tree
.get_selection().get_selected(out model
, out iter
)) {
234 model
.get(iter
,7, out artist
);
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();
244 MPD
.PlayQueue
.queue_add_song(server
, data
.song
.file
);
246 }while(data
!= null);
247 MPD
.PlayQueue
.queue_commit(server
);
254 item
= new Gtk
.ImageMenuItem
.from_stock("gtk-media-play",null);
255 item
.activate
.connect((source
) => {
258 if(tree
.get_selection().get_selected(out model
, out iter
)) {
260 model
.get(iter
,7, out artist
);
262 MPD
.PlayQueue
.clear(server
);
263 MPD
.PlayQueue
.add_artist(server
, artist
);
264 MPD
.Player
.play(server
);
270 menu
.popup(null, null, null, event
.button
, event
.time
);
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" */
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)
294 private bool browser_artist_key_press_event(Gtk
.Widget widget
, Gdk
.EventKey event
)
296 unichar uc
= Gdk
.keyval_to_unicode(event
.keyval
);
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);
313 private void album_add_clicked(Gtk
.Widget item
)
315 string artist
= browser_get_selected_artist();
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
);
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
);
337 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ARTIST
, artist
);
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();
347 MPD
.PlayQueue
.queue_add_song(server
, data
.song
.file
);
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
);
371 item
= new Gtk
.ImageMenuItem
.from_stock("gtk-media-play",null);
372 item
.activate
.connect(album_replace_clicked
);
375 menu
.popup(null, null, null, event
.button
, event
.time
);
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" */
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)
400 private bool browser_album_key_press_event(Gtk
.Widget widget
, Gdk
.EventKey event
)
402 unichar uc
= Gdk
.keyval_to_unicode(event
.keyval
);
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);
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) {
426 this
.tree_album
.grab_focus();
428 this
.model_filter_album
.refilter();
433 private void song_add_clicked(Gtk
.Widget item
)
435 unowned MPD
.Song? song
= browser_get_selected_song();
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
);
459 item
= new Gtk
.ImageMenuItem
.from_stock("gtk-media-play",null);
460 item
.activate
.connect(song_replace_clicked
);
463 menu
.popup(null, null, null, event
.button
, event
.time
);
471 private bool browser_button_release_event(Gtk
.Widget widget
, Gdk
.EventButton event
)
473 if(event
.button
== 8) {
477 else if (event
.button
== 9) {
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
);
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
);
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);
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
);
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
);
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
);
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
);
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());
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);
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()
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);
721 private string?
browser_get_selected_album()
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
))
729 model
.get(iter
, 6,out album
, -1);
734 private unowned MPD
.Song?
browser_get_selected_song()
737 var sel
= this
.tree_songs
.get_selection();
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);
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();
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();
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
);
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
;
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();
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
);
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
);
831 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ARTIST
, artist
);
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();
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();
856 MPD
.PlayQueue
.add_song(server
,song
.file
);
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
);
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
);
879 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ARTIST
, artist
);
881 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ALBUM
, album
);
882 var data
= MPD
.Database
.search_commit(server
);
884 data
.sort_album_disc_track();
886 MPD
.PlayQueue
.queue_add_song(server
, data
.song
.file
);
889 MPD
.PlayQueue
.queue_commit(server
);
894 private void play_selected_song(Gtk
.Button button
)
896 unowned MPD
.Song? song
= browser_get_selected_song();
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
){
915 * Add a row to a gtk table
916 * <b>$label:</b> $value
920 private void add_entry(Gtk
.Table table
, string entry_label
, string? value
,Gtk
.Widget? extra
, ref int i
, string ? image
= null)
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
));
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
);
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);
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);
951 table
.attach(extra
, j
,(j
+1),i
,i
+1,Gtk
.AttachOptions
.SHRINK
|Gtk
.AttachOptions
.FILL
, Gtk
.AttachOptions
.SHRINK
|Gtk
.AttachOptions
.FILL
,0,0);
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 */
969 var hbox
= new Gtk
.HBox(false, 6);
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);
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);
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);
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);
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;
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
);
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);
1059 if(song
.artist
!= null) {
1060 this
.add_entry(info_box
, _("Artist"), song
.artist
, null,ref i
, "media-artist");
1063 if(song
.album
!= null) {
1064 this
.add_entry(info_box
, _("Album"), song
.album
,null, ref i
, "media-album");
1067 if(song
.genre
!= null) {
1068 this
.add_entry(info_box
, _("Genre"), song
.genre
,null, ref i
, "media-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");
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",
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);
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);
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);
1166 button
.clicked
.connect((source
) => {
1167 debug("notebook page %i clicked", j
);
1168 notebook
.set_current_page(j
);
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);
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
);
1201 text_view
.query_from_song(song
);
1202 text_view_queried
= true;
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);
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();
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();
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);
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();
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 */
1272 var page
= config
.get_int_with_default("MetaData", "song-last-page", 0);
1275 notebook
.set_current_page(0);
1277 /* The list is in reversed order, compensate for that. */
1278 var w
= group
.nth_data(i
-page
-1);
1280 notebook
.set_current_page(page
);
1284 ali
= new Gtk
.Alignment(0.0f
, 0.5f
,0f
,0f
);
1287 /* Create pane in 2. */
1288 var bottom_hbox
= new Gtk
.HBox(false, 6);
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);
1308 master_box
.pack_start(vbox
, false, false, 0);
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(),
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
);
1376 stdout
.printf("song context menu show\n");
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();
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
;
1412 this
.browser_box
.hide();
1413 show_button
.label
= _("Show sidebar");
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);
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
;
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);
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);
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);
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);
1555 rbutton
.clicked
.connect((source
) => {
1556 debug("notebook page %i clicked", j
);
1557 notebook
.set_current_page(j
);
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
);
1594 stdout
.printf("song context menu show\n");
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);
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
);
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
);
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
);
1631 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ARTIST
, artist
);
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);
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 */
1654 var page
= config
.get_int_with_default("MetaData", "album-last-page", 0);
1657 notebook
.set_current_page(0);
1659 /* The list is in reversed order, compensate for that. */
1660 var w
= group
.nth_data(i
-page
-1);
1662 notebook
.set_current_page(page
);
1666 ali
= new Gtk
.Alignment(0.0f
, 0.5f
,0f
,0f
);
1669 /* Create pane in 2. */
1670 var bottom_hbox
= new Gtk
.HBox(false, 6);
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
1693 * <image> | <array with info>
1696 * <artist info text>
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);
1706 vbox
.border_width
= 8;
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
;
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);
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);
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);
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);
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
);
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);
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();
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
);
1881 stdout
.printf("song context menu show\n");
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);
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
);
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);
1916 if( song
.artist
!= null)
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.
1928 if(this
.model_albums
.get_iter_first(out titer
))
1931 unowned MPD
.Song? asong
= null;
1932 this
.model_albums
.get(titer
,0, &asong
);
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();
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
);
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");
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
);
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
);
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
);
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
,
1997 MPD
.Player
.play(server
);
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
,
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);
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
);
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 */
2053 var page
= config
.get_int_with_default("MetaData", "artist-last-page", 0);
2056 notebook
.set_current_page(0);
2058 /* The list is in reversed order, compensate for that. */
2059 var w
= group
.nth_data(i
-page
-1);
2061 notebook
.set_current_page(page
);
2066 ali
= new Gtk
.Alignment(0.0f
, 0.5f
,0f
,0f
);
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;
2107 string artist
= browser_get_selected_artist();
2108 string album
= browser_get_selected_album();
2109 unowned MPD
.Song? song
= browser_get_selected_song();
2112 /** Add item to history */
2113 var item
= new
Hitem();
2114 item
.song
= song
.copy();
2115 item
.type
= HitemType
.SONG
;
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
;
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
;
2140 metadata_box_show_artist(artist
);
2142 var item
= new
Hitem();
2143 item
.search_string
= null;
2144 item
.type
= HitemType
.BASE
;
2146 metadata_box_show_base();
2149 this
.update_timeout
= 0;
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();
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
)
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
);
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();
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
);
2217 private enum HitemType
{
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
)
2234 case HitemType
.BASE
:
2237 case HitemType
.ARTIST
:
2238 this
.set_artist(hi
.song
.artist
);
2240 case HitemType
.ALBUM
:
2241 this
.set_album(hi
.song
.artist
, hi
.song
.album
);
2243 case HitemType
.SONG
:
2244 this
.set_song(hi
.song
);
2247 metadata_box_clear();
2251 private void history_previous()
2253 if(history
== null || current
== null){
2256 if(current
.next
== null) {
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){
2269 if(current
.prev
== null) {
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");
2283 show_hitem(current
.data
);
2286 private void history_show_list()
2288 var menu
= new Gtk
.Menu();
2289 unowned List
<Hitem?
> iter
= history
.last();
2291 unowned Hitem i
= iter
.data
;
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
;
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
);
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()) {
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);
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);
2351 bc_box
.pack_start(ali
, true, true, 0);
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
)=>{
2372 bc_box
.pack_start(button
, true, true, 0);
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);
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);
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);
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
);
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);
2447 private void history_add(Hitem hi
)
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
)){
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
);
2474 private void history_clear()
2476 this
.current
= null;
2477 this
.history
= null;
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();
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();
2506 this
.artist_filter_entry
.set_text("");
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();
2522 }while((this
.model_filter_artist
).iter_next(ref iter
));
2524 this
.block_update
--;
2527 this
.metadata_box_clear();
2528 this
.metadata_box_update();
2533 set_album(string artist
, string album
)
2535 if(!this
.get_enabled()) return;
2536 this
.block_update
++;
2537 this
.set_artist(artist
);
2539 this
.album_filter_entry
.set_text("");
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();
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();
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
);
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();
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
;
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();
2617 select_browser(Gtk
.TreeView? tree
)
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();
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
){
2638 bg
.modify_bg(Gtk
.StateType
.NORMAL
,this
.paned
.style
.base[Gtk
.StateType
.NORMAL
]);
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
;
2663 for(;i
>0 && (length
-i
) <8;i
--){
2664 if(path
[i
] == '.') {
2665 retv
= path
.substring(i
+1);
2672 public override void set_enabled(bool state
) {
2675 browser_add( Gmpc
.Playlist3
.get_category_tree_view());
2679 if(this
.rref
!= null) {
2680 var path
= rref
.get_path();
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();
2686 if(model
.get_iter(out iter
, path
))
2695 if(this
.get_name() != null)
2696 Gmpc
.config
.set_int(this
.get_name(), "enabled", (int)state
);