1 /* Gnome Music Player Client
2 * Copyright (C) 2011-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.
24 private const bool use_transition_mb2
= Gmpc
.use_transition
;
25 private const string some_unique_name_mb2
= Config
.VERSION
;
26 private const string np2_LOG_DOMAIN
= "NowPlaying";
29 * This plugin implements an 'improved' now playing interface, modeled after the mockup found here
30 * http://gmpclient.org/wiki/GMPC_Mocup
33 /* create teh plugin of type Gmpc.Plugin.Base that implements the Browser interface */
37 public class Nowplaying
: Gmpc
.Plugin
.Base
, Gmpc
.Plugin
.BrowserIface
{
38 private bool theme_colors
= (bool) config
.get_int_with_default("Now Playing", "use-theme-color",1);
39 private string title_color
= config
.get_string_with_default("Now Playing", "title-color", "#4d90dd");
40 private string item_color
= config
.get_string_with_default("Now Playing", "item-color", "#304ab8");
41 private Gdk
.Color background
;
42 private Gdk
.Color foreground
;
44 private Gtk
.Label bitrate_label
= null;
45 private Gtk
.TreeRowReference np_ref
= null;
47 private bool use_backdrop
= (bool) config
.get_int_with_default("Now Playing", "use-backdrop",0);
51 /* Set the plugin as Browser type*/
52 this
.plugin_type
= 2|8;
53 /* Track changed status */
54 gmpcconn
.status_changed
.connect(status_changed
);
55 /* Track connect/disconnect */
56 gmpcconn
.connection_changed
.connect((source
, connect
) => {
57 /* If disconnect update the page */
58 if(connect
== 0 && this
.paned
!= null)
59 this
.update_not_playing();
62 var background
= config
.get_string_with_default("Now Playing",
63 "background-color", "#000");
64 var foreground
= config
.get_string_with_default("Now Playing",
65 "foreground-color", "#FFF");
66 Gdk
.Color
.parse(background
,out this
.background
);
67 Gdk
.Color
.parse(foreground
,out this
.foreground
);
69 /* Register easycommand to switch to this browser */
70 easy_command
.add_entry(
71 _("switch now playing"),
73 _("Switch to Now Playing"),
74 (Gmpc
.Easy
.Command
.Callback
*)select_now_playing_browser
,
77 /* Version of the plugin*/
78 private const int[] version
= {0,0,0};
79 public override unowned
int[] get_version() {
83 public override unowned
string get_name() {
84 return N_("Now Playing");
87 public override void set_enabled(bool state
) {
90 browser_add( Gmpc
.Playlist3
.get_category_tree_view());
94 if(this
.np_ref
!= null) {
95 var path
= np_ref
.get_path();
97 unowned
int[] indices
= path
.get_indices();
98 config
.set_int(this
.get_name(), "position", indices
[0]);
99 Gtk
.ListStore model
= (Gtk
.ListStore
) np_ref
.get_model();
101 if(model
.get_iter(out iter
, path
))
107 if(this
.paned
!= null) {
108 this
.paned
.destroy();
110 this
.song_checksum
= null;
114 if(this
.get_name() != null)
115 Gmpc
.config
.set_int(this
.get_name(), "enabled", (int)state
);
117 /* Save our position in the side-bar */
118 public override void save_yourself() {
119 if(this
.np_ref
!= null) {
120 var path
= np_ref
.get_path();
122 unowned
int[] indices
= path
.get_indices();
123 config
.set_int(this
.get_name(), "position", indices
[0]);
128 /* React MPD's status changed */
131 status_changed(Gmpc
.Connection conn
, MPD
.Server server
, MPD
.Status
.Changed what
)
133 if(!this
.get_enabled())return;
134 if(!this
.selected
) return;
135 /* If the state changes, update. */
136 if((what
&MPD
.Status
.Changed
.STATE
) == MPD
.Status
.Changed
.STATE
)
140 /* If the playlist changed, this might mean that the metadata of the currently playing song changed, update. */
141 else if ((what
&(MPD
.Status
.Changed
.SONGID
|MPD
.Status
.Changed
.PLAYLIST
)) > 0)
145 /* If the bitrate changed, update the bitrate label. */
146 if((what
&(MPD
.Status
.Changed
.BITRATE
|MPD
.Status
.Changed
.AUDIOFORMAT
)) > 0)
148 if(bitrate_label
!= null)
150 var channels
= MPD
.Status
.get_channels(Gmpc
.server
);
151 debug("bitrate changed");
152 var bitrate
= MPD
.Status
.get_bitrate(Gmpc
.server
);
153 bitrate_label
.set_markup(GLib
.Markup
.printf_escaped(
154 "<span color='%s' weight='bold'>%s:</span> %i %s, %.1f %s, %i %s",
155 this
.item_color
, _("Format"),
156 channels
, GLib
.ngettext(N_("Channel"),N_("Channels"), channels
),
157 MPD
.Status
.get_samplerate(Gmpc
.server
)/1000.0, "kHz",
166 private Gtk
.ScrolledWindow paned
= null;
167 private Gtk
.EventBox container
= null;
168 private bool selected
= false;
170 * Browser Interface bindings
172 public void browser_add (Gtk
.Widget category_tree
)
174 Gtk
.TreeView tree
= (Gtk
.TreeView
)category_tree
;
175 Gtk
.ListStore store
= (Gtk
.ListStore
)tree
.get_model();
176 Gtk
.TreeModel model
= tree
.get_model();
178 Gmpc
.Browser
.insert(out iter
, Gmpc
.Playlist
.BrowserType
.TOP
);
179 store
.set(iter
, 0, this
.id
, 1, this
.get_name(), 3, "media-audiofile");
180 /* Create a row reference */
181 this
.np_ref
= new Gtk
.TreeRowReference(model
, model
.get_path(iter
));
183 /* Called by gmpc, telling the plugin to embed itself in container */
184 public void browser_selected (Gtk
.Container container
)
189 this
.selected
= true;
191 container
.add(this
.paned
);
193 container
.show_all();
194 container
.ensure_style();
195 if(this
.theme_colors
) {
196 this
.title_color
= this
.paned
.style
.text
[Gtk
.StateType
.PRELIGHT
].to_string();
197 this
.item_color
= this
.paned
.style
.text
[Gtk
.StateType
.PRELIGHT
].to_string();
200 this
.title_color
= "#fff";
201 this
.item_color
= "#fff";
202 this
.theme_colors
= false;
207 /* Called by gmpc, telling the plugin to remove itself from the container */
208 public void browser_unselected(Gtk
.Container container
)
210 this
.selected
= false;
211 container
.remove(this
.paned
);
215 * If the style changed because f.e. the user switched theme, make sure the correct colouring is kept preserved.
218 private void browser_bg_style_changed(Gtk
.Widget bg
,Gtk
.Style? style
)
220 debug("Change style signal");
221 if(this
.theme_colors
) {
222 this
.title_color
= this
.paned
.style
.text
[Gtk
.StateType
.PRELIGHT
].to_string();
223 this
.item_color
= this
.paned
.style
.text
[Gtk
.StateType
.PRELIGHT
].to_string();
225 this
.change_color_style(this
.container
);
228 * Recursively force a style on widget bg and children.
230 private void change_color_style(Gtk
.Widget bg
)
232 debug("change style");
233 if(bg is Gtk
.Separator
|| bg is Gtk
.Notebook
|| bg is Gtk
.CheckButton
){
238 bg
.modify_bg(Gtk
.StateType
.NORMAL
,this
.paned
.style
.base[Gtk
.StateType
.NORMAL
]);
239 /*bg.modify_base(Gtk.StateType.NORMAL,this.paned.style.mid[Gtk.StateType.NORMAL]);/*
240 // bg.modify_text(Gtk.StateType.NORMAL,this.paned.style.text[Gtk.StateType.NORMAL]);
241 bg.modify_fg(Gtk.StateType.NORMAL,this.paned.style.text[Gtk.StateType.NORMAL]);
242 bg.modify_text(Gtk.StateType.ACTIVE,this.paned.style.light[Gtk.StateType.NORMAL]);
243 bg.modify_fg(Gtk.StateType.ACTIVE,this.paned.style.light[Gtk.StateType.NORMAL]);*/
245 bg
.modify_bg(Gtk
.StateType
.NORMAL
,this
.background
);
246 bg
.modify_base(Gtk
.StateType
.NORMAL
,this
.background
);
247 bg
.modify_text(Gtk
.StateType
.NORMAL
,this
.foreground
);
248 bg
.modify_fg(Gtk
.StateType
.NORMAL
,this
.foreground
);
249 bg
.modify_text(Gtk
.StateType
.ACTIVE
,this
.foreground
);
250 bg
.modify_fg(Gtk
.StateType
.ACTIVE
,this
.foreground
);
253 /* Recurse into children, if the widget can hold children (so is a container */
254 if(bg is Gtk
.Container
){
255 foreach(Gtk
.Widget child
in ((Gtk
.Container
)bg
).get_children())
257 change_color_style(child
);
261 /* Create the basic gui. on initializing */
262 private void browser_init() {
263 if(this
.paned
== null)
266 this
.paned
= new Gtk
.ScrolledWindow(null,null);
267 this
.paned
.set_policy(Gtk
.PolicyType
.AUTOMATIC
, Gtk
.PolicyType
.AUTOMATIC
);
268 this
.paned
.set_shadow_type(Gtk
.ShadowType
.NONE
);
270 this
.container
= new Gmpc
.MetaData
.Widgets
.Backdrop(Gmpc
.MetaData
.Type
.BACKDROP_ART
);
272 this
.container
= new Gtk
.EventBox();
275 this.container.set_focus_hadjustment(this.paned.get_hadjustment());
276 this.container.set_focus_vadjustment(this.paned.get_vadjustment());
278 this
.paned
.style_set
.connect(browser_bg_style_changed
);
279 this
.paned
.add_with_viewport(this
.container
);
280 this
.paned
.get_vadjustment().set("step-increment", 20.0);
281 this
.paned
.show_all();
286 /* Clear the view inside the scrolled window*/
290 var list
= this
.container
.get_children();
291 foreach(Gtk
.Widget child
in list
){
294 bitrate_label
= null;
298 private string get_extension(string path
)
300 long length
= path
.length
;
303 for(;i
>0 && (length
-i
) <8;i
--){
305 retv
= path
.substring(i
+1);
312 * Show the page when playing
314 private string song_checksum
= null;
315 private void update_playing()
317 unowned MPD
.Song? song
= server
.playlist_get_current_song();
319 debug("GMPC Is playing, cannot get this");
321 (this
.container as Gmpc
.MetaData
.Widgets
.Backdrop
).set_song(null);
322 update_not_playing();
325 /* Force it so we won't update when not needed */
326 var checksum
= Gmpc
.Misc
.song_checksum(song
);
327 if(checksum
== this
.song_checksum
)
329 /* No need to update. */
332 GLib
.Timer t
= new GLib
.Timer();
335 (this
.container as Gmpc
.MetaData
.Widgets
.Backdrop
).set_song(song
);
336 this
.song_checksum
= checksum
;
338 var vbox
= new Gtk
.VBox (false,6);
339 vbox
.border_width
= 8;
340 /* Start building the gui */
342 var hbox
= new Gtk
.HBox(false, 6);
343 Gtk
.Alignment ali
= null;
345 int meta_size
= (int)(this
.container
.allocation
.width
*0.20);
346 /* get size based on alloc */
347 meta_size
= int.min(int.max(100, meta_size
), 250);
348 if(config
.get_int_with_default("Interface", "hide-album-art", 0) == 0)
351 ali
= new Gtk
.Alignment(0f
,0f
,0f
,0f
);
352 //ali.set_size_request(meta_size,meta_size);
353 var album_image
= new Gmpc
.MetaData
.Image(Gmpc
.MetaData
.Type
.ALBUM_ART
, meta_size
);
354 album_image
.set_scale_up(true);
355 album_image
.set_squared(false);
356 ali
.add(album_image
);
357 album_image
.update_from_song(song
);
358 hbox
.pack_start(ali
, false, false, 0);
362 if(config
.get_int_with_default("Interface", "hide-album-art", 0) == 0 && !use_backdrop
)
364 ali
= new Gtk
.Alignment(1f
,0f
,0f
,0f
);
365 var artist_image
= new Gmpc
.MetaData
.Image(Gmpc
.MetaData
.Type
.ARTIST_ART
, meta_size
);
366 artist_image
.set_scale_up(true);
367 artist_image
.set_squared(false);
368 artist_image
.update_from_song(song
);
369 ali
.add(artist_image
);
370 hbox
.pack_end(ali
, false, false, 0);
373 /* Information box */
374 var info_vbox
= new Gtk
.VBox(false, 6);
376 if(song
.title
!= null) {
377 var box
= new Gtk
.HBox(false, 6);
380 if(config
.get_int_with_default("Interface", "hide-favorites-icon",0) == 0){
381 var fav_button
= new Gmpc
.Favorites
.Button();
382 fav_button
.set_song(song
);
383 ali
= new Gtk
.Alignment(0.0f
, 0.5f
,0f
,0f
);
385 box
.pack_start(ali
, false, false, 0);
387 var label
= new Gtk
.Label(song
.title
);
388 label
.selectable
= true;
389 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' size='%i' weight='bold'>%s</span>",
390 this
.title_color
,Pango
.SCALE
*20 ,song
.title
));
391 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
392 label
.set_alignment(0.0f
, 0.5f
);
393 box
.pack_start(label
, true, true, 0);
394 info_vbox
.pack_start(box
, false, false, 0);
396 }else if (song
.name
!= null) {
397 var label
= new Gtk
.Label(song
.name
);
398 label
.selectable
= true;
399 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' size='%i' weight='bold'>%s</span>",this
.
400 title_color
, Pango
.SCALE
*20, song
.name
));
401 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
402 label
.set_alignment(0.0f
, 0.5f
);
403 info_vbox
.pack_start(label
, false, false, 0);
405 else if (song
.file
!= null){
406 var filename
= GLib
.Path
.get_basename(song
.file
);
407 var label
= new Gtk
.Label(song
.name
);
408 label
.selectable
= true;
410 var regex_a
= new GLib
.Regex ("\\.[0-9a-zA-Z]*$");
411 filename
= regex_a
.replace_literal (filename
, -1, 0, "");
412 } catch (GLib
.RegexError e
) {
413 GLib
.error("%s", e
.message
);
416 var regex_b
= new GLib
.Regex ("_");
417 filename
= regex_b
.replace_literal (filename
, -1, 0, " ");
418 } catch (GLib
.RegexError e
) {
419 GLib
.error("%s", e
.message
);
421 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' size='%i' weight='bold'>%s</span>",this
.
422 title_color
, Pango
.SCALE
*20, filename
));
423 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
424 label
.set_alignment(0.0f
, 0.5f
);
425 info_vbox
.pack_start(label
, false, false, 0);
429 if(song
.artist
!= null) {
430 var event
= new Gtk
.EventBox();
431 var box
= new Gtk
.HBox(false, 6);
432 var label
= new Gtk
.Label(song
.artist
);
433 event
.set_visible_window(false);
434 label
.selectable
= true;
435 var image
= new Gtk
.Image
.from_icon_name("media-artist", Gtk
.IconSize
.MENU
);
437 box
.pack_start(event
, false, false, 0);
438 label
.set_markup(GLib
.Markup
.printf_escaped("<span size='xx-large' weight='bold'>%s</span>", song
.artist
));
439 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
440 label
.set_alignment(0.0f
, 0.5f
);
441 box
.pack_start(label
, true, true, 0);
442 info_vbox
.pack_start(box
, false, false, 0);
444 event
.set_data("artist",song
.artist
);
445 event
.button_press_event
.connect((widget
, event
) => {
446 string artist
= (string)widget
.get_data
<string>("artist");
447 Gmpc
.Browser
.Metadata
.show_artist(artist
);
452 if(song
.album
!= null) {
453 var event
= new Gtk
.EventBox();
454 var box
= new Gtk
.HBox(false, 6);
455 var label
= new Gtk
.Label(song
.album
);
456 event
.set_visible_window(false);
457 label
.selectable
= true;
458 var image
= new Gtk
.Image
.from_icon_name("media-album", Gtk
.IconSize
.MENU
);
460 box
.pack_start(event
, false, false, 0);
461 label
.set_markup(GLib
.Markup
.printf_escaped("<span size='x-large' weight='bold'>%s %s</span>", song
.album
,
462 (song
.date
!= null)?
"(%s)".printf(song
.date
):""));
463 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
464 label
.set_alignment(0.0f
, 0.5f
);
465 box
.pack_start(label
, true, true, 0);
466 info_vbox
.pack_start(box
, false, false, 0);
470 event
.set_data("artist",song
.artist
);
471 event
.set_data("album",song
.album
);
472 event
.button_press_event
.connect((widget
, event
) => {
473 string artist
= (string)widget
.get_data
<string>("artist");
474 string album
= (string)widget
.get_data
<string>("album");
475 if(artist
!= null && album
!= null) {
476 Gmpc
.Browser
.Metadata
.show_album(artist
,album
);
483 if(song
.genre
!= null) {
484 var box
= new Gtk
.HBox(false, 6);
485 var label
= new Gtk
.Label(song
.title
);
486 label
.selectable
= true;
487 var image
= new Gtk
.Image
.from_icon_name("media-genre", Gtk
.IconSize
.MENU
);
488 box
.pack_start(image
, false, false, 0);
489 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' weight='bold'>%s:</span> %s",
490 this
.item_color
,_("Genre"), song
.genre
));
491 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
492 label
.set_alignment(0.0f
, 0.5f
);
493 box
.pack_start(label
, true, true, 0);
494 info_vbox
.pack_start(box
, false, false, 0);
499 var box
= new Gtk
.HBox(false, 6);
500 var image
= new Gtk
.Image
.from_icon_name("media-format", Gtk
.IconSize
.MENU
);
501 box
.pack_start(image
, false, false, 0);
503 bitrate_label
= new Gtk
.Label(song
.title
);
504 bitrate_label
.selectable
= true;
505 bitrate_label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
506 bitrate_label
.set_alignment(0.0f
, 0.5f
);
508 box
.pack_start(bitrate_label
, true, true, 0);
510 var bitrate
= MPD
.Status
.get_bitrate(Gmpc
.server
);
511 var channels
= MPD
.Status
.get_channels(Gmpc
.server
);
512 bitrate_label
.set_markup(GLib
.Markup
.printf_escaped(
513 "<span color='%s' weight='bold'>%s:</span> %i %s, %.1f %s, %i %s",
514 this
.item_color
, _("Format"),
515 channels
, GLib
.ngettext(N_("Channel"),N_("Channels"), channels
),
516 MPD
.Status
.get_samplerate(Gmpc
.server
)/1000.0, "kHz",
520 info_vbox
.pack_start(box
, false, false, 0);
522 if(song
.file
!= null)
525 string extension
= null;
526 extension
= get_extension(song
.file
);
527 if(extension
!= null)
529 var box
= new Gtk
.HBox(false, 6);
530 var image
= new Gtk
.Image
.from_icon_name("media-codec", Gtk
.IconSize
.MENU
);
531 box
.pack_start(image
, false, false, 0);
533 var label
= new Gtk
.Label(song
.title
);
534 label
.selectable
= true;
535 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
536 label
.set_alignment(0.0f
, 0.5f
);
537 box
.pack_start(label
, true, true, 0);
538 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' weight='bold'>%s:</span> %s",
539 this
.item_color
, _("Codec"),
541 info_vbox
.pack_start(box
, false, false, 0);
546 var box
= new Gtk
.HBox(false, 6);
547 var image
= new Gtk
.Image
.from_icon_name("media-track-length", Gtk
.IconSize
.MENU
);
548 box
.pack_start(image
, false, false, 0);
549 var label
= new Gtk
.Label("");
550 label
.selectable
= true;
551 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
552 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' weight='bold'>%s:</span> %s",
553 this
.item_color
,_("Length"),
554 Gmpc
.Misc
.format_time((ulong) song
.time
, "")));
555 label
.set_alignment(0.0f
, 0.5f
);
556 box
.pack_start(label
, true, true, 0);
557 info_vbox
.pack_start(box
, false, false, 0);
560 if(song
.track
!= null) {
561 var box
= new Gtk
.HBox(false, 6);
562 var image
= new Gtk
.Image
.from_icon_name("media-num-tracks", Gtk
.IconSize
.MENU
);
563 box
.pack_start(image
, false, false, 0);
564 var label
= new Gtk
.Label("");
565 label
.selectable
= true;
566 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
567 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' weight='bold'>%s:</span> %s %s",
568 this
.item_color
, _("Track number"),
570 (song
.disc
!= null)?
"[%s]".printf(song
.disc
):""
572 label
.set_alignment(0.0f
, 0.5f
);
573 box
.pack_start(label
, true, true, 0);
574 info_vbox
.pack_start(box
, false, false, 0);
578 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "heading took: %.6f seconds.", t
.elapsed());
579 hbox
.pack_start(info_vbox
, true, true, 0);
580 vbox
.pack_start(hbox
, false, false, 0);
583 var sep
= new Gtk
.HSeparator();
584 sep
.set_size_request(-1, 4);
585 vbox
.pack_start(sep
, false, false, 0);
587 var hboxje
= new Gtk
.HBox(false, 6);
589 /* Notebook where all the metadata items are kept, Override the tabs by a radiobutton list. */
590 var notebook
= new Gtk
.Notebook();
591 notebook
.set_show_border(false);
592 notebook
.set_show_tabs(false);
596 unowned SList
<unowned Gtk
.RadioButton
> group
= null;
597 if(config
.get_int_with_default("MetaData", "show-lyrics",1) == 1)
599 var alib
= new Gtk
.Alignment(0f
,0f
,1f
,0f
);
600 var text_view
= new Gmpc
.MetaData
.Widgets
.TextLabel(song
, Gmpc
.MetaData
.Type
.SONG_TXT
);
602 notebook
.append_page(alib
, new Gtk
.Label("Lyrics"));
603 var button
= new Gtk
.RadioButton(group
);//.with_label(group,("Lyrics"));
604 var label
= new Gtk
.Label(_("Lyrics"));
605 label
.ellipsize
= Pango
.EllipsizeMode
.END
;
606 label
.set_alignment(0.0f
, 0.5f
);
607 // button.add(label);
610 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
611 group
= button
.get_group();
612 hboxje
.pack_start(button
, false, false, 0);
613 hboxje
.pack_start(label
, true, true, 0);
615 button
.clicked
.connect((source
) => {
616 if((source as Gtk
.CheckButton
).get_active()) {
617 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "lyrics notebook page %i clicked", j
);
618 notebook
.set_current_page(j
);
625 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "lyrics took: %.6f seconds.", t
.elapsed());
628 if(config
.get_int_with_default("MetaData", "show-guitar-tabs",1) == 1)
630 var alib
= new Gtk
.Alignment(0f
,0f
,1f
,0f
);
631 var text_view_queried
= false;
632 notebook
.append_page(alib
, new Gtk
.Label(_("Guitar Tabs")));
633 var button
= new Gtk
.RadioButton(group
);//.with_label(group,_("Guitar Tabs"));
634 var label
= new Gtk
.Label(_("Guitar Tabs"));
635 label
.ellipsize
= Pango
.EllipsizeMode
.END
;
636 label
.set_alignment(0.0f
, 0.5f
);
639 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
640 group
= button
.get_group();
641 hboxje
.pack_start(button
, false, false, 0);
642 hboxje
.pack_start(label
, true, true, 0);
644 /* Only query the guitar-tab when opened or first notebook page*/
645 button
.clicked
.connect((source
) => {
646 if((source as Gtk
.CheckButton
).get_active()) {
647 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "guitar tab notebook page %i clicked", j
);
648 notebook
.set_current_page(j
);
649 if(!text_view_queried
){
650 var text_view
= new Gmpc
.MetaData
.Widgets
.TextLabel(song
, Gmpc
.MetaData
.Type
.SONG_GUITAR_TAB
);
652 text_view_queried
= true;
653 this
.change_color_style(text_view
);
659 var text_view
= new Gmpc
.MetaData
.Widgets
.TextLabel(song
, Gmpc
.MetaData
.Type
.SONG_GUITAR_TAB
);
661 text_view_queried
= true;
666 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "guitar tabs took: %.6f seconds.", t
.elapsed());
670 if(config
.get_int_with_default("MetaData", "show-similar-songs",1) == 1)
672 var similar_songs_queried
= false;
673 var similar_songs_box
= new Gtk
.Alignment(0f
,0f
,0f
,0f
);
675 notebook
.append_page(similar_songs_box
, new Gtk
.Label(_("Similar Songs")));
677 var button
= new Gtk
.RadioButton(group
);//.with_label(group,_("Similar Songs"));
678 var label
= new Gtk
.Label(_("Similar Songs"));
679 label
.ellipsize
= Pango
.EllipsizeMode
.END
;
680 label
.set_alignment(0.0f
, 0.5f
);
683 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
684 group
= button
.get_group();
685 hboxje
.pack_start(button
, false, false, 0);
686 hboxje
.pack_start(label
, true, true, 0);
689 /* Only query when opened or first notebook page*/
690 button
.clicked
.connect((source
) => {
691 if((source as Gtk
.CheckButton
).get_active()) {
692 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "similar song notebook page %i clicked", j
);
693 notebook
.set_current_page(j
);
694 if(!similar_songs_queried
){
695 var similar_songs
= new Gmpc
.MetaData
.Widgets
.SimilarSongs(song
);
696 similar_songs
.update();
697 similar_songs_queried
= true;
698 similar_songs_box
.add(similar_songs
);
699 this
.change_color_style(similar_songs_box
);
700 similar_songs_box
.show_all();
705 var similar_songs
= new Gmpc
.MetaData
.Widgets
.SimilarSongs(song
);
706 similar_songs
.update();
707 similar_songs_queried
= true;
708 similar_songs_box
.add(similar_songs
);
709 similar_songs_box
.show_all();
711 similar_songs_box
.show();
715 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "similar songs took: %.6f seconds.", t
.elapsed());
716 if(config
.get_int_with_default("MetaData", "show-similar-artist",1) == 1 && song
.artist
!= null)
718 var similar_artist
= new Gmpc
.MetaData
.Widgets
.SimilarArtists(Gmpc
.server
,song
);
720 notebook
.append_page(similar_artist
, new Gtk
.Label(_("Similar Artist")));
722 var button
= new Gtk
.RadioButton(group
);//.with_label(group,_("Similar Artist"));
723 var label
= new Gtk
.Label(_("Similar Artist"));
724 label
.ellipsize
= Pango
.EllipsizeMode
.END
;
725 label
.set_alignment(0.0f
, 0.5f
);
728 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
729 group
= button
.get_group();
730 hboxje
.pack_start(button
, false, false, 0);
731 hboxje
.pack_start(label
, true, true, 0);
734 button
.clicked
.connect((source
) => {
735 if((source as Gtk
.CheckButton
).get_active()) {
736 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "similar artist notebook page %i clicked", j
);
737 similar_artist
.first_show();
738 notebook
.set_current_page(j
);
741 similar_artist
.show();
744 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "similar Artist took: %.6f seconds.", t
.elapsed());
745 if(config
.get_int_with_default("MetaData", "show-artist-information",1) == 1)
747 var alib
= new Gtk
.Alignment(0f
,0f
,1f
,0f
);
748 var text_view_queried
= false;
750 notebook
.append_page(alib
, new Gtk
.Label(_("Artist information")));
751 var button
= new Gtk
.RadioButton(group
);//.with_label(group,_("Artist information"));
752 var label
= new Gtk
.Label(_("Artist information"));
753 label
.ellipsize
= Pango
.EllipsizeMode
.END
;
754 label
.set_alignment(0.0f
, 0.5f
);
757 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
758 group
= button
.get_group();
759 hboxje
.pack_start(button
, false, false, 0);
760 hboxje
.pack_start(label
, true, true, 0);
762 /* Only query the guitar-tab when opened or first notebook page*/
763 button
.clicked
.connect((source
) => {
764 if((source as Gtk
.CheckButton
).get_active()) {
765 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "artist info notebook page %i clicked", j
);
766 notebook
.set_current_page(j
);
767 if(!text_view_queried
){
768 var text_view
= new Gmpc
.MetaData
.Widgets
.TextLabel(song
,Gmpc
.MetaData
.Type
.ARTIST_TXT
);
770 text_view_queried
= true;
772 this
.change_color_style(text_view
);
777 var text_view
= new Gmpc
.MetaData
.Widgets
.TextLabel(song
,Gmpc
.MetaData
.Type
.ARTIST_TXT
);
779 text_view_queried
= true;
785 if(config
.get_int_with_default("MetaData", "show-songs-from-album",1) == 1)
787 var alib
= new Gtk
.Alignment(0f
,0f
,1f
,0f
);
788 var text_view_queried
= false;
790 notebook
.append_page(alib
, new Gtk
.Label(_("Songs from album")));
791 var button
= new Gtk
.RadioButton(group
);//.with_label(group,_("Songs from album"));
792 var label
= new Gtk
.Label(_("Songs from album"));
793 label
.ellipsize
= Pango
.EllipsizeMode
.END
;
794 label
.set_alignment(0.0f
, 0.5f
);
797 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
798 group
= button
.get_group();
799 hboxje
.pack_start(button
, false, false, 0);
800 hboxje
.pack_start(label
, true, true, 0);
802 var sl
= new Gmpc
.Widgets
.Songlist();
804 sl
.song_clicked
.connect((source
, song
) => {
805 if(song
.file
!= null) {
806 Gmpc
.MpdInteraction
.play_path(song
.file
);
809 sl
.play_song_clicked
.connect((source
, song
) => {
810 if(song
.file
!= null) {
811 Gmpc
.MpdInteraction
.play_path(song
.file
);
815 button
.clicked
.connect((source
) => {
816 if((source as Gtk
.CheckButton
).get_active()) {
817 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "artist info notebook page %i clicked", j
);
818 notebook
.set_current_page(j
);
819 if(!text_view_queried
){
820 if(song
.artist
!= null && song
.album
!= null)
822 MPD
.Database
.search_start(server
,true);
823 if(song
.albumartist
!= null) {
824 MPD
.Database
.search_add_constraint(server
,
825 MPD
.Tag
.Type
.ALBUM_ARTIST
, song
.albumartist
);
827 MPD
.Database
.search_add_constraint(server
,
828 MPD
.Tag
.Type
.ARTIST
, song
.artist
);
830 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ALBUM
, song
.album
);
831 var data
= MPD
.Database
.search_commit(server
);
832 data
.sort_album_disc_track();
833 sl
.set_from_data((owned
)data
, true);
834 this
.change_color_style(sl
);
837 alib
.add(new Gtk
.Label(_("Not available")));
839 text_view_queried
= true;
844 if(song
.artist
!= null && song
.album
!= null)
846 MPD
.Database
.search_start(server
,true);
847 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ARTIST
, song
.artist
);
848 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ALBUM
, song
.album
);
849 var data
= MPD
.Database
.search_commit(server
);
850 data
.sort_album_disc_track();
851 sl
.set_from_data((owned
)data
, true);
854 alib
.add(new Gtk
.Label(_("Not available")));
856 text_view_queried
= true;
862 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "Artist info took: %.6f seconds.", t
.elapsed());
863 /* Track changed pages */
864 notebook
.notify
["page"].connect((source
,spec
) => {
865 var page
= notebook
.get_current_page();
866 config
.set_int("NowPlaying", "last-page", (int)page
);
869 /* Restore right page */
871 var page
= config
.get_int_with_default("NowPlaying", "last-page", 0);
874 notebook
.set_current_page(0);
876 /* The list is in reversed order, compensate for that. */
877 var w
= group
.nth_data(i
-page
-1);
879 notebook
.set_current_page(page
);
883 ali
= new Gtk
.Alignment(0.0f
, 0.5f
,1f
,0f
);
886 /* Create pane in 2. */
887 var bottom_hbox
= new Gtk
.HBox(false, 6);
889 var metadata_vbox
= new Gtk
.VBox(false, 0);
890 metadata_vbox
.pack_start(ali
, false, false, 0);
891 sep
= new Gtk
.HSeparator();
892 sep
.set_size_request(-1, 1);
893 metadata_vbox
.pack_start(sep
, false, false, 0);
894 metadata_vbox
.pack_start(notebook
, false, false, 0);
896 bottom_hbox
.pack_start(metadata_vbox
, true, true, 0);
898 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "Building now playing took: %.6f seconds.", t
.elapsed());
899 /* Create album list */
900 if(song
.album
!= null && song
.artist
!= null)
902 var sep2
= new Gtk
.VSeparator();
903 sep2
.set_size_request(-1, 4);
904 bottom_hbox
.pack_start(sep2
, false, false, 0);
906 /* Create album list */
907 ali
= new Gtk
.Alignment(0.0f
, 0.0f
, 0.0f
, 0.0f
);
908 var album_hbox
= new Gtk
.VBox(false, 6);
909 album_hbox
.set_size_request(250, -1);
911 bottom_hbox
.pack_start(ali
, false, false, 0);
913 var label
= new Gtk
.Label(song
.artist
);
914 label
.selectable
= true;
915 label
.set_size_request(240, -1);
916 label
.set_markup(GLib
.Markup
.printf_escaped("<span size='x-large' weight='bold' color='%s'>%s</span><span size='x-large' weight='bold'> %s</span>",this
.item_color
,_("Other albums by"), song
.artist
));
917 label
.set_line_wrap_mode(Pango
.WrapMode
.WORD_CHAR
);
918 label
.set_line_wrap(true);
919 label
.set_alignment(0.0f
, 0.5f
);
920 album_hbox
.pack_start(label
, false, false,0);
922 MPD
.Database
.search_field_start(server
, MPD
.Tag
.Type
.ALBUM
);
923 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ARTIST
, song
.artist
);
924 MPD
.Data
.Item list
= null;
925 var data
= MPD
.Database
.search_commit(server
);
927 unowned MPD
.Data
.Item iter
= data
.get_first();
929 if(iter
.tag
== song
.album
){
934 list
.type
= MPD
.Data
.Type
.SONG
;
935 list
.song
= new MPD
.Song();
936 list
.song
.artist
= song
.artist
;
937 list
.song
.album
= iter
.tag
;
939 MPD.Database.search_field_start(server,MPD.Tag.Type.DATE);
940 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, song.artist);
941 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, iter.tag);
942 var ydata = MPD.Database.search_commit(server);
944 list.song.date = ydata.tag;
948 }while(iter
!= null);
951 list
.sort_album_disc_track();
953 unowned MPD
.Data
.Item iter
= list
.get_first();
955 var button
= new Gtk
.Button();
956 button
.set_relief(Gtk
.ReliefStyle
.NONE
);
957 var but_hbox
= new Gtk
.HBox(false, 6);
958 button
.add(but_hbox
);
959 var image
= new Gmpc
.MetaData
.Image(Gmpc
.MetaData
.Type
.ALBUM_ART
, 48);
960 unowned MPD
.Song but_song
= iter
.song
;
961 image
.set_squared(true);
962 image
.update_from_song_delayed(but_song
);
964 but_hbox
.pack_start(image
, false, false, 0);
966 var but_label
= new Gtk
.Label(iter
.song
.album
);
967 but_label
.selectable
= true;
968 but_label
.set_alignment(0.0f
, 0.5f
);
970 if(iter
.song
.date
!= null && iter
.song
.date
.length
> 0) strlabel
+= "%s\n".printf(iter
.song
.date
);
971 if(iter
.song
.album
!= null) strlabel
+= iter
.song
.album
;
972 else strlabel
+= _("No Album");
973 but_label
.set_markup(GLib
.Markup
.printf_escaped("<b>%s</b>",strlabel
));
974 but_label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
975 but_hbox
.pack_start(but_label
, true, true, 0);
977 album_hbox
.pack_start(button
, false, false,0);
978 button
.set_data("artist",
979 but_song
.artist
.dup());
980 button
.set_data("album",
981 but_song
.album
.dup());
982 stdout
.printf("open: %s %s\n", but_song
.artist
, but_song
.album
);
983 button
.clicked
.connect((source
) => {
984 unowned
string artist
= (string)source
.get_data
<string>("artist");
985 unowned
string album
= (string)source
.get_data
<string>("album");
986 stdout
.printf("open: %s %s\n", artist
, album
);
987 Gmpc
.Browser
.Metadata
.show_album(artist
, album
);
996 album_hbox
.destroy();
1000 vbox
.pack_start(bottom_hbox
, true, true, 0);
1003 this
.container
.add(vbox
);
1004 this
.change_color_style(this
.container
);
1005 this
.container
.show_all();
1008 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "Building now playing took: %.6f seconds.", t
.elapsed());
1011 * This shows the page when mpd is not playing, for now it is the gmpc logo + Gnome Music Player Client
1013 private void update_not_playing()
1016 song_checksum
= null;
1018 (this
.container as Gmpc
.MetaData
.Widgets
.Backdrop
).set_song(null);
1020 var it
= Gtk
.IconTheme
.get_default();
1021 Gtk
.IconInfo info
= it
.lookup_icon("gmpc", 150, 0);
1022 var path
= info
.get_filename();
1023 Gtk
.Image image
= null;
1027 var pb
= new Gdk
.Pixbuf
.from_file_at_scale(path
, 150, 150, true);
1028 image
= new Gtk
.Image
.from_pixbuf(pb
);
1031 warning("Failed to load the gmpc logo: %s", e
.message
);
1036 image
= new Gtk
.Image
.from_icon_name("gmpc", Gtk
.IconSize
.DIALOG
);
1039 var hbox
= new Gtk
.HBox(false, 6);
1040 var label
= new Gtk
.Label(_("Gnome Music Player Client"));
1041 label
.set_selectable(true);
1042 label
.set_markup("<span size='%i' weight='bold'>%s</span>".printf(28*Pango
.SCALE
,_("Gnome Music Player Client")));
1043 hbox
.pack_start(image
, false, false, 0);
1044 hbox
.pack_start(label
, false, false, 0);
1046 var ali
= new Gtk
.Alignment(0.5f
,0.5f
,0.0f
, 0.0f
);
1048 this
.container
.add(ali
);
1050 this
.change_color_style(this
.container
);
1051 this
.container
.show_all();
1055 * Update the view according to state. If playing/paused show song info, other wise the not playing page
1057 private void update()
1059 switch(MPD
.Player
.get_state(Gmpc
.server
))
1061 case MPD
.Player
.State
.PLAY
:
1062 case MPD
.Player
.State
.PAUSE
:
1063 debug("Update playing");
1067 debug("update not playing");
1068 update_not_playing();
1074 * Makes gmpc jump to the now playing browser
1076 public void select_now_playing_browser()
1078 unowned Gtk
.TreeView tree
= Gmpc
.Playlist3
.get_category_tree_view();
1079 var sel
= tree
.get_selection();
1080 var path
= np_ref
.get_path();
1083 sel
.select_path(path
);
1088 * Gmpc.Plugin.BrowserIface.add_go_menu
1090 private int browser_add_go_menu(Gtk
.Menu menu
)
1092 if(this
.get_enabled())
1094 var item
= new Gtk
.ImageMenuItem
.with_mnemonic(_("Now Playing"));
1095 item
.set_image(new Gtk
.Image
.from_icon_name("media-audiofile", Gtk
.IconSize
.MENU
));
1096 item
.activate
.connect(select_now_playing_browser
);
1097 item
.add_accelerator("activate", menu
.get_accel_group(),0x069, Gdk
.ModifierType
.CONTROL_MASK
, Gtk
.AccelFlags
.VISIBLE
);