# 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: widget.py 9956 2009-10-11 18:54:57Z reinhard $
#
# DESCRIPTION:
# A PyWin32 based user interface driver for GNUe forms.
#
# NOTES:
#

import os
import struct
import types

import win32ui
import win32api
import win32gui
import win32con
import commctrl
import win32print
from win32com.shell import shell

from gnue.forms.uidrivers.win32 import dialogs
from gnue.forms.uidrivers.win32.PrintDialog import PrintDialog
from gnue.forms.uidrivers.win32.common import *
from gnue.forms.uidrivers.win32.widgets._base import UIHelper, Win32Window
import wrappers

# Constants for GetDeviceCaps
HORZRES = 8
VERTRES = 10

_MBOX_KIND = {'Info'    : {'type'   : _('Info'),
                           'icon'   : win32con.MB_ICONINFORMATION,
                           'buttons': win32con.MB_OK},
              'Warning' : {'type'   : _('Warning'),
                           'icon'   : win32con.MB_ICONWARNING,
                           'buttons': win32con.MB_OK},
              'Question': {'type'   : _('Question'),
                           'icon'   : win32con.MB_ICONQUESTION,
                           'buttons': win32con.MB_YESNO},
              'Error'   : {'type'   : _('Error'),
                           'icon'   : win32con.MB_ICONERROR,
                           'buttons': win32con.MB_OK}}

_RESPONSE = {win32con.IDOK    : True,
             win32con.IDYES   : True,
             win32con.IDNO    : False,
             win32con.IDCANCEL: None }

#
# UIForm
#
# Widget set specific function that creates a single instance of a Form widget
#

class UIForm(UIHelper):

    def __init__(self, event):
        self._uiPageList=[]
        UIHelper.__init__(self, event)


    def _create_widget(self, event, spacer):
        object = event.object
        #
        # Size based upon the form definition
        #

        formWidth = int(self._form._layout.Char__width)
        formHeight = int(self._form._layout.Char__height)
        self._visibleSize = (formWidth*self._uiDriver.widgetWidth,
                          formHeight*self._uiDriver.widgetHeight)

        self._formSize = formSize = (formWidth*self._uiDriver.widgetWidth,
                          formHeight*self._uiDriver.widgetHeight)


        # Create the main window.
        if self._form._features['GUI:TOOLBAR:SUPPRESS'] or \
           self._form.style == 'dialog':
            self.buttonbarWidth = 0
            self.buttonbarHeight = 0
        else:
            # TODO: calculate
            self.buttonbarWidth = 13*(24+8) + 6*7 #buttons and separators
            self.buttonbarHeight = 24+8

        if object._layout.tabbed != 'none':
            self.tabHeight = self._uiDriver.widgetHeight + 6
        else:
            self.tabHeight = 0

        if self._form.style == 'dialog':
            style = win32con.WS_POPUP | win32con.WS_CAPTION | win32con.WS_CLIPCHILDREN | win32con.WS_SYSMENU #| win32con.WS_OVERLAPPED
        else:
            style = win32con.WS_OVERLAPPEDWINDOW | win32con.WS_CLIPCHILDREN
        styleEx = 0
        self.mainWindow = Win32Window(self._uiDriver, styleEx, self._uiDriver._wndclass, textEncode(self._form.title),
          style, win32con.CW_USEDEFAULT, 0,
          max(formWidth*self._uiDriver.widgetWidth+20, self.buttonbarWidth),
          formHeight*self._uiDriver.widgetHeight+self.buttonbarHeight+self.tabHeight+83,
          0)

        self._uiDriver._win32app._MainWindowList.append(self.mainWindow)
    
        if self._form.style == 'dialog':
            centerWindow(self.mainWindow.GetHwnd())

        # The window in which pages will be rendered
        if object._layout.tabbed != 'none':
            self._wrapper = wrappers.TabbedWrapper(self)
        else:
            self._wrapper = wrappers.PlainWrapper(self)

        newWidget = self._wrapper.pane
        self._container = newWidget

        # Add Statusbar, Toolbar and Menubar as requested and/or allowed
        if self._form.style != 'dialog':
            if not self._form._features['GUI:STATUSBAR:SUPPRESS']:
                hinst = win32api.GetModuleHandle(None)
                style = win32con.WS_CHILD | win32con.WS_VISIBLE | commctrl.SBARS_SIZEGRIP
                styleEx = 0
                self.statusBar = Win32Window(self._uiDriver, styleEx, commctrl.STATUSCLASSNAME, "",
                  style, 0, 0, 0, 0,
                  self.mainWindow, getNextId(), hinst)
                x=formWidth*self._uiDriver.widgetWidth
                statwidths = struct.pack("iiiii",x-75-75-50-50,x-75-75-50,x-75-75,x-75,x)
                win32gui.SendMessage(self.statusBar.GetHwnd(), commctrl.SB_SETPARTS, 5, statwidths)
                win32gui.SendMessage(self.statusBar.GetHwnd(), win32con.WM_SIZE, 0, 0)
                self.mainWindow._statusBar = self.statusBar

        self._eventHandler = event.eventHandler

        self._wrapper.finalize()

        return newWidget

    #
    # _ui_goto_page_
    #
    # Makes the requested page visible while hiding the others
    #
    def _ui_goto_page_(self,page):
        self._wrapper.setPage(page.widgets[0])

    def _ui_show_(self, modal):
        self.mainWindow.Show()
        if self._form.style == 'dialog':
            win32gui.PumpMessages()


    def _ui_set_title_(self, title):
        win32gui.SetWindowText(self.mainWindow.GetHwnd(), event.title)

    # ---------------------------------------------------------------------------
    # User feedback functions
    # ---------------------------------------------------------------------------

    def _ui_begin_wait_(self):
        win32ui.DoWaitCursor(1)

    # ---------------------------------------------------------------------------

    def _ui_end_wait_(self):
        win32ui.DoWaitCursor(0)

    # ---------------------------------------------------------------------------

    def _ui_beep_(self):
        win32gui.MessageBeep(0)

    # ---------------------------------------------------------------------------

    def _ui_update_status_(self, tip, record_status, insert_status,
            record_number, record_count, page_number, page_count):

        if not hasattr (self, 'statusBar'):
            return

        if tip is not None:
            win32gui.SendMessage(self.statusBar.GetHwnd(), commctrl.SB_SETTEXT, 0,
                    textEncode(tip))

        if record_status:
            win32gui.SendMessage(self.statusBar.GetHwnd(), commctrl.SB_SETTEXT, 1,
                    textEncode(record_status))

        if insert_status:
            win32gui.SendMessage(self.statusBar.GetHwnd(), commctrl.SB_SETTEXT, 2,
                    textEncode(insert_status))

        if record_number and record_count:
            win32gui.SendMessage(self.statusBar.GetHwnd(), commctrl.SB_SETTEXT, 3,
                    ("%s/%s" % (record_number, record_count)).strip())

        if page_number and page_count:
            win32gui.SendMessage(self.statusBar.GetHwnd(), commctrl.SB_SETTEXT, 4,
                    ("%s/%s" % (page_number, page_count)).strip())

        return


    # ---------------------------------------------------------------------------
    # create a modal message box
    # ---------------------------------------------------------------------------

    def _ui_show_message_(self, message, kind, title, cancel):
        """
        This function creates a message box of a given kind and returns True, False
        or None depending on the button pressed.
        @param message: the text of the messagebox
        @param kind: type of the message box. Valid types are 'Info', 'Warning',
            'Question', 'Error'
        @param title: title of the message box
        @param cancel: If True a cancel button will be added to the dialog
        @return: True if the Ok-, Close-, or Yes-button was pressed, False if the
            No-button was pressed or None if the Cancel-button was pressed.
        """
        mbRec  = _MBOX_KIND.get (kind)
        flags = win32con.MB_TASKMODAL | mbRec['icon']

        if title is not None and len (title):
            if isinstance (title, types.StringType):
                title = unicode (title, i18n.encoding)
        else:
            title = mbRec['type']

        if cancel:
            if mbRec ['buttons'] == win32con.MB_OK:
                flags = flags | win32con.MB_OKCANCEL
            elif mbRec ['buttons'] == win32con.MB_YESNO:
                flags = flags | win32con.MB_YESNOCANCEL
        else:
            flags = flags | mbRec['buttons']

        res = win32gui.MessageBox(0, message, title, flags)
        return _RESPONSE [res]
    

      # -------------------------------------------------------------------------
      # Show a file selection dialog
      # -------------------------------------------------------------------------

    def _ui_select_files_(self, title, default_dir, default_file, wildcard,
              mode, multiple, overwrite_prompt, file_must_exist):
          """
            Bring up a dialog for selecting filenames.

            @param title: Message to show on the dialog
            @param default_dir: the default directory, or the empty string
            @param default_file: the default filename, or the empty string
            @param wildcard: a list of tuples describing the filters used by the
                dialog.  Such a tuple constists of a description and a fileter.
                Example: [('PNG Files', '*.png'), ('JPEG Files', '*.jpg')]
                If no wildcard is given, all files will match (*.*)
            @param mode: Is this dialog an open- or a save-dialog.  If mode is
                'save' it is a save dialog, everything else would be an
                open-dialog.
            @param multiple: for open-dialog only: if True, allows selecting
                multiple files
            @param overwrite_prompt: for save-dialog only: if True, prompt for a
                confirmation if a file will be overwritten
            @param file_must_exist: if True, the user may only select files that
                actually exist

            @returns: a sequence of filenames or None if the dialog has been
                cancelled.
          """

          wst = '|'.join(['%s|%s' % (descr, filt) for (descr, filt) in wildcard])
          if not wst:
              wst = '%s (*.*)|*.*' % u_('All files')

          flags = win32con.OFN_EXPLORER
          if mode.lower().startswith('save'):
              open = 0
              if overwrite_prompt:
                  flags |= win32con.OFN_OVERWRITEPROMPT
          else:
              open = 1
              if multiple:
                  flags |= win32con.OFN_ALLOWMULTISELECT

          if file_must_exist:
              flags |= win32con.OFN_FILEMUSTEXIST

          dlg = win32ui.CreateFileDialog(open, '', default_file, flags, \
                                         wst, self.mainWindow._PyCWnd)
          dlg.SetOFNTitle(title)
          if default_dir:
              dlg.SetOFNInitialDir(default_dir)

          result = None
          if dlg.DoModal() == win32con.IDOK:
              if multiple:
                  result = dlg.GetPathNames()
              else:
                  result = [dlg.GetPathName()]

          return result


    # -------------------------------------------------------------------------
    # Select a directory
    # -------------------------------------------------------------------------

    def _ui_select_dir_(self, title, default_dir, new_dir):
        """
        Bring up a dialog for selecting a directory path.

        @param title: Message to show on the dialog
        @param default_dir: the default directory, or the empty string
        @param new_dir: If true, add "Create new directory" button and allow
            directory names to be editable. On Windows the new directory button
            is only available with recent versions of the common dialogs.

        @returns: a path or None if the dialog has been cancelled.
        """
        
        BIF_EDITBOX = 0x16            # editbox
        BIF_NEWDIALOGSTYLE = 0x40     # use the new dialog layout
        BIF_NONEWFOLDERBUTTON = 0x200 # hide new folder button
        
        flags = BIF_EDITBOX | BIF_NEWDIALOGSTYLE
        if not new_dir:
            flags |= BIF_NONEWFOLDERBUTTON

        # TODO: root PIDL
        defaultDir = None
        result = shell.SHBrowseForFolder(self.mainWindow.GetHwnd(), defaultDir,\
                                         title, flags, None, None)
        if result is not None:
            result = result[1]
        return result


    # ---------------------------------------------------------------------------
    # Display an about box
    # ---------------------------------------------------------------------------

    def _ui_show_about_(self, name, version, author, description):

        d = dialogs.AboutBox (self.mainWindow.GetHwnd(), name, version, author, description)
        d.DoModal ()


    # ---------------------------------------------------------------------------
    # Print form screenshot
    # ---------------------------------------------------------------------------

    def _ui_printout_(self, title, subtitle, user):

        try:
            from PIL import Image, ImageWin, ImageGrab
        except:
            print 'PIL not installen, no print support...'
            return
        
        print 'PrintForm start...'
    
        window = self.mainWindow
        l,t,r,b = win32gui.GetWindowRect(window.GetHwnd())
        image = ImageGrab.grab((l+2, t+2, r-2, b-2))
        image.save("tmp.bmp")
    
        # Open the bitmap
        bmp = Image.open ("tmp.bmp")
        print "original bitmap size =", bmp.size
    

        pDialog = PrintDialog()
        pDialog.DoModal()
        try:
            # OK selected
            printer = pDialog['deviceName']
        except KeyError:
            # Cancel selected
            return
        #printer = win32print.GetDefaultPrinter()

        print printer
        phandle = win32print.OpenPrinter(printer)
    
        # Find the printer resolution to scale to
        hDC = win32ui.CreateDC ()
        hDC.CreatePrinterDC () # can optionally put a printer name in here
        printer_resolution = hDC.GetDeviceCaps (HORZRES), hDC.GetDeviceCaps (VERTRES)
        print "printer resolution =", printer_resolution
    
        # Resize the image to fit the page but not to overflow
        ratios = [printer_resolution[0] / bmp.size[0], printer_resolution[1] / bmp.size[1]]
        print "ratios =", ratios
        scale = min (ratios)
        print "scale =", scale
    
        # Create the printer document and send the page
        hDC.StartDoc ("GNUe PrintForm")
        hDC.StartPage ()
    
        dib = ImageWin.Dib (bmp)
        scaled_size = [scale * i for i in bmp.size]
        print "scaled bitmap size =", scaled_size
        dib.draw (hDC.GetHandleOutput (), [0, 0] + scaled_size)
    
        hDC.EndPage ()
        hDC.EndDoc ()
    
        win32print.ClosePrinter(phandle)
        del hDC
        del bmp
        os.remove("tmp.bmp")
    
        print 'PrintForm end.'


    # ---------------------------------------------------------------------------
    # Close the window (actually only hide it)
    # ---------------------------------------------------------------------------

    def _ui_close_(self):

        self.mainWindow.Hide()

        if self._form.style == 'dialog':
            win32gui.PostQuitMessage(0) # Terminate the dialog
