3 #$: << File.expand_path(File.dirname(FILE) + “/../lib”))
5 #require 'libgruxclient'
8 require 'xmmsclient_glib'
13 class Grux::ClientWin < Gtk::VBox
16 def initialize( server_address )
20 @server = Xmms::Client.new( 'Grux' )
23 @status[:connected] = self.connect( server_address )
25 @statusBox = Gtk::HBox.new
26 @mainBox = Gtk::VPaned.new
27 @statusBar = Gtk::Statusbar.new
29 self.pack_start @statusBox, false
30 self.pack_start @mainBox
31 self.pack_end @statusBar, false
35 @currentartistFrame = Gtk::Frame.new( "Artist" ).add( @currentartistLabel = Gtk::Label.new ),
36 @currenttitleFrame = Gtk::Frame.new( "Title" ).add( @currenttitleLabel = Gtk::Label.new ),
37 @currentalbumFrame = Gtk::Frame.new( "Album" ).add( @currentalbumLabel = Gtk::Label.new )
39 @statusWdgts.each { |frame| @statusBox.pack_start frame }
43 @cntrlBox = Gtk::HBox.new,
44 @medialistsWin = Gtk::Notebook.new
46 @mainBox.pack1 @cntrlBox, true, false
47 @mainBox.pack2 @medialistsWin, true, false
51 @cntrlBtnBox = Gtk::VButtonBox.new.set_layout_style( Gtk::ButtonBox::CENTER ),
52 @toolChsr = Gtk::Notebook.new
54 @cntrlBox.pack_start @cntrlBtnBox, false
55 @cntrlBox.pack_start @toolChsr
59 @playBtn = Gtk::ToggleButton.new('_Play', true),
60 @pauseBtn = Gtk::ToggleButton.new('Pa_use', true),
61 @stopBtn = Gtk::ToggleButton.new('_Stop', true),
62 @nextBtn = Gtk::Button.new('_Next'),
63 @prevBtn = Gtk::Button.new('P_revious'),
64 @rndmBtn = Gtk::Button.new('Randomi_ze'),
65 @rptBtn = Gtk::ToggleButton.new('Repeat _All', true),
66 @rpt1Btn = Gtk::ToggleButton.new('Repeat _One', true)
68 @cntrlbtns.each do |button|
69 @cntrlBtnBox.pack_start button
74 [ @extinfoTab = Gtk::VBox.new,
75 @extinfoTabLbl = Gtk::Label.new( "Status" ) ],
76 [ @tageditorTab = Gtk::VBox.new,
77 @tageditorTabLbl = Gtk::Label.new( "Tag Editor" ) ]
79 @toolTabs.each do |content, label|
80 @toolChsr.append_page( Gtk::ScrolledWindow.new.add_with_viewport( content ), label )
84 @medialistTextRenderer = Gtk::CellRendererText.new
85 @medialistColumns = [ 'id', 'artist', 'title' ]
88 [ @playlistViewLbl = Gtk::Label.new( "Current Playlist" ),
89 @playlistViewWin = Gtk::ScrolledWindow.new,
90 @playlistView = Gtk::TreeView.new,
91 @playlistStore = Gtk::ListStore.new( String, String, String )
93 [ @medialibViewLbl = Gtk::Label.new( "Medialib" ),
94 @medialibViewWin = Gtk::ScrolledWindow.new,
95 @medialibView = Gtk::TreeView.new,
96 @medialibStore = Gtk::ListStore.new( String, String, String )
100 @medialists.each do |label, window, view, store|
101 @medialistColumns.each_with_index do |tag, index|
102 view.append_column Gtk::TreeViewColumn.new( tag, @medialistTextRenderer, :text => index )
104 view.search_column = 2
105 @medialistsWin.append_page( window.add( view ), label )
112 @playBtnClkdHndlr = @playBtn.signal_connect :clicked do
115 @pauseBtnClkdHndlr = @pauseBtn.signal_connect :clicked do
118 @stopBtnClkdHndlr = @stopBtn.signal_connect :clicked do
121 @nextBtn.signal_connect :clicked do
122 self.step_in_playlist( 1 )
124 @prevBtn.signal_connect :clicked do
125 self.step_in_playlist( -1 )
127 @rndmBtn.signal_connect :clicked do
128 self.randomize_playlist
130 @rptBtnClkdHndlr = @rptBtn.signal_connect :clicked do
131 self.toggle_repeat_all
133 @rpt1BtnClkdHndlr = @rpt1Btn.signal_connect :clicked do
134 self.toggle_repeat_one
138 @playlistView.signal_connect :row_activated do |view, path, column|
139 @server.playlist_set_next( path.to_str.to_i ).notifier do
140 @server.playback_tickle.notifier do
149 @medialibView.signal_connect :row_activated do |view, path, column|
150 songid = @medialibStore.get_iter( path )[0].to_i
151 @server.playlist.add_entry( songid ).notifier do
152 current_row = @playlistStore.append
153 @server.medialib_get_info( songid ).notifier do |songinfo|
154 current_row[0] = songid.to_s
155 current_row[1] = songinfo[:artist] ? songinfo[:artist].first[1] : ""
156 current_row[2] = songinfo[:title] ? songinfo[:title].first[1] : ""
159 @server.playlist_set_next( current_row.to_s.to_i ).notifier do
160 @server.playback_tickle.notifier do
170 if @status[:connected]
177 def connect( address )
179 @server.connect( address )
180 @server.add_to_glib_mainloop
181 rescue Xmms::Client::ClientError => ex
182 warn "Connection failed. Error Code:"
188 @server.broadcast_coll_changed.notifier do |*foo|
189 self.handle_coll_changed foo
192 @server.broadcast_config_value_changed.notifier do |*foo|
193 self.handle_config_value_changed foo
196 @server.broadcast_mediainfo_reader_status.notifier do |*foo|
197 self.handle_mediainfo_reader_status foo
200 @server.broadcast_medialib_entry_added.notifier do |*foo|
201 self.handle_medialib_entry_added foo
204 @server.broadcast_medialib_entry_changed.notifier do |*foo|
205 self.handle_medialib_entry_changed foo
208 @server.broadcast_playback_current_id.notifier do |id|
209 self.handle_playback_current_id id
212 @server.broadcast_playback_status.notifier do |status|
213 self.handle_playback_status status
216 @server.broadcast_playback_volume_changed.notifier do |*foo|
217 self.handle_playback_volume_changed foo
220 @server.broadcast_playlist_changed.notifier do |*foo|
221 self.handle_playlist_changed foo
224 @server.broadcast_playlist_current_pos.notifier do |pos|
225 self.handle_playlist_current_pos pos
228 @server.broadcast_playlist_loaded.notifier do |*foo|
229 self.handle_playlist_loaded foo
232 @server.broadcast_quit.notifier do |*foo|
240 def handle_coll_changed( *foo )
241 p "Collection changed: #{foo}"
244 def handle_config_value_changed( *foo )
245 p "Config changed: #{foo}"
248 def handle_mediainfo_reader_status( *foo )
249 p "Mediainfo reader status changed: #{foo}"
252 def handle_medialib_entry_added( *foo )
253 p "Medialib entry added: #{foo}"
256 def handle_medialib_entry_changed( *foo )
257 p "Medialib entry changed: #{foo}"
262 def handle_playback_current_id( id )
263 p "Current playback ID changed: #{id}"
264 songpath = Gtk::TreePath.new( @status[:playlist].index id )
266 @playlistView.scroll_to_cell( songpath, nil, true, 0.5, 0.5 )
267 @playlistView.set_cursor( songpath, nil, false )
269 @server.medialib_get_info( id ).notifier do |songinfo|
270 @currentartistLabel.text = ""
271 @currenttitleLabel.text = ""
272 @currentalbumLabel.text = ""
274 @extinfoTab.each do |child| @extinfoTab.remove child end
276 songinfo[:artist] && songinfo[:artist].each_pair do |src, value|
277 @currentartistLabel.text += @currentartistLabel.text.empty? ? value : "\n#{value}"
279 songinfo[:title] && songinfo[:title].each_pair do |src, value|
280 @currenttitleLabel.text += @currenttitleLabel.text.empty? ? value : "\n#{value}"
282 songinfo[:album] && songinfo[:album].each_pair do |src, value|
283 @currentalbumLabel.text += @currentalbumLabel.text.empty? ? value : "\n#{value}"
285 songinfo.each_key do |key|
287 songinfo[key].each_pair do |src,value|
288 infos.empty? || infos += "\n"
289 infos += "#{value} (#{src})"
291 @extinfoTab.pack_end( Gtk::Frame.new( key.to_s ).add( Gtk::Label.new( infos ) ) )
300 def handle_playback_status( status )
301 p "Current playback status changed: #{status}"
302 @playbackState = status
307 def handle_playback_volume_changed( *foo )
308 p "Playback volume changed: #{foo}"
311 def handle_playlist_changed( *foo )
312 p "Playlist changed: #{foo}"
317 def handle_playlist_current_pos( pos )
318 p "Current pos changed: #{pos}"
319 #@server.playlist.entries.notifier do |playlist|
320 # @server.medialib_get_info( playlist[pos[:position]] ).notifier do |songinfo|
321 # @nextSongLbl.text = ""
322 # songinfo.each_pair do |key, info|
323 # info.each_pair do |src, value|
324 # @nextSongLbl.text += "[#{src}] #{key} = #{value}\n"
335 def handle_playlist_loaded( *foo )
336 p "Playlist loaded: #{foo}"
339 def handle_quit( *foo )
340 p "Server quit: #{foo}"
345 def xmmspls_to_lststor( songids, store )
347 current_row = store.append
348 @server.medialib_get_info( id ).notifier do |songinfo|
349 current_row[0] = id.to_s
350 current_row[1] = songinfo[:artist] ? songinfo[:artist].first[1] : ""
351 current_row[2] = songinfo[:title] ? songinfo[:title].first[1] : ""
361 @server.playback_start.notifier do |res|
362 @playBtn.signal_handler_block( @playBtnClkdHndlr ) do
363 @playBtn.active = true
365 @pauseBtn.signal_handler_block( @pauseBtnClkdHndlr ) do
366 @pauseBtn.active = false
368 @stopBtn.signal_handler_block( @stopBtnClkdHndlr ) do
369 @stopBtn.active = false
371 @statusBar.push( @statusBar.get_context_id( "playback" ), "Started playback" )
378 @server.playback_pause.notifier do |res|
379 @playBtn.signal_handler_block( @playBtnClkdHndlr ) do
380 @playBtn.active = false
382 @pauseBtn.signal_handler_block( @pauseBtnClkdHndlr ) do
383 @pauseBtn.active = true
385 @stopBtn.signal_handler_block( @stopBtnClkdHndlr ) do
386 @stopBtn.active = false
388 @statusBar.push( @statusBar.get_context_id( "playback" ), "Paused playback" )
395 @server.playback_stop.notifier do |res|
396 @playBtn.signal_handler_block( @playBtnClkdHndlr ) do
397 @playBtn.active = false
399 @pauseBtn.signal_handler_block( @pauseBtnClkdHndlr ) do
400 @pauseBtn.active = false
402 @stopBtn.signal_handler_block( @stopBtnClkdHndlr ) do
403 @stopBtn.active = true
405 @statusBar.push( @statusBar.get_context_id( "playback" ), "Stopped playback" )
412 if @playbackState == 1
413 @server.playback_pause.notifier do |res|
414 @statusBar.push( @statusBar.get_context_id( "playback" ), "Paused playback" )
417 elsif @playbackState == ( 0 || 2 )
418 @server.playback_start.notifier do |res|
419 @statusBar.push( @statusBar.get_context_id( "playback" ), "Started playback" )
423 warn "Playbackstate unknown"
424 @server.playback_start.notifier do |res|
425 @statusBar.push( @statusBar.get_context_id( "playback" ), "Started playback" )
432 def step_in_playlist( offset = 1 )
433 @server.playlist_set_next_rel( offset ).notifier do
434 @server.playback_tickle.notifier do |res|
435 @statusBar.push( @statusBar.get_context_id( "playback" ), "Skipped playback by #{offset} song#{offset.abs >= 1 ? "s" : ''}" )
443 def handle_repeat_all_state( state )
444 @currentconfig[:"playlist.repeat_all"] = ( state ? "1" : "0" )
445 @rptBtn.signal_handler_block( @rptBtnClkdHndlr ) do
446 @rptBtn.active = state
451 def handle_repeat_one_state( state )
452 @currentconfig[:"playlist.repeat_one"] = ( state ? "1" : "0" )
453 @rpt1Btn.signal_handler_block( @rpt1BtnClkdHndlr ) do
454 @rpt1Btn.active = state
459 def toggle_repeat_all
460 if @currentconfig[:"playlist.repeat_all"] == "1"
461 @server.config_set_value( "playlist.repeat_all", "0" ).notifier do |res|
462 @server.config_get_value( "playlist.repeat_all" ).notifier do |value|
463 self.handle_repeat_all_state( value == "1" ? true : false )
468 elsif @currentconfig[:"playlist.repeat_all"] == "0"
469 @server.config_set_value( "playlist.repeat_all", "1" ).notifier do |res|
470 @server.config_get_value( "playlist.repeat_all" ).notifier do |value|
471 self.handle_repeat_all_state( value == "1" ? true : false )
477 warn "Unknown state: #{@currentconfig[:"playlist.repeat_all"]}"
483 def toggle_repeat_one
484 if @currentconfig[:"playlist.repeat_one"] == "1"
485 @server.config_set_value( "playlist.repeat_one", "0" ).notifier do |res|
486 @server.config_get_value( "playlist.repeat_one" ).notifier do |value|
487 self.handle_repeat_one_state( value == "1" ? true : false )
492 elsif @currentconfig[:"playlist.repeat_one"] == "0"
493 @server.config_set_value( "playlist.repeat_one", "1" ).notifier do |res|
494 @server.config_get_value( "playlist.repeat_one" ).notifier do |value|
495 self.handle_repeat_one_state( value == "1" ? true : false )
501 warn "Unknown state: #{@currentconfig[:"playlist.repeat_one"]}"
508 @server.playlist.entries.notifier do |playlist|
509 @status[:playlist] = playlist
510 @playlistView.model = self.xmmspls_to_lststor( playlist, @playlistStore )
514 @server.coll_query_ids( Xmms::Collection.universe ).notifier do |medialibids|
515 @medialibView.model = self.xmmspls_to_lststor( medialibids, @medialibStore )
519 @server.playback_current_id.notifier do |id|
520 self.handle_playback_current_id id
524 @server.config_list_values.notifier do |res|
526 res.each_pair do |key, value|
527 @currentconfig[key] = value
530 @rpt1Btn.signal_handler_block( @rpt1BtnClkdHndlr ) do
531 @rpt1Btn.active = if( @currentconfig[:"playlist.repeat_one"] == "1" )
537 @rptBtn.signal_handler_block( @rptBtnClkdHndlr ) do
538 @rptBtn.active = if @currentconfig[:"playlist.repeat_all"] == "1"
547 @server.coll_list.notifier do |colls|
548 colls.each do |collname|
549 @server.coll_get( collname ).notifier do |coll|
550 @server.coll_query_ids( coll ).notifier do |ids|
566 class Grux::MainWin < Gtk::Window
570 @mainCont = Gtk::VBox.new
571 @btnCont = Gtk::HBox.new
572 @clntCont = Gtk::Notebook.new
574 @cnnctInput = Gtk::ComboBoxEntry.new
575 @cnnctBtn = Gtk::Button.new "_Connect"
576 @quitBtn = Gtk::Button.new "_Quit"
580 @winDstryHndlr = self.signal_connect :destroy do
584 @cnnctInput.append_text 'tcp://127.0.0.1:9667'
586 @cnnctBtnHndlr = @cnnctBtn.signal_connect :clicked do
587 if not @cnnctInput.active_text.empty?
588 server_address = @cnnctInput.active_text
590 server_address = ENV['XMMS_PATH']
593 @clntCont.append_page( Grux::ClientWin.new( server_address ), Gtk::Label.new( server_address ) )
595 @quitBtnHndlr = @quitBtn.signal_connect :clicked do
599 @btnCont.pack_start @cnnctInput
600 @btnCont.pack_start @cnnctBtn
601 @btnCont.pack_end @quitBtn
603 @mainCont.pack_start @btnCont, false
604 @mainCont.pack_end @clntCont
612 gruxWin = Grux::MainWin.new