1 // 2 // PintaCanvas.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 Gdk; 29 using Gtk; 30 using Pinta.Core; 31 32 namespace Pinta.Gui.Widgets 33 { 34 [System.ComponentModel.ToolboxItem (true)] 35 public class PintaCanvas : DrawingArea 36 { 37 Cairo.ImageSurface canvas; 38 CanvasRenderer cr; 39 40 private Document document; 41 private int hScrollAmount = 92; 42 43 public CanvasWindow CanvasWindow { get; private set; } 44 PintaCanvas(CanvasWindow window, Document document)45 public PintaCanvas (CanvasWindow window, Document document) 46 { 47 CanvasWindow = window; 48 this.document = document; 49 50 cr = new CanvasRenderer (true); 51 52 // Keep the widget the same size as the canvas 53 document.Workspace.CanvasSizeChanged += delegate (object sender, EventArgs e) { 54 SetRequisition (document.Workspace.CanvasSize); 55 }; 56 57 // Update the canvas when the image changes 58 document.Workspace.CanvasInvalidated += delegate (object sender, CanvasInvalidatedEventArgs e) { 59 // If GTK+ hasn't created the canvas window yet, no need to invalidate it 60 if (GdkWindow == null) 61 return; 62 63 if (e.EntireSurface) 64 GdkWindow.Invalidate (); 65 else 66 GdkWindow.InvalidateRect (e.Rectangle, false); 67 }; 68 69 // Give mouse press events to the current tool 70 ButtonPressEvent += delegate (object sender, ButtonPressEventArgs e) { 71 // The canvas gets the button press before the tab system, so 72 // if this click is on a canvas that isn't currently the ActiveDocument yet, 73 // we need to go ahead and make it the active document for the tools 74 // to use it, even though right after this the tab system would have switched it 75 if (PintaCore.Workspace.ActiveDocument != document) 76 PintaCore.Workspace.SetActiveDocument (document); 77 78 PintaCore.Tools.CurrentTool.DoMouseDown (this, e, document.Workspace.WindowPointToCanvas (e.Event.X, e.Event.Y)); 79 }; 80 81 // Give mouse release events to the current tool 82 ButtonReleaseEvent += delegate (object sender, ButtonReleaseEventArgs e) { 83 PintaCore.Tools.CurrentTool.DoMouseUp (this, e, document.Workspace.WindowPointToCanvas (e.Event.X, e.Event.Y)); 84 }; 85 86 // Give mouse move events to the current tool 87 MotionNotifyEvent += delegate (object sender, MotionNotifyEventArgs e) { 88 var point = document.Workspace.WindowPointToCanvas (e.Event.X, e.Event.Y); 89 90 if (document.Workspace.PointInCanvas (point)) 91 PintaCore.Chrome.LastCanvasCursorPoint = point.ToGdkPoint (); 92 93 if (PintaCore.Tools.CurrentTool != null) 94 PintaCore.Tools.CurrentTool.DoMouseMove ((DrawingArea)sender, e, point); 95 }; 96 } 97 98 #region Protected Methods OnExposeEvent(EventExpose e)99 protected override bool OnExposeEvent (EventExpose e) 100 { 101 base.OnExposeEvent (e); 102 103 var scale = document.Workspace.Scale; 104 105 var x = (int)document.Workspace.Offset.X; 106 var y = (int)document.Workspace.Offset.Y; 107 108 // Translate our expose area for the whole drawingarea to just our canvas 109 var canvas_bounds = new Rectangle (x, y, document.Workspace.CanvasSize.Width, document.Workspace.CanvasSize.Height); 110 canvas_bounds.Intersect (e.Area); 111 112 if (canvas_bounds.IsEmpty) 113 return true; 114 115 canvas_bounds.X -= x; 116 canvas_bounds.Y -= y; 117 118 // Resize our offscreen surface to a surface the size of our drawing area 119 if (canvas == null || canvas.Width != canvas_bounds.Width || canvas.Height != canvas_bounds.Height) { 120 if (canvas != null) 121 (canvas as IDisposable).Dispose (); 122 123 canvas = CairoExtensions.CreateImageSurface (Cairo.Format.Argb32, canvas_bounds.Width, canvas_bounds.Height); 124 } 125 126 cr.Initialize (document.ImageSize, document.Workspace.CanvasSize); 127 128 using (var g = CairoHelper.Create (GdkWindow)) { 129 // Draw our canvas drop shadow 130 g.DrawRectangle (new Cairo.Rectangle (x - 1, y - 1, document.Workspace.CanvasSize.Width + 2, document.Workspace.CanvasSize.Height + 2), new Cairo.Color (.5, .5, .5), 1); 131 g.DrawRectangle (new Cairo.Rectangle (x - 2, y - 2, document.Workspace.CanvasSize.Width + 4, document.Workspace.CanvasSize.Height + 4), new Cairo.Color (.8, .8, .8), 1); 132 g.DrawRectangle (new Cairo.Rectangle (x - 3, y - 3, document.Workspace.CanvasSize.Width + 6, document.Workspace.CanvasSize.Height + 6), new Cairo.Color (.9, .9, .9), 1); 133 134 // Set up our clip rectangle 135 g.Rectangle (new Cairo.Rectangle (x, y, document.Workspace.CanvasSize.Width, document.Workspace.CanvasSize.Height)); 136 g.Clip (); 137 138 g.Translate (x, y); 139 140 // Render all the layers to a surface 141 var layers = document.GetLayersToPaint (); 142 143 if (layers.Count == 0) 144 canvas.Clear (); 145 146 cr.Render (layers, canvas, canvas_bounds.Location); 147 148 // Paint the surface to our canvas 149 g.SetSourceSurface (canvas, canvas_bounds.X + (int)(0 * scale), canvas_bounds.Y + (int)(0 * scale)); 150 g.Paint (); 151 152 // Selection outline 153 if (document.Selection.Visible) { 154 bool fillSelection = PintaCore.Tools.CurrentTool.Name.Contains ("Select") && 155 !PintaCore.Tools.CurrentTool.Name.Contains ("Selected"); 156 document.Selection.Draw (g, scale, fillSelection); 157 } 158 } 159 160 return true; 161 } 162 OnScrollEvent(EventScroll evnt)163 protected override bool OnScrollEvent (EventScroll evnt) 164 { 165 // Allow the user to zoom in/out with Ctrl-Mousewheel 166 if (evnt.State.FilterModifierKeys () == ModifierType.ControlMask) { 167 switch (evnt.Direction) { 168 case ScrollDirection.Down: 169 case ScrollDirection.Right: 170 document.Workspace.ZoomOutFromMouseScroll (new Cairo.PointD (evnt.X, evnt.Y)); 171 return true; 172 case ScrollDirection.Left: 173 case ScrollDirection.Up: 174 document.Workspace.ZoomInFromMouseScroll (new Cairo.PointD (evnt.X, evnt.Y)); 175 return true; 176 } 177 } 178 179 // Allow the user to scroll left/right with Shift-Mousewheel 180 if (evnt.State.FilterModifierKeys () == ModifierType.ShiftMask) { 181 switch (evnt.Direction) { 182 case ScrollDirection.Down: 183 case ScrollDirection.Right: 184 document.Workspace.ScrollCanvas (hScrollAmount, 0); 185 return true; 186 case ScrollDirection.Up: 187 case ScrollDirection.Left: 188 document.Workspace.ScrollCanvas (-hScrollAmount, 0); 189 return true; 190 } 191 } 192 193 return base.OnScrollEvent (evnt); 194 } 195 #endregion 196 197 #region Private Methods SetRequisition(Size size)198 private void SetRequisition (Size size) 199 { 200 Requisition req = new Requisition (); 201 req.Width = size.Width; 202 req.Height = size.Height; 203 Requisition = req; 204 205 QueueResize (); 206 } 207 DoKeyPressEvent(object o, KeyPressEventArgs e)208 public void DoKeyPressEvent (object o, KeyPressEventArgs e) 209 { 210 // Give the current tool a chance to handle the key press 211 PintaCore.Tools.CurrentTool.DoKeyPress (this, e); 212 } 213 DoKeyReleaseEvent(object o, KeyReleaseEventArgs e)214 public void DoKeyReleaseEvent (object o, KeyReleaseEventArgs e) 215 { 216 PintaCore.Tools.CurrentTool.DoKeyRelease (this, e); 217 } 218 #endregion 219 } 220 } 221