Skip to content

Commit

Permalink
Merge pull request #317 from gaphor/copy-paste-with-parent-items
Browse files Browse the repository at this point in the history
Copy paste with parent items
  • Loading branch information
amolenaar committed May 13, 2020
2 parents 5e13d67 + 6500d23 commit 9911bc2
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 25 deletions.
5 changes: 5 additions & 0 deletions gaphor/UML/components/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def __init__(self, id=None, model=None):
self.watch("subject.appliedStereotype.slot", self.update_shapes)
self.watch("subject.appliedStereotype.slot.definingFeature.name")
self.watch("subject.appliedStereotype.slot.value", self.update_shapes)
self.watch("subject[Classifier].useCase", self.update_shapes)

show_stereotypes: attribute[int] = attribute("show_stereotypes", int)

Expand Down Expand Up @@ -51,6 +52,10 @@ def update_shapes(self, event=None):
draw=draw_border
)

def postload(self):
self.update_shapes()
super().postload()


def draw_component_icon(box, context, bounding_box):
bar_width = 12
Expand Down
4 changes: 4 additions & 0 deletions gaphor/UML/components/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ def update_shapes(self, event=None):
draw=draw_node
)

def postload(self):
self.update_shapes()
super().postload()


def draw_node(box, context, bounding_box):
cr = context.cairo
Expand Down
16 changes: 4 additions & 12 deletions gaphor/UML/components/tests/test_grouping.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,23 +111,17 @@ def test_grouping(self):
self.group(s, uc2)
assert 1 == len(uc2.subject.subject)

# Classifier.useCase is not navigable to UseCase
# self.assertEqual(2, len(s.subject.useCase))
self.assertEqual(2, len(s.subject.useCase))

def test_grouping_with_namespace(self):
"""Test adding an use case to a subsystem (with namespace)
"""
s = self.create(ComponentItem, UML.Component)
uc = self.create(UseCaseItem, UML.UseCase)

# manipulate namespace
c = self.element_factory.create(UML.Class)
attribute = self.element_factory.create(UML.Property)
c.ownedAttribute = attribute

self.group(s, uc)
assert 1 == len(uc.subject.subject)
assert s.subject.namespace is not uc.subject
assert s.subject in uc.subject.subject

def test_ungrouping(self):
"""Test removal of use case from subsystem
Expand All @@ -141,10 +135,8 @@ def test_ungrouping(self):

self.ungroup(s, uc1)
assert 0 == len(uc1.subject.subject)
# Classifier.useCase is not navigable to UseCase
# self.assertEqual(1, len(s.subject.useCase))
self.assertEqual(1, len(s.subject.useCase))

self.ungroup(s, uc2)
assert 0 == len(uc2.subject.subject)
# Classifier.useCase is not navigable to UseCase
# self.assertEqual(0, len(s.subject.useCase))
self.assertEqual(0, len(s.subject.useCase))
4 changes: 3 additions & 1 deletion gaphor/UML/uml.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class Classifier(Namespace, Type, RedefinableElement):
isAbstract: attribute[int]
ownedUseCase: relation_many[UseCase]
generalization: relation_many[Generalization]
useCase: relation_many[UseCase]
redefinedClassifier: relation_many[Classifier]
substitution: relation_many[Substitution]
attribute: relation_many[Property]
Expand Down Expand Up @@ -971,7 +972,8 @@ class BehaviorExecutionSpecification(ExecutionSpecification):
InstanceSpecification.slot = association(
"slot", Slot, composite=True, opposite="owningInstance"
)
UseCase.subject = association("subject", Classifier)
UseCase.subject = association("subject", Classifier, opposite="useCase")
Classifier.useCase = association("useCase", UseCase, opposite="subject")
Property.owningAssociation = association(
"owningAssociation", Association, upper=1, opposite="ownedEnd"
)
Expand Down
5 changes: 5 additions & 0 deletions gaphor/core/modeling/diagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ def create_as(self, type, id, parent=None, subject=None):
self.model.handle(DiagramItemCreated(self.model, item))
return item

def lookup(self, id):
for item in self.canvas.get_all_items():
if item.id == id:
return item

def unlink(self):
"""Unlink all canvas items then unlink this diagram."""

Expand Down
19 changes: 16 additions & 3 deletions gaphor/diagram/copypaste.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ def paste_named_element(copy_data: NamedElementCopy, diagram, lookup):
class PresentationCopy(NamedTuple):
cls: Type[Element]
data: Dict[str, Tuple[str, str]]
parent: Optional[str]


def copy_presentation(item) -> PresentationCopy:
Expand All @@ -149,7 +150,10 @@ def save_func(name, value):
buffer[name] = serialize(value)

item.save(save_func)
return PresentationCopy(cls=item.__class__, data=buffer)
parent = item.canvas.get_parent(item)
return PresentationCopy(
cls=item.__class__, data=buffer, parent=parent.id if parent else None
)


copy.register(Presentation, copy_presentation) # type: ignore[arg-type]
Expand All @@ -158,13 +162,16 @@ def save_func(name, value):

@paste.register
def paste_presentation(copy_data: PresentationCopy, diagram, lookup):
cls, data = copy_data
cls, data, parent = copy_data
item = diagram.create(cls)
if parent:
p = lookup(parent)
if p:
diagram.canvas.reparent(item, p)
for name, ser in data.items():
for value in deserialize(ser, lookup):
item.load(name, value)
item.canvas.update_matrices([item])
item.postload()
return item


Expand Down Expand Up @@ -209,11 +216,17 @@ def item_lookup(ref: str):
elif ref in copy_data.items:
new_items[ref] = paste(copy_data.items[ref], diagram, item_lookup)
return new_items[ref]
looked_up = diagram.lookup(ref)
if looked_up:
return looked_up
return element_lookup(ref)

for old_id, data in copy_data.items.items():
if old_id in new_items:
continue
new_items[old_id] = paste(data, diagram, item_lookup)

for new_item in new_items.values():
new_item.postload()

return set(new_items.values())
59 changes: 59 additions & 0 deletions gaphor/diagram/tests/test_copypaste_grouping.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import pytest

from gaphor import UML
from gaphor.diagram.copypaste import copy, paste
from gaphor.diagram.grouping import Group
from gaphor.diagram.tests.fixtures import copy_clear_and_paste
from gaphor.UML.components import ComponentItem, NodeItem


@pytest.fixture
def node_with_component(diagram, element_factory):
node = element_factory.create(UML.Node)
comp = element_factory.create(UML.Component)
node_item = diagram.create(NodeItem, subject=node)
comp_item = diagram.create(ComponentItem, subject=comp)

Group(node_item, comp_item).group()
diagram.canvas.reparent(comp_item, parent=node_item)

assert diagram.canvas.get_parent(comp_item) is node_item

return node_item, comp_item


def test_copy_paste_of_nested_item(diagram, element_factory, node_with_component):
node_item, comp_item = node_with_component

buffer = copy({comp_item})

(new_comp_item,) = paste(buffer, diagram, element_factory.lookup)

assert diagram.canvas.get_parent(new_comp_item) is node_item


def test_copy_paste_of_item_with_nested_item(
diagram, element_factory, node_with_component
):
node_item, comp_item = node_with_component

buffer = copy(set(node_with_component))

new_items = paste(buffer, diagram, element_factory.lookup)

new_node_item = next(i for i in new_items if isinstance(i, NodeItem))
new_comp_item = next(i for i in new_items if isinstance(i, ComponentItem))

assert diagram.canvas.get_parent(new_comp_item) is new_node_item


def test_copy_remove_paste_of_item_with_nested_item(
diagram, element_factory, node_with_component
):

new_items = copy_clear_and_paste(set(node_with_component), diagram, element_factory)

new_node_item = next(i for i in new_items if isinstance(i, NodeItem))
new_comp_item = next(i for i in new_items if isinstance(i, ComponentItem))

assert diagram.canvas.get_parent(new_comp_item) is new_node_item
14 changes: 5 additions & 9 deletions models/UML.gaphor
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<gaphor xmlns="http://gaphor.sourceforge.net/model" version="3.0" gaphor-version="1.2.0">
<gaphor xmlns="http://gaphor.sourceforge.net/model" version="3.0" gaphor-version="1.3.0">
<Association id="DCE:12A2B620-4B3C-11D7-B391-02BBFE4396CE">
<memberEnd>
<reflist>
Expand Down Expand Up @@ -18088,11 +18088,6 @@ BehavioredClassifier.</val>
<ref refid="DCE:3EEBCC18-6698-11D7-A84E-6C8643AD0CA4"/>
</reflist>
</memberEnd>
<ownedEnd>
<reflist>
<ref refid="DCE:3EEBCC18-6698-11D7-A84E-6C8643AD0CA4"/>
</reflist>
</ownedEnd>
<package>
<ref refid="DCE:5573D924-6690-11D7-A84E-6C8643AD0CA4"/>
</package>
Expand Down Expand Up @@ -18525,6 +18520,7 @@ BehavioredClassifier.</val>
<ref refid="DCE:BFC7B66E-4B37-11D7-B391-02BBFE4396CE"/>
<ref refid="DCE:38A9AE44-4B54-11D7-A5E6-6257AF3C5118"/>
<ref refid="DCE:E1377E1A-4B34-11D7-B391-02BBFE4396CE"/>
<ref refid="DCE:3EEBCC18-6698-11D7-A84E-6C8643AD0CA4"/>
</reflist>
</ownedAttribute>
<package>
Expand Down Expand Up @@ -22444,12 +22440,12 @@ specially for Gaphor</val>
<association>
<ref refid="DCE:3EEB934C-6698-11D7-A84E-6C8643AD0CA4"/>
</association>
<class_>
<ref refid="DCE:F10D682A-4A87-11D7-B08B-133D836EF880"/>
</class_>
<name>
<val>useCase</val>
</name>
<owningAssociation>
<ref refid="DCE:3EEB934C-6698-11D7-A84E-6C8643AD0CA4"/>
</owningAssociation>
<presentation>
<reflist/>
</presentation>
Expand Down

0 comments on commit 9911bc2

Please sign in to comment.