# -*- coding: utf-8 -*-

import time
import gtk
import pango

import adesk.plugin as Plg
import adesk.core as Core

try:
    import wnck
except:
    Core.logINFO('Plugin "tasklist" need python-wnck')
    Core.logINFO(' -- debian/ubuntu : "sudo apt-get install python-wnck"')

def_settings = { 
    'desktop_color':'#EEEEEE', 'desktop_font':'Sans Bold 12',
    'show_desk_pos':True, 'show_desk_name':False,
    }

class Plugin(Plg.Plugin):
    def __init__(self, bar, settings):
        Plg.Plugin.__init__(self, bar, settings)
        self.can_zoom = False
        self.can_show_icon = False
        self.settings = settings
        self.bar = bar

        ## FIXME!!
        for key in def_settings:
            if not self.settings.has_key(key):
                self.settings[key] = def_settings[key]

        self.workspaces = {}
        self.windows = {}
        self.windows_needing_attention = {}

        if bar.cfg['position'] == 'top' or bar.cfg['position'] == 'bottom':
            self.container = gtk.HBox(False, 0)
        else:
            self.container = gtk.VBox(False, 0)
        self.container.set_border_width(0)
        self.container.set_spacing(4)

        screen = wnck.screen_get_default()
        screen.force_update()
        windows = screen.get_windows()

        gtk.gdk.error_trap_push() # silently ignore x errors like a pro

        for i in range(0, screen.get_workspace_count()):
            self.workspace_add(screen, screen.get_workspace(i))

        for window in windows:
            self.window_opened(screen, window)

        self.add(self.container)

        screen.connect('window-opened', self.window_opened)
        screen.connect('window-closed', self.window_closed)
        screen.connect('workspace-created', self.workspace_add)
        screen.connect('workspace-destroyed', self.workspace_remove)
        screen.connect('active-window-changed', self.window_active_changed)

        self.create_menu()
        
        self.screen = screen
        
        if self.settings['show_desk_pos']:
            screen.connect("active_workspace_changed", self.workspace_changed)
            self.active_workspace = gtk.Label()
            self.active_workspace.modify_font(pango.FontDescription(settings['desktop_font']))
            self.active_workspace.set_use_markup(True)
            #~ self.active_workspace.set_alignment(0.5,0.5)
            self.container.pack_end(self.active_workspace, False, False)
            
            if bar.cfg['position'] == 'top' or bar.cfg['position'] == 'bottom':
                self.active_workspace.set_size_request(-1, bar.cfg['icon_size'])
            else:
                self.active_workspace.set_size_request(bar.cfg['icon_size'], -1)
            self.active_workspace.show()
            # update label
            self.workspace_changed(screen, None)

    def showdesktop(self, widget, event):
        showing_windows = not self.screen.get_showing_desktop()
        self.screen.toggle_showing_desktop(showing_windows)

    def window_get_icon(self, window):
        #~ size = self.bar.cfg['icon_size']
        size = 24
        return window.get_icon().scale_simple(size, size, gtk.gdk.INTERP_BILINEAR)

    def workspace_add(self, screen, space):
        ws_event =  gtk.EventBox()
        ws_event.set_visible_window(False)
        ws_event.connect("button-press-event", self.go_to_workspace, space)
        ws_event.connect('enter-notify-event', self.workspace_hover)
        ws_event.connect('leave-notify-event', self.workspace_unhover)

        if self.bar.cfg['position'] == 'top' or self.bar.cfg['position'] == 'bottom':
            workspace = gtk.HBox(False, 0)
        else:
            workspace = gtk.VBox(False, 0)
        workspace.set_border_width(0)
        workspace.set_spacing(8)

        separator = None

        if len(self.workspaces) > 0:
            separator = gtk.Image()
            if self.bar.cfg['position'] == 'top' or self.bar.cfg['position'] == 'bottom':
                img_path = 'images/plugins/tasklist/separator_h.png'
            else:
                img_path = 'images/plugins/tasklist/separator_v.png'
            pbuf = Core.pixbuf_from_file(img_path)
            separator.set_from_pixbuf(pbuf)
            self.container.pack_start(separator, False, False)
            separator.show()

        ws_event.add(workspace)
        self.workspaces[space] = [workspace, separator]

        self.container.pack_start(ws_event, True, True)
        workspace.show()

    def go_to_workspace(self, widget, event, space):
        if event.button==1: # left click
            space.activate(event.time)
        return False

    def workspace_hover(self, widget, event):
        self.bar.can_hide = False
 
    def workspace_unhover(self, widget, event):
        self.bar.can_hide = True
        
    def workspace_remove(self, screen, space):
        self.workspaces[space][0].destroy()
        if self.workspaces[space][1]:
            self.workspaces[space][1].destroy()
        del self.workspaces[space]

    def icon_clicked_cb(self, widget, event):
        window = wnck.window_get(int(widget.get_name()))
        #~ self.window_active = window
        wsp = window.get_workspace()
        active_wsp = self.screen.get_active_workspace()
        
        if event.button == 1: # left click

            if not wsp == active_wsp:
                if wsp:
                    wsp.activate(event.get_time())
                window.activate(event.get_time())
            elif window.is_minimized():
                window.unminimize(event.get_time())
                window.activate(event.get_time())
            elif window == self.window_active:
                window.minimize()
            else:
                window.activate(event.get_time())

        elif event.button==3: # right click
            self.popupMenu.popup(None, None, None, event.button, event.time)
        
        ## need this to avoid call to Bar menu
        return True

    def icon_hover_cb(self, widget, event):
        window = wnck.window_get(int(widget.get_name()))
        pbuf = self.window_get_icon(window)
        image = widget.get_children()[0]
        self.hover = pbuf
        pbuf.saturate_and_pixelate(pbuf, 2, False)
        image.set_from_pixbuf(pbuf)

        ### FIXME !!
        self.bar.can_hide = False
        #~ return True

    def icon_unhover_cb(self, widget, event):
        window = wnck.window_get(int(widget.get_name()))
        pbuf = self.window_get_icon(window)
        image = widget.get_children()[0]
        if window.is_minimized():
            pbuf = self.pixbuf_tint(pbuf, 0.8)
        elif window.is_active():
            self.window_active = window
        else:
            pbuf = self.pixbuf_tint(pbuf, 0.5)

        image.set_from_pixbuf(pbuf)

        ### FIXME !!
        self.bar.can_hide = True
        return True

    def window_add(self, window):
        if window.is_skip_tasklist():
            return
            
        #~ print window
        pbuf = self.window_get_icon(window).copy()
        #~ size = self.bar.cfg['icon_size']
        #~ pbuf = pbuf.scale_simple(size, size, gtk.gdk.INTERP_HYPER)
        
        if window.is_minimized():
            pbuf = self.pixbuf_tint(pbuf, 0.8)
        elif window.is_active():
            self.window_active = window
        else:
            pbuf = self.pixbuf_tint(pbuf, 0.5)

        def window_add(window, workspace):
            icon = gtk.Image()
            icon.set_from_pixbuf(pbuf)
            button = gtk.EventBox()
            button.set_visible_window(False)
            button.set_tooltip_text(window.get_name())

            button.set_name(str(window.get_xid()))
            button.add(icon)
            button.connect('button_press_event', self.icon_clicked_cb)
            button.connect('enter-notify-event', self.icon_hover_cb)
            button.connect('leave-notify-event', self.icon_unhover_cb)
            button.connect('scroll-event', self.on_scroll_event)
            self.workspaces[workspace][0].pack_start(button, False, False)

            # display if necessary! lawl
            if not window.is_skip_pager():
                icon.show()
                button.show()

            return button

        workspace = window.get_workspace()

        buttons = []

        if workspace: buttons.append(window_add(window, workspace))
        else:
            for workspace in self.workspaces: buttons.append(window_add(window, workspace))

        button = buttons[0]
        self.windows[window] = [buttons, window.get_workspace()]

        # XXX: this works only for newly opened windows, fix it
        # (it's because our window has yet to map)
        icon = button.get_children()[0]
        if icon.window:
            x, y = icon.window.get_origin()
            #print "%d %d" % (x, y)
            window.set_icon_geometry(x, y, button.allocation.width, button.allocation.height)


    def window_remove(self, window):
        try:
            wn = self.windows[window]
            for button in wn[0]:
                button.destroy()
        except: pass

    def window_workspace_changed(self, window):
        # xxx: doesn't this lack intelligence?
        self.window_remove(window)
        self.window_add(window)

    def window_name_changed(self, window):
        #~ pass
        buttons = self.windows[window][0]
        for button in buttons:
            button.set_tooltip_text(window.get_name())

    def pixbuf_tint(self, pixbuf, fraction):
        w = pixbuf.get_width()
        h = pixbuf.get_height()
        blank = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, w, h)
        blank.fill(0)

        # dest, dest_x, dest_y, dest_width, dest_height, offset_x, offset_y, scale_x, scale_y, interp_type, overall_alpha
        pixbuf.composite(blank, 0, 0, w, h, 0, 0, 1, 1, gtk.gdk.INTERP_NEAREST, int(fraction * 255.0))
        return blank

    def window_icon_changed(self, window):
        # xxx: doesn't this lack intelligence?
        pos = -1
        if window.get_workspace(): # not sticky
            widget = self.windows[window][0][0]
            parent = widget.get_parent()
            if parent:
                pos = parent.get_children().index(widget)

        self.window_remove(window)
        self.window_add(window)

        if pos != -1:
            widget = self.windows[window][0][0]
            widget.get_parent().reorder_child(widget, pos)

    def window_opened(self, screen, window):
        if window.is_skip_tasklist():
            return
        window.connect('workspace-changed', self.window_workspace_changed)
        window.connect('name-changed', self.window_name_changed)
        window.connect('icon-changed', self.window_icon_changed)
        self.window_add(window)

    def window_closed(self, screen, window):
        if window.is_skip_tasklist():
            return
        self.window_remove(window)
        if self.windows.has_key(window):
            del self.windows[window]


    def window_active_changed(self, screen, previous):
        #~ print "Tasklist : window_active_changed .."
        try:
            pbuf = self.window_get_icon(self.window_active).copy()
            if self.window_active.is_minimized():
                pbuf = self.pixbuf_tint(pbuf, 0.8)
            else:
                pbuf = self.pixbuf_tint(pbuf, 0.5)
            for button in self.windows[self.window_active][0]:
                button.get_children()[0].set_from_pixbuf(pbuf)
        except AttributeError: pass
        except KeyError: pass
        except IndexError: pass # this happens when you close the active window

        self.window_active = screen.get_active_window()

        if self.window_active == None: return

        pbuf = self.window_get_icon(self.window_active)
        
        if self.windows.has_key(self.window_active):
            for button in self.windows[self.window_active][0]:
                #~ print button.get_children()
                try:
                    button.get_children()[0].set_from_pixbuf(pbuf)
                except:
                    pass

    def on_scroll_event(self, widget, event):
        window = wnck.window_get(int(widget.get_name()))
        
        if event.direction == gtk.gdk.SCROLL_DOWN:
                if window.is_minimized():
                    window.unminimize(event.get_time())
                else:
                    window.minimize()
        elif event.direction == gtk.gdk.SCROLL_UP:
            if window.is_maximized():
                window.unmaximize()
            else:
                window.maximize()

    def create_menu(self):
        self.popupMenu = gtk.Menu()
        
        menuPopup = gtk.ImageMenuItem(gtk.STOCK_ZOOM_100)
        menuPopup.get_children()[0].set_label('Maximize')
        menuPopup.connect("activate", self.maximize_active_window)
        self.popupMenu.add(menuPopup)
        
        menuPopup = gtk.ImageMenuItem(gtk.STOCK_UNDO)
        menuPopup.get_children()[0].set_label('MInimize')
        menuPopup.connect("activate", self.minimize_active_window)
        self.popupMenu.add(menuPopup)
        
        menuPopup = gtk.ImageMenuItem(gtk.STOCK_CLOSE)
        menuPopup.connect("activate", self.close_active_window)
        self.popupMenu.add(menuPopup)
        
        self.popupMenu.show_all()
        
    def close_active_window(self, widget):
        timestamp = int(time.time())
        self.window_active.close(timestamp)

    def maximize_active_window(self, widget):
        if self.window_active.is_maximized():
            self.window_active.unmaximize()
        else:
            self.window_active.maximize()
        
    def minimize_active_window(self, widget):
        if self.window_active.is_minimized():
            timestamp = int(time.time())
            self.window_active.unminimize(timestamp)
        else:
            self.window_active.minimize()
        
    def workspace_changed(self, screen, space):
        active_wp = screen.get_active_workspace()
        
        if int(self.settings['show_desk_name']):
            label = active_wp.get_name()
        else:
            label = active_wp.get_number() + 1
            label = str(label)
        
        label = '<span color="%s">%s</span>' % (self.settings['desktop_color'], label)
        self.active_workspace.set_markup(label)
