# GUI Application automation and testing library
# Copyright (C) 2006 Mark Mc Mahon
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# This library 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
#    Free Software Foundation, Inc.,
#    59 Temple Place,
#    Suite 330,
#    Boston, MA 02111-1307 USA

"""Functions to retrieve properties from a window handle

These are implemented in a procedural way so as to to be
useful to other modules with the least conceptual overhead
"""

__revision__ = "$Revision: 727 $"

import ctypes

import win32functions
import win32defines
import win32structures

import findwindows # for children


#=========================================================================
def text(handle):
    "Return the text of the window"

    length = ctypes.c_long()
    win32functions.SendMessageTimeout(
        handle,
        win32defines.WM_GETTEXTLENGTH,
        0,
        0,
        win32defines.SMTO_ABORTIFHUNG,
        100,  # .1 of a second
        ctypes.byref(length))

    length = length.value

    textval = ''
    if length:
        length += 1

        buffer_ = ctypes.create_unicode_buffer(length)

        ret = win32functions.SendMessage(
            handle, win32defines.WM_GETTEXT, length, ctypes.byref(buffer_))

        if ret:
            textval = buffer_.value

    return textval

#=========================================================================
def classname(handle):
    "Return the class name of the window"
    class_name = (ctypes.c_wchar * 257)()
    win32functions.GetClassName (handle, ctypes.byref(class_name), 256)
    return class_name.value


#=========================================================================
def parent(handle):
    "Return the handle of the parent of the window"
    return win32functions.GetParent(handle)

#=========================================================================
def style(handle):
    "Return the style of the window"
    return win32functions.GetWindowLong (handle, win32defines.GWL_STYLE)

#=========================================================================
def exstyle(handle):
    "Return the extended style of the window"
    return win32functions.GetWindowLong (handle, win32defines.GWL_EXSTYLE)

#=========================================================================
def controlid(handle):
    "Return the ID of the control"
    return win32functions.GetWindowLong (handle, win32defines.GWL_ID)

#=========================================================================
def userdata(handle):
    "Return the value of any userdata associated with the window"
    return win32functions.GetWindowLong (handle, win32defines.GWL_USERDATA)

#=========================================================================
def contexthelpid(handle):
    "Return the context help id of the window"
    return win32functions.GetWindowContextHelpId (handle)

#=========================================================================
def iswindow(handle):
    "Return True if the handle is a window"
    return bool(win32functions.IsWindow(handle))

#=========================================================================
def isvisible(handle):
    "Return True if the window is visible"
    return bool(win32functions.IsWindowVisible(handle))

#=========================================================================
def isunicode(handle):
    "Teturn True if the window is a unicode window"
    return bool(win32functions.IsWindowUnicode(handle))

#=========================================================================
def isenabled(handle):
    "Return True if the window is enabled"
    return bool(win32functions.IsWindowEnabled(handle))

#=========================================================================
def clientrect(handle):
    "Return the client rectangle of the control"
    client_rect = win32structures.RECT()
    win32functions.GetClientRect(handle, ctypes.byref(client_rect))
    return client_rect

#=========================================================================
def rectangle(handle):
    "Return the rectangle of the window"
    rect = win32structures.RECT()
    win32functions.GetWindowRect(handle, ctypes.byref(rect))
    return rect

#=========================================================================
def font(handle):
    "Return the font as a LOGFONTW of the window"

    # get the font handle
    font_handle = win32functions.SendMessage(
        handle, win32defines.WM_GETFONT, 0, 0)

    # if the fondUsed is 0 then the control is using the
    # system font (well probably not - even though that is what the docs say)
    # instead we switch to the default GUI font - which is more likely correct.
    if not font_handle:

        # So just get the default system font
        font_handle = win32functions.GetStockObject(win32defines.DEFAULT_GUI_FONT)

        # if we still don't have a font!
        # ----- ie, we're on an antiquated OS, like NT 3.51
        if not font_handle:

            # ----- On Asian platforms, ANSI font won't show.
            if win32functions.GetSystemMetrics(win32defines.SM_DBCSENABLED):
                # ----- was...(SYSTEM_FONT)
                font_handle = win32functions.GetStockObject(
                    win32defines.SYSTEM_FONT)
            else:
                # ----- was...(SYSTEM_FONT)
                font_handle = win32functions.GetStockObject(
                    win32defines.ANSI_VAR_FONT)

    else:
        fontval = win32structures.LOGFONTW()
        ret = win32functions.GetObject(
	    font_handle, ctypes.sizeof(fontval), ctypes.byref(fontval))

    # Get the Logfont structure of the font of the control
    fontval = win32structures.LOGFONTW()
    ret = win32functions.GetObject(
        font_handle, ctypes.sizeof(fontval), ctypes.byref(fontval))

    # The function could not get the font - this is probably
    # because the control does not have associated Font/Text
    # So we should make sure the elements of the font are zeroed.
    if not ret:
        fontval = win32structures.LOGFONTW()

    # if it is a main window
    if is_toplevel_window(handle):

        if "MS Shell Dlg" in fontval.lfFaceName or \
            fontval.lfFaceName == "System":
            # these are not usually the fonts actaully used in for
            # title bars so we need to get the default title bar font

            # get the title font based on the system metrics rather
            # than the font of the control itself
            ncms = win32structures.NONCLIENTMETRICSW()
            ncms.cbSize = ctypes.sizeof(ncms)
            win32functions.SystemParametersInfo(
                win32defines.SPI_GETNONCLIENTMETRICS,
                ctypes.sizeof(ncms),
                ctypes.byref(ncms),
                0)

            # with either of the following 2 flags set the font of the
            # dialog isthe small one (but there is normally no difference!
            if has_style(handle, win32defines.WS_EX_TOOLWINDOW) or \
               has_style(handle, win32defines.WS_EX_PALETTEWINDOW):

                fontval = ncms.lfSmCaptionFont
            else:
                fontval = ncms.lfCaptionFont

    return fontval

#=========================================================================
def processid(handle):
    "Retrun the ID of process that controls this window"
    process_id = ctypes.c_int()
    win32functions.GetWindowThreadProcessId(handle, ctypes.byref(process_id))

    return process_id.value

#=========================================================================
def children(handle):
    "Return a list of handles to the children of this window"
    return findwindows.enum_child_windows(handle)


#=========================================================================
def has_style(handle, tocheck):
    "Return True if the control has style tocheck"
    hwnd_style = style(handle)
    return tocheck & hwnd_style == tocheck

#=========================================================================
def has_exstyle(handle, tocheck):
    "Return True if the control has extended style tocheck"
    hwnd_exstyle = exstyle(handle)
    return tocheck & hwnd_exstyle == tocheck


#=========================================================================
def is_toplevel_window(handle):
    "Return whether the window is a top level window or not"

    # only request the style once - this is an optimization over calling
    # (handle, style) for each style I wan to check!
    style_ = style(handle)

    if (style_ & win32defines.WS_OVERLAPPED == win32defines.WS_OVERLAPPED or \
        style_ & win32defines.WS_CAPTION == win32defines.WS_CAPTION) and \
        not (style_ & win32defines.WS_CHILD == win32defines.WS_CHILD):
        return True
    else:
        return False


#=========================================================================
#def get_button_friendlyclassname(handle):
#    "Return the friendly class name of a button control"
#
#    # get the least significant bit
#    style_lsb = style(handle) & 0xF
#
#    # default to "Button"
#    f_classname = "Button"
#
#    if style_lsb == win32defines.BS_3STATE or \
#        style_lsb == win32defines.BS_AUTO3STATE or \
#        style_lsb == win32defines.BS_AUTOCHECKBOX or \
#        style_lsb == win32defines.BS_CHECKBOX:
#        f_classname = "CheckBox"
#
#    elif style_lsb == win32defines.BS_RADIOBUTTON or \
#        style_lsb == win32defines.BS_AUTORADIOBUTTON:
#        f_classname = "RadioButton"
#
#    elif style_lsb == win32defines.BS_GROUPBOX:
#        f_classname = "GroupBox"
#
#    if style(handle) & win32defines.BS_PUSHLIKE:
#        f_classname = "Button"
#
#    return f_classname


#def friendlyclassname(handle):
#    """Return the friendly class name of the window
#
#    The friendly class name might be subjective, but it
#    tries to be what a normal user would call a window
#    rather then the windows class name for the window.
#    """
#
#    import warnings
#    warnings.warn("handleprops.friendlyclassname() is deprecated. Please use"
#        "FriendlyClassMethod() of HwndWrapper",
#        DeprecationWarning)
#
#    # if it's a dialog then return that
#    if is_toplevel_window(handle) and classname(handle) == "#32770":
#        return "Dialog"
#
#    # otherwise ask the wrapper class for the friendly class name
#    class_name = classname(handle)
#
#    from controls import wraphandle
#    info = wraphandle._find_wrapper(class_name)
#
#    if info:
#       return info.friendlyclassname
#
#    else:
#        return class_name



#
#
#    # Check if the class name is in the known classes
#    for cls_name, f_cls_name in _class_names.items():
#
#        # OK we found it
#        if re.match(cls_name, classname(handle)):
#            # If it is a string then just return it
#            if isinstance(f_cls_name, basestring):
#                return f_cls_name
#            # otherwise it is a function so call it
#            else:
#                return f_cls_name(handle)
#
#    # unknown class - just return it's classname
#    return classname(handle)





#=========================================================================
def dumpwindow(handle):
    "Dump a window to a set of properties"
    props = {}

    for func in (
        text,
        classname,
        rectangle,
        clientrect,
        style,
        exstyle,
        contexthelpid,
        controlid,
        userdata,
        font,
        parent,
        processid,
        isenabled,
        isunicode,
        isvisible,
        children,
        ):

        props[func.__name__] = func(handle)

    return props

