API  0.9.8
 All Classes Files Functions Variables Typedefs Macros Groups Pages
CPEvent.j
Go to the documentation of this file.
1 /*
2  * CPEvent.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
25 
26 
27 @global CPApp
28 
29 @typedef DOMEvent
30 @typedef CPEventType
31 
32 
33 var _CPEventPeriodicEventPeriod = 0,
34  _CPEventPeriodicEventTimer = nil,
35  _CPEventUpperCaseRegex = new RegExp("[A-Z]"),
36  _CPEventStartupMilliseconds = new Date().getTime();
37 
42 @implementation CPEvent : CPObject
43 {
44  CPEventType _type;
45  CGPoint _location;
46  unsigned _modifierFlags;
47  CPTimeInterval _timestamp;
48  CPGraphicsContext _context;
49  int _eventNumber;
50  unsigned _clickCount;
51  float _pressure;
52  CPWindow _window;
53  Number _windowNumber;
54  CPString _characters;
55  CPString _charactersIgnoringModifiers
56  BOOL _isARepeat;
57  unsigned _keyCode;
58  DOMEvent _DOMEvent;
59  int _data1;
60  int _data2;
61  short _subtype;
62 
63  float _deltaX;
64  float _deltaY;
65  float _deltaZ;
66  float _scrollingDeltaX;
67  float _scrollingDeltaY;
68  BOOL _hasPreciseScrollingDeltas;
69 
70 #if PLATFORM(DOM)
71  BOOL _suppressCappuccinoCut;
72  BOOL _suppressCappuccinoPaste;
73 #endif
74 }
75 
79 + (CPTimeInterval)currentTimestamp
80 {
81  return (new Date().getTime() - _CPEventStartupMilliseconds) / 1000;
82 }
83 
101 + (CPEvent)keyEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned int)modifierFlags
102  timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
103  characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)repeatKey keyCode:(unsigned short)code
104 {
105  return [[self alloc] _initKeyEventWithType:anEventType location:aPoint modifierFlags:modifierFlags
106  timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext
107  characters:characters charactersIgnoringModifiers:unmodCharacters isARepeat:repeatKey keyCode:code];
108 }
109 
125 + (id)mouseEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags
126  timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
127  eventNumber:(int)anEventNumber clickCount:(int)aClickCount pressure:(float)aPressure
128 {
129  return [[self alloc] _initMouseEventWithType:anEventType location:aPoint modifierFlags:modifierFlags
130  timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext eventNumber:anEventNumber clickCount:aClickCount pressure:aPressure];
131 }
132 
148 + (CPEvent)otherEventWithType:(CPEventType)anEventType location:(CGPoint)aLocation modifierFlags:(unsigned)modifierFlags
149  timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
150  subtype:(short)aSubtype data1:(int)aData1 data2:(int)aData2
151 {
152  return [[self alloc] _initOtherEventWithType:anEventType location:aLocation modifierFlags:modifierFlags
153  timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext subtype:aSubtype data1:aData1 data2:aData2];
154 }
155 
156 - (id)_initWithType:(CPEventType)anEventType
157 {
158  if (self = [super init])
159  {
160  _type = anEventType;
161 
162  // Make sure these are 0 rather than nil.
163  _deltaX = 0;
164  _scrollingDeltaX = 0;
165  _deltaY = 0;
166  _scrollingDeltaY = 0;
167  _deltaZ = 0;
168  }
169 
170  return self;
171 }
172 
173 /* @ignore */
174 - (id)_initMouseEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags
175  timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
176  eventNumber:(int)anEventNumber clickCount:(int)aClickCount pressure:(float)aPressure
177 {
178  if (self = [self _initWithType:anEventType])
179  {
180  _location = CGPointCreateCopy(aPoint);
181  _modifierFlags = modifierFlags;
182  _timestamp = aTimestamp;
183  _context = aGraphicsContext;
184  _eventNumber = anEventNumber;
185  _clickCount = aClickCount;
186  _pressure = aPressure;
187  _window = [CPApp windowWithWindowNumber:aWindowNumber];
188  }
189 
190  return self;
191 }
192 
193 /* @ignore */
194 - (id)_initKeyEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned int)modifierFlags
195  timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
196  characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)isARepeat keyCode:(unsigned short)code
197 {
198  if (self = [self _initWithType:anEventType])
199  {
200  _location = CGPointCreateCopy(aPoint);
201  _modifierFlags = modifierFlags;
202  _timestamp = aTimestamp;
203  _context = aGraphicsContext;
204  _characters = characters;
205  _charactersIgnoringModifiers = unmodCharacters;
206  _isARepeat = isARepeat;
207  _keyCode = code;
208  _windowNumber = aWindowNumber;
209  }
210 
211  return self;
212 }
213 
214 /* @ignore */
215 - (id)_initOtherEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags
216  timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
217  subtype:(short)aSubtype data1:(int)aData1 data2:(int)aData2
218 {
219  if (self = [self _initWithType:anEventType])
220  {
221  _location = CGPointCreateCopy(aPoint);
222  _modifierFlags = modifierFlags;
223  _timestamp = aTimestamp;
224  _context = aGraphicsContext;
225  _subtype = aSubtype;
226  _data1 = aData1;
227  _data2 = aData2;
228  }
229 
230  return self;
231 }
232 
241 - (CGPoint)locationInWindow
242 {
243  return CGPointMakeCopy(_location);
244 }
245 
246 - (CGPoint)globalLocation
247 {
248  var theWindow = [self window],
249  location = [self locationInWindow];
250 
251  if (theWindow)
252  return [theWindow convertBaseToGlobal:location];
253 
254  return location;
255 }
256 
260 - (unsigned)modifierFlags
261 {
262  return _modifierFlags;
263 }
264 
268 - (CPTimeInterval)timestamp
269 {
270  return _timestamp;
271 }
272 
276 - (CPEventType)type
277 {
278  return _type;
279 }
280 
284 - (CPWindow)window
285 {
286  if (!_window)
287  _window = [CPApp windowWithWindowNumber:_windowNumber];
288 
289  return _window;
290 }
291 
295 - (int)windowNumber
296 {
297  return _windowNumber;
298 }
299 
300 // Mouse Event Information
304 - (int)buttonNumber
305 {
306  if (_type === CPRightMouseDown || _type === CPRightMouseUp || _type === CPRightMouseDragged)
307  return 1;
308 
309  return 0;
310 }
311 
315 - (int)clickCount
316 {
317  return _clickCount;
318 }
319 
325 - (CPString)characters
326 {
327  return _characters;
328 }
329 
335 - (CPString)charactersIgnoringModifiers
336 {
337  return _charactersIgnoringModifiers;
338 }
339 
345 - (BOOL)isARepeat
346 {
347  return _isARepeat;
348 }
349 
355 - (unsigned short)keyCode
356 {
357  return _keyCode;
358 }
359 
360 + (CGPoint)mouseLocation
361 {
362  // FIXME: this is incorrect, we shouldn't depend on the current event.
363  var event = [CPApp currentEvent],
364  eventWindow = [event window];
365 
366  if (eventWindow)
367  return [eventWindow convertBaseToGlobal:[event locationInWindow]];
368 
369  return [event locationInWindow];
370 }
371 
372 - (float)pressure
373 {
374  return _pressure;
375 }
376 
377 /*
378  @ignore
379 */
380 - (DOMEvent)_DOMEvent
381 {
382  return _DOMEvent;
383 }
384 
385 - (int)data1
386 {
387  return _data1;
388 }
389 
390 - (int)data2
391 {
392  return _data2;
393 }
394 
395 // Getting Scroll Wheel Event Information
399 - (float)deltaX
400 {
401  return _deltaX;
402 }
403 
407 - (float)deltaY
408 {
409  return _deltaY;
410 }
411 
415 - (float)deltaZ
416 {
417  return _deltaZ;
418 }
419 
420 - (BOOL)hasPreciseScrollingDeltas
421 {
422  return !!_hasPreciseScrollingDeltas;
423 }
424 
430 - (float)scrollingDeltaX
431 {
432  return _scrollingDeltaX;
433 }
434 
440 - (float)scrollingDeltaY
441 {
442  return _scrollingDeltaY;
443 }
444 
445 - (BOOL)_triggersKeyEquivalent:(CPString)aKeyEquivalent withModifierMask:aKeyEquivalentModifierMask
446 {
447  if (!aKeyEquivalent)
448  return NO;
449 
450  if (_CPEventUpperCaseRegex.test(aKeyEquivalent))
451  aKeyEquivalentModifierMask |= CPShiftKeyMask;
452 
453  // Windows and Linux don't have command keys, so just switch it to ctrl.
454  if (!CPBrowserIsOperatingSystem(CPMacOperatingSystem) && (aKeyEquivalentModifierMask & CPCommandKeyMask))
455  {
456  aKeyEquivalentModifierMask |= CPControlKeyMask;
457  aKeyEquivalentModifierMask &= ~CPCommandKeyMask;
458  }
459 
460  if ((_modifierFlags & (CPShiftKeyMask | CPAlternateKeyMask | CPCommandKeyMask | CPControlKeyMask)) !== aKeyEquivalentModifierMask)
461  return NO;
462 
463  // Treat \r and \n as the same key equivalent. See issue #710.
464  if (_characters === CPNewlineCharacter || _characters === CPCarriageReturnCharacter)
465  return CPNewlineCharacter === aKeyEquivalent || CPCarriageReturnCharacter === aKeyEquivalent;
466 
467  return [_characters caseInsensitiveCompare:aKeyEquivalent] === CPOrderedSame;
468 }
469 
470 - (BOOL)_couldBeKeyEquivalent
471 {
472  if (_type !== CPKeyDown)
473  return NO;
474 
475  var characterCount = _characters.length;
476 
477  if (!characterCount)
478  return NO;
479 
480  if (_modifierFlags & (CPCommandKeyMask | CPControlKeyMask))
481  return YES;
482 
483  // Cocoa allows almost any key as a key equivalent unless the first responder is a
484  // text field (presumably a subclass of NSText.)
485  var firstResponderIsText = [[_window firstResponder] isKindOfClass:[CPTextField class]];
486 
487  // Some keys are accepted as key equivalents even if the first responder is a text
488  // field.
489  for (var i = 0; i < characterCount; i++)
490  {
491  var c = _characters.charAt(i);
492 
493  if ((c >= CPUpArrowFunctionKey && c <= CPModeSwitchFunctionKey) ||
494  c === CPEnterCharacter ||
495  c === CPNewlineCharacter ||
497  c === CPEscapeFunctionKey)
498  {
499  return YES;
500  }
501  }
502 
503  return !firstResponderIsText;
504 }
505 
514 - (BOOL)_platformIsEffectingCutOrPaste
515 {
516 #if PLATFORM(DOM)
517  return _suppressCappuccinoCut || _suppressCappuccinoPaste;
518 #else
519  return NO;
520 #endif
521 }
522 
523 
530 + (void)startPeriodicEventsAfterDelay:(CPTimeInterval)aDelay withPeriod:(CPTimeInterval)aPeriod
531 {
532  _CPEventPeriodicEventPeriod = aPeriod;
533 
534  // FIXME: OH TIMERS!!!
535  _CPEventPeriodicEventTimer = window.setTimeout(function() { _CPEventPeriodicEventTimer = window.setInterval(_CPEventFirePeriodEvent, aPeriod * 1000.0); }, aDelay * 1000.0);
536 }
537 
541 + (void)stopPeriodicEvents
542 {
543  if (_CPEventPeriodicEventTimer === nil)
544  return;
545 
546  window.clearTimeout(_CPEventPeriodicEventTimer);
547 
548  _CPEventPeriodicEventTimer = nil;
549 }
550 
551 - (CPString)description
552 {
553  switch (_type)
554  {
555  case CPKeyDown:
556  case CPKeyUp:
557  case CPFlagsChanged:
558  return [CPString stringWithFormat:@"CPEvent: type=%d loc=%@ time=%.1f flags=0x%X win=%@ winNum=%d ctxt=%@ chars=\"%@\" unmodchars=\"%@\" repeat=%d keyCode=%d", _type, CGStringFromPoint(_location), _timestamp, _modifierFlags, _window, _windowNumber, _context, _characters, _charactersIgnoringModifiers, _isARepeat, _keyCode];
559  case CPLeftMouseDown:
560  case CPLeftMouseUp:
561  case CPRightMouseDown:
562  case CPRightMouseUp:
563  case CPMouseMoved:
564  case CPLeftMouseDragged:
565  case CPRightMouseDragged:
566  case CPMouseEntered:
567  case CPMouseExited:
568  return [CPString stringWithFormat:@"CPEvent: type=%d loc=%@ time=%.1f flags=0x%X win=%@ winNum=%d ctxt=%@ evNum=%d click=%d buttonNumber=%d pressure=%f", _type, CGStringFromPoint(_location), _timestamp, _modifierFlags, _window, _windowNumber, _context, _eventNumber, _clickCount, [self buttonNumber], _pressure];
569  default:
570  return [CPString stringWithFormat:@"CPEvent: type=%d loc=%@ time=%.1f flags=0x%X win=%@ winNum=%d ctxt=%@ subtype=%d data1=%d data2=%d", _type, CGStringFromPoint(_location), _timestamp, _modifierFlags, _window, _windowNumber, _context, _subtype, _data1, _data2];
571  }
572 }
573 
574 @end
575 
576 function _CPEventFirePeriodEvent()
577 {
578  [CPApp sendEvent:[CPEvent otherEventWithType:CPPeriodic location:CGPointMakeZero() modifierFlags:0 timestamp:[CPEvent currentTimestamp] windowNumber:0 context:nil subtype:0 data1:0 data2:0]];
579 }
580 
582 
583 function _CPEventFromNativeMouseEvent(aNativeEvent, anEventType, aPoint, modifierFlags, aTimestamp, aWindowNumber, aGraphicsContext, anEventNumber, aClickCount, aPressure)
584 {
585  aNativeEvent.isa = CPEventClass;
586 
587  aNativeEvent._type = anEventType;
588  aNativeEvent._location = aPoint;
589  aNativeEvent._modifierFlags = modifierFlags;
590  aNativeEvent._timestamp = aTimestamp;
591  aNativeEvent._windowNumber = aWindowNumber;
592  aNativeEvent._window = nil;
593  aNativeEvent._context = aGraphicsContext;
594  aNativeEvent._eventNumber = anEventNumber;
595  aNativeEvent._clickCount = aClickCount;
596  aNativeEvent._pressure = aPressure;
597 
598  return aNativeEvent;
599 }