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
.Separator
|| bg is Gtk
.Notebook
|| bg is Gtk
.CheckButton
||
255 bg is Gtk
.Image
|| bg is Gmpc
.DataView
)
263 bg
.modify_bg(Gtk
.StateType
.NORMAL
,this
.paned
.style
.base[Gtk
.StateType
.NORMAL
]);
264 /*bg.modify_base(Gtk.StateType.NORMAL,this.paned.style.mid[Gtk.StateType.NORMAL]);/*
265 // bg.modify_text(Gtk.StateType.NORMAL,this.paned.style.text[Gtk.StateType.NORMAL]);
266 bg.modify_fg(Gtk.StateType.NORMAL,this.paned.style.text[Gtk.StateType.NORMAL]);
267 bg.modify_text(Gtk.StateType.ACTIVE,this.paned.style.light[Gtk.StateType.NORMAL]);
268 bg.modify_fg(Gtk.StateType.ACTIVE,this.paned.style.light[Gtk.StateType.NORMAL]);*/
272 if(!( bg is Gtk
.Label
)) {
273 bg
.modify_bg(Gtk
.StateType
.NORMAL
,this
.background
);
274 bg
.modify_base(Gtk
.StateType
.NORMAL
,this
.background
);
276 bg
.modify_text(Gtk
.StateType
.NORMAL
,this
.foreground
);
277 bg
.modify_fg(Gtk
.StateType
.NORMAL
,this
.foreground
);
278 bg
.modify_text(Gtk
.StateType
.ACTIVE
,this
.foreground
);
279 bg
.modify_fg(Gtk
.StateType
.ACTIVE
,this
.foreground
);
282 /* Recurse into children, if the widget can hold children (so is a container */
283 if(bg is Gtk
.Container
)
285 foreach(Gtk
.Widget child
in ((Gtk
.Container
)bg
).get_children())
287 change_color_style(child
);
291 /* Create the basic gui. on initializing */
292 private void browser_init()
294 if(this
.paned
== null)
297 this
.paned
= new Gtk
.ScrolledWindow(null,null);
298 this
.paned
.set_policy(Gtk
.PolicyType
.AUTOMATIC
, Gtk
.PolicyType
.AUTOMATIC
);
299 this
.paned
.set_shadow_type(Gtk
.ShadowType
.ETCHED_IN
);
302 this
.container
= new Gmpc
.MetaData
.Widgets
.Backdrop(Gmpc
.MetaData
.Type
.BACKDROP_ART
);
306 this
.container
= new Gtk
.EventBox();
309 this
.container
.key_press_event
.connect((source
, event
)=>{
310 if(event
.keyval
== Gdk
.Key_j
&&
311 !((event
.state
&Gdk
.ModifierType
.CONTROL_MASK
) ==
312 Gdk
.ModifierType
.CONTROL_MASK
))
314 var va
= this
.paned
.get_vadjustment();
315 va
.set_value(va
.get_value()+va
.get_step_increment());
317 else if(event
.keyval
== Gdk
.Key_k
&&
318 !((event
.state
&Gdk
.ModifierType
.CONTROL_MASK
) ==
319 Gdk
.ModifierType
.CONTROL_MASK
))
321 var va
= this
.paned
.get_vadjustment();
322 va
.set_value(va
.get_value()-va
.get_step_increment());
327 this.container.set_focus_hadjustment(this.paned.get_hadjustment());
328 this.container.set_focus_vadjustment(this.paned.get_vadjustment());
330 this
.paned
.style_set
.connect(browser_bg_style_changed
);
331 this
.paned
.add_with_viewport(this
.container
);
332 this
.paned
.get_vadjustment().set("step-increment", 20.0);
333 this
.paned
.show_all();
338 /* Clear the view inside the scrolled window*/
342 var list
= this
.container
.get_children();
343 foreach(Gtk
.Widget child
in list
)
347 bitrate_label
= null;
351 private string get_extension(string path
)
353 long length
= path
.length
;
356 for(; i
>0 && (length
-i
) <8; i
--)
360 retv
= path
.substring(i
+1);
367 * Show the page when playing
369 private string song_checksum
= null;
370 private void update_playing()
372 unowned MPD
.Song? song
= server
.playlist_get_current_song();
375 debug("GMPC Is playing, cannot get this");
377 (this
.container as Gmpc
.MetaData
.Widgets
.Backdrop
).set_song(null);
378 update_not_playing();
381 /* Force it so we won't update when not needed */
382 var checksum
= Gmpc
.Misc
.song_checksum(song
);
383 if(checksum
== this
.song_checksum
)
385 /* No need to update. */
388 GLib
.Timer t
= new GLib
.Timer();
391 (this
.container as Gmpc
.MetaData
.Widgets
.Backdrop
).set_song(song
);
392 this
.song_checksum
= checksum
;
394 var vbox
= new Gtk
.VBox (false,6);
395 vbox
.border_width
= 8;
396 /* Start building the gui */
398 var hbox
= new Gtk
.HBox(false, 6);
399 Gtk
.Alignment ali
= null;
401 int meta_size
= (int)(this
.container
.get_allocated_width()*0.20);
402 /* get size based on alloc */
403 meta_size
= int.min(int.max(100, meta_size
), 250);
404 if(config
.get_int_with_default("Interface", "hide-album-art", 0) == 0)
407 ali
= new Gtk
.Alignment(0f
,0f
,0f
,0f
);
408 //ali.set_size_request(meta_size,meta_size);
409 var album_image
= new Gmpc
.MetaData
.Image(Gmpc
.MetaData
.Type
.ALBUM_ART
, meta_size
);
410 album_image
.set_scale_up(true);
411 album_image
.set_squared(false);
412 ali
.add(album_image
);
413 album_image
.update_from_song(song
);
414 hbox
.pack_start(ali
, false, false, 0);
418 if(config
.get_int_with_default("Interface", "hide-album-art", 0) == 0 && !use_backdrop
)
420 ali
= new Gtk
.Alignment(1f
,0f
,0f
,0f
);
421 var artist_image
= new Gmpc
.MetaData
.Image(Gmpc
.MetaData
.Type
.ARTIST_ART
, meta_size
);
422 artist_image
.set_scale_up(true);
423 artist_image
.set_squared(false);
424 artist_image
.update_from_song(song
);
425 ali
.add(artist_image
);
426 hbox
.pack_end(ali
, false, false, 0);
429 /* Information box */
430 var info_vbox
= new Gtk
.VBox(false, 6);
432 if(song
.title
!= null)
434 var box
= new Gtk
.HBox(false, 6);
437 if(config
.get_int_with_default("Interface", "hide-favorites-icon",0) == 0)
439 var fav_button
= new Gmpc
.Favorites
.Button();
440 fav_button
.set_song(song
);
441 ali
= new Gtk
.Alignment(0.0f
, 0.5f
,0f
,0f
);
443 box
.pack_start(ali
, false, false, 0);
445 var label
= new Gtk
.Label(song
.title
);
446 label
.selectable
= true;
447 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' size='%i' weight='bold'>%s</span>",
448 this
.title_color
,Pango
.SCALE
*20 ,song
.title
));
449 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
450 label
.set_alignment(0.0f
, 0.5f
);
451 box
.pack_start(label
, true, true, 0);
452 info_vbox
.pack_start(box
, false, false, 0);
455 else if (song
.name
!= null)
457 var label
= new Gtk
.Label(song
.name
);
458 label
.selectable
= true;
459 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' size='%i' weight='bold'>%s</span>",this
.
460 title_color
, Pango
.SCALE
*20, song
.name
));
461 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
462 label
.set_alignment(0.0f
, 0.5f
);
463 info_vbox
.pack_start(label
, false, false, 0);
465 else if (song
.file
!= null)
467 var filename
= GLib
.Path
.get_basename(song
.file
);
468 var label
= new Gtk
.Label(song
.name
);
469 label
.selectable
= true;
472 var regex_a
= new GLib
.Regex ("\\.[0-9a-zA-Z]*$");
473 filename
= regex_a
.replace_literal (filename
, -1, 0, "");
475 catch (GLib
.RegexError e
)
477 GLib
.error("%s", e
.message
);
481 var regex_b
= new GLib
.Regex ("_");
482 filename
= regex_b
.replace_literal (filename
, -1, 0, " ");
484 catch (GLib
.RegexError e
)
486 GLib
.error("%s", e
.message
);
488 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' size='%i' weight='bold'>%s</span>",this
.
489 title_color
, Pango
.SCALE
*20, filename
));
490 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
491 label
.set_alignment(0.0f
, 0.5f
);
492 info_vbox
.pack_start(label
, false, false, 0);
496 if(song
.artist
!= null)
498 var event
= new Gtk
.EventBox();
499 var box
= new Gtk
.HBox(false, 6);
500 var label
= new Gtk
.Label(song
.artist
);
501 event
.set_visible_window(false);
502 label
.selectable
= true;
503 var image
= new Gtk
.Image
.from_icon_name("media-artist", Gtk
.IconSize
.MENU
);
505 box
.pack_start(event
, false, false, 0);
506 label
.set_markup(GLib
.Markup
.printf_escaped("<span size='xx-large' weight='bold'>%s</span>", song
.artist
));
507 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
508 label
.set_alignment(0.0f
, 0.5f
);
509 box
.pack_start(label
, true, true, 0);
510 info_vbox
.pack_start(box
, false, false, 0);
512 event
.set_data("artist",song
.artist
);
513 event
.button_press_event
.connect((widget
, event
) =>
515 string artist
= (string)widget
.get_data
<string>("artist");
516 Gmpc
.Browser
.Metadata
.show_artist(artist
);
521 if(song
.album
!= null)
523 var event
= new Gtk
.EventBox();
524 var box
= new Gtk
.HBox(false, 6);
525 var label
= new Gtk
.Label(song
.album
);
526 event
.set_visible_window(false);
527 label
.selectable
= true;
528 var image
= new Gtk
.Image
.from_icon_name("media-album", Gtk
.IconSize
.MENU
);
530 box
.pack_start(event
, false, false, 0);
531 label
.set_markup(GLib
.Markup
.printf_escaped("<span size='x-large' weight='bold'>%s %s</span>", song
.album
,
532 (song
.date
!= null)?
"(%s)".printf(song
.date
):""));
533 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
534 label
.set_alignment(0.0f
, 0.5f
);
535 box
.pack_start(label
, true, true, 0);
536 info_vbox
.pack_start(box
, false, false, 0);
540 event
.set_data("artist",song
.artist
);
541 event
.set_data("album",song
.album
);
542 event
.button_press_event
.connect((widget
, event
) =>
544 string artist
= (string)widget
.get_data
<string>("artist");
545 string album
= (string)widget
.get_data
<string>("album");
546 if(artist
!= null && album
!= null)
548 Gmpc
.Browser
.Metadata
.show_album(artist
,album
);
555 if(song
.genre
!= null)
557 var box
= new Gtk
.HBox(false, 6);
558 var label
= new Gtk
.Label(song
.title
);
559 label
.selectable
= true;
560 var image
= new Gtk
.Image
.from_icon_name("media-genre", Gtk
.IconSize
.MENU
);
561 box
.pack_start(image
, false, false, 0);
562 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' weight='bold'>%s:</span> %s",
563 this
.item_color
,_("Genre"), song
.genre
));
564 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
565 label
.set_alignment(0.0f
, 0.5f
);
566 box
.pack_start(label
, true, true, 0);
567 info_vbox
.pack_start(box
, false, false, 0);
572 var box
= new Gtk
.HBox(false, 6);
573 var image
= new Gtk
.Image
.from_icon_name("media-format", Gtk
.IconSize
.MENU
);
574 box
.pack_start(image
, false, false, 0);
576 bitrate_label
= new Gtk
.Label(song
.title
);
577 bitrate_label
.selectable
= true;
578 bitrate_label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
579 bitrate_label
.set_alignment(0.0f
, 0.5f
);
581 box
.pack_start(bitrate_label
, true, true, 0);
583 var bitrate
= MPD
.Status
.get_bitrate(Gmpc
.server
);
584 var channels
= MPD
.Status
.get_channels(Gmpc
.server
);
585 bitrate_label
.set_markup(GLib
.Markup
.printf_escaped(
586 "<span color='%s' weight='bold'>%s:</span> %i %s, %.1f %s, %i %s",
587 this
.item_color
, _("Format"),
588 channels
, GLib
.ngettext(N_("Channel"),N_("Channels"), channels
),
589 MPD
.Status
.get_samplerate(Gmpc
.server
)/1000.0, "kHz",
593 info_vbox
.pack_start(box
, false, false, 0);
595 if(song
.file
!= null)
598 string extension
= null;
599 extension
= get_extension(song
.file
);
600 if(extension
!= null)
602 var box
= new Gtk
.HBox(false, 6);
603 var image
= new Gtk
.Image
.from_icon_name("media-codec", Gtk
.IconSize
.MENU
);
604 box
.pack_start(image
, false, false, 0);
606 var label
= new Gtk
.Label(song
.title
);
607 label
.selectable
= true;
608 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
609 label
.set_alignment(0.0f
, 0.5f
);
610 box
.pack_start(label
, true, true, 0);
611 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' weight='bold'>%s:</span> %s",
612 this
.item_color
, _("Codec"),
614 info_vbox
.pack_start(box
, false, false, 0);
620 var box
= new Gtk
.HBox(false, 6);
621 var image
= new Gtk
.Image
.from_icon_name("media-track-length", Gtk
.IconSize
.MENU
);
622 box
.pack_start(image
, false, false, 0);
623 var label
= new Gtk
.Label("");
624 label
.selectable
= true;
625 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
626 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' weight='bold'>%s:</span> %s",
627 this
.item_color
,_("Length"),
628 Gmpc
.Misc
.format_time((ulong) song
.time
, "")));
629 label
.set_alignment(0.0f
, 0.5f
);
630 box
.pack_start(label
, true, true, 0);
631 info_vbox
.pack_start(box
, false, false, 0);
634 if(song
.track
!= null)
636 var box
= new Gtk
.HBox(false, 6);
637 var image
= new Gtk
.Image
.from_icon_name("media-num-tracks", Gtk
.IconSize
.MENU
);
638 box
.pack_start(image
, false, false, 0);
639 var label
= new Gtk
.Label("");
640 label
.selectable
= true;
641 label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
642 label
.set_markup(GLib
.Markup
.printf_escaped("<span color='%s' weight='bold'>%s:</span> %s %s",
643 this
.item_color
, _("Track number"),
645 (song
.disc
!= null)?
"[%s]".printf(song
.disc
):""
647 label
.set_alignment(0.0f
, 0.5f
);
648 box
.pack_start(label
, true, true, 0);
649 info_vbox
.pack_start(box
, false, false, 0);
653 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "heading took: %.6f seconds.", t
.elapsed());
654 hbox
.pack_start(info_vbox
, true, true, 0);
655 vbox
.pack_start(hbox
, false, false, 0);
658 var sep
= new Gtk
.HSeparator();
659 sep
.set_size_request(-1, 4);
660 vbox
.pack_start(sep
, false, false, 0);
662 var hboxje
= new Gtk
.HBox(false, 6);
664 /* Notebook where all the metadata items are kept, Override the tabs by a radiobutton list. */
665 var notebook
= new Gtk
.Notebook();
666 notebook
.set_show_border(false);
667 notebook
.set_show_tabs(false);
671 unowned SList
<unowned Gtk
.RadioButton
> group
= null;
672 if(config
.get_int_with_default("MetaData", "show-lyrics",1) == 1)
674 var alib
= new Gtk
.Alignment(0f
,0f
,1f
,0f
);
675 var text_view
= new Gmpc
.MetaData
.Widgets
.TextLabel(song
, Gmpc
.MetaData
.Type
.SONG_TXT
);
677 notebook
.append_page(alib
, new Gtk
.Label("Lyrics"));
678 var button
= new Gtk
.RadioButton
.with_label(group
,("Lyrics"));
679 ((button as Gtk
.Bin
).get_child() as
680 Gtk
.Label
).set_ellipsize(Pango
.EllipsizeMode
.END
);
683 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
684 group
= button
.get_group();
685 hboxje
.pack_start(button
, false, false, 0);
687 button
.clicked
.connect((source
) =>
689 if((source as Gtk
.CheckButton
).get_active())
691 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "lyrics notebook page %i clicked", j
);
692 notebook
.set_current_page(j
);
699 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "lyrics took: %.6f seconds.", t
.elapsed());
701 /* Album information */
702 if(config
.get_int_with_default("MetaData", "show-album-information",1) == 1)
704 var alib
= new Gtk
.Alignment(0f
,0f
,1f
,0f
);
705 var text_view
= new Gmpc
.MetaData
.Widgets
.TextLabel(song
, Gmpc
.MetaData
.Type
.ALBUM_TXT
);
707 notebook
.append_page(alib
, new Gtk
.Label("Album information"));
708 var button
= new Gtk
.RadioButton
.with_label(group
,_("Album information"));
709 ((button as Gtk
.Bin
).get_child() as
710 Gtk
.Label
).set_ellipsize(Pango
.EllipsizeMode
.END
);
713 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
714 group
= button
.get_group();
715 hboxje
.pack_start(button
, false, false, 0);
717 button
.clicked
.connect((source
) =>
719 if((source as Gtk
.CheckButton
).get_active())
721 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "lyrics notebook page %i clicked", j
);
722 notebook
.set_current_page(j
);
731 if(config
.get_int_with_default("MetaData", "show-guitar-tabs",1) == 1)
733 var alib
= new Gtk
.Alignment(0f
,0f
,1f
,0f
);
734 var text_view_queried
= false;
735 notebook
.append_page(alib
, new Gtk
.Label(_("Guitar Tabs")));
736 var button
= new Gtk
.RadioButton
.with_label(group
,_("Guitar Tabs"));
737 ((button as Gtk
.Bin
).get_child() as
738 Gtk
.Label
).set_ellipsize(Pango
.EllipsizeMode
.END
);
742 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
743 group
= button
.get_group();
744 hboxje
.pack_start(button
, false, false, 0);
746 /* Only query the guitar-tab when opened or first notebook page*/
747 button
.clicked
.connect((source
) =>
749 if((source as Gtk
.CheckButton
).get_active())
751 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "guitar tab notebook page %i clicked", j
);
752 notebook
.set_current_page(j
);
753 if(!text_view_queried
)
755 var text_view
= new Gmpc
.MetaData
.Widgets
.TextLabel(song
, Gmpc
.MetaData
.Type
.SONG_GUITAR_TAB
);
757 text_view_queried
= true;
758 this
.change_color_style(text_view
);
765 var text_view
= new Gmpc
.MetaData
.Widgets
.TextLabel(song
, Gmpc
.MetaData
.Type
.SONG_GUITAR_TAB
);
767 text_view_queried
= true;
772 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "guitar tabs took: %.6f seconds.", t
.elapsed());
776 if(config
.get_int_with_default("MetaData", "show-similar-songs",1) == 1)
778 var similar_songs_queried
= false;
779 var similar_songs_box
= new Gtk
.Alignment(0f
,0f
,1f
,0f
);
781 notebook
.append_page(similar_songs_box
, new Gtk
.Label(_("Similar Songs")));
783 var button
= new Gtk
.RadioButton
.with_label(group
,_("Similar Songs"));
784 ((button as Gtk
.Bin
).get_child() as
785 Gtk
.Label
).set_ellipsize(Pango
.EllipsizeMode
.END
);
787 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
788 group
= button
.get_group();
789 hboxje
.pack_start(button
, false, false, 0);
792 /* Only query when opened or first notebook page*/
793 button
.clicked
.connect((source
) =>
795 if((source as Gtk
.CheckButton
).get_active())
797 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "similar song notebook page %i clicked", j
);
798 notebook
.set_current_page(j
);
799 if(!similar_songs_queried
)
801 var similar_songs
= new Gmpc
.MetaData
.Widgets
.SimilarSongs(song
);
802 similar_songs
.update();
803 similar_songs_queried
= true;
804 similar_songs_box
.add(similar_songs
);
805 this
.change_color_style(similar_songs_box
);
806 similar_songs_box
.show_all();
812 var similar_songs
= new Gmpc
.MetaData
.Widgets
.SimilarSongs(song
);
813 similar_songs
.update();
814 similar_songs_queried
= true;
815 similar_songs_box
.add(similar_songs
);
816 similar_songs_box
.show_all();
818 similar_songs_box
.show();
822 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "similar songs took: %.6f seconds.", t
.elapsed());
823 if(config
.get_int_with_default("MetaData", "show-similar-artist",1) == 1 && song
.artist
!= null)
825 var similar_artist
= new Gmpc
.MetaData
.Widgets
.SimilarArtists(Gmpc
.server
,song
);
827 notebook
.append_page(similar_artist
, new Gtk
.Label(_("Similar Artists")));
829 var button
= new Gtk
.RadioButton
.with_label(group
,_("Similar Artists"));
830 ((button as Gtk
.Bin
).get_child() as
831 Gtk
.Label
).set_ellipsize(Pango
.EllipsizeMode
.END
);
833 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
834 group
= button
.get_group();
835 hboxje
.pack_start(button
, false, false, 0);
838 button
.clicked
.connect((source
) =>
840 if((source as Gtk
.CheckButton
).get_active())
842 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "similar artist notebook page %i clicked", j
);
843 similar_artist
.first_show();
844 notebook
.set_current_page(j
);
847 similar_artist
.show();
850 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "similar Artist took: %.6f seconds.", t
.elapsed());
851 if(config
.get_int_with_default("MetaData", "show-artist-information",1) == 1)
853 var alib
= new Gtk
.Alignment(0f
,0f
,1f
,0f
);
854 var text_view_queried
= false;
856 notebook
.append_page(alib
, new Gtk
.Label(_("Artist information")));
857 var button
= new Gtk
.RadioButton
.with_label(group
,_("Artist information"));
858 ((button as Gtk
.Bin
).get_child() as
859 Gtk
.Label
).set_ellipsize(Pango
.EllipsizeMode
.END
);
861 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
862 group
= button
.get_group();
863 hboxje
.pack_start(button
, false, false, 0);
865 /* Only query the guitar-tab when opened or first notebook page*/
866 button
.clicked
.connect((source
) =>
868 if((source as Gtk
.CheckButton
).get_active())
870 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "artist info notebook page %i clicked", j
);
871 notebook
.set_current_page(j
);
872 if(!text_view_queried
)
874 var text_view
= new Gmpc
.MetaData
.Widgets
.TextLabel(song
,Gmpc
.MetaData
.Type
.ARTIST_TXT
);
876 text_view_queried
= true;
878 this
.change_color_style(text_view
);
884 var text_view
= new Gmpc
.MetaData
.Widgets
.TextLabel(song
,Gmpc
.MetaData
.Type
.ARTIST_TXT
);
886 text_view_queried
= true;
892 if(config
.get_int_with_default("MetaData", "show-songs-from-album",1) == 1)
894 var alib
= new Gtk
.Alignment(0f
,0f
,1f
,0f
);
895 var text_view_queried
= false;
897 notebook
.append_page(alib
, new Gtk
.Label(_("Songs from album")));
898 var button
= new Gtk
.RadioButton
.with_label(group
,_("Songs from album"));
899 ((button as Gtk
.Bin
).get_child() as
900 Gtk
.Label
).set_ellipsize(Pango
.EllipsizeMode
.END
);
902 hboxje
.pack_start(new Gtk
.VSeparator(), false, false, 0);
903 group
= button
.get_group();
904 hboxje
.pack_start(button
, false, false, 0);
906 var sl
= new Gmpc
.DataView("now-playing-song-from-album");
907 var sl_model
= new Gmpc
.MpdData
.Model();
908 sl
.set_model(sl_model
);
911 button
.clicked
.connect((source
) =>
913 if((source as Gtk
.CheckButton
).get_active())
915 GLib
.log(np2_LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
, "artist info notebook page %i clicked", j
);
916 notebook
.set_current_page(j
);
917 if(!text_view_queried
)
919 if(song
.artist
!= null && song
.album
!= null)
921 MPD
.Database
.search_start(server
,true);
922 if(song
.albumartist
!= null)
924 MPD
.Database
.search_add_constraint(server
,
925 MPD
.Tag
.Type
.ALBUM_ARTIST
, song
.albumartist
);
929 MPD
.Database
.search_add_constraint(server
,
930 MPD
.Tag
.Type
.ARTIST
, song
.artist
);
932 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ALBUM
, song
.album
);
933 var data
= MPD
.Database
.search_commit(server
);
934 data
.sort_album_disc_track();
935 sl_model
.set_mpd_data((owned
)data
);
936 // sl.set_from_data((owned)data, true);
937 // this.change_color_style(sl);
942 alib
.add(new Gtk
.Label(_("Not available")));
944 text_view_queried
= true;
950 if(song
.artist
!= null && song
.album
!= null)
952 MPD
.Database
.search_start(server
,true);
953 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ARTIST
, song
.artist
);
954 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ALBUM
, song
.album
);
955 var data
= MPD
.Database
.search_commit(server
);
956 data
.sort_album_disc_track();
957 sl_model
.set_mpd_data((owned
)data
);
958 //sl.set_from_data((owned)data, true);
963 alib
.add(new Gtk
.Label(_("Not available")));
965 text_view_queried
= true;
971 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "Artist info took: %.6f seconds.", t
.elapsed());
972 /* Track changed pages */
973 notebook
.notify
["page"].connect((source
,spec
) =>
975 var page
= notebook
.get_current_page();
976 config
.set_int("NowPlaying", "last-page", (int)page
);
979 /* Restore right page */
982 var page
= config
.get_int_with_default("NowPlaying", "last-page", 0);
985 notebook
.set_current_page(0);
989 /* The list is in reversed order, compensate for that. */
990 var w
= group
.nth_data(i
-page
-1);
992 notebook
.set_current_page(page
);
996 ali
= new Gtk
.Alignment(0.0f
, 0.5f
,1f
,0f
);
999 /* Create pane in 2. */
1000 var bottom_hbox
= new Gtk
.HBox(false, 6);
1002 var metadata_vbox
= new Gtk
.VBox(false, 0);
1003 metadata_vbox
.pack_start(ali
, false, false, 0);
1004 sep
= new Gtk
.HSeparator();
1005 sep
.set_size_request(-1, 1);
1006 metadata_vbox
.pack_start(sep
, false, false, 0);
1007 metadata_vbox
.pack_start(notebook
, false, false, 0);
1009 bottom_hbox
.pack_start(metadata_vbox
, true, true, 0);
1011 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "Building now playing took: %.6f seconds.", t
.elapsed());
1012 /* Create album list */
1013 if(song
.album
!= null && song
.artist
!= null)
1015 var sep2
= new Gtk
.VSeparator();
1016 sep2
.set_size_request(-1, 4);
1017 bottom_hbox
.pack_start(sep2
, false, false, 0);
1019 /* Create album list */
1020 ali
= new Gtk
.Alignment(0.0f
, 0.0f
, 0.0f
, 0.0f
);
1021 var album_hbox
= new Gtk
.VBox(false, 6);
1022 album_hbox
.set_size_request(250, -1);
1023 ali
.add(album_hbox
);
1024 bottom_hbox
.pack_start(ali
, false, false, 0);
1026 var label
= new Gtk
.Label(song
.artist
);
1027 label
.selectable
= true;
1028 label
.set_size_request(240, -1);
1029 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
));
1030 label
.set_line_wrap_mode(Pango
.WrapMode
.WORD_CHAR
);
1031 label
.set_line_wrap(true);
1032 label
.set_alignment(0.0f
, 0.5f
);
1033 album_hbox
.pack_start(label
, false, false,0);
1035 MPD
.Database
.search_field_start(server
, MPD
.Tag
.Type
.ALBUM
);
1036 MPD
.Database
.search_add_constraint(server
, MPD
.Tag
.Type
.ARTIST
, song
.artist
);
1037 MPD
.Data
.Item list
= null;
1038 var data
= MPD
.Database
.search_commit(server
);
1041 unowned MPD
.Data
.Item iter
= data
.get_first();
1044 if(iter
.tag
== song
.album
)
1050 list
.type
= MPD
.Data
.Type
.SONG
;
1051 list
.song
= new MPD
.Song();
1052 list
.song
.artist
= song
.artist
;
1053 list
.song
.album
= iter
.tag
;
1055 MPD.Database.search_field_start(server,MPD.Tag.Type.DATE);
1056 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ARTIST, song.artist);
1057 MPD.Database.search_add_constraint(server, MPD.Tag.Type.ALBUM, iter.tag);
1058 var ydata = MPD.Database.search_commit(server);
1060 list.song.date = ydata.tag;
1065 while(iter
!= null);
1068 list
.sort_album_disc_track();
1072 unowned MPD
.Data
.Item iter
= list
.get_first();
1075 if(count
> this
.max_albums
)
1077 var l
= new Gtk
.Label(_("More..."));
1078 l
.set_alignment(0, 0.5f
);
1079 album_hbox
.pack_start(l
, false,false, 0);
1085 var button
= new Gtk
.Button();
1086 button
.set_relief(Gtk
.ReliefStyle
.NONE
);
1087 var but_hbox
= new Gtk
.HBox(false, 6);
1088 button
.add(but_hbox
);
1089 var image
= new Gmpc
.MetaData
.Image(Gmpc
.MetaData
.Type
.ALBUM_ART
, 48);
1090 unowned MPD
.Song but_song
= iter
.song
;
1091 image
.set_squared(true);
1092 image
.update_from_song_delayed(but_song
);
1094 but_hbox
.pack_start(image
, false, false, 0);
1096 var but_label
= new Gtk
.Label(iter
.song
.album
);
1097 but_label
.selectable
= true;
1098 but_label
.set_alignment(0.0f
, 0.5f
);
1100 if(iter
.song
.date
!= null && iter
.song
.date
.length
> 0) strlabel
+= "%s\n".printf(iter
.song
.date
);
1101 if(iter
.song
.album
!= null) strlabel
+= iter
.song
.album
;
1102 else strlabel
+= _("No Album");
1103 but_label
.set_markup(GLib
.Markup
.printf_escaped("<b>%s</b>",strlabel
));
1104 but_label
.set_ellipsize(Pango
.EllipsizeMode
.END
);
1105 but_hbox
.pack_start(but_label
, true, true, 0);
1107 album_hbox
.pack_start(button
, false, false,0);
1108 button
.set_data("artist",
1109 but_song
.artist
.dup());
1110 button
.set_data("album",
1111 but_song
.album
.dup());
1112 stdout
.printf("open: %s %s\n", but_song
.artist
, but_song
.album
);
1113 button
.clicked
.connect((source
) =>
1115 unowned
string artist
= (string)source
.get_data
<string>("artist");
1116 unowned
string album
= (string)source
.get_data
<string>("album");
1117 stdout
.printf("open: %s %s\n", artist
, album
);
1118 Gmpc
.Browser
.Metadata
.show_album(artist
, album
);
1129 album_hbox
.destroy();
1133 vbox
.pack_start(bottom_hbox
, true, true, 0);
1136 this
.container
.add(vbox
);
1137 this
.change_color_style(this
.container
);
1138 this
.container
.show_all();
1141 GLib
.log(np2_LOG_DOMAIN
, GLib
.LogLevelFlags
.LEVEL_DEBUG
, "Building now playing took: %.6f seconds.", t
.elapsed());
1144 * This shows the page when mpd is not playing, for now it is the gmpc logo + Gnome Music Player Client
1146 private void update_not_playing()
1149 song_checksum
= null;
1151 (this
.container as Gmpc
.MetaData
.Widgets
.Backdrop
).set_song(null);
1153 var it
= Gtk
.IconTheme
.get_default();
1154 Gtk
.IconInfo info
= it
.lookup_icon("gmpc", 150, 0);
1155 var path
= info
.get_filename();
1156 Gtk
.Image image
= null;
1161 var pb
= new Gdk
.Pixbuf
.from_file_at_scale(path
, 150, 150, true);
1162 image
= new Gtk
.Image
.from_pixbuf(pb
);
1166 warning("Failed to load the gmpc logo: %s", e
.message
);
1172 image
= new Gtk
.Image
.from_icon_name("gmpc", Gtk
.IconSize
.DIALOG
);
1175 var hbox
= new Gtk
.HBox(false, 6);
1176 var label
= new Gtk
.Label(_("Gnome Music Player Client"));
1177 label
.set_selectable(true);
1178 label
.set_markup("<span size='%i' weight='bold'>%s</span>".printf(28*Pango
.SCALE
,_("Gnome Music Player Client")));
1179 hbox
.pack_start(image
, false, false, 0);
1180 hbox
.pack_start(label
, false, false, 0);
1182 var ali
= new Gtk
.Alignment(0.5f
,0.5f
,0.0f
, 0.0f
);
1184 this
.container
.add(ali
);
1186 this
.change_color_style(this
.container
);
1187 this
.container
.show_all();
1191 * Update the view according to state. If playing/paused show song info, other wise the not playing page
1193 private void update()
1195 switch(MPD
.Player
.get_state(Gmpc
.server
))
1197 case MPD
.Player
.State
.PLAY
:
1198 case MPD
.Player
.State
.PAUSE
:
1199 debug("Update playing");
1203 debug("update not playing");
1204 update_not_playing();
1210 * Makes gmpc jump to the now playing browser
1212 public void select_now_playing_browser()
1214 unowned Gtk
.TreeView tree
= Gmpc
.Playlist3
.get_category_tree_view();
1215 var sel
= tree
.get_selection();
1216 var path
= np_ref
.get_path();
1219 sel
.select_path(path
);
1224 * Gmpc.Plugin.BrowserIface.add_go_menu
1226 private int browser_add_go_menu(Gtk
.Menu menu
)
1228 if(this
.get_enabled())
1230 var item
= new Gtk
.ImageMenuItem
.with_mnemonic(_("Now Playing"));
1231 item
.set_image(new Gtk
.Image
.from_icon_name("media-audiofile", Gtk
.IconSize
.MENU
));
1232 item
.activate
.connect(select_now_playing_browser
);
1233 item
.add_accelerator("activate", menu
.get_accel_group(),0x069, Gdk
.ModifierType
.CONTROL_MASK
, Gtk
.AccelFlags
.VISIBLE
);