1 /*
  2  * Copyright (C) 2008, 2009 Mihai Şucan
  3  *
  4  * This file is part of PaintWeb.
  5  *
  6  * PaintWeb is free software: you can redistribute it and/or modify
  7  * it under the terms of the GNU General Public License as published by
  8  * the Free Software Foundation, either version 3 of the License, or
  9  * (at your option) any later version.
 10  *
 11  * PaintWeb is distributed in the hope that it will be useful,
 12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14  * GNU General Public License for more details.
 15  *
 16  * You should have received a copy of the GNU General Public License
 17  * along with PaintWeb.  If not, see <http://www.gnu.org/licenses/>.
 18  *
 19  * $URL: http://code.google.com/p/paintweb $
 20  * $Date: 2009-06-11 20:21:13 +0300 $
 21  */
 22 
 23 /**
 24  * @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a>
 25  * @fileOverview Holds the rectangle tool implementation.
 26  */
 27 
 28 /**
 29  * @class The rectangle tool.
 30  *
 31  * @param {PaintWeb} app Reference to the main paint application object.
 32  */
 33 pwlib.tools.rectangle = function (app) {
 34   var _self         = this,
 35       clearInterval = app.win.clearInterval,
 36       config        = app.config,
 37       context       = app.buffer.context,
 38       gui           = app.gui,
 39       image         = app.image,
 40       MathAbs       = Math.abs,
 41       MathMin       = Math.min,
 42       mouse         = app.mouse,
 43       setInterval   = app.win.setInterval;
 44 
 45   /**
 46    * The interval ID used for invoking the drawing operation every few 
 47    * milliseconds.
 48    *
 49    * @private
 50    * @see PaintWeb.config.toolDrawDelay
 51    */
 52   var timer = null;
 53 
 54   /**
 55    * Tells if the <kbd>Shift</kbd> key is down or not. This is used by the 
 56    * drawing function.
 57    *
 58    * @private
 59    * @type Boolean
 60    * @default false
 61    */
 62   var shiftKey = false;
 63 
 64   /**
 65    * Tells if the drawing canvas needs to be updated or not.
 66    *
 67    * @private
 68    * @type Boolean
 69    * @default false
 70    */
 71   var needsRedraw = false;
 72 
 73   /**
 74    * Holds the starting point on the <var>x</var> axis of the image, for the 
 75    * current drawing operation.
 76    *
 77    * @private
 78    * @type Number
 79    */
 80   var x0 = 0;
 81 
 82   /**
 83    * Holds the starting point on the <var>y</var> axis of the image, for the 
 84    * current drawing operation.
 85    *
 86    * @private
 87    * @type Number
 88    */
 89   var y0 = 0;
 90 
 91   /**
 92    * Tool deactivation event handler.
 93    */
 94   this.deactivate = function () {
 95     if (timer) {
 96       clearInterval(timer);
 97       timer = null;
 98     }
 99 
100     if (mouse.buttonDown) {
101       context.clearRect(0, 0, image.width, image.height);
102     }
103 
104     needsRedraw = false;
105   };
106 
107   /**
108    * Initialize the drawing operation.
109    *
110    * @param {Event} ev The DOM Event object.
111    */
112   this.mousedown = function (ev) {
113     x0 = mouse.x;
114     y0 = mouse.y;
115 
116     if (!timer) {
117       timer = setInterval(_self.draw, config.toolDrawDelay);
118     }
119     shiftKey = ev.shiftKey;
120     needsRedraw = false;
121 
122     gui.statusShow('rectangleMousedown');
123 
124     return true;
125   };
126 
127   /**
128    * Store the <kbd>Shift</kbd> key state which is used by the drawing function.
129    *
130    * @param {Event} ev The DOM Event object.
131    */
132   this.mousemove = function (ev) {
133     shiftKey = ev.shiftKey;
134     needsRedraw = true;
135   };
136 
137   /**
138    * Perform the drawing operation. This function is called every few 
139    * milliseconds.
140    *
141    * <p>Hold down the <kbd>Shift</kbd> key to draw a square.
142    * <p>Press <kbd>Escape</kbd> to cancel the drawing operation.
143    *
144    * @see PaintWeb.config.toolDrawDelay
145    */
146   this.draw = function () {
147     if (!needsRedraw) {
148       return;
149     }
150 
151     context.clearRect(0, 0, image.width, image.height);
152 
153     var x = MathMin(mouse.x,  x0),
154         y = MathMin(mouse.y,  y0),
155         w = MathAbs(mouse.x - x0),
156         h = MathAbs(mouse.y - y0);
157 
158     if (!w || !h) {
159       needsRedraw = false;
160       return;
161     }
162 
163     // Constrain the shape to a square
164     if (shiftKey) {
165       if (w > h) {
166         if (y == mouse.y) {
167           y -= w-h;
168         }
169         h = w;
170       } else {
171         if (x == mouse.x) {
172           x -= h-w;
173         }
174         w = h;
175       }
176     }
177 
178     if (config.shapeType != 'stroke') {
179       context.fillRect(x, y, w, h);
180     }
181 
182     if (config.shapeType != 'fill') {
183       context.strokeRect(x, y, w, h);
184     }
185 
186     needsRedraw = false;
187   };
188 
189   /**
190    * End the drawing operation, once the user releases the mouse button.
191    *
192    * @param {Event} ev The DOM Event object.
193    */
194   this.mouseup = function (ev) {
195     // Allow click+mousemove, not only mousedown+move+up
196     if (mouse.x == x0 && mouse.y == y0) {
197       mouse.buttonDown = true;
198       return true;
199     }
200 
201     if (timer) {
202       clearInterval(timer);
203       timer = null;
204     }
205 
206     shiftKey = ev.shiftKey;
207     _self.draw();
208     app.layerUpdate();
209     gui.statusShow('rectangleActive');
210 
211     return true;
212   };
213 
214   /**
215    * Allows the user to press <kbd>Escape</kbd> to cancel the drawing operation.
216    *
217    * @param {Event} ev The DOM Event object.
218    *
219    * @returns {Boolean} True if the drawing operation was cancelled, or false if 
220    * not.
221    */
222   this.keydown = function (ev) {
223     if (!mouse.buttonDown || ev.kid_ != 'Escape') {
224       return false;
225     }
226 
227     if (timer) {
228       clearInterval(timer);
229       timer = null;
230     }
231 
232     context.clearRect(0, 0, image.width, image.height);
233     mouse.buttonDown = false;
234     needsRedraw = false;
235 
236     gui.statusShow('rectangleActive');
237 
238     return true;
239   };
240 };
241 
242 // vim:set spell spl=en fo=wan1croqlt tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix:
243 
244 
245