Register a macro.

        If ``macroName`` is **None** then its named is deduced from
        its class name (see ``qteMacroNameMangling`` for details).

        Multiple macros with the same name can co-exist as long as
        their applet- and widget signatures, as reported by the
        ``qteAppletSignature`` and ``qteWidgetSignature`` methods,
        differ. If ``macroCls`` has the same name and signatures as an
        already registered macro then the ``replaceMacro`` flag
        decides:

        * **True**: the existing macro will be replaced for all
          applet- and widget signatures specified by the new macro
          ``macroCls``.
        * **False**: the ``macroCls`` will not be registered.

        The method returns **None** if an error occurred (eg. the
        macro constructor is faulty), or the macro name as a
        string. If a macro was already registered and not replaced
        (ie. ``replaceMacro``) then the macro name is returned
        nonetheless.

        .. note:: if an existing macro is replaced the old macro
           is not deleted (it probably should be, though).

        |Args|

        * ``macroCls`` (**QtmacsMacro**): QtmacsMacro or derived
          (not type checked!)
        * ``replaceMacro`` (**bool**): whether or not to replace
          an existing macro.
        * ``macroName`` (**str**): the name under which the macro
          should be registered.

        |Returns|

        **str**: the name of the just registered macro, or **None** if
          that failed.

        |Raises|

        * **QtmacsArgumentError** if at least one argument has an invalid type.

def qteRegisterMacro(self, macroCls, replaceMacro: bool=False,
                         macroName: str=None):
        """
        Register a macro.

        If ``macroName`` is **None** then its named is deduced from
        its class name (see ``qteMacroNameMangling`` for details).

        Multiple macros with the same name can co-exist as long as
        their applet- and widget signatures, as reported by the
        ``qteAppletSignature`` and ``qteWidgetSignature`` methods,
        differ. If ``macroCls`` has the same name and signatures as an
        already registered macro then the ``replaceMacro`` flag
        decides:

        * **True**: the existing macro will be replaced for all
          applet- and widget signatures specified by the new macro
          ``macroCls``.
        * **False**: the ``macroCls`` will not be registered.

        The method returns **None** if an error occurred (eg. the
        macro constructor is faulty), or the macro name as a
        string. If a macro was already registered and not replaced
        (ie. ``replaceMacro``) then the macro name is returned
        nonetheless.

        .. note:: if an existing macro is replaced the old macro
           is not deleted (it probably should be, though).

        |Args|

        * ``macroCls`` (**QtmacsMacro**): QtmacsMacro or derived
          (not type checked!)
        * ``replaceMacro`` (**bool**): whether or not to replace
          an existing macro.
        * ``macroName`` (**str**): the name under which the macro
          should be registered.

        |Returns|

        **str**: the name of the just registered macro, or **None** if
          that failed.

        |Raises|

        * **QtmacsArgumentError** if at least one argument has an invalid type.
        """
        # Check type of input arguments.
        if not issubclass(macroCls, QtmacsMacro):
            args = ('macroCls', 'class QtmacsMacro', inspect.stack()[0][3])
            raise QtmacsArgumentError(*args)

        # Try to instantiate the macro class.
        try:
            macroObj = macroCls()
        except Exception:
            msg = 'The macro <b>{}</b> has a faulty constructor.'
            msg = msg.format(macroCls.__name__)

            self.qteLogger.error(msg, stack_info=True)
            return None

        # The three options to determine the macro name, in order of
        # precedence, are: passed to this function, specified in the
        # macro constructor, name mangled.
        if macroName is None:
            # No macro name was passed to the function.
            if macroObj.qteMacroName() is None:
                # The macro has already named itself.
                macroName = self.qteMacroNameMangling(macroCls)
            else:
                # The macro name is inferred from the class name.
                macroName = macroObj.qteMacroName()

        # Let the macro know under which name it is known inside Qtmacs.
        macroObj._qteMacroName = macroName

        # Ensure the macro has applet signatures.
        if len(macroObj.qteAppletSignature()) == 0:
            msg = 'Macro <b>{}</b> has no applet signatures.'.format(macroName)
            self.qteLogger.error(msg, stack_info=True)
            return None

        # Ensure the macro has widget signatures.
        if len(macroObj.qteWidgetSignature()) == 0:
            msg = 'Macro <b>{}</b> has no widget signatures.'.format(macroName)
            self.qteLogger.error(msg, stack_info=True)
            return None

        # Flag to indicate that at least one new macro type was
        # registered.
        anyRegistered = False

        # Iterate over all applet signatures.
        for app_sig in macroObj.qteAppletSignature():
            # Iterate over all widget signatures.
            for wid_sig in macroObj.qteWidgetSignature():
                # Infer the macro name from the class name of the
                # passed macro object.
                macroNameInternal = (macroName, app_sig, wid_sig)

                # If a macro with this name already exists then either
                # replace it, or skip the registration process for the
                # new one.
                if macroNameInternal in self._qteRegistryMacros:
                    if replaceMacro:
                        # Remove existing macro.
                        tmp = self._qteRegistryMacros.pop(macroNameInternal)
                        msg = 'Replacing existing macro <b>{}</b> with new {}.'
                        msg = msg.format(macroNameInternal, macroObj)
                        self.qteLogger.info(msg)
                        tmp.deleteLater()
                    else:
                        msg = 'Macro <b>{}</b> already exists (not replaced).'
                        msg = msg.format(macroNameInternal)
                        self.qteLogger.info(msg)
                        # Macro was not registered for this widget
                        # signature.
                        continue

                # Add macro object to the registry.
                self._qteRegistryMacros[macroNameInternal] = macroObj
                msg = ('Macro <b>{}</b> successfully registered.'
                       .format(macroNameInternal))
                self.qteLogger.info(msg)
                anyRegistered = True

        # Return the name of the macro, irrespective of whether or not
        # it is a newly created macro, or if the old macro was kept
        # (in case of a name conflict).
        return macroName