Exemplo n.º 1
0
 def _on_reset_backend_instance(self, _button):
     backend = Settings.get().backend
     Settings.get().reset_instance_url(backend)
     self.backend_instance_stack.set_visible_child_name('view')
     Gtk.StyleContext.remove_class(
         self.backend_instance.get_style_context(), 'error')
     self.error_popover.popdown()
Exemplo n.º 2
0
    def update_trans_button(self, button, keyboard):
        modifiers = keyboard.get_state(
        ) & Gtk.accelerator_get_default_mod_mask()

        control_mask = Gdk.ModifierType.CONTROL_MASK
        shift_mask = Gdk.ModifierType.SHIFT_MASK
        unicode_key_val = Gdk.keyval_to_unicode(keyboard.keyval)
        if (GLib.unichar_isgraph(chr(unicode_key_val))
                and modifiers in (shift_mask, 0)
                and not self.src_text.is_focus()):
            self.src_text.grab_focus()

        if not Settings.get().live_translation:
            if control_mask == modifiers:
                if keyboard.keyval == Gdk.KEY_Return:
                    if not Settings.get().translate_accel_value:
                        self.translation(button)
                        return Gdk.EVENT_STOP
                    return Gdk.EVENT_PROPAGATE
            elif keyboard.keyval == Gdk.KEY_Return:
                if Settings.get().translate_accel_value:
                    self.translation(button)
                    return Gdk.EVENT_STOP
                return Gdk.EVENT_PROPAGATE

        return Gdk.EVENT_PROPAGATE
Exemplo n.º 3
0
    def setup(self):
        self.set_default_icon_name(APP_ID)

        # Load saved dark mode
        gtk_settings = Gtk.Settings.get_default()
        gtk_settings.set_property('gtk-application-prefer-dark-theme',
                                  Settings.get().dark_mode)

        # Connect responsive design function
        self.connect('check-resize', self.responsive_listener)
        # Save settings on close
        self.connect('delete-event', self.save_settings)

        self.setup_headerbar()
        self.setup_actionbar()
        self.setup_translation()
        self.toggle_mobile_mode()

        # Load translator
        self.retry_backend_btn.connect('clicked', self.retry_load_translator)
        threading.Thread(target=self.load_translator,
                         args=[Settings.get().backend, True],
                         daemon=True).start()
        # Get languages available for speech
        if Settings.get().tts != '':
            threading.Thread(target=self.load_lang_speech, daemon=True).start()
Exemplo n.º 4
0
 def save_settings(self, *args, **kwargs):
     if not self.is_maximized():
         size = self.get_size()
         Settings.get().window_size = (size.width, size.height)
     if self.translator is not None:
         Settings.get().set_src_langs(self.translator.name, self.src_langs)
         Settings.get().set_dest_langs(self.translator.name,
                                       self.dest_langs)
Exemplo n.º 5
0
 def __check_instance_support(self):
     backend = Settings.get().backend
     if TRANSLATORS[backend].supported_features['change-instance']:
         self.backend_instance_row.set_visible(True)
         self.backend_instance_label.set_label(
             Settings.get().get_instance_url(backend))
     else:
         self.backend_instance_row.set_visible(False)
Exemplo n.º 6
0
 def _on_settings_changed(self, _settings, key):
     backend = Settings.get().backend
     if key == 'backend-settings':
         if TRANSLATORS[backend].supported_features['change-instance']:
             # Update backend
             Settings.get().reset_src_langs(backend)
             Settings.get().reset_dest_langs(backend)
             self.parent.change_backends(backend)
Exemplo n.º 7
0
 def on_shortcuts(self, _action, _param):
     """Launch the Keyboard Shortcuts window."""
     builder = Gtk.Builder.new_from_resource(
         f'{RES_PATH}/shortcuts-window.ui')
     translate_shortcut = builder.get_object('translate_shortcut')
     translate_shortcut.set_visible(not Settings.get().live_translation)
     translate_shortcut.set_property('accelerator',
                                     Settings.get().translate_accel)
     shortcuts_window = builder.get_object('shortcuts')
     shortcuts_window.set_transient_for(self.window)
     shortcuts_window.show()
Exemplo n.º 8
0
    def _on_save_backend_instance(self, _button):
        backend = Settings.get().backend
        old_value = Settings.get().get_instance_url(backend)
        new_value = self.backend_instance.get_text()

        url = re.compile(r"https?://(www\.)?")
        new_value = url.sub('', new_value).strip().strip('/')

        if new_value != old_value:
            # Validate
            threading.Thread(target=self.__validate_new_backend_instance,
                             args=[new_value],
                             daemon=True).start()
        else:
            self.backend_instance_stack.set_visible_child_name('view')
Exemplo n.º 9
0
        def update_ui():
            # Supported features
            if not self.translator.supported_features['mistakes']:
                self.mistakes.set_revealed(False)

            if not self.translator.supported_features['pronunciation']:
                self.src_pron_revealer.set_reveal_child(False)
                self.dest_pron_revealer.set_reveal_child(False)
                self.app.pronunciation_action.set_enabled(False)

            self.no_retranslate = True
            # Update langs list
            self.src_lang_selector.set_languages(self.translator.languages)
            self.dest_lang_selector.set_languages(self.translator.languages)
            # Update selected langs
            src_lang_default = 'auto' if Settings.get(
            ).src_auto else self.src_langs[0]
            self.src_lang_selector.set_property('selected', src_lang_default)
            self.dest_lang_selector.set_property('selected',
                                                 self.dest_langs[0])

            self.no_retranslate = False

            self.main_stack.set_visible_child_name('translate')
            self.set_property('backend-loading', False)
Exemplo n.º 10
0
    def setup_actions(self):
        """ Setup menu actions """

        self.pronunciation_action = Gio.SimpleAction.new_stateful(
            'pronunciation', None,
            Settings.get().show_pronunciation_value)
        self.pronunciation_action.connect('change-state',
                                          self.on_pronunciation)
        self.add_action(self.pronunciation_action)

        preferences_action = Gio.SimpleAction.new('preferences', None)
        preferences_action.connect('activate', self.on_preferences)
        self.set_accels_for_action('app.preferences', ['<Primary>comma'])
        self.add_action(preferences_action)

        shortcuts_action = Gio.SimpleAction.new('shortcuts', None)
        shortcuts_action.connect('activate', self.on_shortcuts)
        self.add_action(shortcuts_action)

        about_action = Gio.SimpleAction.new('about', None)
        about_action.connect('activate', self.on_about)
        self.add_action(about_action)

        quit_action = Gio.SimpleAction.new('quit', None)
        quit_action.connect('activate', self.on_quit)
        self.set_accels_for_action('app.quit', ['<Primary>Q'])
        self.add_action(quit_action)
Exemplo n.º 11
0
    def setup_translation(self):
        # Left buffer
        self.src_buffer = self.src_text.get_buffer()
        self.src_buffer.set_text(self.launch_text)
        self.src_buffer.connect('changed', self.on_src_text_changed)
        self.src_buffer.connect('end-user-action', self.user_action_ended)
        self.connect('key-press-event', self.update_trans_button)
        # Clear button
        self.clear_btn.connect('clicked', self.ui_clear)
        # Paste button
        self.paste_btn.connect('clicked', self.ui_paste)
        # Translate button
        self.translate_btn.connect('clicked', self.translation)
        # "Did you mean" links
        self.mistakes_label.connect('activate-link', self.on_mistakes_clicked)

        # Right buffer
        self.dest_buffer = self.dest_text.get_buffer()
        self.dest_buffer.set_text('')
        self.dest_buffer.connect('changed', self.on_dest_text_changed)
        # Clipboard button
        self.copy_btn.connect('clicked', self.ui_copy)
        # Translation progress spinner
        self.trans_spinner.hide()
        self.trans_warning.hide()

        # Voice buttons prep-work
        self.src_voice_warning = Gtk.Image.new_from_icon_name(
            'dialog-warning-symbolic', Gtk.IconSize.BUTTON)
        self.src_voice_image = Gtk.Image.new_from_icon_name(
            'audio-speakers-symbolic', Gtk.IconSize.BUTTON)
        self.src_voice_spinner = Gtk.Spinner(
        )  # For use while audio is running or still loading.
        self.src_voice_btn.connect('clicked', self.ui_src_voice)

        self.dest_voice_warning = Gtk.Image.new_from_icon_name(
            'dialog-warning-symbolic', Gtk.IconSize.BUTTON)
        self.dest_voice_image = Gtk.Image.new_from_icon_name(
            'audio-speakers-symbolic', Gtk.IconSize.BUTTON)
        self.dest_voice_spinner = Gtk.Spinner()
        self.dest_voice_btn.connect('clicked', self.ui_dest_voice)

        self.toggle_voice_spinner(True)

        self.src_voice_btn.set_visible(Settings.get().tts != '')
        self.dest_voice_btn.set_visible(Settings.get().tts != '')
Exemplo n.º 12
0
 def spinner_end():
     self.backend.set_sensitive(True)
     self.backend_instance_row.set_sensitive(True)
     self.backend_instance_save.remove(self.instance_save_spinner)
     self.backend_instance_save.add(self.instance_save_image)
     self.backend_instance_label.set_label(
         Settings.get().get_instance_url(backend))
     self.instance_save_spinner.stop()
Exemplo n.º 13
0
    def on_pronunciation(self, action, value):
        """ Update show pronunciation setting """
        action.set_state(value)
        Settings.get().show_pronunciation = value

        # Update UI
        if self.window.trans_src_pron is not None:
            self.window.src_pron_revealer.set_reveal_child(value)
        if self.window.trans_dest_pron is not None:
            self.window.dest_pron_revealer.set_reveal_child(value)
Exemplo n.º 14
0
 def user_action_ended(self, buffer):
     # If the text is over the highest number of characters allowed, it is truncated.
     # This is done for avoiding exceeding the limit imposed by Google.
     if buffer.get_char_count() >= MAX_LENGTH:
         self.send_notification(_('5000 characters limit reached!'))
         src_text = buffer.get_text(buffer.get_start_iter(),
                                    buffer.get_end_iter(), True)
         self.src_buffer.set_text(src_text[:MAX_LENGTH])
     self.char_counter.set_text(
         f'{str(buffer.get_char_count())}/{MAX_LENGTH}')
     if Settings.get().live_translation:
         self.translation(None)
Exemplo n.º 15
0
 def do_activate(self):
     self.window = self.props.active_window
     if not self.window:
         width, height = Settings.get().window_size
         self.window = DialectWindow(
             application=self,
             # Translators: Do not translate the app name!
             title=_('Dialect'),
             default_height=height,
             default_width=width,
             text=self.launch_text,
             langs=self.launch_langs)
     self.window.present()
Exemplo n.º 16
0
        def on_pronunciation():
            reveal = Settings.get().show_pronunciation
            if self.translator.supported_features['pronunciation']:
                if self.trans_src_pron is not None:
                    self.src_pron_label.set_text(self.trans_src_pron)
                    self.src_pron_revealer.set_reveal_child(reveal)
                elif self.src_pron_revealer.get_reveal_child():
                    self.src_pron_revealer.set_reveal_child(False)

                if self.trans_dest_pron is not None:
                    self.dest_pron_label.set_text(self.trans_dest_pron)
                    self.dest_pron_revealer.set_reveal_child(reveal)
                elif self.dest_pron_revealer.get_reveal_child():
                    self.dest_pron_revealer.set_reveal_child(False)
Exemplo n.º 17
0
    def __validate_new_backend_instance(self, url):
        def spinner_start():
            self.backend.set_sensitive(False)
            self.backend_instance_row.set_sensitive(False)
            self.backend_instance_save.remove(self.instance_save_image)
            self.backend_instance_save.add(self.instance_save_spinner)
            self.instance_save_spinner.start()

        def spinner_end():
            self.backend.set_sensitive(True)
            self.backend_instance_row.set_sensitive(True)
            self.backend_instance_save.remove(self.instance_save_spinner)
            self.backend_instance_save.add(self.instance_save_image)
            self.backend_instance_label.set_label(
                Settings.get().get_instance_url(backend))
            self.instance_save_spinner.stop()

        GLib.idle_add(spinner_start)
        backend = Settings.get().backend
        validate = TRANSLATORS[backend].validate_instance_url(url)
        if validate:
            Settings.get().set_instance_url(backend, url)
            GLib.idle_add(Gtk.StyleContext.remove_class,
                          self.backend_instance.get_style_context(), 'error')
            GLib.idle_add(self.backend_instance_stack.set_visible_child_name,
                          'view')
            GLib.idle_add(self.error_popover.popdown)
        else:
            GLib.idle_add(Gtk.StyleContext.add_class,
                          self.backend_instance.get_style_context(), 'error')
            error_text = _('Not a valid {backend} instance')
            error_text = error_text.format(
                backend=TRANSLATORS[backend].prettyname)
            GLib.idle_add(self.error_label.set_label, error_text)
            GLib.idle_add(self.error_popover.popup)

        GLib.idle_add(spinner_end)
Exemplo n.º 18
0
    def _toggle_tts(self, switch, _active):
        value = ''
        if switch.get_active() and len(TTS) >= 1:
            tts = list(TTS.keys())
            value = str(tts[0])

        self.parent.src_voice_btn.set_sensitive(False)
        self.parent.src_voice_btn.set_visible(switch.get_active())
        self.parent.dest_voice_btn.set_sensitive(False)
        self.parent.dest_voice_btn.set_visible(switch.get_active())

        Settings.get().tts = value

        if switch.get_active():
            threading.Thread(target=self.parent.load_lang_speech,
                             daemon=True).start()
Exemplo n.º 19
0
    def on_src_lang_changed(self, _obj, _param):
        code = self.src_lang_selector.get_property('selected')
        dest_code = self.dest_lang_selector.get_property('selected')
        src_text = self.src_buffer.get_text(self.src_buffer.get_start_iter(),
                                            self.src_buffer.get_end_iter(),
                                            True)

        if code == dest_code:
            code = self.dest_langs[1] if code == self.src_langs[
                0] else dest_code
            self.dest_lang_selector.set_property('selected', self.src_langs[0])

        # Disable or enable listen function.
        if self.tts_langs and Settings.get().tts != '':
            self.src_voice_btn.set_sensitive(code in self.tts_langs
                                             and src_text != '')

        if code in self.translator.languages:
            self.src_lang_label.set_label(
                self.translator.languages[code].capitalize())
            # Updated saved left langs list
            if code in self.src_langs:
                # Bring lang to the top
                index = self.src_langs.index(code)
                self.src_langs.insert(0, self.src_langs.pop(index))
            else:
                self.src_langs.pop()
                self.src_langs.insert(0, code)
        else:
            self.src_lang_label.set_label(_('Auto'))

        # Rewrite recent langs
        self.src_lang_selector.clear_recent()
        self.src_lang_selector.insert_recent('auto', _('Auto'))
        for code in self.src_langs:
            name = self.translator.languages[code].capitalize()
            self.src_lang_selector.insert_recent(code, name)

        # Refresh list
        self.src_lang_selector.refresh_selected()

        # Translate again
        if not self.no_retranslate:
            self.translation(None)
Exemplo n.º 20
0
    def load_lang_speech(self, listen=False, text=None, language=None):
        """
        Load the language list for TTS.

        text and language parameters are only needed with listen parameter.
        """
        try:
            self.voice_loading = True
            self.tts = TTS[Settings.get().tts]()
            self.tts_langs = self.tts.languages
            if not listen:
                GLib.idle_add(self.toggle_voice_spinner, False)
            elif language in self.tts_langs and text != '':
                self.voice_download(text, language)

        except RuntimeError as exc:
            GLib.idle_add(self.on_listen_failed)
            print('Error: ' + str(exc))
        finally:
            if not listen:
                self.voice_loading = False
Exemplo n.º 21
0
 def retry_load_translator(self, _button):
     threading.Thread(target=self.load_translator,
                      args=[Settings.get().backend],
                      daemon=True).start()
Exemplo n.º 22
0
    def setup(self):
        # Disable search, we have few preferences
        self.set_search_enabled(False)
        # Temporal fix for crash
        self.connect('destroy', self._unbind_settings)

        # Setup translate accel combo row
        model = Gio.ListStore.new(Handy.ValueObject)
        options = ['Ctrl + Enter', 'Enter']
        for index, value in enumerate(options):
            model.insert(index, Handy.ValueObject.new(value))
        self.translate_accel.bind_name_model(model,
                                             Handy.ValueObject.dup_string)

        # Setup backends combo row
        self.backend_model = Gio.ListStore.new(BackendObject)
        backend_options = [
            BackendObject(translator.name, translator.prettyname)
            for translator in TRANSLATORS.values()
        ]
        selected_backend_index = 0
        for index, value in enumerate(backend_options):
            self.backend_model.insert(index, value)
            if value.name == Settings.get().backend:
                selected_backend_index = index
        self.backend.bind_name_model(self.backend_model,
                                     BackendObject.get_name)

        # Bind preferences with GSettings
        Settings.get().bind('dark-mode', self.dark_mode, 'active',
                            Gio.SettingsBindFlags.DEFAULT)
        Settings.get().bind('live-translation', self.live_translation,
                            'active', Gio.SettingsBindFlags.DEFAULT)
        Settings.get().bind('translate-accel', self.translate_accel,
                            'selected-index', Gio.SettingsBindFlags.DEFAULT)
        Settings.get().bind('src-auto', self.src_auto, 'active',
                            Gio.SettingsBindFlags.DEFAULT)

        # Setup TTS
        self.tts_row.set_visible(len(TTS) >= 1)
        self.tts.set_active(Settings.get().tts != '')

        # Toggle dark mode
        self.dark_mode.connect('notify::active', self._toggle_dark_mode)

        # Set translate accel sensitivity by live translation state
        self.translate_accel.set_sensitive(
            not self.live_translation.get_active())
        self.live_translation.connect('notify::active',
                                      self._toggle_accel_pref)

        # Switch backends
        self.backend.set_selected_index(selected_backend_index)
        self.backend.connect('notify::selected-index', self._switch_backends)
        self.parent.connect('notify::backend-loading',
                            self._on_backend_loading)

        # Toggle TTS
        self.tts.connect('notify::active', self._toggle_tts)

        # Change translator instance
        Settings.get().connect('changed', self._on_settings_changed)
        self.backend_instance_edit.connect('clicked',
                                           self._on_edit_backend_instance)
        self.backend_instance_save.connect('clicked',
                                           self._on_save_backend_instance)
        self.backend_instance_reset.connect('clicked',
                                            self._on_reset_backend_instance)
        self.__check_instance_support()

        self.instance_save_image = Gtk.Image.new_from_icon_name(
            'emblem-ok-symbolic', Gtk.IconSize.BUTTON)
        self.backend_instance_save.add(self.instance_save_image)
        self.instance_save_spinner = Gtk.Spinner()
        self.instance_save_image.show()
        self.instance_save_spinner.show()

        self.error_popover = Gtk.Popover(relative_to=self.backend_instance,
                                         can_focus=False,
                                         modal=False)
        self.error_label = Gtk.Label(label='Not a valid instance')
        error_icon = Gtk.Image.new_from_icon_name('dialog-error-symbolic',
                                                  Gtk.IconSize.LARGE_TOOLBAR)
        error_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,
                            margin=8,
                            spacing=8)
        error_box.pack_start(error_icon, False, False, 0)
        error_box.pack_start(self.error_label, False, False, 0)
        self.error_popover.add(error_box)
        self.error_popover.set_position(Gtk.PositionType.BOTTOM)
        error_box.show_all()
        self.error_popover.hide()

        # Search Provider
        if os.getenv('XDG_CURRENT_DESKTOP') != 'GNOME':
            self.search_provider.hide()
Exemplo n.º 23
0
 def _on_edit_backend_instance(self, _button):
     backend = Settings.get().backend
     self.backend_instance_stack.set_visible_child_name('edit')
     self.backend_instance.set_text(
         Settings.get().get_instance_url(backend))
Exemplo n.º 24
0
 def _switch_backends(self, row, _value):
     backend = self.backend_model[row.get_selected_index()].name
     Settings.get().backend = backend
     self.__check_instance_support()
     self.parent.change_backends(backend)
Exemplo n.º 25
0
    def load_translator(self, backend, launch=False):
        def update_ui():
            # Supported features
            if not self.translator.supported_features['mistakes']:
                self.mistakes.set_revealed(False)

            if not self.translator.supported_features['pronunciation']:
                self.src_pron_revealer.set_reveal_child(False)
                self.dest_pron_revealer.set_reveal_child(False)
                self.app.pronunciation_action.set_enabled(False)

            self.no_retranslate = True
            # Update langs list
            self.src_lang_selector.set_languages(self.translator.languages)
            self.dest_lang_selector.set_languages(self.translator.languages)
            # Update selected langs
            src_lang_default = 'auto' if Settings.get(
            ).src_auto else self.src_langs[0]
            self.src_lang_selector.set_property('selected', src_lang_default)
            self.dest_lang_selector.set_property('selected',
                                                 self.dest_langs[0])

            self.no_retranslate = False

            self.main_stack.set_visible_child_name('translate')
            self.set_property('backend-loading', False)

        # Show loading view
        GLib.idle_add(self.main_stack.set_visible_child_name, 'loading')

        try:
            # Translator object
            if TRANSLATORS[backend].supported_features['change-instance']:
                self.translator = TRANSLATORS[backend](base_url=Settings.get(
                ).get_instance_url(TRANSLATORS[backend].name))
            else:
                self.translator = TRANSLATORS[backend]()

            # Get saved languages
            self.src_langs = Settings.get().get_src_langs(self.translator.name)
            self.dest_langs = Settings.get().get_dest_langs(
                self.translator.name)

            # Update UI
            GLib.idle_add(update_ui)

            if launch:
                self.no_retranslate = True
                if self.launch_langs['src'] is not None:
                    self.src_lang_selector.set_property(
                        'selected', self.launch_langs['src'])
                if self.launch_langs['dest'] is not None and self.launch_langs[
                        'dest'] in self.translator.languages:
                    self.dest_lang_selector.set_property(
                        'selected', self.launch_langs['dest'])
                self.no_retranslate = False

                if self.launch_text != '':
                    GLib.idle_add(self.translate, self.launch_text,
                                  self.launch_langs['src'],
                                  self.launch_langs['dest'])

        except Exception as exc:
            # Show error view
            GLib.idle_add(self.main_stack.set_visible_child_name, 'error')
            GLib.idle_add(self.set_property, 'backend-loading', False)

            self.error_message.set_label(str(exc))
            print('Error: ' + str(exc))
Exemplo n.º 26
0
 def _unbind_settings(self, *args, **kwargs):
     Settings.get().unbind(self.dark_mode, 'active')
     Settings.get().unbind(self.live_translation, 'active')
     Settings.get().unbind(self.src_auto, 'active')