# GUI Application automation and testing library
# Copyright (C) 2006-2018 Mark Mc Mahon and Contributors
# https://github.com/pywinauto/pywinauto/graphs/contributors
# http://pywinauto.readthedocs.io/en/latest/credits.html
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of pywinauto nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""Basic wrapping of Windows controls"""
from __future__ import unicode_literals
from __future__ import print_function

# pylint:  disable-msg=W0611

# import sys
import copy
import time
import re
import ctypes
import win32api
import win32gui
import win32con
import win32process
import win32event
import six
import pywintypes
import warnings

# the wrappers may be used in an environment that does not need
# the actions - as such I don't want to require sendkeys - so
# the following makes the import optional.

from .. import win32functions
from .. import win32defines
from .. import win32structures
from .. import controlproperties
from ..actionlogger import ActionLogger
from .. import keyboard
from .. import mouse
from ..timings import Timings
from .. import timings
from .. import handleprops
from ..win32_element_info import HwndElementInfo
from .. import backend

# I leave this optional because PIL is a large dependency
try:
    from PIL import ImageGrab
except ImportError:
    ImageGrab = None

# also import MenuItemNotEnabled so that it is
# accessible from HwndWrapper module
from .menuwrapper import Menu #, MenuItemNotEnabled

from ..base_wrapper import BaseWrapper
from ..base_wrapper import BaseMeta
from .. import deprecated


#====================================================================
class ControlNotEnabled(RuntimeError):

    """Raised when a control is not enabled"""
    pass

#====================================================================
class ControlNotVisible(RuntimeError):

    """Raised when a control is not visible"""
    pass

#====================================================================
class InvalidWindowHandle(RuntimeError):

    """Raised when an invalid handle is passed to HwndWrapper"""

    def __init__(self, hwnd):
        """Initialise the RuntimError parent with the mesage"""
        RuntimeError.__init__(self,
            "Handle {0} is not a vaild window handle".format(hwnd))

#=========================================================================
class HwndMeta(BaseMeta):

    """Metaclass for HwndWrapper objects"""
    re_wrappers = {}
    str_wrappers = {}

    def __init__(cls, name, bases, attrs):
        """
        Register the class names

        Both the regular expression or the classes directly are registered.
        """
        BaseMeta.__init__(cls, name, bases, attrs)

        for win_class in cls.windowclasses:
            HwndMeta.re_wrappers[re.compile(win_class)] = cls
            HwndMeta.str_wrappers[win_class] = cls

    @staticmethod
    def find_wrapper(element):
        """Find the correct wrapper for this native element"""
        if isinstance(element, six.integer_types):
            element = HwndElementInfo(element)
        class_name = element.class_name

        try:
            return HwndMeta.str_wrappers[class_name]
        except KeyError:
            wrapper_match = None

            for regex, wrapper in HwndMeta.re_wrappers.items():
                if regex.match(class_name):
                    wrapper_match = wrapper
                    HwndMeta.str_wrappers[class_name] = wrapper

                    return wrapper

        # if it is a dialog then override the wrapper we found
        # and make it a DialogWrapper
        if handleprops.is_toplevel_window(element.handle):
            wrapper_match = DialogWrapper

        if wrapper_match is None:
            wrapper_match = HwndWrapper
        return wrapper_match

#====================================================================
@six.add_metaclass(HwndMeta)
class HwndWrapper(BaseWrapper):

    """
    Default wrapper for controls.

    All other wrappers are derived from this.

    This class wraps a lot of functionality of underlying windows API
    features for working with windows.

    Most of the methods apply to every single window type. For example
    you can click() on any window.

    Most of the methods of this class are simple wrappers around
    API calls and as such they try do the simplest thing possible.

    An HwndWrapper object can be passed directly to a ctypes wrapped
    C function - and it will get converted to a Long with the value of
    it's handle (see ctypes, _as_parameter_).
    """
    handle = None

    # -----------------------------------------------------------
    def __new__(cls, element):
        """Construct the control wrapper"""
        return super(HwndWrapper, cls)._create_wrapper(cls, element, HwndWrapper)

    # -----------------------------------------------------------
    def __init__(self, element_info):
        """
        Initialize the control

        * **element_info** is either a valid HwndElementInfo or it can be an
          instance or subclass of HwndWrapper.
        If the handle is not valid then an InvalidWindowHandle error
        is raised.
        """
        if isinstance(element_info, six.integer_types):
            element_info = HwndElementInfo(element_info)
        if hasattr(element_info, "element_info"):
            element_info = element_info.element_info

        BaseWrapper.__init__(self, element_info, backend.registry.backends['win32'])

        # verify that we have been passed in a valid windows handle
        if not handleprops.iswindow(self.handle):
            raise InvalidWindowHandle(self.handle)

        # make it so that ctypes conversion happens correctly
        self._as_parameter_ = self.handle

    @property
    def writable_props(self):
        """Extend default properties list."""
        props = super(HwndWrapper, self).writable_props
        props.extend(['style',
                      'exstyle',
                      'user_data',
                      'context_help_id',
                      'fonts',
                      'client_rects',
                      'is_unicode',
                      'menu_items',
                      'automation_id',
                      ])
        return props

    # -----------------------------------------------------------
    def style(self):
        """
        Returns the style of window

        Return value is a long.

        Combination of WS_* and specific control specific styles.
        See HwndWrapper.has_style() to easily check if the window has a
        particular style.
        """
        return handleprops.style(self)
    # Non PEP-8 alias
    Style = deprecated(style)

    # -----------------------------------------------------------
    def exstyle(self):
        """
        Returns the Extended style of window

        Return value is a long.

        Combination of WS_* and specific control specific styles.
        See HwndWrapper.has_style() to easily check if the window has a
        particular style.
        """
        return handleprops.exstyle(self)
    # Non PEP-8 alias
    ExStyle = deprecated(exstyle, deprecated_name='ExStyle')

    #------------------------------------------------------------
    def automation_id(self):
        """Return the .NET name of the control"""
        return self.element_info.automation_id

    #------------------------------------------------------------
    def control_type(self):
        """Return the .NET type of the control"""
        return self.element_info.control_type

    #------------------------------------------------------------
    def full_control_type(self):
        """Return the .NET type of the control (full, uncut)"""
        return self.element_info.full_control_type

    # -----------------------------------------------------------
    def user_data(self):
        """
        Extra data associted with the window

        This value is a long value that has been associated with the window
        and rarely has useful data (or at least data that you know the use
        of).
        """
        return handleprops.userdata(self)
    # Non PEP-8 alias
    UserData = deprecated(user_data)

    # -----------------------------------------------------------
    def context_help_id(self):
        """Return the Context Help ID of the window"""
        return handleprops.contexthelpid(self)
    # Non PEP-8 alias
    ContextHelpID = deprecated(context_help_id, deprecated_name='ContextHelpID')

    # -----------------------------------------------------------
    def is_active(self):
        """Whether the window is active or not"""
        return self.top_level_parent() == self.get_active()
    # Non PEP-8 alias
    IsActive = deprecated(is_active)

    # -----------------------------------------------------------
    def is_unicode(self):
        """
        Whether the window is unicode or not

        A window is Unicode if it was registered by the Wide char version
        of RegisterClass(Ex).
        """
        return handleprops.isunicode(self)
    # Non PEP-8 alias
    IsUnicode = deprecated(is_unicode)

    # -----------------------------------------------------------
    def client_rect(self):
        """
        Returns the client rectangle of window

        The client rectangle is the window rectangle minus any borders that
        are not available to the control for drawing.

        Both top and left are always 0 for this method.

        This method returns a RECT structure, Which has attributes - top,
        left, right, bottom. and has methods width() and height().
        See win32structures.RECT for more information.
        """
        return handleprops.clientrect(self)
    # Non PEP-8 alias
    ClientRect = deprecated(client_rect)

    # -----------------------------------------------------------
    #def client_to_screen(self, client_point):
    #    """Maps point from client to screen coordinates"""
    #    point = win32structures.POINT()
    #    if isinstance(client_point, win32structures.POINT):
    #        point.x = client_point.x
    #        point.y = client_point.y
    #    else:
    #        point.x = client_point[0]
    #        point.y = client_point[1]
    #    win32functions.client_to_screen(self, ctypes.byref(point))
    #
    #    # return tuple in any case because
    #    # coords param is always expected to be tuple
    #    return point.x, point.y

    # -----------------------------------------------------------
    def font(self):
        """
        Return the font of the window

        The font of the window is used to draw the text of that window.
        It is a structure which has attributes for font name, height, width
        etc.

        See win32structures.LOGFONTW for more information.
        """
        return handleprops.font(self)
    # Non PEP-8 alias
    Font = deprecated(font)

    # -----------------------------------------------------------
    def has_style(self, style):
        """Return True if the control has the specified style"""
        return handleprops.has_style(self, style)
    # Non PEP-8 alias
    HasStyle = deprecated(has_style)

    # -----------------------------------------------------------
    def has_exstyle(self, exstyle):
        """Return True if the control has the specified extended style"""
        return handleprops.has_exstyle(self, exstyle)
    # Non PEP-8 alias
    HasExStyle = deprecated(has_exstyle, deprecated_name='HasExStyle')

    # -----------------------------------------------------------
    def is_dialog(self):
        """Return true if the control is a top level window"""
        if not ("isdialog" in self._cache.keys()):
            self._cache['isdialog'] = handleprops.is_toplevel_window(self)

        return self._cache['isdialog']

    # -----------------------------------------------------------
    def client_rects(self):
        """
        Return the client rect for each item in this control

        It is a list of rectangles for the control. It is frequently over-ridden
        to extract all rectangles from a control with multiple items.

        It is always a list with one or more rectangles:

          * First elemtent is the client rectangle of the control
          * Subsequent elements contain the client rectangle of any items of
            the control (e.g. items in a listbox/combobox, tabs in a
            tabcontrol)
        """
        return [self.client_rect(), ]
    # Non PEP-8 alias
    ClientRects = deprecated(client_rects)

    # -----------------------------------------------------------
    def fonts(self):
        """
        Return the font for each item in this control

        It is a list of fonts for the control. It is frequently over-ridden
        to extract all fonts from a control with multiple items.

        It is always a list with one or more fonts:

          * First elemtent is the control font
          * Subsequent elements contain the font of any items of
            the control (e.g. items in a listbox/combobox, tabs in a
            tabcontrol)
        """
        return [self.font(), ]
    # Non PEP-8 alias
    Fonts = deprecated(fonts)

    # -----------------------------------------------------------
    def send_command(self, commandID):
        return self.send_message(win32defines.WM_COMMAND, commandID)
    # Non PEP-8 alias
    SendCommand = deprecated(send_command)

    # -----------------------------------------------------------
    def post_command(self, commandID):
        return self.post_message(win32defines.WM_COMMAND, commandID)
    # Non PEP-8 alias
    PostCommand = deprecated(post_command)

    # -----------------------------------------------------------
    #def notify(self, code):
    #    "Send a notification to the parent (not tested yet)"

    #    # now we need to notify the parent that the state has changed
    #    nmhdr = win32structures.NMHDR()
    #    nmhdr.hwndFrom = self.handle
    #    nmhdr.idFrom = self.control_id()
    #    nmhdr.code = code

    #    from ..remote_memory_block import RemoteMemoryBlock
    #    remote_mem = RemoteMemoryBlock(self, size=ctypes.sizeof(nmhdr))
    #    remote_mem.Write(nmhdr, size=ctypes.sizeof(nmhdr))

    #    retval = self.parent().send_message(
    #        win32defines.WM_NOTIFY,
    #        self.handle,
    #        remote_mem)
    #    #if retval != win32defines.TRUE:
    #    #    print('retval = ' + str(retval))
    #    #    raise ctypes.WinError()
    #    del remote_mem

    #    return retval
    # Non PEP-8 alias
    #Notify = deprecated(notify)

    # -----------------------------------------------------------
    def _ensure_enough_privileges(self, message_name):
        """Ensure the Python process has enough rights to send some window messages"""
        pid = handleprops.processid(self.handle)
        if not handleprops.has_enough_privileges(pid):
            raise RuntimeError('Not enough rights to use {} message/function for target process ' \
                '(to resolve it run the script as Administrator)'.format(message_name))

    # -----------------------------------------------------------
    def send_message(self, message, wparam = 0, lparam = 0):
        """Send a message to the control and wait for it to return"""
        wParamAddress = wparam
        if hasattr(wparam, 'mem_address'):
            wParamAddress = wparam.mem_address
        lParamAddress = lparam
        if hasattr(lparam, 'mem_address'):
            lParamAddress = lparam.mem_address

        CArgObject = type(ctypes.byref(ctypes.c_int(0)))
        if isinstance(wparam, CArgObject):
            wParamAddress = ctypes.addressof(wparam._obj)
        if isinstance(lparam, CArgObject):
            lParamAddress = ctypes.addressof(lparam._obj)

        return win32gui.SendMessage(self.handle, message, wParamAddress, lParamAddress)

    # Non PEP-8 alias
    SendMessage = deprecated(send_message)

    # -----------------------------------------------------------
    def send_chars(self,
                   chars,
                   with_spaces=True,
                   with_tabs=True,
                   with_newlines=True):
        """
        Silently send a character string to the control in an inactive window

        If a virtual key with no corresponding character is encountered
        (e.g. VK_LEFT, VK_DELETE), a KeySequenceError is raised. Consider using
        the method send_keystrokes for such input.
        """
        input_locale_id = ctypes.windll.User32.GetKeyboardLayout(0)
        keys = keyboard.parse_keys(chars, with_spaces, with_tabs, with_newlines)

        for key in keys:
            key_info = key.get_key_info()
            flags = key_info[2]

            unicode_char = (
                flags & keyboard.KEYEVENTF_UNICODE ==
                keyboard.KEYEVENTF_UNICODE)

            if unicode_char:
                _, char = key_info[:2]
                vk = ctypes.windll.User32.VkKeyScanExW(char, input_locale_id) & 0xFF
                scan = keyboard.MapVirtualKey(vk, 0)
            else:
                vk, scan = key_info[:2]
                char = keyboard.MapVirtualKey(vk, 2)

            if char > 0:
                lparam = 1 << 0 | scan << 16 | (flags & 1) << 24
                win32api.SendMessage(self.handle, win32con.WM_CHAR, char, lparam)
            else:
                raise keyboard.KeySequenceError(
                    'no WM_CHAR code for {key}, use method send_keystrokes instead'.
                    format(key=key))

    # -----------------------------------------------------------
    def send_keystrokes(self,
                   keystrokes,
                   with_spaces=True,
                   with_tabs=True,
                   with_newlines=True):
        """
        Silently send keystrokes to the control in an inactive window

        It parses modifiers Shift(+), Control(^), Menu(%) and Sequences like "{TAB}", "{ENTER}"
        For more information about Sequences and Modifiers navigate to module `keyboard`_

        .. _`keyboard`: pywinauto.keyboard.html

        Due to the fact that each application handles input differently and this method
        is meant to be used on inactive windows, it may work only partially depending
        on the target app. If the window being inactive is not essential, use the robust
        `type_keys`_ method.

        .. _`type_keys`: pywinauto.base_wrapper.html#pywinauto.base_wrapper.BaseWrapper.type_keys
        """
        user32 = ctypes.windll.User32
        PBYTE256 = ctypes.c_ubyte * 256

        win32gui.SendMessage(self.handle, win32con.WM_ACTIVATE,
                             win32con.WA_ACTIVE, 0)
        target_thread_id = user32.GetWindowThreadProcessId(self.handle, None)
        current_thread_id = win32api.GetCurrentThreadId()
        attach_success = user32.AttachThreadInput(target_thread_id, current_thread_id, True) != 0
        if not attach_success:
            warnings.warn('Failed to attach app\'s thread to the current thread\'s message queue',
                          UserWarning, stacklevel=2)

        keyboard_state_stack = [PBYTE256()]
        user32.GetKeyboardState(ctypes.byref(keyboard_state_stack[-1]))

        input_locale_id = ctypes.windll.User32.GetKeyboardLayout(0)
        context_code = 0

        keys = keyboard.parse_keys(keystrokes, with_spaces, with_tabs, with_newlines)
        key_combos_present = any([isinstance(k, keyboard.EscapedKeyAction) for k in keys])
        if key_combos_present:
            warnings.warn('Key combinations may or may not work depending on the target app',
                          UserWarning, stacklevel=2)

        try:
            for key in keys:
                vk, scan, flags = key.get_key_info()

                if vk == keyboard.VK_MENU or context_code == 1:
                    down_msg, up_msg = win32con.WM_SYSKEYDOWN, win32con.WM_SYSKEYUP
                else:
                    down_msg, up_msg = win32con.WM_KEYDOWN, win32con.WM_KEYUP

                repeat = 1
                shift_state = 0
                unicode_codepoint = flags & keyboard.KEYEVENTF_UNICODE != 0
                if unicode_codepoint:
                    char = scan
                    vk_with_flags = user32.VkKeyScanExW(char, input_locale_id)
                    vk = vk_with_flags & 0xFF
                    shift_state = (vk_with_flags & 0xFF00) >> 8
                    scan = keyboard.MapVirtualKey(vk, 0)

                if key.down and vk > 0:
                    new_keyboard_state = copy.deepcopy(keyboard_state_stack[-1])

                    new_keyboard_state[vk] |= 128
                    if shift_state & 1 == 1:
                        new_keyboard_state[keyboard.VK_SHIFT] |= 128
                    # NOTE: if there are characters with CTRL or ALT in the shift
                    # state, make sure to add these keys to new_keyboard_state

                    keyboard_state_stack.append(new_keyboard_state)

                    lparam = (
                        repeat << 0 |
                        scan << 16 |
                        (flags & 1) << 24 |
                        context_code << 29 |
                        0 << 31)

                    user32.SetKeyboardState(ctypes.byref(keyboard_state_stack[-1]))
                    win32api.PostMessage(self.handle, down_msg, vk, lparam)
                    if vk == keyboard.VK_MENU:
                        context_code = 1

                    # a delay for keyboard state to take effect
                    time.sleep(0.01)

                if key.up and vk > 0:
                    keyboard_state_stack.pop()

                    lparam = (
                        repeat << 0 |
                        scan << 16 |
                        (flags & 1) << 24 |
                        context_code << 29 |
                        1 << 30 |
                        1 << 31)

                    win32api.PostMessage(self.handle, up_msg, vk, lparam)
                    user32.SetKeyboardState(ctypes.byref(keyboard_state_stack[-1]))

                    if vk == keyboard.VK_MENU:
                        context_code = 0

                    # a delay for keyboard state to take effect
                    time.sleep(0.01)

        except pywintypes.error as e:
            if e.winerror == 1400:
                warnings.warn('Application exited before the end of keystrokes',
                              UserWarning, stacklevel=2)
            else:
                warnings.warn(e.strerror, UserWarning, stacklevel=2)
            user32.SetKeyboardState(ctypes.byref(keyboard_state_stack[0]))

        if attach_success:
            user32.AttachThreadInput(target_thread_id, current_thread_id, False)

    # -----------------------------------------------------------
    def send_message_timeout(
        self,
        message,
        wparam = 0,
        lparam = 0,
        timeout = None,
        timeoutflags = win32defines.SMTO_NORMAL):
        """
        Send a message to the control and wait for it to return or to timeout

        If no timeout is given then a default timeout of .01 of a second will
        be used.
        """
        if timeout is None:
            timeout = Timings.sendmessagetimeout_timeout

        result = -1
        try:
            (_, result) = win32gui.SendMessageTimeout(
                    int(self.handle),
                    message,
                    wparam,
                    lparam,
                    timeoutflags,
                    int(timeout * 1000)
                    )
        except Exception as exc:
            #import traceback
            #print('____________________________________________________________')
            #print('self.handle =', int(self.handle), ', message =', message,
            #      ', wparam =', wparam, ', lparam =', lparam, ', timeout =', timeout)
            #print('Exception: ', exc)
            #print(traceback.format_exc())
            result = str(exc)

        return result #result.value
    # Non PEP-8 alias
    SendMessageTimeout = deprecated(send_message_timeout)

    # -----------------------------------------------------------
    def post_message(self, message, wparam = 0, lparam = 0):
        """Post a message to the control message queue and return"""
        return win32functions.PostMessage(self, message, wparam, lparam)

    # Non PEP-8 alias
    PostMessage = deprecated(post_message)

#    # -----------------------------------------------------------
#    def notify_menu_select(self, menu_id):
#        """Notify the dialog that one of it's menu items was selected
#
#        **This method is Deprecated**
#        """
#
#        import warnings
#        warning_msg = "HwndWrapper.NotifyMenuSelect() is deprecated - " \
#            "equivalent functionality is being moved to the MenuWrapper class."
#        warnings.warn(warning_msg, DeprecationWarning)
#
#        self.set_focus()
#
#        msg = win32defines.WM_COMMAND
#        return self.send_message_timeout(
#            msg,
#            win32functions.MakeLong(0, menu_id), #wparam
#            )
    # Non PEP-8 alias
    # NotifyMenuSelect = deprecated(notify_menu_select)

    # -----------------------------------------------------------
    def notify_parent(self, message, controlID = None):
        """Send the notification message to parent of this control"""
        if controlID is None:
            controlID = self.control_id()

        return self.parent().post_message(
            win32defines.WM_COMMAND,
            win32functions.MakeLong(message, controlID),
            self)
    # Non PEP-8 alias
    NotifyParent = deprecated(notify_parent)

    # -----------------------------------------------------------
    def __hash__(self):
        """Returns the hash value of the handle"""
        return hash(self.handle)

    #-----------------------------------------------------------
    def wait_for_idle(self):
        """Backend specific function to wait for idle state of a thread or a window"""
        win32functions.WaitGuiThreadIdle(self.handle)

    # -----------------------------------------------------------
    def click(self, button="left", pressed="", coords=(0, 0),
              double=False, absolute=False):
        """
        Simulates a mouse click on the control

        This method sends WM_* messages to the control, to do a more
        'realistic' mouse click use click_input() which uses mouse_event() API
        to perform the click.

        This method does not require that the control be visible on the screen
        (i.e. it can be hidden beneath another window and it will still work).
        """
        self.verify_actionable()
        self._ensure_enough_privileges('WM_*BUTTONDOWN/UP')

        _perform_click(self, button, pressed, coords, double, absolute=absolute)
        return self
    # Non PEP-8 alias
    Click = deprecated(click)

    # -----------------------------------------------------------
    def close_click(
        self, button = "left", pressed = "", coords = (0, 0), double = False):
        """
        Perform a click action that should make the window go away

        The only difference from click is that there are extra delays
        before and after the click action.
        """
        time.sleep(Timings.before_closeclick_wait)

        _perform_click(self, button, pressed, coords, double)

        def has_closed():
            closed = not (
                    handleprops.iswindow(self) or
                    handleprops.iswindow(self.parent()))
            if not closed:
                # try closing again
                try:
                    _perform_click(self, button, pressed, coords, double)
                except Exception:
                    return True # already closed
            return closed

        # Keep waiting until both this control and it's parent
        # are no longer valid controls
        timings.wait_until(
            Timings.closeclick_dialog_close_wait,
            Timings.closeclick_retry,
            has_closed
        )

        time.sleep(Timings.after_closeclick_wait)

        return self
    # Non PEP-8 alias
    CloseClick = deprecated(close_click)

    # -----------------------------------------------------------
    def close_alt_f4(self):
        """Close the window by pressing Alt+F4 keys."""
        time.sleep(Timings.before_closeclick_wait)
        self.type_keys('%{F4}')
        time.sleep(Timings.after_closeclick_wait)

        return self
    # Non PEP-8 alias
    CloseAltF4 = deprecated(close_alt_f4)

    # -----------------------------------------------------------
    def double_click(
        self, button = "left", pressed = "", coords = (0, 0)):
        """Perform a double click action"""
        _perform_click(self, button, pressed, coords, double = True)
        return self
    # Non PEP-8 alias
    DoubleClick = deprecated(double_click)

    # -----------------------------------------------------------
    def right_click(
        self, pressed = "", coords = (0, 0)):
        """Perform a right click action"""
        _perform_click(
            self, "right", "right " + pressed, coords, button_up = False)
        _perform_click(self, "right", pressed, coords, button_down = False)
        return self
    # Non PEP-8 alias
    RightClick = deprecated(right_click)

    # -----------------------------------------------------------
    def press_mouse(self, button ="left", coords = (0, 0), pressed =""):
        """Press the mouse button"""
        #flags, click_point = _calc_flags_and_coords(pressed, coords)

        _perform_click(self, button, pressed, coords, button_down=True, button_up=False)
        return self
    # Non PEP-8 alias
    PressMouse = deprecated(press_mouse)

    # -----------------------------------------------------------
    def release_mouse(self, button ="left", coords = (0, 0), pressed =""):
        """Release the mouse button"""
        #flags, click_point = _calc_flags_and_coords(pressed, coords)
        _perform_click(self, button, pressed, coords, button_down=False, button_up=True)
        return self
    # Non PEP-8 alias
    ReleaseMouse = deprecated(release_mouse)

    # -----------------------------------------------------------
    def move_mouse(self, coords = (0, 0), pressed ="", absolute = False):
        """Move the mouse by WM_MOUSEMOVE"""
        if not absolute:
            self.actions.log('Moving mouse to relative (client) coordinates ' + str(coords).replace('\n', ', '))

        _perform_click(self, button='move', coords=coords, absolute=absolute, pressed=pressed)

        win32functions.WaitGuiThreadIdle(self.handle)
        return self
    # Non PEP-8 alias
    MoveMouse = deprecated(move_mouse)

    # -----------------------------------------------------------
    def drag_mouse(self, button ="left",
                   press_coords = (0, 0),
                   release_coords = (0, 0),
                   pressed = ""):
        """Drag the mouse"""
        if isinstance(press_coords, win32structures.POINT):
            press_coords = (press_coords.x, press_coords.y)

        if isinstance(release_coords, win32structures.POINT):
            release_coords = (release_coords.x, release_coords.y)

        _pressed = pressed
        if not _pressed:
            _pressed = "left"

        self.press_mouse(button, press_coords, pressed=pressed)
        for i in range(5):
            self.move_mouse((press_coords[0] + i, press_coords[1]), pressed=_pressed)
            time.sleep(Timings.drag_n_drop_move_mouse_wait)
        self.move_mouse(release_coords, pressed=_pressed)
        time.sleep(Timings.before_drop_wait)
        self.release_mouse(button, release_coords, pressed=pressed)
        time.sleep(Timings.after_drag_n_drop_wait)
        return self
    # Non PEP-8 alias
    DragMouse = deprecated(drag_mouse)

    # -----------------------------------------------------------
    def set_window_text(self, text, append = False):
        """Set the text of the window"""
        self.verify_actionable()

        if append:
            text = self.window_text() + text

        text = ctypes.c_wchar_p(six.text_type(text))
        self.post_message(win32defines.WM_SETTEXT, 0, text)
        win32functions.WaitGuiThreadIdle(self.handle)

        self.actions.log('Set text to the ' + self.friendly_class_name() + ': ' + str(text))
        return self
    # Non PEP-8 alias
    SetWindowText = deprecated(set_window_text)

    # -----------------------------------------------------------
    def debug_message(self, text):
        """Write some debug text over the window"""
        # don't draw if dialog is not visible

        dc = win32functions.CreateDC("DISPLAY", None, None, None )

        if not dc:
            raise ctypes.WinError()

        rect = self.rectangle()

        #ret = win32functions.TextOut(
        #    dc, rect.left, rect.top, six.text_type(text), len(text))
        ret = win32functions.DrawText(
            dc,
            six.text_type(text),
            len(text),
            ctypes.byref(rect),
            win32defines.DT_SINGLELINE)

        # delete the Display context that we created
        win32functions.DeleteDC(dc)

        if not ret:
            raise ctypes.WinError()

        return self
    # Non PEP-8 alias
    DebugMessage = deprecated(debug_message)

    # -----------------------------------------------------------
    def set_transparency(self, alpha = 120):
        """Set the window transparency from 0 to 255 by alpha attribute"""
        if not (0 <= alpha <= 255):
            raise ValueError('alpha should be in [0, 255] interval!')
        # TODO: implement SetExStyle method
        win32gui.SetWindowLong(self.handle, win32defines.GWL_EXSTYLE, self.exstyle() | win32con.WS_EX_LAYERED)
        win32gui.SetLayeredWindowAttributes(self.handle, win32api.RGB(0,0,0), alpha, win32con.LWA_ALPHA)
    # Non PEP-8 alias
    SetTransparency = deprecated(set_transparency)

    # -----------------------------------------------------------
    def popup_window(self):
        """Return owned enabled Popup window wrapper if shown.

        If there is no enabled popups at that time, it returns **self**.
        See MSDN reference:
        https://msdn.microsoft.com/en-us/library/windows/desktop/ms633515.aspx

        Please do not use in production code yet - not tested fully
        """
        popup = win32functions.GetWindow(self, win32defines.GW_ENABLEDPOPUP)

        return popup
    # Non PEP-8 alias
    PopupWindow = deprecated(popup_window)

    # -----------------------------------------------------------
    def owner(self):
        """Return the owner window for the window if it exists

        Returns None if there is no owner.
        """
        owner = win32functions.GetWindow(self, win32defines.GW_OWNER)
        if owner:
            return HwndWrapper(owner)
        else:
            return None
    # Non PEP-8 alias
    Owner = deprecated(owner)

    # -----------------------------------------------------------
#    def context_menu_select(self, path, x = None, y = None):
#        "TODO context_menu_select Not Implemented"
#        pass
#        #raise NotImplementedError(
#        #    "HwndWrapper.ContextMenuSelect not implemented yet")
#    # Non PEP-8 alias
#    ContextMenuSelect = deprecated(context_menu_select)

    # -----------------------------------------------------------
    def _menu_handle(self):
        """Simple overridable method to get the menu handle"""
        hMenu = win32gui.GetMenu(self.handle)
        is_main_menu = True
        if not hMenu:
            self._ensure_enough_privileges('MN_GETHMENU')
            hMenu = self.send_message(self.handle, win32defines.MN_GETHMENU);
            is_main_menu = False
        return (hMenu, is_main_menu)

    # -----------------------------------------------------------
    def menu(self):
        """Return the menu of the control"""
        hMenu, is_main_menu = self._menu_handle()
        if hMenu: # and win32functions.IsMenu(menu_hwnd):
            return Menu(self, hMenu, is_main_menu=is_main_menu)
        return None
    # Non PEP-8 alias
    Menu = deprecated(menu)

    # -----------------------------------------------------------
    def menu_item(self, path, exact = False):
        """Return the menu item specified by path

        Path can be a string in the form "MenuItem->MenuItem->MenuItem..."
        where each MenuItem is the text of an item at that level of the menu.
        E.g. ::

          File->Export->ExportAsPNG

        spaces are not important so you could also have written... ::

          File -> Export -> Export As PNG

        """
        if self.appdata is not None:
            menu_appdata = self.appdata['menu_items']
        else:
            menu_appdata = None

        menu = self.menu()
        if menu:
            return self.menu().get_menu_path(path, appdata = menu_appdata, exact=exact)[-1]

        raise RuntimeError("There is no menu.")
    # Non PEP-8 alias
    MenuItem = deprecated(menu_item)

    # -----------------------------------------------------------
    def menu_items(self):
        """Return the menu items for the dialog

        If there are no menu items then return an empty list
        """
        if self.is_dialog() and self.menu():
            #menu_handle = win32functions.GetMenu(self)
            #self.send_message(win32defines.WM_INITMENU, menu_handle)
            return self.menu().get_properties()['menu_items']

            #self.send_message(win32defines.WM_INITMENU, menu_handle)
            #return _GetMenuItems(menu_handle, self)
        else:
            return []
    # Non PEP-8 alias
    MenuItems = deprecated(menu_items)

#    # -----------------------------------------------------------
#    def menu_click(self, path):
#        "Select the MenuItem specifed in path"
#
#        self.verify_actionable()
#
#        self.set_focus()
#
#        menu = Menu(self, self._menu_handle())
#
#        path_items = menu.get_menu_path(path)
#
#        for menu_item in path_items:
#            if not menu_item.is_enabled():
#                raise MenuItemNotEnabled(
#                    "MenuItem '%s' is disabled"% menu_item.text())
#
#            menu_item.click()
#
#        return self
#    # Non PEP-8 alias
#    MenuClick = deprecated(menu_click)

    # -----------------------------------------------------------
    def menu_select(self, path, exact=False, ):
        """Find a menu item specified by the path

        The full path syntax is specified in:
        :py:meth:`.controls.menuwrapper.Menu.get_menu_path`
        """
        self.verify_actionable()

        self.menu_item(path, exact=exact).select()
    # Non PEP-8 alias
    MenuSelect = deprecated(menu_select)

    # -----------------------------------------------------------
    def move_window(
        self,
        x = None,
        y = None,
        width = None,
        height = None,
        repaint = True):
        """Move the window to the new coordinates

        * **x** Specifies the new left position of the window.
          Defaults to the current left position of the window.
        * **y** Specifies the new top position of the window.
          Defaults to the current top position of the window.
        * **width** Specifies the new width of the window. Defaults to the
          current width of the window.
        * **height** Specifies the new height of the window. Default to the
          current height of the window.
        * **repaint** Whether the window should be repainted or not.
          Defaults to True

        """
        cur_rect = self.rectangle()

        # if no X is specified - so use current coordinate
        if x is None:
            x = cur_rect.left
        else:
            try:
                y = x.top
                width = x.width()
                height = x.height()
                x = x.left
            except AttributeError:
                pass

        # if no Y is specified - so use current coordinate
        if y is None:
            y = cur_rect.top

        # if no width is specified - so use current width
        if width is None:
            width = cur_rect.width()

        # if no height is specified - so use current height
        if height is None:
            height = cur_rect.height()

        # ask for the window to be moved
        ret = win32functions.MoveWindow(self, x, y, width, height, repaint)

        # check that it worked correctly
        if not ret:
            raise ctypes.WinError()

        win32functions.WaitGuiThreadIdle(self.handle)
        time.sleep(Timings.after_movewindow_wait)
    # Non PEP-8 alias
    MoveWindow = deprecated(move_window)

    # -----------------------------------------------------------
    def close(self, wait_time = 0):
        """Close the window

        Code modified from http://msdn.microsoft.com/msdnmag/issues/02/08/CQA/

        """
        window_text = self.window_text()

        # tell the window it must close
        self.post_message(win32defines.WM_CLOSE)

        #unused var: start = time.time()
        # Keeps trying while
        #    we have not timed out and
        #    window is still a valid handle and
        #    window is still visible
        # any one of these conditions evaluates to false means the window is
        # closed or we have timed out
        def has_closed():
            return not (handleprops.iswindow(self) and self.is_visible())

        if not wait_time:
            wait_time = Timings.closeclick_dialog_close_wait

        # Keep waiting until both this control and it's parent
        # are no longer valid controls
        timings.wait_until(
            wait_time,
            Timings.closeclick_retry,
            has_closed
        )

        self.actions.log('Closed window "{0}"'.format(window_text))
    # Non PEP-8 alias
    Close = deprecated(close)

    # -----------------------------------------------------------
    def maximize(self):
        """Maximize the window"""
        win32functions.ShowWindow(self, win32defines.SW_MAXIMIZE)
        self.actions.log('Maximized window "{0}"'.format(self.window_text()))
        return self
    # Non PEP-8 alias
    Maximize = deprecated(maximize)

    # -----------------------------------------------------------
    def minimize(self):
        """Minimize the window"""
        win32functions.ShowWindow(self, win32defines.SW_MINIMIZE)
        # TODO: wait while window is minimized
        self.actions.log('Minimized window "{0}"'.format(self.window_text()))
        return self
    # Non PEP-8 alias
    Minimize = deprecated(minimize)

    # -----------------------------------------------------------
    def restore(self):
        """Restore the window to its previous state (normal or maximized)"""
        win32functions.ShowWindow(self, win32defines.SW_RESTORE)
        self.actions.log('Restored window "{0}"'.format(self.window_text()))
        return self
    # Non PEP-8 alias
    Restore = deprecated(restore)

    # -----------------------------------------------------------
    def get_show_state(self):
        """Get the show state and Maximized/minimzed/restored state

        Returns a value that is a union of the following

        * SW_HIDE the window is hidden.
        * SW_MAXIMIZE the window is maximized
        * SW_MINIMIZE the window is minimized
        * SW_RESTORE the window is in the 'restored'
          state (neither minimized or maximized)
        * SW_SHOW The window is not hidden
        """
        wp = win32structures.WINDOWPLACEMENT()
        wp.lenght = ctypes.sizeof(wp)

        ret = win32functions.GetWindowPlacement(self, ctypes.byref(wp))

        if not ret:
            raise ctypes.WinError()

        return wp.showCmd
    # Non PEP-8 alias
    GetShowState = deprecated(get_show_state)

    # -----------------------------------------------------------
    def is_minimized(self):
        """Indicate whether the window is minimized or not"""
        return self.get_show_state() == win32defines.SW_SHOWMINIMIZED

    # -----------------------------------------------------------
    def is_maximized(self):
        """Indicate whether the window is maximized or not"""
        return self.get_show_state() == win32defines.SW_SHOWMAXIMIZED

    # -----------------------------------------------------------
    def is_normal(self):
        """Indicate whether the window is normal (i.e. not minimized and not maximized)"""
        return self.get_show_state() == win32defines.SW_SHOWNORMAL

    # -----------------------------------------------------------
    def get_active(self):
        """Return a handle to the active window within the process"""
        gui_info = win32structures.GUITHREADINFO()
        gui_info.cbSize = ctypes.sizeof(gui_info)
        window_thread_id, _ = win32process.GetWindowThreadProcessId(int(self.handle))
        ret = win32functions.GetGUIThreadInfo(
            window_thread_id,
            ctypes.byref(gui_info))

        if not ret:
            raise ctypes.WinError()

        hwndActive = gui_info.hwndActive
        if hwndActive:
            return HwndWrapper(hwndActive)
        else:
            return None
    # Non PEP-8 alias
    GetActive = deprecated(get_active)

    # -----------------------------------------------------------
    def get_focus(self):
        """Return the control in the process of this window that has the Focus
        """
        gui_info = win32structures.GUITHREADINFO()
        gui_info.cbSize = ctypes.sizeof(gui_info)
        window_thread_id, _ = win32process.GetWindowThreadProcessId(self.handle)
        ret = win32functions.GetGUIThreadInfo(
            window_thread_id,
            ctypes.byref(gui_info))

        if not ret:
            return None

        return HwndWrapper(gui_info.hwndFocus)
    # Non PEP-8 alias
    GetFocus = deprecated(get_focus)

    # -----------------------------------------------------------
    def set_focus(self):
        """
        Set the focus to this control.

        Bring the window to the foreground first.
        The system restricts which processes can set the foreground window
        (https://msdn.microsoft.com/en-us/library/windows/desktop/ms633539(v=vs.85).aspx)
        so the mouse cursor is removed from the screen to prevent any side effects.
        """
        # "steal the focus" if there is another active window
        # otherwise it is already into the foreground and no action required
        if not self.has_focus():
            # Notice that we need to move the mouse out of the screen
            # but we don't use the built-in methods of the class:
            # self.mouse_move doesn't do the job well even with absolute=True
            # self.move_mouse_input can't be used as it calls click_input->set_focus
            mouse.move(coords=(-10000, 500))  # move the mouse out of screen to the left

            # change active window
            if self.is_minimized():
                if self.was_maximized():
                    self.maximize()
                else:
                    self.restore()
            else:
                win32gui.ShowWindow(self.handle, win32con.SW_SHOW)
            win32gui.SetForegroundWindow(self.handle)

            # make sure that we are idle before returning
            win32functions.WaitGuiThreadIdle(self.handle)

            # only sleep if we had to change something!
            time.sleep(Timings.after_setfocus_wait)

        return self
    # Non PEP-8 alias
    SetFocus = deprecated(set_focus)

    def has_focus(self):
        """Check the window is in focus (foreground)"""
        return self.handle == win32gui.GetForegroundWindow()

    def has_keyboard_focus(self):
        """Check the keyboard focus on this control."""
        control_thread = win32process.GetWindowThreadProcessId(self.handle)[0]
        win32process.AttachThreadInput(control_thread, win32api.GetCurrentThreadId(), 1)
        focused = win32gui.GetFocus()
        win32process.AttachThreadInput(control_thread, win32api.GetCurrentThreadId(), 0)

        win32functions.WaitGuiThreadIdle(self.handle)

        return self.handle == focused

    def set_keyboard_focus(self):
        """Set the keyboard focus to this control."""
        control_thread = win32process.GetWindowThreadProcessId(self.handle)[0]
        win32process.AttachThreadInput(control_thread, win32api.GetCurrentThreadId(), 1)
        win32gui.SetFocus(self.handle)
        win32process.AttachThreadInput(control_thread, win32api.GetCurrentThreadId(), 0)

        win32functions.WaitGuiThreadIdle(self.handle)

        time.sleep(Timings.after_setfocus_wait)
        return self

    # -----------------------------------------------------------
    def set_application_data(self, appdata):
        """Application data is data from a previous run of the software

        It is essential for running scripts written for one spoke language
        on a different spoken language
        """
        self.appdata = appdata


    _scroll_types = {"left": {
            "line" : win32defines.SB_LINELEFT,
            "page" : win32defines.SB_PAGELEFT,
            "end" :  win32defines.SB_LEFT,
            },
        "right": {
                "line" : win32defines.SB_LINERIGHT,
                "page" : win32defines.SB_PAGERIGHT,
                "end" :  win32defines.SB_RIGHT,
            },
        "up": {
                "line" : win32defines.SB_LINEUP,
                "page" : win32defines.SB_PAGEUP,
                "end" :  win32defines.SB_TOP,
            },
        "down": {
                "line" : win32defines.SB_LINEDOWN,
                "page" : win32defines.SB_PAGEDOWN,
                "end" :  win32defines.SB_BOTTOM,
            },
        }
    # Non PEP-8 alias
    SetApplicationData = deprecated(set_application_data)

    # -----------------------------------------------------------
    def scroll(self, direction, amount, count = 1, retry_interval = None):
        """Ask the control to scroll itself

        **direction** can be any of "up", "down", "left", "right"
        **amount** can be one of "line", "page", "end"
        **count** (optional) the number of times to scroll
        """
        self._ensure_enough_privileges('WM_HSCROLL/WM_VSCROLL')

        # check which message we want to send
        if direction.lower() in ("left", "right"):
            message = win32defines.WM_HSCROLL
        elif direction.lower() in ("up", "down"):
            message = win32defines.WM_VSCROLL

        # the constant that matches direction, and how much
        try:
            scroll_type = \
                self._scroll_types[direction.lower()][amount.lower()]
        except KeyError:
            raise ValueError("""Wrong arguments:
                direction can be any of "up", "down", "left", "right"
                amount can be any of "line", "page", "end"
                """)

        # Scroll as often as we have been asked to
        if retry_interval is None:
            retry_interval = Timings.scroll_step_wait
        while count > 0:
            self.send_message(message, scroll_type)
            time.sleep(retry_interval)
            count -= 1

        return self
    # Non PEP-8 alias
    Scroll = deprecated(scroll)

    # -----------------------------------------------------------
    def get_toolbar(self):
        """Get the first child toolbar if it exists"""
        for child in self.children():
            if child.__class__.__name__ == 'ToolbarWrapper':
                return child

        return None
    # Non PEP-8 alias
    GetToolbar = deprecated(get_toolbar)

    # Non PEP-8 aliases for BaseWrapper methods
    # We keep them for the backward compatibility in legacy scripts
    ClickInput = deprecated(BaseWrapper.click_input)
    DoubleClickInput = deprecated(BaseWrapper.double_click_input)
    RightClickInput = deprecated(BaseWrapper.right_click_input)
    VerifyVisible = deprecated(BaseWrapper.verify_visible)
    _NeedsImageProp = deprecated(BaseWrapper._needs_image_prop, deprecated_name='_NeedsImageProp')
    FriendlyClassName = deprecated(BaseWrapper.friendly_class_name)
    Class = deprecated(BaseWrapper.class_name, deprecated_name='Class')
    WindowText = deprecated(BaseWrapper.window_text)
    ControlID = deprecated(BaseWrapper.control_id, deprecated_name='ControlID')
    IsVisible = deprecated(BaseWrapper.is_visible)
    IsEnabled = deprecated(BaseWrapper.is_enabled)
    Rectangle = deprecated(BaseWrapper.rectangle)
    ClientToScreen = deprecated(BaseWrapper.client_to_screen)
    ProcessID = deprecated(BaseWrapper.process_id, deprecated_name='ProcessID')
    IsDialog = deprecated(BaseWrapper.is_dialog)
    Parent = deprecated(BaseWrapper.parent)
    TopLevelParent = deprecated(BaseWrapper.top_level_parent)
    Texts = deprecated(BaseWrapper.texts)
    Children = deprecated(BaseWrapper.children)
    CaptureAsImage = deprecated(BaseWrapper.capture_as_image)
    GetProperties = deprecated(BaseWrapper.get_properties)
    DrawOutline = deprecated(BaseWrapper.draw_outline)
    IsChild = deprecated(BaseWrapper.is_child)
    VerifyActionable = deprecated(BaseWrapper.verify_actionable)
    VerifyEnabled = deprecated(BaseWrapper.verify_enabled)
    PressMouseInput = deprecated(BaseWrapper.press_mouse_input)
    ReleaseMouseInput = deprecated(BaseWrapper.release_mouse_input)
    MoveMouseInput = deprecated(BaseWrapper.move_mouse_input)
    DragMouseInput = deprecated(BaseWrapper.drag_mouse_input)
    WheelMouseInput = deprecated(BaseWrapper.wheel_mouse_input)
    TypeKeys = deprecated(BaseWrapper.type_keys)


#====================================================================
# the main reason for this is just to make sure that
# a Dialog is a known class - and we don't need to take
# an image of it (as an unknown control class)
class DialogWrapper(HwndWrapper):

    """Wrap a dialog"""

    friendlyclassname = "Dialog"
    #windowclasses = ["#32770", ]
    can_be_label = True

    #-----------------------------------------------------------
    def __init__(self, hwnd):
        """Initialize the DialogWrapper

        The only extra functionality here is to modify self.friendlyclassname
        to make it "Dialog" if the class is "#32770" otherwise to leave it
        the same as the window class.
        """
        HwndWrapper.__init__(self, hwnd)

        if self.class_name() == "#32770":
            self.friendlyclassname = "Dialog"
        else:
            self.friendlyclassname = self.class_name()

    #-----------------------------------------------------------
    def run_tests(self, tests_to_run = None, ref_controls = None):
        """Run the tests on dialog"""
        # the tests package is imported only when running unittests
        from .. import tests

        # get all the controls
        controls = [self] + self.children()

        # add the reference controls
        if ref_controls is not None:
            matched_flags = controlproperties.SetReferenceControls(
                controls, ref_controls)

            # todo: allow some checking of how well the controls matched
            # matched_flags says how well they matched
            # 1 = same number of controls
            # 2 = ID's matched
            # 4 = control classes matched
            # i.e. 1 + 2 + 4 = perfect match

        return tests.run_tests(controls, tests_to_run)
    # Non PEP-8 alias
    RunTests = deprecated(run_tests)

    #-----------------------------------------------------------
    def write_to_xml(self, filename):
        """Write the dialog an XML file (requires elementtree)"""
        controls = [self] + self.children()
        props = [ctrl.get_properties() for ctrl in controls]

        from .. import xml_helpers
        xml_helpers.WriteDialogToFile(filename, props)
    # Non PEP-8 alias
    WriteToXML = deprecated(write_to_xml)

    #-----------------------------------------------------------
    def client_area_rect(self):
        """Return the client area rectangle

        From MSDN:
        The client area of a control is the bounds of the control, minus the
        nonclient elements such as scroll bars, borders, title bars, and
        menus.
        """
        rect = win32structures.RECT(self.rectangle())
        self.send_message(win32defines.WM_NCCALCSIZE, 0, ctypes.byref(rect))
        return rect
    # Non PEP-8 alias
    ClientAreaRect = deprecated(client_area_rect)

    #-----------------------------------------------------------
    def hide_from_taskbar(self):
        """Hide the dialog from the Windows taskbar"""
        win32functions.ShowWindow(self, win32defines.SW_HIDE)
        win32functions.SetWindowLongPtr(self, win32defines.GWL_EXSTYLE, self.exstyle() | win32defines.WS_EX_TOOLWINDOW)
        win32functions.ShowWindow(self, win32defines.SW_SHOW)
    # Non PEP-8 alias
    HideFromTaskbar = deprecated(hide_from_taskbar)

    #-----------------------------------------------------------
    def show_in_taskbar(self):
        """Show the dialog in the Windows taskbar"""
        win32functions.ShowWindow(self, win32defines.SW_HIDE)
        win32functions.SetWindowLongPtr(self, win32defines.GWL_EXSTYLE,
            self.exstyle() | win32defines.WS_EX_APPWINDOW)
        win32functions.ShowWindow(self, win32defines.SW_SHOW)
    # Non PEP-8 alias
    ShowInTaskbar = deprecated(show_in_taskbar)

    #-----------------------------------------------------------
    def is_in_taskbar(self):
        """Check whether the dialog is shown in the Windows taskbar

        Thanks to David Heffernan for the idea:
        http://stackoverflow.com/questions/30933219/hide-window-from-taskbar-without-using-ws-ex-toolwindow
        A window is represented in the taskbar if:
        It has no owner and it does not have the WS_EX_TOOLWINDOW extended style,
        or it has the WS_EX_APPWINDOW extended style.
        """
        return self.has_exstyle(win32defines.WS_EX_APPWINDOW) or \
               (self.owner() is None and not self.has_exstyle(win32defines.WS_EX_TOOLWINDOW))
    # Non PEP-8 alias
    IsInTaskbar = deprecated(is_in_taskbar)

    #-----------------------------------------------------------
    def force_close(self):
        """Close the dialog forcefully using WM_QUERYENDSESSION and return the result

        Window has let us know that it doesn't want to die - so we abort
        this means that the app is not hung - but knows it doesn't want
        to close yet - e.g. it is asking the user if they want to save.
        """
        self.send_message_timeout(
            win32defines.WM_QUERYENDSESSION,
            timeout = .5,
            timeoutflags = (win32defines.SMTO_ABORTIFHUNG)) # |
        #win32defines.SMTO_NOTIMEOUTIFNOTHUNG)) # |
        #win32defines.SMTO_BLOCK)

        # get a handle we can wait on
        _, pid = win32process.GetWindowThreadProcessId(int(self.handle))
        try:
            process_wait_handle = win32api.OpenProcess(
                win32con.SYNCHRONIZE | win32con.PROCESS_TERMINATE,
                0,
                pid)
        except win32gui.error:
            return True # already closed

        result = win32event.WaitForSingleObject(
            process_wait_handle,
            int(Timings.after_windowclose_timeout * 1000))

        return result != win32con.WAIT_TIMEOUT

#    #-----------------------------------------------------------
#    def read_controls_from_xml(self, filename):
#        from pywinauto import xml_helpers
#        [controlproperties.ControlProps(ctrl) for
#            ctrl in xml_helpers.ReadPropertiesFromFile(handle)]
#    # Non PEP-8 alias
#    ReadControlsFromXML = deprecated(read_controls_from_xml)

#    #-----------------------------------------------------------
#    def add_reference(self, reference):
#
#        if len(self.children() != len(reference)):
#            raise "different number of reference controls"
#
#        for i, ctrl in enumerate(reference):
#        # loop over each of the controls
#        # and set the reference
#            if isinstance(ctrl, dict):
#                ctrl = CtrlProps(ctrl)
#
#            self.
#            if ctrl.class_name() != self.children()[i+1].class_name():
#                print "different classes"
#    # Non PEP-8 alias
#    AddReference = deprecated(add_reference)


#====================================================================
def _perform_click(
        ctrl,
        button = "left",
        pressed = "",
        coords = (0, 0),
        double = False,
        button_down = True,
        button_up = True,
        absolute = False,
        ):
    """Low level method for performing click operations"""
    if ctrl is None:
        ctrl = HwndWrapper(win32functions.GetDesktopWindow())
    ctrl.verify_actionable()
    ctrl_text = ctrl.window_text()
    if ctrl_text is None:
        ctrl_text = six.text_type(ctrl_text)

    ctrl_friendly_class_name = ctrl.friendly_class_name()

    if isinstance(coords, win32structures.RECT):
        coords = coords.mid_point()
    # allow points objects to be passed as the coords
    elif isinstance(coords, win32structures.POINT):
        coords = [coords.x, coords.y]
    else:
        coords = list(coords)

    if absolute:
        coords = ctrl.client_to_screen(coords)

    # figure out the messages for click/press
    msgs = []
    if not double:
        if button.lower() == 'left':
            if button_down:
                msgs.append(win32defines.WM_LBUTTONDOWN)
            if button_up:
                msgs.append(win32defines.WM_LBUTTONUP)
        elif button.lower() == 'middle':
            if button_down:
                msgs.append(win32defines.WM_MBUTTONDOWN)
            if button_up:
                msgs.append(win32defines.WM_MBUTTONUP)
        elif button.lower() == 'right':
            if button_down:
                msgs.append(win32defines.WM_RBUTTONDOWN)
            if button_up:
                msgs.append(win32defines.WM_RBUTTONUP)
        elif button.lower() == 'move':
            msgs.append(win32defines.WM_MOUSEMOVE)

    # figure out the messages for double clicking
    else:
        if button.lower() == 'left':
            msgs = (
                win32defines.WM_LBUTTONDOWN,
                win32defines.WM_LBUTTONUP,
                win32defines.WM_LBUTTONDBLCLK,
                win32defines.WM_LBUTTONUP)
        elif button.lower() == 'middle':
            msgs = (
                win32defines.WM_MBUTTONDOWN,
                win32defines.WM_MBUTTONUP,
                win32defines.WM_MBUTTONDBLCLK,
                win32defines.WM_MBUTTONUP)
        elif button.lower() == 'right':
            msgs = (
                win32defines.WM_RBUTTONDOWN,
                win32defines.WM_RBUTTONUP,
                win32defines.WM_RBUTTONDBLCLK,
                win32defines.WM_RBUTTONUP)
        elif button.lower() == 'move':
            msgs.append(win32defines.WM_MOUSEMOVE)

    # figure out the flags and pack coordinates
    flags, click_point = _calc_flags_and_coords(pressed, coords)

    #control_thread = win32functions.GetWindowThreadProcessId(ctrl, 0)
    #win32functions.AttachThreadInput(win32functions.GetCurrentThreadId(), control_thread, win32defines.TRUE)
    # TODO: check return value of AttachThreadInput properly

    # send each message
    for msg in msgs:
        win32functions.PostMessage(ctrl, msg, win32structures.WPARAM(flags), win32structures.LPARAM(click_point))
        #ctrl.post_message(msg, flags, click_point)
        #flags = 0

        time.sleep(Timings.sendmessagetimeout_timeout)

        # wait until the thread can accept another message
        win32functions.WaitGuiThreadIdle(ctrl.handle)

    # detach the Python process with the process that self is in
    #win32functions.AttachThreadInput(win32functions.GetCurrentThreadId(), control_thread, win32defines.FALSE)
    # TODO: check return value of AttachThreadInput properly

    # wait a certain(short) time after the click
    time.sleep(Timings.after_click_wait)

    if button.lower() == 'move':
        message = 'Moved mouse over ' + ctrl_friendly_class_name + ' "' + ctrl_text + \
                  '" to screen point ' + str(tuple(coords)) + ' by WM_MOUSEMOVE'
    else:
        message = 'Clicked ' + ctrl_friendly_class_name + ' "' + ctrl_text + \
                  '" by ' + str(button) + ' button event ' + str(tuple(coords))
        if double:
            message = 'Double-c' + message[1:]
    ActionLogger().log(message)

_mouse_flags = {
    "left": win32defines.MK_LBUTTON,
    "right": win32defines.MK_RBUTTON,
    "middle": win32defines.MK_MBUTTON,
    "shift": win32defines.MK_SHIFT,
    "control": win32defines.MK_CONTROL,
}

#====================================================================
def _calc_flags_and_coords(pressed, coords):
    """Calculate the flags to use and the coordinates for mouse actions"""
    flags = 0

    for key in pressed.split():
        flags |= _mouse_flags[key.lower()]

    click_point = win32functions.MakeLong(coords[1], coords[0])

    return flags, click_point

#====================================================================
class _DummyControl(dict):

    """A subclass of dict so that we can assign attributes"""
    pass

#====================================================================
def get_dialog_props_from_handle(hwnd):
    """Get the properties of all the controls as a list of dictionaries"""
    # wrap the dialog handle and start a new list for the
    # controls on the dialog
    try:
        controls = [hwnd, ]
        controls.extend(hwnd.children())
    except AttributeError:
        controls = [HwndWrapper(hwnd), ]

        # add all the children of the dialog
        controls.extend(controls[0].children())

    props = []

    # Add each control to the properties for this dialog
    for ctrl in controls:
        # Get properties for each control and wrap them in
        # _DummyControl so that we can assign handle
        ctrl_props = _DummyControl(ctrl.get_properties())

        # assign the handle
        ctrl_props.handle = ctrl.handle

        # offset the rectangle from the dialog rectangle
        ctrl_props['rectangle'] -= controls[0].rectangle()

        props.append(ctrl_props)

    return props
# Non PEP-8 alias
GetDialogPropsFromHandle = deprecated(get_dialog_props_from_handle)


backend.register('win32', HwndElementInfo, HwndWrapper)
backend.registry.backends['win32'].dialog_class = DialogWrapper
backend.activate('win32') # default
