/* 
Copyright (c) 1991-2000 UserLand Software, Inc. 

Permission is hereby granted, free of charge, to any person obtaining a 
copy of this software and associated documentation files (the 
"Software"), to deal in the Software without restriction, including 
without limitation the rights to use, copy, modify, merge, publish, 
distribute, sublicense, and/or sell copies of the Software, and to 
permit persons to whom the Software is furnished to do so, subject to the 
following conditions: 

The above copyright notice and this permission notice shall be 
included in all copies or substantial portions of the Software. 

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
*/ 


#include "appletmemory.h"
#include "appletcursor.h"
#include "appletkb.h"



#define keycodeclear 71 /*keycodes for numeric keybad*/
#define keycodeminus 78
#define keycodeplus 69
#define keycodetimes 67
#define keycodeseven 89
#define keycodeeight 91
#define keycodenine 92
#define keycodedivide 77
#define keycodefour 86
#define keycodefive 87
#define keycodesix 88
#define keycodecomma 72
#define keycodeone 83
#define keycodetwo 84
#define keycodethree 85
#define keycodeenter 76
#define keycodezero 82
#define keycodeperiod 65


tykeystrokerecord keyboardstatus; 

static boolean flescapepending = false;




boolean arrowkey (char chkb) {
	
	/*
	return true if the indicated key character is an arrow key.
	*/
	
	char ch = chkb;
	
	return (
		(ch == chuparrow) || (ch == chdownarrow) || 
		
		(ch == chleftarrow) || (ch == chrightarrow));
	} /*arrowkey*/


tydirection keystroketodirection (char ch) {
	
	switch (ch) {
		
		case chleftarrow:
			return (left);
			
		case chrightarrow:
			return (right);
			
		case chuparrow:
			return (up);
			
		case chdownarrow:
			return (down);
		} /*switch*/
		
	return (nodirection);
	} /*keystroketodirection*/
	

static void kbsetstatus (EventRecord ev, tykeystrokerecord *kbs) {
	
	/*
	12/11/90 dmb: don't count the caps lock as a modifier in ctmodifiers.
	
	2/21/91 dmb: handle calls for mousedown events.  we just want to 
	record modifier keys
	*/
	
	short ct;
	char chkb;
	short keycode;
	tykeystrokerecord kbcurrent;
	
	kbcurrent.flshiftkey = (ev.modifiers & shiftKey) != 0;
	
	kbcurrent.flcmdkey = (ev.modifiers & cmdKey) != 0;
	
	kbcurrent.floptionkey = (ev.modifiers & optionKey) != 0;
	
	kbcurrent.flalphalock = (ev.modifiers & alphaLock) != 0;
	
	kbcurrent.flcontrolkey = (ev.modifiers & controlKey) != 0;
	
	ct = 0;
	
	if (kbcurrent.flshiftkey) ct++;
	
	if (kbcurrent.flcmdkey) ct++;
	
	if (kbcurrent.floptionkey) ct++;
	
	/*
	if (kbcurrent.flalphalock) ct++;
	*/
	
	if (kbcurrent.flcontrolkey) ct++;
	
	kbcurrent.ctmodifiers = ct;	
	
	if (ev.what == mouseDown) {
		
		kbcurrent.chkb = chnul;
		
		kbcurrent.keycode = 0;
		
		kbcurrent.flautokey = false;
		
		kbcurrent.keydirection = nodirection;
		}
	else {
		
		kbcurrent.chkb = chkb = ev.message & charCodeMask; /*get the keystroke*/
		
		kbcurrent.flautokey = (ev.what == autoKey);
		
		kbcurrent.keycode = keycode = (ev.message & keyCodeMask) >> 8;
	    
		kbcurrent.keydirection = keystroketodirection (chkb);
		
		if (kbcurrent.flcmdkey  &&  kbcurrent.floptionkey) {
	
			/*
			we don't want to option character, so find the normal character from 
			the keymap.  see IM V-195
			*/
			
			Handle hkchr;
			unsigned long state = 0;
			
			if ((hkchr = GetResource ('KCHR', 0)) != NULL)
				kbcurrent.chkb = KeyTrans (*hkchr, kbcurrent.keycode, &state) & 0x000000ff;
			};
	
		kbcurrent.flkeypad = /*true if it is a keystroke from the numeric keypad*/
			
			(keycode == keycodeclear) 		|| (keycode == keycodeminus) 	|| 
			
			(keycode == keycodeplus) 		|| (keycode == keycodetimes) 	|| 
			
			(keycode == keycodeseven) 		|| (keycode == keycodeeight) 	||
			
			(keycode == keycodenine) 		|| (keycode == keycodedivide) 	||
			
			(keycode == keycodefour) 		|| (keycode == keycodefive) 	||
			
			(keycode == keycodesix) 		|| (keycode == keycodecomma) 	||
			
			(keycode == keycodeone) 		|| (keycode == keycodetwo) 		||
			
			(keycode == keycodethree) 		|| (keycode == keycodeenter) 	||
			
			(keycode == keycodezero) 		|| (keycode == keycodeperiod);
		}
	
	*kbs = kbcurrent; /*set for caller*/
	} /*kbsetstatus*/
	

void setkeyboardstatus (EventRecord ev) {
	
	/*
	sets a global that may be referenced by anyone.
	
	should be called every time a new event is received.
	*/
	
	kbsetstatus (ev, &keyboardstatus); /*use global*/
	} /*setkeyboardstatus*/


void keyboardclearescape (void) {
	
	flescapepending = false;
	} /*keyboardclearescape*/


boolean keyboardescape (void) {
	
	/*
	check to see if the user has pressed cmd-period.  if not, no effect on
	the event queue and we return false.
	
	otherwise, we remove the keystroke and return true.  the caller is expected
	to return quickly!
	
	DW 8/18/93: also handle Esc key.
	
	DW 8/30/93: the manual can say that you can press cmd-period or Esc 
	whenever you see the beachball rolling.
	
	DW 9/16/93: check once per second.
	*/
	
	if (flescapepending)
		return (true);
		
	rollbeachball (); /*DW 8/30/93*/
		
	/*return false if it's been less than 1 second since we checked*/ {
		
		static unsigned long tclastcheck = 0;
		unsigned long tc;
		
		tc = TickCount ();
		
		if ((tc - tclastcheck) < 60)
			return (false);
		
		tclastcheck = tc; /*... and fall thru*/
		}
	
	/*check the event queue for an Esc or cmd-period*/ {
	
		EventRecord ev;
		
		if (EventAvail (keyDownMask, &ev)) {
			
			tykeystrokerecord kbcurrent;
	
			kbsetstatus (ev, &kbcurrent);
			
			if (kbcurrent.chkb == chescape) /*Esc key*/
				flescapepending = true;
				
			if (kbcurrent.flcmdkey && (kbcurrent.chkb == '.'))
				flescapepending = true;
				
			if (flescapepending) 		
				GetNextEvent (keyDownMask, &ev); /*get rid of the Esc keystroke*/
			} 
		}
	
	return (flescapepending);
	} /*keyboardescape*/


void keyboardpeek (tykeystrokerecord *kbrecord) {
	
	ptrkeystrokerecord p = kbrecord;
	KeyMap keys;
	
	clearbytes (p, longsizeof (tykeystrokerecord));
	
	GetKeys (keys);
	
	(*p).flshiftkey = BitTst (&keys, 63);
		
	(*p).flcmdkey = BitTst (&keys, 48);
		
	(*p).floptionkey = BitTst (&keys, 61);
		
	(*p).flcontrolkey = BitTst (&keys, 60);
	} /*keyboardpeek*/


static boolean keydown (short keycode) {

	KeyMap keys;
	
	GetKeys (keys);
	
	return (BitTst (&keys, keycode));
	} /*keydown*/
	
	
boolean enterkeydown (void) {	
	
	return (keydown (75));
	} /*enterkeydown*/
	
	
boolean optionkeydown (void) {
	
	return (keydown (61));
	} /*optionkeydown*/
	
	
boolean cmdkeydown (void) {
		
	return (keydown (48));
	} /*cmdkeydown*/
	
	
boolean shiftkeydown (void) {
		
	return (keydown (63));
	} /*shiftkeydown*/
	
	
boolean returnkeydown (void) {
	
	/*
	short i; 
	bigstring bs;
	
	for (i = 1; i <= 200; i++) {
	
		if (keydown (i)) {
		
			NumToString (i, bs);
			
			DebugStr (bs);
			}
		}
	*/
		
	return (keydown (35)); 
	} /*returnkeydown*/