#!/usr/bin/env ruby # vim:syn=ruby # # Copyright (c) 2006 Tilman Sauerbeck (tilman at code-monkey de) # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. require "gtk2" require "xmmsclient" require "xmmsclient_glib" require "singleton" require "pp" PKG_NAME = "snett" DATADIR = "/usr/local/share/#{PKG_NAME}/" SIZE = 64 class Gtk::MenuItem def set_callback(meth, args = []) args.unshift(meth) signal_connect("activate") do Snett::Snett.instance.xmms.send(*args) false end end end class Xmms::Client def playback_next playlist_set_next_rel(1).notifier { playback_tickle } end def playback_prev playlist_set_next_rel(-1).notifier { playback_tickle } end end module Snett VERSION = "0.0.1" class Snett < Gtk::Window attr_reader :icon, :xmms include Singleton def initialize super(Gtk::Window::TOPLEVEL) @menu = nil @tooltips = Gtk::Tooltips.new @xmms = Xmms::Client.new(PKG_NAME) @xmms.connect(ENV["XMMS_PATH"]) @xmms.add_to_glib_mainloop @xmms.on_disconnect { destroy } @broadcasts = [] self.set_title(PKG_NAME) signal_connect("destroy") do @broadcasts.each { |bc| bc.disconnect } Gtk.main_quit end self.resizable = false self.decorated = false @icon = Gdk::Pixbuf.new(DATADIR + "logo#{SIZE}.png") self.resize(@icon.width, @icon.height) #self.colormap = self.screen.rgba_colormap pixmap, mask = @icon.render_pixmap_and_mask(self.colormap, 255) self.shape_combine_mask(mask, 0, 0) # load icon and put it in an eventbox event_box = Gtk::EventBox.new.add(Gtk::Image.new(@icon)) event_box.events = Gdk::Event::BUTTON_PRESS_MASK | Gdk::Event::BUTTON_RELEASE_MASK event_box.signal_connect("button_press_event") do |_, event| if event.button == 1 x, y = Gdk::Display.default.pointer[1, 2] self.begin_move_drag(event.button, x, y, event.time) elsif event.button == 3 menu.popup(event) end false end @broadcasts << @xmms.broadcast_playback_current_id @broadcasts.last.notifier(&method(:on_playback_current_id)) @xmms.playback_current_id. notifier(&method(:on_playback_current_id)) add(event_box) end private def menu @menu ||= Menu.new end def on_playback_current_id(res) return if res.value.zero? @xmms.medialib_get_info(res.value). notifier(&method(:on_mlib_get_info)) end def on_mlib_get_info(res) info = res.value[:server] s = "%s - %s" % [info[:artist], info[:title]] @tooltips.set_tip(self, s, nil) end end class Menu < Gtk::Menu def initialize super [ [Gtk::Stock::MEDIA_PLAY, :playback_start], [Gtk::Stock::MEDIA_PAUSE, :playback_pause], [Gtk::Stock::MEDIA_STOP, :playback_stop] ].each do |(stock, meth)| item = Gtk::ImageMenuItem.new(stock) item.set_callback(meth) append(item) end append(Gtk::SeparatorMenuItem.new) [ [Gtk::Stock::MEDIA_NEXT, :playback_next], [Gtk::Stock::MEDIA_PREVIOUS, :playback_prev] ].each do |(stock, meth)| item = Gtk::ImageMenuItem.new(stock) item.set_callback(meth) append(item) end append(Gtk::SeparatorMenuItem.new) [ [Gtk::Stock::OPEN, "Load playlist...", :load], [Gtk::Stock::ADD, "Append playlist...", :append] ].each do |(stock, text, sym)| item = Gtk::ImageMenuItem.new(text) item.image = Gtk::Image.new(stock, Gtk::IconSize::MENU) item.signal_connect("activate") do PlaylistDialog.new(sym).show_all false end append(item) end item = Gtk::ImageMenuItem.new(Gtk::Stock::CLEAR) item.set_callback(:playlist_clear) append(item) append(Gtk::SeparatorMenuItem.new) item = Gtk::ImageMenuItem.new(Gtk::Stock::ABOUT) item.signal_connect("activate") do props = { "name" => PKG_NAME, "version" => VERSION, "copyright" => "(c) 2006 Tilman Sauerbeck", "logo" => Gdk::Pixbuf.new(DATADIR + "logo128.png") } Gtk::AboutDialog.show(Snett.instance, props) false end append(item) item = Gtk::ImageMenuItem.new(Gtk::Stock::QUIT) item.signal_connect("activate") do Snett.instance.signal_emit("destroy") false end append(item) show_all end def popup(event) super(nil, nil, event.button, event.time) end end class PlaylistDialog < Gtk::Dialog def initialize(mode) super("Playlists", Snett.instance, Gtk::Dialog::DESTROY_WITH_PARENT | Gtk::Dialog::MODAL, [Gtk::Stock::OK, Gtk::Dialog::RESPONSE_ACCEPT], [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_REJECT]) @mode = mode signal_connect("response") do |_, id| on_ok if id == Gtk::Dialog::RESPONSE_ACCEPT destroy end @store = Gtk::ListStore.new(String) @view = Gtk::TreeView.new(@store) renderer = Gtk::CellRendererText.new col = Gtk::TreeViewColumn.new("Name", renderer, :text => 0) @view.append_column(col) scrolled = Gtk::ScrolledWindow.new scrolled.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC) scrolled.add(@view) vbox.add(scrolled) resize(350, 400) Snett.instance.xmms.medialib_playlists_list. notifier(&method(:on_playlists_list)) end private def on_playlists_list(res) res.value.sort.each do |list| next if list[0, 1] == "_" @store.append[0] = list end end def on_ok selection = @view.selection iter = selection.selected return if iter.nil? Snett.instance.xmms.playlist_clear if @mode == :load Snett.instance.xmms.medialib_playlist_load(iter[0]) end end end Gtk.init Snett::Snett.instance.show_all Gtk.main