view libervia/tui/profile_manager.py @ 4320:9658c534287e

plugin XEP-0215, XEP-0376: fix bad calls to `hasFeature`: `hasFeature` was called like blocking code, missing the `await`. This has been fixed, and is now using the `memory.disco.has_feature` version.
author Goffi <goffi@goffi.org>
date Mon, 30 Sep 2024 14:14:38 +0200
parents b620a8e882e1
children
line wrap: on
line source

#!/usr/bin/env python3


# Libervia TUI
# Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org)

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program 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 Affero General Public License for more details.

# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from libervia.backend.core.i18n import _
from libervia.backend.core import log as logging

log = logging.getLogger(__name__)
from libervia.frontends.quick_frontend.quick_profile_manager import QuickProfileManager
from libervia.tui.constants import Const as C
from libervia.tui.keys import action_key_map as a_key
from urwid_satext import sat_widgets
import urwid


class ProfileManager(QuickProfileManager, urwid.WidgetWrap):
    def __init__(self, host, autoconnect=None):
        QuickProfileManager.__init__(self, host, autoconnect)

        # login & password box must be created before list because of on_profile_change
        self.login_wid = sat_widgets.AdvancedEdit(_("Login:"), align="center")
        self.pass_wid = sat_widgets.Password(_("Password:"), align="center")

        style = ["no_first_select"]
        profiles = host.bridge.profiles_list_get()
        profiles.sort()
        self.list_profile = sat_widgets.List(
            profiles, style=style, align="center", on_change=self.on_profile_change
        )

        # new & delete buttons
        buttons = [
            urwid.Button(_("New"), self.on_new_profile),
            urwid.Button(_("Delete"), self.on_delete_profile),
        ]
        buttons_flow = urwid.GridFlow(
            buttons,
            max([len(button.get_label()) for button in buttons]) + 4,
            1,
            1,
            "center",
        )

        # second part: login information:
        divider = urwid.Divider("-")

        # connect button
        connect_button = sat_widgets.CustomButton(
            _("Connect"), self.on_connect_profiles, align="center"
        )

        # we now build the widget
        list_walker = urwid.SimpleFocusListWalker(
            [
                buttons_flow,
                self.list_profile,
                divider,
                self.login_wid,
                self.pass_wid,
                connect_button,
            ]
        )
        frame_body = urwid.ListBox(list_walker)
        frame = urwid.Frame(
            frame_body,
            urwid.AttrMap(urwid.Text(_("Profile Manager"), align="center"), "title"),
        )
        self.main_widget = urwid.LineBox(frame)
        urwid.WidgetWrap.__init__(self, self.main_widget)

        self.go(autoconnect)

    def keypress(self, size, key):
        if key == a_key["APP_QUIT"]:
            self.host.on_exit()
            raise urwid.ExitMainLoop()
        elif key in (a_key["FOCUS_UP"], a_key["FOCUS_DOWN"]):
            focus_diff = 1 if key == a_key["FOCUS_DOWN"] else -1
            list_box = self.main_widget.base_widget.body
            current_focus = list_box.body.get_focus()[1]
            if current_focus is None:
                return
            while True:
                current_focus += focus_diff
                if current_focus < 0 or current_focus >= len(list_box.body):
                    break
                if list_box.body[current_focus].selectable():
                    list_box.set_focus(
                        current_focus, "above" if focus_diff == 1 else "below"
                    )
                    list_box._invalidate()
                    return
        return super(ProfileManager, self).keypress(size, key)

    def cancel_dialog(self, button):
        self.host.remove_pop_up()

    def new_profile(self, button, edit):
        """Create the profile"""
        name = edit.get_edit_text()
        self.host.bridge.profile_create(
            name,
            callback=lambda: self.new_profile_created(name),
            errback=self.profile_creation_failure,
        )

    def new_profile_created(self, profile):
        # new profile will be selected, and a selected profile assume the session is started
        self.host.bridge.profile_start_session(
            "",
            profile,
            callback=lambda __: self.new_profile_session_started(profile),
            errback=self.profile_creation_failure,
        )

    def new_profile_session_started(self, profile):
        self.host.remove_pop_up()
        self.refill_profiles()
        self.list_profile.select_value(profile)
        self.current.profile = profile
        self.get_connection_params(profile)
        self.host.redraw()

    def profile_creation_failure(self, reason):
        self.host.remove_pop_up()
        message = self._get_error_message(reason)
        self.host.alert(_("Can't create profile"), message)

    def delete_profile(self, button):
        self._delete_profile()
        self.host.remove_pop_up()

    def on_new_profile(self, e):
        pop_up_widget = sat_widgets.InputDialog(
            _("New profile"),
            _("Please enter a new profile name"),
            cancel_cb=self.cancel_dialog,
            ok_cb=self.new_profile,
        )
        self.host.show_pop_up(pop_up_widget)

    def on_delete_profile(self, e):
        if self.current.profile:
            pop_up_widget = sat_widgets.ConfirmDialog(
                _("Are you sure you want to delete the profile {} ?").format(
                    self.current.profile
                ),
                no_cb=self.cancel_dialog,
                yes_cb=self.delete_profile,
            )
            self.host.show_pop_up(pop_up_widget)

    def on_connect_profiles(self, button):
        """Connect the profiles and start the main widget

        @param button: the connect button
        """
        self._on_connect_profiles()

    def reset_fields(self):
        """Set profile to None, and reset fields"""
        super(ProfileManager, self).reset_fields()
        self.list_profile.unselect_all(invisible=True)

    def set_profiles(self, profiles):
        """Update the list of profiles"""
        self.list_profile.change_values(profiles)
        self.host.redraw()

    def get_profiles(self):
        return self.list_profile.get_selected_values()

    def get_jid(self):
        return self.login_wid.get_edit_text()

    def getPassword(self):
        return self.pass_wid.get_edit_text()

    def set_jid(self, jid_):
        self.login_wid.set_edit_text(jid_)
        self.current.login = jid_
        self.host.redraw()  # FIXME: redraw should be avoided

    def set_password(self, password):
        self.pass_wid.set_edit_text(password)
        self.current.password = password
        self.host.redraw()

    def on_profile_change(self, list_wid, widget=None, selected=None):
        """This is called when a profile is selected in the profile list.

        @param list_wid: the List widget who sent the event
        """
        self.update_connection_params()
        focused = list_wid.focus
        selected = focused.get_state() if focused is not None else False
        if not selected:  # profile was just unselected
            return
        focused.set_state(
            False, invisible=True
        )  # we don't want the widget to be selected until we are sure we can access it

        def authenticate_cb(data, cb_id, profile):
            if C.bool(data.pop("validated", C.BOOL_FALSE)):
                self.current.profile = profile
                focused.set_state(True, invisible=True)
                self.get_connection_params(profile)
                self.host.redraw()
            self.host.action_manager(data, callback=authenticate_cb, profile=profile)

        self.host.action_launch(
            C.AUTHENTICATE_PROFILE_ID, callback=authenticate_cb, profile=focused.text
        )