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).strip() # New document has id == -1 # No need to save new empty documents if self.document.document_id == -1 and len(text) == 0: # storage.delete(self.document.document_id) return False if self.document.title in ('', 'Nameless'): try: self.document.title = text.partition('\n')[0].lstrip( ' #') or "Nameless" except TypeError: pass # Save new document to get ID before continue if self.document.document_id == -1: self.document.document_id = storage.add(self.document) 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 rename_folder(self, folder: Folder, title: str) -> bool: """Renames folder with given `folder` to `title`. """ if title == '..': return False query = f"UPDATE folders SET title=? WHERE path=? AND title=?" try: self.conn.execute(query, ( title, folder.path, folder.title, )) self.conn.commit() # Store old path old_path = folder.absolute_path # Set new title to get the new path folder.title = title new_path = folder.absolute_path self.move_folders(old_path, new_path) self.move_documents(old_path, new_path) except Exception as e: Logger.error(e) return False return True
def move_folder(self, folder: Folder, path: str = '/') -> bool: """Moves folder to the given `path`. Returns True if folder was moved successfully. """ query = 'UPDATE folders SET path=? WHERE path=? AND title=?' try: self.conn.execute(query, (path, folder.path, folder.title)) self.conn.commit() # Store old path old_path = folder.absolute_path # Set new title to get the new path folder.path = path new_path = folder.absolute_path self.move_folders(old_path, new_path) self.move_documents(old_path, new_path) except Exception as e: Logger.error(e) return False return True
def on_preview(self, sender, event): if not self.is_document_editing: Logger.debug('Not in edit mode') return doc = self.document_grid.selected_document or self.editor.document if not doc: return 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 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 update(self, doc_id: int, data: dict) -> bool: """Updates document with given `doc_id` with given `data`. `data` can contain any of the following keys: - title - content - archived - tags - path - encrypted However, if you need to move document to another folder, you should use :func:`move` method. """ fields = {field: value for field, value in data.items()} query = f"UPDATE documents SET {','.join(f'{key}=?' for key in fields.keys())}, modified=? WHERE id=?" try: self.conn.execute( query, tuple(fields.values()) + ( datetime.now(), doc_id, )) self.conn.commit() except Exception as e: Logger.error(e) return False return True
def document_activate(self, doc_id): Logger.info(f'Document {doc_id} activated') editor = self.screens.get_child_by_name('editor-grid') editor.load_document(doc_id) editor.connect('update-document-stats', self.update_document_stats) 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 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 storage.update(self.document._id, {"content": text}): self.document.content = text Logger.debug('Document %s saved', self.document._id) return True
def count_all(self, path: str = '/', with_archived: bool = False) -> int: """Counts all documents and folders in the given path. If `with_archived` is True then archived documents and folders will be counted too. """ folders = self.count_folders(path, with_archived) documents = self.count_documents(path, with_archived) Logger.debug( f'{folders} folders + {documents} documents found in {path}') return folders + documents
def delete(self, doc_id: int) -> bool: query = f"DELETE FROM documents WHERE id=?" try: self.conn.execute(query, (doc_id, )) self.conn.commit() except Exception as e: Logger.error(e) return False return True
def count_folders(self, path: str = '/', with_archived: bool = False) -> int: """Counts folders in the given path. If `with_archived` is True then archived folders will be counted too. Not yet implemented. """ query = 'SELECT COUNT (1) AS count FROM folders WHERE path=?' cursor = self.conn.cursor().execute(query, (path, )) row = cursor.fetchone() Logger.debug(f'{row[0]} folders found in {path}') return row[0]
def save(self, document: Document) -> bool: query = "UPDATE documents SET title=?, content=?, archived=?, modified=? WHERE id=?" try: self.conn.execute(query, (document.title, document.content, document.archived, datetime.now())) self.conn.commit() except Exception as e: Logger.error(e) return False return True
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 update(self, doc_id: int, data: dict) -> bool: fields = {field: value for field, value in data.items()} query = f"UPDATE documents SET {','.join(f'{key}=?' for key in fields.keys())} WHERE id=?" try: self.conn.execute(query, tuple(fields.values()) + (doc_id, )) self.conn.commit() except Exception as e: Logger.error(e) return False return True
def delete_folders(self, path: str) -> bool: """Permanently deletes folders under given `path` """ query = f"DELETE FROM folders WHERE path LIKE ?" try: self.conn.execute(query, (f'{path}%', )) self.conn.commit() except Exception as e: Logger.error(e) return False return True
def selected_document(self) -> Optional[Document]: """Returns selected :model:`Document` or `None` """ Logger.debug('DocumentGrid.selected_document') if self.is_folder_selected: return None if self.selected_path is None: return None model_iter = self.model.get_iter(self.selected_path) doc_id = self.model.get_value(model_iter, 3) return self.storage.get(doc_id)
def delete_documents(self, path: str) -> bool: """Permanently deletes documents under given `path`. Returns True if documents were deleted successfully. """ query = 'DELETE FROM documents WHERE path LIKE ?' try: self.conn.execute(query, (f'{path}%', )) self.conn.commit() except Exception as e: Logger.error(e) return False return True
def count_documents(self, path: str = '/', with_archived: bool = False) -> int: """Counts documents in the given path. If `with_archived` is True then archived documents will be counted too. """ query = 'SELECT COUNT (1) AS count FROM documents WHERE path=?' if not with_archived: query += " AND archived=0" cursor = self.conn.cursor().execute(query, (path, )) row = cursor.fetchone() Logger.debug(f'{row[0]} documents found in {path}') return row[0]
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 all(self, with_archived: bool = False) -> list: query = "SELECT * FROM documents" if not with_archived: query += " WHERE archived=0" Logger.debug('> ALL QUERY: %s', query) cursor = self.conn.cursor().execute(query) rows = cursor.fetchall() docs = [] for row in rows: docs.append(Document.new_with_row(row)) return docs
def delete(self, doc_id: int) -> bool: """Permanently deletes document with given `doc_id`. Returns True if document was deleted successfully. """ query = f"DELETE FROM documents WHERE id=?" try: self.conn.execute(query, (doc_id, )) self.conn.commit() except Exception as e: Logger.error(e) return False return True
def move_folders(self, old_path: str, new_path: str) -> bool: query = f"UPDATE folders SET path=REPLACE(path, ?, ?) WHERE path LIKE ?" try: self.conn.execute(query, ( old_path, new_path, f"{old_path}%", )) self.conn.commit() except Exception as e: Logger.error(e) return False return True
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_settings_changed(self, settings, key): Logger.debug(f'SETTINGS: %s changed', key) if key == "autosave": self.window.autosave = settings.get_boolean(key) elif key == "spellcheck": self.window.toggle_spellcheck(settings.get_boolean(key)) elif key == 'stylescheme': self.window.set_style_scheme(settings.get_string(key)) elif key == 'autoindent': self.window.set_autoindent(settings.get_boolean('autoindent')) elif key == 'spaces-instead-of-tabs': self.window.set_tabs_spaces(settings.get_boolean('spaces-instead-of-tabs')) elif key == 'indent-width': self.window.set_indent_width(settings.get_int('indent-width')) elif key == 'font': self.window.editor.update_font(settings.get_string('font'))
def init(self): if not os.path.exists(self.base_path): os.mkdir(self.base_path) Logger.info('Storage folder created at %s', self.base_path) Logger.info(f'Storage located at %s', self.file_path) self.conn = sqlite3.connect(self.file_path) self.conn.execute(""" CREATE TABLE IF NOT EXISTS `documents` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `title` TEXT NOT NULL, `content` TEXT, `archived` INTEGER NOT NULL DEFAULT 0 ) """)
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: """ folder = self.document_grid.selected_folder if folder: self.folder_activate(folder.absolute_path) Logger.debug(f'Activated Folder {folder.absolute_path}') else: doc_id = self.document_grid.selected_document_id Logger.debug(f'Activated Document.Id {doc_id}') self.document_activate(doc_id)
def move(self, doc_id: int, path: str = '/') -> bool: """Moves document to the given `path`. Returns True if document was moved successfully. """ query = 'UPDATE documents SET path=? WHERE id=?' try: self.conn.execute(query, ( path, doc_id, )) self.conn.commit() except Exception as e: Logger.error(e) return False 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(' #') 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 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 delete_folder(self, folder: Folder) -> bool: """Permanently deletes `folder` :param folder: :class:`Folder` to be deleted. """ query = f"DELETE FROM folders WHERE path=? AND title=?" try: self.conn.execute(query, ( folder.path, folder.title, )) self.conn.commit() self.delete_documents(folder.absolute_path) self.delete_folders(folder.absolute_path) except Exception as e: Logger.error(e) return False return True