diff --git a/scriptorium/dialogs/preferences.blp b/scriptorium/dialogs/preferences.blp
index e18ece6..6eb95e4 100644
--- a/scriptorium/dialogs/preferences.blp
+++ b/scriptorium/dialogs/preferences.blp
@@ -23,12 +23,25 @@ template $ScrptPreferencesDialog: Adw.PreferencesDialog {
title: _("Restore Previous Session");
subtitle: _("Open the last project when starting Scriptorium");
}
+
+ Adw.ActionRow projects_directory {
+ title: _("Data directory");
+ subtitle: _("");
+
+ [suffix]
+ Gtk.Button projects_directory_button {
+ icon-name: "folder-symbolic";
+ tooltip-text: _("Change storage location");
+ valign: center;
+ clicked => $on_projects_directory_button_clicked();
+ }
+ }
}
}
Adw.PreferencesPage {
title: _("Editor");
- icon-name: "settings-symbolic";
+ icon-name: "edit-symbolic";
Adw.PreferencesGroup {
title: _("Preview");
@@ -95,3 +108,8 @@ template $ScrptPreferencesDialog: Adw.PreferencesDialog {
}
}
}
+
+Gtk.FileDialog projects_directory_dialog {
+ title: _("Select storage location");
+ modal: true;
+}
diff --git a/scriptorium/dialogs/preferences.py b/scriptorium/dialogs/preferences.py
index 820873d..d900d02 100644
--- a/scriptorium/dialogs/preferences.py
+++ b/scriptorium/dialogs/preferences.py
@@ -17,7 +17,7 @@
#
# SPDX-License-Identifier: GPL-3.0-or-later
"""Dialog to select scenes in Scriptorium."""
-from gi.repository import Adw, Gtk, Gio, Pango
+from gi.repository import Adw, Gtk, Gio, Pango, GLib
from scriptorium.globals import BASE
from scriptorium.utils import html_to_buffer
from gettext import gettext as _
@@ -27,9 +27,9 @@
logger = logging.getLogger(__name__)
PLACEHOLDER_TEXT = _(
-"
This is a placeholder text to select"
+"
This is a placeholder text to select "
"the font of the scene editor.
"
-"You can also see how annotations are shown and how words"
+"
You can also see how annotations are shown and how words "
"with an emphasis or noted as strong will appear.
"
)
UNDERLINE_OPTIONS = ["single", "double", "dashed"]
@@ -39,13 +39,20 @@
class ScrptPreferencesDialog(Adw.PreferencesDialog):
__gtype_name__ = "ScrptPreferencesDialog"
+ # Setting to open the last project on application launch
open_last_project = Gtk.Template.Child()
+
+ # Settings for the editor
text_view = Gtk.Template.Child()
font_dialog_button = Gtk.Template.Child()
editor_line_height = Gtk.Template.Child()
- font_dialog_button = Gtk.Template.Child()
editor_underline_style = Gtk.Template.Child()
+ # Information about the storage location for manuscripts
+ projects_directory: Adw.ActionRow = Gtk.Template.Child()
+ projects_directory_button: Gtk.Button = Gtk.Template.Child()
+ projects_directory_dialog: Gtk.FileDialog = Gtk.Template.Child()
+
def __init__(self):
"""Create a new instance of the class."""
super().__init__()
@@ -62,6 +69,13 @@ def __init__(self):
Gio.SettingsBindFlags.DEFAULT
)
+ settings.bind(
+ "manuscripts-folder",
+ self.projects_directory,
+ "subtitle",
+ Gio.SettingsBindFlags.DEFAULT
+ )
+
settings.bind(
"editor-line-height",
self.editor_line_height,
@@ -111,3 +125,27 @@ def on_underline_style_selected(self, _combo, _value):
UNDERLINE_OPTIONS[selected_value]
)
+ @Gtk.Template.Callback()
+ def on_projects_directory_button_clicked(self, _button):
+ """Handle a click to select storage location."""
+ def on_folder_selected(dialog, result):
+ try:
+ folder = dialog.select_folder_finish(result)
+ except GLib.Error as e:
+ logger.info(f"Error when changing data folder: {e}")
+ return
+ self.projects_directory.set_subtitle(folder.get_path())
+
+ # Configure the dialog to open the current storage value
+ self.projects_directory_dialog.set_initial_folder(
+ Gio.File.new_for_path(self.projects_directory.get_subtitle())
+ )
+
+ # Open the dialog
+ self.projects_directory_dialog.select_folder(
+ parent=self.get_root(),
+ cancellable=None,
+ callback=on_folder_selected,
+ )
+
+
diff --git a/scriptorium/views/editor.blp b/scriptorium/views/editor.blp
index 34a8b87..cf88727 100644
--- a/scriptorium/views/editor.blp
+++ b/scriptorium/views/editor.blp
@@ -4,7 +4,8 @@ using Adw 1;
template $ScrptEditorView: Adw.NavigationPage {
title: _("Editor");
tag: "editor";
- unrealize => $on_editorview_closed();
+ realize => $on_scrpteditorview_realize();
+ unrealize => $on_scrpteditorview_unrealize();
child: Adw.ToolbarView {
top-bar-style: raised_border;
diff --git a/scriptorium/views/editor.py b/scriptorium/views/editor.py
index f09f2dc..5911e70 100644
--- a/scriptorium/views/editor.py
+++ b/scriptorium/views/editor.py
@@ -53,7 +53,7 @@ class ScrptEditorView(Adw.NavigationPage):
plan_page = Gtk.Template.Child()
file_filter_image = Gtk.Template.Child()
- def __init__(self):
+ def __init__(self, project: Project):
"""Create a new instance of the editor."""
super().__init__()
@@ -114,16 +114,20 @@ def __init__(self):
)
group.add_action(action)
- def connect_to_project(self, project: Project):
- # Keep track of the project the editor is associated to
+ # Keep track of the project for this editor window
self.project = project
- self.write_page.connect_to_project(project)
- self.publish_page.connect_to_project(project)
- self.plan_page.connect_to_project(project)
+ @Gtk.Template.Callback()
+ def on_scrpteditorview_realize(self, _editorview):
+ """Called when the editor widgets are all created."""
+ if self.project is not None:
+ logger.info("Editor is open, connect the tabs to the manuscript")
+ self.plan_page.connect_to_project(self.project)
+ self.write_page.connect_to_project(self.project)
+ self.publish_page.connect_to_project(self.project)
@Gtk.Template.Callback()
- def on_editorview_closed(self, _editorview):
+ def on_scrpteditorview_unrealize(self, _editorview):
"""Handle a request to close the editor."""
if self.project is not None:
logger.info("Editor is closed, saving the manuscript")
diff --git a/scriptorium/views/library.blp b/scriptorium/views/library.blp
index 6ef1370..c4881c9 100644
--- a/scriptorium/views/library.blp
+++ b/scriptorium/views/library.blp
@@ -6,6 +6,7 @@ template $ScrptLibraryView: Adw.NavigationPage {
tag: "library";
shown => $on_scrptlibraryview_shown();
+ realize => $on_scrptlibraryview_realize();
child: Adw.ToolbarView {
[top]
diff --git a/scriptorium/views/library.py b/scriptorium/views/library.py
index 2afb862..d8a44c5 100644
--- a/scriptorium/views/library.py
+++ b/scriptorium/views/library.py
@@ -20,6 +20,8 @@
from gi.repository import Adw, GObject, Gio, Gtk
from gi.repository import GLib
+from scriptorium.views import ScrptEditorView
+
from scriptorium.globals import BASE
from scriptorium.models import Library, Project
from scriptorium.dialogs import ScrptAddDialog
@@ -38,8 +40,6 @@ class ScrptLibraryView(Adw.NavigationPage):
# The Library is the data model holding the list of projects
library: Library = Library()
- selected_project = GObject.Property(type=Project)
-
# The base path of all the manuscripts
manuscripts_base_path = GObject.Property(type=str)
@@ -55,8 +55,10 @@ class ScrptLibraryView(Adw.NavigationPage):
identifier = Gtk.Template.Child()
- def __init__(self, **kwargs):
- super().__init__(**kwargs)
+ def __init__(self, manuscripts_base_path):
+ super().__init__()
+
+ self.manuscripts_base_path = manuscripts_base_path
# Connect an instance of the theme button to the menu
popover = self.win_menu.get_popover()
@@ -83,6 +85,7 @@ def __init__(self, **kwargs):
group.add_action(action)
# Signal to the list model to detect when content is available
+ # this is useful when a new Manuscript is created
self.library.projects.connect(
"items-changed",
self.on_grid_content_changed
@@ -98,16 +101,25 @@ def __init__(self, **kwargs):
@Gtk.Template.Callback()
def on_scrptlibraryview_shown(self, _src):
- """Update the selected project to None."""
+ """
+ Called when the window becomes visible. This is likely to happen
+ when the editor is closed so we use the callback to unset the selected
+ manuscript.
+ """
+ logger.info("Shown")
# Disable the current selection
selection_model = self.projects_grid.get_model()
selection_model.set_selected(Gtk.INVALID_LIST_POSITION)
- # Set the window to no project opened
- window = self.props.root
- if window is not None:
- window.project = None
+ @Gtk.Template.Callback()
+ def on_scrptlibraryview_realize(self, _src):
+ logger.info(f"Opening library at {self.manuscripts_base_path}")
+
+ # Connect the library to the folder
+ self.library.open_folder(self.manuscripts_base_path)
+
+ self.open_last_project()
@Gtk.Template.Callback()
def on_add_manuscript_clicked(self, _button):
@@ -142,20 +154,40 @@ def on_grid_content_changed(self, list_model, _position, _added, _removed):
def on_selection_changed(self, selection_model, position, n_items):
"""
- Called when a manuscript is selected
+ Called when a manuscript is selected in the list.
"""
# Get the selected project
- selected_item = selection_model.get_selected_item()
- if selected_item is not None:
- selected_project = selection_model.get_selected_item()
+ selected_project = selection_model.get_selected_item()
+
+ # Keep track of the last manuscript selected (this could be None)
+ logger.info(f"Set last selected project to {selected_project}")
+ settings = Gio.Settings(schema_id="io.github.cgueret.Scriptorium")
+ settings.set_string(
+ "last-manuscript-name",
+ selected_project.identifier if selected_project is not None else ""
+ )
+
+ selected_project = selection_model.get_selected_item()
+ if selected_project is not None:
logger.info(f"Selected project {selected_project.identifier}")
if not selected_project.can_be_opened:
self.migrate_dialog.choose(self)
#selection_model.set_selected(Gtk.INVALID_LIST_POSITION)
else:
# Open the project
- window = self.props.root
- window.project = selected_project
+ self._open_project(selected_project)
+
+ def _open_project(self, project):
+ """
+ Open a selected project and remember it as the last project selected
+ """
+ # If we did select something, open the editor
+ if project is not None:
+ logger.info(f"\"{project.title}\": create and open editor")
+
+ # Create an editor navigation page and push it to the navigation
+ editor_page = ScrptEditorView(project)
+ self.get_parent().push(editor_page)
def open_last_project(self):
"""Check if we need to open the last project."""
@@ -182,15 +214,6 @@ def open_last_project(self):
if model[index].can_be_opened:
model.select_item(index, True)
- def on_projects_base_path_changed(self, window, parameter):
- base_path = window.get_property(parameter.name)
- logger.info(f"Opening library at {base_path}")
-
- # Connect the library to the folder
- self.library.open_folder(base_path)
-
- self.open_last_project()
-
@Gtk.Template.Callback()
def on_migrate_dialog_response(self, _dialog, response):
"""Handle a response to migrating a project."""
@@ -211,7 +234,7 @@ def on_migrate_dialog_response(self, _dialog, response):
window.inform("Project successfuly migrated!")
# Open the project right away
- window.project = selected_project
+ self._open_project(selected_project)
else:
window.inform("Something went wrong. See logs for details")
diff --git a/scriptorium/views/write/navigation.py b/scriptorium/views/write/navigation.py
index 837fac1..cb68893 100644
--- a/scriptorium/views/write/navigation.py
+++ b/scriptorium/views/write/navigation.py
@@ -16,9 +16,11 @@
# along with this program. If not, see .
#
# SPDX-License-Identifier: GPL-3.0-or-later
+from gettext import gettext as _
+
import logging
-from gi.repository import Gio, Graphene, Gtk, Adw
+from gi.repository import Gio, Graphene, Gtk, Adw, GLib
from scriptorium.globals import BASE
from scriptorium.models import Chapter, Manuscript, Scene
@@ -76,6 +78,7 @@ def on_list_item_bind(self, _, list_item):
def connect_to(self, project):
"""Connect the navigation to the project and its contents."""
+ logger.info(f"Connect navigation to {project}")
# Turn the content into a tree, instance of Chapter may have children
roots = Gio.ListStore.new(Manuscript)
@@ -102,6 +105,7 @@ def connect_to(self, project):
# append items at the end of the manuscript by default. Users who want
# to position items directly can use the context actions instead
manuscript_id = project.manuscript.identifier
+ logger.info(f"Connect navigation to {manuscript_id}")
menu = Gio.Menu()
menu.append(
label=_("Add new Scene"),
@@ -112,7 +116,11 @@ def connect_to(self, project):
detailed_action=f"editor.add_resource(('Chapter', '{manuscript_id}'))"
)
self.add_menu.set_menu_model(menu)
- self.add_menu.set_detailed_action_name(
- f"editor.add_resource(('Scene', '{manuscript_id}'))"
+ self.add_menu.connect(
+ "clicked",
+ lambda _: self.activate_action(
+ "editor.add_resource",
+ GLib.Variant("(ss)", ("Scene", manuscript_id))
+ )
)
diff --git a/scriptorium/views/write/page.py b/scriptorium/views/write/page.py
index 924fd26..f6ef48f 100644
--- a/scriptorium/views/write/page.py
+++ b/scriptorium/views/write/page.py
@@ -60,6 +60,7 @@ class WritePage(Adw.Bin):
def __init__(self):
"""Create an instance of the editor."""
super().__init__()
+
# By default we have no active scene
self.active_scene = None
diff --git a/scriptorium/window.blp b/scriptorium/window.blp
index 0df8f4f..e614fa6 100644
--- a/scriptorium/window.blp
+++ b/scriptorium/window.blp
@@ -8,11 +8,11 @@ template $ScrptWindow: Adw.ApplicationWindow {
content: Adw.ToastOverlay toast_overlay {
child: Adw.NavigationView navigation {
- $ScrptLibraryView library_panel {}
};
};
close-request => $on_close_request();
+ realize => $on_scrptwindow_realize();
ShortcutController {
Shortcut {
diff --git a/scriptorium/window.py b/scriptorium/window.py
index 97f90c9..bc094ab 100644
--- a/scriptorium/window.py
+++ b/scriptorium/window.py
@@ -21,8 +21,9 @@
from gi.repository import Gtk, Adw, GObject, Gdk, Gio, GLib
from pathlib import Path
-# It seems Builder won't find the widgets unless we import them?
-from scriptorium.views import ScrptEditorView
+# Needed here for some weird reason, Builder does not find it otherwise
+# TODO: idea: maybe instantiate the libraryview and reset it on folder change
+from scriptorium.views import ScrptLibraryView
from scriptorium.models import Project
from scriptorium.globals import BASE
@@ -38,17 +39,25 @@ class ScrptWindow(Adw.ApplicationWindow):
__gtype_name__ = 'ScrptWindow'
navigation = Gtk.Template.Child()
- library_panel = Gtk.Template.Child()
toast_overlay = Gtk.Template.Child()
# This is a pointer to the currently open project, defaults to None
- project = GObject.Property(type=Project, default=None)
+ #project = GObject.Property(
+ # type=Project,
+ # default=None
+ #)
# The base path of all the manuscripts
- projects_base_path = GObject.Property(type=str)
+ manuscripts_folder = GObject.Property(
+ type=str,
+ default=None
+ )
# This is the identifier of the manuscript that was last opened
- last_manuscript_name = GObject.Property(type=str, default=None)
+ last_manuscript_name = GObject.Property(
+ type=str,
+ default=None
+ )
def __init__(self, **kwargs):
super().__init__(**kwargs)
@@ -58,9 +67,11 @@ def __init__(self, **kwargs):
css_provider.load_from_file(
Gio.File.new_for_uri(f"resource:/{BASE}/style.css")
)
- Gtk.StyleContext.add_provider_for_display(Gdk.Display.get_default(),
+ Gtk.StyleContext.add_provider_for_display(
+ Gdk.Display.get_default(),
css_provider,
- Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
+ Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
+ )
# Load custom icons
theme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default())
@@ -70,17 +81,26 @@ def __init__(self, **kwargs):
self.settings = Gio.Settings(schema_id="io.github.cgueret.Scriptorium")
# Bind the settings related to the window
- self.settings.bind("window-width", self, "default-width",
+ self.settings.bind(
+ "window-width", self, "default-width",
Gio.SettingsBindFlags.DEFAULT
)
- self.settings.bind("window-height", self, "default-height",
+ self.settings.bind(
+ "window-height", self, "default-height",
Gio.SettingsBindFlags.DEFAULT
)
- self.settings.bind("window-maximized", self, "maximized",
+ self.settings.bind(
+ "window-maximized", self, "maximized",
Gio.SettingsBindFlags.DEFAULT
)
- self.settings.bind("last-manuscript-name", self, "last-manuscript-name",
+ # Bindings related to projects management
+ self.settings.bind(
+ "last-manuscript-name", self, "last-manuscript-name",
+ Gio.SettingsBindFlags.DEFAULT
+ )
+ self.settings.bind(
+ "manuscripts-folder", self, "manuscripts-folder",
Gio.SettingsBindFlags.DEFAULT
)
@@ -98,74 +118,40 @@ def __init__(self, **kwargs):
# The library is where a project is selected by the user. We keep an
# eye on actions there
self.connect(
- 'notify::project',
- self.on_project_changed
- )
-
- # Connect a callback in the library to keep an eye on projects base path
- self.connect(
- 'notify::projects-base-path',
- self.library_panel.on_projects_base_path_changed
+ 'notify::manuscripts-folder',
+ self.on_manuscripts_folder_changed
)
# Open the default data directory
# (TODO Implement the setting for data folder)
- projects_path = Path(GLib.get_user_data_dir()) / Path('manuscripts')
- if not projects_path.exists():
- projects_path.mkdir()
- self.projects_base_path = projects_path.resolve()
+ #projects_path = Path(self.manuscripts_folder) / Path('manuscripts')
+ #if not projects_path.exists():
+ # projects_path.mkdir()
+ #self.projects_base_path = projects_path.resolve()
+
+ @Gtk.Template.Callback()
+ def on_scrptwindow_realize(self, window):
+ """Called with the window is created."""
+
+ # See if we have a manuscript folder path set
+ # If not, use the app base directory
+ if not self.manuscripts_folder or self.manuscripts_folder == "":
+ folder = Path(GLib.get_user_data_dir()) / "manuscripts"
+ folder.mkdir(exist_ok=True)
+ self.manuscripts_folder = folder
+
+ # Inform the user of the data folder
+ logger.info(f'Data location: {self.manuscripts_folder}')
+
+ # Create a library panel
+ library_panel = ScrptLibraryView(self.manuscripts_folder)
+
+ # Add it to the navigation
+ self.navigation.push(library_panel)
@Gtk.Template.Callback()
def on_close_request(self, event):
logger.info("Window close requested")
- # Save the name of the last edited project
-
- def _open_library(self):
- # Get a reference to the library panel
- self.library_panel.connect('notify::selected-project',
- self.on_selected_project_changed)
-
- # Set the data folder
- manuscript_path = Path(GLib.get_user_data_dir()) / Path('manuscripts')
- if not manuscript_path.exists():
- manuscript_path.mkdir()
- logger.info(f'Data location: {manuscript_path}')
- self.library_panel.set_property('manuscripts_base_path',
- manuscript_path.resolve())
-
- #last_opened = self.settings.get_string("last-manuscript-name")
- #logger.info(f"Trigger selection for last opened: {last_opened}")
-
- #manuscripts_model = self._library_panel.manuscripts_grid.get_model()
- #if self.settings.get_boolean("open-last-project"):
- # if len(manuscripts_model) > 0:
- # index = 0
- # for i in range(len(manuscripts_model)):
- # if manuscripts_model[i].identifier == last_opened:
- # index = i
- # manuscripts_model.select_item(index, True)
-
- def on_project_changed(self, _navigation, _other):
- """Handle a change in the selected project."""
- logger.info(f"Change currently edited project to {self.project}")
-
- # If we did select something, open the editor
- if self.project is not None:
- logger.info(f"\"{self.project.title}\": create and open editor")
-
- # Create an editor navigation page and push it to the stack
- editor_page = ScrptEditorView()
- if not self.project.is_opened:
- self.project.open()
- editor_page.connect_to_project(self.project)
- self.navigation.push(editor_page)
-
- # Keep track of the last manuscript selected
- settings = Gio.Settings(schema_id="io.github.cgueret.Scriptorium")
- settings.set_string(
- "last-manuscript-name",
- self.project.identifier if self.project is not None else ""
- )
def close_editor(self, editor_view):
self.navigation.pop()
@@ -174,3 +160,15 @@ def inform(self, message: str):
toast = Adw.Toast.new(title=message)
toast.set_timeout(3)
self.toast_overlay.add_toast(toast)
+
+ def on_manuscripts_folder_changed(self, _src, _value):
+ """Called when the manuscripts storage location is changed."""
+ self.inform("Data folder changed")
+
+ # Create a library panel
+ library_panel = ScrptLibraryView(self.manuscripts_folder)
+
+ # Replace all the other panels by that one
+ self.navigation.replace(pages=[library_panel])
+
+