Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show in diagram #230

Merged
merged 5 commits into from
Nov 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion gaphor/misc/generic/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ class Manager:
to corresponding methods of class.
"""

registry: Registry[HandlerSet]

def __init__(self) -> None:
axes = (("event_type", TypeAxis()),)
self.registry = Registry[HandlerSet](*axes)
self.registry = Registry(*axes)

def subscribe(self, handler: Handler, event_type: Type[Event]) -> None:
""" Subscribe ``handler`` to specified ``event_type``"""
Expand Down
19 changes: 9 additions & 10 deletions gaphor/ui/diagrampage.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,12 @@ def get_toolbox_shortcuts(self):
return shortcuts

@event_handler(ElementDeleted)
def _on_element_delete(self, event):
def _on_element_delete(self, event: ElementDeleted):
if event.element is self.diagram:
self.close()

@event_handler(PropertyChanged)
def _on_sloppy_lines(self, event=None):
def _on_sloppy_lines(self, event: PropertyChanged = None):
if not event or event.key == "diagram.sloppiness":
self.set_drawing_style(event and event.new_value or 0.0)

Expand Down Expand Up @@ -305,15 +305,14 @@ def _on_key_press_event(self, view, event):
GTK's accelerators) since otherwise this key will confuse the text
edit stuff.
"""
if view.is_focus():
if event.keyval == Gdk.KEY_Delete and (
event.get_state() == 0 or event.get_state() & Gdk.ModifierType.MOD2_MASK
):
self.delete_selected_items()
elif event.keyval == Gdk.KEY_BackSpace and (
if (
view.is_focus()
and event.keyval in (Gdk.KEY_Delete, Gdk.KEY_BackSpace)
and (
event.get_state() == 0 or event.get_state() & Gdk.ModifierType.MOD2_MASK
):
self.delete_selected_items()
)
):
self.delete_selected_items()

def _on_view_selection_changed(self, view, selection_or_focus):
self.event_manager.handle(
Expand Down
73 changes: 54 additions & 19 deletions gaphor/ui/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
a result only classifiers are shown here.
"""

from __future__ import annotations

import logging

from typing import Optional
from typing import Optional, TYPE_CHECKING

from gi.repository import GObject, Gio, Gdk, Gtk
from gi.repository import GLib, Gio, GObject, Gdk, Gtk

from gaphor import UML
from gaphor.UML.event import (
Expand All @@ -25,6 +27,10 @@
from gaphor.ui.abc import UIComponent
from gaphor.ui.iconname import get_icon_name

if TYPE_CHECKING:
from gaphor.UML.elementfactory import ElementFactory
from gaphor.services.eventmanager import EventManager

# The following items will be shown in the treeview, although they
# are UML.Namespace elements.
_default_filter_list = (
Expand Down Expand Up @@ -59,10 +65,10 @@ class NamespaceView(Gtk.TreeView):
Gtk.TargetEntry.new("gaphor/element-id", 0, TARGET_ELEMENT_ID),
]

def __init__(self, model, factory):
def __init__(self, model: Gtk.TreeModel, element_factory: ElementFactory):
GObject.GObject.__init__(self)
self.set_model(model)
self.factory = factory
self.element_factory = element_factory

self.set_property("headers-visible", False)
self.set_property("search-column", 0)
Expand Down Expand Up @@ -211,7 +217,10 @@ def on_drag_data_received(self, context, x, y, selection, info, time):

if drop_info:
model = self.get_model()
element = self.factory.lookup(element_id)
element = self.element_factory.lookup(element_id)
assert isinstance(
element, (UML.Diagram, UML.Package, UML.Type)
), f"Element with id {element_id} is not part of the model"
path, position = drop_info
iter = model.get_iter(path)
dest_element = model.get_value(iter, 0)
Expand Down Expand Up @@ -262,12 +271,12 @@ class Namespace(UIComponent):

title = _("Namespace")

def __init__(self, event_manager, element_factory):
def __init__(self, event_manager: EventManager, element_factory: ElementFactory):
self.event_manager = event_manager
self.element_factory = element_factory
self._namespace: Optional[NamespaceView] = None
self.model = Gtk.TreeStore.new([object])
self.filter = _default_filter_list
self.toplevel_types = _default_filter_list

def init(self):
# Event handler registration is in a separate function,
Expand All @@ -276,7 +285,7 @@ def init(self):
em = self.event_manager
em.subscribe(self._on_element_create)
em.subscribe(self._on_element_delete)
em.subscribe(self._on_model_factory)
em.subscribe(self._on_model_ready)
em.subscribe(self._on_flush_factory)
em.subscribe(self._on_association_set)
em.subscribe(self._on_attribute_change)
Expand All @@ -293,7 +302,7 @@ def close(self):
em = self.event_manager
em.unsubscribe(self._on_element_create)
em.unsubscribe(self._on_element_delete)
em.unsubscribe(self._on_model_factory)
em.unsubscribe(self._on_model_ready)
em.unsubscribe(self._on_flush_factory)
em.unsubscribe(self._on_association_set)
em.unsubscribe(self._on_attribute_change)
Expand Down Expand Up @@ -330,11 +339,12 @@ def sort_func(model, iter_a, iter_b, userdata):
view.connect_after("cursor-changed", self._on_view_cursor_changed)
view.connect("destroy", self._on_view_destroyed)
self._namespace = view
self._on_model_factory()
self._on_model_ready()

return scrolled_window

def namespace_popup_model(self):
assert self._namespace
model = Gio.Menu.new()

part = Gio.Menu.new()
Expand All @@ -351,6 +361,23 @@ def namespace_popup_model(self):
part.append(_("De_lete"), "tree-view.delete")
model.append_section(None, part)

element = self._namespace.get_selected_element()

part = Gio.Menu.new()
for presentation in element.presentation:
diagram = presentation.canvas.diagram
menu_item = Gio.MenuItem.new(
_(f'Show in "{diagram.name}"'), "tree-view.show-in-diagram"
)
menu_item.set_attribute_value("target", GLib.Variant.new_string(diagram.id))
part.append_item(menu_item)

# Play it safe with an (arbitrary) upper bound
if part.get_n_items() > 29:
break

if part.get_n_items() > 0:
model.append_section(None, part)
return model

def iter_for_element(self, element, old_namespace=0):
Expand Down Expand Up @@ -380,12 +407,12 @@ def iter_for_element(self, element, old_namespace=0):

def _visible(self, element):
# Spacial case: Non-navigable properties
return type(element) in self.filter and not (
return type(element) in self.toplevel_types and not (
isinstance(element, UML.Property) and element.namespace is None
)

@event_handler(ModelReady)
def _on_model_factory(self, event=None):
def _on_model_ready(self, event=None):
"""
Load a new model completely.
"""
Expand All @@ -404,7 +431,9 @@ def add(element, iter=None):
self.model.clear()

toplevel = self.element_factory.select(
lambda e: type(e) in self.filter and not e.namespace
lambda e: isinstance(e, UML.NamedElement)
and type(e) in self.toplevel_types
and not e.namespace
)

for element in toplevel:
Expand All @@ -420,24 +449,24 @@ def _on_flush_factory(self, event):
self.model.clear()

@event_handler(ElementCreated)
def _on_element_create(self, event):
def _on_element_create(self, event: ElementCreated):
element = event.element
if self._visible(element) and not self.iter_for_element(element):
iter = self.iter_for_element(element.namespace)
self.model.append(iter, [element])

@event_handler(ElementDeleted)
def _on_element_delete(self, event):
def _on_element_delete(self, event: ElementDeleted):
element = event.element
if type(element) in self.filter:
if type(element) in self.toplevel_types:
iter = self.iter_for_element(element)
# iter should be here, unless we try to delete an element who's
# parent element is already deleted, so let's be lenient.
if iter:
self.model.remove(iter)

@event_handler(DerivedSet)
def _on_association_set(self, event):
def _on_association_set(self, event: DerivedSet):

element = event.element
if event.property is UML.NamedElement.namespace:
Expand All @@ -454,7 +483,7 @@ def _on_association_set(self, event):
self.model.append(new_iter, [element])

@event_handler(AttributeUpdated)
def _on_attribute_change(self, event):
def _on_attribute_change(self, event: AttributeUpdated):
"""
Element changed, update appropriate row.
"""
Expand All @@ -479,6 +508,8 @@ def _on_view_event(self, view, event):
menu = Gtk.Menu.new_from_model(self.namespace_popup_model())
menu.attach_to_widget(view, None)
menu.popup_at_pointer(event)
elif event.type == Gdk.EventType.KEY_PRESS and event.key.keyval == Gdk.KEY_F2:
self.tree_view_rename_selected()

def _on_view_row_activated(self, view, path, column):
"""
Expand Down Expand Up @@ -543,10 +574,14 @@ def tree_view_open_selected(self):
# TODO: Candidate for adapter?
if isinstance(element, UML.Diagram):
self.event_manager.handle(DiagramOpened(element))

else:
log.debug(f"No action defined for element {type(element).__name__}")

@action(name="tree-view.show-in-diagram")
def tree_view_show_in_diagram(self, diagam_id: str):
element = self.element_factory.lookup(diagam_id)
self.event_manager.handle(DiagramOpened(element))

@action(name="tree-view.rename", shortcut="F2")
def tree_view_rename_selected(self):
assert self._namespace
Expand Down