# an example plugin for mpdtunes that builds a trayicon.

import gtk
import gobject
from pympd.modules import trayicon
import sys

__name = 'Trayicon'
__version = '0.01'
__author = "Natan 'whatah' Zohar"
__blurb = 'Adds a trayicon that can control mpd to the system tray and optionally pops up tooltips when the song changes.'


def secToString(secs):
    try:
        secs = int(secs)
        minutes = secs / 60
        seconds = secs % 60
        if seconds < 10:
#            seconds = "0%(seconds)s" % locals()
            return "%(minutes)s:0%(seconds)s"%locals()

        return "%(minutes)s:%(seconds)s"%locals()
    except:
        return '0:00'

class TrayIconTips(gtk.Window):
    """Custom tooltips derived from gtk.Window() that allow for markup text and multiple widgets, e.g. a progress bar. ;)"""
    # For placing the tooltip win only; internal spacing is done in glade
    MARGIN = 4
    
    def __init__(self, widget=None):
        gtk.Window.__init__(self, gtk.WINDOW_POPUP)
        # from gtktooltips.c:gtk_tooltips_force_window
        self.set_app_paintable(True)
        self.set_resizable(False)
        self.set_name("gtk-tooltips")
        self.connect('expose-event', self._on__expose_event)

        if widget:
            self.add(widget)
        else:
            self.add(gtk.Label())

        self._show_timeout_id = -1
        self.timer_tag = None
        
    # from gtktooltips.c:gtk_tooltips_draw_tips
    def _calculate_pos(self, widget):
        screen = widget.get_screen()
        x, y = widget.window.get_origin()
        w, h = self.size_request()

        if widget.flags() & gtk.NO_WINDOW:
            x += widget.allocation.x
            y += widget.allocation.y
            
        pointer_screen, px, py, _ = screen.get_display().get_pointer()
        if pointer_screen != screen:
            px = x
            py = y

        monitor_num = screen.get_monitor_at_point(px, py)
        monitor = screen.get_monitor_geometry(monitor_num)
        
        # If the tooltip goes off the screen horizontally, realign it so that
        # it all displays.
        if (x + w) > monitor.width:
            x = monitor.width - w

        # If the tooltip goes off the screen vertically (i.e. the system tray
        # icon is on the bottom of the screen), realign the icon so that it
        # shows above the icon.
        if ((y + h + widget.allocation.height + self.MARGIN) >
            monitor.y + monitor.height):
            y = y - h - self.MARGIN
        else:
            y = y + widget.allocation.height + self.MARGIN

        return x, y

    def _event_handler (self, widget):
        widget.connect_after("event-after", self._motion_cb)

    def _motion_cb (self, widget, event):
        if event.type == gtk.gdk.LEAVE_NOTIFY:
            self._remove_timer()
        if event.type == gtk.gdk.ENTER_NOTIFY: 
            self._start_delay(widget)

    def _start_delay (self, widget):
        self.timer_tag = gobject.timeout_add(500, self._tips_timeout, widget)

    def _tips_timeout (self, widget):
        gtk.gdk.threads_enter()
        self._real_display(widget)
        gtk.gdk.threads_leave()

    def _remove_timer(self):
        self.hide()
        if self.timer_tag:
            gobject.source_remove(self.timer_tag)
        self.timer_tag = None

    # from gtktooltips.c:gtk_tooltips_paint_window
    def _on__expose_event(self, window, event):
        w, h = window.size_request()
        window.style.paint_flat_box(window.window,
                                    gtk.STATE_NORMAL, gtk.SHADOW_OUT,
                                    None, window, "tooltip",
                                    0, 0, w, h)
        return False
    
    def _real_display(self, widget):
        x, y = self._calculate_pos(widget)
        w, h = self.size_request()
        self.move(x, y)
        self.resize(w, h)
        self.show()

    # Public API
    
    def set_text(self, text):
        self._label.set_text(text)

    def hide(self):
        gtk.Window.hide(self)
        gobject.source_remove(self._show_timeout_id)
        self._show_timeout_id = -1

    def display(self, widget):
        if self._show_timeout_id != -1:
            return
        
        self._show_timeout_id = gobject.timeout_add(500, self._real_display, widget)
        gobject.timeout_add(2500, self.hide)

    def set_tip (self, widget):
        self.widget = widget
        self._event_handler (self.widget)

    def add_widget (self, widget_to_add):
        self.widget_to_add = widget_to_add
        self.add(self.widget_to_add)

def mpd_pause(obj, mpd):
    mpd.pause()

def mpd_next(obj, mpd):
    mpd.next()

def mpd_prev(obj, mpd):
    mpd.prev()

def mpd_play(obj, mpd):
    mpd.play()

def mpd_quit(obj, mainWindow):
    e = gtk.gdk.Event(gtk.gdk.DELETE)
    mainWindow.event(e)
    
class Trayicon:

    # called when we load the plugin. we get passed the mainWindow, plugin menu
    # item as well as a connection to mpd.
    def _init(self, data, get):
        self.xml, self.pluginMenu, self.pympd, self.base_dir, self.parser = data
        self.mainWindow = self.xml.get_widget("mainWindow")
        self.data = data
        self.readConfig()
        self.buildIcon()
        self.buildMenu()
        self.buildPluginMenu(self.pluginMenu)
        self.prev_event_time = 0

    def _conf(self, hasConf=False):
        return False

    # update plugin, data is of type mpdclient.Status
    def _spin(self, data, songChanged=False):
        status = data
        stateStr = status.state
        try:
            self.tip_pbar.set_fraction(float(data.elapsedTime)/data.totalTime)
            self.tip_pbar.set_text("%s/%s" %(secToString(data.elapsedTime), secToString(data.totalTime)))
        except Exception, e:
            print e
        if stateStr == 'play':
#            self.image.set_from_stock(gtk.STOCK_MEDIA_PLAY, gtk.ICON_SIZE_LARGE_TOOLBAR)
            self.image.set_from_file("%s/pixmaps/play.png"%self.base_dir)
            if songChanged:
                song = self.pympd.getCurrentSong()
                l = "<span><b>%s</b></span>"%(song.title)
                if (song.artist.strip() != ""):
                    l += "\n<span size='small'>by %s</span>"%(song.artist)
                if (song.album.strip() != ""):
                    l += "\n<span size='small'>from %s</span>" % (song.album)

                self.tip_label.set_markup(l)

                if self.popup:
                    self.tooltip.display(self.icon)
            else:
                try:
                    song = self.song
                except Exception, e:
                    song = self.pympd.getCurrentSong()
            self.song = song
        elif stateStr == 'pause':
#            self.image.set_from_stock(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_LARGE_TOOLBAR)
            self.image.set_from_file("%s/pixmaps/pause.png"%self.base_dir)
        self.stateStr = stateStr
#        self.tooltip.enable()

    # called on plugin unloaded
    def _unload(self):
        self.pluginMenuEntry.destroy()
        self.icon.destroy()
        self.menu.destroy()
        self.mainWindow.present()
        self.saveConfig()
        return

    # callback for click on icon
    def icon_clicked(self, obj, event):
        # hack to prevent 2 simultaneously firing click events
        event_time = event.get_time()
        if event_time == self.prev_event_time: 
            return
        self.prev_event_time = event_time
        # endhack
        if event.button == 1:
            if not self.mainWindow.get_property('visible'):
                self.mainWindow.show()
                self.mainWindow.deiconify()
            else:
                self.mainWindow.hide()
        elif event.button == 3:
            self.menu.popup(None, None, None, event.button, event.time)
            pass # bring up right click

    # callback for menu item in pluginMenu
    def toggleIcon(self, obj):
        self.visible = not self.visible
        if self.icon.get_property('visible'):
            self.icon.hide()
        else:
            self.icon.show()
    
    def togglePopup(self, obj):
        self.popup = not self.popup

    # Init Functions
    def readConfig(self):
        self.visible = True
        self.popup = True
        if self.parser.has_section('trayicon'):
            try: 
                self.visible = self.parser.get('trayicon', 'visible')
                self.popup = self.parser.get('trayicon', 'popup')
            except:
                print "Error reading trayicon config"
        if self.visible == 'False':
            self.visible = False
        else:
            self.visible = True
        if self.popup == 'False':
            self.popup = False
        else:
            self.popup = True

    def saveConfig(self):
        if not self.parser.has_section('trayicon'):
            self.parser.add_section('trayicon')
        self.parser.set('trayicon', 'visible', str(self.visible))
        self.parser.set('trayicon', 'popup', str(self.popup))
            



    # build the menu for pluginMenu. 
    # NOTE: you build a menuItem() and append it to the plugin menu passed in from pympd
    def buildPluginMenu(self, pluginMenu):
        self.pluginMenuEntry = gtk.MenuItem('Trayicon')
        pluginSubMenu = gtk.Menu()
        pluginSubMenu.set_title('Trayicon')
        menuEntry = gtk.CheckMenuItem('Show Icon in Tray')
        menuEntry.set_active(self.visible)
        menuEntry.connect('activate', self.toggleIcon)
        pluginSubMenu.append(menuEntry)
        self.popupEntry = gtk.CheckMenuItem('Popup Tooltip on Song Change')
        self.popupEntry.set_active(self.popup)
        self.popupEntry.connect('activate', self.togglePopup)
        pluginSubMenu.append(self.popupEntry)
        self.pluginMenuEntry.set_submenu(pluginSubMenu)
        self.pluginMenuEntry.show_all()
        pluginMenu.append(self.pluginMenuEntry)
    
    # build the right click menu for the icon.
    def buildMenu(self):
        self.menu = gtk.Menu()
        
        play = gtk.ImageMenuItem(gtk.STOCK_MEDIA_PLAY)
        pause = gtk.ImageMenuItem(gtk.STOCK_MEDIA_PAUSE)
        next = gtk.ImageMenuItem(gtk.STOCK_MEDIA_NEXT)
        prev = gtk.ImageMenuItem(gtk.STOCK_MEDIA_PREVIOUS)
        separator = gtk.MenuItem()
        quit = gtk.ImageMenuItem(gtk.STOCK_QUIT)
        
        self.menu.append(play)
        self.menu.append(pause)
        self.menu.append(prev)
        self.menu.append(next)
        self.menu.append(separator)
        self.menu.append(quit)

        play.connect('activate', mpd_play, self.pympd)
        pause.connect('activate', mpd_pause, self.pympd)
        next.connect('activate', mpd_next, self.pympd)
        prev.connect('activate', mpd_prev, self.pympd)
        quit.connect('activate', mpd_quit, self.mainWindow)

        self.menu.show_all()

    def buildIcon(self):
        self.icon = trayicon.TrayIcon("pympd")
        self.stateStr = None
        self.image = gtk.Image()
#        self.image.set_from_stock(gtk.STOCK_MEDIA_PLAY, gtk.ICON_SIZE_LARGE_TOOLBAR)
        self.image.set_from_file("icons/play.png")
        colorbox = gtk.EventBox()
        colorbox.set_size_request(48, -1)
        colorbox.set_border_width(1)
        colorbox.set_state(gtk.STATE_SELECTED)
        box = gtk.VBox()
        self.tip_label = gtk.Label()
        self.tip_pbar = gtk.ProgressBar()
        box.set_spacing(5)
        box.set_border_width(6)
        box.add(self.tip_label)
        box.add(self.tip_pbar)
        align = gtk.HBox()
        align.pack_start(colorbox)
        align.pack_start(box)
#        align.set_padding(5, 5, 5, 5)
	
        align.show_all()

        self.tooltip = TrayIconTips(align)
        self.trayEvent = gtk.EventBox()
        self.trayEvent.add(self.image)
        self.trayEvent.connect('button-press-event', self.icon_clicked)
        self.tooltip.set_tip(self.trayEvent)
        self.icon.add(self.trayEvent)
        self.icon.show_all()
        self.icon.set_property('visible', self.visible)
