root/gaphor/tags/gaphor-0.12.5/gaphor/services/copyservice.py

Revision 1861, 5.3 kB (checked in by arj..@yirdis.nl, 1 year ago)

Changed !camelCase method to underscore notation, according to coding guidelines.

Line 
1 """
2 Copy / Paste functionality
3 """
4
5 from zope import interface, component
6 import gaphas
7 from gaphor import UML
8 from gaphor.interfaces import IService, IActionProvider
9 from gaphor.ui.interfaces import IDiagramSelectionChange
10 from gaphor.core import _, inject, action, build_action_group, transactional
11
12
13 class CopyService(object):
14     """
15     Copy/Cut/Paste functionality required a lot of thinking:
16
17     Store a list of DiagramItems that have to be copied in a global
18     'copy-buffer'.
19
20     - in order to make copy/paste work, the load/save functions should be
21       generatlised to allow a subset to be saved/loaded (which is needed
22       anyway for exporting/importing stereotype Profiles).
23     - How many data should be saved? (e.g. we copy a diagram item, remove it
24       (the underlaying UML element is removed) and the paste the copied item.
25       The diagram should act as if we have placed a copy of the removed item
26       on the canvas and make the uml element visible again.
27     """
28
29     interface.implements(IService, IActionProvider)
30
31     element_factory = inject('element_factory')
32     gui_manager = inject('gui_manager')
33
34     menu_xml = """
35       <ui>
36         <menubar action="mainwindow">
37           <menu action="edit">
38             <placeholder name="primary">
39               <menuitem action="edit-copy" />
40               <menuitem action="edit-paste" />
41             </placeholder>
42           </menu>
43         </menubar>
44       </ui>
45     """
46
47     def __init__(self):
48         self.copy_buffer = set()
49         self.action_group = build_action_group(self)
50
51     def init(self, app):
52         self._app = app
53         self.action_group.get_action('edit-copy').props.sensitive = False
54         self.action_group.get_action('edit-paste').props.sensitive = False
55        
56         app.register_handler(self._update)
57
58     def shutdown(self):
59         self.copy_buffer = set()
60         self._app.unregister_handler(self._update)
61
62     @component.adapter(IDiagramSelectionChange)
63     def _update(self, event):
64         diagram_view = event.diagram_view
65         self.action_group.get_action('edit-copy').props.sensitive = bool(diagram_view.selected_items)
66
67     def copy(self, items):
68         if items:
69             self.copy_buffer = set(items)
70             self.action_group.get_action('edit-paste').props.sensitive = True
71
72     def _load_element(self, name, value):
73         """
74         Copy an element, preferbly from the list of new items,
75         otherwise from the element factory.
76         If it does not exist there, do not copy it!
77         """
78         item = self._new_items.get(value.id)
79         if item:
80             self._item.load(name, item)
81         else:
82             item = self.element_factory.lookup(value.id)
83             if item:
84                 self._item.load(name, item)
85
86     def copy_func(self, name, value, reference=False):
87         if reference or isinstance(value, UML.Element):
88             self._load_element(name, value)
89         elif isinstance(value, UML.collection):
90             for item in value:
91                 self._load_element(name, item)
92         elif isinstance(value, gaphas.Item):
93             self._load_element(name, value)
94         else:
95             # Plain attribute
96             self._item.load(name, str(value))
97
98     @transactional
99     def paste(self, diagram):
100         """
101         Paste items in the copy-buffer to the diagram
102         """
103         canvas = diagram.canvas
104         if not canvas:
105             return
106
107         copy_items = [ c for c in self.copy_buffer if c.canvas ]
108
109         # Mapping original id -> new item
110         self._new_items = {}
111
112         # Create new id's that have to be used to create the items:
113         for ci in copy_items:
114             self._new_items[ci.id] = diagram.create(type(ci))
115
116         # Copy attributes and references. References should be
117         #  1. in the ElementFactory (hence they are model elements)
118         #  2. refered to in new_items
119         #  3. canvas property is overridden
120         for ci in copy_items:
121             self._item = self._new_items[ci.id]
122             ci.save(self.copy_func)
123
124         # move pasted items a bit, so user can see result of his action :)
125         # update items' matrix immediately
126         # TODO: if it is new canvas, then let's not move, how to do it?
127         for item in self._new_items.values():
128             item.matrix.translate(10, 10)
129             canvas.update_matrix(item)
130
131         # solve internal constraints of items immediately as item.postload
132         # reconnects items and all handles has to be in place
133         canvas.solver.solve()
134         for item in self._new_items.values():
135             item.postload()
136
137
138     @action(name='edit-copy', stock_id='gtk-copy')
139     def _copy(self):
140         view = self.gui_manager.main_window.get_current_diagram_view()
141         if view.is_focus():
142             items = view.selected_items
143             copy_items = []
144             for i in items:
145                 copy_items.append(i)
146             self.copy(copy_items)
147
148     @action(name='edit-paste', stock_id='gtk-paste')
149     def paste_action(self):
150         view = self.gui_manager.main_window.get_current_diagram_view()
151         diagram = self.gui_manager.main_window.get_current_diagram()
152         if not view:
153             return
154
155         self.paste(diagram)
156
157         view.unselect_all()
158
159         for item in self._new_items.values():
160             view.select_item(item)
161
162
163 # vim:sw=4:et:ai
Note: See TracBrowser for help on using the browser.