Handle recent files through the GTK facility

This saves me some headaches synching files with GUI and it's done GNOME
wide.
This commit is contained in:
Arjan Molenaar 2019-09-27 00:01:00 +02:00
parent da7a063f33
commit e95e3a7241
8 changed files with 105 additions and 42 deletions

View File

@ -15,7 +15,7 @@ from gaphor.misc.gidlethread import GIdleThread, Queue
from gaphor.misc.xmlwriter import XMLWriter
from gaphor.storage import storage, verify
import gaphor.ui
from gaphor.ui.event import FilenameChanged, RecentFilesUpdated, WindowClose
from gaphor.ui.event import FilenameChanged, WindowClose
from gaphor.ui.filedialog import FileDialog
from gaphor.ui.questiondialog import QuestionDialog
from gaphor.ui.statuswindow import StatusWindow
@ -110,6 +110,8 @@ class FileManager(Service, ActionProvider):
if filename != self._filename:
self._filename = filename
if self.event_manager:
self.event_manager.handle(FilenameChanged(self, filename))
self.update_recent_files(filename)
filename = property(get_filename, set_filename)
@ -136,11 +138,12 @@ class FileManager(Service, ActionProvider):
recent_files = self.recent_files
if new_filename and new_filename not in recent_files:
if new_filename:
if new_filename in recent_files:
recent_files.remove(new_filename)
recent_files.insert(0, new_filename)
recent_files = recent_files[0 : (MAX_RECENT - 1)]
self.recent_files = recent_files
self.event_manager.handle(RecentFilesUpdated(self, recent_files))
# TODO: Old code, remove.
for i in range(0, (MAX_RECENT - 1)):
@ -162,7 +165,6 @@ class FileManager(Service, ActionProvider):
filename = self.recent_files[index]
self.load(filename)
self.event_manager.handle(FilenameChanged(self, filename))
def load(self, filename):
"""Load the Gaphor model from the supplied file name. A status window
@ -358,8 +360,6 @@ class FileManager(Service, ActionProvider):
# main_window.select_element(diagram)
# main_window.show_diagram(diagram)
self.event_manager.handle(FilenameChanged(self))
@action(name="file-new-template", label=_("New from template"))
def action_new_from_template(self):
"""This menu action opens the new model from template dialog."""
@ -380,7 +380,6 @@ class FileManager(Service, ActionProvider):
if filename:
self.load(filename)
self.filename = None
self.event_manager.handle(FilenameChanged(self))
@action(
name="file-open",
@ -406,7 +405,6 @@ class FileManager(Service, ActionProvider):
if filename:
self.load(filename)
self.event_manager.handle(FilenameChanged(self, filename))
@action(
name="file-save",
@ -426,7 +424,6 @@ class FileManager(Service, ActionProvider):
if filename:
self.save(filename)
self.event_manager.handle(FilenameChanged(self, filename))
return True
else:
return self.action_save_as()
@ -455,7 +452,6 @@ class FileManager(Service, ActionProvider):
if filename:
self.save(filename)
self.event_manager.handle(FilenameChanged(self, filename))
return True
return False

View File

@ -14,6 +14,9 @@ import os.path
from gaphor.ui.actiongroup import apply_application_actions
APPLICATION_ID = "org.gaphor.Gaphor"
icon_theme = Gtk.IconTheme.get_default()
with importlib.resources.path("gaphor.ui", "pixmaps") as path:
icon_theme.append_search_path(str(path))
@ -31,7 +34,7 @@ css_provider.load_from_data(b"#diagram-tab { background: white }")
def run(application, model):
gtk_app = Gtk.Application(
application_id="org.gaphor.Gaphor", flags=Gio.ApplicationFlags.FLAGS_NONE
application_id=APPLICATION_ID, flags=Gio.ApplicationFlags.FLAGS_NONE
)
def app_startup(app):

View File

@ -38,13 +38,3 @@ class FilenameChanged:
def __init__(self, service, filename=None):
self.service = service
self.filename = filename
class RecentFilesUpdated:
"""
Event class used to send state changes on the Undo Manager.
"""
def __init__(self, service, recent_files=[]):
self.service = service
self.recent_files = recent_files

View File

@ -6,6 +6,7 @@ from typing import List, Tuple
import logging
import os.path
from pathlib import Path
import importlib.resources
from gi.repository import Gio, Gdk, Gtk, GLib
@ -23,6 +24,7 @@ from gaphor.core import (
from gaphor.abc import Service, ActionProvider
from gaphor.UML.event import AttributeChangeEvent, FlushFactoryEvent
from gaphor.services.undomanager import UndoManagerStateChanged
from gaphor.ui import APPLICATION_ID
from gaphor.ui.abc import UIComponent
from gaphor.ui.actiongroup import window_action_group
from gaphor.ui.accelmap import load_accel_map, save_accel_map
@ -43,6 +45,28 @@ ICONS = (
)
class RecentFilesMenu(Gio.Menu):
def __init__(self, recent_manager):
super().__init__()
self._on_recent_manager_changed(recent_manager)
recent_manager.connect("changed", self._on_recent_manager_changed)
def _on_recent_manager_changed(self, recent_manager):
self.remove_all()
home = str(Path.home())
for item in recent_manager.get_items():
if APPLICATION_ID in item.get_applications():
menu_item = Gio.MenuItem.new(
item.get_uri_display().replace(home, "~"), None
)
self.append_item(menu_item)
if self.get_n_items() > 9:
break
if self.get_n_items() == 0:
self.append_item(Gio.MenuItem.new(_("No recently opened models"), None))
def hamburger_menu():
button = Gtk.MenuButton()
image = Gtk.Image.new_from_icon_name("open-menu-symbolic", Gtk.IconSize.MENU)
@ -56,40 +80,30 @@ def create_dummy_popover(parent):
model = Gio.Menu.new()
part = Gio.Menu.new()
part.append_item(Gio.MenuItem.new(_("Save As..."), "win.file-save-as"))
part.append(_("New"), "win.file-new")
part.append(_("New from Template"), "win.file-new-template")
model.append_section(None, part)
part = Gio.Menu.new()
part.append_item(
Gio.MenuItem.new(_("Hand-Drawn Style"), "win.diagram-drawing-style")
)
part.append(_("Save As..."), "win.file-save-as")
model.append_section(None, part)
part = Gio.Menu.new()
part.append_item(Gio.MenuItem.new(_("Preferences"), "app.preferences"))
part.append_item(Gio.MenuItem.new(_("About Gaphor"), "app.about"))
part.append(_("Hand-Drawn Style"), "win.diagram-drawing-style")
model.append_section(None, part)
part = Gio.Menu.new()
part.append(_("Preferences"), "app.preferences")
part.append(_("About Gaphor"), "app.about")
model.append_section(None, part)
return Gtk.Popover.new_from_model(parent, model)
def create_recent_files_popover(parent):
model = Gio.Menu.new()
part = Gio.Menu.new()
menu_item = Gio.MenuItem.new(_("New"), "win.file-new")
menu_item.set_attribute_value("iconic", GLib.Variant.new_boolean(True))
part.append_item(menu_item)
part.append_item(Gio.MenuItem.new(_("New from Template"), "win.file-new-template"))
model.append_section(None, part)
part = Gio.Menu.new()
part.append_item(
Gio.MenuItem.new("~/gaphor/UML/uml2.gaphor", "recent.file-open-recent-1")
return Gtk.Popover.new_from_model(
parent, RecentFilesMenu(Gtk.RecentManager.get_default())
)
model.append_section(None, part)
return Gtk.Popover.new_from_model(parent, model)
class MainWindow(Service, ActionProvider):

31
gaphor/ui/recentfiles.py Normal file
View File

@ -0,0 +1,31 @@
import os
from gi.repository import GLib, Gtk
from gaphor.abc import Service
from gaphor.core import event_handler
from gaphor.ui import APPLICATION_ID
from gaphor.ui.event import FilenameChanged
class RecentFiles(Service):
def __init__(self, event_manager, recent_manager=None):
print("Recent files initated")
self.event_manager = event_manager
self.recent_manager = recent_manager or Gtk.RecentManager.get_default()
self.event_manager.subscribe(self._on_filename_changed)
def shutdown(self):
self.event_manager.unsubscribe(self._on_filename_changed)
@event_handler(FilenameChanged)
def _on_filename_changed(self, event):
filename = event.filename
if not filename:
return
uri = GLib.filename_to_uri(os.path.abspath(filename))
meta = Gtk.RecentData()
meta.app_name = APPLICATION_ID
meta.app_exec = f"{GLib.get_prgname()} %u"
meta.mime_type = "application/x-gaphor"
self.recent_manager.add_full(uri, meta)

View File

@ -0,0 +1,27 @@
import pytest
from gaphor.services.eventmanager import EventManager
from gaphor.ui.event import FilenameChanged
from gaphor.ui.recentfiles import RecentFiles
class RecentManagerStub:
def __init__(self):
self.items = []
def add_full(self, uri, meta):
self.items.append(uri)
@pytest.fixture
def event_manager():
return EventManager()
def test_add_new_recent_file(event_manager):
recent_manager = RecentManagerStub()
recent_files = RecentFiles(event_manager, recent_manager)
event_manager.handle(FilenameChanged(None, "testfile.gaphor"))
assert len(recent_manager.items) == 1
assert recent_manager.items[0].startswith("file:///"), recent_manager.items[0]

View File

@ -59,6 +59,7 @@ gaphorconvert = 'gaphor.tools.gaphorconvert:main'
"undo_manager" = "gaphor.services.undomanager:UndoManager"
"element_factory" = "gaphor.UML.elementfactory:ElementFactory"
"file_manager" = "gaphor.services.filemanager:FileManager"
"recent_files" = "gaphor.ui.recentfiles:RecentFiles"
"diagram_export_manager" = "gaphor.services.diagramexportmanager:DiagramExportManager"
"action_manager" = "gaphor.services.actionmanager:ActionManager"
"main_window" = "gaphor.ui.mainwindow:MainWindow"

View File

@ -108,6 +108,7 @@ setup(
"undo_manager = gaphor.services.undomanager:UndoManager",
"element_factory = gaphor.UML.elementfactory:ElementFactory",
"file_manager = gaphor.services.filemanager:FileManager",
"recent_files = gaphor.ui.recentfiles:RecentFiles",
"diagram_export_manager = gaphor.services.diagramexportmanager:DiagramExportManager",
"action_manager = gaphor.services.actionmanager:ActionManager",
"main_window = gaphor.ui.mainwindow:MainWindow",