A4

Merge lp:~gaspa/a4/presentation-objects into lp:a4

Proposed by Andrea Gasparini
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
Reviewer Review Type Date Requested Status
Andrea Gasparini Pending
Review via email: mp+41838@code.launchpad.net

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).

To post a comment you must log in.
Revision history for this message
Andrea Colangelo (warp10) wrote :

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

lp:~gaspa/a4/presentation-objects updated
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

Revision history for this message
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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- #print
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>

Subscribers

People subscribed via source and target branches