Beispiel #1
0
        def __init__(self, wiface='wlan0', socket_id=0, no_confirm_ether=False):

            self.wiface = wiface
            self.network_list = []
            self.no_confirm_ether = no_confirm_ether

            # Default basic styling
            apply_common_to_screen()

            # Attach specific styling
            apply_styling_to_screen(self.CSS_PATH)

            # Set window
            base_class.__init__(
                self,
                _("Kano WiFi"),
                self.width,
                self.height,
                socket_id
            )

            self.top_bar = TopBar(_("Kano WiFi"))
            self.top_bar.set_prev_callback(self.refresh_networks)
            self.top_bar.set_close_callback(Gtk.main_quit)
            self.prev_handler = None
            self.connect('delete-event', Gtk.main_quit)
            self.set_keep_above(True)
            self.set_icon_name('kano-settings')
            self.set_decorated(True)

            if self._base_name == "Window":
                self.set_titlebar(self.top_bar)

            self._launch_application()
        def __init__(self, wiface='wlan0', socket_id=0, no_confirm_ether=False):

            self.wiface = wiface
            self.network_list = []
            self.no_confirm_ether = no_confirm_ether

            # Default basic styling
            apply_common_to_screen()

            # Attach specific styling
            apply_styling_to_screen(self.CSS_PATH)

            # Set window
            base_class.__init__(
                self,
                _("Kano WiFi"),
                self.width,
                self.height,
                socket_id
            )

            self.top_bar = TopBar(_("Kano WiFi"))
            self.top_bar.set_prev_callback(self.refresh_networks)
            self.top_bar.set_close_callback(Gtk.main_quit)
            self.prev_handler = None
            self.connect('delete-event', Gtk.main_quit)
            self.set_keep_above(True)
            self.set_icon_name('kano-settings')
            self.set_decorated(True)

            if self._base_name == "Window":
                self.set_titlebar(self.top_bar)

            self._launch_application()
Beispiel #3
0
        def __init__(self,
                     screen_number=None,
                     screen_name=None,
                     socket_id=0,
                     onescreen=False):
            # Check for internet, if screen is 12 means no internet
            if screen_number == 12 or screen_name == 'no-internet':
                common.has_internet = False
            else:
                common.has_internet = is_internet()

            # Set combobox styling to the screen
            # Is done here so we don't attach the styling multiple times when
            # switching screens
            apply_styling_to_screen(self.CSS_PATH)
            apply_common_to_screen()
            KanoComboBox.apply_styling_to_screen()
            ScrolledWindow.apply_styling_to_screen(wide=True)

            # Set window
            base_class.__init__(self, _("Settings"), self.width, self.height,
                                socket_id)

            self.set_decorated(True)
            self.top_bar = TopBar(_("Settings"))
            self.top_bar.set_close_callback(self.close_window)
            self.prev_handler = None
            self.set_icon_name('kano-settings')

            if self._base_name == "Window":
                self.set_titlebar(self.top_bar)

            self._onescreen = onescreen

            self.connect('delete-event', Gtk.main_quit)
            # In case we are called from kano-world-launcher, terminate splash
            os.system('kano-stop-splash')
            # Init to Home Screen
            HomeScreen(self,
                       screen_number=screen_number,
                       screen_name=screen_name)
Beispiel #4
0
    def __init__(self):
        apply_common_to_screen()

        self.a = self.b = self.c = 0
        self.switching = 0

        ApplicationWindow.__init__(self, _('Login'), self.WIDTH, self.HEIGHT)
        self.connect("delete-event", Gtk.main_quit)

        # Create a new LightDM.Greeter instance which will be used by the 2 views
        self.greeter = GreeterWindow.greeter.new()

        # Create the two views: one for normal Login, the other to create a new account
        self.password_view = PasswordView(None, self.greeter)
        self.newuser_view = NewUserView(self.greeter)
        self.login_with_kw_view = LoginWithKanoWorldView(self.greeter)

        self.grid = Gtk.Grid()
        self.set_main_widget(self.grid)

        self.grid.set_column_spacing(30)
        self.grid.set_row_spacing(30)

        self.top_bar = TopBar(_('Login'))
        self._remove_top_bar_buttons()
        self.top_bar.set_size_request(self.WIDTH, -1)
        self.grid.attach(self.top_bar, 0, 0, 3, 1)

        self.grid.attach(Gtk.Label(), 0, 3, 3, 1)

        self.top_bar.set_prev_callback(self._back_cb)

        self.user_list = UserListView()

        self.go_to_users()

        cursor = Gdk.Cursor.new(Gdk.CursorType.ARROW)
        self.get_root_window().set_cursor(cursor)
Beispiel #5
0
    def __init__(self, install=None, icon_only=False, tutorial=False):
        ApplicationWindow.__init__(self, 'Apps', 755, 588)

        self._install = install
        self._tutorial = tutorial
        self._icon_only = icon_only
        self._last_page = 0

        self.connect("show", self._app_loaded)

        # Destructor
        self.connect('delete-event', Gtk.main_quit)

        self.set_icon_from_file("/usr/share/kano-desktop/icons/apps.png")

        # Styling
        screen = Gdk.Screen.get_default()
        specific_css_provider = Gtk.CssProvider()
        specific_css_provider.load_from_path(Media.media_dir() +
                                             'css/style.css')
        specific_style_context = Gtk.StyleContext()
        specific_style_context.add_provider_for_screen(
            screen, specific_css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
        style = self.get_style_context()
        style.add_class('main_window')

        # Setup widgets
        self.set_decorated(True)
        self._top_bar = TopBar(_("Apps"), self._win_width, False)
        self._top_bar.set_close_callback(Gtk.main_quit)
        self.set_titlebar(self._top_bar)

        self._contents = Contents(self)

        self.set_main_widget(self._contents)

        self.show_apps_view()
Beispiel #6
0
    def __init__(self, install=None, icon_only=False, tutorial=False):
        ApplicationWindow.__init__(self, 'Apps', 755, 588)

        self._install = install
        self._tutorial = tutorial
        self._icon_only = icon_only
        self._last_page = 0

        self.connect("show", self._app_loaded)

        # Destructor
        self.connect('delete-event', Gtk.main_quit)

        self.set_icon_from_file("/usr/share/kano-desktop/icons/apps.png")

        # Styling
        screen = Gdk.Screen.get_default()
        specific_css_provider = Gtk.CssProvider()
        specific_css_provider.load_from_path(Media.media_dir() +
                                             'css/style.css')
        specific_style_context = Gtk.StyleContext()
        specific_style_context.add_provider_for_screen(
            screen,
            specific_css_provider,
            Gtk.STYLE_PROVIDER_PRIORITY_USER
        )
        style = self.get_style_context()
        style.add_class('main_window')

        # Setup widgets
        self.set_decorated(True)
        self._top_bar = TopBar(_("Apps"), self._win_width, False)
        self._top_bar.set_close_callback(Gtk.main_quit)
        self.set_titlebar(self._top_bar)

        self._contents = Contents(self)

        self.set_main_widget(self._contents)

        self.show_apps_view()
        def __init__(self, screen_number=None, screen_name=None,
                     socket_id=0, onescreen=False):
            # Check for internet, if screen is 12 means no internet
            if screen_number == 12 or screen_name == 'no-internet':
                common.has_internet = False
            else:
                common.has_internet = is_internet()

            # Set combobox styling to the screen
            # Is done here so we don't attach the styling multiple times when
            # switching screens
            apply_styling_to_screen(self.CSS_PATH)
            apply_common_to_screen()
            KanoComboBox.apply_styling_to_screen()
            ScrolledWindow.apply_styling_to_screen(wide=True)

            # Set window
            base_class.__init__(self, _("Settings"), self.width,
                                self.height, socket_id)

            self.set_decorated(True)
            self.top_bar = TopBar(_("Settings"))
            self.top_bar.set_close_callback(self.close_window)
            self.prev_handler = None
            self.set_icon_name('kano-settings')

            if self._base_name == "Window":
                self.set_titlebar(self.top_bar)

            self._onescreen = onescreen

            self.connect('delete-event', Gtk.main_quit)
            # In case we are called from kano-world-launcher, terminate splash
            os.system('kano-stop-splash')
            # Init to Home Screen
            HomeScreen(self, screen_number=screen_number, screen_name=screen_name)
Beispiel #8
0
class MainWindow(ApplicationWindow):
    def __init__(self, install=None, icon_only=False, tutorial=False):
        ApplicationWindow.__init__(self, 'Apps', 755, 588)

        self._install = install
        self._tutorial = tutorial
        self._icon_only = icon_only
        self._last_page = 0

        self.connect("show", self._app_loaded)

        # Destructor
        self.connect('delete-event', Gtk.main_quit)

        self.set_icon_from_file("/usr/share/kano-desktop/icons/apps.png")

        # Styling
        screen = Gdk.Screen.get_default()
        specific_css_provider = Gtk.CssProvider()
        specific_css_provider.load_from_path(Media.media_dir() +
                                             'css/style.css')
        specific_style_context = Gtk.StyleContext()
        specific_style_context.add_provider_for_screen(
            screen,
            specific_css_provider,
            Gtk.STYLE_PROVIDER_PRIORITY_USER
        )
        style = self.get_style_context()
        style.add_class('main_window')

        # Setup widgets
        self.set_decorated(True)
        self._top_bar = TopBar(_("Apps"), self._win_width, False)
        self._top_bar.set_close_callback(Gtk.main_quit)
        self.set_titlebar(self._top_bar)

        self._contents = Contents(self)

        self.set_main_widget(self._contents)

        self.show_apps_view()

    def get_main_area(self):
        return self._contents

    def get_last_page(self):
        return self._last_page

    def set_last_page(self, last_page_num):
        self._last_page = last_page_num

    def show_apps_view(self, button=None, event=None):
        self._top_bar.disable_prev()
        self._apps = apps = Apps(get_applications(), self)
        self.get_main_area().set_contents(apps)

    def refresh(self, category=None):
        for app in get_applications():
            if self._apps.has_app(app):
                self._apps.update_app(app)
            else:
                self._apps.add_app(app)

    def _app_loaded(self, widget):
        if self._install is not None:
            self._install_apps()
        elif self._tutorial:
            self._show_icon_tutorial()

    def _show_icon_tutorial(self):
        if load_app_state_variable('kano-apps', 'icon-tutorial-shown'):
            return
        else:
            save_app_state_variable('kano-apps', 'icon-tutorial-shown', True)

        kdialog = KanoDialog(
            _("Add more apps to the desktop"),
            _(
                "Click the '+' button to the right of the app name to "
                "make it appear on the desktop. You can remove it again "
                "by clicking on 'x'."
            ),
            {
                _("OK, GOT IT"): {
                    "return_value": 0,
                    "color": "green"
                }
            },
            parent_window=self
        )
        kdialog.set_action_background("grey")
        kdialog.title.description.set_max_width_chars(40)
        kdialog.run()

    def _install_apps(self):
        pw = None
        for app in self._install:
            inst = AppInstaller(app, self._apps, pw, self)
            inst.set_check_if_installed(True)
            inst.set_icon_only(self._icon_only)
            inst.install()
            pw = inst.get_sudo_pw()

        self.set_last_page(0)
        refresh_package_list()
        self.refresh()
Beispiel #9
0
class MainWindow(ApplicationWindow):
    def __init__(self, install=None, icon_only=False, tutorial=False):
        ApplicationWindow.__init__(self, 'Apps', 755, 588)

        self._install = install
        self._tutorial = tutorial
        self._icon_only = icon_only
        self._last_page = 0

        self.connect("show", self._app_loaded)

        # Destructor
        self.connect('delete-event', Gtk.main_quit)

        self.set_icon_from_file("/usr/share/kano-desktop/icons/apps.png")

        # Styling
        screen = Gdk.Screen.get_default()
        specific_css_provider = Gtk.CssProvider()
        specific_css_provider.load_from_path(Media.media_dir() +
                                             'css/style.css')
        specific_style_context = Gtk.StyleContext()
        specific_style_context.add_provider_for_screen(
            screen, specific_css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
        style = self.get_style_context()
        style.add_class('main_window')

        # Setup widgets
        self.set_decorated(True)
        self._top_bar = TopBar(_("Apps"), self._win_width, False)
        self._top_bar.set_close_callback(Gtk.main_quit)
        self.set_titlebar(self._top_bar)

        self._contents = Contents(self)

        self.set_main_widget(self._contents)

        self.show_apps_view()

    def get_main_area(self):
        return self._contents

    def get_last_page(self):
        return self._last_page

    def set_last_page(self, last_page_num):
        self._last_page = last_page_num

    def show_apps_view(self, button=None, event=None):
        self._top_bar.disable_prev()
        self._apps = apps = Apps(get_applications(), self)
        self.get_main_area().set_contents(apps)

    def refresh(self, category=None):
        for app in get_applications():
            if self._apps.has_app(app):
                self._apps.update_app(app)
            else:
                self._apps.add_app(app)

    def _app_loaded(self, widget):
        if self._install is not None:
            self._install_apps()
        elif self._tutorial:
            self._show_icon_tutorial()

    def _show_icon_tutorial(self):
        try:
            from kano_profile.apps import save_app_state_variable, load_app_state_variable

            if load_app_state_variable('kano-apps', 'icon-tutorial-shown'):
                return
            else:
                save_app_state_variable('kano-apps', 'icon-tutorial-shown',
                                        True)
        except ImportError:
            # ignore problems importing kano_profile, as we don't want it to
            # be a dependency
            pass

        kdialog = KanoDialog(
            _("Add more apps to the desktop"),
            _("Click the '+' button to the right of the app name to "
              "make it appear on the desktop. You can remove it again "
              "by clicking on 'x'."),
            {_("OK, GOT IT"): {
                 "return_value": 0,
                 "color": "green"
             }},
            parent_window=self)
        kdialog.set_action_background("grey")
        kdialog.title.description.set_max_width_chars(40)
        kdialog.run()

    def _install_apps(self):
        pw = None
        for app in self._install:
            inst = AppInstaller(app, self._apps, pw, self)
            inst.set_check_if_installed(True)
            inst.set_icon_only(self._icon_only)
            inst.install()
            pw = inst.get_sudo_pw()

        self.set_last_page(0)
        refresh_package_list()
        self.refresh()
Beispiel #10
0
    class MainWindow(base_class):
        state = 0
        last_level_visited = 0
        width = 680
        height = 405
        CSS_PATH = os.path.join(common.css_dir, 'style.css')

        def __init__(self, screen_number=None, screen_name=None,
                     socket_id=0, onescreen=False):
            # Check for internet, if screen is 12 means no internet
            if screen_number == 12 or screen_name == 'no-internet':
                common.has_internet = False
            else:
                common.has_internet = is_internet()

            # Set combobox styling to the screen
            # Is done here so we don't attach the styling multiple times when
            # switching screens
            apply_styling_to_screen(self.CSS_PATH)
            apply_common_to_screen()
            KanoComboBox.apply_styling_to_screen()
            ScrolledWindow.apply_styling_to_screen(wide=True)

            # Set window
            base_class.__init__(self, _("Settings"), self.width,
                                self.height, socket_id)

            self.set_decorated(True)
            self.top_bar = TopBar(_("Settings"))
            self.top_bar.set_close_callback(self.close_window)
            self.prev_handler = None
            self.set_icon_name('kano-settings')

            if self._base_name == "Window":
                self.set_titlebar(self.top_bar)

            self._onescreen = onescreen

            self.connect('delete-event', Gtk.main_quit)
            # In case we are called from kano-world-launcher, terminate splash
            os.system('kano-stop-splash')
            # Init to Home Screen
            HomeScreen(self, screen_number=screen_number, screen_name=screen_name)

        def clear_win(self):
            self.remove_main_widget()

        def go_to_home(self, widget=None, event=None):
            self.clear_win()

            if self._onescreen:
                sys.exit(0)

            HomeScreen(self)

        def change_prev_callback(self, callback):
            # first time, no event attached
            self.remove_prev_callback()
            self.prev_handler = self.top_bar.prev_button.connect(
                'button-release-event', callback
            )

        def remove_prev_callback(self):
            if self.prev_handler:
                self.top_bar.prev_button.disconnect(self.prev_handler)
                self.prev_handler = None

        def _trigger_tracking_event(self):
            """ Generate a tracker event with some hardware settings.

                This will send a track_date event called 'user-settings'
                with the audio setting, parental lock level and display
                configuration.
            """

            track_data('user-settings', {
                'audio': 'hdmi' if is_HDMI() else 'analog',
                'parental-lock-level': get_setting('Parental-level'),
                'display': get_status()
            })

        # On closing window, will alert if any of the listed booleans are True
        def close_window(self, button, event):
            if common.need_reboot:
                kdialog = KanoDialog(
                    _("Reboot?"),
                    _("Your Kano needs to reboot for changes to apply"),
                    [
                        {
                            'label': _("LATER"),
                            'color': 'grey',
                            'return_value': False
                        },
                        {
                            'label': _("REBOOT NOW"),
                            'color': 'orange',
                            'return_value': True
                        }
                    ],
                    parent_window=self.get_toplevel()
                )

                kdialog.set_action_background('grey')
                do_reboot_now = kdialog.run()
                if do_reboot_now:
                    os.system("sudo systemctl reboot")

            self._trigger_tracking_event()

            Gtk.main_quit()
class FeedbackWindow(MainWindow):
    CLOSE_FEEDBACK = 0
    KEEP_OPEN = 1
    LAUNCH_WIFI = 2
    WIDTH = 400

    def __init__(self, bug_report=False):
        '''
        Initialises the window, creating a report or contact window
        '''
        MainWindow.__init__(self, subject='Kano Desktop Feedback Widget')
        self.bug_report = bug_report
        if self.bug_report:
            self.report_window()
        else:
            self.contact_window()

    def contact_window(self):
        '''
        Contact Us window
        Contains text view and a Send button
        '''
        # delete the directory containing all the info we'll send, and recreate
        delete_tmp_dir()
        create_tmp_dir()

        ApplicationWindow.__init__(
            self,
            _('Contact Us'),  # noqa: F821
            self.WIDTH,
            0.35
        )

        screen = Gdk.Screen.get_default()
        specific_provider = Gtk.CssProvider()
        specific_provider.load_from_path(Media.media_dir() + 'css/style.css')
        style_context = Gtk.StyleContext()
        style_context.add_provider_for_screen(screen, specific_provider,
                                              Gtk.STYLE_PROVIDER_PRIORITY_USER)

        # Make sure this window has no icon in the task bar
        # so it plays nice with kdesk-blur
        self.set_property('skip-taskbar-hint', True)

        self._grid = Gtk.Grid()

        # Create top bar
        self._top_bar = TopBar(
            title=_("Contact Us"),  # noqa: F821
            window_width=self.WIDTH,
            has_buttons=False
        )
        self._top_bar.set_close_callback(Gtk.main_quit)
        self.set_decorated(True)
        self.set_titlebar(self._top_bar)

        # Create Text view
        self._text = Gtk.TextView()
        self._text.set_editable(True)
        self._text.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
        self._text.set_size_request(self.WIDTH, -1)

        self._textbuffer = self._text.get_buffer()
        self._textbuffer.set_text(
            _("Type your feedback here!")  # noqa: F821
        )
        self._clear_buffer_handler_id = self._textbuffer.connect(
            "insert-text", self.clear_buffer
        )

        scrolledwindow = ScrolledWindow()
        scrolledwindow.set_vexpand(True)
        scrolledwindow.set_policy(Gtk.PolicyType.NEVER,
                                  Gtk.PolicyType.AUTOMATIC)
        scrolledwindow.apply_styling_to_widget()
        scrolledwindow.add(self._text)
        scrolledwindow.set_margin_left(2)
        scrolledwindow.set_margin_right(2)
        scrolledwindow.set_margin_top(2)
        scrolledwindow.set_margin_bottom(2)

        # Very hacky way to get a border: create a grey event box
        # which is a little bigger than the widget below
        border = Gtk.EventBox()
        border.get_style_context().add_class("grey")
        border.add(scrolledwindow)
        self._grid.attach(border, 0, 0, 1, 1)
        border.set_margin_left(20)
        border.set_margin_right(20)
        border.set_margin_top(10)
        border.set_margin_bottom(20)

        # Create send button
        self._send_button = KanoButton(
            _("SEND")  # noqa: F821
        )
        self._send_button.set_sensitive(False)
        self._send_button.connect("button_press_event", self.send_feedback)
        self._send_button.pack_and_align()
        self._send_button.align.set_padding(10, 10, 0, 0)

        bottom_background = Gtk.EventBox()
        bottom_background.get_style_context().add_class("grey")
        bottom_background.add(self._send_button.align)

        self._grid.attach(bottom_background, 0, 1, 1, 1)

        self._grid.set_row_spacing(0)
        self.set_main_widget(self._grid)

        # kano-profile stat collection
        try:
            from kano_profile.badges import \
                increment_app_state_variable_with_dialog
            increment_app_state_variable_with_dialog(
                'kano-feedback', 'starts', 1
            )
        except Exception:
            pass

    def report_window(self):
        '''
        Report window
        Contains 2 text views and Take Screenshot, Add Image and Send buttons
        '''
        ApplicationWindow.__init__(
            self,
            _('Report a Problem'),  # noqa: F821
            self.WIDTH,
            0.35
        )

        screen = Gdk.Screen.get_default()
        specific_provider = Gtk.CssProvider()
        specific_provider.load_from_path(Media.media_dir() + 'css/style.css')
        style_context = Gtk.StyleContext()
        style_context.add_provider_for_screen(screen, specific_provider,
                                              Gtk.STYLE_PROVIDER_PRIORITY_USER)

        self.set_icon_name("feedback")
        self._grid = Gtk.Grid()

        # Create top bar
        self._top_bar = TopBar(
            title=_("Report a Problem"),  # noqa: F821
            window_width=self.WIDTH,
            has_buttons=False
        )
        self._top_bar.set_close_callback(Gtk.main_quit)
        self.set_decorated(True)
        self.set_titlebar(self._top_bar)

        self.entry = Gtk.Entry()
        self.entry.props.placeholder_text = \
            _("Add subject (optional)")  # noqa: F821
        self.entry.set_margin_left(20)
        self.entry.set_margin_right(20)
        self.entry.set_margin_top(20)
        self.entry.set_margin_bottom(10)
        self._grid.attach(self.entry, 0, 0, 1, 1)

        # Create Text view
        self._text = Gtk.TextView()
        self._text.set_editable(True)
        self._text.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
        self._text.set_size_request(self.WIDTH, -1)

        self._textbuffer = self._text.get_buffer()
        self._textbuffer.set_text(
            _("Type your problem here!")  # noqa: F821
        )

        self._clear_buffer_handler_id = self._textbuffer.connect(
            "insert-text", self.clear_buffer
        )

        scrolledwindow = ScrolledWindow()
        scrolledwindow.set_vexpand(True)
        scrolledwindow.set_policy(
            Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC
        )
        scrolledwindow.apply_styling_to_widget()
        scrolledwindow.add(self._text)
        scrolledwindow.set_margin_left(2)
        scrolledwindow.set_margin_right(2)
        scrolledwindow.set_margin_top(2)
        scrolledwindow.set_margin_bottom(2)

        # Very hacky way to get a border: create a grey event box
        # which is a little bigger than the widget below
        border = Gtk.EventBox()
        border.get_style_context().add_class("grey")
        border.add(scrolledwindow)
        self._grid.attach(border, 0, 1, 1, 1)
        border.set_margin_left(20)
        border.set_margin_right(20)
        border.set_margin_top(10)
        border.set_margin_bottom(20)

        # Create take screenshot button
        self._screenshot_button = KanoButton(
            _("TAKE SCREENSHOT"),  # noqa: F821
            "blue"
        )
        self._screenshot_button.set_sensitive(True)
        self._screenshot_button.connect("button_press_event",
                                        self.screenshot_clicked)

        # Create attach screenshot button
        self._attach_button = KanoButton(
            _("ADD IMAGE"),  # noqa: F821
            "blue"
        )
        self._attach_button.set_sensitive(True)
        self._attach_button.connect("button_press_event", self.attach_clicked)

        # Create send button
        self._send_button = KanoButton(
            _("SEND")  # noqa: F821
        )
        self._send_button.set_sensitive(False)
        self._send_button.connect("button_press_event", self.send_feedback)
        self._send_button.pack_and_align()
        self._send_button.set_margin(10, 0, 10, 0)

        self.screenshot_box = Gtk.ButtonBox()
        self.screenshot_box.set_layout(Gtk.ButtonBoxStyle.CENTER)
        self.screenshot_box.set_spacing(20)
        self.pack_screenshot_buttons()
        self.screenshot_box.set_margin_bottom(20)

        self._grid.attach(self.screenshot_box, 0, 2, 1, 1)

        # Create grey box to put the button in
        self.bottom_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.bottom_box.pack_start(self._send_button.align, False, False, 0)

        bottom_background = Gtk.EventBox()
        bottom_background.get_style_context().add_class("grey")
        bottom_background.add(self.bottom_box)

        self._grid.attach(bottom_background, 0, 3, 1, 1)

        self._grid.set_row_spacing(0)
        self.set_main_widget(self._grid)

        # kano-profile stat collection
        try:
            from kano_profile.badges import increment_app_state_variable_with_dialog
            increment_app_state_variable_with_dialog(
                'kano-feedback', 'starts', 1
            )
        except Exception:
            pass

    def screenshot_clicked(self, button=None, event=None):
        '''
        Takes a screenshot while minimising the window
        '''
        # minimise the window
        self.iconify()
        take_screenshot()
        self.include_screenshot()
        # restore the window
        self.deiconify()

    def attach_clicked(self, button=None, event=None):
        '''
        Opens the File Chooser Dialog.
        If image selected then copy it to the feedback folder
        '''
        screenshot = None
        # Open file manager
        dialog = Gtk.FileChooserDialog(
            _("Please choose a file"),  # noqa: F821
            self,
            Gtk.FileChooserAction.OPEN,
            (Gtk.STOCK_CANCEL,
            Gtk.ResponseType.CANCEL,
            Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
        )

        self.add_filters(dialog)

        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            screenshot = dialog.get_filename()

        dialog.destroy()
        # Copy image file into feedback folder
        if screenshot is not None:
            copy_screenshot(screenshot)
            self.include_screenshot()

    def add_filters(self, dialog):
        '''
        Add image type filters
        Used for the File Chooser Dialog
        '''
        # Image filter
        filter_images = Gtk.FileFilter()
        filter_images.set_name("Images")
        filter_images.add_mime_type("image/png")
        filter_images.add_mime_type("image/jpeg")
        filter_images.add_mime_type("image/gif")
        filter_images.add_pattern("*.png")
        filter_images.add_pattern("*.jpg")
        filter_images.add_pattern("*.gif")
        filter_images.add_pattern("*.tif")
        filter_images.add_pattern("*.xpm")
        dialog.add_filter(filter_images)

        # Any file filter
        filter_any = Gtk.FileFilter()
        filter_any.set_name("Any files")
        filter_any.add_pattern("*")
        dialog.add_filter(filter_any)

    def include_screenshot(self):
        '''
        This is the box containing the filename of the screenshot,
        and the option to display it or remove it
        '''
        if not hasattr(self, "screenshot"):

            # We pack the buttons into an event box with styling
            # so that we can have a container with a border radius
            # without having ugly gaps between the buttons.
            self.screenshot = Gtk.EventBox()
            self.screenshot.get_style_context().add_class("kano_button")
            self.screenshot.get_style_context().add_class("blue_background")

            remove_screenshot = Gtk.Button()
            attach_cursor_events(remove_screenshot)
            remove_icon = Gtk.Image.new_from_file("/usr/share/kano-feedback/media/icons/close.png")
            remove_screenshot.add(remove_icon)
            remove_screenshot.connect("button-release-event",
                                      self.remove_screenshot)
            remove_screenshot.get_style_context().add_class("blue_background")

            show_screenshot = Gtk.Button()
            attach_cursor_events(show_screenshot)
            show_icon = Gtk.Image()
            show_icon.set_from_file("/usr/share/kano-feedback/media/icons/preview.png")
            show_screenshot.add(show_icon)
            show_screenshot.connect("button-release-event", self.show_screenshot)
            show_screenshot.get_style_context().add_class("blue_background")

            label = Gtk.Label(SCREENSHOT_NAME.upper())
            label.set_padding(10, 0)
            box = Gtk.Box()
            box.pack_start(label, False, False, 0)
            box.pack_end(remove_screenshot, False, False, 0)
            box.pack_end(show_screenshot, False, False, 0)

            self.screenshot.add(box)

        self.screenshot_box.remove(self._screenshot_button)
        self.screenshot_box.remove(self._attach_button)
        self.screenshot_box.pack_start(self.screenshot, False, False, 0)

    def remove_screenshot(self, widget, event):
        '''
        Remove screenshot button action
        '''
        delete_screenshot()
        self.screenshot_box.remove(self.screenshot)
        self.pack_screenshot_buttons()
        self.show_all()

    def show_screenshot(self, widget, event):
        '''
        Creates and displays a dialog with the screenshot image
        '''
        height = Gdk.Screen().get_default().get_height()
        width = Gdk.Screen().get_default().get_width()
        pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(SCREENSHOT_PATH,
                                                        width * 0.5,
                                                        height * 0.5)
        image = Gtk.Image.new_from_pixbuf(pixbuf)

        dialog = KanoDialog(
            _("Screenshot"),  # noqa: F821
            widget=image
        )
        dialog.run()

    def pack_screenshot_buttons(self):
        '''
        Pack the screenshot buttons into a box
        '''
        self.screenshot_box.pack_start(self._screenshot_button,
                                       False, False, 0)
        self.screenshot_box.set_child_non_homogeneous(self._screenshot_button,
                                                      True)
        self.screenshot_box.pack_start(self._attach_button, False, False, 0)
        self.screenshot_box.set_child_non_homogeneous(self._attach_button,
                                                      True)
    class KanoWifiGui(base_class):

        CSS_PATH = os.path.join(css_dir, 'kano_wifi_gui.css')
        width = 350
        height = 450

        def __init__(self, wiface='wlan0', socket_id=0, no_confirm_ether=False):

            self.wiface = wiface
            self.network_list = []
            self.no_confirm_ether = no_confirm_ether

            # Default basic styling
            apply_common_to_screen()

            # Attach specific styling
            apply_styling_to_screen(self.CSS_PATH)

            # Set window
            base_class.__init__(
                self,
                _("Kano WiFi"),
                self.width,
                self.height,
                socket_id
            )

            self.top_bar = TopBar(_("Kano WiFi"))
            self.top_bar.set_prev_callback(self.refresh_networks)
            self.top_bar.set_close_callback(Gtk.main_quit)
            self.prev_handler = None
            self.connect('delete-event', Gtk.main_quit)
            self.set_keep_above(True)
            self.set_icon_name('kano-settings')
            self.set_decorated(True)

            if self._base_name == "Window":
                self.set_titlebar(self.top_bar)

            self._launch_application()

        def _launch_application(self, widget=None):
            # Decide whether application prompts user to plug in WiFi dongle
            # or tell them they have ethernet.
            # Don't want to call this function more than once
            self.wiface = get_wlan_device()

            has_internet = is_internet()
            ethernet_plugged = is_ethernet_plugged()
            dongle_is_plugged_in = is_device(self.wiface)

            if has_internet and ethernet_plugged and self.no_confirm_ether:
                sys.exit(0)

            # For testing
            # dongle_is_plugged_in = False
            # ethernet_plugged = True
            # has_internet = False

            if has_internet and ethernet_plugged:
                self._you_are_connected_via_ethernet()

            elif dongle_is_plugged_in:
                if has_internet:
                    self._you_have_internet_screen(self.wiface)
                else:
                    # Refresh the networks list
                    self.refresh_networks()

            else:
                self._plug_in_wifi_dongle()

        def refresh_networks(self, widget=None, event=None):
            RefreshNetworks(self)

        def _plug_in_wifi_dongle(self):
            self.remove_main_widget()
            title = _("You don't seem to have a WiFi dongle\nplugged in.")
            description = _("Plug one in and try again")
            buttons = [
                {
                    'label': ""
                },
                {
                    'label': _("TRY AGAIN"),
                    'color': 'green',
                    'callback': self._launch_application,
                    'type': 'KanoButton',
                    'focus': True
                },
                {
                    'label': _("Skip"),
                    'callback': Gtk.main_quit,
                    'type': 'OrangeButton'
                }
            ]

            img_path = os.path.join(img_dir, "dongle2.png")

            screen = Template(
                title,
                description,
                buttons,
                self.is_plug(),
                img_path
            )
            self.set_main_widget(screen)
            screen.button_grab_focus()
            screen.show_all()

        def _you_are_connected_via_ethernet(self):
            self.remove_main_widget()
            title = _("You are already connected via ethernet.")
            description = _("Do you still want to connect with WiFi?")

            # Decide which callback to use depending on if wifi dongle is
            # plugged in
            buttons = [
                {
                    'label': _("NO"),
                    'color': 'red',
                    'callback': Gtk.main_quit,
                    'type': 'KanoButton'
                },
                {
                    'label': _("YES"),
                    'color': 'green',
                    'callback': self._ethernet_next_step,
                    'type': 'KanoButton',
                    'focus': True
                }
            ]

            img_path = os.path.join(img_dir, "ethernet-2.png")

            screen = Template(
                title,
                description,
                buttons,
                self.is_plug(),
                img_path
            )
            self.set_main_widget(screen)
            screen.button_grab_focus()
            screen.show_all()

        def _ethernet_next_step(self, widget=None):
            dongle_is_plugged_in = is_device(self.wiface)
            if dongle_is_plugged_in:
                self.refresh_networks()
            else:
                self._plug_in_wifi_dongle()

        def _you_have_internet_screen(self, wiface):
            self.remove_main_widget()
            title = _("You already have internet!")
            description = _("Do you want to change network?")
            buttons = [
                {
                    'label': _("NO"),
                    'color': 'red',
                    'callback': Gtk.main_quit,
                    'type': 'KanoButton'
                },
                {
                    'label': _("YES"),
                    'color': 'green',
                    'callback': self.refresh_networks,
                    'type': 'KanoButton',
                    'focus': True
                }
            ]
            img_path = os.path.join(img_dir, "internet.png")

            screen = Template(
                title,
                description,
                buttons,
                self.is_plug(),
                img_path
            )
            self.set_main_widget(screen)
            screen.button_grab_focus()
            screen.show_all()

        def _decide(self):
            if is_device(self.wiface):
                self.refresh_networks()
            else:
                self._plug_in_wifi_dongle()
Beispiel #13
0
    class KanoWifiGui(base_class):

        CSS_PATH = os.path.join(css_dir, 'kano_wifi_gui.css')
        width = 350
        height = 450

        def __init__(self, wiface='wlan0', socket_id=0, no_confirm_ether=False):

            self.wiface = wiface
            self.network_list = []
            self.no_confirm_ether = no_confirm_ether

            # Default basic styling
            apply_common_to_screen()

            # Attach specific styling
            apply_styling_to_screen(self.CSS_PATH)

            # Set window
            base_class.__init__(
                self,
                _("Kano WiFi"),
                self.width,
                self.height,
                socket_id
            )

            self.top_bar = TopBar(_("Kano WiFi"))
            self.top_bar.set_prev_callback(self.refresh_networks)
            self.top_bar.set_close_callback(Gtk.main_quit)
            self.prev_handler = None
            self.connect('delete-event', Gtk.main_quit)
            self.set_keep_above(True)
            self.set_icon_name('kano-settings')
            self.set_decorated(True)

            if self._base_name == "Window":
                self.set_titlebar(self.top_bar)

            self._launch_application()

        def _launch_application(self, widget=None):
            # Decide whether application prompts user to plug in WiFi dongle
            # or tell them they have ethernet.
            # Don't want to call this function more than once
            self.wiface = get_wlan_device()

            has_internet = is_internet()
            ethernet_plugged = is_ethernet_plugged()
            dongle_is_plugged_in = is_device(self.wiface)

            if has_internet and ethernet_plugged and self.no_confirm_ether:
                sys.exit(0)

            # For testing
            # dongle_is_plugged_in = False
            # ethernet_plugged = True
            # has_internet = False

            if has_internet and ethernet_plugged:
                self._you_are_connected_via_ethernet()

            elif dongle_is_plugged_in:
                if has_internet:
                    self._you_have_internet_screen(self.wiface)
                else:
                    # Refresh the networks list
                    self.refresh_networks()

            else:
                self._plug_in_wifi_dongle()

        def refresh_networks(self, widget=None, event=None):
            RefreshNetworks(self)

        def _plug_in_wifi_dongle(self):
            self.remove_main_widget()
            title = _("You don't seem to have a WiFi dongle\nplugged in.")
            description = _("Plug one in and try again")
            buttons = [
                {
                    'label': ""
                },
                {
                    'label': _("TRY AGAIN"),
                    'color': 'green',
                    'callback': self._launch_application,
                    'type': 'KanoButton',
                    'focus': True
                },
                {
                    'label': _("Skip"),
                    'callback': Gtk.main_quit,
                    'type': 'OrangeButton'
                }
            ]

            img_path = os.path.join(img_dir, "dongle2.png")

            screen = Template(
                title,
                description,
                buttons,
                self.is_plug(),
                img_path
            )
            self.set_main_widget(screen)
            screen.button_grab_focus()
            screen.show_all()

        def _you_are_connected_via_ethernet(self):
            self.remove_main_widget()
            title = _("You are already connected via ethernet.")
            description = _("Do you still want to connect with WiFi?")

            # Decide which callback to use depending on if wifi dongle is
            # plugged in
            buttons = [
                {
                    'label': _("NO"),
                    'color': 'red',
                    'callback': Gtk.main_quit,
                    'type': 'KanoButton'
                },
                {
                    'label': _("YES"),
                    'color': 'green',
                    'callback': self._ethernet_next_step,
                    'type': 'KanoButton',
                    'focus': True
                }
            ]

            img_path = os.path.join(img_dir, "ethernet-2.png")

            screen = Template(
                title,
                description,
                buttons,
                self.is_plug(),
                img_path
            )
            self.set_main_widget(screen)
            screen.button_grab_focus()
            screen.show_all()

        def _ethernet_next_step(self, widget=None):
            dongle_is_plugged_in = is_device(self.wiface)
            if dongle_is_plugged_in:
                self.refresh_networks()
            else:
                self._plug_in_wifi_dongle()

        def _you_have_internet_screen(self, wiface):
            self.remove_main_widget()
            title = _("You already have internet!")
            description = _("Do you want to change network?")
            buttons = [
                {
                    'label': _("NO"),
                    'color': 'red',
                    'callback': Gtk.main_quit,
                    'type': 'KanoButton'
                },
                {
                    'label': _("YES"),
                    'color': 'green',
                    'callback': self.refresh_networks,
                    'type': 'KanoButton',
                    'focus': True
                }
            ]
            img_path = os.path.join(img_dir, "internet.png")

            screen = Template(
                title,
                description,
                buttons,
                self.is_plug(),
                img_path
            )
            self.set_main_widget(screen)
            screen.button_grab_focus()
            screen.show_all()

        def _decide(self):
            if is_device(self.wiface):
                self.refresh_networks()
            else:
                self._plug_in_wifi_dongle()
    def contact_window(self):
        '''
        Contact Us window
        Contains text view and a Send button
        '''
        # delete the directory containing all the info we'll send, and recreate
        delete_tmp_dir()
        create_tmp_dir()

        ApplicationWindow.__init__(
            self,
            _('Contact Us'),  # noqa: F821
            self.WIDTH,
            0.35)

        screen = Gdk.Screen.get_default()
        specific_provider = Gtk.CssProvider()
        specific_provider.load_from_path(Media.media_dir() + 'css/style.css')
        style_context = Gtk.StyleContext()
        style_context.add_provider_for_screen(screen, specific_provider,
                                              Gtk.STYLE_PROVIDER_PRIORITY_USER)

        # Make sure this window has no icon in the task bar
        # so it plays nice with kdesk-blur
        self.set_property('skip-taskbar-hint', True)

        self._grid = Gtk.Grid()

        # Create top bar
        self._top_bar = TopBar(
            title=_("Contact Us"),  # noqa: F821
            window_width=self.WIDTH,
            has_buttons=False)
        self._top_bar.set_close_callback(Gtk.main_quit)
        self.set_decorated(True)
        self.set_titlebar(self._top_bar)

        # Create Text view
        self._text = Gtk.TextView()
        self._text.set_editable(True)
        self._text.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
        self._text.set_size_request(self.WIDTH, -1)
        self._text.set_sensitive(self._pii_allowed())

        self._textbuffer = self._text.get_buffer()
        if self._pii_allowed():
            self._textbuffer.set_text(
                _("Type your feedback here!"))  # noqa: F821
        else:
            self._textbuffer.set_text(
                _("Want to send us more information? Ask your parent to check their email."
                  ))  # noqa: F821
        self._clear_buffer_handler_id = self._textbuffer.connect(
            "insert-text", self.clear_buffer)

        scrolledwindow = ScrolledWindow()
        scrolledwindow.set_vexpand(True)
        scrolledwindow.set_policy(Gtk.PolicyType.NEVER,
                                  Gtk.PolicyType.AUTOMATIC)
        scrolledwindow.apply_styling_to_widget()
        scrolledwindow.add(self._text)
        scrolledwindow.set_margin_left(2)
        scrolledwindow.set_margin_right(2)
        scrolledwindow.set_margin_top(2)
        scrolledwindow.set_margin_bottom(2)

        # Very hacky way to get a border: create a grey event box
        # which is a little bigger than the widget below
        border = Gtk.EventBox()
        border.get_style_context().add_class("grey")
        border.add(scrolledwindow)
        self._grid.attach(border, 0, 0, 1, 1)
        border.set_margin_left(20)
        border.set_margin_right(20)
        border.set_margin_top(10)
        border.set_margin_bottom(20)

        # Create send button
        self._send_button = KanoButton(_("SEND")  # noqa: F821
                                       )
        self._send_button.set_sensitive(not self._pii_allowed())
        self._send_button.connect("button_press_event", self.send_feedback)
        self._send_button.pack_and_align()
        self._send_button.align.set_padding(10, 10, 0, 0)

        bottom_background = Gtk.EventBox()
        bottom_background.get_style_context().add_class("grey")
        bottom_background.add(self._send_button.align)

        self._grid.attach(bottom_background, 0, 1, 1, 1)

        self._grid.set_row_spacing(0)
        self.set_main_widget(self._grid)

        # kano-profile stat collection
        try:
            from kano_profile.badges import \
                increment_app_state_variable_with_dialog
            increment_app_state_variable_with_dialog('kano-feedback', 'starts',
                                                     1)
        except Exception:
            pass
class FeedbackWindow(MainWindow):
    CLOSE_FEEDBACK = 0
    KEEP_OPEN = 1
    LAUNCH_WIFI = 2
    WIDTH = 400

    def __init__(self, bug_report=False):
        '''
        Initialises the window, creating a report or contact window
        '''
        MainWindow.__init__(self, subject='Kano Desktop Feedback Widget')
        self.kano_world_client = KanoWorld()
        self.bug_report = bug_report
        if self.bug_report:
            self.report_window()
        else:
            self.contact_window()

    def contact_window(self):
        '''
        Contact Us window
        Contains text view and a Send button
        '''
        # delete the directory containing all the info we'll send, and recreate
        delete_tmp_dir()
        create_tmp_dir()

        ApplicationWindow.__init__(
            self,
            _('Contact Us'),  # noqa: F821
            self.WIDTH,
            0.35)

        screen = Gdk.Screen.get_default()
        specific_provider = Gtk.CssProvider()
        specific_provider.load_from_path(Media.media_dir() + 'css/style.css')
        style_context = Gtk.StyleContext()
        style_context.add_provider_for_screen(screen, specific_provider,
                                              Gtk.STYLE_PROVIDER_PRIORITY_USER)

        # Make sure this window has no icon in the task bar
        # so it plays nice with kdesk-blur
        self.set_property('skip-taskbar-hint', True)

        self._grid = Gtk.Grid()

        # Create top bar
        self._top_bar = TopBar(
            title=_("Contact Us"),  # noqa: F821
            window_width=self.WIDTH,
            has_buttons=False)
        self._top_bar.set_close_callback(Gtk.main_quit)
        self.set_decorated(True)
        self.set_titlebar(self._top_bar)

        # Create Text view
        self._text = Gtk.TextView()
        self._text.set_editable(True)
        self._text.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
        self._text.set_size_request(self.WIDTH, -1)
        self._text.set_sensitive(self._pii_allowed())

        self._textbuffer = self._text.get_buffer()
        if self._pii_allowed():
            self._textbuffer.set_text(
                _("Type your feedback here!"))  # noqa: F821
        else:
            self._textbuffer.set_text(
                _("Want to send us more information? Ask your parent to check their email."
                  ))  # noqa: F821
        self._clear_buffer_handler_id = self._textbuffer.connect(
            "insert-text", self.clear_buffer)

        scrolledwindow = ScrolledWindow()
        scrolledwindow.set_vexpand(True)
        scrolledwindow.set_policy(Gtk.PolicyType.NEVER,
                                  Gtk.PolicyType.AUTOMATIC)
        scrolledwindow.apply_styling_to_widget()
        scrolledwindow.add(self._text)
        scrolledwindow.set_margin_left(2)
        scrolledwindow.set_margin_right(2)
        scrolledwindow.set_margin_top(2)
        scrolledwindow.set_margin_bottom(2)

        # Very hacky way to get a border: create a grey event box
        # which is a little bigger than the widget below
        border = Gtk.EventBox()
        border.get_style_context().add_class("grey")
        border.add(scrolledwindow)
        self._grid.attach(border, 0, 0, 1, 1)
        border.set_margin_left(20)
        border.set_margin_right(20)
        border.set_margin_top(10)
        border.set_margin_bottom(20)

        # Create send button
        self._send_button = KanoButton(_("SEND")  # noqa: F821
                                       )
        self._send_button.set_sensitive(not self._pii_allowed())
        self._send_button.connect("button_press_event", self.send_feedback)
        self._send_button.pack_and_align()
        self._send_button.align.set_padding(10, 10, 0, 0)

        bottom_background = Gtk.EventBox()
        bottom_background.get_style_context().add_class("grey")
        bottom_background.add(self._send_button.align)

        self._grid.attach(bottom_background, 0, 1, 1, 1)

        self._grid.set_row_spacing(0)
        self.set_main_widget(self._grid)

        # kano-profile stat collection
        try:
            from kano_profile.badges import \
                increment_app_state_variable_with_dialog
            increment_app_state_variable_with_dialog('kano-feedback', 'starts',
                                                     1)
        except Exception:
            pass

    def report_window(self):
        '''
        Report window
        Contains 2 text views and Take Screenshot, Add Image and Send buttons
        '''
        ApplicationWindow.__init__(
            self,
            _('Report a Problem'),  # noqa: F821
            self.WIDTH,
            0.35)

        screen = Gdk.Screen.get_default()
        specific_provider = Gtk.CssProvider()
        specific_provider.load_from_path(Media.media_dir() + 'css/style.css')
        style_context = Gtk.StyleContext()
        style_context.add_provider_for_screen(screen, specific_provider,
                                              Gtk.STYLE_PROVIDER_PRIORITY_USER)

        self.set_icon_name("feedback")
        self._grid = Gtk.Grid()

        # Create top bar
        self._top_bar = TopBar(
            title=_("Report a Problem"),  # noqa: F821
            window_width=self.WIDTH,
            has_buttons=False)
        self._top_bar.set_close_callback(Gtk.main_quit)
        self.set_decorated(True)
        self.set_titlebar(self._top_bar)

        self.entry = Gtk.Entry()
        if self._pii_allowed():
            self.entry.props.placeholder_text = _(
                "Add subject (optional)")  # noqa: F821
        else:
            self.entry.props.placeholder_text = _(
                "Want to send us more information?")  # noqa: F821
        self.entry.set_margin_left(20)
        self.entry.set_margin_right(20)
        self.entry.set_margin_top(20)
        self.entry.set_margin_bottom(10)
        self.entry.set_sensitive(self._pii_allowed())
        self._grid.attach(self.entry, 0, 0, 1, 1)

        # Create Text view
        self._text = Gtk.TextView()
        self._text.set_editable(True)
        self._text.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
        self._text.set_size_request(self.WIDTH, -1)
        self._text.set_sensitive(self._pii_allowed())

        self._textbuffer = self._text.get_buffer()
        if self._pii_allowed():
            self._textbuffer.set_text(
                _("Type your problem here!"))  # noqa: F821
        else:
            self._textbuffer.set_text(
                _("Ask your parent to check their email."))  # noqa: F821

        self._clear_buffer_handler_id = self._textbuffer.connect(
            "insert-text", self.clear_buffer)

        scrolledwindow = ScrolledWindow()
        scrolledwindow.set_vexpand(True)
        scrolledwindow.set_policy(Gtk.PolicyType.NEVER,
                                  Gtk.PolicyType.AUTOMATIC)
        scrolledwindow.apply_styling_to_widget()
        scrolledwindow.add(self._text)
        scrolledwindow.set_margin_left(2)
        scrolledwindow.set_margin_right(2)
        scrolledwindow.set_margin_top(2)
        scrolledwindow.set_margin_bottom(2)

        # Very hacky way to get a border: create a grey event box
        # which is a little bigger than the widget below
        border = Gtk.EventBox()
        border.get_style_context().add_class("grey")
        border.add(scrolledwindow)
        self._grid.attach(border, 0, 1, 1, 1)
        border.set_margin_left(20)
        border.set_margin_right(20)
        border.set_margin_top(10)
        border.set_margin_bottom(20)

        # Create take screenshot button
        self._screenshot_button = KanoButton(
            _("TAKE SCREENSHOT"),  # noqa: F821
            "blue")
        self._screenshot_button.set_sensitive(self._pii_allowed())
        self._screenshot_button.connect("button_press_event",
                                        self.screenshot_clicked)

        # Create attach screenshot button
        self._attach_button = KanoButton(
            _("ADD IMAGE"),  # noqa: F821
            "blue")
        self._attach_button.set_sensitive(self._pii_allowed())
        self._attach_button.connect("button_press_event", self.attach_clicked)

        # Create send button
        self._send_button = KanoButton(_("SEND")  # noqa: F821
                                       )
        self._send_button.set_sensitive(not self._pii_allowed())
        self._send_button.connect("button_press_event", self.send_feedback)
        self._send_button.pack_and_align()
        self._send_button.set_margin(10, 0, 10, 0)

        self.screenshot_box = Gtk.ButtonBox()
        self.screenshot_box.set_layout(Gtk.ButtonBoxStyle.CENTER)
        self.screenshot_box.set_spacing(20)
        self.pack_screenshot_buttons()
        self.screenshot_box.set_margin_bottom(20)

        self._grid.attach(self.screenshot_box, 0, 2, 1, 1)

        # Create grey box to put the button in
        self.bottom_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.bottom_box.pack_start(self._send_button.align, False, False, 0)

        bottom_background = Gtk.EventBox()
        bottom_background.get_style_context().add_class("grey")
        bottom_background.add(self.bottom_box)

        self._grid.attach(bottom_background, 0, 3, 1, 1)

        self._grid.set_row_spacing(0)
        self.set_main_widget(self._grid)

        # kano-profile stat collection
        try:
            from kano_profile.badges import increment_app_state_variable_with_dialog
            increment_app_state_variable_with_dialog('kano-feedback', 'starts',
                                                     1)
        except Exception:
            pass

    def screenshot_clicked(self, button=None, event=None):
        '''
        Takes a screenshot while minimising the window
        '''
        # minimise the window
        self.iconify()
        take_screenshot()
        self.include_screenshot()
        # restore the window
        self.deiconify()

    def attach_clicked(self, button=None, event=None):
        '''
        Opens the File Chooser Dialog.
        If image selected then copy it to the feedback folder
        '''
        screenshot = None
        # Open file manager
        dialog = Gtk.FileChooserDialog(
            _("Please choose a file"),  # noqa: F821
            self,
            Gtk.FileChooserAction.OPEN,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN,
             Gtk.ResponseType.OK))

        self.add_filters(dialog)

        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            screenshot = dialog.get_filename()

        dialog.destroy()
        # Copy image file into feedback folder
        if screenshot is not None:
            copy_screenshot(screenshot)
            self.include_screenshot()

    def add_filters(self, dialog):
        '''
        Add image type filters
        Used for the File Chooser Dialog
        '''
        # Image filter
        filter_images = Gtk.FileFilter()
        filter_images.set_name("Images")
        filter_images.add_mime_type("image/png")
        filter_images.add_mime_type("image/jpeg")
        filter_images.add_mime_type("image/gif")
        filter_images.add_pattern("*.png")
        filter_images.add_pattern("*.jpg")
        filter_images.add_pattern("*.gif")
        filter_images.add_pattern("*.tif")
        filter_images.add_pattern("*.xpm")
        dialog.add_filter(filter_images)

        # Any file filter
        filter_any = Gtk.FileFilter()
        filter_any.set_name("Any files")
        filter_any.add_pattern("*")
        dialog.add_filter(filter_any)

    def include_screenshot(self):
        '''
        This is the box containing the filename of the screenshot,
        and the option to display it or remove it
        '''
        if not hasattr(self, "screenshot"):

            # We pack the buttons into an event box with styling
            # so that we can have a container with a border radius
            # without having ugly gaps between the buttons.
            self.screenshot = Gtk.EventBox()
            self.screenshot.get_style_context().add_class("kano_button")
            self.screenshot.get_style_context().add_class("blue_background")

            remove_screenshot = Gtk.Button()
            attach_cursor_events(remove_screenshot)
            remove_icon = Gtk.Image.new_from_file(
                "/usr/share/kano-feedback/media/icons/close.png")
            remove_screenshot.add(remove_icon)
            remove_screenshot.connect("button-release-event",
                                      self.remove_screenshot)
            remove_screenshot.get_style_context().add_class("blue_background")

            show_screenshot = Gtk.Button()
            attach_cursor_events(show_screenshot)
            show_icon = Gtk.Image()
            show_icon.set_from_file(
                "/usr/share/kano-feedback/media/icons/preview.png")
            show_screenshot.add(show_icon)
            show_screenshot.connect("button-release-event",
                                    self.show_screenshot)
            show_screenshot.get_style_context().add_class("blue_background")

            label = Gtk.Label(SCREENSHOT_NAME.upper())
            label.set_padding(10, 0)
            box = Gtk.Box()
            box.pack_start(label, False, False, 0)
            box.pack_end(remove_screenshot, False, False, 0)
            box.pack_end(show_screenshot, False, False, 0)

            self.screenshot.add(box)

        self.screenshot_box.remove(self._screenshot_button)
        self.screenshot_box.remove(self._attach_button)
        self.screenshot_box.pack_start(self.screenshot, False, False, 0)

    def remove_screenshot(self, widget, event):
        '''
        Remove screenshot button action
        '''
        delete_screenshot()
        self.screenshot_box.remove(self.screenshot)
        self.pack_screenshot_buttons()
        self.show_all()

    def show_screenshot(self, widget, event):
        '''
        Creates and displays a dialog with the screenshot image
        '''
        height = Gdk.Screen().get_default().get_height()
        width = Gdk.Screen().get_default().get_width()
        pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(
            SCREENSHOT_PATH, width * 0.5, height * 0.5)
        image = Gtk.Image.new_from_pixbuf(pixbuf)

        dialog = KanoDialog(
            _("Screenshot"),  # noqa: F821
            widget=image)
        dialog.run()

    def pack_screenshot_buttons(self):
        '''
        Pack the screenshot buttons into a box
        '''
        self.screenshot_box.pack_start(self._screenshot_button, False, False,
                                       0)
        self.screenshot_box.set_child_non_homogeneous(self._screenshot_button,
                                                      True)
        self.screenshot_box.pack_start(self._attach_button, False, False, 0)
        self.screenshot_box.set_child_non_homogeneous(self._attach_button,
                                                      True)

    def _pii_allowed(self):
        """
        Helper to check whether or not users are allowed to send us text,
        screenshots, images.
        """
        return (self.kano_world_client.is_logged_in()
                and self.kano_world_client.get_account_verified())
    def report_window(self):
        '''
        Report window
        Contains 2 text views and Take Screenshot, Add Image and Send buttons
        '''
        ApplicationWindow.__init__(
            self,
            _('Report a Problem'),  # noqa: F821
            self.WIDTH,
            0.35)

        screen = Gdk.Screen.get_default()
        specific_provider = Gtk.CssProvider()
        specific_provider.load_from_path(Media.media_dir() + 'css/style.css')
        style_context = Gtk.StyleContext()
        style_context.add_provider_for_screen(screen, specific_provider,
                                              Gtk.STYLE_PROVIDER_PRIORITY_USER)

        self.set_icon_name("feedback")
        self._grid = Gtk.Grid()

        # Create top bar
        self._top_bar = TopBar(
            title=_("Report a Problem"),  # noqa: F821
            window_width=self.WIDTH,
            has_buttons=False)
        self._top_bar.set_close_callback(Gtk.main_quit)
        self.set_decorated(True)
        self.set_titlebar(self._top_bar)

        self.entry = Gtk.Entry()
        if self._pii_allowed():
            self.entry.props.placeholder_text = _(
                "Add subject (optional)")  # noqa: F821
        else:
            self.entry.props.placeholder_text = _(
                "Want to send us more information?")  # noqa: F821
        self.entry.set_margin_left(20)
        self.entry.set_margin_right(20)
        self.entry.set_margin_top(20)
        self.entry.set_margin_bottom(10)
        self.entry.set_sensitive(self._pii_allowed())
        self._grid.attach(self.entry, 0, 0, 1, 1)

        # Create Text view
        self._text = Gtk.TextView()
        self._text.set_editable(True)
        self._text.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
        self._text.set_size_request(self.WIDTH, -1)
        self._text.set_sensitive(self._pii_allowed())

        self._textbuffer = self._text.get_buffer()
        if self._pii_allowed():
            self._textbuffer.set_text(
                _("Type your problem here!"))  # noqa: F821
        else:
            self._textbuffer.set_text(
                _("Ask your parent to check their email."))  # noqa: F821

        self._clear_buffer_handler_id = self._textbuffer.connect(
            "insert-text", self.clear_buffer)

        scrolledwindow = ScrolledWindow()
        scrolledwindow.set_vexpand(True)
        scrolledwindow.set_policy(Gtk.PolicyType.NEVER,
                                  Gtk.PolicyType.AUTOMATIC)
        scrolledwindow.apply_styling_to_widget()
        scrolledwindow.add(self._text)
        scrolledwindow.set_margin_left(2)
        scrolledwindow.set_margin_right(2)
        scrolledwindow.set_margin_top(2)
        scrolledwindow.set_margin_bottom(2)

        # Very hacky way to get a border: create a grey event box
        # which is a little bigger than the widget below
        border = Gtk.EventBox()
        border.get_style_context().add_class("grey")
        border.add(scrolledwindow)
        self._grid.attach(border, 0, 1, 1, 1)
        border.set_margin_left(20)
        border.set_margin_right(20)
        border.set_margin_top(10)
        border.set_margin_bottom(20)

        # Create take screenshot button
        self._screenshot_button = KanoButton(
            _("TAKE SCREENSHOT"),  # noqa: F821
            "blue")
        self._screenshot_button.set_sensitive(self._pii_allowed())
        self._screenshot_button.connect("button_press_event",
                                        self.screenshot_clicked)

        # Create attach screenshot button
        self._attach_button = KanoButton(
            _("ADD IMAGE"),  # noqa: F821
            "blue")
        self._attach_button.set_sensitive(self._pii_allowed())
        self._attach_button.connect("button_press_event", self.attach_clicked)

        # Create send button
        self._send_button = KanoButton(_("SEND")  # noqa: F821
                                       )
        self._send_button.set_sensitive(not self._pii_allowed())
        self._send_button.connect("button_press_event", self.send_feedback)
        self._send_button.pack_and_align()
        self._send_button.set_margin(10, 0, 10, 0)

        self.screenshot_box = Gtk.ButtonBox()
        self.screenshot_box.set_layout(Gtk.ButtonBoxStyle.CENTER)
        self.screenshot_box.set_spacing(20)
        self.pack_screenshot_buttons()
        self.screenshot_box.set_margin_bottom(20)

        self._grid.attach(self.screenshot_box, 0, 2, 1, 1)

        # Create grey box to put the button in
        self.bottom_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.bottom_box.pack_start(self._send_button.align, False, False, 0)

        bottom_background = Gtk.EventBox()
        bottom_background.get_style_context().add_class("grey")
        bottom_background.add(self.bottom_box)

        self._grid.attach(bottom_background, 0, 3, 1, 1)

        self._grid.set_row_spacing(0)
        self.set_main_widget(self._grid)

        # kano-profile stat collection
        try:
            from kano_profile.badges import increment_app_state_variable_with_dialog
            increment_app_state_variable_with_dialog('kano-feedback', 'starts',
                                                     1)
        except Exception:
            pass
    class MainWindow(base_class):
        state = 0
        last_level_visited = 0
        width = 680
        height = 405
        CSS_PATH = os.path.join(common.css_dir, 'style.css')

        def __init__(self, screen_number=None, screen_name=None,
                     socket_id=0, onescreen=False):
            # Check for internet, if screen is 12 means no internet
            if screen_number == 12 or screen_name == 'no-internet':
                common.has_internet = False
            else:
                common.has_internet = is_internet()

            # Set combobox styling to the screen
            # Is done here so we don't attach the styling multiple times when
            # switching screens
            apply_styling_to_screen(self.CSS_PATH)
            apply_common_to_screen()
            KanoComboBox.apply_styling_to_screen()
            ScrolledWindow.apply_styling_to_screen(wide=True)

            # Set window
            base_class.__init__(self, _("Settings"), self.width,
                                self.height, socket_id)

            self.set_decorated(True)
            self.top_bar = TopBar(_("Settings"))
            self.top_bar.set_close_callback(self.close_window)
            self.prev_handler = None
            self.set_icon_name('kano-settings')

            if self._base_name == "Window":
                self.set_titlebar(self.top_bar)

            self._onescreen = onescreen

            self.connect('delete-event', Gtk.main_quit)
            # In case we are called from kano-world-launcher, terminate splash
            os.system('kano-stop-splash')
            # Init to Home Screen
            HomeScreen(self, screen_number=screen_number, screen_name=screen_name)

        def clear_win(self):
            self.remove_main_widget()

        def go_to_home(self, widget=None, event=None):
            self.clear_win()

            if self._onescreen:
                sys.exit(0)

            HomeScreen(self)

        def change_prev_callback(self, callback):
            # first time, no event attached
            self.remove_prev_callback()
            self.prev_handler = self.top_bar.prev_button.connect(
                'button-release-event', callback
            )

        def remove_prev_callback(self):
            if self.prev_handler:
                self.top_bar.prev_button.disconnect(self.prev_handler)
                self.prev_handler = None

        def _trigger_tracking_event(self):
            """ Generate a tracker event with some hardware settings.

                This will send a track_date event called 'user-settings'
                with the audio setting, parental lock level and display
                configuration.
            """

            track_data('user-settings', {
                'audio': 'hdmi' if is_HDMI() else 'analog',
                'parental-lock-level': get_setting('Parental-level'),
                'display': get_status()
            })

        # On closing window, will alert if any of the listed booleans are True
        def close_window(self, button, event):
            if common.need_reboot:
                kdialog = KanoDialog(
                    _("Reboot?"),
                    _("Your Kano needs to reboot for changes to apply"),
                    [
                        {
                            'label': _("LATER"),
                            'color': 'grey',
                            'return_value': False
                        },
                        {
                            'label': _("REBOOT NOW"),
                            'color': 'orange',
                            'return_value': True
                        }
                    ],
                    parent_window=self.get_toplevel()
                )

                kdialog.set_action_background('grey')
                do_reboot_now = kdialog.run()
                if do_reboot_now:
                    os.system("sudo systemctl reboot")

            self._trigger_tracking_event()

            Gtk.main_quit()
    def contact_window(self):
        '''
        Contact Us window
        Contains text view and a Send button
        '''
        # delete the directory containing all the info we'll send, and recreate
        delete_tmp_dir()
        create_tmp_dir()

        ApplicationWindow.__init__(
            self,
            _('Contact Us'),  # noqa: F821
            self.WIDTH,
            0.35
        )

        screen = Gdk.Screen.get_default()
        specific_provider = Gtk.CssProvider()
        specific_provider.load_from_path(Media.media_dir() + 'css/style.css')
        style_context = Gtk.StyleContext()
        style_context.add_provider_for_screen(screen, specific_provider,
                                              Gtk.STYLE_PROVIDER_PRIORITY_USER)

        # Make sure this window has no icon in the task bar
        # so it plays nice with kdesk-blur
        self.set_property('skip-taskbar-hint', True)

        self._grid = Gtk.Grid()

        # Create top bar
        self._top_bar = TopBar(
            title=_("Contact Us"),  # noqa: F821
            window_width=self.WIDTH,
            has_buttons=False
        )
        self._top_bar.set_close_callback(Gtk.main_quit)
        self.set_decorated(True)
        self.set_titlebar(self._top_bar)

        # Create Text view
        self._text = Gtk.TextView()
        self._text.set_editable(True)
        self._text.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
        self._text.set_size_request(self.WIDTH, -1)

        self._textbuffer = self._text.get_buffer()
        self._textbuffer.set_text(
            _("Type your feedback here!")  # noqa: F821
        )
        self._clear_buffer_handler_id = self._textbuffer.connect(
            "insert-text", self.clear_buffer
        )

        scrolledwindow = ScrolledWindow()
        scrolledwindow.set_vexpand(True)
        scrolledwindow.set_policy(Gtk.PolicyType.NEVER,
                                  Gtk.PolicyType.AUTOMATIC)
        scrolledwindow.apply_styling_to_widget()
        scrolledwindow.add(self._text)
        scrolledwindow.set_margin_left(2)
        scrolledwindow.set_margin_right(2)
        scrolledwindow.set_margin_top(2)
        scrolledwindow.set_margin_bottom(2)

        # Very hacky way to get a border: create a grey event box
        # which is a little bigger than the widget below
        border = Gtk.EventBox()
        border.get_style_context().add_class("grey")
        border.add(scrolledwindow)
        self._grid.attach(border, 0, 0, 1, 1)
        border.set_margin_left(20)
        border.set_margin_right(20)
        border.set_margin_top(10)
        border.set_margin_bottom(20)

        # Create send button
        self._send_button = KanoButton(
            _("SEND")  # noqa: F821
        )
        self._send_button.set_sensitive(False)
        self._send_button.connect("button_press_event", self.send_feedback)
        self._send_button.pack_and_align()
        self._send_button.align.set_padding(10, 10, 0, 0)

        bottom_background = Gtk.EventBox()
        bottom_background.get_style_context().add_class("grey")
        bottom_background.add(self._send_button.align)

        self._grid.attach(bottom_background, 0, 1, 1, 1)

        self._grid.set_row_spacing(0)
        self.set_main_widget(self._grid)

        # kano-profile stat collection
        try:
            from kano_profile.badges import \
                increment_app_state_variable_with_dialog
            increment_app_state_variable_with_dialog(
                'kano-feedback', 'starts', 1
            )
        except Exception:
            pass
Beispiel #19
0
class GreeterWindow(ApplicationWindow):
    WIDTH = 400
    HEIGHT = -1

    greeter = LightDM.Greeter

    def __init__(self):
        apply_common_to_screen()

        self.a = self.b = self.c = 0
        self.switching = 0

        ApplicationWindow.__init__(self, _('Login'), self.WIDTH, self.HEIGHT)
        self.connect("delete-event", Gtk.main_quit)

        # Create a new LightDM.Greeter instance which will be used by the 2 views
        self.greeter = GreeterWindow.greeter.new()

        # Create the two views: one for normal Login, the other to create a new account
        self.password_view = PasswordView(None, self.greeter)
        self.newuser_view = NewUserView(self.greeter)
        self.login_with_kw_view = LoginWithKanoWorldView(self.greeter)

        self.grid = Gtk.Grid()
        self.set_main_widget(self.grid)

        self.grid.set_column_spacing(30)
        self.grid.set_row_spacing(30)

        self.top_bar = TopBar(_('Login'))
        self._remove_top_bar_buttons()
        self.top_bar.set_size_request(self.WIDTH, -1)
        self.grid.attach(self.top_bar, 0, 0, 3, 1)

        self.grid.attach(Gtk.Label(), 0, 3, 3, 1)

        self.top_bar.set_prev_callback(self._back_cb)

        self.user_list = UserListView()

        self.go_to_users()

        cursor = Gdk.Cursor.new(Gdk.CursorType.ARROW)
        self.get_root_window().set_cursor(cursor)

    def _remove_top_bar_buttons(self):
        self.top_bar.box.remove(self.top_bar.close_button)
        self.top_bar.box.remove(self.top_bar.next_button)

    def set_main(self, wdg):
        child = self.grid.get_child_at(1, 1)
        if child:
            self.grid.remove(child)

        self.grid.attach(wdg, 1, 1, 1, 1)
        self.show_all()

    def go_to_users(self):
        self.set_main(self.user_list)
        self.top_bar.disable_prev()

    def go_to_password(self, user):
        self.set_main(self.password_view)
        self.top_bar.enable_prev()

        if not self.switching == 1:
            self.greeter.disconnect(self.a)
            self.greeter.disconnect(self.b)
            self.greeter.disconnect(self.c)

            self.greeter = GreeterWindow.greeter.new()
            self.password_view.greeter = self.greeter
            (self.a, self.b, self.c) = self.password_view._reset_greeter()
            self.switching = 1

        self.password_view.grab_focus(user)

    def go_to_newuser(self):
        self.set_main(self.newuser_view)
        self.top_bar.enable_prev()

        if not self.switching == 2:
            self.greeter.disconnect(self.a)
            self.greeter.disconnect(self.b)
            self.greeter.disconnect(self.c)

            self.greeter = GreeterWindow.greeter.new()
            self.newuser_view.greeter = self.greeter

            # FIXME: We do not reset the greeter in the newuser view
            # It will be done only when the local user account has been forced
            # so as to avoid LightDM freezes
            # (self.a, self.b, self.c) = self.newuser_view._reset_greeter()
            self.switching = 2

    def go_to_login_with_kw(self):
        self.set_main(self.login_with_kw_view)
        self.top_bar.enable_prev()

        if not self.switching == 3:
            self.greeter.disconnect(self.a)
            self.greeter.disconnect(self.b)
            self.greeter.disconnect(self.c)

            self.greeter = GreeterWindow.greeter.new()
            self.login_with_kw_view.greeter = self.greeter

            # FIXME: We do not reset the greeter in the newuser view
            # It will be done only when the local user account has been forced
            # so as to avoid LightDM freezes
            # (self.a, self.b, self.c) = self.login_with_kw_view._reset_greeter()
            self.switching = 3

        self.login_with_kw_view.grab_focus()

    def _back_cb(self, event, button):
        self.go_to_users()
    def report_window(self):
        '''
        Report window
        Contains 2 text views and Take Screenshot, Add Image and Send buttons
        '''
        ApplicationWindow.__init__(
            self,
            _('Report a Problem'),  # noqa: F821
            self.WIDTH,
            0.35
        )

        screen = Gdk.Screen.get_default()
        specific_provider = Gtk.CssProvider()
        specific_provider.load_from_path(Media.media_dir() + 'css/style.css')
        style_context = Gtk.StyleContext()
        style_context.add_provider_for_screen(screen, specific_provider,
                                              Gtk.STYLE_PROVIDER_PRIORITY_USER)

        self.set_icon_name("feedback")
        self._grid = Gtk.Grid()

        # Create top bar
        self._top_bar = TopBar(
            title=_("Report a Problem"),  # noqa: F821
            window_width=self.WIDTH,
            has_buttons=False
        )
        self._top_bar.set_close_callback(Gtk.main_quit)
        self.set_decorated(True)
        self.set_titlebar(self._top_bar)

        self.entry = Gtk.Entry()
        self.entry.props.placeholder_text = \
            _("Add subject (optional)")  # noqa: F821
        self.entry.set_margin_left(20)
        self.entry.set_margin_right(20)
        self.entry.set_margin_top(20)
        self.entry.set_margin_bottom(10)
        self._grid.attach(self.entry, 0, 0, 1, 1)

        # Create Text view
        self._text = Gtk.TextView()
        self._text.set_editable(True)
        self._text.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
        self._text.set_size_request(self.WIDTH, -1)

        self._textbuffer = self._text.get_buffer()
        self._textbuffer.set_text(
            _("Type your problem here!")  # noqa: F821
        )

        self._clear_buffer_handler_id = self._textbuffer.connect(
            "insert-text", self.clear_buffer
        )

        scrolledwindow = ScrolledWindow()
        scrolledwindow.set_vexpand(True)
        scrolledwindow.set_policy(
            Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC
        )
        scrolledwindow.apply_styling_to_widget()
        scrolledwindow.add(self._text)
        scrolledwindow.set_margin_left(2)
        scrolledwindow.set_margin_right(2)
        scrolledwindow.set_margin_top(2)
        scrolledwindow.set_margin_bottom(2)

        # Very hacky way to get a border: create a grey event box
        # which is a little bigger than the widget below
        border = Gtk.EventBox()
        border.get_style_context().add_class("grey")
        border.add(scrolledwindow)
        self._grid.attach(border, 0, 1, 1, 1)
        border.set_margin_left(20)
        border.set_margin_right(20)
        border.set_margin_top(10)
        border.set_margin_bottom(20)

        # Create take screenshot button
        self._screenshot_button = KanoButton(
            _("TAKE SCREENSHOT"),  # noqa: F821
            "blue"
        )
        self._screenshot_button.set_sensitive(True)
        self._screenshot_button.connect("button_press_event",
                                        self.screenshot_clicked)

        # Create attach screenshot button
        self._attach_button = KanoButton(
            _("ADD IMAGE"),  # noqa: F821
            "blue"
        )
        self._attach_button.set_sensitive(True)
        self._attach_button.connect("button_press_event", self.attach_clicked)

        # Create send button
        self._send_button = KanoButton(
            _("SEND")  # noqa: F821
        )
        self._send_button.set_sensitive(False)
        self._send_button.connect("button_press_event", self.send_feedback)
        self._send_button.pack_and_align()
        self._send_button.set_margin(10, 0, 10, 0)

        self.screenshot_box = Gtk.ButtonBox()
        self.screenshot_box.set_layout(Gtk.ButtonBoxStyle.CENTER)
        self.screenshot_box.set_spacing(20)
        self.pack_screenshot_buttons()
        self.screenshot_box.set_margin_bottom(20)

        self._grid.attach(self.screenshot_box, 0, 2, 1, 1)

        # Create grey box to put the button in
        self.bottom_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.bottom_box.pack_start(self._send_button.align, False, False, 0)

        bottom_background = Gtk.EventBox()
        bottom_background.get_style_context().add_class("grey")
        bottom_background.add(self.bottom_box)

        self._grid.attach(bottom_background, 0, 3, 1, 1)

        self._grid.set_row_spacing(0)
        self.set_main_widget(self._grid)

        # kano-profile stat collection
        try:
            from kano_profile.badges import increment_app_state_variable_with_dialog
            increment_app_state_variable_with_dialog(
                'kano-feedback', 'starts', 1
            )
        except Exception:
            pass