Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGonzalo Odiard <godiard@gmail.com>2011-05-03 18:29:08 (GMT)
committer Gonzalo Odiard <godiard@gmail.com>2011-05-03 18:29:08 (GMT)
commit44ce7d95c33fc76078d88fc43e9c4453ca6522e9 (patch)
tree0974e2d8fe36e5c992e94f0e4fad4621fdf1e9a3
parent149d8ceb9bfd288c2ccdcff5f787305dc7332275 (diff)
Implementation of Stamp functionality
The user can select a part of the image to use it like a stamp. When the stamp is selected, can change the size, and use it to draw. Signed-of: manuel quiƱones <manuel.por.aca@gmail.com> Reviewed-by: Gonzalo Odiard <gonzalo@laptop.org>
-rw-r--r--Area.py94
-rw-r--r--Desenho.py27
-rw-r--r--icons/tool-stamp.svg122
-rw-r--r--toolbox.py22
-rw-r--r--widgets.py123
5 files changed, 350 insertions, 38 deletions
diff --git a/Area.py b/Area.py
index 2e979a8..90bec38 100644
--- a/Area.py
+++ b/Area.py
@@ -131,6 +131,7 @@ class Area(gtk.DrawingArea):
## with the following keys:
## - 'name' : a string
## - 'line size' : a integer
+ ## - 'stamp size' : a integer
## - 'fill color' : a gtk.gdk.Color object
## - 'stroke color' : a gtk.gdk.Color object
## - 'line shape' : a string - 'circle' or 'square', for now
@@ -140,6 +141,7 @@ class Area(gtk.DrawingArea):
self.tool = {
'name': 'pencil',
'line size': 4,
+ 'stamp size': 20,
'fill color': None,
'stroke color': None,
'line shape': 'circle',
@@ -286,14 +288,24 @@ class Area(gtk.DrawingArea):
Show the shape of the tool selected for pencil, brush,
rainbow and eraser
"""
- if self.tool['name'] in ['pencil', 'eraser', 'brush', 'rainbow']:
+ if self.tool['name'] in ['pencil', 'eraser', 'brush', 'rainbow',
+ 'stamp']:
if not self.drawing:
- size = self.tool['line size']
- if self.tool['line shape'] == 'circle':
+ # draw stamp border in widget.window
+ if self.tool['name'] == 'stamp':
+ wr, hr = self.stamp_dimentions
+ widget.window.draw_rectangle(self.gc_brush, False,
+ self.x_cursor - wr / 2, self.y_cursor - hr / 2,
+ wr, hr)
+
+ # draw shape of the brush, square or circle
+ elif self.tool['line shape'] == 'circle':
+ size = self.tool['line size']
widget.window.draw_arc(self.gc_brush, False,
self.x_cursor - size / 2, self.y_cursor - size / 2,
size, size, 0, 360 * 64)
else:
+ size = self.tool['line size']
widget.window.draw_rectangle(self.gc_brush, False,
self.x_cursor - size / 2, self.y_cursor - size / 2,
size, size)
@@ -355,6 +367,12 @@ class Area(gtk.DrawingArea):
self.tool['line shape'])
self.last = coords
self.drawing = True
+ elif self.tool['name'] == 'stamp':
+ self.last = []
+ self.d.stamp(widget, coords, self.last,
+ self.tool['stamp size'])
+ self.last = coords
+ self.drawing = True
elif self.tool['name'] == 'rainbow':
self.last = []
self.d.rainbow(widget, coords, self.last,
@@ -412,7 +430,7 @@ class Area(gtk.DrawingArea):
if state & gtk.gdk.BUTTON1_MASK and self.pixmap != None:
if self.tool['name'] == 'pencil':
self.d.brush(widget, coords, self.last,
- self.tool['line size'], 'circle')
+ self.tool['line size'], self.tool['line shape'])
self.last = coords
elif self.tool['name'] == 'eraser':
@@ -425,6 +443,11 @@ class Area(gtk.DrawingArea):
self.tool['line size'], self.tool['line shape'])
self.last = coords
+ elif self.tool['name'] == 'stamp':
+ self.d.stamp(widget, coords, self.last,
+ self.tool['stamp size'])
+ self.last = coords
+
elif self.tool['name'] == 'rainbow':
self.d.rainbow(widget, coords, self.last,
self.rainbow_counter, self.tool['line size'],
@@ -496,7 +519,8 @@ class Area(gtk.DrawingArea):
self.configure_line(self.tool['line size'])
self.d.heart(widget, coords, True, self.tool['fill'])
else:
- if self.tool['name'] in ['brush', 'eraser', 'rainbow', 'pencil']:
+ if self.tool['name'] in ['brush', 'eraser', 'rainbow', 'pencil',
+ 'stamp']:
widget.queue_draw()
if self.tool['name'] == 'marquee-rectangular' and self.selmove:
size = self.pixmap_sel.get_size()
@@ -616,7 +640,8 @@ class Area(gtk.DrawingArea):
self.d.heart(widget, coords, False, self.tool['fill'])
self.enableUndo(widget)
- if self.tool['name'] in ['brush', 'eraser', 'rainbow', 'pencil']:
+ if self.tool['name'] in ['brush', 'eraser', 'rainbow', 'pencil',
+ 'stamp']:
self.last = []
widget.queue_draw()
self.enableUndo(widget)
@@ -624,19 +649,65 @@ class Area(gtk.DrawingArea):
self.desenha = False
def mouseleave(self, widget, event):
- if self.tool['name'] in ['pencil', 'eraser', 'brush', 'rainbow']:
+ if self.tool['name'] in ['pencil', 'eraser', 'brush', 'rainbow',
+ 'stamp']:
self.drawing = True
size = self.tool['line size']
widget.queue_draw_area(self.x_cursor - size, self.y_cursor - size,
size * 2, size * 2)
def mouseenter(self, widget, event):
- if self.tool['name'] in ['pencil', 'eraser', 'brush', 'rainbow']:
+ if self.tool['name'] in ['pencil', 'eraser', 'brush', 'rainbow',
+ 'stamp']:
self.drawing = False
size = self.tool['line size']
widget.queue_draw_area(self.x_cursor - size, self.y_cursor - size,
size * 2, size * 2)
+ def setup_stamp(self):
+ """Prepare for stamping from the selected area.
+
+ @param self -- the Area object (GtkDrawingArea)
+ """
+ logging.debug('Area.setup_stamp(self)')
+
+ if self.is_selected():
+ # Change stamp, get it from selection:
+ width, height = self.pixmap_sel.get_size()
+ self.pixbuf_stamp = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False,
+ 8, width, height)
+ self.pixbuf_stamp.get_from_drawable(self.pixmap_sel,
+ gtk.gdk.colormap_get_system(), 0, 0, 0, 0, width, height)
+ self.stamp_size = 0
+ # Set white color as transparent:
+ stamp_alpha = self.pixbuf_stamp.add_alpha(True, 255, 255, 255)
+ self.pixbuf_stamp = stamp_alpha
+
+ return self.resize_stamp(self.tool['stamp size'])
+
+ def resize_stamp(self, stamp_size):
+ """Change stamping pixbuffer from the given size.
+
+ @param self -- the Area object (GtkDrawingArea)
+ @param stamp_size -- the stamp will be inscripted in this size
+ """
+
+ # Area.setup_stamp needs to be called first:
+ assert self.pixbuf_stamp
+
+ self.stamp_size = stamp_size
+ w = self.pixbuf_stamp.get_width()
+ h = self.pixbuf_stamp.get_height()
+ if w >= h:
+ wr, hr = stamp_size, int(stamp_size * h * 1.0 / w)
+ else:
+ wr, hr = int(stamp_size * w * 1.0 / h), stamp_size
+ self.stamp_dimentions = wr, hr
+ self.resized_stamp = self.pixbuf_stamp.scale_simple(wr, hr,
+ gtk.gdk.INTERP_HYPER)
+
+ return self.resized_stamp
+
def undo(self):
"""Undo the last drawing change.
@@ -1242,7 +1313,8 @@ class Area(gtk.DrawingArea):
cursors = {'pencil': 'pencil',
'brush': 'paintbrush',
'eraser': 'eraser',
- 'bucket': 'paint-bucket'}
+ 'bucket': 'paint-bucket',
+ 'stamp': 'pencil'}
display = gtk.gdk.display_get_default()
if self.tool['name'] in cursors:
@@ -1329,8 +1401,10 @@ class Area(gtk.DrawingArea):
self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.CROSS))
widget.queue_draw()
+ # TODO: unused method?
def change_line_size(self, delta):
- if self.tool['name'] in ['pencil', 'eraser', 'brush', 'rainbow']:
+ if self.tool['name'] in ['pencil', 'eraser', 'brush', 'rainbow',
+ 'stamp']:
size = self.tool['line size'] + delta
if size < 1:
size = 1
diff --git a/Desenho.py b/Desenho.py
index 4f8f196..1d46d4a 100644
--- a/Desenho.py
+++ b/Desenho.py
@@ -128,6 +128,28 @@ class Desenho:
widget.desenha = False
self._trace(widget, widget.gc_brush, coords, last, size, shape)
+ def stamp(self, widget, coords, last, stamp_size=20):
+ """Paint with stamp.
+
+ @param self -- Desenho.Desenho instance
+ @param last -- last of oldx
+ @param widget -- Area object (GtkDrawingArea)
+ @param coords -- Two value tuple
+ @param stamp_size -- integer (default 20)
+
+ """
+ widget.desenha = False
+ gc = widget.gc_brush
+
+ width = widget.resized_stamp.get_width()
+ height = widget.resized_stamp.get_height()
+ dx = coords[0] - width / 2
+ dy = coords[1] - height / 2
+ widget.pixmap.draw_pixbuf(gc, widget.resized_stamp,
+ 0, 0, dx, dy, width, height)
+
+ widget.queue_draw()
+
def rainbow(self, widget, coords, last, color, size=5, shape='circle'):
"""Paint with rainbow.
@@ -161,7 +183,7 @@ class Desenho:
self._trace(widget, widget.gc_rainbow, coords, last, size, shape)
def _trace(self, widget, gc, coords, last, size, shape):
- if(shape == 'circle'):
+ if shape == 'circle':
widget.pixmap.draw_arc(gc, True,
coords[0] - size / 2, coords[1] - size / 2,
size, size, 0, 360 * 64)
@@ -172,7 +194,7 @@ class Desenho:
last[0], last[1], coords[0], coords[1])
gc.set_line_attributes(0, gtk.gdk.LINE_SOLID,
gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_ROUND)
- if(shape == 'square'):
+ elif shape == 'square':
widget.pixmap.draw_rectangle(gc, True,
coords[0] - size / 2, coords[1] - size / 2, size, size)
if last:
@@ -186,7 +208,6 @@ class Desenho:
(coords[0] - size / 2, coords[1] + size / 2),
(last[0] - size / 2, last[1] + size / 2)]
widget.pixmap.draw_polygon(gc, True, points)
-
if last:
x = min(coords[0], last[0])
width = max(coords[0], last[0]) - x
diff --git a/icons/tool-stamp.svg b/icons/tool-stamp.svg
new file mode 100644
index 0000000..2a3ace3
--- /dev/null
+++ b/icons/tool-stamp.svg
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ x="0px"
+ y="0px"
+ width="55px"
+ height="55px"
+ viewBox="0 0 55 55"
+ enable-background="new 0 0 55 55"
+ xml:space="preserve"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="stamp.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ sodipodi:modified="true"><metadata
+ id="metadata10"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+ id="defs8">
+
+</defs><sodipodi:namedview
+ inkscape:window-height="693"
+ inkscape:window-width="1366"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#ffffff"
+ id="base"
+ inkscape:zoom="8.9274636"
+ inkscape:cx="34.877834"
+ inkscape:cy="26.152016"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:current-layer="svg2"
+ showgrid="false"
+ inkscape:window-maximized="1" />
+
+
+
+<path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 84.708695,28.377703 -19.69915,0 0.01707,-1.710302 c 0.313444,-2.141866 2.1844,-4.719789 3.177515,-5.057654 l 10.15005,0 c 0,0 1.167236,-3.018039 2.473305,-3.163158 1.306068,-0.145119 0.94435,-7.590535 0.94435,-7.590535 0,0 -3.078113,-0.374709 -3.192613,-4.5090981 -0.07136,-2.576553 1.423887,-4.242029 5.541811,-4.046442"
+ id="path3957"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccscsc" /><use
+ x="0"
+ y="0"
+ xlink:href="#path3957"
+ id="use4065"
+ transform="matrix(-1,0,0,1,168.82057,5.2700672e-8)"
+ width="55"
+ height="55" /><path
+ id="use4065-4"
+ style="fill:#ffffff;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 69.988314,61.877421 c 0.252546,-1.770392 0.976244,-4.506115 3.194588,-6.767956 l 10.15005,0 c 0,0 1.167236,-3.018039 2.473305,-3.163158 1.306068,-0.145119 0.94435,-7.590536 0.94435,-7.590536 0,0 -3.078113,-0.374709 -3.192613,-4.509097 -0.07136,-2.576553 1.423887,-4.242029 5.83104,-4.046442 4.407154,-0.195587 5.902401,1.469889 5.831041,4.046442 -0.1145,4.134388 -3.192613,4.509097 -3.192613,4.509097 0,0 -0.361718,7.445417 0.94435,7.590536 1.306069,0.145119 2.473305,3.163158 2.473305,3.163158 l 10.150053,0 c 2.21834,2.261841 2.94204,4.997564 3.19458,6.767956 l -19.400716,0 z"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccscscscsccccc" /><path
+ style="fill:none;stroke:#000000;stroke-width:0.50062037;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m 65.17528,26.773621 38.34165,0.0281"
+ id="path4093"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" /><path
+ style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m 78.18891,21.637236 c 1.351814,1.622176 3.947296,1.622176 6.596851,1.622176"
+ id="path4095"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" /><use
+ x="0"
+ y="0"
+ xlink:href="#path4095"
+ id="use4097"
+ transform="matrix(-1,0,0,1,168.54414,0)"
+ width="55"
+ height="55" /><path
+ style="fill:none;stroke:#000000;stroke-width:0.43943578;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m 79.573345,9.3627663 c 1.044162,1.6221757 3.048951,1.6221757 5.095508,1.6221757"
+ id="path4095-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" /><use
+ x="0"
+ y="0"
+ xlink:href="#path4095-5"
+ id="use4097-6"
+ transform="matrix(-1,0,0,1,168.54414,4.964343e-7)"
+ width="55"
+ height="55" /><path
+ style="fill:#ffffff;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="M 26.5625 12.375 C 22.120995 12.411571 20.609209 14.298275 20.6875 17.125 C 20.746122 19.241716 21.455801 20.547869 22.25 21.375 C 23.741475 22.632297 25.700095 23.179394 27.375 23.1875 C 28.433911 23.158056 29.576952 23.032294 30.59375 22.71875 C 30.593779 22.71816 30.59375 22.6875 30.59375 22.6875 C 30.59375 22.6875 30.816458 22.64412 31.15625 22.5 C 32.175626 22.067639 34.212006 20.753655 34.3125 17.125 C 34.396008 14.109827 32.657407 12.146117 27.5 12.375 C 27.177662 12.360695 26.8586 12.372562 26.5625 12.375 z M 24.46875 24.3125 C 24.505729 26.764709 24.414375 31.158819 23.3125 31.28125 C 22.105137 31.415401 21.036355 33.568208 20.625 34.5 C 22.482265 35.991783 24.995333 36.182999 27.34375 36.15625 C 29.729999 36.155749 32.249423 35.916094 34.34375 34.375 C 33.882989 33.366951 32.826049 31.407755 31.6875 31.28125 C 30.585626 31.158819 30.494271 26.764709 30.53125 24.3125 C 29.499845 24.565064 28.405974 24.677426 27.375 24.6875 C 26.495235 24.6875 25.473948 24.593186 24.46875 24.3125 z M 12.53125 34.96875 C 11.518506 35.313292 9.7182795 37.652816 9.03125 39.90625 L 45.96875 39.9375 C 45.288056 37.675514 43.485656 35.314708 42.46875 34.96875 L 35.25 34.96875 C 33.193912 37.188826 30.361472 37.773869 27.34375 37.65625 C 24.357499 37.655934 21.940962 37.424495 19.8125 34.96875 L 12.53125 34.96875 z M 8.8125 41.40625 L 8.78125 44.6875 L 27.5 44.6875 L 46.1875 44.6875 L 46.1875 41.4375 L 8.8125 41.40625 z "
+ id="use4065-2" /><path
+ id="use4065-2-5"
+ style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m 1.7724505,93.226744 0.019976,-3.780038 c 0.3668031,-2.506487 2.5562611,-5.523263 3.7184391,-5.918644 l 7.8779444,0 c 0,0 1.365941,-3.531815 2.894348,-3.701638 1.528407,-0.169824 1.105112,-8.590293 1.105112,-8.590293 0,0 -3.602116,-0.730915 -3.736108,-5.569122 -0.08351,-3.015173 1.666283,-4.964171 6.82369,-4.735288 5.157407,-0.228883 6.907198,1.720115 6.82369,4.735288 -0.133992,4.838207 -3.736108,5.569122 -3.736108,5.569122 0,0 -0.423295,8.420469 1.105111,8.590293 1.528408,0.169823 2.894349,3.701638 2.894349,3.701638 l 7.877944,0 c 1.162178,0.395381 3.351636,3.412157 3.718439,5.918644 l 0.01998,3.780038 -18.703401,0 z"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccscscscscccccc" /><path
+ style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m -1.6779401,89.215292 44.8687431,0.03288"
+ id="path4093-6-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" /><path
+ id="use4097-4-2"
+ style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m 13.195349,83.56023 c 1.58194,1.898328 4.619264,1.898328 7.118725,1.898328 2.49946,0 5.536784,0 7.118725,-1.898328"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccc" /><path
+ id="use4097-6-3-8"
+ style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m 14.519882,69.422959 c 1.221915,1.898327 3.863571,2.456648 5.794192,2.456648 1.930621,0 4.802175,-0.26274 6.02409,-2.161067"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccc" /></svg> \ No newline at end of file
diff --git a/toolbox.py b/toolbox.py
index dafb4e7..00d98b1 100644
--- a/toolbox.py
+++ b/toolbox.py
@@ -137,6 +137,7 @@ class DrawToolbarBox(ToolbarBox):
brush_button = tools_builder._stroke_color.color_button
brush_button.set_brush_shape(self._activity.area.tool['line shape'])
brush_button.set_brush_size(self._activity.area.tool['line size'])
+ brush_button.set_stamp_size(self._activity.area.tool['stamp size'])
if self._activity.area.tool['stroke color'] is not None:
brush_button.set_color(self._activity.area.tool['stroke color'])
@@ -228,6 +229,7 @@ class ToolsToolbarBuilder():
_TOOL_BRUSH_NAME = 'brush'
_TOOL_ERASER_NAME = 'eraser'
_TOOL_BUCKET_NAME = 'bucket'
+ _TOOL_STAMP_NAME = 'stamp'
_TOOL_MARQUEE_RECT_NAME = 'marquee-rectangular'
##The Constructor
@@ -267,6 +269,14 @@ class ToolsToolbarBuilder():
activity.tool_group, _('Bucket'))
toolbar.insert(self._tool_bucket, -1)
+ self._tool_stamp = DrawToolButton('tool-stamp',
+ activity.tool_group, _('Stamp'))
+ toolbar.insert(self._tool_stamp, -1)
+
+ is_selected = self._activity.area.is_selected()
+ self._tool_stamp.set_sensitive(is_selected)
+ self._activity.area.connect('select', self._on_signal_select_cb)
+
self._tool_marquee_rectangular = \
DrawToolButton('tool-marquee-rectangular',
activity.tool_group, _('Select Area'))
@@ -286,6 +296,8 @@ class ToolsToolbarBuilder():
self._TOOL_ERASER_NAME)
self._tool_bucket.connect('clicked', self.set_tool,
self._TOOL_BUCKET_NAME)
+ self._tool_stamp.connect('clicked', self.set_tool,
+ self._TOOL_STAMP_NAME)
self._tool_marquee_rectangular.connect('clicked', self.set_tool,
self._TOOL_MARQUEE_RECT_NAME)
@@ -298,6 +310,12 @@ class ToolsToolbarBuilder():
necessary in case this method is used in a connect()
@param tool_name --The name of the selected tool
"""
+ if tool_name == 'stamp':
+ resized_stamp = self._activity.area.setup_stamp()
+ self._stroke_color.color_button.set_resized_stamp(resized_stamp)
+ else:
+ self._stroke_color.color_button.stop_stamping()
+ self._stroke_color.update_stamping()
self.properties['name'] = tool_name
self._activity.area.set_tool(self.properties)
@@ -308,6 +326,10 @@ class ToolsToolbarBuilder():
self._activity.area.set_stroke_color(new_color)
self.properties['stroke color'] = new_color
+ def _on_signal_select_cb(self, widget, data=None):
+ is_selected = self._activity.area.is_selected()
+ self._tool_stamp.set_sensitive(is_selected)
+
class ButtonFillColor(ColorToolButton):
"""Class to manage the Fill Color of a Button"""
diff --git a/widgets.py b/widgets.py
index 832649b..7c23683 100644
--- a/widgets.py
+++ b/widgets.py
@@ -28,7 +28,9 @@ class BrushButton(_ColorButton):
self._palette = None
self._accept_drag = True
self._brush_size = 2
+ self._stamp_size = 20
self._brush_shape = 'circle'
+ self._resized_stamp = None
self._preview = gtk.DrawingArea()
self._preview.set_size_request(style.STANDARD_ICON_SIZE,
style.STANDARD_ICON_SIZE)
@@ -83,6 +85,26 @@ class BrushButton(_ColorButton):
self._color = color
self._preview.queue_draw()
+ def get_stamp_size(self):
+ return self._stamp_size
+
+ def set_stamp_size(self, stamp_size):
+ self._stamp_size = stamp_size
+ self._preview.queue_draw()
+
+ stamp_size = gobject.property(type=int, getter=get_stamp_size,
+ setter=set_stamp_size)
+
+ def set_resized_stamp(self, resized_stamp):
+ self._resized_stamp = resized_stamp
+
+ def stop_stamping(self):
+ self._resized_stamp = None
+ self._preview.queue_draw()
+
+ def is_stamping(self):
+ return self._resized_stamp != None
+
def expose(self, widget, event):
if self._gc is None:
self._setup()
@@ -93,16 +115,26 @@ class BrushButton(_ColorButton):
True, 0, 0, style.STANDARD_ICON_SIZE, style.STANDARD_ICON_SIZE)
self._gc.set_foreground(self._color)
- if(self._brush_shape == 'circle'):
- self.pixmap.draw_arc(self._gc, True,
- center - self._brush_size / 2,
- center - self._brush_size / 2,
- self._brush_size, self._brush_size, 0, 360 * 64)
- if(self._brush_shape == 'square'):
- self.pixmap.draw_rectangle(self._gc, True,
- center - self._brush_size / 2,
- center - self._brush_size / 2,
- self._brush_size, self._brush_size)
+ if self.is_stamping():
+ width = self._resized_stamp.get_width()
+ height = self._resized_stamp.get_height()
+ dx = center - width / 2
+ dy = center - height / 2
+ self.pixmap.draw_pixbuf(self._gc, self._resized_stamp,
+ 0, 0, dx, dy, width, height)
+
+ else:
+ if self._brush_shape == 'circle':
+ self.pixmap.draw_arc(self._gc, True,
+ center - self._brush_size / 2,
+ center - self._brush_size / 2,
+ self._brush_size, self._brush_size, 0, 360 * 64)
+
+ elif self._brush_shape == 'square':
+ self.pixmap.draw_rectangle(self._gc, True,
+ center - self._brush_size / 2,
+ center - self._brush_size / 2,
+ self._brush_size, self._brush_size)
area = event.area
widget.window.draw_drawable(self._gc, self.pixmap,
@@ -157,6 +189,7 @@ class ButtonStrokeColor(gtk.ToolItem):
self.add(self.color_button)
self.color_button.set_brush_size(2)
self.color_button.set_brush_shape('circle')
+ self.color_button.set_stamp_size(20)
# The following is so that the behaviour on the toolbar is correct.
self.color_button.set_relief(gtk.RELIEF_NONE)
@@ -171,6 +204,8 @@ class ButtonStrokeColor(gtk.ToolItem):
self.color_button.connect('can-activate-accel',
self.__button_can_activate_accel_cb)
+ self.create_palette()
+
def __button_can_activate_accel_cb(self, button, signal_id):
# Accept activation via accelerators regardless of this widget's state
return True
@@ -193,20 +228,22 @@ class ButtonStrokeColor(gtk.ToolItem):
color_palette_hbox = self._palette._picker_hbox
content_box = gtk.VBox()
- size_spinbutton = gtk.SpinButton()
+ self.size_spinbutton = gtk.SpinButton()
# This is where we set restrictions for size:
# Initial value, minimum value, maximum value, step
adj = gtk.Adjustment(self.properties['line size'], 1.0, 100.0, 1.0)
- size_spinbutton.set_adjustment(adj)
- size_spinbutton.set_numeric(True)
+ self.size_spinbutton.set_adjustment(adj)
+ self.size_spinbutton.set_numeric(True)
label = gtk.Label(_('Size: '))
- hbox = gtk.HBox()
- content_box.pack_start(hbox)
+ hbox_size = gtk.HBox()
+ self.vbox_brush_options = gtk.VBox()
+ content_box.pack_start(hbox_size)
+ content_box.pack_start(self.vbox_brush_options)
- hbox.pack_start(label)
- hbox.pack_start(size_spinbutton)
- size_spinbutton.connect('value-changed', self._on_value_changed)
+ hbox_size.pack_start(label)
+ hbox_size.pack_start(self.size_spinbutton)
+ self.size_spinbutton.connect('value-changed', self._on_value_changed)
# User is able to choose Shapes for 'Brush' and 'Eraser'
item1 = gtk.RadioButton(None, _('Circle'))
@@ -233,37 +270,73 @@ class ButtonStrokeColor(gtk.ToolItem):
label = gtk.Label(_('Shape'))
- content_box.pack_start(label)
- content_box.pack_start(item1)
- content_box.pack_start(item2)
+ self.vbox_brush_options.pack_start(label)
+ self.vbox_brush_options.pack_start(item1)
+ self.vbox_brush_options.pack_start(item2)
keep_aspect_checkbutton = gtk.CheckButton(_('Keep aspect'))
ratio = self._activity.area.keep_aspect_ratio
keep_aspect_checkbutton.set_active(ratio)
keep_aspect_checkbutton.connect('toggled',
self._keep_aspect_checkbutton_toggled)
- content_box.pack_start(keep_aspect_checkbutton)
+ self.vbox_brush_options.pack_start(keep_aspect_checkbutton)
color_palette_hbox.pack_start(gtk.VSeparator(),
padding=style.DEFAULT_SPACING)
color_palette_hbox.pack_start(content_box)
color_palette_hbox.show_all()
+ self._update_palette()
return self._palette
def _keep_aspect_checkbutton_toggled(self, checkbutton):
self._activity.area.keep_aspect_ratio = checkbutton.get_active()
+ def _update_palette(self):
+ palette_children = self._palette._picker_hbox.get_children()
+ if self.color_button.is_stamping():
+ # Hide palette color widgets:
+ for ch in palette_children[:4]:
+ ch.hide_all()
+ # Hide brush options:
+ self.vbox_brush_options.hide_all()
+ # Change title:
+ self.set_title(_('Stamp properties'))
+ else:
+ # Show palette color widgets:
+ for ch in palette_children[:4]:
+ ch.show_all()
+ # Show brush options:
+ self.vbox_brush_options.show_all()
+ # Change title:
+ self.set_title(_('Brush properties'))
+
+ self._palette._picker_hbox.resize_children()
+ self._palette._picker_hbox.queue_draw()
+
+ def update_stamping(self):
+ if self.color_button.is_stamping():
+ self.size_spinbutton.set_value(self.color_button.stamp_size)
+ else:
+ self.size_spinbutton.set_value(self.color_button.brush_size)
+ self._update_palette()
+
def _on_value_changed(self, spinbutton):
size = spinbutton.get_value_as_int()
- self.properties['line size'] = size
+ if self.color_button.is_stamping():
+ self.properties['stamp size'] = size
+ resized_stamp = self._activity.area.resize_stamp(size)
+ self.color_button.set_resized_stamp(resized_stamp)
+ self.color_button.set_stamp_size(self.properties['stamp size'])
+ else:
+ self.properties['line size'] = size
+ self.color_button.set_brush_size(self.properties['line size'])
self._activity.area.set_tool(self.properties)
- self.color_button.set_brush_size(self.properties['line size'])
def _on_toggled(self, radiobutton, tool, shape):
if radiobutton.get_active():
self.properties['line shape'] = shape
self.color_button.set_brush_shape(shape)
- self.color_button.set_brush_size(self.properties['line size'])
+ self.color_button.set_brush_size(self.properties['line size'])
def get_palette_invoker(self):
return self._palette_invoker