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 */
39 public class Nowplaying
: Gmpc
.Plugin
.Base
, Gmpc
.Plugin
.BrowserIface
41 private int max_albums
= 10;
42 private bool theme_colors
= (bool) config
.get_int_with_default("Now Playing", "use-theme-color",1);
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 Gdk
.Color background
;
46 private Gdk
.Color foreground
;
48 private Gtk
.Label bitrate_label
= null;
49 private Gtk
.TreeRowReference np_ref
= null;
51 private bool use_backdrop
= (bool) config
.get_int_with_default("Now Playing", "use-backdrop",0);
56 /* Set the plugin as Browser type*/
57 this
.plugin_type
= 2|8;
58 /* Track changed status */
59 gmpcconn
.status_changed
.connect(status_changed
);
60 /* Track connect/disconnect */
61 gmpcconn
.connection_changed
.connect((source
, connect
) => {
62 /* If disconnect update the page */
63 if(connect
== 0 && this
.paned
!= null)
64 this
.update_not_playing();
67 var background
= config
.get_string_with_default("Now Playing",
68 "background-color", "#000");
69 var foreground
= config
.get_string_with_default("Now Playing",
70 "foreground-color", "#FFF");
71 Gdk
.Color
.parse(background
,out this
.background
);
72 Gdk
.Color
.parse(foreground
,out this
.foreground
);
74 /* Register easycommand to switch to this browser */
75 easy_command
.add_entry(
76 _("switch now playing"),
78 _("Switch to Now Playing"),
79 (Gmpc
.Easy
.Command
.Callback
*)select_now_playing_browser
,
82 /* Version of the plugin*/
83 private const int[] version
= {0,0,0};
84 public override unowned
int[] get_version()
89 public override unowned
string get_name()
91 return N_("Now Playing");
94 public override void set_enabled(bool state
)
100 browser_add( Gmpc
.Playlist3
.get_category_tree_view());
106 if(this
.np_ref
!= null)
108 var path
= np_ref
.get_path();
111 unowned
int[] indices
= path
.get_indices();
112 config
.set_int(this
.get_name(), "position", indices
[0]);
113 Gtk
.ListStore model
= (Gtk
.ListStore
) np_ref
.get_model();
115 if(model
.get_iter(out iter
, path
))
121 if(this
.paned
!= null)
123 this
.paned
.destroy();
125 this
.song_checksum
= null;
129 if(this
.get_name() != null)
130 Gmpc
.config
.set_int(this
.get_name(), "enabled", (int)state
);
132 /* Save our position in the side-bar */
133 public override void save_yourself()
135 if(this
.np_ref
!= null)
137 var path
= np_ref
.get_path();
140 unowned
int[] indices
= path
.get_indices();
141 config
.set_int(this
.get_name(), "position", indices
[0]);
146 /* React MPD's status changed */
149 status_changed(Gmpc
.Connection conn
, MPD
.Server server
, MPD
.Status
.Changed what
)
151 if(!this
.get_enabled())return;
152 if(!this
.selected
) return;
153 /* If the state changes, update. */
154 if((what
&MPD
.Status
.Changed
.STATE
) == MPD
.Status
.Changed
.STATE
)
158 /* If the playlist changed, this might mean that the metadata of the currently playing song changed, update. */
159 else if ((what
&(MPD
.Status
.Changed
.SONGID
|MPD
.Status
.Changed
.PLAYLIST
)) > 0)
163 /* If the bitrate changed, update the bitrate label. */
164 if((what
&(MPD
.Status
.Changed
.BITRATE
|MPD
.Status
.Changed
.AUDIOFORMAT
)) > 0)
166 if(bitrate_label
!= null)
168 var channels
= MPD
.Status
.get_channels(Gmpc
.server
);
169 debug("bitrate changed");
170 var bitrate
= MPD
.Status
.get_bitrate(Gmpc
.server
);
171 bitrate_label
.set_markup(GLib
.Markup
.printf_escaped(
172 "<span color='%s' weight='bold'>%s:</span> %i %s, %.1f %s, %i %s",
173 this
.item_color
, _("Format"),
174 channels
, GLib
.ngettext(N_("Channel"),N_("Channels"), channels
),
175 MPD
.Status
.get_samplerate(Gmpc
.server
)/1000.0, "kHz",
184 private Gtk
.ScrolledWindow paned
= null;
185 private Gtk
.EventBox container
= null;
186 private bool selected
= false;
188 * Browser Interface bindings
190 public void browser_add (Gtk
.Widget category_tree
)
192 Gtk
.TreeView tree
= (Gtk
.TreeView
)category_tree
;
193 Gtk
.ListStore store
= (Gtk
.ListStore
)tree
.get_model();
194 Gtk
.TreeModel model
= tree
.get_model();
196 Gmpc
.Browser
.insert(out iter
, Gmpc
.Playlist
.BrowserType
.TOP
);
197 store
.set(iter
, 0, this
.id
, 1, this
.get_name(), 3, "media-audiofile");
198 /* Create a row reference */
199 this
.np_ref
= new Gtk
.TreeRowReference(model
, model
.get_path(iter
));
201 /* Called by gmpc, telling the plugin to embed itself in container */
202 public void browser_selected (Gtk
.Container container
)
207 this
.selected
= true;
209 container
.add(this
.paned
);
211 container
.show_all();
212 container
.ensure_style();
213 if(this
.theme_colors
)
215 this
.title_color
= this
.paned
.style
.text
[Gtk
.StateType
.PRELIGHT
].to_string();
216 this
.item_color
= this
.paned
.style
.text
[Gtk
.StateType
.PRELIGHT
].to_string();
220 this
.title_color
= "#fff";
221 this
.item_color
= "#fff";
222 this
.theme_colors
= false;
227 /* Called by gmpc, telling the plugin to remove itself from the container */
228 public void browser_unselected(Gtk
.Container container
)
230 this
.selected
= false;
231 container
.remove(this
.paned
);
235 * If the style changed because f.e. the user switched theme, make sure the correct colouring is kept preserved.
238 private void browser_bg_style_changed(Gtk
.Widget bg
,Gtk
.Style? style
)
240 debug("Change style signal");
241 if(this
.theme_colors
)
243 this
.title_color
= this
.paned
.style
.text
[Gtk
.StateType
.PRELIGHT
].to_string();
244 this
.item_color
= this
.paned
.style
.text
[Gtk
.StateType
.PRELIGHT
].to_string();
246 this
.change_color_style(this
.container
);
249 * Recursively force a style on widget bg and children.
251 private void change_color_style(Gtk
.Widget bg
)
253 debug("change style");
254 if( bg is Gtk
.Notebook
|| bg is Gtk
.CheckButton
||
255 bg is Gtk
.Image
|| bg is Gmpc
.DataView
|| bg is Gtk
.Container
)
259 else if (bg is Gtk
.Separator
)
261 bg
.modify_fg(Gtk
.StateType
.NORMAL
,this
.foreground
);
267 bg
.modify_bg(Gtk
.StateType
.NORMAL
,this
.paned
.style
.base[Gtk
.StateType
.NORMAL
]);
268 /*bg.modify_base(Gtk.StateType.NORMAL,this.paned.style.mid[Gtk.StateType.NORMAL]);/*
269 // bg.modify_text(Gtk.StateType.NORMAL,this.paned.style.text[Gtk.StateType.NORMAL]);
270 bg.modify_fg(Gtk.StateType.NORMAL,this.paned.style.text[Gtk.StateType.NORMAL]);
271 bg.modify_text(Gtk.StateType.ACTIVE,this.paned.style.light[Gtk.StateType.NORMAL]);
272 bg.modify_fg(Gtk.StateType.ACTIVE,this.paned.style.light[Gtk.StateType.NORMAL]);*/
276 if(!( bg is Gtk
.Label
)) {
277 bg
.modify_bg(Gtk
.StateType
.NORMAL
,this
.background
);
278 bg
.modify_base(Gtk
.StateType
.NORMAL
,this
.background
);
280 bg
.modify_text(Gtk
.StateType
.NORMAL
,this
.foreground
);
281 bg
.modify_fg(Gtk
.StateType
.NORMAL
,this
.foreground
);
282 bg
.modify_text(Gtk
.StateType
.ACTIVE
,this
.foreground
);
283 bg
.modify_fg(Gtk
.StateType
.ACTIVE
,this
.foreground
);
286 /* Recurse into children, if the widget can hold children (so is a container */
287 if(bg is Gtk
.Container
)
289 foreach(Gtk
.Widget child
in ((Gtk
.Container
)bg
).get_children())
291 change_color_style(child
);
295 /* Create the basic gui. on initializing */
296 private void browser_init()
298 if(this
.paned
== null)
301 this
.paned
= new Gtk
.ScrolledWindow(null,null);
302 this
.paned
.set_policy(Gtk
.PolicyType
.AUTOMATIC
, Gtk
.PolicyType
.AUTOMATIC
);
303 this
.paned
.set_shadow_type(Gtk
.ShadowType
.ETCHED_IN
);
306 this
.container
= new Gmpc
.MetaData
.Widgets
.Backdrop(Gmpc
.MetaData
.Type
.BACKDROP_ART
);
310 this
.container
= new Gtk
.EventBox();
313 this
.container
.key_press_event
.connect((source
, event
)=>{
314 if(event
.keyval
== Gdk
.Key_j
&&
315 !((event
.state
&Gdk
.ModifierType
.CONTROL_MASK
) ==
316 Gdk
.ModifierType
.CONTROL_MASK
))
318 var va
= this
.paned
.get_vadjustment();
319 va
.set_value(va
.get_value()+va
.get_step_increment());
321 else if(event
.keyval
== Gdk
.Key_k
&&
322 !((event
.state
&Gdk
.ModifierType
.CONTROL_MASK
) ==
323 Gdk
.ModifierType
.CONTROL_MASK
))
325 var va
= this
.paned
.get_vadjustment();
326 va
.set_value(va
.get_value()-va
.get_step_increment());
331 this.container.set_focus_hadjustment(this.paned.get_hadjustment());
332 this.container.set_focus_vadjustment(this.paned.get_vadjustment());
334 this
.paned
.style_set
.connect(browser_bg_style_changed
);
335 this
.paned
.add_with_viewport(this
.container
);
336 this
.paned
.get_vadjustment().set("step-increment", 20.0);
337 this
.paned
.show_all();
342 /* Clear the view inside the scrolled window*/
346 var list
= this
.container
.get_children();
347 foreach(Gtk
.Widget child
in list
)
351 bitrate_label
= null;
355 private string get_extension(string path
)
357 long length
= path
.length
;
360 for(; i
>0 && (length
-i
) <8; i
--)
364 retv
= path
.substring(i
+1);
371 * Show the page when playing
373 private string song_checksum
= null;
374 private void update_playing()
376 unowned MPD
.Song? song
= server
.playlist_get_current_song();
379 debug("GMPC Is playing, cannot get this");
381 (this
.container as Gmpc
.MetaData
.Widgets
.Backdrop
).set_song(null);
382 update_not_playing();
385 /* Force it so we won't update when not needed */
386 var checksum
= Gmpc
.Misc
.song_checksum(song
);
387 if(checksum
== this
.song_checksum
)
389 /* No need to update. */
392 GLib
.Timer t
= new GLib
.Timer();
395 (this
.container as Gmpc
.MetaData
.Widgets
.Backdrop
).set_song(song
);
396 this
.song_checksum
= checksum
;
398 var vbox
= new Gtk
.VBox (false,6);
399 vbox
.border_width
= 8;
400 /* Start building the gui */
402 var hbox
= new Gtk
.HBox(false, 6);
403 Gtk
.Alignment ali
= null;
405 int meta_size
= (int)(this
.container
.get_allocated_width()*0.20);
406 /* get size based on alloc */
407 meta_size
= int.min(int.max(100, meta_size
), 250);
408 if(config
.get_int_with_default("Interface", "hide-album-art", 0) == 0)
411 ali
= new Gtk
.Alignment(0f
,0f
,0f
,0f
);
412 //ali.set_size_request(meta_size,meta_size);
413 var album_image
= new Gmpc
.MetaData
.Image(Gmpc
.MetaData
.Type
.ALBUM_ART
, meta_size
);
414 album_image
.set_scale_up(true);
415 album_image
.set_squared(false);
416 ali
.add(album_image
);
417 album_image
.update_from_song(song
);
418 hbox
.pack_start(ali
, false, false, 0);
422 if(config
.get_int_with_default("Interface", "hide-album-art", 0) == 0 && !use_backdrop
)
424 ali
= new Gtk
.Alignment(1f
,0f
,0f
,0f
);
425 var artist_image
= new Gmpc
.MetaData
.Image(Gmpc
.MetaData
.Type
.ARTIST_ART
, meta_size
);
426 artist_image
.set_scale_up(true);
427 artist_image
.set_squared(false);
428 artist_image
.update_from_song(song
);
429 ali
.add(artist_image
);
430 hbox
.pack_end(ali
, false, false, 0);
433 /* Information box */
434 var info_vbox
= new Gtk
.VBox(false, 6);
436 if(song
.title
!= null)
438 var box
= new Gtk
.HBox(false, 6);
441 if(config
.get_int_with_default("Interface", "hide-favorites-icon",0) == 0)
443 var fav_button
= new Gmpc
.Favorites
.Button();
444 fav_button
.set_song(song
);
445 ali
= new Gtk
.Alignment(0.0f
, 0.5f
,0f
,0f
);
447 box
.pack_start(ali
, false, false, 0);
449 var label
= new Gtk
.Label(song
.title
);
450 label
.selectable
= true;
451 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' size='%i' weight='bold'>%s</span>",
452 this
.title_color
,Pango
.SCALE
*20 ,song
.title
));
453 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
454 label
.set_alignment(0.0f
, 0.5f
);
455 box
.pack_start(label
, true, true, 0);
456 info_vbox
.pack_start(box
, false, false, 0);
458 if(MPD
.Sticker
.supported(server
))
461 var rating_button
= new Gmpc
.Rating(server
, song
);
462 ali
= new Gtk
.Alignment(0.0f
, 0.5f
,0f
,0f
);
463 ali
.add(rating_button
);
464 info_vbox
.pack_start(ali
, false, false, 0);
467 else if (song
.name
!= null)
469 var label
= new Gtk
.Label(song
.name
);
470 label
.selectable
= true;
471 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' size='%i' weight='bold'>%s</span>",this
.
472 title_color
, Pango
.SCALE
*20, song
.name
));
473 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
474 label
.set_alignment(0.0f
, 0.5f
);
475 info_vbox
.pack_start(label
, false, false, 0);
477 else if (song
.file
!= null)
479 var filename
= GLib
.Path
.get_basename(song
.file
);
480 var label
= new Gtk
.Label(song
.name
);
481 label
.selectable
= true;
484 var regex_a
= new GLib
.Regex ("\\.[0-9a-zA-Z]*$");
485 filename
= regex_a
.replace_literal (filename
, -1, 0, "");
487 catch (GLib
.RegexError e
)
489 GLib
.error("%s", e
.message
);
493 var regex_b
= new GLib
.Regex ("_");
494 filename
= regex_b
.replace_literal (filename
, -1, 0, " ");
496 catch (GLib
.RegexError e
)
498 GLib
.error("%s", e
.message
);
500 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' size='%i' weight='bold'>%s</span>",this
.
501 title_color
, Pango
.SCALE
*20, filename
));
502 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
503 label
.set_alignment(0.0f
, 0.5f
);
504 info_vbox
.pack_start(label
, false, false, 0);
508 if(song
.artist
!= null)
510 var event
= new Gtk
.EventBox();
511 var box
= new Gtk
.HBox(false, 6);
512 var label
= new Gtk
.Label(song
.artist
);
513 event
.set_visible_window(false);
514 label
.selectable
= true;
515 var image
= new Gtk
.Image
.from_icon_name("media-artist", Gtk
.IconSize
.MENU
);
517 box
.pack_start(event
, false, false, 0);
518 label
.set_markup(GLib
.Markup
.printf_escaped("<span size='xx-large' weight='bold'>%s</span>", song
.artist
));
519 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
520 label
.set_alignment(0.0f
, 0.5f
);
521 box
.pack_start(label
, true, true, 0);
522 info_vbox
.pack_start(box
, false, false, 0);
524 event
.set_data("artist",song
.artist
);
525 event
.button_press_event
.connect((widget
, event
) =>
527 string artist
= (string)widget
.get_data
<string>("artist");
528 Gmpc
.Browser
.Metadata
.show_artist(artist
);
533 if(song
.album
!= null)
535 var event
= new Gtk
.EventBox();
536 var box
= new Gtk
.HBox(false, 6);
537 var label
= new Gtk
.Label(song
.album
);
538 event
.set_visible_window(false);
539 label
.selectable
= true;
540 var image
= new Gtk
.Image
.from_icon_name("media-album", Gtk
.IconSize
.MENU
);
542 box
.pack_start(event
, false, false, 0);
543 label
.set_markup(GLib
.Markup
.printf_escaped("<span size='x-large' weight='bold'>%s %s</span>", song
.album
,
544 (song
.date
!= null)?
"(%s)".printf(song
.date
):""));
545 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
546 label
.set_alignment(0.0f
, 0.5f
);
547 box
.pack_start(label
, true, true, 0);
548 info_vbox
.pack_start(box
, false, false, 0);
552 event
.set_data("artist",song
.artist
);
553 event
.set_data("album",song
.album
);
554 event
.button_press_event
.connect((widget
, event
) =>
556 string artist
= (string)widget
.get_data
<string>("artist");
557 string album
= (string)widget
.get_data
<string>("album");
558 if(artist
!= null && album
!= null)
560 Gmpc
.Browser
.Metadata
.show_album(artist
,album
);
567 if(song
.genre
!= null)
569 var box
= new Gtk
.HBox(false, 6);
570 var label
= new Gtk
.Label(song
.title
);
571 label
.selectable
= true;
572 var image
= new Gtk
.Image
.from_icon_name("media-genre", Gtk
.IconSize
.MENU
);
573 box
.pack_start(image
, false, false, 0);
574 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' weight='bold'>%s:</span> %s",
575 this
.item_color
,_("Genre"), song
.genre
));
576 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
577 label
.set_alignment(0.0f
, 0.5f
);
578 box
.pack_start(label
, true, true, 0);
579 info_vbox
.pack_start(box
, false, false, 0);
584 var box
= new Gtk
.HBox(false, 6);
585 var image
= new Gtk
.Image
.from_icon_name("media-format", Gtk
.IconSize
.MENU
);
586 box
.pack_start(image
, false, false, 0);
588 bitrate_label
= new Gtk
.Label(song
.title
);
589 bitrate_label
.selectable
= true;
590 bitrate_label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
591 bitrate_label
.set_alignment(0.0f
, 0.5f
);
593 box
.pack_start(bitrate_label
, true, true, 0);
595 var bitrate
= MPD
.Status
.get_bitrate(Gmpc
.server
);
596 var channels
= MPD
.Status
.get_channels(Gmpc
.server
);
597 bitrate_label
.set_markup(GLib
.Markup
.printf_escaped(
598 "<span color='%s' weight='bold'>%s:</span> %i %s, %.1f %s, %i %s",
599 this
.item_color
, _("Format"),
600 channels
, GLib
.ngettext(N_("Channel"),N_("Channels"), channels
),
601 MPD
.Status
.get_samplerate(Gmpc
.server
)/1000.0, "kHz",
605 info_vbox
.pack_start(box
, false, false, 0);
607 if(song
.file
!= null)
610 string extension
= null;
611 extension
= get_extension(song
.file
);
612 if(extension
!= null)
614 var box
= new Gtk
.HBox(false, 6);
615 var image
= new Gtk
.Image
.from_icon_name("media-codec", Gtk
.IconSize
.MENU
);
616 box
.pack_start(image
, false, false, 0);
618 var label
= new Gtk
.Label(song
.title
);
619 label
.selectable
= true;
620 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
621 label
.set_alignment(0.0f
, 0.5f
);
622 box
.pack_start(label
, true, true, 0);
623 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' weight='bold'>%s:</span> %s",
624 this
.item_color
, _("Codec"),
626 info_vbox
.pack_start(box
, false, false, 0);
632 var box
= new Gtk
.HBox(false, 6);
633 var image
= new Gtk
.Image
.from_icon_name("media-track-length", Gtk
.IconSize
.MENU
);
634 box
.pack_start(image
, false, false, 0);
635 var label
= new Gtk
.Label("");
636 label
.selectable
= true;
637 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
638 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' weight='bold'>%s:</span> %s",
639 this
.item_color
,_("Length"),
640 Gmpc
.Misc
.format_time((ulong) song
.time
, "")));
641 label
.set_alignment(0.0f
, 0.5f
);
642 box
.pack_start(label
, true, true, 0);
643 info_vbox
.pack_start(box
, false, false, 0);
646 if(song
.track
!= null)
648 var box
= new Gtk
.HBox(false, 6);
649 var image
= new Gtk
.Image
.from_icon_name("media-num-tracks", Gtk
.IconSize
.MENU
);
650 box
.pack_start(image
, false, false, 0);
651 var label
= new Gtk
.Label("");
652 label
.selectable
= true;
653 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
654 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' weight='bold'>%s:</span> %s %s",
655 this
.item_color
, _("Track number"),
657 (song
.disc
!= null)?
"[%s]".printf(song
.disc
):""
659 label
.set_alignment(0.0f
, 0.5f
);
660 box
.pack_start(label
, true, true, 0);
661 info_vbox
.pack_start(box
, false, false, 0);
665 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "heading took: %.6f seconds.", t
.elapsed());
666 hbox
.pack_start(info_vbox
, true, true, 0);
667 vbox
.pack_start(hbox
, false, false, 0);
670 var sep
= new Gtk
.HSeparator();
671 sep
.set_size_request(-1, 4);
672 vbox
.pack_start(sep
, false, false, 0);
674 var hboxje
= new Gtk
.HBox(false, 6);
676 /* Notebook where all the metadata items are kept, Override the tabs by a radiobutton list. */
677 var notebook
= new Gtk
.Notebook();
678 notebook
.set_show_border(false);
679 notebook
.set_show_tabs(false);
683 unowned SList
<unowned Gtk
.RadioButton
> group
= null;
684 if(config
.get_int_with_default("MetaData", "show-lyrics",1) == 1)
686 var alib
= new Gtk
.Alignment(0f
,0f
,1f
,0f
);
687 var text_view
= new Gmpc
.MetaData
.Widgets
.TextLabel(song
, Gmpc
.MetaData
.Type
.SONG_TXT
);
689 notebook
.append_page(alib
, new Gtk
.Label("Lyrics"));
690 var button
= new Gtk
.RadioButton
.with_label(group
,("Lyrics"));
691 ((button as Gtk
.Bin
).get_child() as
692 Gtk
.Label
).set_ellipsize(Pango
.EllipsizeMode
.END
);
695 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
696 group
= button
.get_group();
697 hboxje
.pack_start(button
, false, false, 0);
699 button
.clicked
.connect((source
) =>
701 if((source as Gtk
.CheckButton
).get_active())
703 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "lyrics notebook page %i clicked", j
);
704 notebook
.set_current_page(j
);
711 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "lyrics took: %.6f seconds.", t
.elapsed());
713 /* Album information */
714 if(config
.get_int_with_default("MetaData", "show-album-information",1) == 1)
716 var alib
= new Gtk
.Alignment(0f
,0f
,1f
,0f
);
717 var text_view
= new Gmpc
.MetaData
.Widgets
.TextLabel(song
, Gmpc
.MetaData
.Type
.ALBUM_TXT
);
719 notebook
.append_page(alib
, new Gtk
.Label("Album information"));
720 var button
= new Gtk
.RadioButton
.with_label(group
,_("Album information"));
721 ((button as Gtk
.Bin
).get_child() as
722 Gtk
.Label
).set_ellipsize(Pango
.EllipsizeMode
.END
);
725 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
726 group
= button
.get_group();
727 hboxje
.pack_start(button
, false, false, 0);
729 button
.clicked
.connect((source
) =>
731 if((source as Gtk
.CheckButton
).get_active())
733 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "lyrics notebook page %i clicked", j
);
734 notebook
.set_current_page(j
);
743 if(config
.get_int_with_default("MetaData", "show-guitar-tabs",1) == 1)
745 var alib
= new Gtk
.Alignment(0f
,0f
,1f
,0f
);
746 var text_view_queried
= false;
747 notebook
.append_page(alib
, new Gtk
.Label(_("Guitar Tabs")));
748 var button
= new Gtk
.RadioButton
.with_label(group
,_("Guitar Tabs"));
749 ((button as Gtk
.Bin
).get_child() as
750 Gtk
.Label
).set_ellipsize(Pango
.EllipsizeMode
.END
);
754 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
755 group
= button
.get_group();
756 hboxje
.pack_start(button
, false, false, 0);
758 /* Only query the guitar-tab when opened or first notebook page*/
759 button
.clicked
.connect((source
) =>
761 if((source as Gtk
.CheckButton
).get_active())
763 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "guitar tab notebook page %i clicked", j
);
764 notebook
.set_current_page(j
);
765 if(!text_view_queried
)
767 var text_view
= new Gmpc
.MetaData
.Widgets
.TextLabel(song
, Gmpc
.MetaData
.Type
.SONG_GUITAR_TAB
);
769 text_view_queried
= true;
770 this
.change_color_style(text_view
);
777 var text_view
= new Gmpc
.MetaData
.Widgets
.TextLabel(song
, Gmpc
.MetaData
.Type
.SONG_GUITAR_TAB
);
779 text_view_queried
= true;
784 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "guitar tabs took: %.6f seconds.", t
.elapsed());
788 if(config
.get_int_with_default("MetaData", "show-similar-songs",1) == 1)
790 var similar_songs_queried
= false;
791 var similar_songs_box
= new Gtk
.Alignment(0f
,0f
,1f
,0f
);
793 notebook
.append_page(similar_songs_box
, new Gtk
.Label(_("Similar Songs")));
795 var button
= new Gtk
.RadioButton
.with_label(group
,_("Similar Songs"));
796 ((button as Gtk
.Bin
).get_child() as
797 Gtk
.Label
).set_ellipsize(Pango
.EllipsizeMode
.END
);
799 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
800 group
= button
.get_group();
801 hboxje
.pack_start(button
, false, false, 0);
804 /* Only query when opened or first notebook page*/
805 button
.clicked
.connect((source
) =>
807 if((source as Gtk
.CheckButton
).get_active())
809 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "similar song notebook page %i clicked", j
);
810 notebook
.set_current_page(j
);
811 if(!similar_songs_queried
)
813 var similar_songs
= new Gmpc
.MetaData
.Widgets
.SimilarSongs(song
);
814 similar_songs
.update();
815 similar_songs_queried
= true;
816 similar_songs_box
.add(similar_songs
);
817 this
.change_color_style(similar_songs_box
);
818 similar_songs_box
.show_all();
824 var similar_songs
= new Gmpc
.MetaData
.Widgets
.SimilarSongs(song
);
825 similar_songs
.update();
826 similar_songs_queried
= true;
827 similar_songs_box
.add(similar_songs
);
828 similar_songs_box
.show_all();
830 similar_songs_box
.show();
834 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "similar songs took: %.6f seconds.", t
.elapsed());
835 if(config
.get_int_with_default("MetaData", "show-similar-artist",1) == 1 && song
.artist
!= null)
837 var similar_artist
= new Gmpc
.MetaData
.Widgets
.SimilarArtists(Gmpc
.server
,song
);
839 notebook
.append_page(similar_artist
, new Gtk
.Label(_("Similar Artists")));
841 var button
= new Gtk
.RadioButton
.with_label(group
,_("Similar Artists"));
842 ((button as Gtk
.Bin
).get_child() as
843 Gtk
.Label
).set_ellipsize(Pango
.EllipsizeMode
.END
);
845 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
846 group
= button
.get_group();
847 hboxje
.pack_start(button
, false, false, 0);
850 button
.clicked
.connect((source
) =>
852 if((source as Gtk
.CheckButton
).get_active())
854 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "similar artist notebook page %i clicked", j
);
855 similar_artist
.first_show();
856 notebook
.set_current_page(j
);
859 similar_artist
.show();
862 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "similar Artist took: %.6f seconds.", t
.elapsed());
863 if(config
.get_int_with_default("MetaData", "show-artist-information",1) == 1)
865 var alib
= new Gtk
.Alignment(0f
,0f
,1f
,0f
);
866 var text_view_queried
= false;
868 notebook
.append_page(alib
, new Gtk
.Label(_("Artist information")));
869 var button
= new Gtk
.RadioButton
.with_label(group
,_("Artist information"));
870 ((button as Gtk
.Bin
).get_child() as
871 Gtk
.Label
).set_ellipsize(Pango
.EllipsizeMode
.END
);
873 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
874 group
= button
.get_group();
875 hboxje
.pack_start(button
, false, false, 0);
877 /* Only query the guitar-tab when opened or first notebook page*/
878 button
.clicked
.connect((source
) =>
880 if((source as Gtk
.CheckButton
).get_active())
882 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "artist info notebook page %i clicked", j
);
883 notebook
.set_current_page(j
);
884 if(!text_view_queried
)
886 var text_view
= new Gmpc
.MetaData
.Widgets
.TextLabel(song
,Gmpc
.MetaData
.Type
.ARTIST_TXT
);
888 text_view_queried
= true;
890 this
.change_color_style(text_view
);
896 var text_view
= new Gmpc
.MetaData
.Widgets
.TextLabel(song
,Gmpc
.MetaData
.Type
.ARTIST_TXT
);
898 text_view_queried
= true;
904 if(config
.get_int_with_default("MetaData", "show-songs-from-album",1) == 1)
906 var alib
= new Gtk
.Alignment(0f
,0f
,1f
,0f
);
907 var text_view_queried
= false;
909 notebook
.append_page(alib
, new Gtk
.Label(_("Songs from album")));
910 var button
= new Gtk
.RadioButton
.with_label(group
,_("Songs from album"));
911 ((button as Gtk
.Bin
).get_child() as
912 Gtk
.Label
).set_ellipsize(Pango
.EllipsizeMode
.END
);
914 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
915 group
= button
.get_group();
916 hboxje
.pack_start(button
, false, false, 0);
918 var sl
= new Gmpc
.DataView("now-playing-song-from-album");
920 var sl_model
= new Gmpc
.MpdData
.Model();
921 sl
.set_model(sl_model
);
924 button
.clicked
.connect((source
) =>
926 if((source as Gtk
.CheckButton
).get_active())
928 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "artist info notebook page %i clicked", j
);
929 notebook
.set_current_page(j
);
930 if(!text_view_queried
)
932 if(song
.artist
!= null && song
.album
!= null)
934 MPD
.Database
.search_start(server
,true);
935 if(song
.albumartist
!= null)
937 MPD
.Database
.search_add_constraint(server
,
938 MPD
.Tag
.Type
.ALBUM_ARTIST
, song
.albumartist
);
942 MPD
.Database
.search_add_constraint(server
,
943 MPD
.Tag
.Type
.ARTIST
, song
.artist
);
945 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ALBUM
, song
.album
);
946 var data
= MPD
.Database
.search_commit(server
);
947 data
.sort_album_disc_track();
948 sl_model
.set_mpd_data((owned
)data
);
949 // sl.set_from_data((owned)data, true);
950 // this.change_color_style(sl);
955 alib
.add(new Gtk
.Label(_("Not available")));
957 text_view_queried
= true;
963 if(song
.artist
!= null && song
.album
!= null)
965 MPD
.Database
.search_start(server
,true);
966 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ARTIST
, song
.artist
);
967 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ALBUM
, song
.album
);
968 var data
= MPD
.Database
.search_commit(server
);
969 data
.sort_album_disc_track();
970 sl_model
.set_mpd_data((owned
)data
);
971 //sl.set_from_data((owned)data, true);
976 alib
.add(new Gtk
.Label(_("Not available")));
978 text_view_queried
= true;
984 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "Artist info took: %.6f seconds.", t
.elapsed());
985 /* Track changed pages */
986 notebook
.notify
["page"].connect((source
,spec
) =>
988 var page
= notebook
.get_current_page();
989 config
.set_int("NowPlaying", "last-page", (int)page
);
992 /* Restore right page */
995 var page
= config
.get_int_with_default("NowPlaying", "last-page", 0);
998 notebook
.set_current_page(0);
1002 /* The list is in reversed order, compensate for that. */
1003 var w
= group
.nth_data(i
-page
-1);
1005 notebook
.set_current_page(page
);
1009 ali
= new Gtk
.Alignment(0.0f
, 0.5f
,1f
,0f
);
1012 /* Create pane in 2. */
1013 var bottom_hbox
= new Gtk
.HBox(false, 6);
1015 var metadata_vbox
= new Gtk
.VBox(false, 0);
1016 metadata_vbox
.pack_start(ali
, false, false, 0);
1017 sep
= new Gtk
.HSeparator();
1018 sep
.set_size_request(-1, 1);
1019 metadata_vbox
.pack_start(sep
, false, false, 0);
1020 metadata_vbox
.pack_start(notebook
, false, false, 0);
1022 bottom_hbox
.pack_start(metadata_vbox
, true, true, 0);
1024 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "Building now playing took: %.6f seconds.", t
.elapsed());
1025 /* Create album list */
1026 if(song
.album
!= null && song
.artist
!= null)
1028 var sep2
= new Gtk
.VSeparator();
1029 sep2
.set_size_request(-1, 4);
1030 bottom_hbox
.pack_start(sep2
, false, false, 0);
1032 /* Create album list */
1033 ali
= new Gtk
.Alignment(0.0f
, 0.0f
, 0.0f
, 0.0f
);
1034 var album_hbox
= new Gtk
.VBox(false, 6);
1035 album_hbox
.set_size_request(250, -1);
1036 ali
.add(album_hbox
);
1037 bottom_hbox
.pack_start(ali
, false, false, 0);
1039 var label
= new Gtk
.Label(song
.artist
);
1040 label
.selectable
= true;
1041 label
.set_size_request(240, -1);
1042 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
));
1043 label
.set_line_wrap_mode(Pango
.WrapMode
.WORD_CHAR
);
1044 label
.set_line_wrap(true);
1045 label
.set_alignment(0.0f
, 0.5f
);
1046 album_hbox
.pack_start(label
, false, false,0);
1048 MPD
.Database
.search_field_start(server
, MPD
.Tag
.Type
.ALBUM
);
1049 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ARTIST
, song
.artist
);
1050 MPD
.Data
.Item list
= null;
1051 var data
= MPD
.Database
.search_commit(server
);
1054 unowned MPD
.Data
.Item iter
= data
.get_first();
1057 if(iter
.tag
== song
.album
)
1063 list
.type
= MPD
.Data
.Type
.SONG
;
1064 list
.song
= new MPD
.Song();
1065 list
.song
.artist
= song
.artist
;
1066 list
.song
.album
= iter
.tag
;
1068 MPD.Database.search_field_start(server,MPD.Tag.Type.DATE);
1069 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, song.artist);
1070 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, iter.tag);
1071 var ydata = MPD.Database.search_commit(server);
1073 list.song.date = ydata.tag;
1078 while(iter
!= null);
1081 list
.sort_album_disc_track();
1085 unowned MPD
.Data
.Item iter
= list
.get_first();
1088 if(count
> this
.max_albums
)
1090 var l
= new Gtk
.Label(_("More..."));
1092 href=\"artist\">%s</a>".printf(_("More...")));
1093 l
.set_alignment(0, 0.5f
);
1094 album_hbox
.pack_start(l
, false,false, 0);
1095 l
.activate_link
.connect((source
)=>{
1096 Gmpc
.Browser
.Metadata
.show_artist(song
.artist
);
1104 var button
= new Gtk
.Button();
1105 button
.set_relief(Gtk
.ReliefStyle
.NONE
);
1106 var but_hbox
= new Gtk
.HBox(false, 6);
1107 button
.add(but_hbox
);
1108 var image
= new Gmpc
.MetaData
.Image(Gmpc
.MetaData
.Type
.ALBUM_ART
, 48);
1109 unowned MPD
.Song but_song
= iter
.song
;
1110 image
.set_squared(true);
1111 image
.update_from_song_delayed(but_song
);
1113 but_hbox
.pack_start(image
, false, false, 0);
1115 var but_label
= new Gtk
.Label(iter
.song
.album
);
1116 but_label
.selectable
= true;
1117 but_label
.set_alignment(0.0f
, 0.5f
);
1119 if(iter
.song
.date
!= null && iter
.song
.date
.length
> 0) strlabel
+= "%s\n".printf(iter
.song
.date
);
1120 if(iter
.song
.album
!= null) strlabel
+= iter
.song
.album
;
1121 else strlabel
+= _("No Album");
1122 but_label
.set_markup(GLib
.Markup
.printf_escaped("<b>%s</b>",strlabel
));
1123 but_label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
1124 but_hbox
.pack_start(but_label
, true, true, 0);
1126 album_hbox
.pack_start(button
, false, false,0);
1127 button
.set_data("artist",
1128 but_song
.artist
.dup());
1129 button
.set_data("album",
1130 but_song
.album
.dup());
1131 stdout
.printf("open: %s %s\n", but_song
.artist
, but_song
.album
);
1132 button
.clicked
.connect((source
) =>
1134 unowned
string artist
= (string)source
.get_data
<string>("artist");
1135 unowned
string album
= (string)source
.get_data
<string>("album");
1136 stdout
.printf("open: %s %s\n", artist
, album
);
1137 Gmpc
.Browser
.Metadata
.show_album(artist
, album
);
1148 album_hbox
.destroy();
1152 vbox
.pack_start(bottom_hbox
, true, true, 0);
1155 this
.container
.add(vbox
);
1156 this
.change_color_style(this
.container
);
1157 this
.container
.show_all();
1160 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "Building now playing took: %.6f seconds.", t
.elapsed());
1163 * This shows the page when mpd is not playing, for now it is the gmpc logo + Gnome Music Player Client
1165 private void update_not_playing()
1168 song_checksum
= null;
1170 (this
.container as Gmpc
.MetaData
.Widgets
.Backdrop
).set_song(null);
1172 var it
= Gtk
.IconTheme
.get_default();
1173 Gtk
.IconInfo info
= it
.lookup_icon("gmpc", 150, 0);
1174 var path
= info
.get_filename();
1175 Gtk
.Image image
= null;
1180 var pb
= new Gdk
.Pixbuf
.from_file_at_scale(path
, 150, 150, true);
1181 image
= new Gtk
.Image
.from_pixbuf(pb
);
1185 warning("Failed to load the gmpc logo: %s", e
.message
);
1191 image
= new Gtk
.Image
.from_icon_name("gmpc", Gtk
.IconSize
.DIALOG
);
1194 var hbox
= new Gtk
.HBox(false, 6);
1195 var label
= new Gtk
.Label(_("Gnome Music Player Client"));
1196 label
.set_selectable(true);
1197 label
.set_markup("<span size='%i' weight='bold'>%s</span>".printf(28*Pango
.SCALE
,_("Gnome Music Player Client")));
1198 hbox
.pack_start(image
, false, false, 0);
1199 hbox
.pack_start(label
, false, false, 0);
1201 var ali
= new Gtk
.Alignment(0.5f
,0.5f
,0.0f
, 0.0f
);
1203 this
.container
.add(ali
);
1205 this
.change_color_style(this
.container
);
1206 this
.container
.show_all();
1210 * Update the view according to state. If playing/paused show song info, other wise the not playing page
1212 private void update()
1214 switch(MPD
.Player
.get_state(Gmpc
.server
))
1216 case MPD
.Player
.State
.PLAY
:
1217 case MPD
.Player
.State
.PAUSE
:
1218 debug("Update playing");
1222 debug("update not playing");
1223 update_not_playing();
1229 * Makes gmpc jump to the now playing browser
1231 public void select_now_playing_browser()
1233 unowned Gtk
.TreeView tree
= Gmpc
.Playlist3
.get_category_tree_view();
1234 var sel
= tree
.get_selection();
1235 var path
= np_ref
.get_path();
1238 sel
.select_path(path
);
1243 * Gmpc.Plugin.BrowserIface.add_go_menu
1245 private int browser_add_go_menu(Gtk
.Menu menu
)
1247 if(this
.get_enabled())
1249 var item
= new Gtk
.ImageMenuItem
.with_mnemonic(_("Now Playing"));
1250 item
.set_image(new Gtk
.Image
.from_icon_name("media-audiofile", Gtk
.IconSize
.MENU
));
1251 item
.activate
.connect(select_now_playing_browser
);
1252 item
.add_accelerator("activate", menu
.get_accel_group(),0x069, Gdk
.ModifierType
.CONTROL_MASK
, Gtk
.AccelFlags
.VISIBLE
);