# GNU Enterprise Forms - Win32 UI Driver - UI specific dialogs
#
# Copyright 2001-2009 Free Software Foundation
#
# 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.
#
# $Id: dialogs.py 9956 2009-10-11 18:54:57Z reinhard $

import os
import struct
import textwrap

import win32api
import win32gui
import win32con
import commctrl

from gnue.common.apps import i18n, errors
from gnue.forms.uidrivers.win32.dialog import BaseDialog
from gnue.forms.uidrivers.win32.common import getNextId, centerWindow, textEncode
from gnue.forms import VERSION

BUTTON    = 0x0080
EDIT     = 0x0081
STATIC   = 0x0082
LISTBOX  = 0x0084
DROPDOWN = 0x0085

# =============================================================================
# Exceptions
# =============================================================================

class InvalidFieldTypeError (errors.ApplicationError):
    def __init__ (self, fieldtype):
        msg = u_("%s is not a valid type for an input field") % fieldtype
        errors.ApplicationError.__init__ (self, msg)
    
    
class InputDialog(BaseDialog):
    """
    Dialog class prompting the user for a given number of fields. These field
    definitions are specified as follows:

    A field definition is a tuple having these elements:
    - fieldlabel: This text will be used as label in the left column
    - fieldname: This is the key in the result-dictionary to contain the value
        entered by the user
    - fieldtype: Currently these types are supported:
        - label: The contents of 'fieldlabel' as static text
        - warning: The contents of 'fieldlabel' as static text, formatted as
            warning
        - string: A text entry control
        - password: A text entry control with obscured characters
        - dropdown: Foreach element given in 'elements' a separate ComboBox
            control will be created, where each one has it's own dictionary of
            allowed values. If a value is selected in one control, all others are
            synchronized to represent the same key-value.
    - default: Default value to use
    - masterfield: Used for 'dropdowns'. This item specifies another field
        definition acting as master field. If this master field is changed, the
        allowedValues of this dropdown will be changed accordingly. If a
        masterfield is specified the 'allowedValues' dictionaries are built like
        {master1: {key: value, key: value, ...}, master2: {key: value, ...}}
    - elements: sequence of input element tuples (label, allowedValues). This is
        used for dropdowns only. 'label' will be used as ToolTip for the control
        and 'allowedValues' gives a dictionary with all valid keys to be selected
        in the dropdown.

    @return: If closed by 'Ok' the result is a dictionary with all values entered
      by the user, where the "fieldname"s will be used as keys. If the user has
      not selected a value from a dropdown (i.e. it has no values to select)
      there will be no such key in the result dictionary. If the dialog is
      canceled ('Cancel'-Button) the result will be None.
    """
    _FIELDTYPES = ['label', 'warning', 'string', 'password', 'dropdown', 'image']
    _NO_LABELS  = ['label', 'warning', 'image']

    def __init__ (self, parent, title, fields, cancel = True):
        """
        Create a new input dialog

        @param title: Dialog title
        @param fields: sequence of field definition tuples
        @param cancel: If True add a Cancel button to the dialog
        """
        BaseDialog.__init__(self, parent, title, cancel)
        self.message_map [win32con.WM_CTLCOLORSTATIC] = self.OnCtlColorStatic
        self.message_map [win32con.WM_CLOSE] = self.OnClose
        
        self.fields = fields
        self.inputData  = {}
        self.controlsName = {}
        self.__controls  = []
        self.__labels    = []
        self.__texts     = []
        self.__inputs    = []
        self.__warnings  = []
        self.__images    = {}
        self.__dropdowns = {}
    
        # Now build and add all controls
        for field in fields:
            fieldtype = field[2]
            if not fieldtype in self._FIELDTYPES:
                raise InvalidFieldTypeError, fieldtype
            else:
                self.AddControl(field)
        
        
    def AddControl(self, field):
        label, name, ftype, default, master, elements = field
        cs = win32con.WS_CHILD | win32con.WS_VISIBLE
        es = win32con.WS_EX_CLIENTEDGE

        position = (0, 0, 0, 0)
        if ftype == 'label' or ftype == 'warning':
            ID = getNextId()
            s = cs | win32con.SS_CENTER #| win32con.WS_BORDER
            self.template.append([STATIC, textEncode(label), ID, position, s])
            self.__texts.append (ID)
            if ftype == 'warning':
                self.__warnings.append (ID)

        elif ftype == 'string' or ftype == 'password':
            ID = getNextId()
            s = cs | win32con.SS_LEFT #| win32con.WS_BORDER
            self.template.append([STATIC, textEncode(label), ID, position, s])
            self.__labels.append (ID)
            self.__controls.append (ID)
            self.controlsName [ID] = name

            ID = getNextId()
            s = cs | win32con.WS_TABSTOP #| win32con.WS_BORDER
            if ftype == 'password':
                s = s | win32con.ES_PASSWORD
            self.template.append([EDIT, default, ID, position, s,es])
            self.__inputs.append (ID)

        elif ftype == 'image':
            ID = getNextId()
            s = cs | win32con.SS_BITMAP
            self.template.append([STATIC, None, ID, position, s])
            imageFile = name
            bmp = win32gui.LoadImage(0, imageFile, win32con.IMAGE_BITMAP, \
                                     0, 0, win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE)
            self.__images[ID] = bmp

        elif ftype == 'button':
            ID = getNextId()
            s = cs | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON
            self.template.append([BUTTON, textEncode(label), ID, position, s])

        elif ftype == 'dropdown':
            ID = getNextId()
            s = cs | win32con.SS_LEFT #| win32con.WS_BORDER
            self.template.append([STATIC, textEncode(label), ID, position, s])
            self.__labels.append (ID)
            self.__controls.append (ID)
            self.controlsName [ID] = name
            
            ID = getNextId()
            s = cs | win32con.WS_TABSTOP | win32con.CBS_DROPDOWNLIST
            self.template.append([DROPDOWN, default, ID, position, s])
            self.__inputs.append (ID)
            self.__dropdowns[ID] = [default, master, elements, {}]
      
        self.__controls.append (ID)
        self.controlsName [ID] = name
    
    
    def OnInitDialog(self, hwnd, msg, wparam, lparam):
        BaseDialog.OnInitDialog(self, hwnd, msg, wparam, lparam)

        for ID in self.__images.keys():
            bmCtrl = win32gui.GetDlgItem(hwnd, ID)
            win32gui.SendMessage(bmCtrl, win32con.STM_SETIMAGE, win32con.IMAGE_BITMAP, \
                                 self.__images[ID])

        self.__maxLabelWidth = 0
        for label in self.__labels:
            self.__maxLabelWidth = max(self.__maxLabelWidth, self.Width(label))

        self.dlgWidth = 0
        for control in self.__controls:
            if control in self.__inputs:
                self.dlgWidth = max(self.dlgWidth, self.__maxLabelWidth + self.Width(control))
            else:
                if control not in self.__warnings:
                    self.dlgWidth = max(self.dlgWidth, self.Width(control))
              
        border = 10
        ypos = border
        for control in self.__controls:
            if (control in self.__images.keys()) or (control in self.__texts):
                if control in self.__warnings:
                    dlgItem = win32gui.GetDlgItem(hwnd, control)
                    text = win32gui.GetWindowText(dlgItem)
                    #~ newText = self.wrapText(text, self.dlgWidth)
                    #~ win32gui.SetDlgItemText(hwnd, control, newText)
                    win32gui.SetDlgItemText(hwnd, control, text)
                    
                self.SetPosition(control, (self.dlgWidth-self.Width(control))/2 + border + 8 , ypos)
                ypos += self.Height(control) + 5
            elif control in self.__labels:
                self.SetPosition(control, border, ypos)
            else:
                self.SetPosition(control, border + self.__maxLabelWidth + 10, ypos)
                ypos += self.Height(control) + 3
        
            if control in self.__dropdowns.keys():
                bm = win32gui.GetDlgItem(hwnd, control)
                default, master, elements, rmap = self.__dropdowns[control]
        
                (label, allowed) = elements [0]
                if master is None:
                    data = [allowed]
                else:
                    data = allowed.values ()

                for vdict in data:
                    for key,value in vdict.items():
                        value = textEncode("%s" % value)
                        self.__dropdowns[control][3][value] = key
                        win32gui.SendMessage(bm, win32con.CB_ADDSTRING, 0, value)

                win32gui.SendMessage(bm, win32con.CB_SELECTSTRING, -1, default)
        
        self.SetPosition(win32con.IDCANCEL, self.dlgWidth + 2*border\
                         - self.Width(win32con.IDCANCEL), ypos + 15)
        self.SetPosition(win32con.IDOK, self.dlgWidth + 2*border \
                         - self.Width(win32con.IDCANCEL) \
                         - self.Width(win32con.IDOK) -4, ypos + 15)
        ypos += self.Height(win32con.IDOK) + border
        
        # resize the dialog
        win32gui.SetWindowPos(hwnd, 0,
                                  0, 0,
                                  self.dlgWidth + 2*border + 16, ypos + 45,
                                  win32con.SWP_NOACTIVATE | win32con.SWP_NOZORDER)
        # center the dialog
        centerWindow(hwnd)
    
        # set focus to first input item
        first = win32gui.GetDlgItem(hwnd, self.__inputs[0])
        win32gui.SetFocus(first)
    
    def OnClose(self, hwnd, msg, wparam, lparam):
        self.inputData = None
        win32gui.EndDialog(hwnd, 0)
        win32gui.SetFocus(self.oldfocus)

    def OnCommand(self, hwnd, msg, wparam, lparam):
        id = win32api.LOWORD(wparam)
        if id == win32con.IDOK:
            for control in self.__inputs:
                dlgItem = win32gui.GetDlgItem(self.hwnd, control)
                text = win32gui.GetWindowText(dlgItem)
                if control in self.__dropdowns.keys():
                    key =  self.__dropdowns[control][3].get(text)
                    if key:
                        self.inputData [self.controlsName [control]] = key
                else:
                    self.inputData [self.controlsName [control]] = text
            win32gui.EndDialog(hwnd, 1)
            win32gui.SetFocus(self.oldfocus)

        elif id == win32con.IDCANCEL:
            self.inputData = None
            win32gui.EndDialog(hwnd, 0)
            win32gui.SetFocus(self.oldfocus)
      
    def OnCtlColorStatic(self, hwnd, msg, wparam, lparam):
        if win32gui.GetDlgCtrlID(lparam) in self.__warnings:
            win32gui.SetBkMode(wparam, win32con.TRANSPARENT) #OPAQUE
            win32gui.SetTextColor(wparam, win32api.RGB(0XFF, 0, 0))
            return 1
      
    def Width(self, id):
        item = win32gui.GetDlgItem(self.hwnd, id)
        dc = win32gui.GetDC(item)
        if (id in self.__labels) or (id in self.__texts):
            text = win32gui.GetWindowText(item)
            if '\n' in text:
                w = max ([win32gui.GetTextExtentPoint32(dc, t) [0] for t in text.split ('\n')])
            else:
                w, h = win32gui.GetTextExtentPoint32(dc, text)
            return w
        elif id in self.__inputs:
            w, h = win32gui.GetTextExtentPoint32(dc, 'W'*8)
            return w
        else:
            l,t,r,b = win32gui.GetWindowRect(item)
            return r-l

    def Height(self, id):
        item = win32gui.GetDlgItem(self.hwnd, id)
        dc = win32gui.GetDC(item)
        if (id in self.__labels) or (id in self.__texts):
            text = win32gui.GetWindowText(item)
            if '\n' in text:
                h = sum ([win32gui.GetTextExtentPoint32(dc, t) [1] for t in text.split ('\n')])
            else:
                w, h = win32gui.GetTextExtentPoint32(dc, text)
            return h
        elif id in self.__inputs:
            w, h = win32gui.GetTextExtentPoint32(dc, 'W')
            return h + 6
        else:
            l,t,r,b = win32gui.GetWindowRect(item)
            return b-t

    def SetPosition(self, id, x, y):
        item = win32gui.GetDlgItem(self.hwnd, id)
        flags = win32con.SWP_NOACTIVATE | win32con.SWP_NOZORDER
        w = self.Width(id)
        h = self.Height(id)
    
        win32gui.SetWindowPos(item, self.hwnd, x, y, w, h, flags)
    
    #~ def wrapText(self, text, width):
        #~ textSoFar = ""
        #~ thisLine = ""
        #~ for part in text.split('\n'):
            #~ for word in part.split():
                #~ if win32gui.GetTextExtentPoint32(dc, thisLine + word)[0] > width:
                    #~ textSoFar += thisLine + " \n"
                    #~ thisLine = word + " "
                #~ else:
                    #~ thisLine += word + " "

            #~ textSoFar += thisLine + " \n"
            #~ thisLine = ""
      
        #~ return textSoFar
    

# =============================================================================
# About Box
# =============================================================================
class AboutBox (BaseDialog):

    def __init__ (self, parent, name, version, author, description):
        """
        """
        title = textEncode(u_("About %s") % name)
        BaseDialog.__init__(self, parent, title, cancel= False)

        self.__boxes    = []
        self.__labels   = []
        self.__contents = []

        # Upper box with info about GNUe Forms
        self.template.append (self.__addBox (textEncode(u_('GNUe Forms')), 8, 44))
        self.template.append (self.__addLabel (self.__labels, textEncode(u_('Version:')), 20))
        self.template.append (self.__addLabel (self.__labels, textEncode(u_('Driver:')), 34))

        self.template.append (self.__addLabel (self.__contents, textEncode(VERSION), 20))
        self.template.append (self.__addLabel (self.__contents, 'win32', 34))

        # Lower box with info about the form currently displayed
        self.template.append (self.__addBox (textEncode(u_('Form Information')), 56, 74))
        self.template.append (self.__addLabel (self.__labels, textEncode(u_('Name:')), 74))
        self.template.append (self.__addLabel (self.__labels, textEncode(u_('Version:')), 88))
        self.template.append (self.__addLabel (self.__labels, textEncode(u_('Author:')), 102))
        self.template.append (self.__addLabel (self.__labels, textEncode(u_('Description:')), 116))

        self.template.append (self.__addLabel (self.__contents, textEncode(name), 74))
        self.template.append (self.__addLabel (self.__contents, textEncode(version), 88))
        self.template.append (self.__addLabel (self.__contents, textEncode(author), 102))
        descr = '\n'.join (textwrap.wrap (description))
        self.template.append (self.__addLabel (self.__contents, textEncode(descr), 116))
    
    
    # ---------------------------------------------------------------------------
    # Finalize the dialog's initialization
    # ---------------------------------------------------------------------------
    def OnInitDialog(self, hwnd, msg, wparam, lparam):
        BaseDialog.OnInitDialog(self, hwnd, msg, wparam, lparam)
        """
        Recalculate sizes of all labels and boxes.
        """
        # Stretch all labels in the left column and calculate the widest label
        maxW  = 0
        flags = win32con.SWP_NOMOVE | win32con.SWP_NOOWNERZORDER | \
                win32con.SWP_NOZORDER | win32con.SWP_SHOWWINDOW

        for (ix, itemId) in enumerate (self.__labels):
            item = win32gui.GetDlgItem(hwnd, itemId)
            dc = win32gui.GetDC(item)
            text = win32gui.GetWindowText(item)
            (width, height) = win32gui.GetTextExtentPoint32(dc, text)
            maxW = max (maxW, width)

            win32gui.SetWindowPos(item, 0, 0, 0, width, height, flags)


        # Reposition and stretch all labels in the second column
        maxBottom = 0
        right = 0
        flags = win32con.SWP_NOOWNERZORDER | \
                win32con.SWP_NOZORDER | win32con.SWP_SHOWWINDOW

        for (ix, itemId) in enumerate (self.__contents):
            item = win32gui.GetDlgItem(hwnd, itemId)
            dc = win32gui.GetDC(item)
            text = win32gui.GetWindowText(item)
            if '\n' in text:
                width  = max ([win32gui.GetTextExtentPoint32(dc, p) [0] for p in text.split ('\n')])
                height = sum ([win32gui.GetTextExtentPoint32(dc, p) [1] for p in text.split ('\n')])
            else:
                (width, height) = win32gui.GetTextExtentPoint32(dc, text)

            (left, top, w, h) = win32gui.GetWindowRect(item)
            left, top = win32gui.ScreenToClient(self.hwnd, (left, top))
            left = maxW + 40

            win32gui.SetWindowPos (item, 0, left, top, width, height, flags)
            right = max ((left + width), right)
            maxBottom =  max (top + height, maxBottom,)

        # Now stretch all boxes to fit the newly calculated width
        right += 8
        flags  = win32con.SWP_NOMOVE | win32con.SWP_NOOWNERZORDER | \
                 win32con.SWP_NOZORDER | win32con.SWP_SHOWWINDOW
        for itemId in self.__boxes:
            item = win32gui.GetDlgItem(hwnd, itemId)
            (left, top, r, bottom) = win32gui.GetWindowRect(item)
            left, top = win32gui.ScreenToClient(self.hwnd, (left, top))
            r, bottom = win32gui.ScreenToClient(self.hwnd, (r,bottom))
            win32gui.SetWindowPos (item, 0, 0, 0, right, bottom - top, flags)

        # Resize the dialog itself to the new width
        right += 24
        flags = win32con.SWP_NOACTIVATE | win32con.SWP_NOZORDER
        win32gui.SetWindowPos (self.hwnd, 0, 0, 0, right, maxBottom + 100, flags)

        # Re-Center the Ok button
        flags = win32con.SWP_NOOWNERZORDER | win32con.SWP_NOZORDER | \
                win32con.SWP_SHOWWINDOW | win32con.SWP_NOSIZE
        item = win32gui.GetDlgItem(hwnd, win32con.IDOK)
        (l, t, r, b) = win32gui.GetWindowRect(item)
        l, t = win32gui.ScreenToClient(self.hwnd, (l, t))
        r, b = win32gui.ScreenToClient(self.hwnd, (r, b))
        left = right / 2 - (r - l) / 2
        win32gui.SetWindowPos (item, 0, left, maxBottom + 25, 0, 0, flags)
    
        centerWindow(hwnd)
    

    # ---------------------------------------------------------------------------
    # Create a resource definition for a label
    # ---------------------------------------------------------------------------
    def __addLabel (self, collection, label, row):

        s = win32con.WS_CHILD | win32con.WS_VISIBLE
        ID = getNextId()
        collection.append (ID)
        return ['Static', label, ID,  (16, row, 5, 14), s]


    # ---------------------------------------------------------------------------
    # Create a resource sequence for a labeled box
    # ---------------------------------------------------------------------------
    def __addBox (self, label, top, height):

        self.__boxes.append (getNextId())
        style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_TABSTOP | \
                win32con.BS_GROUPBOX
        return ['button', label, self.__boxes [-1], (6, top, 282, height), style]

    
# =============================================================================
# This class implements a dialog for displaying exceptions
# =============================================================================

class ExceptionDialog (BaseDialog):

    _TITLE = {'system'     : _("GNUe Internal System Error"),
              'admin'      : _("GNUe Unexpected Error"),
              'application': _("GNUe Application Error")}
  
    _FORMAT = {
       'system': u_("An unexpected internal error has occured:\r\n%s.\r\n"
                    "This means you have found a bug in GNU Enterprise. "
                    "Please report it to gnue-dev@gnu.org"),
       'admin': u_("An unexpected error has occured:\r\n%s.\r\n"
                   "Please contact your system administrator."),
       'application': u_("An unexpected error has occured:\r\n%s.\r\n"
                         "Please contact your system administrator.")}
  
    # ---------------------------------------------------------------------------
    # Constructor
    # ---------------------------------------------------------------------------
  
    def __init__ (self, parent, group, name, message, detail):
  
        BaseDialog.__init__(self, parent, self._TITLE.get (group, _('Error')), cancel = False, ok = False)
        self.message_map [win32con.WM_CTLCOLORSTATIC] = self.OnCtlColorStatic
    
        self.detail = detail.replace('\n','\r\n')
        
        cs = win32con.WS_CHILD | win32con.WS_VISIBLE
        position = (0, 0, 0, 0)
    
        self.iconID = ID = getNextId()
        s = cs | win32con.SS_ICON #| win32con.WS_BORDER
        self.template.append([STATIC, 'icon', ID, (5,8,32,32), s])
    
        self.messageID = ID = getNextId()
        s = cs | win32con.SS_LEFT
        self.template.append([STATIC, textEncode(self._FORMAT.get (group, "%s") % message), \
                              ID, position, s])
    
        s = cs | win32con.WS_TABSTOP | win32con.BS_DEFPUSHBUTTON
        self.template.append([BUTTON, textEncode(u_('Close')), win32con.IDCLOSE, (0, 0, 56, 15), s])
    
        self.detailsID = ID = getNextId()
        s = cs | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON
        self.template.append([BUTTON, textEncode(u_('>> Details')), ID, (0, 0, 56, 15), s])
    
        self.detailID = ID = getNextId()
        s = cs | win32con.ES_MULTILINE | win32con.ES_READONLY | win32con.WS_TABSTOP #\
#           | win32con.ES_AUTOVSCROLL | win32con.WS_VSCROLL
        es = win32con.WS_EX_STATICEDGE
        self.template.append([EDIT, textEncode(self.detail), ID, position, s, es])
    
        self._showsDetail  = False
    
    
    def OnInitDialog(self, hwnd, msg, wparam, lparam):
        BaseDialog.OnInitDialog(self, hwnd, msg, wparam, lparam)
    
        item = win32gui.GetDlgItem(self.hwnd, self.detailID)
        win32gui.EnableWindow(item, 0)
    
        ico = win32gui.LoadIcon(0, win32con.IDI_ERROR)
        bmCtrl = win32gui.GetDlgItem(self.hwnd, self.iconID)
        win32gui.SendMessage(bmCtrl, win32con.STM_SETICON, ico, 0)
    
        self.Recalculate()
    
    
    def OnCommand(self, hwnd, msg, wparam, lparam):
        id = win32api.LOWORD(wparam)
        if id == win32con.IDCLOSE:
            win32gui.EndDialog(hwnd, 1)
            win32gui.SetFocus(self.oldfocus)
      
        elif id == self.detailsID:
            self._showsDetail = not self._showsDetail
            if self._showsDetail:
                item = win32gui.GetDlgItem(self.hwnd, self.detailID)
                win32gui.EnableWindow(item, 1)
                win32gui.SetDlgItemText(hwnd, id, u_("<< Details"))
                self.Recalculate()
            else:
                item = win32gui.GetDlgItem(self.hwnd, self.detailID)
                win32gui.EnableWindow(item, 0)
                win32gui.SetDlgItemText(hwnd, id, u_(">> Details"))
                self.Recalculate()
        
    def OnCtlColorStatic(self, hwnd, msg, wparam, lparam):
        if win32gui.GetDlgCtrlID(lparam) == self.detailID:
            win32gui.SetBkMode(wparam, win32con.OPAQUE) #TRANSPARENT) #
            win32gui.SetTextColor(wparam, win32api.RGB(0, 0, 0))
            return 1
      
    def Recalculate(self):
        border = 10
        
        dlgWidth = self.Width(self.detailsID) + self.Width(win32con.IDCLOSE) + 20
        dlgWidth = max(dlgWidth, self.Width(self.messageID))
        if self._showsDetail:
            dlgWidth = max(dlgWidth, self.Width(self.detailID))
       
        ypos = border
    
        self.SetPosition(self.messageID, border+40, ypos)
        ypos += self.Height(self.messageID) + 10
        
        if self._showsDetail:
            self.SetPosition(self.detailID, border, ypos)
            ypos += self.Height(self.detailID) + 10
        else:
            dlgItem = win32gui.GetDlgItem(self.hwnd, self.detailID)
            win32gui.SetWindowPos(dlgItem, 0, 0, 0, 0, 0, 0)
      
        self.SetPosition(self.detailsID, dlgWidth + 2*border \
                         - self.Width(self.detailsID) - 8, ypos + 10)
    
        self.SetPosition(win32con.IDCLOSE, dlgWidth + 2*border \
                         - self.Width(self.detailsID) \
                         - self.Width(win32con.IDCLOSE) -15, ypos + 10)
    
        ypos += self.Height(win32con.IDCLOSE) + border
        
        # resize the dialog
        win32gui.SetWindowPos(self.hwnd, 0,
                                  0, 0,
                                  dlgWidth + 2*border + 10, ypos + 45,
                                  win32con.SWP_NOACTIVATE | win32con.SWP_NOZORDER \
                                  | win32con.SWP_NOMOVE)
    
        # center the dialog
        centerWindow(self.hwnd)
    
    
    def Width(self, id):
        item = win32gui.GetDlgItem(self.hwnd, id)
        dc = win32gui.GetDC(item)
        if id == self.messageID or id == self.detailID:
            text = win32gui.GetWindowText(item)
            # GetWindowText has only 512 byte buffer, sigh...
            if id == self.detailID:
                text = self.detail
            if '\n' in text:
                w = max ([win32gui.GetTextExtentPoint32(dc, t) [0] for t in text.split ('\n')])
            else:
                w, h = win32gui.GetTextExtentPoint32(dc, text)
            return w
        else:
            l,t,r,b = win32gui.GetWindowRect(item)
            return r-l
      
      
    def Height(self, id):
        item = win32gui.GetDlgItem(self.hwnd, id)
        dc = win32gui.GetDC(item)
        if id == self.messageID or id == self.detailID:
            text = win32gui.GetWindowText(item)
            if id == self.detailID:
                text = self.detail
            if '\n' in text:
                h = sum ([win32gui.GetTextExtentPoint32(dc, t) [1] -2 for t in text.split ('\n')])
            else:
                w, h = win32gui.GetTextExtentPoint32(dc, text)
            #~ if id == self.detailID:
                #~ h += -8 # extra for WS_EX_STATICEDGE
            return h
        else:
            l,t,r,b = win32gui.GetWindowRect(item)
            return b-t
      
      
    def SetPosition(self, id, x, y):
        item = win32gui.GetDlgItem(self.hwnd, id)
        win32gui.SetWindowPos(item, 0,
                              x, y,
                              self.Width(id), self.Height(id),
                              win32con.SWP_NOACTIVATE | win32con.SWP_NOZORDER)


if __name__ == '__main__':
    dialog = ExceptionDialog('system', 'name', 'message', 'detail')
    dialog.DoModal ()

    desc = "This is a quite long description of the application.\n" \
           "It also contains newlines as well as a lot of text. This text " \
           "get's continued in the third line too.\n" \
           "And here comes the rest. Here we go."
    #desc = "Hey boyz, that thingy is quite complicated"
    dialog = AboutBox ('Foobar', '1.0', 'Frodo', desc)
    dialog.DoModal ()
   
     # ---------------------------------------------------------------------------

    cname = {'c1': 'demoa', 'c2': 'demob'}
    ckey  = {'c1': 'ck-A', 'c2': 'ck-B'}

    wija = {'c1': {'04': '2004', '05': '2005'},
            'c2': {'24': '2024', '25': '2025', '26': '2026'}}

    codes = {'24': {'241': 'c-24-1', '242': 'c-24-2'},
             '25': {'251': 'c-25-1'}}

    fields = [('Foo!', 'c:/gnue.bmp', 'image',
               None, None, []),
              ('Username', '_username', 'string', 'frodo', None, \
                [('Name of the user', None)]),
              ('Password', '_password', 'password', 'foo', None, [('yeah',1)]),
              ('Foobar', '_foobar', 'dropdown', 'frob', None, \
                  [('single', {'trash': 'Da Trash', 'frob': 'Frob'})]),
              ('Multi', '_multi', 'dropdown', '100', None, \
                  [('name', {'50': 'A 50', '100': 'B 100', '9': 'C 9'}),
                  ('sepp', {'50': 'se 50', '100': 'se 100', '9': 'se 9'})]),
              ('Noe', '_depp', 'label', 'furz', None, []),
              ('Das ist jetzt ein Fehler', None, 'warning', None, None, []),
              ('Firma', 'company', 'dropdown', 'c1', None,
                  [('Name', cname), ('Code', ckey)]),
              ('Wirtschaftsjahr', 'wija', 'dropdown', '05', 'company',
                  [('Jahr', wija)]),
              ('Codes', 'codes', 'dropdown', None, 'wija',
                  [('Code', codes)])]

    dialog = InputDialog('Foobar', fields)
    dialog.DoModal()
    print dialog.inputData
