diff options
-rw-r--r-- | Area.py | 94 | ||||
-rw-r--r-- | Desenho.py | 27 | ||||
-rw-r--r-- | icons/tool-stamp.svg | 122 | ||||
-rw-r--r-- | toolbox.py | 22 | ||||
-rw-r--r-- | widgets.py | 123 |
5 files changed, 350 insertions, 38 deletions
@@ -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 @@ -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 @@ -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""" @@ -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 |