Merge lp:~gaspa/a4/presentation-objects into lp:a4
- presentation-objects
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 106 | ||||
Proposed branch: | lp:~gaspa/a4/presentation-objects | ||||
Merge into: | lp:a4 | ||||
Prerequisite: | lp:~gaspa/a4/neweditor | ||||
Diff against target: |
672 lines (+400/-84) 7 files modified
a4lib/app.py (+34/-4) a4lib/editor.py (+58/-48) a4lib/presentation.py (+10/-32) a4lib/presentation_objects.py (+139/-0) a4lib/region.py (+22/-0) data/window_main.glade (+59/-0) tests/objects/polaroid.svg (+78/-0) |
||||
To merge this branch: | bzr merge lp:~gaspa/a4/presentation-objects | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrea Gasparini | Pending | ||
Review via email: mp+41838@code.launchpad.net |
Commit message
Description of the change
Refactoring code for a better and simpler use of object in the editor.
it should add some base objects and a test object (PolaroidObject).
Andrea Colangelo (warp10) wrote : | # |
- 91. By Andrea Gasparini
-
added test svg for "polaroid" object
- 92. By Andrea Gasparini
-
refactoring of objects and editor modes
- 93. By Andrea Gasparini
-
started on menus
- 94. By Andrea Gasparini
-
merged from trunk a couple of fixes
- 95. By Andrea Gasparini
-
select button,working on getting the right position of shapes
- 96. By Andrea Gasparini
-
drawing rectangles in right position,even when region is rotated (but still no rotated rect)
- 97. By Andrea Gasparini
-
rotated shapes work(!!)
- 98. By Andrea Gasparini
-
little cleaning
- 99. By Andrea Gasparini
-
reenable button for "polaroid" object
- 100. By Andrea Gasparini
-
polaroid objects working
- 101. By Andrea Gasparini
-
polaroid object has now proportional border
- 102. By Andrea Gasparini
-
text in polaroid start working
- 103. By Andrea Gasparini
-
undo saved test image
- 104. By Andrea Gasparini
-
added images, near to work
- 105. By Andrea Gasparini
-
image objects work
- 106. By Andrea Gasparini
-
little cleaning
Andrea Gasparini (gaspa) wrote : | # |
now bugs should be fixed.
Re-writing down what this branch aims to do: implement a (still stub) system to handle graphics objects, both simple as rects/circles and more complex as the image or "polaroid" object.
What comes toghether with these changes:
* a method in region.py that helps calculate point coordinates between screen coords and svg coords.
* I added a sample of the polaroid object, perhaps we could keep them in that directory (I'd like to have more 'complex objects' than simple ones, that can be drawn simply by a normal svg editor)
* there is a couple of unused button in the editor toolbar (things like 'frames','notes') that could be drop... perhaps :p
If someone can do a little test if everything seems to work, I'd merge this to trunk in the next days.
Preview Diff
1 | === modified file 'a4lib/app.py' |
2 | --- a4lib/app.py 2010-12-01 23:24:34 +0000 |
3 | +++ a4lib/app.py 2010-12-22 22:58:09 +0000 |
4 | @@ -66,10 +66,10 @@ |
5 | # Try to open a file. |
6 | if is_edited: |
7 | self.player = Editor(file_name, self.drawing_area, |
8 | - self.builder.get_object('iconview1'),force_loading) |
9 | + self.builder.get_object('iconview1'), force_loading) |
10 | else: |
11 | self.player = GtkCairoPlayer(file_name, self.drawing_area, |
12 | - force_loading) |
13 | + force_loading) |
14 | except PresentationError as error: |
15 | # The file doesn't exist, or is not an SVG file. Show an error |
16 | # dialog and don't do anything else. |
17 | @@ -241,11 +241,34 @@ |
18 | widget.set_active(False) |
19 | self.builder.get_object('drawing_toolbar').set_visible(False) |
20 | |
21 | + def _ask_image_dialog(self): |
22 | + dialog = gtk.FileChooserDialog( |
23 | + None, None, gtk.FILE_CHOOSER_ACTION_OPEN, ( |
24 | + gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, |
25 | + gtk.STOCK_SAVE, gtk.RESPONSE_OK)) |
26 | + dialog.set_default_response(gtk.RESPONSE_OK) |
27 | + # If an another file has been opened, use its location as starting |
28 | + # folder for the dialog. |
29 | + if self.last_path is not None: |
30 | + dialog.set_current_folder(path) |
31 | + |
32 | + # Show the dialog, back up the relevant properties and destroy it. |
33 | + response = dialog.run() |
34 | + file_name = dialog.get_filename() |
35 | + dialog.destroy() |
36 | + # Save in the specified location |
37 | + if response == gtk.RESPONSE_OK and self.player: |
38 | + return file_name |
39 | + return "" |
40 | + |
41 | def on_toggle_tool(self, widget): |
42 | if not isinstance(self.player, Editor): |
43 | return |
44 | - toggle_modes = {'PathToolbutton': 'path', |
45 | + toggle_modes = {'SelectToolbutton': 'select', |
46 | + 'PathToolbutton': 'path', |
47 | 'ShapesToolbutton': 'rect', |
48 | + 'ImageToolbutton': 'image', |
49 | + 'PolaroidToolbutton': 'polaroid', |
50 | 'TextToolbutton': 'text'} |
51 | |
52 | widget_name = gtk.Buildable.get_name(widget) |
53 | @@ -257,7 +280,14 @@ |
54 | obj = self.builder.get_object(i) |
55 | if obj.get_active(): |
56 | obj.set_active(False) |
57 | - self.player.set_editor_mode(toggle_modes[widget_name]) |
58 | + object_name = toggle_modes[widget_name] |
59 | + if object_name in ['path', 'select']: |
60 | + self.player.set_editor_mode(object_name) |
61 | + else: |
62 | + self.player.set_editor_mode('objects') |
63 | + self.player.set_next_new_object(toggle_modes[widget_name]) |
64 | + if toggle_modes[widget_name] == 'image': |
65 | + self.player.set_target_image(self._ask_image_dialog()) |
66 | if not any(self.builder.get_object(i).get_active() for i in toggle_modes.keys()): |
67 | self.player.set_editor_mode() |
68 | self.drawing_area.queue_draw() |
69 | |
70 | === modified file 'a4lib/editor.py' |
71 | --- a4lib/editor.py 2010-12-01 23:24:34 +0000 |
72 | +++ a4lib/editor.py 2010-12-22 22:58:09 +0000 |
73 | @@ -9,6 +9,14 @@ |
74 | from a4lib.player import GtkCairoPlayer |
75 | from a4lib.region import Region |
76 | |
77 | +from presentation_objects import RectObject, TextObject, PolaroidObject, ImageObject |
78 | + |
79 | + |
80 | +def object_class_from_name(name): |
81 | + obj_dict = {'rect': RectObject, 'text': TextObject, |
82 | + 'image': ImageObject, 'polaroid': PolaroidObject} |
83 | + return obj_dict[name] |
84 | + |
85 | |
86 | class Editor(GtkCairoPlayer): |
87 | |
88 | @@ -25,15 +33,18 @@ |
89 | self.treeview.set_item_width(120) |
90 | self.treeview.set_size_request(130, -1) |
91 | |
92 | - ## used to keep status of each tool. |
93 | + ## used to keep status of mouse actions: |
94 | + ## value currently used: [None,'start','moving'] |
95 | self.action_status = None |
96 | ## tells if we should draw the rect of the path. |
97 | self.show_path = None |
98 | ## tell the tool we're using and how we should behave. |
99 | - self.editor_modes = ['select', 'path', 'rect', 'text'] |
100 | + self.editor_modes = ['select', 'path', 'objects'] |
101 | self.editor_mode = 'select' |
102 | + self.new_object_name = None |
103 | ## the selected object id: |
104 | self.current_object = None |
105 | + self.target_filename = None |
106 | |
107 | for i in xrange(0, len(self.presentation.path)): |
108 | region = self.presentation.get_path_region(i) |
109 | @@ -42,64 +53,72 @@ |
110 | def set_show_path(self, flag=True): |
111 | self.show_path = flag |
112 | |
113 | + # objects mode: rect/polaroid |
114 | + def set_next_new_object(self, obj_name): |
115 | + self.new_object_name = obj_name |
116 | + |
117 | + # select/objects |
118 | def set_editor_mode(self, status='select'): |
119 | if status in self.editor_modes: |
120 | self.editor_mode = status |
121 | |
122 | + def set_target_image(self, file_name): |
123 | + self.target_filename = file_name |
124 | + |
125 | def on_area_button_press_event(self, widget, event): |
126 | + event_point = (event.x, event.y) |
127 | + winsize = self.area.window.get_size() |
128 | + cr = self.current_region |
129 | + point = cr.get_point_from_window_coordinates(winsize, event_point) |
130 | + |
131 | + self.selection_start_position = (event.x, event.y) |
132 | + self.action_status = 'start' |
133 | if self.editor_mode == 'select': |
134 | pass # presentation.select(something) |
135 | - elif self.editor_mode == 'rect': |
136 | - props = {'x': event.x, 'y': event.y} |
137 | - myid = self.presentation.draw_rect(0, 0, props) |
138 | - self.current_object = myid |
139 | - self.action_status = "selection" |
140 | - self.selection_start_position = (event.x, event.y) |
141 | - elif self.editor_mode == 'text': |
142 | - props = {'x': event.x, 'y': event.y} |
143 | - myid = self.presentation.draw_text("", props) |
144 | - self.current_object = myid |
145 | - self.action_status = "selection" |
146 | - self.selection_start_position = (event.x, event.y) |
147 | - elif self.editor_mode == 'path' and event.state & gtk.gdk.CONTROL_MASK: |
148 | - self.action_status = "selection" |
149 | - self.selection_start_position = (event.x, event.y) |
150 | + elif self.editor_mode == 'objects': |
151 | + cls = object_class_from_name(self.new_object_name) |
152 | + self.current_object = cls(x=point[0], y=point[1], |
153 | + rotate=self.current_region.rotate) |
154 | + if self.new_object_name == 'image': |
155 | + self.current_object.set_image(self.target_filename) |
156 | + self.presentation.add_object(self.current_object) |
157 | else: |
158 | GtkCairoPlayer.on_area_button_press_event(self, widget, event) |
159 | |
160 | def on_area_button_release_event(self, widget, event): |
161 | GtkCairoPlayer.on_area_button_release_event(self, widget, event) |
162 | - if self.editor_mode == 'path' and self.action_status is not None: |
163 | + if self.editor_mode == 'path' and event.state & gtk.gdk.CONTROL_MASK: |
164 | rect = (self.selection_start_position, self.mouse_position) |
165 | self.add_treeview_icon(rect) |
166 | - if self.editor_mode == 'rect' and self.action_status is not None: |
167 | - self.current_object = None |
168 | - if self.editor_mode != 'text': |
169 | - self.action_status = None |
170 | + elif self.editor_mode == 'objects': |
171 | + pass |
172 | + self.action_status = None |
173 | |
174 | def on_area_motion_notify_event(self, widget, event): |
175 | if self.action_status is None: |
176 | GtkCairoPlayer.on_area_motion_notify_event(self, widget, event) |
177 | - elif self.editor_mode == 'path': |
178 | - self.mouse_position = (event.x, event.y) |
179 | - self.area.queue_draw() |
180 | - elif self.editor_mode == 'rect' and self.action_status is not None: |
181 | - if self.current_object: |
182 | - ## x/y is ignored |
183 | - props = {'id': self.current_object} |
184 | - w = event.x - self.selection_start_position[0] |
185 | - h = event.y - self.selection_start_position[1] |
186 | - myid = self.presentation.draw_rect(w, h, props) |
187 | - self.mouse_position = (event.x, event.y) |
188 | - self.area.queue_draw() |
189 | + else: |
190 | + if self.editor_mode == 'path': |
191 | + if event.state & gtk.gdk.CONTROL_MASK: |
192 | + self.mouse_position = (event.x, event.y) |
193 | + self.area.queue_draw() |
194 | + elif self.editor_mode == 'objects' and self.action_status: |
195 | + if self.current_object: |
196 | + winsize = self.area.window.get_size() |
197 | + cr = self.current_region |
198 | + scale = self.current_region.get_window_scale_factor(winsize) |
199 | + w = (event.x - self.selection_start_position[0]) / scale |
200 | + h = (event.y - self.selection_start_position[1]) / scale |
201 | + self.current_object.set_size(w, h) |
202 | + self.mouse_position = (event.x, event.y) |
203 | + self.area.queue_draw() |
204 | + self.action_status = 'moving' |
205 | |
206 | def on_area_key_press(self, widget, event): |
207 | - if self.editor_mode == 'text' and self.action_status: |
208 | + if self.current_object: |
209 | if True: # premo tutto tranne esc: |
210 | - props = {'id': self.current_object} |
211 | key = gtk.gdk.keyval_name(event.keyval) |
212 | - text = self.presentation.get_text(self.current_object) + key |
213 | - self.presentation.draw_text(text, props) |
214 | + self.current_object.character_pressed(key) |
215 | self.area.queue_draw() |
216 | elif False: # premo esc |
217 | self.action_status = None |
218 | @@ -109,14 +128,10 @@ |
219 | context.save() |
220 | if not region: |
221 | context.set_matrix(cairo.Matrix(1, 0, 0, 1, 0, 0)) |
222 | - #a = context.set_matrix( 1,0,0,1,0,0 ) |
223 | - #matrix = array([[a[0], a[2], a[4]], [a[1], a[3], a[5]], [0, 0, 1]]) |
224 | x0 = self.selection_start_position[0] |
225 | y0 = self.selection_start_position[1] |
226 | x1 = self.mouse_position[0] |
227 | y1 = self.mouse_position[1] |
228 | - #x0, y0, nulla = dot(inv(matrix), [x0, y0, 1]) |
229 | - #x1, y1, nulla = dot(inv(matrix), [x1, y1, 1]) |
230 | else: |
231 | context.translate(region.xc, region.yc) |
232 | context.rotate(region.rotate) |
233 | @@ -149,9 +164,8 @@ |
234 | for i in xrange(0, len(self.presentation.path)): |
235 | region = self.presentation.get_path_region(i) |
236 | self.draw_selection_box(context, region) |
237 | - if self.editor_mode == 'path' and self.action_status == "selection": |
238 | + if self.editor_mode == 'path' and self.action_status: |
239 | self.draw_selection_box(context) |
240 | - #self.rotate_icon.render(context, self.area.window.get_size() ) |
241 | |
242 | def add_treeview_icon(self, rect=None): |
243 | ## * trasforma rect in Regione |
244 | @@ -174,10 +188,6 @@ |
245 | (rect2[1][0] - rect2[0][0]), |
246 | (rect2[1][1] - rect2[0][1]), |
247 | self.current_region.rotate) |
248 | - #print "Finestra", self.area.window.get_size() |
249 | - #print "Current", self.current_region |
250 | - #print "Aggiungo", region |
251 | |
252 | ## * metti la regione in lista |
253 | self.presentation.path.append(region) |
254 | ## * usa la regione per la pixbuf. |
255 | |
256 | === modified file 'a4lib/presentation.py' |
257 | --- a4lib/presentation.py 2010-12-01 23:24:34 +0000 |
258 | +++ a4lib/presentation.py 2010-12-22 22:58:09 +0000 |
259 | @@ -20,6 +20,7 @@ |
260 | a4nsmap = {'a4': A4HTMLNS} |
261 | nsmap = { |
262 | 'a4': A4HTMLNS, |
263 | + 'xlink': 'http://www.w3.org/1999/xlink', |
264 | 'svg': 'http://www.w3.org/2000/svg'} |
265 | |
266 | available_version = '0.1' |
267 | @@ -269,7 +270,8 @@ |
268 | Presentation.__init__(self, file_name, force_loading) |
269 | # main_g is the main <g> tag. it will be used mainly to add othe |
270 | # tag painted with A4 |
271 | - self.main_g = self.svgtree.xpath('/svg:svg/svg:g', namespaces=nsmap)[0] |
272 | + #self.main_g = self.svgtree.xpath('/svg:svg/svg:g', namespaces=nsmap)[0] |
273 | + self.main_g = self.svgtree.xpath('/svg:svg', namespaces=nsmap)[0] |
274 | self.render_cairo = self._render_cairo |
275 | |
276 | def _render_cairo(self, context): |
277 | @@ -277,39 +279,15 @@ |
278 | svg_handler = rsvg.Handle(data=tempdata) |
279 | svg_handler.render_cairo(context) |
280 | |
281 | - def _generate_new_id(self): |
282 | - import random |
283 | - return str(random.random()).split('.')[1] |
284 | + def add_object(self, presentation_object): |
285 | + self.main_g.append(presentation_object.element) |
286 | |
287 | - def _init_tag(self, tagname, piuprops={}): |
288 | - ## if piuprops contains an id already present modify inplace the rect. |
289 | - if 'id' in piuprops: |
290 | - target = self.svgtree.xpath( |
291 | + def search_id(self, object_id): |
292 | + target = self.svgtree.xpath( |
293 | "//*[@id='{0}']".format(piuprops['id']), namespaces=nsmap)[0] |
294 | - else: |
295 | - target = etree.SubElement(self.main_g, 'rect') |
296 | - if 'x' not in piuprops or 'y' not in piuprops: |
297 | - return False |
298 | - target.attrib['id'] = self._generate_new_id() |
299 | - for k in piuprops.keys(): |
300 | - target.attrib[k] = str(piuprops[k]) |
301 | - return target |
302 | - |
303 | - def draw_rect(self, width, height, piuprops={}): |
304 | - target_rect = self._init_tag('rect', piuprops) |
305 | - target_rect.attrib['width'] = str(width) |
306 | - target_rect.attrib['height'] = str(height) |
307 | - return target_rect.attrib['id'] |
308 | - |
309 | - def get_text(self, textid): |
310 | - target_text = self.svgtree.xpath( |
311 | - "//*[@id='{0}']".format(textid), namespaces=nsmap)[0] |
312 | - return target_text.text |
313 | - |
314 | - def draw_text(self, text="", piuprops={}): |
315 | - target_text = self._init_tag('text', piuprops) |
316 | - target_text.text = text |
317 | - return target_text.attrib['id'] |
318 | + if target: |
319 | + return target |
320 | + return False |
321 | |
322 | |
323 | class PathElement(object): |
324 | |
325 | === added file 'a4lib/presentation_objects.py' |
326 | --- a4lib/presentation_objects.py 1970-01-01 00:00:00 +0000 |
327 | +++ a4lib/presentation_objects.py 2010-12-22 22:58:09 +0000 |
328 | @@ -0,0 +1,139 @@ |
329 | +# Copyright 2010 A4 Developers. This software is licensed under the |
330 | +# GNU General Public License version 3 (see the file COPYING). |
331 | + |
332 | +import random |
333 | +import math |
334 | +from lxml import etree |
335 | + |
336 | +import presentation |
337 | + |
338 | + |
339 | +class PresentationObject(object): |
340 | + def __init__(self, tag_name, x=0, y=0, rotate=0.0): |
341 | + self.element = etree.Element(tag_name, nsmap=presentation.nsmap) |
342 | + self.element.attrib['id'] = self._generate_new_id() |
343 | + self.move(x, y) |
344 | + self.rotate(rotate) |
345 | + |
346 | + def _generate_new_id(self, length=10): |
347 | + seed = "abcdef0123456789" |
348 | + return ''.join(random.choice(seed) for i in xrange(length)) |
349 | + |
350 | + def append_attributes(self, attribs): |
351 | + for key in attribs: |
352 | + self.element.attrib[key] = str(piuprops[key]) |
353 | + |
354 | + @property |
355 | + def get_id(self): |
356 | + return self.element.attrib['id'] |
357 | + |
358 | + def move(self, x, y): |
359 | + self.element.attrib['x'] = str(x) |
360 | + self.element.attrib['y'] = str(y) |
361 | + |
362 | + def rotate(self, rotate): |
363 | + # need to calculate the transform matrix to rotate the object around his (x,y), |
364 | + # otherwise it will rotate around (0,0) |
365 | + xc = float(self.element.attrib['x']) |
366 | + yc = float(self.element.attrib['y']) |
367 | + xc1 = math.cos(-rotate) * xc - math.sin(-rotate) * yc |
368 | + yc1 = math.sin(-rotate) * xc + math.cos(-rotate) * yc |
369 | + self.element.attrib['transform'] = ("rotate(%s)" % str(math.degrees(rotate))) + \ |
370 | + (" translate(%s,%s)" % (xc1 - xc, yc1 - yc)) |
371 | + |
372 | + def set_size(self, width, height): |
373 | + self.element.attrib['width'] = str(width) |
374 | + self.element.attrib['height'] = str(height) |
375 | + |
376 | + ## Methods that could be implemented in subclasses. |
377 | + ## Implementing a stub here so subclasses doesn't have to reimplement. |
378 | + def start_move(self): |
379 | + pass |
380 | + def end_move(self): |
381 | + pass |
382 | + def character_pressed(self, char): |
383 | + pass |
384 | + def clicked_on_(self, x, y): |
385 | + pass |
386 | + |
387 | + |
388 | +class RectObject(PresentationObject): |
389 | + def __init__(self, x=0, y=0, rotate=0.0): |
390 | + PresentationObject.__init__(self, 'rect', x, y, rotate) |
391 | + |
392 | + |
393 | +class TextObject(PresentationObject): |
394 | + def __init__(self, x=0, y=0, rotate=0.0, text=""): |
395 | + PresentationObject.__init__(self, 'text', x, y, rotate) |
396 | + self.element.text = text |
397 | + |
398 | + def character_pressed(self, char): |
399 | + self.element.text += char |
400 | + |
401 | + def get_text(self): |
402 | + return self.element.text |
403 | + |
404 | + def set_text(self, text): |
405 | + self.element.text = text |
406 | + |
407 | + text = property(get_text, set_text) |
408 | + |
409 | + |
410 | +class PolaroidObject(PresentationObject): |
411 | + def __init__(self, x=0, y=0, rotate=0.0, text=""): |
412 | + PresentationObject.__init__(self, 'g', x, y, rotate) |
413 | + |
414 | + self.xratio = 0.95 |
415 | + self.yratio = 0.80 |
416 | + |
417 | + self.rect1 = etree.Element('rect') |
418 | + self.rect1.attrib['id'] = self._generate_new_id() |
419 | + self.rect1.attrib['style'] = "fill:#fbfae1;fill-opacity:1;stroke:#000000;stroke-opacity:1" |
420 | + self.rect1.attrib['x'] = str(x) |
421 | + self.rect1.attrib['y'] = str(y) |
422 | + |
423 | + self.rect2 = etree.Element('rect') |
424 | + self.rect2.attrib['id'] = self._generate_new_id() |
425 | + self.rect2.attrib['style'] = "fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" |
426 | + self.rect2.attrib['x'] = str(x) |
427 | + self.rect2.attrib['y'] = str(y) |
428 | + |
429 | + self.textelement = etree.Element('text') |
430 | + self.textelement.attrib['id'] = self._generate_new_id() |
431 | + self.textelement.text = "" |
432 | + |
433 | + self.element.append(self.rect1) |
434 | + self.element.append(self.rect2) |
435 | + self.element.append(self.textelement) |
436 | + |
437 | + def set_size(self, width, height): |
438 | + self.rect1.attrib['width'] = str(width) |
439 | + self.rect1.attrib['height'] = str(height) |
440 | + |
441 | + self.rect2.attrib['width'] = str(width * self.xratio) |
442 | + self.rect2.attrib['height'] = str(height * self.yratio) |
443 | + |
444 | + top = (width - width * self.xratio) / 2 |
445 | + |
446 | + self.rect2.attrib['x'] = str(float(self.rect1.attrib['x']) + top) |
447 | + self.rect2.attrib['y'] = str(float(self.rect1.attrib['y']) + top) |
448 | + |
449 | + self.textelement.attrib['x'] = str(float(self.rect1.attrib['x']) + |
450 | + width / 2) |
451 | + self.textelement.attrib['y'] = str(float(self.rect1.attrib['y']) + |
452 | + height - (height - (height * self.yratio + top)) / 2) |
453 | + |
454 | + def get_size(self): |
455 | + return (self.rect1.attrib['width'], self.rect1.attrib['height']) |
456 | + |
457 | + def character_pressed(self, char): |
458 | + self.textelement.text += char |
459 | + |
460 | + |
461 | +class ImageObject(PresentationObject): |
462 | + def __init__(self, x=0, y=0, rotate=0.0): |
463 | + PresentationObject.__init__(self, 'image', x, y, rotate) |
464 | + |
465 | + def set_image(self, target): |
466 | + href_attr = '{%s}href' % "http://www.w3.org/1999/xlink" |
467 | + self.element.attrib[href_attr] = "file://" + target |
468 | |
469 | === modified file 'a4lib/region.py' |
470 | --- a4lib/region.py 2010-08-11 16:33:02 +0000 |
471 | +++ a4lib/region.py 2010-12-22 22:58:09 +0000 |
472 | @@ -83,6 +83,28 @@ |
473 | else: |
474 | return ((scale_x - scale_y) * self.width, 0) |
475 | |
476 | + def get_point_from_window_coordinates(self, window_size, window_point): |
477 | + scale = self.get_window_scale_factor(window_size) |
478 | + translate = self.get_window_fit_vector(window_size) |
479 | + |
480 | + x = window_point[0] |
481 | + y = window_point[1] |
482 | + |
483 | + x -= translate[0] / 2.0 |
484 | + y -= translate[1] / 2.0 |
485 | + |
486 | + x = x / scale |
487 | + y = y / scale |
488 | + |
489 | + x -= self.width / 2 |
490 | + y -= self.height / 2 |
491 | + x1 = math.cos(self.rotate) * x - math.sin(self.rotate) * y |
492 | + y1 = math.sin(self.rotate) * x + math.cos(self.rotate) * y |
493 | + x1 += self.xc |
494 | + y1 += self.yc |
495 | + |
496 | + return (x1, y1) |
497 | + |
498 | def render(self, context, svg_image, window_size): |
499 | """Render the region of interest to the given Cairo context.""" |
500 | scale = self.get_window_scale_factor(window_size) |
501 | |
502 | === modified file 'data/window_main.glade' |
503 | --- data/window_main.glade 2010-11-20 12:46:38 +0000 |
504 | +++ data/window_main.glade 2010-12-22 22:58:09 +0000 |
505 | @@ -304,6 +304,7 @@ |
506 | <property name="label" translatable="yes">Selector</property> |
507 | <property name="use_underline">True</property> |
508 | <property name="active">True</property> |
509 | + <signal name="toggled" handler="on_toggle_tool"/> |
510 | </object> |
511 | <packing> |
512 | <property name="expand">False</property> |
513 | @@ -337,11 +338,38 @@ |
514 | </packing> |
515 | </child> |
516 | <child> |
517 | + <object class="GtkToggleToolButton" id="ImageToolbutton"> |
518 | + <property name="visible">True</property> |
519 | + <property name="is_important">True</property> |
520 | + <property name="label" translatable="yes">Image</property> |
521 | + <property name="use_underline">True</property> |
522 | + <signal name="toggled" handler="on_toggle_tool"/> |
523 | + </object> |
524 | + <packing> |
525 | + <property name="expand">False</property> |
526 | + <property name="homogeneous">True</property> |
527 | + </packing> |
528 | + </child> |
529 | + <child> |
530 | + <object class="GtkToggleToolButton" id="PolaroidToolbutton"> |
531 | + <property name="visible">True</property> |
532 | + <property name="is_important">True</property> |
533 | + <property name="label" translatable="yes">Polaroid</property> |
534 | + <property name="use_underline">True</property> |
535 | + <signal name="toggled" handler="on_toggle_tool"/> |
536 | + </object> |
537 | + <packing> |
538 | + <property name="expand">False</property> |
539 | + <property name="homogeneous">True</property> |
540 | + </packing> |
541 | + </child> |
542 | + <child> |
543 | <object class="GtkMenuToolButton" id="toolbutton4"> |
544 | <property name="visible">True</property> |
545 | <property name="is_important">True</property> |
546 | <property name="label" translatable="yes">Frames</property> |
547 | <property name="use_underline">True</property> |
548 | + <property name="menu">FramesMenu</property> |
549 | </object> |
550 | <packing> |
551 | <property name="expand">False</property> |
552 | @@ -452,5 +480,36 @@ |
553 | <object class="GtkAction" id="action1"/> |
554 | <object class="GtkMenu" id="ShapesMenu"> |
555 | <property name="visible">True</property> |
556 | + <child> |
557 | + <object class="GtkMenuItem" id="rect_item"> |
558 | + <property name="visible">True</property> |
559 | + <property name="label" translatable="yes">Rect</property> |
560 | + <property name="use_underline">True</property> |
561 | + </object> |
562 | + </child> |
563 | + <child> |
564 | + <object class="GtkMenuItem" id="polaroid_item"> |
565 | + <property name="visible">True</property> |
566 | + <property name="label" translatable="yes">Polaroid</property> |
567 | + <property name="use_underline">True</property> |
568 | + </object> |
569 | + </child> |
570 | + </object> |
571 | + <object class="GtkMenu" id="FramesMenu"> |
572 | + <property name="visible">True</property> |
573 | + <child> |
574 | + <object class="GtkMenuItem" id="normal_frame"> |
575 | + <property name="visible">True</property> |
576 | + <property name="label" translatable="yes">Normal frame</property> |
577 | + <property name="use_underline">True</property> |
578 | + </object> |
579 | + </child> |
580 | + <child> |
581 | + <object class="GtkMenuItem" id="hidden_frame"> |
582 | + <property name="visible">True</property> |
583 | + <property name="label" translatable="yes">Hidden Frame</property> |
584 | + <property name="use_underline">True</property> |
585 | + </object> |
586 | + </child> |
587 | </object> |
588 | </interface> |
589 | |
590 | === added directory 'tests/objects' |
591 | === added file 'tests/objects/polaroid.svg' |
592 | --- tests/objects/polaroid.svg 1970-01-01 00:00:00 +0000 |
593 | +++ tests/objects/polaroid.svg 2010-12-22 22:58:09 +0000 |
594 | @@ -0,0 +1,78 @@ |
595 | +<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
596 | +<!-- Created with Inkscape (http://www.inkscape.org/) --> |
597 | + |
598 | +<svg |
599 | + xmlns:dc="http://purl.org/dc/elements/1.1/" |
600 | + xmlns:cc="http://creativecommons.org/ns#" |
601 | + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" |
602 | + xmlns:svg="http://www.w3.org/2000/svg" |
603 | + xmlns="http://www.w3.org/2000/svg" |
604 | + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" |
605 | + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" |
606 | + width="744.09448819" |
607 | + height="1052.3622047" |
608 | + id="svg2" |
609 | + version="1.1" |
610 | + inkscape:version="0.48.0 r9654" |
611 | + sodipodi:docname="polaroid.svg"> |
612 | + <defs |
613 | + id="defs4" /> |
614 | + <sodipodi:namedview |
615 | + id="base" |
616 | + pagecolor="#ffffff" |
617 | + bordercolor="#666666" |
618 | + borderopacity="1.0" |
619 | + inkscape:pageopacity="0.0" |
620 | + inkscape:pageshadow="2" |
621 | + inkscape:zoom="1.4" |
622 | + inkscape:cx="305.17697" |
623 | + inkscape:cy="860.0891" |
624 | + inkscape:document-units="px" |
625 | + inkscape:current-layer="layer1" |
626 | + showgrid="false" |
627 | + inkscape:window-width="1440" |
628 | + inkscape:window-height="827" |
629 | + inkscape:window-x="0" |
630 | + inkscape:window-y="24" |
631 | + inkscape:window-maximized="1" /> |
632 | + <metadata |
633 | + id="metadata7"> |
634 | + <rdf:RDF> |
635 | + <cc:Work |
636 | + rdf:about=""> |
637 | + <dc:format>image/svg+xml</dc:format> |
638 | + <dc:type |
639 | + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> |
640 | + <dc:title></dc:title> |
641 | + </cc:Work> |
642 | + </rdf:RDF> |
643 | + </metadata> |
644 | + <g id="layer1"> |
645 | + <rect |
646 | + style="fill:#fbfae1;fill-opacity:1;stroke:#000000;stroke-opacity:1" |
647 | + id="rect2985" |
648 | + width="327.14285" |
649 | + height="220.71428" |
650 | + x="82.85714" |
651 | + y="20.933611" /> |
652 | + <rect |
653 | + style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" |
654 | + id="rect3755" |
655 | + width="314.28571" |
656 | + height="186.42857" |
657 | + x="89.285713" |
658 | + y="26.647896" /> |
659 | + <text |
660 | + xml:space="preserve" |
661 | + style="font-size:12px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans" |
662 | + x="245" |
663 | + y="227.36218" |
664 | + id="text3757" |
665 | + sodipodi:linespacing="125%"><tspan |
666 | + sodipodi:role="line" |
667 | + id="tspan3759" |
668 | + x="245" |
669 | + y="227.36218" |
670 | + style="font-family:Times New Roman;-inkscape-font-specification:Times New Roman">polaroid-text</tspan></text> |
671 | + </g> |
672 | +</svg> |
List of bugs:
1- all rectangles are always drawn from the presentation upper edge.
2- all rectangles have a lower border way bigger than other ones
3- very small rectangles are badly drawn
4- rectangles have weird starting points (I mean: there is a weird correlation between the point you begin to draw and the place the rectangle is actually drawn)
5- when a selection is drawn downside up, the rectangle is drawn outside of the presentation, starting from the upper edge. Further, the black part of the rectangle is bigger than the white part