<commit_before># Copyright 2018 Canonical, Ltd.
#
# 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/>.

import logging

from urwid import (
    connect_signal,
    LineBox,
    Text,
    )

from subiquitycore.ui.buttons import (
    cancel_btn,
    ok_btn,
    other_btn,
    )
from subiquitycore.ui.container import (
    Columns,
    Pile,
    WidgetWrap,
    )
from subiquitycore.ui.form import (
    ChoiceField,
    Form,
    )
from subiquitycore.ui.selector import Selector, Option
from subiquitycore.ui.stretchy import (
    Stretchy,
    )
from subiquitycore.ui.utils import button_pile, Color, Padding, screen
from subiquitycore.view import BaseView

from subiquity.models.keyboard import KeyboardSetting
from subiquity.ui.spinner import Spinner
from subiquity.ui.views import pc105

log = logging.getLogger("subiquity.ui.views.keyboard")


class AutoDetectBase(WidgetWrap):
    def __init__(self, keyboard_detector, step):
        # step is an instance of pc105.Step
        self.keyboard_detector = keyboard_detector
        self.step = step
        lb = LineBox(self.make_body(), _("Keyboard auto-detection"))
        super().__init__(lb)

    def start(self):
        pass

    def stop(self):
        pass

    def keypress(self, size, key):
        if key == 'esc':
            self.keyboard_detector.backup()
        else:
            return super().keypress(size, key)


class AutoDetectIntro(AutoDetectBase):

    def ok(self, sender):
        self.keyboard_detector.do_step(0)

    def cancel(self, sender):
        self.keyboard_detector.abort()

    def make_body(self):
        return Pile([
                Text(_("Keyboard detection starting. You will be asked a "
                       "series of questions about your keyboard. Press escape "
                       "at any time to go back to the previous screen.")),
                Text(""),
                button_pile([
                    ok_btn(label=_("OK"), on_press=self.ok),
                    ok_btn(label=_("Cancel"), on_press=self.cancel),
                    ]),
                ])


class AutoDetectFailed(AutoDetectBase):

    def ok(self, sender):
        self.keyboard_detector.abort()

    def make_body(self):
        return Pile([
                Text(_("Keyboard auto detection failed, sorry")),
                Text(""),
                button_pile([ok_btn(label="OK", on_press=self.ok)]),
                ])


class AutoDetectResult(AutoDetectBase):

    preamble = _("""\
Keyboard auto detection completed.

Your keyboard was detected as:
""")
    postamble = _("""\

If this is correct, select Done on the next screen. If not you can select \
another layout or run the automated detection again.

""")

    def ok(self, sender):
        self.keyboard_detector.keyboard_view.found_layout(self.step.result)

    def make_body(self):
        model = self.keyboard_detector.keyboard_view.model
        layout, variant = model.lookup(self.step.result)
        var_desc = []
        layout_text = _("Layout")
        var_text = _("Variant")
        width = max(len(layout_text), len(var_text), 12)
        if variant is not None:
            var_desc = [Text("%*s: %s" % (width, var_text, variant))]
        return Pile([
                Text(_(self.preamble)),
                Text("%*s: %s" % (width, layout_text, layout)),
            ] + var_desc + [
                Text(_(self.postamble)),
                button_pile([ok_btn(label=_("OK"), on_press=self.ok)]),
                ])


class AutoDetectPressKey(AutoDetectBase):
    # This is the tricky case. We need access to the "keycodes" not
    # the characters that the current keyboard set up maps the
    # keycodes to. The heavy lifting is done by the InputFilter class
    # in subiquitycore.core.

    def selectable(self):
        return True

    def make_body(self):
        self.error_text = Text("", align="center")
        return Pile([
            Text(_("Please press one of the following keys:")),
            Text(""),
            Columns([Text(s, align="center")
                     for s in self.step.symbols], dividechars=1),
            Text(""),
            Color.info_error(self.error_text),
            ])

    @property
    def input_filter(self):
        return self.keyboard_detector.keyboard_view.controller.input_filter

    def start(self):
        self.input_filter.enter_keycodes_mode()

    def stop(self):
        self.input_filter.exit_keycodes_mode()

    def keypress(self, size, key):
        log.debug('keypress %r %r', size, key)
        if key.startswith('release '):
            # Escape is key 1 on all keyboards and all layouts except
            # amigas and very old Macs so this seems safe enough.
            if key == 'release 1':
                return super().keypress(size, 'esc')
            else:
                return
        elif key.startswith('press '):
            code = int(key[len('press '):])
            if code not in self.step.keycodes:
                self.error_text.set_text(_("Input was not recognized, "
                                           "try again"))
                return
            v = self.step.keycodes[code]
        else:
            # If we're not on a linux tty, the filtering won't have
            # happened and so there's no way to get the keycodes. Do
            # something literally random instead.
            import random
            v = random.choice(list(self.step.keycodes.values()))
        self.keyboard_detector.do_step(v)


class AutoDetectKeyPresent(AutoDetectBase):

    def yes(self, sender):
        self.keyboard_detector.do_step(self.step.yes)

    def no(self, sender):
        self.keyboard_detector.do_step(self.step.no)

    def make_body(self):
        return Pile([
            Text(_("Is the following key present on your keyboard?")),
            Text(""),
            Text(self.step.symbol, align="center"),
            Text(""),
            button_pile([
                ok_btn(label=_("Yes"), on_press=self.yes),
                other_btn(label=_("No"), on_press=self.no),
                ]),
            ])
<commit_msg>improve layout of keyboard detection dialogs<commit_after># Copyright 2018 Canonical, Ltd.
#
# 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/>.

import logging

from urwid import (
    connect_signal,
    LineBox,
    Text,
    Padding as UrwidPadding
    )

from subiquitycore.ui.buttons import (
    cancel_btn,
    ok_btn,
    other_btn,
    )
from subiquitycore.ui.container import (
    Columns,
    Pile,
    WidgetWrap,
    )
from subiquitycore.ui.form import (
    ChoiceField,
    Form,
    )
from subiquitycore.ui.selector import Selector, Option
from subiquitycore.ui.stretchy import (
    Stretchy,
    )
from subiquitycore.ui.utils import button_pile, Color, Padding, screen
from subiquitycore.view import BaseView

from subiquity.models.keyboard import KeyboardSetting
from subiquity.ui.spinner import Spinner
from subiquity.ui.views import pc105

log = logging.getLogger("subiquity.ui.views.keyboard")


class AutoDetectBase(WidgetWrap):
    def __init__(self, keyboard_detector, step):
        # step is an instance of pc105.Step
        self.keyboard_detector = keyboard_detector
        self.step = step
        lb = LineBox(
            Pile([
                ('pack', Text("")),
                ('pack', UrwidPadding(self.make_body(), left=2, right=2)),
                ('pack', Text(""))
            ]),
            _("Keyboard auto-detection"))
        super().__init__(lb)

    def start(self):
        pass

    def stop(self):
        pass

    def keypress(self, size, key):
        if key == 'esc':
            self.keyboard_detector.backup()
        else:
            return super().keypress(size, key)


class AutoDetectIntro(AutoDetectBase):

    def ok(self, sender):
        self.keyboard_detector.do_step(0)

    def cancel(self, sender):
        self.keyboard_detector.abort()

    def make_body(self):
        return Pile([
                Text(_("Keyboard detection starting. You will be asked a "
                       "series of questions about your keyboard. Press escape "
                       "at any time to go back to the previous screen.")),
                Text(""),
                button_pile([
                    ok_btn(label=_("OK"), on_press=self.ok),
                    ok_btn(label=_("Cancel"), on_press=self.cancel),
                    ]),
                ])


class AutoDetectFailed(AutoDetectBase):

    def ok(self, sender):
        self.keyboard_detector.abort()

    def make_body(self):
        return Pile([
                Text(_("Keyboard auto detection failed, sorry")),
                Text(""),
                button_pile([ok_btn(label="OK", on_press=self.ok)]),
                ])


class AutoDetectResult(AutoDetectBase):

    preamble = _("""\
Keyboard auto detection completed.

Your keyboard was detected as:
""")
    postamble = _("""\

If this is correct, select Done on the next screen. If not you can select \
another layout or run the automated detection again.

""")

    def ok(self, sender):
        self.keyboard_detector.keyboard_view.found_layout(self.step.result)

    def make_body(self):
        model = self.keyboard_detector.keyboard_view.model
        layout, variant = model.lookup(self.step.result)
        var_desc = []
        layout_text = _("Layout")
        var_text = _("Variant")
        width = max(len(layout_text), len(var_text), 12)
        if variant is not None:
            var_desc = [Text("%*s: %s" % (width, var_text, variant))]
        return Pile([
                Text(_(self.preamble)),
                Text("%*s: %s" % (width, layout_text, layout)),
            ] + var_desc + [
                Text(_(self.postamble)),
                button_pile([ok_btn(label=_("OK"), on_press=self.ok)]),
                ])


class AutoDetectPressKey(AutoDetectBase):
    # This is the tricky case. We need access to the "keycodes" not
    # the characters that the current keyboard set up maps the
    # keycodes to. The heavy lifting is done by the InputFilter class
    # in subiquitycore.core.

    def selectable(self):
        return True

    def make_body(self):
        self.error_text = Text("", align="center")
        self.pile = Pile([
            ('pack', Text(_("Please press one of the following keys:"))),
            ('pack', Text("")),
            ('pack', Columns([Text(s, align="center")
                              for s in self.step.symbols], dividechars=1)),
            ])
        return self.pile

    @property
    def input_filter(self):
        return self.keyboard_detector.keyboard_view.controller.input_filter

    def start(self):
        self.input_filter.enter_keycodes_mode()

    def stop(self):
        self.input_filter.exit_keycodes_mode()

    def error(self, message):
        t = Color.info_error(Text(message, align='center'))
        self.pile.contents.extend([
            (Text(""), self.pile.options('pack')),
            (t,        self.pile.options('pack')),
            ])

    def keypress(self, size, key):
        log.debug('keypress %r %r', size, key)
        if key.startswith('release '):
            # Escape is key 1 on all keyboards and all layouts except
            # amigas and very old Macs so this seems safe enough.
            if key == 'release 1':
                return super().keypress(size, 'esc')
            else:
                return
        elif key.startswith('press '):
            code = int(key[len('press '):])
            if code not in self.step.keycodes:
                self.error(_("Input was not recognized, try again"))
                return
            v = self.step.keycodes[code]
        else:
            # If we're not on a linux tty, the filtering won't have
            # happened and so there's no way to get the keycodes. Do
            # something literally random instead.
            if key == 'e':
                self.error(_("Input was not recognized, try again"))
                return
            import random
            v = random.choice(list(self.step.keycodes.values()))
        self.keyboard_detector.do_step(v)


class AutoDetectKeyPresent(AutoDetectBase):

    def yes(self, sender):
        self.keyboard_detector.do_step(self.step.yes)

    def no(self, sender):
        self.keyboard_detector.do_step(self.step.no)

    def make_body(self):
        return Pile([
            Text(_("Is the following key present on your keyboard?")),
            Text(""),
            Text(self.step.symbol, align="center"),
            Text(""),
            button_pile([
                ok_btn(label=_("Yes"), on_press=self.yes),
                other_btn(label=_("No"), on_press=self.no),
                ]),
            ])

{'fraction_non_alphanumeric': 0.10542168674698796, 'fraction_numerical': 0.0025817555938037868, 'mean_word_length': 2.1867001828153563, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4015948', 'n_tokens_mistral': 4080, 'n_tokens_neox': 3869, 'n_words': 1133}