#!/usr/bin/env python
#coding=utf-8

# Module    : sysmenu.py
# author    : Oswaldo Hernández - oswaldo@soft-com.es
# Date      : 16-11-2009
# License   : wxPython license

"""
Add methods to Frames and Dialogs to use the system menu.    
"""
import wx

if wx.Platform != '__WXMSW__':
    raise NotImplementedError("Only Implemented in Windows")
            
import ctypes
user32 = ctypes.windll.user32
GWL_WNDPROC = -4
WM_DESTROY = 0x0002 # 2
WM_SYSCOMMAND = 0x0112 # 274
MF_BYCOMMAND = 0x00000000L
MF_BYPOSITION = 0x00000400L
MF_SEPARATOR = 0x00000800L #2048
MF_STRING = 0x00000000L # 0
MF_UNCHECKED = 0x00000000L
MF_CHECKED = 0x00000008L
MF_ENABLED = 0x00000000L
MF_GRAYED = 0x00000001L
MF_DISABLED = 0x00000002L
MF_UNHILITE = 0x00000000L
MF_HILITE = 0x00000080L
        
class mixSysMenu:
    
    __SysMenuProcOrg = None
    __SysMenuHandlers = None
    
    # --- Public methods ---
    
    def SysMenuAppendItem(self, MenuText, ItemId, CallBack):            
        """ 
        Append option to system menu 
        MenuText: Text to display
        MenuId: Identifier for the option, the ID must be unique. 
                If the identifier is None will be generated automatically.
        CallBack: method or function to execute when you select the option
            CallBack prototype:
                def OnSysMenu(self, ItemId)
        """
        if not self.IsTopLevel():
            raise ValueError("mixSysMenu only for Top Level Windows")
        
        hWnd = self.GetHandle()
        if not ItemId:  ItemId = wx.NewId()
        SysMenu = user32.GetSystemMenu(hWnd, 0)        
        user32.AppendMenuW(SysMenu, MF_STRING, ItemId, unicode(MenuText))
        
        if not self.__SysMenuProcOrg:            
            Prototype = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_long, ctypes.c_int, ctypes.c_int, ctypes.c_int)
            self.__NewProc = Prototype(self.__SysMenuHandler)
            self.__SysMenuProcOrg = user32.SetWindowLongW(hWnd, GWL_WNDPROC, self.__NewProc)
            self.__SysMenuHandlers = {}
        
        self.__SysMenuHandlers[ItemId] = CallBack
        
    def SysMenuAddSeparator(self):
        """ Append separator line """
        SysMenu = user32.GetSystemMenu(self.GetHandle(), 0)
        user32.AppendMenuW(SysMenu, MF_SEPARATOR, 0, 0)
        
    def SysMenuSetState(self, ItemId, state):
        """
        Set a new state to the menu item.
        States: MF_CHECKED, MF_UNCHECKED, MF_GRAYED, MF_ENABLED, MF_DISABLED, 
                MF_HILITE, MF_UNHILITE
        """
        SysMenu = user32.GetSystemMenu(self.GetHandle(), 0)
        text = self.SysMenuGetMenuString(ItemId)
        user32.ModifyMenuW(SysMenu, ItemId, MF_BYCOMMAND|MF_STRING|state, ItemId, text)
        
    def SysMenuGetState(self, ItemId, state):
        """ Checks if the item is in the state indicated """
        SysMenu = user32.GetSystemMenu(self.GetHandle(), 0)
        mask = user32.GetMenuState(SysMenu, ItemId, MF_BYCOMMAND)
        return mask & state == state

    def SysMenuSetText(self, ItemId, MenuText):
        """ Change item text """
        SysMenu = user32.GetSystemMenu(self.GetHandle(), 0)
        flags = user32.GetMenuState(SysMenu, ItemId, MF_BYCOMMAND)
        user32.ModifyMenuW(SysMenu, ItemId, MF_BYCOMMAND|MF_STRING|flags, ItemId, unicode(MenuText))
        
    def SysMenuGetMenuString(self, ItemId):
        """ Get item text """
        SysMenu = user32.GetSystemMenu(self.GetHandle(), 0)
        buf = ctypes.create_unicode_buffer('\000' * 128)
        user32.GetMenuStringW(SysMenu, ItemId, buf, 100, MF_BYCOMMAND)
        return buf.value
        
    def SysMenuDeleteItem(self, ItemId):
        """ Delete item """
        SysMenu = user32.GetSystemMenu(self.GetHandle(), 0)
        user32.DeleteMenu(SysMenu, ItemId,  MF_BYCOMMAND)
        
    def SysMenuGetItemsList(self):
        """ Get list of (ItemId, ItemText)
        """
        SysMenu = user32.GetSystemMenu(self.GetHandle(), 0)
        nItems = user32.GetMenuItemCount(SysMenu)
        Items = []
        for i in range(nItems):
            itemid = user32.GetMenuItemID(SysMenu, i)
            #if id in self.__SysMenuHandlers or id == 0:
            if itemid in self.__SysMenuHandlers:
                Items.append((itemid, self.SysMenuGetMenuString(itemid)))
        
        return Items

    # --- Internal Handler ---

    def __SysMenuHandler(self, hWnd, msg, wParam, lParam):        
        if msg == WM_DESTROY:
            user32.SetWindowLongW(hWnd, GWL_WNDPROC, self.__SysMenuProcOrg)
            
        elif msg == WM_SYSCOMMAND and wParam in self.__SysMenuHandlers:
            #self.__SysMenuHandlers[wParam](wParam)
            wx.CallAfter(self.__SysMenuHandlers[wParam], wParam)
        
        return user32.CallWindowProcW(self.__SysMenuProcOrg, hWnd, msg, wParam, lParam)

