diff --git a/bin/manuskript b/bin/manuskript
index 1fef8fe..cedac92 100755
--- a/bin/manuskript
+++ b/bin/manuskript
@@ -14,5 +14,6 @@ from manuskript.ui import MainWindow
path = os.path.join(os.getcwd(), "sample-projects/book-of-acts")
-window = MainWindow(path + ".msk")
+window = MainWindow()
+window.openProject(path + ".msk")
window.run()
diff --git a/manuskript/ui/mainWindow.py b/manuskript/ui/mainWindow.py
index ec51b2a..aec7e14 100644
--- a/manuskript/ui/mainWindow.py
+++ b/manuskript/ui/mainWindow.py
@@ -17,36 +17,14 @@ from manuskript.ui.tools import *
from manuskript.ui.aboutDialog import AboutDialog
from manuskript.ui.settingsWindow import SettingsWindow
from manuskript.ui.startupWindow import StartupWindow
-from manuskript.ui.util import bindMenuItem
-from manuskript.util import profileTime
+from manuskript.ui.util import bindMenuItem, packViewIntoSlot, unpackFromSlot
+from manuskript.util import parseFilenameFromURL
class MainWindow:
- @classmethod
- def packViewIntoSlot(cls, builder, id, view_cls, data=None):
- slot = builder.get_object(id)
-
- if slot is None:
- return None
-
- try:
- if data is None:
- view = profileTime(view_cls)
- else:
- view = profileTime(view_cls, data)
- except Exception:
- return None
-
- if view.widget is None:
- return None
-
- slot.pack_start(view.widget, True, True, 0)
- return view
-
- def __init__(self, path):
- self.project = Project(path)
- self.project.load()
+ def __init__(self):
+ self.project = None
builder = Gtk.Builder()
builder.add_from_file("ui/main.glade")
@@ -58,20 +36,25 @@ class MainWindow:
self.leaflet = builder.get_object("leaflet")
self.viewSwitcherBar = builder.get_object("view_switcher_bar")
- self.headerBar.set_subtitle(self.project.info.title)
-
self.leaflet.bind_property("folded", self.viewSwitcherBar, "reveal", GObject.BindingFlags.SYNC_CREATE)
self.leaflet.bind_property("folded", self.headerBar, "show-close-button", GObject.BindingFlags.SYNC_CREATE |
GObject.BindingFlags.INVERT_BOOLEAN)
- self.generalView = MainWindow.packViewIntoSlot(builder, "general_slot", GeneralView, self.project.info)
- self.summaryView = MainWindow.packViewIntoSlot(builder, "summary_slot", SummaryView, self.project.summary)
- self.charactersView = MainWindow.packViewIntoSlot(builder, "characters_slot", CharactersView,
- self.project.characters)
- self.plotView = MainWindow.packViewIntoSlot(builder, "plot_slot", PlotView, self.project.plots)
- self.worldView = MainWindow.packViewIntoSlot(builder, "world_slot", WorldView, self.project.world)
- self.outlineView = MainWindow.packViewIntoSlot(builder, "outline_slot", OutlineView, self.project.outline)
- self.editorView = MainWindow.packViewIntoSlot(builder, "editor_slot", EditorView, self.project)
+ self.generalSlot = builder.get_object("general_slot")
+ self.summarySlot = builder.get_object("summary_slot")
+ self.charactersSlot = builder.get_object("characters_slot")
+ self.plotSlot = builder.get_object("plot_slot")
+ self.worldSlot = builder.get_object("world_slot")
+ self.outlineSlot = builder.get_object("outline_slot")
+ self.editorSlot = builder.get_object("editor_slot")
+
+ self.generalView = None
+ self.summaryView = None
+ self.charactersView = None
+ self.plotView = None
+ self.worldView = None
+ self.outlineView = None
+ self.editorView = None
self.startupWindow = StartupWindow(self)
self.aboutDialog = AboutDialog(self)
@@ -85,6 +68,9 @@ class MainWindow:
self.settingsWindow
]
+ self.recentChooserMenu = builder.get_object("recent_chooser_menu")
+ self.recentChooserMenu.connect("item-activated", self._recentAction)
+
bindMenuItem(builder, "open_menu_item", self._openAction)
bindMenuItem(builder, "save_menu_item", self._saveAction)
bindMenuItem(builder, "close_menu_item", self._closeAction)
@@ -94,18 +80,66 @@ class MainWindow:
bindMenuItem(builder, "frequency_menu_item", self._frequencyAction)
bindMenuItem(builder, "about_menu_item", self._aboutAction)
+ self.hide()
+
def getProject(self):
return self.project
- def openProject(self):
- pass
+ def openProject(self, path=None, dialog=False):
+ if self.project is not None:
+ self.closeProject()
+
+ if dialog:
+ return
+
+ if path is None:
+ return
+
+ self.project = Project(path)
+ self.project.load()
+
+ self.headerBar.set_subtitle(self.project.info.title)
+
+ self.generalView = packViewIntoSlot(self.generalSlot, GeneralView, self.project.info)
+ self.summaryView = packViewIntoSlot(self.summarySlot, SummaryView, self.project.summary)
+ self.charactersView = packViewIntoSlot(self.charactersSlot, CharactersView, self.project.characters)
+ self.plotView = packViewIntoSlot(self.plotSlot, PlotView, self.project.plots)
+ self.worldView = packViewIntoSlot(self.worldSlot, WorldView, self.project.world)
+ self.outlineView = packViewIntoSlot(self.outlineSlot, OutlineView, self.project.outline)
+ self.editorView = packViewIntoSlot(self.editorSlot, EditorView, self.project)
+
+ self.startupWindow.hide()
+ self.show()
def closeProject(self):
+ if self.project is not None:
+ self.generalView = unpackFromSlot(self.generalSlot, self.generalView)
+ self.summaryView = unpackFromSlot(self.summarySlot, self.summaryView)
+ self.charactersView = unpackFromSlot(self.charactersSlot, self.charactersView)
+ self.plotView = unpackFromSlot(self.plotSlot, self.plotView)
+ self.worldView = unpackFromSlot(self.worldSlot, self.worldView)
+ self.outlineView = unpackFromSlot(self.outlineSlot, self.outlineView)
+ self.editorView = unpackFromSlot(self.editorSlot, self.editorView)
+
+ del self.project
+ self.project = None
+
self.hide()
self.startupWindow.show()
def _openAction(self, menuItem: Gtk.MenuItem):
- self.openProject()
+ self.openProject(dialog=True)
+
+ def _recentAction(self, recentChooser: Gtk.RecentChooser):
+ uri = recentChooser.get_current_uri()
+ if uri is None:
+ return
+
+ path = parseFilenameFromURL(uri)
+ if path is None:
+ return
+
+ self.openProject(path)
def _saveAction(self, menuItem: Gtk.MenuItem):
self.getProject().save()
diff --git a/manuskript/ui/startupWindow.py b/manuskript/ui/startupWindow.py
index 1300949..cd98323 100644
--- a/manuskript/ui/startupWindow.py
+++ b/manuskript/ui/startupWindow.py
@@ -6,8 +6,8 @@ import gi
gi.require_version("Gtk", "3.0")
from gi.repository import GObject, Gtk, Handy
-from manuskript.data import Template, TemplateLevel, TemplateKind
-from manuskript.util import validInt, validString
+from manuskript.data import Template, TemplateKind
+from manuskript.util import validInt, validString, parseFilenameFromURL
from manuskript.ui.abstractDialog import AbstractDialog
from manuskript.ui.startup import TemplateEntry
@@ -25,6 +25,8 @@ class StartupWindow(AbstractDialog):
self.headerBar = None
self.templatesLeaflet = None
+ self.recentChooserMenu = None
+
self.templatesStore = None
self.fictionTemplatesStore = None
self.nonfictionTemplatesStore = None
@@ -45,6 +47,9 @@ class StartupWindow(AbstractDialog):
GObject.BindingFlags.SYNC_CREATE |
GObject.BindingFlags.INVERT_BOOLEAN)
+ self.recentChooserMenu = builder.get_object("recent_chooser_menu")
+ self.recentChooserMenu.connect("item-activated", self._recentAction)
+
bindMenuItem(builder, "open_menu_item", self._openAction)
bindMenuItem(builder, "quit_menu_item", self._quitAction)
@@ -154,6 +159,17 @@ class StartupWindow(AbstractDialog):
def _openAction(self, menuItem: Gtk.MenuItem):
self.mainWindow.openProject()
+ def _recentAction(self, recentChooser: Gtk.RecentChooser):
+ uri = recentChooser.get_current_uri()
+ if uri is None:
+ return
+
+ path = parseFilenameFromURL(uri)
+ if path is None:
+ return
+
+ self.mainWindow.openProject(path)
+
def _quitAction(self, menuItem: Gtk.MenuItem):
self.mainWindow.exit(True)
diff --git a/manuskript/ui/util.py b/manuskript/ui/util.py
index 8b3ed0b..c31661e 100644
--- a/manuskript/ui/util.py
+++ b/manuskript/ui/util.py
@@ -8,6 +8,7 @@ gi.require_version('GdkPixbuf', '2.0')
from gi.repository import GdkPixbuf, Gdk
from manuskript.data import Color, OutlineItem, OutlineText, OutlineFolder
+from manuskript.util import profileTime
def rgbaFromColor(color: Color) -> Gdk.RGBA:
@@ -34,6 +35,36 @@ def bindMenuItem(builder, id, action):
menuItem.connect("activate", action)
+def packViewIntoSlot(slot, view_cls, data=None):
+ if slot is None:
+ return None
+
+ for child in slot.get_children():
+ slot.remove(child)
+
+ try:
+ if data is None:
+ view = profileTime(view_cls)
+ else:
+ view = profileTime(view_cls, data)
+ except Exception:
+ return None
+
+ if view.widget is None:
+ return None
+
+ slot.pack_start(view.widget, True, True, 0)
+ return view
+
+
+def unpackFromSlot(slot, view):
+ if (slot is not None) and (view.widget is not None):
+ slot.remove(view.widget)
+
+ del view
+ return None
+
+
def iconByOutlineItemType(outlineItem: OutlineItem) -> str:
if type(outlineItem) is OutlineFolder:
return "folder-symbolic"
diff --git a/manuskript/util/__init__.py b/manuskript/util/__init__.py
index c5f2bd0..5023f68 100644
--- a/manuskript/util/__init__.py
+++ b/manuskript/util/__init__.py
@@ -4,6 +4,7 @@
import re
import time
import traceback
+import urllib.parse
from manuskript.util.counter import CounterKind, CharCounter, WordCounter, PageCounter
@@ -51,6 +52,18 @@ def safeFilename(filename: str, extension: str = None) -> str:
return re.sub(r"[^a-zA-Z0-9._\-+()]", "_", name)
+def parseFilenameFromURL(url: str) -> str | None:
+ result = urllib.parse.urlparse(url)
+
+ if result is None:
+ return None
+
+ if result.scheme != "file":
+ return None
+
+ return result.path
+
+
def countText(text: str, kind: CounterKind = CounterKind.WORDS):
if text is None:
return 0
diff --git a/ui/characters.glade b/ui/characters.glade
index e17aae5..6a2d588 100644
--- a/ui/characters.glade
+++ b/ui/characters.glade
@@ -923,20 +923,6 @@ summary
2
-
-
-
- False
- True
- end
- 3
-
-
False