#!/usr/bin/env python

import gtk
import gobject
import alsaaudio
import sys

PROG_NAME = "PyVOL"
COPYRIGHT = 'Copyright (c) 2008, Alex Cornejo'
LICENSE = 'This software is released under the terms of the GNU GPL version 2 and above.\n Please read the license at http://www.gnu.org/copyleft/gtpl.html'
AUTHOR = 'Alex Cornejo <acornejo@gmail.com>'
VERSION = '1.0'

MIXER = "PCM"
TIMEOUT = 1000
STEP_SIZE = 10

def get_volume_icon(volume,size=64):
    theme=gtk.icon_theme_get_default()
    if volume <= 0:
        return theme.load_icon('stock_volume-mute',size,0)
    elif volume <= 100.0/3.0:
        return theme.load_icon('stock_volume-min',size,0)
    elif volume <= 200.0/3.0:
        return theme.load_icon('stock_volume-med',size,0)
    else:
        return theme.load_icon('stock_volume-max',size,0)

class VolumeWindow:
    def __init__(self):
        self.win=gtk.Window(gtk.WINDOW_POPUP)
        self.win.set_decorated(False)
        self.win.set_resizable(False)
        self.win.set_position(gtk.WIN_POS_CENTER_ALWAYS)
        self.win.set_deletable(False)
        self.win.set_focus_on_map(False)
        vport=gtk.Viewport()
        vbox=gtk.VBox()
        vport.add(vbox)
        vbox.set_border_width(10)
        self.icon=gtk.Image()
        self.progress=gtk.ProgressBar()
        vbox.add(self.icon)
        vbox.add(self.progress)
        self.win.add(vport)

    def show(self,volume):
        self.icon.set_from_pixbuf(get_volume_icon(volume))
        self.progress.set_fraction(volume/100.0)
        gobject.timeout_add(TIMEOUT,self.hide)
        self.win.show_all()
        gtk.main()

    def hide(self):
        self.win.hide()
        gtk.main_quit()
        return False

class VolumeController:
    def __init__(self,mixer=None):
        self.win=VolumeWindow()
        if mixer:
            self.mixer=alsaaudio.Mixer(mixer)
        else:
            self.mixer=alsaaudio.Mixer()

    def up(self):
        vol=self.mixer.getvolume()
        newvol=min(vol[0]+STEP_SIZE,100)
        self.mixer.setvolume(newvol)

    def down(self):
        vol=self.mixer.getvolume()
        newvol=max(vol[0]-STEP_SIZE,0)
        if newvol == 0:
            self.mute()
        else:
            self.mixer.setvolume(newvol)

    def mute(self):
        try:
            self.mixer.setmute(True)
        except:
            self.mixer.setvolume(0)

    def unmute(self):
        try:
            self.mixer.setmute(False)
        except:
            self.mixer.setvolume(STEP_SIZE)

    def set_vol(self,vol):
        self.mixer.setvolume(vol)

    def get_vol(self):
        return self.mixer.getvolume()[0]

class VolumeIcon(gtk.StatusIcon):
    def __init__(self):
        gtk.StatusIcon.__init__(self)
        self.set_visible(True)
        self.scale_window=gtk.Window(gtk.WINDOW_POPUP)
        if True or self.get_property("orientation") == gtk.ORIENTATION_HORIZONTAL:
            self.scale=gtk.VScale()
            self.scale.set_size_request(16,100)
        else:
            self.scale=gtk.HScale()
            self.scale.set_size_request(100,16)
        self.scale.set_update_policy(gtk.UPDATE_CONTINUOUS)
        self.scale.set_digits(0)
        self.scale.set_value_pos(gtk.POS_TOP)
        self.scale.set_draw_value(False)
        self.scale.set_inverted(True)
        self.scale.set_range(0,100)
        self.scale_window.add(self.scale)
        self.scale_window.connect("leave-notify-event", self.setted)
        self.scale.connect("motion_notify_event", self.setted)
        self.connect("activate", self.clicked)
        self.connect('popup-menu',self.menu)
        self.show_scale=False
        self.calls=0
        self.oldvol=0

        self.update()
        gobject.timeout_add(500,self.update)

    def update(self):
        vol=alsaaudio.Mixer().getvolume()[0]
        self.scale.set_value(vol)
        screen,rect,orientation=self.get_geometry()
        self.set_from_pixbuf(get_volume_icon(vol,rect.height))
        self.set_tooltip("Volume %d%%"%(vol))
        self.set_visible(True)
        return True

    def setted(self,widget,event):
        newvol=int(self.scale.get_value())
        if newvol != self.oldvol:
            alsaaudio.Mixer().setvolume(newvol)
            self.oldvol=newvol
            self.calls=self.calls+1
            gobject.timeout_add(1000,self.close)

    def clicked(self,widget):
        if self.show_scale:
            self.scale_window.hide()
        else:
            screen,rect,orientation=self.get_geometry()
            size=self.scale.size_request()
            x=rect.x
            y=rect.y
            if orientation == gtk.ORIENTATION_HORIZONTAL:
                if rect.y+size[1]+rect.height > screen.get_height():
                    y=y-size[1]
                else:
                    y=y+rect.height
            else:
                if rect.x+size[0]+rect.width > screen.get_width():
                    x=x-size[0]
                else:
                    x=x+rect.width
            self.scale_window.move(x,y)
            self.scale_window.show_all()
        self.show_scale=not self.show_scale

    def menu(self,icon,button,activate_time):
        menu=gtk.Menu()
        about=gtk.ImageMenuItem(gtk.STOCK_ABOUT)
        about.connect('activate', self.about)
        quit=gtk.ImageMenuItem(gtk.STOCK_QUIT)
        quit.connect('activate', lambda x: gtk.main_quit())
        menu.append(about)
        menu.append(quit)
        menu.show_all()
        menu.popup(None,None,gtk.status_icon_position_menu,button,activate_time,icon)

    def about(self,item):
        dlg = gtk.AboutDialog()
        dlg.set_name(PROG_NAME)
        dlg.set_version(VERSION)
        dlg.set_copyright(COPYRIGHT)
        dlg.set_license(LICENSE)
        dlg.set_authors((AUTHOR,))
        dlg.run()
        dlg.destroy()

    def close(self):
        if self.calls > 0:
            self.calls=self.calls-1
        if self.calls == 0:
            self.show_scale=False
            self.scale_window.hide()

def run(args):
    if len(args) > 0:
        ctrl=VolumeController()
        for arg in args:
            if arg.startswith("--mixer="):
                mix=arg.split('=')[1]
                ctrl=VolumeControl(mix)
            elif arg.startswith("--vol="):
                volume=arg.split('=')[1]
                ctrl.set_vol(volume)
            elif arg == "--volume-up":
                ctrl.up()
            elif arg == "--volume-down":
                ctrl.down()
            elif arg == "--mute":
                ctrl.mute()
            elif arg == "--unmute":
                ctrl.unmute()
            elif arg == "--icon":
                icon=VolumeIcon(mix)
                gtk.main()
                return
            else:
                print "Invalid argument %s\n"%arg
                return
        win=VolumeWindow()
        win.show(ctrl.get_vol())
    else:
        print "%s %s     %s\n"%(PROG_NAME,VERSION,COPYRIGHT)
        print "Usage: pyvol [--mixer=MIXER] [--vol=VOL] [--volume-up] [--volume-down] [--mute] [--unmute]"
        print "%s\n"%LICENSE

if __name__ == "__main__":
    run(sys.argv[1:])
