def _on_detect_button_clicked(self, button):
        '''Handle the "Detect" button's "clicked" signal.'''

        @thread_callback
        def report_failure(message):
            '''Handle failures reported by the service backend.'''

            self.__detection_driver = None
            self.__progressbar_detect_basics.hide()
            self.__button_detect_basics.set_sensitive(True)
            show_message(self.__dialog, _('Remote Configuration Failed'), message)

            if service:
                service.ManageLircDaemon('start')

        @thread_callback
        def report_success(result, sender=None):
            '''Handle success reported by the service backend.'''

            self.__detection_driver = None
            self.__progressbar_detect_basics.hide()
            self.__button_detect_basics.set_sensitive(True)

            hwdb = lirc.RemotesDatabase()
            hwdb.read(StringIO(result))

            remote = hwdb[0]
            remote.properties['name'] = [self.__retrieve_remote_name()]

            self.__remote.properties = remote.properties
            self.__update_basics_model(self.__remote.properties)

            if service:
                service.ManageLircDaemon('start')

        @thread_callback
        def report_progress(sender=None):
            '''Handle progress reported by the service backend.'''

            self.__progressbar_detect_basics.pulse()

        @thread_callback
        def request_action(message, details=None, sender=None):
            '''Forward actions requests from the service backend to the user.'''

            self.__progressbar_detect_basics.set_text(message)
            self.__progressbar_detect_basics.set_fraction(0)

            if details:
                # TODO: This dialog should probably have a cancel button.
                response_buttons = (
                    (gtk.RESPONSE_ACCEPT, _('_Start'), gtk.STOCK_EXECUTE),
                )

                show_message(self.__dialog, message, details,
                             message_type=gtk.MESSAGE_INFO,
                             buttons=response_buttons)

            driver.Proceed()

        bus, service, driver = None, None, None

        try:
            self.__stop_detection()

            bus     = backend.get_service_bus()
            service = backend.get_service(bus)

            driver = service.DetectParameters(self.__receiver.lirc_driver or '',
                                              self.__device or '')

            driver = bus.get_object(service.requested_bus_name, driver)
            driver = dbus.Interface(driver, backend.ExternalToolDriver.INTERFACE_NAME)

            driver.connect_to_signal('RequestAction',  request_action)
            driver.connect_to_signal('ReportProgress', report_progress)
            driver.connect_to_signal('ReportSuccess',  report_success)
            driver.connect_to_signal('ReportFailure',  report_failure)

            # TODO: Stop the key-listener, when we know that lircd is disabled.
            service.ManageLircDaemon('stop')

            self.__detection_driver = driver
            self.__detection_driver.Execute()

            self.__button_detect_basics.set_sensitive(False)
            self.__progressbar_detect_basics.show()

        except dbus.DBusException, ex:
            report_failure.callback(ex.message)
            self.__stop_detection()
    def __start_learning(self, path):
        '''Start learning of a remote control's key code.'''

        keys = self.__treeview_keys.get_model()

        if self.__learning_row:
            keys.set_state(self.__learning_row.get_path(), None)
        if not self.__learning_timeout:
            self.__learning_timeout = gobject.timeout_add(100, self._on_learning_timeout)

        self.__learning_row = gtk.TreeRowReference(keys, path)
        mapping, = keys.get(keys.get_iter(path), 1)

        configuration = self.__remote.configuration
        driver = self.__receiver.lirc_driver or ''
        device = self.__device or ''

        # pylint: disable-msg=W0613
        @thread_callback
        def report_success(result, sender=None):
            '''Handle success reported by the service backend.'''

            self.__learning_driver = None
            self.__stop_learning()

            hwdb = lirc.RemotesDatabase()
            hwdb.read(StringIO(result))
            remote = len(hwdb) and hwdb[0]

            if remote:
                self.__treeview_keys.get_model().update(remote.key_codes)

        # pylint: disable-msg=W0613
        @thread_callback
        def report_failure(message, sender=None):
            '''Handle failures reported by the service backend.'''

            show_message(self.__dialog, _('Learning of Key Code Failed'), message)

            self.__learning_driver = None
            self.__stop_learning()

        try:
            bus     = backend.get_service_bus()
            service = backend.get_service(bus)

            # Stop the lirc deamon, as some drivers (e.g. lirc_streamzap) do
            # not support concurrent device access:
            service.ManageLircDaemon('stop')

            driver  = service.LearnKeyCode(driver, device, configuration, [mapping.key])

            driver = bus.get_object(service.requested_bus_name, driver)
            driver = dbus.Interface(driver, backend.ExternalToolDriver.INTERFACE_NAME)

            self.__learning_driver = driver

            driver.connect_to_signal('ReportSuccess', report_success)
            driver.connect_to_signal('ReportFailure', report_failure)

            driver.Execute()

        except dbus.DBusException, ex:
            report_failure.callback(ex.message)