Exemplo n.º 1
0
    def __init__(self):
        super().__init__()

        self.document = None

        self.view = Editor()

        self.scrolled = Gtk.ScrolledWindow(hexpand=True, vexpand=True)
        self.scrolled.get_style_context().add_class('scrolled-editor')
        self.scrolled.add(self.view)

        # SearchBar
        self.search_bar = SearchBar()
        self.search_revealer = Gtk.Revealer()
        self.search_revealer.add(self.search_bar)
        self.search_bar.connect('find-changed', self.do_next_match)
        self.search_bar.connect('find-next', self.do_next_match)
        self.search_bar.connect('find-prev', self.do_previous_match)
        self.search_bar.connect('stop-search', self.do_stop_search)

        content_grid = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        content_grid.pack_start(self.search_revealer, False, True, 0)
        content_grid.pack_start(self.scrolled, True, True, 0)
        content_grid.show_all()

        self.overlay = Gtk.Overlay()
        self.overlay.add(content_grid)
        self.stats_overlay = Granite.WidgetsOverlayBar.new(self.overlay)

        self.stats_handler = StatsHandler(overlay_bar=self.stats_overlay,
                                          buffer=self.buffer)
        self.stats_handler.update_default_stat()

        self.add(self.overlay)

        self.font_desc = Pango.FontDescription()
        self.spellchecker = GtkSpell.Checker()

        self.search_settings = GtkSource.SearchSettings(wrap_around=True)
        self.search_context = GtkSource.SearchContext(
            buffer=self.buffer, settings=self.search_settings)
        self.search_iter = None
Exemplo n.º 2
0
    def __init__(self, settings: Gio.Settings, **kwargs):
        super().__init__(**kwargs)

        self.set_default_icon(Pixbuf.new_from_resource_at_scale(
            '/com/github/tenderowl/norka/icons/com.github.tenderowl.norka.svg',
            128, 128, True
        ))
        self.settings = settings
        self._configure_timeout_id = None
        self.preview = None

        self.apply_styling()

        self.current_size = (786, 520)
        self.resize(*self.settings.get_value('window-size'))
        self.connect('configure-event', self.on_configure_event)
        self.connect('destroy', self.on_window_delete_event)

        # Export clients
        self.medium_client = Medium()
        self.writeas_client = Writeas()
        self.uri_to_open = None

        # Make a header
        self.header = Header(self.settings)
        self.set_titlebar(self.header)
        self.header.show()

        # Init screens
        self.welcome_grid = Welcome()
        self.welcome_grid.connect('activated', self.on_welcome_activated)
        self.welcome_grid.connect('document-import', self.on_document_import)

        self.document_grid = DocumentGrid()
        self.document_grid.connect('document-create', self.on_document_create_activated)
        self.document_grid.connect('document-import', self.on_document_import)
        self.document_grid.view.connect('item-activated', self.on_document_item_activated)

        self.editor = Editor()

        self.screens = Gtk.Stack()
        self.screens.set_transition_duration(400)
        self.screens.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)

        self.screens.add_named(self.welcome_grid, 'welcome-grid')
        self.screens.add_named(self.document_grid, 'document-grid')
        self.screens.add_named(self.editor, 'editor-grid')

        self.screens.show_all()

        self.toast = Granite.WidgetsToast()

        self.overlay = Gtk.Overlay()
        self.overlay.add_overlay(self.screens)
        self.overlay.add_overlay(self.toast)
        self.overlay.show_all()

        self.add(self.overlay)

        # Init actions
        self.init_actions()

        # If here's at least one document in storage
        # then show documents grid
        self.check_documents_count()

        # Pull the Settings
        self.toggle_spellcheck(self.settings.get_boolean('spellcheck'))
        self.autosave = self.settings.get_boolean('autosave')
        self.set_autoindent(self.settings.get_boolean('autoindent'))
        self.set_tabs_spaces(self.settings.get_boolean('spaces-instead-of-tabs'))
        self.set_indent_width(self.settings.get_int('indent-width'))
        self.set_style_scheme(self.settings.get_string('stylescheme'))
        self.editor.update_font(self.settings.get_string('font'))
Exemplo n.º 3
0
class NorkaWindow(Gtk.ApplicationWindow):
    __gtype_name__ = 'NorkaWindow'

    def __init__(self, settings: Gio.Settings, **kwargs):
        super().__init__(**kwargs)

        self.set_default_icon(Pixbuf.new_from_resource_at_scale(
            '/com/github/tenderowl/norka/icons/com.github.tenderowl.norka.svg',
            128, 128, True
        ))
        self.settings = settings
        self._configure_timeout_id = None
        self.preview = None

        self.apply_styling()

        self.current_size = (786, 520)
        self.resize(*self.settings.get_value('window-size'))
        self.connect('configure-event', self.on_configure_event)
        self.connect('destroy', self.on_window_delete_event)

        # Export clients
        self.medium_client = Medium()
        self.writeas_client = Writeas()
        self.uri_to_open = None

        # Make a header
        self.header = Header(self.settings)
        self.set_titlebar(self.header)
        self.header.show()

        # Init screens
        self.welcome_grid = Welcome()
        self.welcome_grid.connect('activated', self.on_welcome_activated)
        self.welcome_grid.connect('document-import', self.on_document_import)

        self.document_grid = DocumentGrid()
        self.document_grid.connect('document-create', self.on_document_create_activated)
        self.document_grid.connect('document-import', self.on_document_import)
        self.document_grid.view.connect('item-activated', self.on_document_item_activated)

        self.editor = Editor()

        self.screens = Gtk.Stack()
        self.screens.set_transition_duration(400)
        self.screens.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)

        self.screens.add_named(self.welcome_grid, 'welcome-grid')
        self.screens.add_named(self.document_grid, 'document-grid')
        self.screens.add_named(self.editor, 'editor-grid')

        self.screens.show_all()

        self.toast = Granite.WidgetsToast()

        self.overlay = Gtk.Overlay()
        self.overlay.add_overlay(self.screens)
        self.overlay.add_overlay(self.toast)
        self.overlay.show_all()

        self.add(self.overlay)

        # Init actions
        self.init_actions()

        # If here's at least one document in storage
        # then show documents grid
        self.check_documents_count()

        # Pull the Settings
        self.toggle_spellcheck(self.settings.get_boolean('spellcheck'))
        self.autosave = self.settings.get_boolean('autosave')
        self.set_autoindent(self.settings.get_boolean('autoindent'))
        self.set_tabs_spaces(self.settings.get_boolean('spaces-instead-of-tabs'))
        self.set_indent_width(self.settings.get_int('indent-width'))
        self.set_style_scheme(self.settings.get_string('stylescheme'))
        self.editor.update_font(self.settings.get_string('font'))

    def apply_styling(self):
        """Apply elementary OS header styling only for elementary OS"""
        if distro.id() == 'elementary':
            Granite.widgets_utils_set_color_primary(self,
                                                    Gdk.RGBA(red=0.29, green=0.50, blue=0.64, alpha=1.0),
                                                    Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
            self.get_style_context().add_class('elementary')


    def init_actions(self) -> None:
        """Initialize app-wide actions.

        """
        action_items = {
            'document': [
                {
                    'name': 'create',
                    'action': self.on_document_create_activated,
                    'accels': ('<Control>n',)
                },
                {
                    'name': 'save',
                    'action': self.on_document_save_activated,
                    'accels': ('<Control>s',)
                },
                {
                    'name': 'close',
                    'action': self.on_document_close_activated,
                    'accels': ('<Control>w',)
                },
                {
                    'name': 'rename',
                    'action': self.on_document_rename,
                    'accels': ('F2',)
                },
                {
                    'name': 'archive',
                    'action': self.on_document_archive_activated,
                    'accels': (None,)
                },
                {
                    'name': 'unarchive',
                    'action': self.on_document_unarchive_activated,
                    'accels': (None,)
                },
                {
                    'name': 'delete',
                    'action': self.on_document_delete_activated,
                    'accels': ('<Shift>Delete',)
                },
                {
                    'name': 'import',
                    'action': self.on_document_import_activated,
                    'accels': ('<Control>o',)
                },
                {
                    'name': 'export',
                    'action': self.on_export_plaintext,
                    'accels': (None,)
                },
                {
                    'name': 'export-markdown',
                    'action': self.on_export_markdown,
                    'accels': ('<Control><Shift>s',)
                },
                {
                    'name': 'export-html',
                    'action': self.on_export_html,
                    'accels': (None,)
                },
                {
                    'name': 'export-medium',
                    'action': self.on_export_medium,
                    'accels': (None,)
                },
                {
                    'name': 'export-writeas',
                    'action': self.on_export_writeas,
                    'accels': (None,)
                },
                {
                    'name': 'preview',
                    'action': self.on_preview,
                    'accels': ('<Control><Shift>p',)
                },
                # {
                #     'name': 'print',
                #     'action': self.on_print,
                #     'accels': ('<Control>p',)
                # },
                # {
                #     'name': 'search',
                #     'action': self.search_activated,
                #     'accels': ('<Control>k',)
                # },
                {
                    'name': 'zoom_in',
                    'action': self.on_zoom_in,
                    'accels': ('<Control>equal', '<Control>plus')
                },
                {
                    'name': 'zoom_out',
                    'action': self.on_zoom_out,
                    'accels': ('<Control>minus',)
                },
                {
                    'name': 'zoom_default',
                    'action': self.on_zoom_default,
                    'accels': ('<Control>0',)
                },
                {
                    'name': 'search_text',
                    'action': self.search_activated,
                    'accels': ('<Control>f',)
                },
                {
                    'name': 'search_text_next',
                    'action': self.on_text_search_forward,
                    'accels': ('<Control>g',)
                },
                {
                    'name': 'search_text_prev',
                    'action': self.on_text_search_backward,
                    'accels': ('<Control><Shift>g',)
                },
                {
                    'name': 'toggle_archived',
                    'action': self.on_toggle_archive,
                    'accels': (None,)
                },
            ]
        }

        for action_group_key, actions in action_items.items():
            action_group = Gio.SimpleActionGroup()

            for item in actions:
                action = Gio.SimpleAction(name=item['name'])
                action.connect('activate', item['action'])
                self.get_application().set_accels_for_action(f'{action_group_key}.{item["name"]}', item["accels"])
                action_group.add_action(action)

            self.insert_action_group(action_group_key, action_group)

    def on_window_delete_event(self, sender: Gtk.Widget = None) -> None:
        """Save opened document before window is closed.

        """
        try:
            if self.autosave:
                self.editor.save_document()
            else:
                print('Ask for action!')

            if not self.is_maximized():
                self.settings.set_value("window-size", GLib.Variant("ai", self.current_size))
                self.settings.set_value("window-position", GLib.Variant("ai", self.current_position))

        except Exception as e:
            Logger.error(e)

    def on_configure_event(self, window, event: Gdk.EventConfigure):
        if self._configure_timeout_id:
            GLib.source_remove(self._configure_timeout_id)

        self.current_size = window.get_size()
        self.current_position = window.get_position()

    def check_documents_count(self) -> None:
        """Check for documents count in storage and switch between screens
        whether there is at least one document or not.

        """
        if storage.count() > 0:
            self.screens.set_visible_child_name('document-grid')

            last_doc_id = self.settings.get_int('last-document-id')
            if last_doc_id and last_doc_id != -1:
                self.screens.set_visible_child_name('editor-grid')
                self.editor.load_document(last_doc_id)
                self.header.toggle_document_mode()
                self.header.update_title(title=self.editor.document.title)
        else:
            self.toggle_welcome(True)

    def toggle_welcome(self, state=True):
        if state:
            self.screens.set_visible_child_name('welcome-grid')
        else:
            self.screens.set_visible_child_name('document-grid')

    def on_document_close_activated(self, sender: Gtk.Widget, event=None) -> None:
        """Save and close opened document.

        """

        # Should work only in editor mode.
        if self.screens.get_visible_child_name() == 'editor-grid':
            self.screens.set_visible_child_name('document-grid')
            self.editor.unload_document(save=self.autosave)
            self.document_grid.reload_items()
            self.header.toggle_document_mode()
            self.header.update_title()
            self.settings.set_int('last-document-id', -1)

    def on_welcome_activated(self, sender: Welcome, index: int) -> None:
        if index == 0:
            self.on_document_create_activated(sender, index)

    def on_document_item_activated(self, sender: Gtk.Widget, path: Gtk.TreePath) -> None:
        """Activate currently selected document in grid and open it in editor.

        :param sender:
        :param path:
        :return:
        """
        model_iter = self.document_grid.model.get_iter(path)
        doc_id = self.document_grid.model.get_value(model_iter, 3)
        Logger.debug('Activated Document.Id %s', doc_id)

        self.document_activate(doc_id)

    def document_activate(self, doc_id):
        editor = self.screens.get_child_by_name('editor-grid')
        editor.load_document(doc_id)
        self.screens.set_visible_child_name('editor-grid')
        self.header.toggle_document_mode()
        self.header.update_title(title=editor.document.title)
        self.settings.set_int('last-document-id', doc_id)

    def on_document_create_activated(self, sender: Gtk.Widget = None, event=None) -> None:
        """Create new document named 'Nameless' :) and activate it in editor.

        :param sender:
        :param event:
        :return:
        """
        # If document already loaded to editor we need to close it before create new one
        if self.editor.document:
            self.on_document_close_activated(sender, event)

        self.editor.create_document()
        self.screens.set_visible_child_name('editor-grid')
        self.header.toggle_document_mode()
        self.header.update_title(title=self.editor.document.title)

    def on_document_save_activated(self, sender: Gtk.Widget = None, event=None) -> None:
        """Save opened document to storage.

        :param sender:
        :param event:
        :return:
        """
        self.editor.save_document()

    def on_document_import_activated(self, sender, event):
        dialog = Gtk.FileChooserNative.new(
            _("Import files into Norka"),
            self,
            Gtk.FileChooserAction.OPEN
        )

        filter_markdown = Gtk.FileFilter()
        filter_markdown.set_name(_("Text Files"))
        filter_markdown.add_mime_type("text/plain")
        dialog.add_filter(filter_markdown)
        dialog_result = dialog.run()

        if dialog_result == Gtk.ResponseType.ACCEPT:
            file_path = dialog.get_filename()
            self.import_document(file_path)

        dialog.destroy()

    def on_document_import(self, sender: Gtk.Widget = None, file_path: str = None) -> None:
        if self.import_document(file_path=file_path):
            self.check_documents_count()

    def import_document(self, file_path: str) -> bool:
        """Import files from filesystem.
        Creates new document in storage and fill it with file's contents.

        :param sender:
        :param filepath: path to file to import
        """
        if not os.path.exists(file_path):
            return False

        self.header.show_spinner(True)
        try:
            with open(file_path, 'r') as _file:
                lines = _file.readlines()
                filename = os.path.basename(file_path)[:file_path.rfind('.')]

                _doc = Document(title=filename, content='\r\n'.join(lines))
                _doc_id = storage.add(_doc)

                self.document_grid.reload_items()
            return True
        except Exception as e:
            print(e)
            return False
        finally:
            self.header.show_spinner(False)

    def on_document_rename(self, sender: Gtk.Widget = None, event=None) -> None:
        """Rename currently selected document.
        Show rename dialog and update document's title
        if user puts new one in the entry.

        :param sender:
        :param event:
        :return:
        """
        doc = self.document_grid.selected_document or self.editor.document
        if not doc:
            return

        found, rect = self.document_grid.view.get_cell_rect(self.document_grid.selected_path)
        popover = RenamePopover(self.overlay, doc.title)
        popover.set_pointing_to(rect)
        popover.connect('activate', self.on_document_rename_activated)
        popover.popup()

    def on_document_rename_activated(self, sender: Gtk.Widget, title: str):
        sender.destroy()

        doc = self.document_grid.selected_document or self.editor.document
        if not doc:
            return

        if storage.update(doc_id=doc.document_id, data={'title': title}):
            self.document_grid.reload_items()

    def on_document_archive_activated(self, sender: Gtk.Widget = None, event=None) -> None:
        """Marks document as archived. Recoverable.

        :param sender:
        :param event:
        :return:
        """
        doc = self.document_grid.selected_document
        if doc:
            if storage.update(doc_id=doc.document_id, data={'archived': True}):
                self.check_documents_count()
                self.document_grid.reload_items()

    def on_document_unarchive_activated(self, sender: Gtk.Widget = None, event=None) -> None:
        """Unarchive document.

        :param sender:
        :param event:
        :return:
        """
        doc = self.document_grid.selected_document
        if doc:
            if storage.update(doc_id=doc.document_id, data={'archived': False}):
                self.check_documents_count()
                self.document_grid.reload_items()

    def on_document_delete_activated(self, sender: Gtk.Widget = None, event=None) -> None:
        """Permanently remove document from storage. Non-recoverable.

        :param sender:
        :param event:
        :return:
        """
        doc = self.document_grid.selected_document

        prompt = MessageDialog(
            f"Permanently delete “{doc.title}”?",
            "Deleted items are not sent to Archive and not recoverable at all",
            "dialog-warning",
        )

        if doc:
            result = prompt.run()
            prompt.destroy()

            if result == Gtk.ResponseType.APPLY and storage.delete(doc.document_id):
                self.document_grid.reload_items()
                self.check_documents_count()

    def on_export_plaintext(self, sender: Gtk.Widget = None, event=None) -> None:
        """Export document from storage to local files or web-services.

        :param sender:
        :param event:
        :return:
        """
        doc = self.document_grid.selected_document or self.editor.document
        if not doc:
            return

        dialog = ExportFileDialog(
            "Export document to file",
            self,
            Gtk.FileChooserAction.SAVE
        )
        dialog.set_current_name(doc.title)
        export_format = ExportFormat.PlainText
        dialog.set_format(export_format)
        dialog_result = dialog.run()

        if dialog_result == Gtk.ResponseType.ACCEPT:
            self.header.show_spinner(True)
            basename, ext = os.path.splitext(dialog.get_filename())
            if ext not in export_format[1]:
                ext = export_format[1][0][1:]

            GObjectWorker.call(Exporter.export_plaintext,
                               (basename + ext, doc),
                               callback=self.on_export_callback)

        dialog.destroy()

    def on_export_markdown(self, sender: Gtk.Widget = None, event=None) -> None:
        """Export document from storage to local files or web-services.

        :param sender:
        :param event:
        :return:
        """
        doc = self.document_grid.selected_document or self.editor.document
        if not doc:
            return

        dialog = ExportFileDialog(
            "Export document to file",
            self,
            Gtk.FileChooserAction.SAVE
        )
        dialog.set_current_name(doc.title)
        export_format = ExportFormat.Markdown
        dialog.set_format(export_format)
        dialog_result = dialog.run()

        if dialog_result == Gtk.ResponseType.ACCEPT:
            self.header.show_spinner(True)
            basename, ext = os.path.splitext(dialog.get_filename())
            if ext not in export_format[1]:
                ext = export_format[1][0][1:]

            GObjectWorker.call(Exporter.export_markdown,
                               (basename + ext, doc),
                               callback=self.on_export_callback)

        dialog.destroy()

    def on_export_html(self, sender: Gtk.Widget = None, event=None) -> None:
        """Export document from storage to local files or web-services.

        :param sender:
        :param event:
        :return:
        """
        doc = self.document_grid.selected_document or self.editor.document
        if not doc:
            return

        dialog = ExportFileDialog(
            _("Export document to file"),
            self,
            Gtk.FileChooserAction.SAVE
        )
        dialog.set_current_name(doc.title)
        export_format = ExportFormat.Html
        dialog.set_format(export_format)
        dialog_result = dialog.run()

        if dialog_result == Gtk.ResponseType.ACCEPT:
            self.header.show_spinner(True)
            basename, ext = os.path.splitext(dialog.get_filename())
            if ext not in export_format[1]:
                ext = export_format[1][0][1:]

            GObjectWorker.call(Exporter.export_html,
                               (basename + ext, doc),
                               callback=self.on_export_callback)

        dialog.destroy()

    def on_export_callback(self, result):
        self.header.show_spinner(False)
        self.disconnect_toast()
        if result:
            self.toast.set_title(_("Document exported."))
            self.toast.set_default_action(_("Open folder"))
            self.uri_to_open = f"file://{os.path.dirname(result)}"
            self.toast.connect("default-action", self.open_uri)
            self.toast.send_notification()
        else:
            self.toast.set_title(_("Export goes wrong."))
            self.toast.send_notification()

    def on_export_medium(self, sender: Gtk.Widget = None, event=None) -> None:
        """Configure Medium client and export document asynchronously

        :param sender:
        :param event:
        :return:
        """

        doc = self.document_grid.selected_document or self.editor.document
        if not doc:
            return

        token = self.settings.get_string("medium-personal-token")
        user_id = self.settings.get_string("medium-user-id")

        if not token or not user_id:
            self.toast.set_title(_("You need to set Medium token in Preferences -> Export"))
            self.toast.set_default_action(_("Configure"))
            self.disconnect_toast()
            self.toast.connect("default-action", self.get_application().on_preferences)
            self.toast.send_notification()

        else:
            self.header.show_spinner(True)
            self.medium_client.set_token(token)
            GObjectWorker.call(self.medium_client.create_post,
                               args=(user_id, doc, PublishStatus.DRAFT),
                               callback=self.on_export_medium_callback)

    def on_export_medium_callback(self, result):

        self.header.show_spinner(False)
        if result:
            self.toast.set_title(_("Document successfully exported!"))
            self.toast.set_default_action(_("View"))
            self.uri_to_open = result["url"]
            self.disconnect_toast()
            self.toast.connect("default-action", self.open_uri)
        else:
            self.toast.set_title(_("Export failed!"))
            self.toast.set_default_action(None)
        self.toast.send_notification()

    def on_export_writeas(self, sender: Gtk.Widget = None, event=None) -> None:
        """Configure Write.as client and export document asynchronously

        :param sender:
        :param event:
        :return:
        """

        doc = self.document_grid.selected_document or self.editor.document
        if not doc:
            return

        token = self.settings.get_string("writeas-access-token")

        if not token:
            self.toast.set_title("You have to login to Write.as in Preferences -> Export")
            self.toast.set_default_action("Configure")
            self.disconnect_toast()
            self.toast.connect("default-action", self.get_application().on_preferences)
            self.toast.send_notification()

        else:
            self.header.show_spinner(True)
            self.writeas_client.set_token(access_token=token)
            GObjectWorker.call(self.writeas_client.create_post,
                               args=(doc,),
                               callback=self.on_export_writeas_callback)

    def on_export_writeas_callback(self, result):
        self.header.show_spinner(False)
        if result:
            self.toast.set_title(_("Document successfully exported!"))
            self.toast.set_default_action(_("View"))
            self.disconnect_toast()
            self.uri_to_open = f"https://write.as/{result['id']}"
            self.toast.connect("default-action", self.open_uri)
        else:
            self.toast.set_title(_("Export failed."))
            self.toast.set_default_action(None)
        self.toast.send_notification()

    def search_activated(self, sender, event=None):
        if self.screens.get_visible_child_name() == 'document-grid':
            self.on_document_search_activated(sender, event)
        elif self.screens.get_visible_child_name() == 'editor-grid':
            self.on_text_search_activated(sender, event)
        else:
            pass

    def on_document_search_activated(self, sender: Gtk.Widget = None, event=None) -> None:
        """Open search dialog to find a documents

        :param sender:
        :param event:
        :return:
        """
        dialog = QuickFindDialog()
        response = dialog.run()

        if response == Gtk.ResponseType.APPLY and dialog.document_id:
            self.document_activate(dialog.document_id)
        dialog.destroy()

    def on_text_search_activated(self, sender: Gtk.Widget = None, event=None) -> None:
        """Open search dialog to find text in a documents
        """
        self.editor.on_search_text_activated(sender, event)

    def on_text_search_forward(self, sender: Gtk.Widget = None, event=None) -> None:
        if self.screens.get_visible_child_name() == 'editor-grid' \
                and self.editor.search_revealer.get_child_revealed():
            self.editor.search_forward(sender=sender, event=event)

    def on_text_search_backward(self, sender: Gtk.Widget = None, event=None) -> None:
        if self.screens.get_visible_child_name() == 'editor-grid' \
                and self.editor.search_revealer.get_child_revealed():
            self.editor.search_backward(sender=sender, event=event)

    def on_zoom_in(self, sender, event) -> None:
        self.zooming(Gdk.ScrollDirection.UP)

    def on_zoom_out(self, sender, event) -> None:
        self.zooming(Gdk.ScrollDirection.DOWN)

    def on_zoom_default(self, sender, event) -> None:
        self.settings.set_int('zoom', 100)
        self.settings.set_string("font", f'{FONT_SIZE_FAMILY} {FONT_SIZE_DEFAULT}')

    def toggle_spellcheck(self, state: bool) -> None:
        self.editor.set_spellcheck(state)

    def set_style_scheme(self, scheme_id: str) -> None:
        self.editor.set_style_scheme(scheme_id)

    def set_autoindent(self, autoindent: bool) -> None:
        self.editor.view.set_auto_indent(autoindent)

    def set_tabs_spaces(self, state: bool) -> None:
        self.editor.view.set_insert_spaces_instead_of_tabs(state)

    def set_indent_width(self, size: int) -> None:
        self.editor.view.set_tab_width(size)

    def zooming(self, direction: Gdk.ScrollDirection) -> None:

        font = self.get_current_font()
        zoom = self.settings.get_int('zoom')

        if direction == Gdk.ScrollDirection.UP:
            zoom += 10
        elif direction == Gdk.ScrollDirection.DOWN:
            zoom -= 10

        font_size = int(FONT_SIZE_DEFAULT * zoom / 100)
        if font_size < FONT_SIZE_MIN or font_size > FONT_SIZE_MAX:
            return

        self.settings.set_string('font', f'{font} {font_size}')
        self.settings.set_int('zoom', zoom)

    def get_current_font(self) -> str:
        font = self.settings.get_string("font")
        return font[:font.rfind(" ")]

    def get_current_font_size(self) -> float:
        font = self.settings.get_string("font")
        return float(font[font.rfind(" ") + 1:])

    def on_toggle_archive(self, action: Gio.SimpleAction, name: str = None):
        show_archived = self.header.archived_button.get_active()
        self.document_grid.show_archived = show_archived
        self.document_grid.reload_items()

        self.toggle_welcome(not show_archived and storage.count(with_archived=show_archived) == 0)

    def open_uri(self, event):
        if self.uri_to_open:
            Gtk.show_uri_on_window(None, self.uri_to_open, Gdk.CURRENT_TIME)
            self.uri_to_open = None

    def disconnect_toast(self):
        """Disconnect toast action. Weird way, need ti rewrite it"""
        try:
            self.toast.disconnect_by_func(self.get_application().on_preferences)
        except:
            pass

        try:
            self.toast.disconnect_by_func(self.open_uri)
        except:
            pass

    def on_preview(self, sender, event):
        doc = self.document_grid.selected_document or self.editor.document

        if not self.preview:
            # create preview window
            text = doc.content if doc else None
            self.preview = Preview(parent=self, text=text)
            # connect signal handlers
            # self.editor.scrolled.get_vscrollbar().connect('value-changed', self.scroll_preview)
            self.editor.buffer.connect('changed', self.preview.buffer_changed)
            self.editor.connect('document-load', self.preview.show_preview)
            self.editor.connect('document-close', self.preview.show_empty_page)
            self.preview.connect('destroy', self.on_preview_close)
            self.preview.show_all()
        else:
            self.preview.present()

        if doc:
            self.preview.show_preview(self)

    def scroll_preview(self, range: Gtk.Range):
        adjustment = range.get_adjustment()
        percent = adjustment.get_value() / adjustment.get_upper()
        print(f'Scrolled to: {percent * 100}% / {adjustment.get_lower()} / {adjustment.get_upper()}')
        self.preview.scroll_to(percent)

    def on_preview_close(self, sender):
        self.preview = None
Exemplo n.º 4
0
class EditorView(Gtk.Grid):
    __gtype_name__ = 'EditorView'

    def __init__(self):
        super().__init__()

        self.document = None

        self.view = Editor()

        self.scrolled = Gtk.ScrolledWindow(hexpand=True, vexpand=True)
        self.scrolled.get_style_context().add_class('scrolled-editor')
        self.scrolled.add(self.view)

        # SearchBar
        self.search_bar = SearchBar()
        self.search_revealer = Gtk.Revealer()
        self.search_revealer.add(self.search_bar)
        self.search_bar.connect('find-changed', self.do_next_match)
        self.search_bar.connect('find-next', self.do_next_match)
        self.search_bar.connect('find-prev', self.do_previous_match)
        self.search_bar.connect('stop-search', self.do_stop_search)

        content_grid = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        content_grid.pack_start(self.search_revealer, False, True, 0)
        content_grid.pack_start(self.scrolled, True, True, 0)
        content_grid.show_all()

        self.overlay = Gtk.Overlay()
        self.overlay.add(content_grid)
        self.stats_overlay = Granite.WidgetsOverlayBar.new(self.overlay)

        self.stats_handler = StatsHandler(overlay_bar=self.stats_overlay,
                                          buffer=self.buffer)
        self.stats_handler.update_default_stat()

        self.add(self.overlay)

        self.font_desc = Pango.FontDescription()
        self.spellchecker = GtkSpell.Checker()

        self.search_settings = GtkSource.SearchSettings(wrap_around=True)
        self.search_context = GtkSource.SearchContext(
            buffer=self.buffer, settings=self.search_settings)
        self.search_iter = None

    def create_document(self, title: str = 'Nameless') -> None:
        """Create new document and put it to storage

        :param title: title of the document. Defaults to 'Nameless'
        :type title: str
        :return: None
        """
        self.document = Document(title=title)
        self.document.document_id = storage.add(self.document)
        self.view.grab_focus()

    def load_document(self, doc_id: int) -> None:
        """Load :model:`Document` from storage with given `doc_id`.

        :param doc_id: id of the document to load
        :type doc_id: int
        :return: None
        """
        self.document = storage.get(doc_id)

        self.buffer.set_text(self.document.content)
        self.buffer.end_not_undoable_action()
        self.buffer.set_modified(False)
        self.buffer.place_cursor(self.buffer.get_start_iter())
        self.view.grab_focus()

    def unload_document(self, save=True) -> None:
        """Save current document and clear text buffer

        :return: None
        """
        # self.stats_overlay.destroy()
        if not self.document:
            return

        if save:
            self.save_document()
        self.buffer.set_text('')
        self.document = None
        self.hide_search_bar()

    def hide_search_bar(self):
        self.search_revealer.set_reveal_child(False)
        self.search_bar.stop_search()

    def load_file(self, path: str) -> bool:
        self.buffer.begin_not_undoable_action()
        try:
            txt = open(path).read()
        except Exception as e:
            Logger.error(e)
            return False

        self.buffer.set_text(txt)
        self.buffer.end_not_undoable_action()
        self.buffer.set_modified(False)
        self.buffer.place_cursor(self.buffer.get_start_iter())

        return True

    def save_document(self) -> bool:
        if not self.document:
            return False

        text = self.buffer.get_text(self.buffer.get_start_iter(),
                                    self.buffer.get_end_iter(), True)
        if self.document.title in ('', 'Nameless'):
            try:
                self.document.title = text.partition('\n')[0].lstrip(
                    ' #') or "Nameless"
            except TypeError:
                pass

        if storage.update(self.document.document_id, {
                "content": text,
                'title': self.document.title
        }):
            self.document.content = text
            Logger.debug('Document %s saved', self.document.document_id)
            return True

    def get_text(self) -> str:
        return self.buffer.get_text(self.buffer.get_start_iter(),
                                    self.buffer.get_end_iter(), True).strip()

    def on_key_release_event(self, text_view: GtkSource.View,
                             event: Gdk.EventKey) -> None:
        """Handle release event and iterate markdown list markup

        :param text_view: widget emitted the event
        :param event: key release event
        :return:
        """
        buffer = text_view.get_buffer()
        if event.keyval == Gdk.KEY_Return:
            buffer.begin_user_action()
            curr_iter = buffer.get_iter_at_mark(buffer.get_insert())
            curr_line = curr_iter.get_line()
            if curr_line > 0:
                # Get prev line text
                prev_line = curr_line - 1
                prev_iter = buffer.get_iter_at_line(prev_line)
                prev_line_text = buffer.get_text(prev_iter, curr_iter, False)
                # Check if prev line starts from markdown list chars
                match = re.search(r"^(\s){,4}([0-9]*.|-|\*|\+)\s+",
                                  prev_line_text)
                if match:
                    sign = match.group(2)
                    if re.match(r'^[0-9]+.', sign):
                        # ordered list should increment number
                        sign = str(int(sign[:-1]) + 1) + '.'

                    buffer.insert_at_cursor(sign + ' ')

            buffer.end_user_action()

    def on_search_text_activated(self, sender: Gtk.Widget = None, event=None):
        state = self.search_revealer.get_child_revealed()
        if not state:
            # If there is selected text in view put it as search text
            bounds = self.buffer.get_selection_bounds()
            if bounds:
                text = bounds[0].get_text(bounds[1])
                self.search_bar.search_entry.set_text(text)

            self.search_revealer.set_reveal_child(True)
            self.search_bar.search_entry.grab_focus()

    def set_spellcheck(self, spellcheck: bool) -> None:
        if spellcheck:
            self.spellchecker.attach(self.view)
        else:
            self.spellchecker.detach()

    def set_style_scheme(self, scheme_id: str) -> None:
        scheme = GtkSource.StyleSchemeManager().get_scheme(scheme_id)
        self.buffer.set_style_scheme(scheme)

        try:
            bgcolor = scheme.get_style('text').props.background
        except AttributeError:
            bgcolor = '#fff'

        css_provider = Gtk.CssProvider()
        css_provider.load_from_data(
            f'.scrolled-editor {{background: {bgcolor}}}'.encode('ascii'))
        self.scrolled.get_style_context().add_provider(
            css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)

    def update_font(self, font: str) -> None:
        self.font_desc = Pango.FontDescription.from_string(font)
        self.view.override_font(self.font_desc)

    def do_stop_search(self, event: Gdk.Event = None) -> None:
        self.search_revealer.set_reveal_child(False)
        self.view.grab_focus()

    def search(self, text: str, forward: bool = True) -> bool:

        self.search_context.set_highlight(False)

        # Can't search anything in an inexistant buffer and/or without anything to search.
        if any([not self.buffer, not self.get_text(), not text]):
            self.search_bar.search_entry.props.primary_icon_name = "edit-find-symbolic"
            return False

        self.search_context.set_highlight(True)

        if self.search_settings.get_search_text() != text:
            self.search_settings.set_search_text(text)
            self.search_iter = self.buffer.get_iter_at_mark(
                self.buffer.get_insert())
        elif self.search_iter:
            self.search_iter.forward_char()
        else:
            return False

        if forward:
            found, self.search_iter = self.search_for_iter(self.search_iter)
        else:
            found, self.search_iter = self.search_for_iter_backward(
                self.search_iter)

        if found:
            self.search_bar.search_entry.get_style_context().remove_class(
                Gtk.STYLE_CLASS_ERROR)
            self.search_bar.search_entry.props.primary_icon_name = "edit-find-symbolic"
        else:
            self.search_iter = self.buffer.get_start_iter()
            found, end_iter = self.search_for_iter(self.search_iter)
            if found:
                self.search_bar.search_entry.get_style_context().remove_class(
                    Gtk.STYLE_CLASS_ERROR)
                self.search_bar.search_entry.props.primary_icon_name = "edit-find-symbolic"
            else:
                self.search_iter.set_offset(-1)
                self.buffer.select_range(self.search_iter, self.search_iter)
                self.search_bar.search_entry.get_style_context().add_class(
                    Gtk.STYLE_CLASS_ERROR)
                self.search_bar.search_entry.props.primary_icon_name = "dialog-error-symbolic"
                return False

        return True

    def search_for_iter(self, start_iter) -> (bool, Gtk.TextIter):
        found, start_iter, end_iter, has_wrapped = self.search_context.forward2(
            start_iter)
        if found:
            self.scroll_to(start_iter, end_iter)
        return found, end_iter

    def search_for_iter_backward(self, start_iter) -> (bool, Gtk.TextIter):
        found, start_iter, end_iter, has_wrapped = self.search_context.backward2(
            start_iter)
        if found:
            self.scroll_to(start_iter, end_iter)
        return found, start_iter

    def do_next_match(self, sender, text: str) -> bool:
        return self.search(text)

    def do_previous_match(self, sender, text: str) -> bool:
        return self.search(text, False)

    def search_forward(self, sender=None, event=None) -> bool:
        return self.do_next_match(sender,
                                  self.search_bar.search_entry.get_text())

    def search_backward(self, sender=None, event=None) -> bool:
        return self.do_previous_match(sender,
                                      self.search_bar.search_entry.get_text())

    def scroll_to(self, start_iter, end_iter):

        mark = self.buffer.create_mark(None, start_iter, False)
        self.view.scroll_to_mark(mark, 0, False, 0, 0)

        self.buffer.place_cursor(start_iter)
        self.buffer.select_range(start_iter, end_iter)
Exemplo n.º 5
0
class NorkaWindow(Gtk.ApplicationWindow):
    __gtype_name__ = 'NorkaWindow'

    def __init__(self, settings: Gio.Settings, **kwargs):
        super().__init__(**kwargs)

        self.set_default_icon(
            Pixbuf.new_from_resource_at_scale(
                '/com/github/tenderowl/norka/icons/com.github.tenderowl.norka.svg',
                128, 128, True))
        self.settings = settings
        self._configure_timeout_id = None

        self.current_size = (786, 520)
        self.resize(*self.settings.get_value('window-size'))
        self.connect('configure-event', self.on_configure_event)
        self.connect('destroy', self.on_window_delete_event)

        # Init actions
        self.init_actions()

        # Make a header
        self.header = Header(self.settings)
        self.set_titlebar(self.header)
        self.header.show()

        # Init screens
        self.welcome_grid = Welcome()
        self.welcome_grid.connect('activated', self.on_welcome_activated)

        self.document_grid = DocumentGrid()
        self.document_grid.connect('document-create',
                                   self.on_document_create_activated)
        self.document_grid.view.connect('item-activated',
                                        self.on_document_item_activated)

        self.editor = Editor()

        self.screens = Gtk.Stack()
        self.screens.set_transition_duration(400)
        self.screens.set_transition_type(
            Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)

        self.screens.add_named(self.welcome_grid, 'welcome-grid')
        self.screens.add_named(self.document_grid, 'document-grid')
        self.screens.add_named(self.editor, 'editor-grid')

        self.screens.show_all()

        self.add(self.screens)

        # If here's at least one document in storage
        # then show documents grid
        self.check_documents_count()

        # Pull the Settings
        self.toggle_spellcheck(self.settings.get_boolean('spellcheck'))
        self.autosave = self.settings.get_boolean('autosave')
        self.set_autoindent(self.settings.get_boolean('autoindent'))
        self.set_tabs_spaces(
            self.settings.get_boolean('spaces-instead-of-tabs'))
        self.set_indent_width(self.settings.get_int('indent-width'))
        self.set_style_scheme(self.settings.get_string('stylescheme'))
        self.editor.update_font(self.settings.get_string('font'))

    def init_actions(self) -> None:
        """Initialize app-wide actions.

        """
        action_items = {
            'document': [
                {
                    'name': 'create',
                    'action': self.on_document_create_activated,
                    'accels': ('<Control>n', )
                },
                {
                    'name': 'save',
                    'action': self.on_document_save_activated,
                    'accels': ('<Control>s', )
                },
                {
                    'name': 'close',
                    'action': self.on_document_close_activated,
                    'accels': ('<Control>w', )
                },
                {
                    'name': 'rename',
                    'action': self.on_document_rename_activated,
                    'accels': ('F2', )
                },
                {
                    'name': 'archive',
                    'action': self.on_document_archive_activated,
                    'accels': ('Delete', )
                },
                {
                    'name': 'delete',
                    'action': self.on_document_delete_activated,
                    'accels': ('<Shift>Delete', )
                },
                {
                    'name': 'export',
                    'action': self.on_document_export_activated,
                    'accels': ('<Control><Shift>s', )
                },
                {
                    'name': 'search',
                    'action': self.on_document_search_activated,
                    'accels': ('<Control>k', )
                },
                {
                    'name': 'zoom_in',
                    'action': self.on_zoom_in,
                    'accels': ('<Control>equal', '<Control>plus')
                },
                {
                    'name': 'zoom_out',
                    'action': self.on_zoom_out,
                    'accels': ('<Control>minus', )
                },
                {
                    'name': 'zoom_default',
                    'action': self.on_zoom_default,
                    'accels': ('<Control>0', )
                },
            ]
        }

        for action_group_key, actions in action_items.items():
            action_group = Gio.SimpleActionGroup()

            for item in actions:
                action = Gio.SimpleAction(name=item['name'])
                action.connect('activate', item['action'])
                self.get_application().set_accels_for_action(
                    f'{action_group_key}.{item["name"]}', item["accels"])
                action_group.add_action(action)

            self.insert_action_group(action_group_key, action_group)

    def on_window_delete_event(self, sender: Gtk.Widget = None) -> None:
        """Save opened document before window is closed.

        """
        try:
            if self.autosave:
                self.editor.save_document()
            else:
                print('Ask for action!')

            if not self.is_maximized():
                self.settings.set_value("window-size",
                                        GLib.Variant("ai", self.current_size))
                self.settings.set_value(
                    "window-position", GLib.Variant("ai",
                                                    self.current_position))

        except Exception as e:
            Logger.error(e)

    def on_configure_event(self, window, event: Gdk.EventConfigure):
        if self._configure_timeout_id:
            GLib.source_remove(self._configure_timeout_id)

        self.current_size = window.get_size()
        self.current_position = window.get_position()

    def check_documents_count(self) -> None:
        """Check for documents count in storage and switch between screens
        whether there is at least one document or not.

        """
        if storage.count() > 0:
            self.screens.set_visible_child_name('document-grid')

            last_doc_id = self.settings.get_int('last-document-id')
            if last_doc_id and last_doc_id != -1:
                self.screens.set_visible_child_name('editor-grid')
                self.editor.load_document(last_doc_id)
                self.header.toggle_document_mode()
                self.header.update_title(title=self.editor.document.title)
        else:
            self.screens.set_visible_child_name('welcome-grid')

    def on_document_close_activated(self,
                                    sender: Gtk.Widget,
                                    event=None) -> None:
        """Save and close opened document.

        """
        self.screens.set_visible_child_name('document-grid')
        self.editor.unload_document(save=self.autosave)
        self.document_grid.reload_items()
        self.header.toggle_document_mode()
        self.header.update_title()
        self.settings.set_int('last-document-id', -1)

    def on_welcome_activated(self, sender: Welcome, index: int) -> None:
        if index == 0:
            self.on_document_create_activated(sender, index)

    def on_document_item_activated(self, sender: Gtk.Widget,
                                   path: Gtk.TreePath) -> None:
        """Activate currently selected document in grid and open it in editor.

        :param sender:
        :param path:
        :return:
        """
        model_iter = self.document_grid.model.get_iter(path)
        doc_id = self.document_grid.model.get_value(model_iter, 3)
        Logger.debug('Activated Document.Id %s', doc_id)

        editor = self.screens.get_child_by_name('editor-grid')
        editor.load_document(doc_id)
        self.screens.set_visible_child_name('editor-grid')

        self.header.toggle_document_mode()
        self.header.update_title(
            title=self.document_grid.model.get_value(model_iter, 1))
        self.settings.set_int('last-document-id', doc_id)

    def on_document_create_activated(self,
                                     sender: Gtk.Widget = None,
                                     event=None) -> None:
        """Create new document named 'Nameless' :) and activate it in editor.

        :param sender:
        :param event:
        :return:
        """
        self.editor.create_document()
        self.screens.set_visible_child_name('editor-grid')
        self.header.toggle_document_mode()
        self.header.update_title(title=self.editor.document.title)

    def on_document_save_activated(self,
                                   sender: Gtk.Widget = None,
                                   event=None) -> None:
        """Save opened document to storage.

        :param sender:
        :param event:
        :return:
        """
        self.editor.save_document()

    def on_document_rename_activated(self,
                                     sender: Gtk.Widget = None,
                                     event=None) -> None:
        """Rename currently selected document.
        Show rename dialog and update document's title
        if user puts new one in the entry.

        :param sender:
        :param event:
        :return:
        """
        doc = self.document_grid.selected_document
        if doc:
            popover = RenameDialog(doc.title)
            response = popover.run()
            try:
                if response == Gtk.ResponseType.APPLY:
                    new_title = popover.entry.get_text()

                    if storage.update(doc_id=doc._id,
                                      data={'title': new_title}):
                        self.document_grid.reload_items()
            except Exception as e:
                Logger.debug(e)
            finally:
                popover.destroy()

    def on_document_archive_activated(self,
                                      sender: Gtk.Widget = None,
                                      event=None) -> None:
        """Marks document as archived. Recoverable.

        :param sender:
        :param event:
        :return:
        """
        doc = self.document_grid.selected_document
        if doc:
            if storage.update(doc_id=doc._id, data={'archived': True}):
                self.check_documents_count()
                self.document_grid.reload_items()

    def on_document_delete_activated(self,
                                     sender: Gtk.Widget = None,
                                     event=None) -> None:
        """Permanently remove document from storage. Non-recoverable.

        :param sender:
        :param event:
        :return:
        """
        doc = self.document_grid.selected_document

        prompt = MessageDialog(
            f"Permanently delete “{doc.title}”?",
            "Deleted items are not sent to Archive and not recoverable at all",
            "dialog-warning",
        )

        if doc:
            result = prompt.run()
            prompt.destroy()

            if result == Gtk.ResponseType.APPLY and storage.update(
                    doc_id=doc._id, data={'archived': True}):
                self.document_grid.reload_items()
                self.check_documents_count()

    def on_document_export_activated(self,
                                     sender: Gtk.Widget = None,
                                     event=None) -> None:
        """Export document from storage to local files or web-services.

        :param sender:
        :param event:
        :return:
        """
        doc = self.document_grid.selected_document or self.editor.document
        if not doc:
            return

        dialog = Gtk.FileChooserDialog(
            "Export document to file",
            self,
            Gtk.FileChooserAction.SAVE,
            (
                Gtk.STOCK_CANCEL,
                Gtk.ResponseType.CANCEL,
                Gtk.STOCK_SAVE,
                Gtk.ResponseType.ACCEPT,
            ),
            # use_header_bar=1
        )
        dialog.set_current_name(doc.title)

        filter_markdown = Gtk.FileFilter()
        filter_markdown.set_name("Markdown")
        filter_markdown.add_pattern("*.md")
        filter_markdown.add_pattern("*.MD")
        filter_markdown.add_pattern("*.markdown")
        dialog.add_filter(filter_markdown)
        dialog.set_do_overwrite_confirmation(True)

        extensions = (
            '.md',
            '.markdown',
        )

        if dialog.run() == Gtk.ResponseType.ACCEPT:
            file_name = dialog.get_filename()
            ex_ok = False

            for extension in extensions:
                if file_name.lower().endswith(extension):
                    ex_ok = True
                    break
            if not ex_ok and extensions:
                file_name += extensions[0]

            with open(file_name, "w+", encoding="utf-8") as output:
                data = self.editor.get_text()
                output.write(data)
        dialog.destroy()

    def on_document_search_activated(self,
                                     sender: Gtk.Widget = None,
                                     event=None) -> None:
        """Open search dialog.

        :param sender:
        :param event:
        :return:
        """
        # dialog = SearchDialog()
        # dialog.run()
        # dialog.destroy()
        pass

    def on_zoom_in(self, sender, event) -> None:
        self.zooming(Gdk.ScrollDirection.UP)

    def on_zoom_out(self, sender, event) -> None:
        self.zooming(Gdk.ScrollDirection.DOWN)

    def on_zoom_default(self, sender, event) -> None:
        self.settings.set_int('zoom', 100)
        self.settings.set_string("font",
                                 f'{FONT_SIZE_FAMILY} {FONT_SIZE_DEFAULT}')

    def toggle_spellcheck(self, state: bool) -> None:
        self.editor.set_spellcheck(state)

    def set_style_scheme(self, scheme_id: str) -> None:
        self.editor.set_style_scheme(scheme_id)

    def set_autoindent(self, autoindent: bool) -> None:
        self.editor.view.set_auto_indent(autoindent)

    def set_tabs_spaces(self, state: bool) -> None:
        self.editor.view.set_insert_spaces_instead_of_tabs(state)

    def set_indent_width(self, size: int) -> None:
        self.editor.view.set_tab_width(size)

    def zooming(self, direction: Gdk.ScrollDirection) -> None:

        font = self.get_current_font()
        zoom = self.settings.get_int('zoom')

        if direction == Gdk.ScrollDirection.UP:
            zoom += 10
        elif direction == Gdk.ScrollDirection.DOWN:
            zoom -= 10

        font_size = int(FONT_SIZE_DEFAULT * zoom / 100)
        if font_size < FONT_SIZE_MIN or font_size > FONT_SIZE_MAX:
            return

        self.settings.set_string('font', f'{font} {font_size}')
        self.settings.set_int('zoom', zoom)

    def get_current_font(self) -> str:
        font = self.settings.get_string("font")
        return font[:font.rfind(" ")]

    def get_current_font_size(self) -> float:
        font = self.settings.get_string("font")
        return float(font[font.rfind(" ") + 1:])
Exemplo n.º 6
0
    def __init__(self, settings: Gio.Settings, storage: Storage, **kwargs):
        super().__init__(**kwargs)

        self.set_default_icon(
            Pixbuf.new_from_resource_at_scale(
                f'{RESOURCE_PREFIX}/icons/com.github.tenderowl.norka.svg', 128,
                128, True))
        self.settings = settings
        self.storage = storage
        self._configure_timeout_id = None
        self.preview = None
        self.extended_stats_dialog = None

        self.apply_styling()

        self.current_size = (786, 520)
        self.resize(*self.settings.get_value('window-size'))

        hints = Gdk.Geometry()
        hints.min_width = 554
        hints.min_height = 435
        self.set_geometry_hints(None, hints, Gdk.WindowHints.MIN_SIZE)
        self.connect('configure-event', self.on_configure_event)
        self.connect('destroy', self.on_window_delete_event)

        # Export clients
        self.medium_client = Medium()
        self.writeas_client = Writeas()
        self.uri_to_open = None

        # Make a header
        self.header = Header(self.settings)
        # self.set_titlebar(self.header)
        self.header.show()

        # Init screens
        self.welcome_grid = Welcome()
        # self.welcome_grid.connect('activated', self.on_welcome_activated)
        self.welcome_grid.connect('document-import', self.on_document_import)

        self.document_grid = DocumentGrid(self.settings, storage=self.storage)
        self.document_grid.connect('path-changed', self.on_path_changed)
        self.document_grid.connect('document-create',
                                   self.on_document_create_activated)
        self.document_grid.connect('document-import', self.on_document_import)
        self.document_grid.connect('rename-folder',
                                   self.on_folder_rename_activated)
        self.document_grid.view.connect('item-activated',
                                        self.on_document_item_activated)

        self.editor = Editor(self.storage, self.settings)
        self.editor.connect('update-document-stats',
                            self.update_document_stats)

        self.screens = Gtk.Stack()
        self.screens.set_transition_duration(400)
        self.screens.set_transition_type(
            Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)

        self.screens.add_named(self.welcome_grid, 'welcome-grid')
        self.screens.add_named(self.document_grid, 'document-grid')
        self.screens.add_named(self.editor, 'editor-grid')

        self.screens.show_all()

        self.toast = Granite.WidgetsToast()

        self.overlay = Gtk.Overlay()
        self.overlay.add_overlay(self.screens)
        self.overlay.add_overlay(self.toast)
        self.overlay.show_all()

        self.content_box.pack_start(self.header, False, False, 0)
        self.content_box.pack_end(self.overlay, True, True, 0)

        # Init actions
        self.init_actions()

        # If here's at least one document in storage
        # then show documents grid
        self.check_grid_items()

        # Pull the Settings
        self.toggle_spellcheck(self.settings.get_boolean('spellcheck'))
        self.autosave = self.settings.get_boolean('autosave')
        self.set_autoindent(self.settings.get_boolean('autoindent'))
        self.set_tabs_spaces(
            self.settings.get_boolean('spaces-instead-of-tabs'))
        self.set_indent_width(self.settings.get_int('indent-width'))
        self.set_style_scheme(self.settings.get_string('stylescheme'))
        self.editor.update_font(self.settings.get_string('font'))