class Text(DynStrActionBase):
    """
        Action that sends keyboard events to type text.

        Arguments:
         - *spec* (*str*) -- the text to type
         - *static* (boolean) --
           if *True*, do not dynamically interpret *spec*
           when executing this action
         - *pause* (*float*) --
           the time to pause between each keystroke, given
           in seconds
         - *autofmt* (boolean) --
           if *True*, attempt to format the text with correct
           spacing and capitalization.  This is done by first mimicking
           a word recognition and then analyzing its spacing and
           capitalization and applying the same formatting to the text.

    """

    _pause_default = 0.02
    _keyboard = Keyboard()
    _specials = {
                 "\n":   typeables["enter"],
                 "\t":   typeables["tab"],
                }

    def __init__(self, spec=None, static=False, pause=_pause_default,
                 autofmt=False):
        self._pause = pause
        self._autofmt = autofmt
        DynStrActionBase.__init__(self, spec=spec, static=static)

    def _parse_spec(self, spec):
        """ Convert the given *spec* to keyboard events. """
        events = []
        for character in spec:
            if character in self._specials:
                typeable = self._specials[character]
            else:
                typeable = Keyboard.get_typeable(character)
            events.extend(typeable.events(self._pause))
        return events

    def _execute_events(self, events):
        """
            Send keyboard events.

            If instance was initialized with *autofmt* True,
            then this method will mimic a word recognition
            and analyze its formatting so as to autoformat
            the text's spacing and capitalization before
            sending it as keyboard events.

        """

        if self._autofmt:
            # Mimic a word, select and copy it to retrieve capitalization.
            get_engine().mimic("test")
            Key("cs-left, c-c/5").execute()
            word = Clipboard.get_text()

            # Inspect formatting of the mimicked word.
            index = word.find("test")
            if index == -1:
                index = word.find("Test")
                capitalize = True
                if index == -1:
                    self._log.error("Failed to autoformat.")
                    return False
            else:
                capitalize = False

            # Capitalize given text if necessary.
            text = self._spec
            if capitalize:
                text = text[0].capitalize() + text[1:]

            # Reconstruct autoformatted output and convert it
            #  to keyboard events.
            prefix = word[:index]
            suffix = word[index + 4:]
            events = self._parse_spec(prefix + text + suffix)

        # Send keyboard events.
        self._keyboard.send_keyboard_events(events)
        return True
class BaseKeyboardAction(DynStrActionBase):
    """
        Base keystroke emulation action.

    """

    _keyboard = Keyboard()
    _pause_default = PAUSE_DEFAULT

    def __init__(self, spec=None, static=False, use_hardware=False):
        self._use_hardware = use_hardware

        # Load the Windows-only config file if necessary.
        if not _CONFIG_LOADED and os.name == "nt":
            load_configuration()

        super(BaseKeyboardAction, self).__init__(spec, static)

    def require_hardware_events(self):
        """
        Return `True` if the current context requires hardware emulation.
        """
        if self._use_hardware:
            return True

        # Load the keyboard configuration, if necessary.
        global _CONFIG_LOADED
        if not _CONFIG_LOADED:
            load_configuration()

        # Otherwise check if hardware events should be used with the current
        # foreground window.
        from dragonfly.windows import Window
        foreground_executable = basename(Window.get_foreground()
                                         .executable.lower())
        return ((not UNICODE_KEYBOARD) or
                (foreground_executable in HARDWARE_APPS))

    def _get_typeable(self, key_symbol, use_hardware):
        # Use the Typeable object for the symbol, if it exists.
        typeable = typeables.get(key_symbol)
        if typeable:
            # Update the object and return it.
            typeable.update(use_hardware)
            return typeable

        # Otherwise, get a new Typeable for the symbol, if possible.
        is_text = not use_hardware
        try:
            typeable = self._keyboard.get_typeable(key_symbol,
                                                   is_text=is_text)
        except ValueError:
            pass

        # If getting a Typeable failed, then, if it is allowed, try
        #  again with is_text=True.  On Windows, this will use Unicode
        #  events instead.
        if not (typeable or use_hardware):
            try:
                typeable = self._keyboard.get_typeable(key_symbol,
                                                       is_text=True)
            except ValueError:
                pass

        return typeable