From fb3564a3aa770a6dfd20ad5b7a61487fcc07edc1 Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Wed, 29 Mar 2023 21:44:57 -0500 Subject: [PATCH] Adding notebook save and loading structure --- README.md | 16 +-- src/__main__.py | 1 + src/app.py | 2 +- src/core/controller.py | 22 +++++ src/core/mixins/signals/ipc_signals_mixin.py | 3 + src/core/widgets/create_notebook_widget.py | 99 +++++++++++++++++++ src/core/widgets/pages/pages_widget.py | 36 ++++++- src/core/widgets/sections/sections_widget.py | 45 +++++++-- src/utils/ipc_server.py | 5 + src/utils/settings/settings.py | 24 +++-- .../usr/applications/coherence.desktop | 2 +- 11 files changed, 217 insertions(+), 38 deletions(-) create mode 100644 src/core/widgets/create_notebook_widget.py diff --git a/README.md b/README.md index 4896ce9..d03ce3b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# Python-With-Gtk-Template -A template project for Python with Gtk applications. +# Coherence +Coherence is like a OneNote sibling that lets you store notes, images, links, etc. ### Requirements * PyGObject @@ -7,14 +7,4 @@ A template project for Python with Gtk applications. * pyxdg ### Note -There are a "\" strings and files that need to be set according to your app's name located at: -* \_\_builtins\_\_.py -* user_config/bin/app_name -* user_config/usr/share/app_name -* user_config/usr/share/app_name/icons/app_name.png -* user_config/usr/share/app_name/icons/app_name-64x64.png -* user_config/usr/share/applications/app_name.desktop - - -For the user_config, after changing names and files, copy all content to their respective destinations. -The logic follows Debian Dpkg packaging and its placement logic. +* n/a diff --git a/src/__main__.py b/src/__main__.py index 8ab4870..065a783 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -32,6 +32,7 @@ if __name__ == "__main__": # Add long and short arguments parser.add_argument("--debug", "-d", default="false", help="Do extra console messaging.") parser.add_argument("--trace-debug", "-td", default="false", help="Disable saves, ignore IPC lock, do extra console messaging.") + parser.add_argument("--no-plugins", "-np", default="false", help="Do not load plugins.") parser.add_argument("--new-tab", "-nt", default="false", help="Opens a 'New Tab' if a handler is set for it.") parser.add_argument("--file", "-f", default="default", help="JUST SOME FILE ARG.") diff --git a/src/app.py b/src/app.py index 1ea9b36..dc32ca4 100644 --- a/src/app.py +++ b/src/app.py @@ -26,7 +26,7 @@ class Application(IPCServer): if not self.is_ipc_alive: for arg in unknownargs + [args.new_tab,]: - if os.path.isdir(arg): + if os.path.isfile(arg): message = f"FILE|{arg}" self.send_ipc_message(message) diff --git a/src/core/controller.py b/src/core/controller.py index 0008156..71b91eb 100644 --- a/src/core/controller.py +++ b/src/core/controller.py @@ -1,4 +1,5 @@ # Python imports +import os # Lib imports import gi @@ -11,6 +12,7 @@ from gi.repository import GLib # Application imports from .mixins.signals_mixins import SignalsMixins from .controller_data import ControllerData +from .widgets.create_notebook_widget import CreateNotebookWidget from .widgets.sections.sections_widget import Sections @@ -22,6 +24,23 @@ class Controller(SignalsMixins, ControllerData): self._setup_styling() self._setup_signals() self._subscribe_to_events() + self._load_widgets() + + if args.no_plugins == "false": + self.plugins.launch_plugins() + + for arg in unknownargs + [args.new_tab,]: + if os.path.isfile(arg): + message = f"FILE|{arg}" + event_system.emit("post_file_to_ipc", message) + + if os.path.isdir(arg): + message = f"DIR|{arg}" + event_system.emit("post_file_to_ipc", message) + + event_system.emit_and_await("load_notebook") + if settings.get_active_notebook(): + event_system.emit("load_notebook_data") logger.info(f"Made it past {self.__class__} loading...") @@ -38,6 +57,9 @@ class Controller(SignalsMixins, ControllerData): event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc) event_system.subscribe("tggl_top_main_menubar", self._tggl_top_main_menubar) + def _load_widgets(self): + CreateNotebookWidget() + def load_glade_file(self): self.builder = Gtk.Builder() self.builder.add_from_file(settings.get_glade_file()) diff --git a/src/core/mixins/signals/ipc_signals_mixin.py b/src/core/mixins/signals/ipc_signals_mixin.py index 34c6555..d3685cd 100644 --- a/src/core/mixins/signals/ipc_signals_mixin.py +++ b/src/core/mixins/signals/ipc_signals_mixin.py @@ -15,3 +15,6 @@ class IPCSignalsMixin: def handle_file_from_ipc(self, path: str) -> None: print(f"Path From IPC: {path}") + + def handle_dir_from_ipc(self, path: str) -> None: + print(f"Dir From IPC: {path}") diff --git a/src/core/widgets/create_notebook_widget.py b/src/core/widgets/create_notebook_widget.py new file mode 100644 index 0000000..0963238 --- /dev/null +++ b/src/core/widgets/create_notebook_widget.py @@ -0,0 +1,99 @@ +# Python imports +import os + +# Lib imports +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + +# Application imports + + +class CreateNotebookWidget(Gtk.Dialog): + def __init__(self): + super(CreateNotebookWidget, self).__init__() + + self._nb_path = settings.get_notebooks_path() + + self._setup_styling() + self._setup_signals() + self._subscribe_to_events() + self._load_widgets() + + + def _setup_styling(self): + ... + + def _setup_signals(self): + self.set_default_geometry(280, 80) + self.set_size_request(280, 80) + self.set_modal(True) + self.set_transient_for( settings.get_main_window() ) + + def _subscribe_to_events(self): + event_system.subscribe("load_notebook", self._load_notebook) + event_system.subscribe("create_notebook", self._create_notebook) + + def _load_widgets(self): + area = self.get_content_area() + label = Gtk.Label(label="Create A New Notebook") + nb_entry = Gtk.Entry() + create_btn = Gtk.Button(label="Create") + cancel_btn = Gtk.Button(label="Cancel") + + create_btn.set_image( Gtk.Image.new_from_icon_name("gtk-add", 4) ) + cancel_btn.set_image( Gtk.Image.new_from_icon_name("gtk-cancel", 4) ) + nb_entry.set_max_length(15) + nb_entry.set_placeholder_text("Notebook Name") + + area.add(label) + area.add(nb_entry) + + self.add_action_widget(cancel_btn, 1) + self.add_action_widget(create_btn, 0) + + area.show_all() + + + def _load_notebook(self): + notebooks = os.listdir(self._nb_path) + if len(notebooks) == 0: + if self._create_notebook(True) in (-1, 1): + return + + notebooks = os.listdir(self._nb_path) + for notebook in notebooks: + path = os.path.join(self._nb_path, notebook) + if os.path.isdir(path): + # TODO: Add to a selection window then load choice + settings.set_active_notebook(path) # NOTE: Temporary + ... + + # settings.set_active_notebook(path) + + + def _create_notebook(self, is_first_notebook = False): + response = self.run() + self.hide() + + if response == 0: + nb_entry = self.get_content_area().get_children()[1] + notebook = nb_entry.get_text().strip() + + if not notebook in ("", None): + path = os.path.join(self._nb_path, notebook) + try: + logger.info(f"Creating Notebook at: {path}") + os.mkdir(path) + settings.set_active_notebook(path) + logger.info(f"Created and loaded Notebook: {notebook}") + except Exception as e: + logger.info(f"Notebook creation failed! ") + response = -1 + else: + response = 1 + + if response == 1: + logger.info(f"Canceled notebook creation...") + + return response diff --git a/src/core/widgets/pages/pages_widget.py b/src/core/widgets/pages/pages_widget.py index c5cf9c7..9ff408e 100644 --- a/src/core/widgets/pages/pages_widget.py +++ b/src/core/widgets/pages/pages_widget.py @@ -30,10 +30,37 @@ class Pages(Gtk.Notebook): def _setup_signals(self): - ... + self.set_scrollable(True) def _load_widgets(self): - page = Page(self.close_tab) + start_box = Gtk.Box() + end_box = Gtk.Box() + + search = Gtk.SearchEntry() + search.set_placeholder_text("Search...") + search.connect("changed", self._text_search) + + add_btn = Gtk.Button() + add_btn.set_image( Gtk.Image.new_from_icon_name("add", 4) ) + add_btn.set_always_show_image(True) + add_btn.connect("released", self.create_page_view) + + start_box.add(search) + start_box.add(add_btn) + + start_box.show_all() + end_box.show_all() + + # PACKTYPE: 0 Start, 1 = End + self.set_action_widget(start_box, 0) + self.set_action_widget(end_box, 1) + + def _close_tab(self, button, page, eve = None): + page_num = self.page_num(page) + self.remove_page(page_num) + + def create_page_view(self, widget = None, eve = None): + page = Page(self._close_tab) page_num = self.append_page(page, page.get_tab_widget()) self.set_tab_detachable(page, False) @@ -46,6 +73,5 @@ class Pages(Gtk.Notebook): def get_tab_widget(self): return self._tab_widget - def close_tab(self, button, page, eve = None): - page_num = self.page_num(page) - self.remove_page(page_num) + def _text_search(self, widget = None, eve = None): + ... diff --git a/src/core/widgets/sections/sections_widget.py b/src/core/widgets/sections/sections_widget.py index 84e8b36..0f5e5e4 100644 --- a/src/core/widgets/sections/sections_widget.py +++ b/src/core/widgets/sections/sections_widget.py @@ -1,4 +1,5 @@ # Python imports +import os # Lib imports import gi @@ -16,23 +17,45 @@ class Sections(Gtk.Notebook): self._setup_styling() self._setup_signals() + self._subscribe_to_events() self._load_widgets() self.show_all() def _setup_styling(self): - ... + self.set_scrollable(True) def _setup_signals(self): ... - def _load_widgets(self): - self.create_view() - ... + def _subscribe_to_events(self): + event_system.subscribe("load_notebook_data", self._load_notebook_data) - def create_view(self, widget = None, eve = None, gfile = None): - pages = Pages(self.close_tab) + def _load_widgets(self): + start_box = Gtk.Box() + end_box = Gtk.Box() + + add_btn = Gtk.Button() + add_btn.set_image( Gtk.Image.new_from_icon_name("add", 4) ) + add_btn.set_always_show_image(True) + add_btn.connect("released", self.create_pages_view) + + end_box.add(add_btn) + + start_box.show_all() + end_box.show_all() + + # PACKTYPE: 0 Start, 1 = End + self.set_action_widget(start_box, 0) + self.set_action_widget(end_box, 1) + + def _close_tab(self, button, pages, eve = None): + page_num = self.page_num(pages) + self.remove_page(page_num) + + def create_pages_view(self, widget = None, eve = None): + pages = Pages(self._close_tab) page_num = self.append_page(pages, pages.get_tab_widget()) self.set_tab_detachable(pages, True) @@ -41,6 +64,10 @@ class Sections(Gtk.Notebook): self.show_all() self.set_current_page(page_num) - def close_tab(self, button, pages, eve = None): - page_num = self.page_num(pages) - self.remove_page(page_num) + def _load_notebook_data(self): + path = settings.get_active_notebook() + sections = os.listdir(path) + logger.info(f"Loading Notebook sections from: {path}") + if len(sections) == 0: + section = os.path.join(path, "New Section") + os.mkdir(section) diff --git a/src/utils/ipc_server.py b/src/utils/ipc_server.py index 8226247..ebfa47f 100644 --- a/src/utils/ipc_server.py +++ b/src/utils/ipc_server.py @@ -73,6 +73,11 @@ class IPCServer: if file: event_system.emit("handle_file_from_ipc", file) + if "DIR|" in msg: + file = msg.split("DIR|")[1].strip() + if file: + event_system.emit("handle_dir_from_ipc", file) + conn.close() break diff --git a/src/utils/settings/settings.py b/src/utils/settings/settings.py index c3ee5f3..4927b7b 100644 --- a/src/utils/settings/settings.py +++ b/src/utils/settings/settings.py @@ -23,6 +23,7 @@ class Settings(StartCheckMixin): self._USR_CONFIG_FILE = f"{self._USR_PATH}/settings.json" self._HOME_CONFIG_PATH = f"{self._USER_HOME}/.config/{app_name.lower()}" self._PLUGINS_PATH = f"{self._HOME_CONFIG_PATH}/plugins" + self._NOTEBOOKS_PATH = f"{self._HOME_CONFIG_PATH}/notebooks" self._DEFAULT_ICONS = f"{self._HOME_CONFIG_PATH}/icons" self._CONFIG_FILE = f"{self._HOME_CONFIG_PATH}/settings.json" self._GLADE_FILE = f"{self._HOME_CONFIG_PATH}/Main_Window.glade" @@ -35,6 +36,8 @@ class Settings(StartCheckMixin): os.mkdir(self._HOME_CONFIG_PATH) if not os.path.exists(self._PLUGINS_PATH): os.mkdir(self._PLUGINS_PATH) + if not os.path.exists(self._NOTEBOOKS_PATH): + os.mkdir(self._NOTEBOOKS_PATH) if not os.path.exists(self._CONFIG_FILE): import shutil @@ -69,15 +72,16 @@ class Settings(StartCheckMixin): bindings = json.load(file)["keybindings"] keybindings.configure(bindings) - self._main_window = None - self._main_window_w = 800 - self._main_window_h = 600 - self._builder = None - self.PAINT_BG_COLOR = (0, 0, 0, 0.54) + self._main_window = None + self._main_window_w = 800 + self._main_window_h = 600 + self._builder = None + self.PAINT_BG_COLOR = (0, 0, 0, 0.54) - self._trace_debug = False - self._debug = False - self._dirty_start = False + self._trace_debug = False + self._debug = False + self._dirty_start = False + self._active_notebook = None self.load_settings() @@ -97,7 +101,8 @@ class Settings(StartCheckMixin): def set_main_window(self, window): self._main_window = window def set_builder(self, builder) -> any: self._builder = builder - + def set_active_notebook(self, path: str): self._active_notebook = path + def get_active_notebook(self) -> str: return self._active_notebook def get_monitor_data(self) -> list: screen = self._main_window.get_screen() @@ -121,6 +126,7 @@ class Settings(StartCheckMixin): def get_home_config_path(self) -> str: return self._HOME_CONFIG_PATH def get_window_icon(self) -> str: return self._WINDOW_ICON def get_home_path(self) -> str: return self._USER_HOME + def get_notebooks_path(self) -> str: return self._NOTEBOOKS_PATH # Filter returns def get_office_filter(self) -> tuple: return tuple(self._settings["filters"]["office"]) diff --git a/user_config/usr/applications/coherence.desktop b/user_config/usr/applications/coherence.desktop index 3e2e28f..93e451f 100755 --- a/user_config/usr/applications/coherence.desktop +++ b/user_config/usr/applications/coherence.desktop @@ -1,6 +1,6 @@ [Desktop Entry] Name=Coherence -GenericName=Coherence is like a OneNote sibling thatlets you store notes, images, links, etc. +GenericName=Coherence is like a OneNote sibling that lets you store notes, images, links, etc. Comment=Coherence Exec=/bin/coherence %F Icon=/usr/share/Coherence/icons/coherence.png