# This file is part of GNU Enterprise.
#
# GNU Enterprise 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 3, or (at your option) any later version.
#
# GNU Enterprise 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 program; see the file COPYING. If not,
# write to the Free Software Foundation, Inc., 59 Temple Place
# - Suite 330, Boston, MA 02111-1307, USA.
#
# Copyright 2000-2009 Free Software Foundation
#
# $Id: _base.py 10007 2009-10-26 21:19:19Z reinhard $
#
# DESCRIPTION:
# A PyWin32 based user interface driver for GNUe forms.
#
# NOTES:
#

import struct

import win32ui
import win32api
import win32gui
import win32con
import commctrl
import win32clipboard

from gnue.common import events

from gnue.forms.GFForm import *
from gnue.forms.uidrivers._base.widgets._base import *
from gnue.forms.uidrivers.win32.common import *

try:
    from PIL import Image as PILImage
    from PIL import ImageWin
except:
    print 'PIL not installed, no image support...'
    PILImage = None

#TCN_SELCHANGE = (commctrl.TCN_FIRST - 1)
#TCN_SELCHANGING = (commctrl.TCN_FIRST - 2)

# user defined events for edit part of dropdown
WM_TAB = win32con.WM_USER
WM_RETURN = win32con.WM_USER + 1

def _MyCallback( hwnd, windows ):
    windows.append(hwnd)


class Win32Base:
    _connectTable = {}

    def __init__(self, uidriver, styleEx, className, windowTitle, style, x,y, width,height, parent,menu=0,instance=0):
        if parent == 0:
            hparent = 0
        else:
            hparent = parent.GetHwnd()

        self._id = menu
        self._className = className
        self._parent = parent
        self._children = []
        if parent:
            parent._children.append(self)

        self._hwnd = win32gui.CreateWindowEx(styleEx, className, windowTitle, style, x,y, width,height, hparent, menu, instance, None)

        # this is only for SetFont
        self._PyCWnd = win32ui.CreateWindowFromHandle(self._hwnd)

        self._uiDriver = uidriver
        self._uiDriver._win32app._HwndToTkObj[self._hwnd] = self

        if className in ['STATIC', 'BUTTON', 'COMBOBOX', 'LISTBOX', 'EDIT']:
            # subclassing edit control part of the combo box eventhandler
            # to produce incremental search/autocomplete
            if className == 'COMBOBOX':
                self._AutoComplete = True
                windows = []
                try:
                    win32gui.EnumChildWindows(self._hwnd, _MyCallback, windows)
                    self._editHwnd = windows[0]
                    self._oldWndProc1 = win32gui.SetWindowLong(self._editHwnd, win32con.GWL_WNDPROC, self._wndproc1)
                except:
                    pass

            self.Show()
            self._oldWndProc = win32gui.SetWindowLong(self._hwnd, win32con.GWL_WNDPROC, self._wndproc)

    # edit control part of the combo box eventhandler
    def _wndproc1(self, hwnd, msg, wParam, lParam):
        if msg == win32con.WM_LBUTTONDOWN:
            # TODO: only needed if this widget doesn't have the focus yet
            gfObject = self._uiDriver._IdToGFObj[self._id]
            uiObject = self._uiDriver._IdToUIObj[self._id]
            gfObject._event_set_focus(uiObject.widgets.index(self))

        elif msg == win32con.WM_KEYDOWN:
            keycode = wParam
            self._AutoComplete = True

            # let the delete/back keys do their job
            if keycode in (win32con.VK_DELETE, win32con.VK_BACK):
                self._AutoComplete = False

            elif keycode == win32con.VK_TAB:
                # we have to deal with tab and enter
                ShiftDown = (win32api.GetKeyState(win32con.VK_SHIFT) & 0x8000) and 1 or 0
                if ShiftDown:
                    win32gui.SendMessage(self._hwnd, WM_TAB, 0, 1)
                else:
                    win32gui.SendMessage(self._hwnd, WM_TAB, 0, 0)
                return 0

            elif keycode == win32con.VK_RETURN:
                win32gui.SendMessage(self._hwnd, WM_RETURN, 0, 0)
                return 0

            elif keycode in F_KEYS:
                win32gui.SendMessage(self._hwnd, msg, wParam, lParam)
                return 0

        elif msg == win32con.WM_CHAR:
            keycode = wParam
            if keycode in (win32con.VK_TAB, win32con.VK_RETURN):
                return 0

        return win32gui.CallWindowProc(self._oldWndProc1, hwnd, msg, wParam, lParam)

    # main eventhandler
    def _wndproc(self, hwnd, msg, wParam, lParam):
        if msg == win32con.WM_LBUTTONDOWN:
            gfObject = self._uiDriver._IdToGFObj[self._id]
            uiObject = self._uiDriver._IdToUIObj[self._id]

            gfObject._event_set_focus(uiObject.widgets.index(self))

            if gfObject._type == 'GFButton':
                self.SetFocus()
                self._connectTable[self._id](self._id)
            elif gfObject.style == 'checkbox':
                uiObject._request('TOGGLECHKBOX')
            else:
                if gfObject._type != 'GFEntry':
                    cursorPosition = self.getSelectedArea()[1]
                    uiObject._request ('CURSORMOVE', position = cursorPosition)

                return win32gui.CallWindowProc(self._oldWndProc, hwnd, msg, wParam, lParam)

        elif msg == win32con.WM_LBUTTONUP:
            gfObject = self._uiDriver._IdToGFObj[self._id]
            uiObject = self._uiDriver._IdToUIObj[self._id]

            if gfObject._type == 'GFEntry' and gfObject.style == 'default':
                left, right = self.getSelectedArea ()
                if left == right:
                    uiObject._request ('CURSORMOVE', position = left)
                else:
                    uiObject._request ('SELECTWITHMOUSE', position1 = left,
                                                          position2 = right)

            return win32gui.CallWindowProc(self._oldWndProc, hwnd, msg, wParam, lParam)

        elif msg == win32con.WM_KEYDOWN:
            keycode = wParam
            gfObject = self._uiDriver._IdToGFObj[self._id]
            if (keycode in NOT_WM_CHAR_KEYS):

                if gfObject._type == 'GFEntry' and \
                   gfObject.style in ('dropdown', 'listbox') and \
                   keycode not in F_KEYS:
                    return win32gui.CallWindowProc(self._oldWndProc, hwnd, msg, wParam, lParam)

                else:
                    action = None

                    ShiftDown = (win32api.GetKeyState(win32con.VK_SHIFT) & 0x8000) and 1 or 0
                    ControlDown = (win32api.GetKeyState(win32con.VK_CONTROL) & 0x8000) and 1 or 0
                    AltDown = (win32api.GetKeyState(win32con.VK_MENU) & 0x8000) and 1 or 0

                    command, args = GFKeyMapper.KeyMapper.getEvent(
                      keycode,
                      ShiftDown,
                      ControlDown,
                      AltDown)

                    if command:
                        uiObject = self._uiDriver._IdToUIObj[self._id]
                        # handle up and down arrows in multiline edit
                        if gfObject._type == 'GFEntry'  and \
                           hasattr(gfObject,'Char__height') and gfObject.Char__height > 1 and \
                           keycode in (win32con.VK_UP, win32con.VK_DOWN):

                            lines = win32gui.SendMessage(hwnd, win32con.EM_GETLINECOUNT, 0, 0)
                            line = win32gui.SendMessage(hwnd, win32con.EM_LINEFROMCHAR, -1, 0)

                            if line == 0 and keycode == win32con.VK_UP:
                                uiObject._request ('PREVENTRY')
                            elif line == lines-1 and keycode == win32con.VK_DOWN:
                                uiObject._request ('NEXTENTRY')
                            else:
                                win32gui.CallWindowProc(self._oldWndProc, hwnd, msg, wParam, lParam)
                                pos = self.getSelectedArea ()[0]
                                uiObject._request ('CURSORMOVE', position = pos)

                        else:
                            action = events.Event('request%s' % command, triggerName = args)
                            # Add the object's _form to the outgoing event
                            # rather than every event in the function
                            action.__dict__.update({'_form':gfObject._form})
                            uiObject._eventHandler(action)

                    win32gui.SendMessage(hwnd, win32con.EM_SCROLLCARET, 0, 0)


        elif msg == win32con.WM_CHAR:
    
            # The TranslateMessage function generates a WM_CHAR message
            # when the user presses any of the following keys:
            # Any character key
            # backspace
            # enter (carriage return)
            # esc
            # shift+enter (linefeed)
            # tab

            gfObject = self._uiDriver._IdToGFObj[self._id]
            action = None

            keycode = wParam

            ShiftDown = (win32api.GetKeyState(win32con.VK_SHIFT) & 0x8000) and 1 or 0
            ControlDown = (win32api.GetKeyState(win32con.VK_CONTROL) & 0x8000) and 1 or 0
            AltDown = (win32api.GetKeyState(win32con.VK_MENU) & 0x8000) and 1 or 0

            #
            # Sigh... a hack for using <enter> in multiline entries
            #
            if keycode == 13 and \
                not ShiftDown and \
                not ControlDown and \
                not AltDown and \
                gConfigForms('enterIsNewLine') and \
                (hasattr(gfObject,'Char__height') and gfObject.Char__height) > 1:

                command = 'NEWLINE'

            else:

                #
                # Get the event to process from the KeyMapper
                #
                (command, args) = GFKeyMapper.KeyMapper.getEvent(
                  keycode,
                  ShiftDown,
                  ControlDown,
                  AltDown)

            if command == 'NEWLINE':
                action = events.Event('requestKEYPRESS', '\r\n',
                             text='\r\n',
                             code=10)

            elif command and not keycode in NOT_WM_CHAR_KEYS:
                action = events.Event('request%s' % command, triggerName = args)

            else:
                try:
                    char = chr(keycode)
                    import string
                    if char in string.printable or char == "\n" or \
                      128 <= keycode <= 255:
                        action = events.Event('requestKEYPRESS', textDecode(char),
                                     text=textDecode(char),
                                     code=keycode)
                except ValueError:
                    pass

            if action:
                if gfObject._type == 'GFButton' and keycode == 13:
                    self.SetFocus()
                    self._connectTable[self._id](self._id)
            # Add the object's _form to the outgoing event
            # rather than every event in the function
                action.__dict__.update({'_form':gfObject._form})
                uiObject = self._uiDriver._IdToUIObj[self._id]
                uiObject._eventHandler(action)
        
            win32gui.SendMessage(hwnd, win32con.EM_SCROLLCARET, 0, 0)

            if gfObject._type == 'GFEntry':
                if gfObject.style == 'dropdown' or gfObject.style == 'listbox':
                    return win32gui.CallWindowProc(self._oldWndProc, hwnd, msg, wParam, lParam)

        elif msg == win32con.WM_COMMAND:
            gfObject = self._uiDriver._IdToGFObj[self._id]
            uiObject = self._uiDriver._IdToUIObj[self._id]

            if gfObject.style == 'dropdown':
                # Autocompletition goes here:
                # First find if there any element in the dropdown wth prefix matching, then
                # select it with CB_SELECTSTRING, then
                # highlight(select) the not typed yet part of the string
                if win32api.HIWORD(wParam) == win32con.EN_UPDATE and self._AutoComplete:

                    text = win32gui.GetWindowText(hwnd)
                    length = len(text)
                    gs = win32gui.SendMessage(self._editHwnd, win32con.EM_GETSEL, 0, 0)
                    (start, end) = (win32api.LOWORD(gs), win32api.HIWORD(gs))

                    if win32gui.SendMessage(hwnd, win32con.CB_SELECTSTRING, -1, text) == win32con.CB_ERR:
                        win32gui.SetWindowText(self._editHwnd, text)

                    selection = self.GetValue()
                    if selection >= 0:
                        string = uiObject._choices[selection]
                        uiObject._request('REPLACEVALUE',
                                          index=selection, text=string)

                    if end < length:
                        win32gui.SendMessage(self._editHwnd, win32con.EM_SETSEL, start, end)
                    else:
                        win32gui.SendMessage(self._editHwnd, win32con.EM_SETSEL, length, -1)

                # selection from listbox part of dropdown with mouse or arrows
                elif win32api.HIWORD(wParam) == win32con.CBN_SELCHANGE:
                    selection = self.GetValue()
                    if selection >= 0:
                        string = uiObject._choices[selection]
                        uiObject._request('REPLACEVALUE',
                                          index=selection, text=string)
                    return win32gui.CallWindowProc(self._oldWndProc, hwnd, msg, wParam, lParam)
                else:
                    return win32gui.CallWindowProc(self._oldWndProc, hwnd, msg, wParam, lParam)
            else:
                return win32gui.CallWindowProc(self._oldWndProc, hwnd, msg, wParam, lParam)


        elif msg == win32con.WM_PAINT:
            try:
                gfObject = self._uiDriver._IdToGFObj[self._id]
            except:
                return win32gui.CallWindowProc(self._oldWndProc, hwnd, msg, wParam, lParam)

            if PILImage and gfObject._type == 'GFImage':
                win32gui.CallWindowProc(self._oldWndProc, hwnd, msg, wParam, lParam)
                hdc = win32gui.GetDC(self._hwnd)
                self.dib.expose(hdc)
                win32gui.ReleaseDC(self._hwnd,hdc)
            else:
                return win32gui.CallWindowProc(self._oldWndProc, hwnd, msg, wParam, lParam)

        # user defined events for edit part of dropdown
        elif msg == WM_TAB or msg == WM_RETURN:
            gfObject = self._uiDriver._IdToGFObj[self._id]
            uiObject = self._uiDriver._IdToUIObj[self._id]

            text = win32gui.GetWindowText(hwnd)
            win32gui.SendMessage(hwnd, win32con.CB_SELECTSTRING, -1, text)

            selection = self.GetValue()
            if selection == -1:
                selection = 0

            string = uiObject._choices[selection]
            uiObject._request('REPLACEVALUE',
                                index=selection, text=string)
            if lParam == 1:
                action = events.Event('requestPREVENTRY')
            else:
                action = events.Event('requestNEXTENTRY')
            action.__dict__.update({'_form':gfObject._form})
            uiObject._eventHandler(action)
      
        elif msg == win32con.WM_CUT:
            uiObject = self._uiDriver._IdToUIObj[self._id]
      
            (start_pos, end_pos) = Win32Entry.getSelectedArea(self)
            text = win32gui.GetWindowText(hwnd)
            uiObject._request('DELETERANGE',
                                start_pos=start_pos, end_pos=end_pos,
                                position=start_pos)
      
            if win32clipboard.OpenClipboard() == None:
                try:
                    win32clipboard.EmptyClipboard()
                    win32clipboard.SetClipboardText(text[start_pos : end_pos])
                finally:
                    win32clipboard.CloseClipboard()
          
        elif msg == win32con.WM_PASTE:
            uiObject = self._uiDriver._IdToUIObj[self._id]
      
            if win32clipboard.OpenClipboard() == None:
                try:
                    data = win32clipboard.GetClipboardData(win32con.CF_UNICODETEXT)
                    if data:
                        uiObject._request('KEYPRESS', text=data)
                finally:
                    win32clipboard.CloseClipboard()

        else:
            return win32gui.CallWindowProc(self._oldWndProc, hwnd, msg, wParam, lParam)


    def SetFont(self, font):
        self._PyCWnd.SetFont(font,1)

    def GetId(self):
        return self._id

    def GetHwnd(self):
        return self._hwnd

    def Show(self):
        win32gui.ShowWindow(self._hwnd, win32con.SW_SHOWNORMAL)

    def Hide(self):
        try:
            win32gui.ShowWindow(self._hwnd, win32con.SW_HIDE)
        except:
            pass

    def GetParent(self):
        return self._parent

    def GetChildren(self):
        return self._children

    def SetFocus(self):
        try:
            win32gui.SetFocus(self._hwnd)
        except:
            pass

    def Enable(self, enabled):
        if enabled:
            win32gui.EnableWindow(self._hwnd, 1)
        else:
            win32gui.EnableWindow(self._hwnd, 0)

    def Destroy(self):
        win32gui.DestroyWindow(self._hwnd)

    def Connect(self, id, func):
        self._connectTable[id] = func

    
class Win32Window(Win32Base):
    _descriptionTable = {}
    _statusBar = None

    def addDescription(self, id, description):
        self._descriptionTable[id] = description

    def OnWMMenuselect(self, hwnd, msg, wParam, lParam):
        id = win32api.LOWORD(wParam)
        if self._descriptionTable.has_key(id):
            msg = self._descriptionTable[id]
        else:
            msg = ''
        win32gui.SendMessage(self._statusBar.GetHwnd(), commctrl.SB_SETTEXT, 0, msg)

    def OnWMClose(self, hwnd, msg, wParam, lParam):
        self._uiDriver._form.close()

    def OnWMSize(self, hwnd, msg, wParam, lParam):
        for child in self._children:
            # resizing statusbar as needed
            if child._className == commctrl.STATUSCLASSNAME:
                (left, top, right, bottom) = win32gui.GetClientRect(hwnd)
                x = right - left
                statwidths = struct.pack("iiiii",x-75-75-50-50,x-75-75-50,x-75-75,x-75,x)
                win32gui.SendMessage(child.GetHwnd(), commctrl.SB_SETPARTS, 5, statwidths)
                win32gui.SendMessage(child.GetHwnd(), win32con.WM_SIZE, 0, 0)
            # and toolbar too
            elif child._className == commctrl.TOOLBARCLASSNAME:
                win32gui.SendMessage(child.GetHwnd(), commctrl.TB_AUTOSIZE, 0, 0)

    def OnWMCommand(self, hwnd, msg, wParam, lParam):
        # menu/toolbar selection happend'
        self._connectTable[wParam]()

    def OnWMNotify(self, hwnd, msg, wParam, lParam):
        # handling tabbed pages
        format = "iiiiiiiiiii"
        buf = win32gui.PyMakeBuffer(struct.calcsize(format), lParam)
        hwndFrom, idFrom, code, iItem, iSubItem, uNewState, uOldState, uChanged, actionx, actiony, lparam \
                  = struct.unpack(format, buf)
        if code == -552: #commctrl.TCN_SELCHANGING
            window = self._uiDriver._win32app._HwndToTkObj[hwndFrom]
            pageId = win32gui.SendMessage(window.GetHwnd(), commctrl.TCM_GETCURSEL, 0, 0)
            window._children[pageId].Hide()
        elif code == -551: #commctrl.TCN_SELCHANGE
            window = self._uiDriver._win32app._HwndToTkObj[hwndFrom]
            pageId = win32gui.SendMessage(window.GetHwnd(), commctrl.TCM_GETCURSEL, 0, 0)
            window._children[pageId].Show()

            id = window._children[pageId].GetId()
            gfObject = self._uiDriver._IdToGFObj[id]
            gfObject._form._event_page_changed(pageId)


class Win32Entry(Win32Base):

    def OnWMCommand(self, hwnd, msg, wParam, lParam):
        pass
#    print 'Win32Entry.OnWMCommand', hwnd, msg, wParam, lParam

    def SetValue(self, value):
        try:
            gfObject = self._uiDriver._IdToGFObj[self._id]
            if gfObject.style == 'checkbox':
                if value:
                    val = win32con.BST_CHECKED
                else:
                    val = win32con.BST_UNCHECKED
                win32gui.SendMessage(self._hwnd, win32con.BM_SETCHECK, val, 0)
            elif gfObject.style == 'dropdown':
                if value:
                    win32gui.SendMessage(self._hwnd, win32con.CB_SELECTSTRING, -1, value)
                else:
                    win32gui.SendMessage(self._hwnd, win32con.CB_SETCURSEL, 0, 0)
            elif gfObject.style == 'listbox':
                if value:
                    win32gui.SendMessage(self._hwnd, win32con.LB_SELECTSTRING, -1, value)
                else:
                    win32gui.SendMessage(self._hwnd, win32con.LB_SETCURSEL, 0, 0)
            else:
                if hasattr(gfObject,'Char__height') and gfObject.Char__height > 1:
                    corrvalue = '\r\n'.join (value.splitlines ())
                    if len(value) and value[-1] == '\n':
                        corrvalue += '\r\n'
                    win32gui.SetWindowText(self._hwnd, corrvalue)
                else:
                    win32gui.SetWindowText(self._hwnd, str(value))
        except:
            assert gDebug(6, "SetValue '%s' failed" %value)

    def GetValue(self):
        gfObject = self._uiDriver._IdToGFObj[self._id]
        if gfObject.style == 'checkbox':
            val = win32gui.SendMessage(self._hwnd, win32con.BM_GETCHECK, 0, 0) & win32con.BST_CHECKED
            if val:
                return 1
            else:
                return 0
        elif gfObject.style == 'dropdown':
            return win32gui.SendMessage(self._hwnd, win32con.CB_GETCURSEL, 0, 0)
        elif gfObject.style == 'listbox':
            return win32gui.SendMessage(self._hwnd, win32con.LB_GETCURSEL, 0, 0)
        else:
            return win32gui.GetWindowText(self._hwnd)

    def set_cursor_position(self, position):
        win32gui.SendMessage(self._hwnd, win32con.EM_SETSEL, position, position)

    def set_selected_area(self, selection1, selection2):
        win32gui.SendMessage(self._hwnd, win32con.EM_SETSEL, selection1, selection2)

    def getSelectedArea(self):
        gs = win32gui.SendMessage(self._hwnd, win32con.EM_GETSEL, 0, 0)
        return (win32api.LOWORD(gs), win32api.HIWORD(gs))

    def Cut(self):
        win32gui.SendMessage(self._hwnd, win32con.WM_CUT, 0, 0)
    
    def Copy(self):
        win32gui.SendMessage(self._hwnd, win32con.WM_COPY, 0, 0)
    
    def Paste(self):
        win32gui.SendMessage(self._hwnd, win32con.WM_PASTE, 0, 0)
    
    def SelectAll(self):
        self.set_selected_area(0,-1)
        (start_pos, end_pos) = self.getSelectedArea()
    
        uiObject = self._uiDriver._IdToUIObj[self._id]
        uiObject._request('SELECTWITHMOUSE',
                      position1=start_pos, position2=end_pos,
                      cursor=end_pos)
    

class Win32Button(Win32Base):

    def OnWMCommand(self, hwnd, msg, wParam, lParam):
        pass
#    print 'Win32Button.OnWMCommand', hwnd, msg, wParam, lParam


class Win32Page(Win32Base):

    def OnWMNotify(self, hwnd, msg, wParam, lParam):
        pass
#    print 'Win32Page.OnWMNotify', hwnd, msg, wParam, lParam

    def OnWMCommand(self, hwnd, msg, wParam, lParam):
        if win32api.HIWORD(wParam) == win32con.LBN_SELCHANGE:
            lbWindow = self._uiDriver._win32app._HwndToTkObj[lParam]
            id = lbWindow.GetId()
            gfObject = self._uiDriver._IdToGFObj[id]
            uiObject = self._uiDriver._IdToUIObj[id]
            if gfObject.style == 'listbox':
                selection = lbWindow.GetValue()
                string = uiObject._choices[selection]
                uiObject._request('REPLACEVALUE',
                                    index=selection, text=string)

        else:
            window = self._uiDriver._win32app._HwndToTkObj[lParam]
            window.OnWMCommand(hwnd, msg, wParam, lParam)

    def OnWMVScroll(self, hwnd, msg, wParam, lParam):
        sbWindow = self._uiDriver._win32app._HwndToTkObj[lParam]
        id = sbWindow.GetId()
        gfObject = self._uiDriver._IdToGFObj[id]
        uiObject = self._uiDriver._IdToUIObj[id]
        scrollCode = win32api.LOWORD(wParam)

        if scrollCode == win32con.SB_THUMBTRACK:
            format = "IIiiIii"
            size = struct.calcsize (format)
            scrollinfo = struct.pack (format, size, win32con.SIF_ALL, 0, 0, 0, 0, 0)
            win32gui.SendMessage(lParam, win32con.SBM_GETSCROLLINFO, 0, scrollinfo)
            size, mask, min, max, page, pos, trackpos = struct.unpack (format,
                scrollinfo)
            gfObject._event_scrollToRecord (int (trackpos))

        elif scrollCode == win32con.SB_PAGEUP:
            gfObject._event_scrollDelta (- uiObject.pageSize)

        elif scrollCode == win32con.SB_PAGEDOWN:
            gfObject._event_scrollDelta (uiObject.pageSize)

        elif scrollCode == win32con.SB_LINEDOWN:
            gfObject._event_scrollDelta (1)

        elif scrollCode == win32con.SB_LINEUP:
            gfObject._event_scrollDelta (-1)

        elif scrollCode == win32con.SB_BOTTOM:
            gfObject._event_scrollToRecord (uiObject.lastRecord)

        elif scrollCode == win32con.SB_TOP:
            gfObject._event_scrollToRecord (0)


class Win32Image(Win32Base):

    def SetValue(self, value):
        if PILImage is None:
            return

        l,t,r,b = win32gui.GetWindowRect(self._hwnd)
        scrx,scry = (r-l, b-t)
        imgx, imgy = value.size

        gfObject = self._uiDriver._IdToGFObj[self._id]

        scalex = scaley = 1

        fit = gfObject.fit
        if fit == "auto":
            if float(scrx) / imgx < float(scry) / imgy:
                fit = "width"
            else:
                fit = "height"

        if fit == "width":
            scalex = scaley = float(scrx) / imgx
        elif fit == "height":
            scalex = scaley = float(scry) / imgy
        elif fit == "both":
            scalex = float(scrx) / imgx
            scaley = float(scry) / imgy

        if scalex != 1 or scaley != 1:
            value = value.resize((abs(int(imgx * (scalex))), abs(int(imgy * (scaley)))), PILImage.BICUBIC)

        # clear the previous image
        win32gui.SetWindowPos(self._hwnd, 0, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOZORDER)

        # resize the bitmap holder to match the image size again
        win32gui.SetWindowPos(self._hwnd, 0, 0, 0, scrx, scry, win32con.SWP_NOMOVE | win32con.SWP_NOZORDER)

        # convert a PIL image to Dib
        self.dib = ImageWin.Dib(value)

        # paint it
        win32gui.SendMessage(self._hwnd, win32con.WM_PAINT, 0, 0)


#
# UIHelper
#
# Provides a UI widget set of std functions to reduce the number of functions
# require in each UIclass listed below.
#
class UIHelper(UIWidget):
    def _addToCrossRef(self, widget, gfobject, uiobject):
        id = widget.GetId()
        self._uiDriver._IdToGFObj[id]=gfobject
        self._uiDriver._IdToUIObj[id]=uiobject

    def _deleteFromCrossRef(self, widget, object):
        id = widget.GetId()
        try:
            del self._uiDriver._IdToGFObj[id]
            del self._uiDriver._IdToUIObj[id]
        except:
            assert gDebug(6, "_deleteFromCrossRef failed")

    def _ui_set_focus_(self, index):
        self.widgets[index].SetFocus()

    def _ui_focus_in_(self, index):
        # If we are in a grid, exchange label with entry widget
        assert gDebug(6, "_ui_focus_in_ not implemented for win32 driver yet")

    def _ui_focus_out_(self, index):
        # If we are in a grid, exchange entry widget with the label
        assert gDebug(6, "_ui_focus_out_ not implemented for win32 driver yet")

    def _ui_set_current_row_(self, index):
        pass

    def _ui_set_value_(self, index, value):
        widget = self.widgets[index]
        widget.SetValue(textEncode(value))

    def _ui_set_cursor_position_(self, index, position):
        self.widgets[index].set_cursor_position(position)

    def _ui_set_selected_area_(self, index, selection1, selection2):
        self.widgets[index].set_selected_area(selection1, selection2)

    def _create_widget_(self, event, spacer):
        gfObject = event.object

        if hasattr (gfObject, 'Char__y'):
            posY = gfObject.Char__y
            gap  = gfObject._gap + 1
            self.itemY = (posY + spacer * gap) * event.widgetHeight

        newWidget = self._create_widget(event, spacer)
        newWidget.SetFont(self._uiDriver._font)
        if event.initialize:
            self._eventHandler = event.eventHandler
            self._addToCrossRef(newWidget,gfObject,self)

        return newWidget

    def cleanup(self, object):
        for widget in self.widgets[:]:
            id = widget.GetId()
            del self._uiDriver._IdToGFObj[id]
            del self._uiDriver._IdToUIObj[id]
            self.widgets.pop(0)
            self._deleteFromCrossRef(widget, object)


#####################################################################
##
## Keymapper Support
##
#####################################################################
from gnue.forms.input import GFKeyMapper
from gnue.forms.input.GFKeyMapper import vk

# Translate from Win32 keystrokes to our virtual keystrokes
win32KeyTranslations = {
    vk.C      : 3,
    vk.V      : 22,
    vk.X      : 24,
    vk.A      : 1,
    vk.Q      : 17,
    vk.F1     : win32con.VK_F1,        vk.F2        : win32con.VK_F2,
    vk.F3     : win32con.VK_F3,        vk.F4        : win32con.VK_F4,
    vk.F5     : win32con.VK_F5,        vk.F6        : win32con.VK_F6,
    vk.F7     : win32con.VK_F7,        vk.F8        : win32con.VK_F8,
    vk.F9     : win32con.VK_F9,        vk.F10       : win32con.VK_F10,
    vk.F11    : win32con.VK_F11,       vk.F12       : win32con.VK_F12,
    vk.INSERT : win32con.VK_INSERT,    vk.DELETE    : win32con.VK_DELETE,
    vk.HOME   : win32con.VK_HOME,      vk.END       : win32con.VK_END,
    vk.PAGEUP : win32con.VK_PRIOR,     vk.PAGEDOWN  : win32con.VK_NEXT,
    vk.UP     : win32con.VK_UP,        vk.DOWN      : win32con.VK_DOWN,
    vk.LEFT   : win32con.VK_LEFT,      vk.RIGHT     : win32con.VK_RIGHT,
    vk.TAB    : win32con.VK_TAB,
    vk.ENTER  : win32con.VK_RETURN,    vk.BACKSPACE : win32con.VK_BACK,
    vk.RETURN : win32con.VK_RETURN}

GFKeyMapper.KeyMapper.setUIKeyMap(win32KeyTranslations)

F_KEYS = [
  win32con.VK_F1,
  win32con.VK_F2,
  win32con.VK_F3,
  win32con.VK_F4,
  win32con.VK_F5,
  win32con.VK_F6,
  win32con.VK_F7,
  win32con.VK_F8,
  win32con.VK_F9,
  win32con.VK_F10,
  win32con.VK_F11,
  win32con.VK_F12]

NOT_WM_CHAR_KEYS =[
  win32con.VK_HOME,
  win32con.VK_END,
  win32con.VK_UP,
  win32con.VK_DOWN,
  win32con.VK_RIGHT,
  win32con.VK_LEFT,
  win32con.VK_INSERT,
  win32con.VK_DELETE,
  win32con.VK_PRIOR,
  win32con.VK_NEXT] + F_KEYS
