#!/usr/bin/python -O
"""
PyPanel v2.0 - Lightweight panel/taskbar for X11 window managers
Copyright (c) 2003-2004 Jon Gelo (ziljian@users.sourceforge.net)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
"""

#----------------------------------------------------------------------------                
class Obj(object):
#---------------------------------------------------------------------------- 
    """ Multi-purpose class """
    #----------------------------
    def __init__(self, **kwargs):
    #---------------------------- 
        self.__dict__.update(kwargs)
        
#----------------------------------------------------------------------------
class PyPanel(object):
#----------------------------------------------------------------------------
    #---------------------------
    def __init__(self, display):
    #---------------------------
        """ Initialize and display the panel """
        self.display = display                   # Display obj
        self.screen  = display.screen()          # Screen obj
        self.root    = self.screen.root          # Display root
        self.depth   = self.screen.root_depth    # Screen depth
        self.error   = error.CatchError()        # Error Handler/Suppressor
        self.panel   = {"sections":[]}           # Panel data and layout
        self.colors  = {}                        # Alloc'd colors
        self.hidden  = 0                         # Panel hidden/minimized
        self.focus   = 0                         # Currently focused window
        self.rpm     = None                      # Root pixmap ID
        
        # Misc. initializations
        if SHOWLINES:
            self.lgc = self.root.create_gc(foreground=self.getColor(LINE_COLOR))
        
        global P_LOCATION
        if P_LOCATION:
            P_LOCATION = self.screen.height_in_pixels - P_HEIGHT
            
        global P_WIDTH
        if not P_WIDTH:
            P_WIDTH = self.screen.width_in_pixels - P_START
        
        # Setup the panel's window
        self.window = self.screen.root.create_window(P_START, P_LOCATION,
            P_WIDTH, P_HEIGHT, 0, self.depth, window_class=X.InputOutput,
            visual=X.CopyFromParent, colormap=X.CopyFromParent,
            event_mask=(X.ExposureMask|X.ButtonPressMask|X.ButtonReleaseMask|X.EnterWindowMask))
        ppinit(self.window.id, FONT, ICON)
        
        # Init the panel sections
        if DESKTOP:
            self.panel["sections"].append(DESKTOP)
            self.panel[DESKTOP] = Obj(id="desktop", last=0, color=self.getColor(DESKTOP_COLOR))
        if CLOCK:
            self.panel["sections"].append(CLOCK)
            self.panel[CLOCK] = Obj(id="clock", last=0, color=self.getColor(CLOCK_COLOR))
        if TASKS:
            self.panel["sections"].append(TASKS)
            self.panel[TASKS] = Obj(id="tasks", tasks={}, order=[], last=0)
        if TRAY:
            self.panel["sections"].append(TRAY)
            self.panel[TRAY] = Obj(id="tray", tasks={}, last=0, window=self.window)
            self.createTray(self.display, self.screen)
            
        self.panel["sections"].sort()
        self.panel[self.panel["sections"][-1]].last = 1

        # Init the properties and then start the event loop
        self.setProps(self.display, self.window)
        self.root.change_attributes(event_mask=(X.PropertyChangeMask)) 
        self.window.map()
        self.display.flush()
        self.loop(self.display, self.root, self.window, self.panel)
        
    #------------------------------
    def createTray(self, dsp, scr):
    #------------------------------
        """ Create the System Tray Selection Owner Window """
        self._OPCODE = dsp.intern_atom("_NET_SYSTEM_TRAY_OPCODE")
        manager      = dsp.intern_atom("MANAGER")
        selection    = dsp.intern_atom("_NET_SYSTEM_TRAY_S%d" % dsp.get_default_screen())
          
        # Selection owner window          
        self.selowin = scr.root.create_window(-1, -1, 1, 1, 0, self.depth)
        self.selowin.set_selection_owner(selection, X.CurrentTime)
        self.sendEvent(self.root, manager, [X.CurrentTime, selection,
            self.selowin.id], (X.StructureNotifyMask))
                        
    #--------------------------------------
    def setStruts(self, win, option="set"):
    #--------------------------------------
        """ Set/Unset the panel struts """
        if option == "set":
            if P_LOCATION:
                top = top_start = top_end = 0
                bottom       = P_HEIGHT
                bottom_start = P_START
                bottom_end   = P_START + P_WIDTH
            else:
                top       = P_HEIGHT
                top_start = P_START
                top_end   = P_START + P_WIDTH
                bottom = bottom_start = bottom_end = 0
            
            win.change_property(self._STRUT, Xatom.CARDINAL, 32, [0, 0, top, bottom])
            win.change_property(self._STRUTP, Xatom.CARDINAL, 32,
                [0, 0, top, bottom, 0, 0, 0, 0, top_start, top_end,
                bottom_start, bottom_end])
        else:
            win.change_property(self._STRUT, Xatom.CARDINAL, 32, [0,0,0,0])
            win.change_property(self._STRUTP, Xatom.CARDINAL, 32, [0,0,0,0,0,0,0,0,0,0,0,0])
        
    #----------------------------
    def setProps(self, dsp, win):
    #----------------------------
        """ Set general and window manager specific properties/hints """
        win.set_wm_name("PyPanel")
        win.set_wm_class("pypanel","PyPanel")
        win.set_wm_hints(flags=Xutil.InputHint, input=0)
        win.set_wm_normal_hints(flags=(Xutil.PPosition))
        win.change_property(dsp.intern_atom("_MOTIF_WM_HINTS"),
            dsp.intern_atom("_MOTIF_WM_HINTS"), 32, [0x2, 0x0, 0x0, 0x0, 0x0])
        
        self._SHOWING_DESKTOP = dsp.intern_atom("_NET_SHOWING_DESKTOP")
        self._CHANGE_STATE    = dsp.intern_atom("WM_CHANGE_STATE")
        self._CURRENT_DESKTOP = dsp.intern_atom("_NET_CURRENT_DESKTOP")
        self._SKIP_TASKBAR    = dsp.intern_atom("_NET_WM_STATE_SKIP_TASKBAR")
        self._BELOW           = dsp.intern_atom("_NET_WM_STATE_BELOW")  
        self._ABOVE           = dsp.intern_atom("_NET_WM_STATE_ABOVE")
        self._HIDDEN          = dsp.intern_atom("_NET_WM_STATE_HIDDEN")
        self._SHADED          = dsp.intern_atom("_NET_WM_STATE_SHADED")
        self._STRUTP          = dsp.intern_atom("_NET_WM_STRUT_PARTIAL")
        self._STRUT           = dsp.intern_atom("_NET_WM_STRUT")
        self._NAME            = dsp.intern_atom("_NET_WM_NAME")
        self._RPM             = dsp.intern_atom("_XROOTPMAP_ID")
        self._ICON            = dsp.intern_atom("_NET_WM_ICON", 1)
        
        if EWMH:
            # EWMH specific hints
            self._DESKTOP_NAMES   = dsp.intern_atom("_NET_DESKTOP_NAMES")             
            self._NUM_DESKTOPS    = dsp.intern_atom("_NET_NUMBER_OF_DESKTOPS")
            self._DESKTOP         = dsp.intern_atom("_NET_WM_DESKTOP")
            self._CLIENT_LIST     = dsp.intern_atom("_NET_CLIENT_LIST")
            self._STATE           = dsp.intern_atom("_NET_WM_STATE")
            win.change_property(self._DESKTOP, Xatom.CARDINAL, 32, [0xffffffffL])
            if not XFWM4:
                win.change_property(dsp.intern_atom("_NET_WM_WINDOW_TYPE"),
                    Xatom.ATOM, 32, [dsp.intern_atom("_NET_WM_WINDOW_TYPE_DOCK")])
            self.setStruts(win)
        else:
            # Window Maker specific hints
            self._DESKTOP_NAMES = dsp.intern_atom("_WIN_WORKSPACE_NAMES") 
            self._NUM_DESKTOPS  = dsp.intern_atom("_WIN_WORKSPACE_COUNT")
            self._DESKTOP       = dsp.intern_atom("_WIN_WORKSPACE")
            self._CLIENT_LIST   = dsp.intern_atom("_WIN_CLIENT_LIST")
            self._STATE         = dsp.intern_atom("_WIN_STATE")
            win.change_property(self._DESKTOP, Xatom.CARDINAL, 32, [0])
            win.change_property(self._STATE, Xatom.CARDINAL, 32, [1])
            win.change_property(dsp.intern_atom("_WIN_LAYER"), Xatom.CARDINAL, 32, [10])
            win.change_property(dsp.intern_atom("_WIN_HINTS"), Xatom.CARDINAL, 32, [39])
            
    #-------------------------------------------------
    def setState(self, task, panel, draw=0, win=None):
    #-------------------------------------------------
        """ Set/Update a tasks state and add it to the task list """
        try:
            state = task.obj.get_wm_state()
            if not state:
                task.state = Xutil.NormalState
            else:
                task.state = state["state"]
        except:
            return
            
        if task.state == Xutil.IconicState:
            task.color = self.getColor(MINIMIZED_COLOR)
        elif task.id == self.focus:
            task.color = self.getColor(FOCUSED_COLOR)
        else:
            task.color = self.getColor(TASK_COLOR)
            try:
                if EWMH:
                    if self._SHADED in task.obj.get_full_property(self._STATE,
                        Xatom.ATOM).value:
                        task.color = self.getColor(SHADED_COLOR)
                elif task.obj.get_full_property(self._STATE,
                    Xatom.CARDINAL).value[0] == 32:
                    task.color = self.getColor(SHADED_COLOR)
            except:
                pass
        if draw:
            x = task.x1+P_SPACER
            if APPICONS:
                x += I_WIDTH+P_SPACER
            ppclear(win.id, x, 0, task.x2-x, P_HEIGHT)
            ppfont(win.id, task.color, x, P_HEIGHT, task.x2-x-P_SPACER, task.name)
        
        panel[TASKS].tasks[task.id] = task
        if not task.id in panel[TASKS].order:
            panel[TASKS].order.append(task.id)
            
    #--------------------------
    def getIcon(self, task, x):
    #--------------------------
        """ Get the icon from the given task and draw it at x """
        if not APPICONS: 
            return 0
        
        y = (P_HEIGHT-I_HEIGHT)/2             
        try:
            # _net_wm_icon
            data = task.obj.get_full_property(self._ICON, 0)
            if data:
                data = data.value[:]
                w    = data[0]
                h    = data[1]
                return ppicon(self.window.id, 0, 0, x, y, w, h, I_WIDTH,
                    I_HEIGHT, data[2:w*h+2].tostring())
            else:
                # wmhints icon
                try:
                    hints = task.obj.get_wm_hints()
                    geom  = hints.icon_pixmap.get_geometry()
                    return ppicon(self.window.id, hints.icon_pixmap.id,
                        hints.icon_mask.id, x, y, geom.width, geom.height,
                        I_WIDTH, I_HEIGHT, "")
                except:
                    raise
        except:
            # default icon
            return ppicon(self.window.id, 0, 0, x, y, 0, 0,
                I_WIDTH, I_HEIGHT, "")
    
    #----------------------------------
    def getDesktop(self, task, root=0):
    #----------------------------------
        """ Return the desktop number of the given task obj """
        try:
            if EWMH and root:
                return task.get_full_property(self._CURRENT_DESKTOP,
                    Xatom.CARDINAL).value[0]
            return task.get_full_property(self._DESKTOP, Xatom.CARDINAL).value[0]
        except:
            return None 
            
    #-------------------------
    def getColor(self, color):
    #-------------------------
        """ Function to get/convert/alloc a color given a single hex str """
        try:
            return self.colors[color]
        except:
            pass
            
        r = int("0x"+color[2:4],0)*257
        g = int("0x"+color[4:6],0)*257
        b = int("0x"+color[6:8],0)*257
        c = self.screen.default_colormap.alloc_color(r, g, b)
        
        if not c:
            sys.stderr.write("Error allocating color: %s\n" % color)
            return self.screen.white_pixel
        else:
            self.colors[color] = c.pixel
            return c.pixel
            
    #-----------------------
    def getName(self, task):
    #-----------------------
        """ Return the name of the given task obj """
        try:
            name = None
            if EWMH:
                name = task.get_full_property(self._NAME, X.AnyPropertyType)
            if not name or not EWMH:
                name = task.get_full_property(Xatom.WM_NAME, X.AnyPropertyType)
            if not name:
                return "<no name>"
            else:
                return name.value
        except:
            return "<no name>"
        
    #------------------------------------------------
    def sendEvent(self, win, ctype, data, mask=None):
    #------------------------------------------------
        """ Send a ClientMessage event to the root """
        data = (data+[0]*(5-len(data)))[:5]
        ev = Xlib.protocol.event.ClientMessage(window=win, client_type=ctype,
            data=(32,(data)))

        if not mask:
            mask = (X.SubstructureRedirectMask|X.SubstructureNotifyMask)
        self.root.send_event(ev, event_mask=mask)
                                             
    #----------------------------
    def changeDesktop(self, num):
    #----------------------------
        """ Increase/Decrease the current desktop number by num """
        cur = self.getDesktop(self.root, 1) + num
        tot = self.root.get_full_property(self._NUM_DESKTOPS,
            X.AnyPropertyType).value[0] - 1

        if cur < 0:
            cur = tot
        elif cur > tot:
            cur = 0
            
        if EWMH:
            self.sendEvent(self.root, self._CURRENT_DESKTOP, [cur])
        else:
            self.sendEvent(self.root, self._DESKTOP, [cur])
            
    #---------------------
    def showDesktop(self):
    #---------------------
        """ Toggle between hiding and unhiding ALL applications """
        try:  
            showing = self.root.get_full_property(self._SHOWING_DESKTOP,
                X.AnyPropertyType).value[0]
                
            if showing == 0:     
                self.sendEvent(self.root, self._SHOWING_DESKTOP, [1])
            else:
                self.sendEvent(self.root, self._SHOWING_DESKTOP, [0])
        except:
            pass
        
    #----------------------
    def toggleHidden(self):
    #----------------------
        """ Hide/Unhide the Panel """
        if self.hidden:
            self.window.configure(y=P_LOCATION, height=P_HEIGHT)
            self.setStruts(self.window, "set")
            self.sendEvent(self.window, self._STATE, [0, self._BELOW])
            self.sendEvent(self.window, self._STATE, [1, self._ABOVE])
        else:
            if P_LOCATION:
                y = self.screen.height_in_pixels - 2
            else: 
                y = 0
            
            self.window.configure(y=y, height=2)
            self.setStruts(self.window, "unset")
            self.sendEvent(self.window, self._STATE, [1, self._BELOW])
            self.sendEvent(self.window, self._STATE, [0, self._ABOVE])
            
        self.hidden = not self.hidden
            
    #----------------------------------------
    def toggleMinimize(self, task, traise=1):
    #----------------------------------------
        """ Iconify/Deiconify a task """ 
        if EWMH and not OPENBOX and not XFWM4:
            self.sendEvent(task.obj, self._STATE, [2, self._HIDDEN])
        else:
            if task.state == Xutil.NormalState:
                self.sendEvent(task.obj, self._CHANGE_STATE, [Xutil.IconicState])
            else:
                task.obj.map()
        if traise:
            self.taskRaise(task, 0)
                
    #---------------------------
    def toggleShade(self, task):
    #---------------------------
        """ Shade/Unshade a task """
        if EWMH:
            self.sendEvent(task.obj, self._STATE, [2, self._SHADED])
        else:
            if task.obj.get_full_property(self._STATE, Xatom.CARDINAL).value[0] == 0:
                shade = 32
            else:
                shade = 0
            task.obj.change_property(self._STATE, Xatom.CARDINAL, 32, [shade])
            task.obj.unmap()
            task.obj.map()
            
    #-------------------------
    def taskFocus(self, task):
    #-------------------------
        """ Give focus to an unfocused task else toggle minimization """
        if task.id == self.focus or task.state == Xutil.IconicState:
            self.toggleMinimize(task)
        else:
            self.taskRaise(task, 1)
            
    #----------------------------------
    def taskRaise(self, task, focus=0):
    #----------------------------------
        """ Raise a task """
        if task.state == Xutil.NormalState:
            task.obj.configure(stack_mode=X.Above)
            if focus:
                task.obj.set_input_focus(X.RevertToNone, X.CurrentTime)
    
    #----------------------------------
    def taskLower(self, task, focus=0):
    #----------------------------------
        """ Lower a task """
        if task.state == Xutil.NormalState:
            task.obj.configure(stack_mode=X.Below)
            if focus:
                task.obj.set_input_focus(X.RevertToNone, X.CurrentTime)
                
    #---------------------------------------
    def buttonRelease(self, root, panel, e):
    #---------------------------------------
        """ Button Release event handler """
        x = e.event_x
        for section in panel["sections"]:
            if panel[section].id == "tray":
                continue
            elif panel[section].id == "desktop":
                if x > panel[section].x1 and x < panel[section].x2:
                    desktopButtonEvent(self, e.detail)
                    return
            elif panel[section].id == "clock":
                if x > panel[section].x1 and x < panel[section].x2:
                    clockButtonEvent(self, e.detail)
                    return
            else:
                for t in panel[TASKS].tasks.values():
                    if x > t.x1 and x < t.x2:
                        cdt = self.getDesktop(root, 1)
                        tdt = self.getDesktop(t.obj)
                        if SHOWALL and cdt != tdt and tdt != 0xffffffffL:
                            if SHOWALL == 1:
                                # Move task to current desktop
                                if EWMH:
                                    self.sendEvent(t.obj, self._DESKTOP, [cdt])
                                else:
                                    # Can't move task to current desktop under
                                    # WindowMaker, just switch to its desktop
                                    self.sendEvent(root, self._DESKTOP, [tdt])
                            elif SHOWALL == 2:
                                # Switch to tasks desktop
                                if EWMH:
                                    self.sendEvent(root, self._CURRENT_DESKTOP, [tdt])
                                else:
                                    self.sendEvent(root, self._DESKTOP, [tdt])
                                
                            t.obj.map()
                            t.obj.configure(stack_mode=X.Above)
                        else:
                            taskButtonEvent(self, e.detail, t)
                        return
                
    #-------------------------------------
    def updateBackground(self, root, win):
    #-------------------------------------
        """ Update the panel background """   
        rpm = root.get_full_property(self._RPM, X.AnyPropertyType)
        
        if rpm:
            rpm = rpm.value[0]
        else:
            rpm = root.id
            
        if self.rpm != rpm:
            self.rpm = rpm
            r = int("0x"+BG_COLOR[2:4],0)
            g = int("0x"+BG_COLOR[4:6],0)
            b = int("0x"+BG_COLOR[6:8],0)
            ppshade(win.id, rpm, P_START, P_LOCATION, P_WIDTH, P_HEIGHT,
                r, g, b, SHADE)  
                         
    #-------------------------------------------------
    def updatePanel(self, dsp, root, win, cdt, panel):
    #-------------------------------------------------
        """ Display the tasks, clock and desktop name on the panel """
        curr_x     = 0
        tasks      = panel[TASKS].tasks
        task_width = P_WIDTH
        
        if CLOCK:
            panel[CLOCK].name  = time.strftime(CLOCK_FORMAT, time.localtime())
            panel[CLOCK].width = ppfontsize(panel[CLOCK].name)
            task_width -= panel[CLOCK].width + P_SPACER*2
            
        if DESKTOP:
            try:
                if DESKTOP_NAMES:
                    panel[DESKTOP].name = DESKTOP_NAMES[cdt]
                else:
                    panel[DESKTOP].name = root.get_full_property(
                        self._DESKTOP_NAMES, X.AnyPropertyType).value.split("\x00")[cdt]
            except:
                panel[DESKTOP].name = str(cdt+1)
            panel[DESKTOP].width = ppfontsize(panel[DESKTOP].name)
            task_width -= panel[DESKTOP].width + P_SPACER*2
            
        if TRAY:            
            task_width -= (len(panel[TRAY].tasks)*TRAY_I_WIDTH) + P_SPACER*2

        if tasks:    
            name_limit = (task_width / len(tasks)) - (P_SPACER*2)
            if APPICONS:
                name_limit -= I_WIDTH + P_SPACER
        
        # Clear the panel and add the objects
        self.updateBackground(root, win)
        ppclear(win.id, 0, 0, 0, 0)
              
        for section in panel["sections"]:
            if panel[section].id == "tasks":
                if not tasks:
                    curr_x += task_width
                else:
                    count = 1
                    for tid in panel[TASKS].order:
                        t = tasks[tid]
                        tasks[t.id].x1 = curr_x
                        curr_x += P_SPACER
                        if self.getIcon(t, curr_x):
                            curr_x += I_WIDTH + P_SPACER
                        ppfont(win.id, t.color, curr_x, P_HEIGHT, name_limit, t.name)
                        curr_x += name_limit + P_SPACER
                        if count < len(tasks) and SHOWLINES:
                            win.poly_segment(self.lgc, [(curr_x, 0, curr_x, P_HEIGHT)])
                        tasks[t.id].x2 = curr_x
                        count += 1
                if SHOWLINES and not panel[section].last:
                    win.poly_segment(self.lgc, [(curr_x, 0, curr_x, P_HEIGHT)])
            elif panel[section].id == "tray":
                # System Tray
                y = (P_HEIGHT-TRAY_I_HEIGHT)/2
                curr_x += 2
                for task in panel[TRAY].tasks.values():
                    task.x = curr_x
                    task.y = y   
                    task.obj.configure(x=curr_x, y=y, width=TRAY_I_WIDTH,
                         height=TRAY_I_HEIGHT, onerror=self.error)
                    task.obj.map(onerror=self.error)
                    curr_x += TRAY_I_WIDTH + 1
                if SHOWLINES and not panel[section].last:
                    win.poly_segment(self.lgc, [(curr_x, 0, curr_x, P_HEIGHT)])
            else:
                # Clock or Desktop
                panel[section].x1 = curr_x
                curr_x += P_SPACER
                ppfont(win.id, panel[section].color, curr_x, P_HEIGHT, 0,
                       panel[section].name)
                curr_x += panel[section].width + P_SPACER
                panel[section].x2 = curr_x
                if SHOWLINES and not panel[section].last:
                    win.poly_segment(self.lgc, [(curr_x, 0, curr_x, P_HEIGHT)])
                
    #--------------------------------------------
    def updateTasks(self, dsp, root, win, panel):
    #--------------------------------------------
        """ Update the task list for the current desktop """
        pt = panel[TASKS]
        pt.tasks  = {}
        pt.groups = {}
        pt.order  = []
        
        cdt   = self.getDesktop(root, 1)
        tasks = root.get_full_property(self._CLIENT_LIST, X.AnyPropertyType)
        
        if tasks and tasks.value:
            try:
                self.focus = dsp.get_input_focus().focus.id
            except:
                pass
                
            for task in tasks.value:
                obj    = dsp.create_resource_object("window", task)
                obj_ws = self.getDesktop(obj)
                if obj_ws != None and (SHOWALL or (cdt==obj_ws or obj_ws==0xffffffffL)):
                    try:
                        if self._SKIP_TASKBAR in obj.get_full_property(
                            self._STATE, X.AnyPropertyType).value: continue
                    except:
                        pass
                    try:
                        tclass = obj.get_wm_class() or ()
                        for t in tclass:
                            if t in HIDE_LIST:
                                raise
                        name = self.getName(obj)
                        if name == "PyPanel":
                            continue
                    except:
                        continue
                else:
                    continue
                    
                obj.change_attributes(event_mask=(X.PropertyChangeMask|
                    X.FocusChangeMask|X.StructureNotifyMask))
                task = Obj(id=task, obj=obj, name=name, tclass=tclass, x1=-1, x2=-1)
                self.setState(task, panel)
                    
        self.updatePanel(dsp, root, win, cdt, panel)
        
    #-------------------------------------
    def loop(self, dsp, root, win, panel):
    #-------------------------------------
        """ Event loop - handle events as they occur until we're killed """
        states = [dsp.intern_atom("WM_STATE"), self._STATE]
        props  = [self._CLIENT_LIST, self._CURRENT_DESKTOP, self._DESKTOP]   
        
        while 1:
            while dsp.pending_events():
                e = dsp.next_event()
                if e.type == X.ButtonRelease:
                    self.buttonRelease(root, panel, e)
                elif e.type == X.DestroyNotify:
                    if e.window.id in panel[TASKS].tasks:
                        del panel[TASKS].tasks[e.window.id]
                        panel[TASKS].order.remove(e.window.id)
                    elif TRAY and e.window.id in panel[TRAY].tasks:
                        del panel[TRAY].tasks[e.window.id]
                    if not self.hidden:
                        self.updatePanel(dsp, root, win, self.getDesktop(root, 1), panel)
                elif e.type == X.PropertyNotify:
                    if e.atom in props and not self.hidden:
                        self.updateTasks(dsp, root, win, panel)
                    elif e.atom == self._RPM:
                        win.unmap()
                        self.updateBackground(root, win)
                        win.map()
                        win.change_property(self._DESKTOP, Xatom.CARDINAL, 32, [0xffffffffL])            
                    elif e.window.id in panel[TASKS].tasks:
                        if e.atom in states:
                            self.setState(panel[TASKS].tasks[e.window.id], panel, 1, win)
                        elif e.atom == Xatom.WM_NAME or e.atom == self._NAME:
                            t = panel[TASKS].tasks[e.window.id]
                            name = self.getName(t.obj)
                            if t.name != name:
                                t.name = name
                                x = t.x1 + P_SPACER
                                if APPICONS:
                                    x += I_WIDTH+P_SPACER
                                ppclear(win.id, x, 0, t.x2-x, P_HEIGHT)
                                ppfont(win.id, t.color, x, P_HEIGHT, t.x2-x-P_SPACER, t.name)
                        elif e.atom == Xatom.WM_HINTS and APPICONS:
                            t = panel[TASKS].tasks[e.window.id]
                            x = t.x1 + P_SPACER
                            ppclear(win.id, x, 0, t.x2-x, P_HEIGHT) 
                            self.getIcon(t, x)
                            x += I_WIDTH+P_SPACER
                            ppfont(win.id, t.color, x, P_HEIGHT, t.x2-x-P_SPACER, t.name)
                elif e.type == X.ConfigureNotify and TRAY:
                    if e.window.id in panel[TRAY].tasks:
                        task = panel[TRAY].tasks[e.window.id]
                        task.obj.configure(width=task.w, height=task.h)                                            
                elif e.type == X.ClientMessage and TRAY:
                    if e.window == self.selowin:
                        data = e.data[1][1] # opcode
                        task = e.data[1][2] # taskid
                        if e.client_type == self._OPCODE and data == 0:
                            # SYSTEM_TRAY_REQUEST_DOCK opcode = 0
                            obj = dsp.create_resource_object("window", task)
                            obj.reparent(panel[TRAY].window.id, 0, 0)     
                            obj.change_attributes(event_mask=(X.ExposureMask|X.StructureNotifyMask))
                            panel[TRAY].tasks[task] = Obj(obj=obj, w=TRAY_I_WIDTH, h=TRAY_I_HEIGHT)
                elif e.type == X.EnterNotify and self.hidden:
                    if e.window.id == win.id:
                        self.toggleHidden() 
                        self.updateTasks(dsp, root, win, panel)              
                elif e.type == X.FocusIn:
                    prev_focus = self.focus
                    self.focus = e.window.id
                    for wid in (e.window.id, prev_focus):
                        if wid in panel[TASKS].tasks:
                             self.setState(panel[TASKS].tasks[wid], panel, 1, win)
                elif e.type == X.Expose and e.count == 0:
                    win.change_property(self._DESKTOP, Xatom.CARDINAL, 32, [0xffffffffL])
                    if not self.hidden:
                        self.sendEvent(win, self._STATE, [1, self._SKIP_TASKBAR])
                        self.sendEvent(win, self._STATE, [1, self._ABOVE])
                        self.updateTasks(dsp, root, win, panel)
            
            rs, ws, es = select.select([dsp.display.socket], [], [], CLOCK_DELAY)
            if not rs and not self.hidden:
                if AUTOHIDE:
                    self.toggleHidden()
                elif CLOCK:
                    now = time.strftime(CLOCK_FORMAT, time.localtime())
                    if panel[CLOCK].name != now:
                        panel[CLOCK].name = now
                        width = panel[CLOCK].width + P_SPACER*2 - 1
                        ppclear(win.id, panel[CLOCK].x1+1, 0, width, P_HEIGHT) 
                        ppfont(win.id, panel[CLOCK].color,
                            panel[CLOCK].x1+P_SPACER, P_HEIGHT, 0, now)
            
#----------------------------------------------------------------------------
#                                  Main
#----------------------------------------------------------------------------
from distutils import sysconfig
from ppmodule import ppinit, ppshade, ppicon, ppfont, ppfontsize, ppclear
from Xlib import X, display, error, Xatom, Xutil
import Xlib.protocol.event
import os, popen2, pwd, select, sys, time

if __name__ == "__main__":
    try:
        home = pwd.getpwuid(os.getuid())[5]
        if os.path.isfile("/etc/pypanelrc"):
            execfile("/etc/pypanelrc")
        if not os.path.isfile("%s/.pypanelrc" % home):
            import shutil
            src = "%s/pypanel/pypanelrc" % sysconfig.get_python_lib()
            dst = "%s/.pypanelrc" % home
            shutil.copyfile(src, dst)
            del src, dst
        execfile("%s/.pypanelrc" % home)
        del home
    except StandardError, e:
        sys.stderr.write("\nFailed to open ~/.pypanelrc -\n\n")
        sys.stderr.write(str(e)+"\n\n")
        sys.exit()
        
    if not ICON:
        ICON = "%s/pypanel/ppicon.png" % sysconfig.get_python_lib()
    if not os.access(ICON, os.F_OK|os.R_OK):
        sys.stderr.write("\nFailed to open icon image file -\n\n")
        sys.stderr.write("%s\n\n" % ICON)
        sys.stderr.write("Please check the path and/or read permissions ...\n\n")
        sys.exit()
        
    # Kludge Zone - XFWM4 and Openbox have some quirks, check if they're
    # running.  Also, if Window Maker is running we're not using EWMH.
    EWMH    = 1
    XFWM4   = 0
    OPENBOX = 0
    
    for wm in ("wmaker", "openbox", "xfwm"):
        p = popen2.Popen3("ps aux | grep %s | grep -v grep" % wm, 1)
        p.wait()
        out = p.fromchild.read()
        err = p.childerr.read() 
            
        if err:
            p = popen2.Popen3("ps -ef | grep %s | grep -v grep" % wm, 1)
            p.wait()
            out = p.fromchild.read()
            err = p.childerr.read() 
        if out:
            if wm == "wmaker":
                EWMH = 0
                break
            elif wm == "openbox":
                OPENBOX = 1
                break
            elif wm == "xfwm":
                XFWM4 = 1
                break
    del p
    PyPanel(display.Display())
