1 //
2 // CloneStampTool.cs
3 //
4 // Author:
5 //       Jonathan Pobst <monkey@jpobst.com>
6 //
7 // Copyright (c) 2010 Jonathan Pobst
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 //
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
18 //
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 // THE SOFTWARE.
26 
27 using System;
28 using Pinta.Core;
29 using Mono.Unix;
30 using Gdk;
31 
32 namespace Pinta.Tools
33 {
34 	public class CloneStampTool : BaseBrushTool
35 	{
36 		private bool painting;
37 		private Point origin = new Point (int.MinValue, int.MinValue);
38 		private Point offset = new Point (int.MinValue, int.MinValue);
39 		private Point last_point = new Point (int.MinValue, int.MinValue);
40 
41 		public override string Name {
42 			get { return Catalog.GetString ("Clone Stamp"); }
43 		}
44 		public override string Icon {
45 			get { return "Tools.CloneStamp.png"; }
46 		}
47 		public override string StatusBarText { get { return Catalog.GetString ("Ctrl-left click to set origin, left click to paint."); } }
48 
49 		public override Gdk.Cursor DefaultCursor {
50 			get {
51 				int iconOffsetX, iconOffsetY;
52 				var icon = CreateIconWithShape ("Cursor.CloneStamp.png",
53 				                                CursorShape.Ellipse, BrushWidth, 16, 26,
54 				                                out iconOffsetX, out iconOffsetY);
55                 return new Gdk.Cursor (Gdk.Display.Default, icon, iconOffsetX, iconOffsetY);
56 			}
57 		}
58 		public override bool CursorChangesOnZoom { get { return true; } }
59 
60 		public override Gdk.Key ShortcutKey { get { return Gdk.Key.L; } }
61 		public override int Priority { get { return 33; } }
62 		protected override bool ShowAntialiasingButton { get { return true; } }
63 
OnBuildToolBar(Gtk.Toolbar tb)64 		protected override void OnBuildToolBar(Gtk.Toolbar tb)
65 		{
66 			base.OnBuildToolBar(tb);
67 
68 			// Change the cursor when the BrushWidth is changed.
69 			brush_width.ComboBox.Changed += (sender, e) => SetCursor (DefaultCursor);
70 		}
71 
OnMouseDown(Gtk.DrawingArea canvas, Gtk.ButtonPressEventArgs args, Cairo.PointD point)72 		protected override void OnMouseDown (Gtk.DrawingArea canvas, Gtk.ButtonPressEventArgs args, Cairo.PointD point)
73 		{
74 			Document doc = PintaCore.Workspace.ActiveDocument;
75 
76 			// We only do stuff with the left mouse button
77 			if (args.Event.Button != 1)
78 				return;
79 
80 			// Ctrl click is set origin, regular click is begin drawing
81 			if (!args.Event.IsControlPressed ()) {
82 				if (origin.IsNotSet ())
83 					return;
84 
85 				painting = true;
86 
87 				if (offset.IsNotSet ())
88 					offset = new Point ((int)point.X - origin.X, (int)point.Y - origin.Y);
89 
90 				doc.ToolLayer.Clear ();
91 				doc.ToolLayer.Hidden = false;
92 
93 				surface_modified = false;
94 				undo_surface = doc.CurrentUserLayer.Surface.Clone ();
95 			} else {
96 				origin = point.ToGdkPoint ();
97 			}
98 		}
99 
OnMouseMove(object o, Gtk.MotionNotifyEventArgs args, Cairo.PointD point)100 		protected override void OnMouseMove (object o, Gtk.MotionNotifyEventArgs args, Cairo.PointD point)
101 		{
102 			Document doc = PintaCore.Workspace.ActiveDocument;
103 
104 			if (!painting || offset.IsNotSet ())
105 				return;
106 
107 			int x = (int)point.X;
108 			int y = (int)point.Y;
109 
110 			if (last_point.IsNotSet ()) {
111 				last_point = new Point (x, y);
112 				return;
113 			}
114 
115 			using (var g = doc.CreateClippedToolContext ()) {
116 				g.Antialias = UseAntialiasing ? Cairo.Antialias.Subpixel : Cairo.Antialias.None;
117 
118 				g.MoveTo (last_point.X, last_point.Y);
119 				g.LineTo (x, y);
120 
121 				g.SetSource (doc.CurrentUserLayer.Surface, offset.X, offset.Y);
122 				g.LineWidth = BrushWidth;
123 				g.LineCap = Cairo.LineCap.Round;
124 
125 				g.Stroke ();
126 			}
127 
128 			var dirty_rect = GetRectangleFromPoints (last_point, new Point (x, y));
129 
130 			last_point = new Point (x, y);
131 			surface_modified = true;
132 			doc.Workspace.Invalidate (dirty_rect);
133 		}
134 
OnMouseUp(Gtk.DrawingArea canvas, Gtk.ButtonReleaseEventArgs args, Cairo.PointD point)135 		protected override void OnMouseUp (Gtk.DrawingArea canvas, Gtk.ButtonReleaseEventArgs args, Cairo.PointD point)
136 		{
137 			Document doc = PintaCore.Workspace.ActiveDocument;
138 
139 			painting = false;
140 
141 			using (Cairo.Context g = new Cairo.Context (doc.CurrentUserLayer.Surface)) {
142 				g.SetSource (doc.ToolLayer.Surface);
143 				g.Paint ();
144 			}
145 
146 			base.OnMouseUp (canvas, args, point);
147 
148 			offset = new Point (int.MinValue, int.MinValue);
149 			last_point = new Point (int.MinValue, int.MinValue);
150 
151 			doc.ToolLayer.Clear ();
152 			doc.ToolLayer.Hidden = true;
153 			doc.Workspace.Invalidate ();
154 		}
155 
OnKeyDown(Gtk.DrawingArea canvas, Gtk.KeyPressEventArgs args)156 		protected override void OnKeyDown (Gtk.DrawingArea canvas, Gtk.KeyPressEventArgs args)
157 		{
158 			base.OnKeyDown(canvas, args);
159 			//note that this WONT work if user presses control key and THEN selects the tool!
160 			if (args.Event.Key == Key.Control_L || args.Event.Key == Key.Control_R) {
161 				Gdk.Pixbuf icon = PintaCore.Resources.GetIcon ("Cursor.CloneStampSetSource.png");
162 				Gdk.Cursor setSourceCursor = new Gdk.Cursor (Gdk.Display.Default, icon, 16, 26);
163 				SetCursor(setSourceCursor);
164 			}
165 		}
166 
OnKeyUp(Gtk.DrawingArea canvas, Gtk.KeyReleaseEventArgs args)167 		protected override void OnKeyUp (Gtk.DrawingArea canvas, Gtk.KeyReleaseEventArgs args)
168 		{
169 			base.OnKeyUp(canvas, args);
170 			if (args.Event.Key == Key.Control_L || args.Event.Key == Key.Control_R)
171 				SetCursor(DefaultCursor);
172 		}
173 
OnDeactivated(BaseTool newTool)174 		protected override void OnDeactivated(BaseTool newTool)
175 		{
176 			origin = new Point (int.MinValue, int.MinValue);
177 		}
178 	}
179 }
180