1 /*
  2  * © 2009 ROBO Design
  3  * http://www.robodesign.ro
  4  *
  5  * $Date: 2009-04-21 14:42:13 +0300 $
  6  */
  7 
  8 /**
  9  * @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a>
 10  * @fileOverview Extension for the paint application. Allows users to draw 
 11  * inside the paint application using the keyboard, without any pointing 
 12  * device.
 13  */
 14 
 15 
 16 /**
 17  * @class The MouseKeys action.
 18  *
 19  * @param {Painter} app Reference to the main paint application object.
 20  */
 21 function PaintMouseKeys (app) {
 22   var canvas = app.buffer.canvas,
 23       mouse  = app.mouse;
 24 
 25   /**
 26    * Holds the current mouse movement speed in pixels.
 27    *
 28    * @private
 29    * @type Number
 30    */
 31   var speed = 1;
 32 
 33   /**
 34    * Holds the current mouse movement acceleration, taken from the 
 35    * configuration.
 36    *
 37    * @private
 38    * @type Number
 39    * @see PainterConfig.mousekeys_accel The mouse keys acceleration setting.
 40    */
 41   var accel = PainterConfig.mousekeys_accel;
 42 
 43   if (!canvas || !canvas.parentNode) {
 44     return false;
 45   }
 46 
 47   /**
 48    * Holds a reference to the DOM element representing the pointer on top of the 
 49    * canvas element.
 50    *
 51    * @private
 52    * @type Element
 53    */
 54   var pointer = document.createElement('div');
 55   if (!pointer) {
 56     return false;
 57   }
 58   pointer.id = 'mousekeysPointer';
 59   pointer.style.display = 'none';
 60   canvas.parentNode.appendChild(pointer);
 61 
 62   /**
 63    * Track the virtual pointer coordinates, by updating the position of the 
 64    * <var>pointer</var> element. This allows the keyboard users to see where 
 65    * they moved the virtual pointer.
 66    *
 67    * @param {Event} ev The DOM Event object.
 68    */
 69   function pointerMousemove (ev) {
 70     if (typeof ev.x_ == 'undefined' || !ev.kobj_ || !ev.kobj_.action || ev.kobj_.action != 'mousekeys') {
 71       if (pointer.style.display == 'block') {
 72         pointer.style.display = 'none';
 73       }
 74       return;
 75     }
 76 
 77     pointer.style.top  = ev.y_ + 'px';
 78     pointer.style.left = ev.x_ + 'px';
 79   };
 80 
 81   /**
 82    * Dispatch a synthetic event to the buffer canvas element.
 83    *
 84    * @private
 85    * @param {String} type The mouse event type to dispatch.
 86    * @param {Event} ev The original DOM Event object.
 87    */
 88   function dispatch (type, ev) {
 89     var ev_new = document.createEvent('MouseEvents');
 90 
 91     ev_new.initMouseEvent(type,
 92         ev.bubbles,  ev.cancelable,
 93         ev.view,     0,
 94         0,           0,
 95         0,           0,
 96         ev.ctrlKey,  ev.altKey,
 97         ev.shiftKey, ev.metaKey,
 98         0,           ev.relatedTarget);
 99 
100     // Make sure the new coordinates are passed to the event handlers.
101     ev_new.x_ = mouse.x;
102     ev_new.y_ = mouse.y;
103 
104     // Make sure the event handlers can check this is a synthetic event.
105     // This is needed by the pointerMousemove() function.
106     ev_new.keyCode_  = ev.keyCode_;
107     ev_new.key_      = ev.key_;
108     ev_new.kid_      = ev.kid_;
109     ev_new.kobj_     = ev.kobj_;
110 
111     canvas.dispatchEvent(ev_new);
112   };
113 
114   /**
115    * The <code>keydown</code> event handler.
116    *
117    * <p>This method requires a DOM Event object which has the 
118    * <var>ev.kobj_</var> object reference from the keyboard shortcuts 
119    * configuration. The <var>kobj_</var> object must have the <var>param</var> 
120    * property. Support for the "Toggle" parameter is implemented. This parameter 
121    * essentially means that a mouse event will be generated, either 
122    * <code>mousedown</code> or <code>mouseup</code>. By alternating these two 
123    * events, this method allows the user to start and stop the drawing operation 
124    * at any moment using the keyboard shortcut they have configured.
125    *
126    * <p>Irrespective of the key the user pressed, this method does always reset 
127    * the speed and acceleration of the pointer movement.
128    *
129    * @param {Event} ev The DOM Event object.
130    *
131    * @returns {Boolean} True if the keyboard shortcut was recognized, or false 
132    * if not.
133    *
134    * @see PainterConfig.keys The keyboard shortcuts configuration object.
135    */
136   this.keydown = function (ev) {
137     speed = 1;
138     accel = PainterConfig.mousekeys_accel;
139 
140     if (pointer.style.display == 'none') {
141       pointer.style.display = 'block';
142       pointer.style.top  = mouse.y + 'px';
143       pointer.style.left = mouse.x + 'px';
144     }
145 
146     if (!ev || !ev.kobj_ || ev.kobj_.param != 'Toggle') {
147       return false;
148     }
149 
150     var type = mouse.buttonDown ? 'mouseup' : 'mousedown';
151     dispatch(type, ev);
152 
153     return true;
154   };
155 
156   /**
157    * The <code>keypress</code> event handler.
158    *
159    * <p>This method requires a DOM Event object with a <var>ev.kobj_</var> 
160    * object reference to the keyboard shortcut configuration. The keyboard 
161    * shortcut configuration object must have the <var>param</var> property.
162    *
163    * <p>This event handler implements support for the following <var>param</var>  
164    * values: "SouthWest", "South", "SouthEast", "West", "East", "NorthWest", 
165    * "North" and "NorthEast", All of these values indicate the movement 
166    * direction. This method generates synthetic <var>movemove</var> events based 
167    * on the direction desired, effectively emulating the use of a real pointing 
168    * device.
169    *
170    * @param {Event} ev The DOM Event object.
171    *
172    * @returns {Boolean} True if the keyboard shortcut was recognized, or false 
173    * if not.
174    *
175    * @see PainterConfig.keys The keyboard shortcuts configuration object.
176    */
177   this.keypress = function (ev) {
178     if (!ev || !ev.kobj_ || !ev.kobj_.param) {
179       return false;
180     }
181 
182     if (ev.shiftKey) {
183       speed += speed * accel * 3;
184     } else {
185       speed += speed * accel;
186     }
187 
188     var w = canvas.width,
189         h = canvas.height,
190         x = mouse.x,
191         y = mouse.y,
192         step = Math.ceil(speed);
193 
194     switch (ev.kobj_.param) {
195       case 'SouthWest':
196         x -= step;
197         y += step;
198         break;
199       case 'South':
200         y += step;
201         break;
202       case 'SouthEast':
203         x += step;
204         y += step;
205         break;
206       case 'West':
207         x -= step;
208         break;
209       case 'East':
210         x += step;
211         break;
212       case 'NorthWest':
213         x -= step;
214         y -= step;
215         break;
216       case 'North':
217         y -= step;
218         break;
219       case 'NorthEast':
220         x += step;
221         y -= step;
222         break;
223       default:
224         return false;
225     }
226 
227     if (x < 0) {
228       x = 0;
229     } else if (x > w) {
230       x = w;
231     }
232 
233     if (y < 0) {
234       y = 0;
235     } else if (y > h) {
236       y = h;
237     }
238 
239     mouse.x = x;
240     mouse.y = y;
241 
242     dispatch('mousemove', ev);
243 
244     return true;
245   };
246 
247   /**
248    * Handles action removal. This will remove the pointer DOM element and the 
249    * canvas event listener.
250    */
251   this.actionRemove = function () {
252     canvas.parentNode.removeChild(pointer);
253     canvas.removeEventListener('mousemove', pointerMousemove, false);
254   };
255 
256   canvas.addEventListener('mousemove', pointerMousemove, false);
257 };
258 
259 window.addEventListener('load', function () {
260   // Add the MouseKeys action to the Painter instance.
261   if (window.PainterInstance) {
262     PainterInstance.actionAdd('mousekeys', PaintMouseKeys);
263   }
264 }, false);
265 
266 // vim:set spell spl=en fo=wan1croql tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix:
267