/*
 * Copyright 1999-2009 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Jeremy Friesner
 *		Fredrik Modéen
 */
 
 
#include "CommandActuators.h"
 
 
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
 
#include <String.h>
#include <Roster.h>
#include <Alert.h>
#include <Screen.h>
#include <Rect.h>
#include <View.h>
#include <Directory.h>
#include <Entry.h>
#include <List.h>
#include <Beep.h>
 
 
#include "ParseCommandLine.h"
#include "KeyInfos.h"
 
#define IS_KEY_DOWN(msg) ((msg->what == B_KEY_DOWN) \
	|| (msg->what == B_UNMAPPED_KEY_DOWN))
 
 
// factory function
CommandActuator*
CreateCommandActuator(const char* command)
{
	CommandActuator* act = NULL;
	int32 argc;
	char** argv = ParseArgvFromString(command, argc);
	if (command[0] == '*') {
		if (argc > 0) {
			char* c = argv[0] + 1;
			if (strcmp(c, "InsertString") == 0)
				act = new KeyStrokeSequenceCommandActuator(argc, argv);
			else if (strcmp(c, "MoveMouse") == 0)
				act = new MoveMouseByCommandActuator(argc, argv);
			else if (strcmp(c, "MoveMouseTo") == 0)
				act = new MoveMouseToCommandActuator(argc, argv);
			else if (strcmp(c, "MouseButton") == 0)
				act = new MouseButtonCommandActuator(argc, argv);
			else if (strcmp(c, "LaunchHandler") == 0)
				act = new MIMEHandlerCommandActuator(argc, argv);
			else if (strcmp(c, "Multi") == 0)
				act = new MultiCommandActuator(argc, argv);
			else if (strcmp(c, "MouseDown") == 0)
				act = new MouseDownCommandActuator(argc, argv);
			else if (strcmp(c, "MouseUp") == 0)
				act = new MouseUpCommandActuator(argc, argv);
			else if (strcmp(c, "SendMessage") == 0)
				act = new SendMessageCommandActuator(argc, argv);
			else
				act = new BeepCommandActuator(argc, argv);
		}
	} else
		act = new LaunchCommandActuator(argc, argv);
 
	FreeArgv(argv);
	return act;
}
 
 
//	#pragma mark - CommandActuator
 
 
CommandActuator::CommandActuator(int32 argc, char** argv)
{
}
 
 
CommandActuator::CommandActuator(BMessage* from)
	:
	BArchivable(from)
{
}
 
 
status_t
CommandActuator::Archive(BMessage* into, bool deep) const
{
	status_t ret = BArchivable::Archive(into, deep);
	return ret;
}
 
 
//	#pragma mark - LaunchCommandActuator
 
 
LaunchCommandActuator::LaunchCommandActuator(int32 argc, char** argv)
	:
	CommandActuator(argc, argv),
	fArgv(CloneArgv(argv)),
	fArgc(argc)
{
}
 
 
LaunchCommandActuator::LaunchCommandActuator(BMessage* from)
	:
	CommandActuator(from)
{
	BList argList;
	const char* temp;
	int idx = 0;
	while (from->FindString("largv", idx++, &temp) == B_OK) {
		if (temp) {
			char* copy = new char[strlen(temp) + 1];
			strcpy(copy, temp);
			argList.AddItem(copy);
		}
	}
 
	fArgc = argList.CountItems();
	fArgv = new char*[fArgc+ 1];
 
	for (int i = 0; i < fArgc; i++)
		fArgv[i] = (char*) argList.ItemAt(i);
 
	fArgv[fArgc] = NULL;// terminate the array
}
 
 
LaunchCommandActuator::~LaunchCommandActuator()
{
	FreeArgv(fArgv);
}
 
 
filter_result
LaunchCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
	void** setAsyncData, BMessage* mouseMessage)
{
	if (IS_KEY_DOWN(keyMessage)) {
		// cause KeyEventAsync() to be called asynchronously
		*setAsyncData = (void*) true;
	}
	return B_SKIP_MESSAGE;
}
 
 
status_t
LaunchCommandActuator::Archive(BMessage* into, bool deep) const
{
	status_t ret = CommandActuator::Archive(into, deep);
 
	for (int i = 0; i < fArgc; i++)
		into->AddString("largv", fArgv[i]);
 
	return ret;
}
 
 
BArchivable*
LaunchCommandActuator::Instantiate(BMessage* from)
{
	if (validate_instantiation(from, "LaunchCommandActuator"))
		return new LaunchCommandActuator(from);
	else
		return NULL;
}
 
 
void
LaunchCommandActuator::KeyEventAsync(const BMessage* keyMessage,
	void* asyncData)
{
	if (be_roster == NULL)
		return;
 
	status_t result = B_OK;
	BString string;
	if (fArgc < 1)
		string << "You didn't specify a command for this hotkey.";
	else if ((result = LaunchCommand(fArgv, fArgc)) != B_OK) {
		string << "Can't launch " << fArgv[0];
		string << ", no such file exists.";
		string << " Please check your Shortcuts settings.";
	}
 
	if (fArgc < 1 || result != B_OK) {
		BAlert* alert = new BAlert("Shortcuts launcher error",
			string.String(), "OK");
		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
		alert->Go(NULL);
	}
}
 
 
//	#pragma mark - MouseCommandActuator
 
 
MouseCommandActuator::MouseCommandActuator(int32 argc, char** argv)
	:
	CommandActuator(argc, argv),
	fWhichButtons(B_PRIMARY_MOUSE_BUTTON)
{
	if (argc > 1) {
		fWhichButtons = 0;
 
		for (int i = 1; i < argc; i++) {
			int buttonNumber = atoi(argv[i]);
 
			switch(buttonNumber) {
				case 1:
					fWhichButtons |= B_PRIMARY_MOUSE_BUTTON;
				break;
				case 2:
					fWhichButtons |= B_SECONDARY_MOUSE_BUTTON;
				break;
				case 3:
					fWhichButtons |= B_TERTIARY_MOUSE_BUTTON;
				break;
			}
		}
	}
}
 
 
MouseCommandActuator::MouseCommandActuator(BMessage* from)
	:
	CommandActuator(from),
	fWhichButtons(B_PRIMARY_MOUSE_BUTTON)
{
	from->FindInt32("buttons", &fWhichButtons);
}
 
 
MouseCommandActuator::~MouseCommandActuator()
{
}
 
 
status_t
MouseCommandActuator::Archive(BMessage* into, bool deep) const
{
	status_t ret = CommandActuator::Archive(into, deep);
	into->AddInt32("buttons", fWhichButtons);
	return ret;
}
 
 
int32
MouseCommandActuator::_GetWhichButtons() const
{
	return fWhichButtons;
}
 
 
void
MouseCommandActuator::_GenerateMouseButtonEvent(bool mouseDown,
	const BMessage* keyMessage, BList* outList, BMessage* mouseMessage)
{
	BMessage* fakeMouse = new BMessage(*mouseMessage);
	fakeMouse->what = mouseDown ? B_MOUSE_DOWN : B_MOUSE_UP;
 
	// Update the buttons to reflect which mouse buttons we are faking
	fakeMouse->RemoveName("buttons");
 
	if (mouseDown)
		fakeMouse->AddInt32("buttons", fWhichButtons);
 
	// Trey sez you gotta keep then "when"'s increasing if you want
	// click & drag to work!
	int64 when;
 
	const BMessage* lastMessage;
	if (outList->CountItems() > 0) {
		int32 last = outList->CountItems() - 1;
		lastMessage = (const BMessage*)outList->ItemAt(last);
	} else
		lastMessage = keyMessage;
 
	if (lastMessage->FindInt64("when", &when) == B_OK) {
		when++;
		fakeMouse->RemoveName("when");
		fakeMouse->AddInt64("when", when);
	}
 
	outList->AddItem(fakeMouse);
}
 
 
//	#pragma mark - MouseDownCommandActuator
 
 
MouseDownCommandActuator::MouseDownCommandActuator(int32 argc, char** argv)
	:
	MouseCommandActuator(argc, argv)
{
}
 
 
MouseDownCommandActuator::MouseDownCommandActuator(BMessage* from)
	:
	MouseCommandActuator(from)
{
}
 
 
MouseDownCommandActuator::~MouseDownCommandActuator()
{
}
 
 
filter_result
MouseDownCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
	void** setAsyncData, BMessage* mouseMessage)
{
	if (IS_KEY_DOWN(keyMessage))
		_GenerateMouseButtonEvent(true, keyMessage, outList, mouseMessage);
 
	return B_DISPATCH_MESSAGE;
}
 
 
status_t
MouseDownCommandActuator::Archive(BMessage* into, bool deep) const
{
	return MouseCommandActuator::Archive(into, deep);
}
 
 
BArchivable*
MouseDownCommandActuator::Instantiate(BMessage* from)
{
	if (validate_instantiation(from, "MouseDownCommandActuator"))
		return new MouseDownCommandActuator(from);
	else
		return NULL;
}
 
 
//	#pragma mark - MouseUpCommandActuator
 
 
MouseUpCommandActuator::MouseUpCommandActuator(int32 argc, char** argv)
	:
	MouseCommandActuator(argc, argv)
{
}
 
 
MouseUpCommandActuator::MouseUpCommandActuator(BMessage* from)
	:
	MouseCommandActuator(from)
{
}
 
 
MouseUpCommandActuator::~MouseUpCommandActuator()
{
}
 
 
filter_result
MouseUpCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
	void** setAsyncData, BMessage* mouseMessage)
{
	if (IS_KEY_DOWN(keyMessage))
		_GenerateMouseButtonEvent(false, keyMessage, outList, mouseMessage);
 
	return B_DISPATCH_MESSAGE;
}
 
 
status_t
MouseUpCommandActuator::Archive(BMessage* into, bool deep) const
{
	return MouseCommandActuator::Archive(into, deep);
}
 
 
BArchivable*
MouseUpCommandActuator::Instantiate(BMessage* from)
{
	if (validate_instantiation(from, "MouseUpCommandActuator"))
		return new MouseUpCommandActuator(from);
	else
		return NULL;
}
 
 
//	#pragma mark - MouseButtonCommandActuator
 
 
MouseButtonCommandActuator::MouseButtonCommandActuator(int32 argc, char** argv)
	:
	MouseCommandActuator(argc, argv),
	fKeyDown(false)
{
}
 
 
MouseButtonCommandActuator::MouseButtonCommandActuator(BMessage* from)
	:
	MouseCommandActuator(from),
	fKeyDown(false)
{
}
 
 
MouseButtonCommandActuator::~MouseButtonCommandActuator()
{
}
 
 
filter_result
MouseButtonCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
	void** setAsyncData, BMessage* mouseMessage)
{
	if (IS_KEY_DOWN(keyMessage) != fKeyDown) {
		_GenerateMouseButtonEvent(IS_KEY_DOWN(keyMessage), keyMessage, outList,
			mouseMessage);
		fKeyDown = IS_KEY_DOWN(keyMessage);
 
		return B_DISPATCH_MESSAGE;
	} else {
		// This will handle key-repeats, which we don't want turned into lots
		// of B_MOUSE_DOWN messages.
		return B_SKIP_MESSAGE;
	}
}
 
 
status_t
MouseButtonCommandActuator::Archive(BMessage* into, bool deep) const
{
	return MouseCommandActuator::Archive(into, deep);
}
 
 
BArchivable*
MouseButtonCommandActuator::Instantiate(BMessage* from)
{
	if (validate_instantiation(from, "MouseButtonCommandActuator"))
		return new MouseButtonCommandActuator(from);
	else
		return NULL;
}
 
 
//	#pragma mark - KeyStrokeSequenceCommandActuator
 
 
KeyStrokeSequenceCommandActuator::KeyStrokeSequenceCommandActuator(int32 argc,
	char** argv)
	:
	CommandActuator(argc, argv)
{
	for (int s = 1; s < argc; s++) {
		fSequence.Append(argv[s]);
		if (s < argc - 1)
			fSequence.Append(" ");
	}
 
	// Find any insert-unicode-here sequences and replace them with spaces...
	int32 nextStart;
	while ((nextStart = fSequence.FindFirst("$$")) >= 0) {
		int32 nextEnd = fSequence.FindFirst("$$", nextStart + 2);
		if (nextEnd >= 0) {
			uint32 customKey= 0;
			int32 unicodeVal= 0;
			uint32 customMods = 0;
			BString sub;
			fSequence.CopyInto(sub, nextStart + 2, nextEnd-(nextStart + 2));
			sub.ToLower();
 
			if ((sub.FindFirst('-') >= 0) || ((sub.Length() > 0)
				&& ((sub.String()[0] < '0') || (sub.String()[0] > '9')))) {
 
				const char* s = sub.String();
				while (*s == '-') s++;// go past any initial dashes
 
				bool lastWasDash = true;
				while (*s) {
					if (lastWasDash) {
						if (strncmp(s, "shift",5) == 0)
							customMods |=B_LEFT_SHIFT_KEY| B_SHIFT_KEY;
						else if (strncmp(s, "leftsh", 6) == 0)
							customMods |=B_LEFT_SHIFT_KEY| B_SHIFT_KEY;
						else if (strncmp(s, "rightsh",7) == 0)
							customMods |=B_RIGHT_SHIFT_KEY | B_SHIFT_KEY;
						else if (strncmp(s, "alt",3) == 0)
							customMods |=B_LEFT_COMMAND_KEY| B_COMMAND_KEY;
						else if (strncmp(s, "leftalt",7) == 0)
							customMods |=B_LEFT_COMMAND_KEY| B_COMMAND_KEY;
						else if (strncmp(s, "rightalt", 8) == 0)
							customMods |=B_RIGHT_COMMAND_KEY | B_COMMAND_KEY;
						else if (strncmp(s, "com",3) == 0)
							customMods |=B_LEFT_COMMAND_KEY| B_COMMAND_KEY;
						else if (strncmp(s, "leftcom",7) == 0)
							customMods |=B_LEFT_COMMAND_KEY| B_COMMAND_KEY;
						else if (strncmp(s, "rightcom", 8) == 0)
							customMods |=B_RIGHT_COMMAND_KEY | B_COMMAND_KEY;
						else if (strncmp(s, "con",3) == 0)
							customMods |=B_LEFT_CONTROL_KEY| B_CONTROL_KEY;
						else if (strncmp(s, "leftcon",7) == 0)
							customMods |=B_LEFT_CONTROL_KEY| B_CONTROL_KEY;
						else if (strncmp(s, "rightcon", 8) == 0)
							customMods |=B_RIGHT_CONTROL_KEY | B_CONTROL_KEY;
						else if (strncmp(s, "win",3) == 0)
							customMods |=B_LEFT_OPTION_KEY | B_OPTION_KEY;
						else if (strncmp(s, "leftwin",7) == 0)
							customMods |=B_LEFT_OPTION_KEY | B_OPTION_KEY;
						else if (strncmp(s, "rightwin", 8) == 0)
							customMods |=B_RIGHT_OPTION_KEY| B_OPTION_KEY;
						else if (strncmp(s, "opt",3) == 0)
							customMods |=B_LEFT_OPTION_KEY | B_OPTION_KEY;
						else if (strncmp(s, "leftopt",7) == 0)
							customMods |=B_LEFT_OPTION_KEY | B_OPTION_KEY;
						else if (strncmp(s, "rightopt", 8) == 0)
							customMods |=B_RIGHT_OPTION_KEY| B_OPTION_KEY;
						else if (strncmp(s, "menu", 4) == 0)
							customMods |=B_MENU_KEY;
						else if (strncmp(s, "caps", 4) == 0)
							customMods |=B_CAPS_LOCK;
						else if (strncmp(s, "scroll", 6) == 0)
							customMods |=B_SCROLL_LOCK;
						else if (strncmp(s, "num",3) == 0)
							customMods |=B_NUM_LOCK;
						else if (customKey == 0) {
							BString arg = s;
							int32 dashIdx = arg.FindFirst('-');
 
							if (dashIdx >= 0)
								arg.Truncate(dashIdx);
 
							uint32 key = (uint32)FindKeyCode(arg.String());
 
							if (key > 0) {
								customKey = key;
								const char* u = GetKeyUTF8(key);
 
								//Parse the UTF8 back into an int32
								switch(strlen(u)) {
									case 1:
										unicodeVal = ((uint32)(u[0]&0x7F));
										break;
									case 2:
										unicodeVal = ((uint32)(u[1]&0x3F)) |
											(((uint32)(u[0]&0x1F)) << 6);
										break;
									case 3:
										unicodeVal = ((uint32)(u[2]&0x3F)) |
											(((uint32)(u[1]&0x3F)) << 6) |
											(((uint32)(u[0]&0x0F)) << 12);
										break;
									default: unicodeVal = 0;
										break;
								}
							}
						}
						lastWasDash = false;
					} else
						lastWasDash = (*s == '-');
 
					s++;
				}
 
				// If we have a letter, try to make it the correct case
				if ((unicodeVal >= 'A') && (unicodeVal <= 'Z')) {
					if ((customMods & B_SHIFT_KEY) == 0)
						unicodeVal += 'a'-'A';
				} else if ((unicodeVal >= 'a') && (unicodeVal <= 'z')) {
					if ((customMods & B_SHIFT_KEY) != 0)
						unicodeVal -= 'a'-'A';
				}
			} else {
				unicodeVal = strtol(&(fSequence.String())[nextStart + 2],
					NULL, 0);
				customMods = (uint32) -1;
			}
 
			if (unicodeVal == 0)
				unicodeVal = ' ';
 
			BString newString = fSequence;
			newString.Truncate(nextStart);
			fOverrides.AddItem((void*)(addr_t)unicodeVal);
			fOverrideOffsets.AddItem((void*)(addr_t)newString.Length());
			fOverrideModifiers.AddItem((void*)(addr_t)customMods);
			fOverrideKeyCodes.AddItem((void*)(addr_t)customKey);
			newString.Append((unicodeVal > 0
				&& unicodeVal < 127) ? (char)unicodeVal : ' ', 1);
			newString.Append(&fSequence.String()[nextEnd + 2]);
			fSequence = newString;
		} else
			break;
	}
 
	_GenerateKeyCodes();
}
 
 
KeyStrokeSequenceCommandActuator::KeyStrokeSequenceCommandActuator(
	BMessage* from)
	:
	CommandActuator(from)
{
	const char* sequence;
	if (from->FindString("sequence", 0, &sequence) == B_OK)
		fSequence = sequence;
 
	int32 temp;
	for (int32 i = 0; from->FindInt32("ooffsets", i, &temp) == B_OK; i++) {
		fOverrideOffsets.AddItem((void*)(addr_t)temp);
 
		if (from->FindInt32("overrides", i, &temp) != B_OK)
			temp = ' ';
 
		fOverrides.AddItem((void*)(addr_t)temp);
 
		if (from->FindInt32("omods", i, &temp) != B_OK)
			temp = -1;
 
		fOverrideModifiers.AddItem((void*)(addr_t)temp);
 
		if (from->FindInt32("okeys", i, &temp) != B_OK)
			temp = 0;
 
		fOverrideKeyCodes.AddItem((void*)(addr_t)temp);
	}
 
	_GenerateKeyCodes();
}
 
 
KeyStrokeSequenceCommandActuator::~KeyStrokeSequenceCommandActuator()
{
	delete[] fKeyCodes;
	delete[] fModCodes;
	delete[] fStates;
}
 
 
void
KeyStrokeSequenceCommandActuator::_GenerateKeyCodes()
{
	int slen = fSequence.Length();
	fKeyCodes = new int32[slen];
	fModCodes = new int32[slen];
	fStates = new uint8[slen * 16];
 
	memset(fStates, 0, slen * 16);
 
	key_map* map;
	char* keys;
	get_key_map(&map, &keys);
	for (int i = 0; i < slen; i++) {
		uint32 overrideKey = 0;
		uint32 overrideMods = (uint32)-1;
		for (int32 j = fOverrideOffsets.CountItems()-1; j >= 0; j--) {
			if ((int32)(addr_t)fOverrideOffsets.ItemAt(j) == i) {
				overrideKey= (uint32)(addr_t) fOverrideKeyCodes.ItemAt(j);
				overrideMods = (uint32)(addr_t) fOverrideModifiers.ItemAt(j);
				break;
			}
		}
 
		uint8* states = &fStates[i * 16];
		int32& modCode = fModCodes[i];
		if (overrideKey == 0) {
			// Gotta do reverse-lookups to find out the raw keycodes for a
			// given character. Expensive--there oughtta be a better way to do
			// this.
			char next = fSequence.ByteAt(i);
			int32 key = _LookupKeyCode(map, keys, map->normal_map, next,
				states, modCode, 0);
			if (key < 0) {
				key = _LookupKeyCode(map, keys, map->shift_map, next, states,
					modCode, B_LEFT_SHIFT_KEY | B_SHIFT_KEY);
			}
 
			if (key < 0) {
				key = _LookupKeyCode(map, keys, map->caps_map, next, states,
					modCode, B_CAPS_LOCK);
			}
 
			if (key < 0) {
				key = _LookupKeyCode(map, keys, map->caps_shift_map, next,
					states, modCode,
					B_LEFT_SHIFT_KEY | B_SHIFT_KEY | B_CAPS_LOCK);
			}
 
			if (key < 0) {
				key = _LookupKeyCode(map, keys, map->option_map, next, states,
					modCode, B_LEFT_OPTION_KEY | B_OPTION_KEY);
			}
 
			if (key < 0) {
				key = _LookupKeyCode(map, keys, map->option_shift_map, next,
					states, modCode, B_LEFT_OPTION_KEY | B_OPTION_KEY
						| B_LEFT_SHIFT_KEY | B_SHIFT_KEY);
			}
 
			if (key < 0) {
				key = _LookupKeyCode(map, keys, map->option_caps_map, next,
					states, modCode,
					B_LEFT_OPTION_KEY | B_OPTION_KEY | B_CAPS_LOCK);
			}
 
			if (key < 0) {
				key = _LookupKeyCode(map, keys, map->option_caps_shift_map,
					next, states, modCode, B_LEFT_OPTION_KEY | B_OPTION_KEY
						| B_CAPS_LOCK | B_LEFT_SHIFT_KEY | B_SHIFT_KEY);
			}
 
			if (key < 0) {
				key = _LookupKeyCode(map, keys, map->control_map, next, states,
					modCode, B_CONTROL_KEY);
			}
 
			fKeyCodes[i] = key >= 0 ? key : 0;
		}
 
		if (overrideMods != (uint32)-1) {
			modCode = (int32)overrideMods;
 
			// Clear any bits that might have been set by the lookups...
			_SetStateBit(states, map->caps_key, false);
			_SetStateBit(states, map->scroll_key, false);
			_SetStateBit(states, map->num_key, false);
			_SetStateBit(states, map->menu_key, false);
			_SetStateBit(states, map->left_shift_key, false);
			_SetStateBit(states, map->right_shift_key, false);
			_SetStateBit(states, map->left_command_key, false);
			_SetStateBit(states, map->right_command_key, false);
			_SetStateBit(states, map->left_control_key, false);
			_SetStateBit(states, map->right_control_key, false);
			_SetStateBit(states, map->left_option_key, false);
			_SetStateBit(states, map->right_option_key, false);
 
			// And then set any bits that were specified in our override.
			if (modCode & B_CAPS_LOCK)
				_SetStateBit(states, map->caps_key);
 
			if (modCode & B_SCROLL_LOCK)
				_SetStateBit(states, map->scroll_key);
 
			if (modCode & B_NUM_LOCK)
				_SetStateBit(states, map->num_key);
 
			if (modCode & B_MENU_KEY)
				_SetStateBit(states, map->menu_key);
 
			if (modCode & B_LEFT_SHIFT_KEY)
				_SetStateBit(states, map->left_shift_key);
 
			if (modCode & B_RIGHT_SHIFT_KEY)
				_SetStateBit(states, map->right_shift_key);
 
			if (modCode & B_LEFT_COMMAND_KEY)
				_SetStateBit(states, map->left_command_key);
 
			if (modCode & B_RIGHT_COMMAND_KEY)
				_SetStateBit(states, map->right_command_key);
 
			if (modCode & B_LEFT_CONTROL_KEY)
				_SetStateBit(states, map->left_control_key);
 
			if (modCode & B_RIGHT_CONTROL_KEY)
				_SetStateBit(states, map->right_control_key);
 
			if (modCode & B_LEFT_OPTION_KEY)
				_SetStateBit(states, map->left_option_key);
 
			if (modCode & B_RIGHT_OPTION_KEY)
				_SetStateBit(states, map->right_option_key);
		}
 
		if (overrideKey > 0) {
			if (overrideKey > 127) {
				// invalid value?
				overrideKey = 0;
			}
 
			fKeyCodes[i] = overrideKey;
			_SetStateBit(states, overrideKey);
		}
	}
 
	free(keys);
	free(map);
}
 
 
int32
KeyStrokeSequenceCommandActuator::_LookupKeyCode(key_map* map, char* keys,
	int32 offsets[128], char c, uint8* setStates, int32& setModifier,
	int32 setTo) const
{
	for (int i = 0; i < 128; i++) {
		if (keys[offsets[i]+ 1] == c) {
			_SetStateBit(setStates, i);
 
			if (setTo & B_SHIFT_KEY)
				_SetStateBit(setStates, map->left_shift_key);
 
			if (setTo & B_OPTION_KEY)
				_SetStateBit(setStates, map->left_option_key);
 
			if (setTo & B_CONTROL_KEY)
				_SetStateBit(setStates, map->left_control_key);
 
			if (setTo & B_CAPS_LOCK)
				_SetStateBit(setStates, map->caps_key);
 
			setModifier = setTo;
 
			return i;
		}
	}
 
	return -1;
}
 
 
void
KeyStrokeSequenceCommandActuator::_SetStateBit(uint8* setStates, uint32 key,
	bool on) const
{
	if (on)
		setStates[key / 8] |= (0x80 >> (key % 8));
	else
		setStates[key / 8] &= ~(0x80 >> (key % 8));
}
 
 
status_t
KeyStrokeSequenceCommandActuator::Archive(BMessage* into, bool deep) const
{
	status_t result = CommandActuator::Archive(into, deep);
	if (result != B_OK)
		return result;
 
	into->AddString("sequence", fSequence.String());
	int32 numOverrides = fOverrideOffsets.CountItems();
 
	status_t overridesResult = B_OK;
	for (int32 i = 0; i < numOverrides; i++) {
		result = into->AddInt32("ooffsets",
			(int32)(addr_t)fOverrideOffsets.ItemAt(i));
		if (result != B_OK)
			overridesResult = B_ERROR;
 
		result = into->AddInt32("overrides",
			(int32)(addr_t)fOverrides.ItemAt(i));
		if (result != B_OK)
			overridesResult = B_ERROR;
 
		result = into->AddInt32("omods",
			(int32)(addr_t)fOverrideModifiers.ItemAt(i));
		if (result != B_OK)
			overridesResult = B_ERROR;
 
		result = into->AddInt32("okeys",
			(int32)(addr_t)fOverrideKeyCodes.ItemAt(i));
	}
 
	return overridesResult == B_ERROR ? B_ERROR : result;
}
 
 
filter_result
KeyStrokeSequenceCommandActuator::KeyEvent(const BMessage* keyMessage,
	BList* outList, void** setAsyncData, BMessage* mouseMessage)
{
	if (IS_KEY_DOWN(keyMessage)) {
		BMessage temp(*keyMessage);
		int numChars = fSequence.Length();
		for (int i = 0; i < numChars; i++) {
			char nextChar = fSequence.ByteAt(i);
 
			temp.RemoveName("modifiers");
			temp.AddInt32("modifiers", fModCodes[i]);
			temp.RemoveName("key");
			temp.AddInt32("key", fKeyCodes[i]);
			temp.RemoveName("raw_char");
			temp.AddInt32("raw_char", (int32) nextChar);
			temp.RemoveName("byte");
 
			int32 override = -1;
			for (int32 j = fOverrideOffsets.CountItems()-1; j >= 0; j--) {
				int32 offset = (int32)(addr_t) fOverrideOffsets.ItemAt(j);
				if (offset == i) {
					override = (int32)(addr_t) fOverrides.ItemAt(j);
					break;
				}
			}
 
			char t[4];
			if (override >= 0) {
				if (override < 0x80) {
					// one-byte encoding
					t[0] = (char) override;
					t[1] = 0x00;
				} else if (override < 0x800) {
					// two-byte encoding
					t[0] = 0xC0 | ((char)((override & 0x7C0)>>6));
					t[1] = 0x80 | ((char)((override & 0x03F)>>0));
					t[2] = 0x00;
				} else {
					// three-byte encoding
					t[0] = 0xE0 | ((char)((override & 0xF000)>>12));
					t[1] = 0x80 | ((char)((override & 0x0FC0)>>6));
					t[2] = 0x80 | ((char)((override & 0x003F)>>0));
					t[3] = 0x00;
				}
			} else {
				t[0] = nextChar;
				t[1] = 0x00;
			}
 
			temp.RemoveName("byte");
 
			for (int m = 0; t[m] != 0x00; m++)
				temp.AddInt8("byte", t[m]);
 
			temp.RemoveName("states");
			temp.AddData("states", B_UINT8_TYPE, &fStates[i * 16], 16, true, 16);
			temp.RemoveName("bytes");
			temp.AddString("bytes", t);
			temp.what = B_KEY_DOWN;
			outList->AddItem(new BMessage(temp));
			temp.what = B_KEY_UP;
			outList->AddItem(new BMessage(temp));
		}
 
		return B_DISPATCH_MESSAGE;
	} else
		return B_SKIP_MESSAGE;
}
 
 
BArchivable*
KeyStrokeSequenceCommandActuator::Instantiate(BMessage* from)
{
	if (validate_instantiation(from, "KeyStrokeSequenceCommandActuator"))
		return new KeyStrokeSequenceCommandActuator(from);
	else
		return NULL;
}
 
 
//	#pragma mark - MIMEHandlerCommandActuator
 
 
MIMEHandlerCommandActuator::MIMEHandlerCommandActuator(int32 argc, char** argv)
	:
	CommandActuator(argc, argv),
	fMimeType((argc > 1) ? argv[1] : "")
{
}
 
 
MIMEHandlerCommandActuator::MIMEHandlerCommandActuator(BMessage* from)
	:
	CommandActuator(from)
{
	const char* temp;
	if (from->FindString("mimeType", 0, &temp) == B_OK)
		fMimeType = temp;
}
 
 
MIMEHandlerCommandActuator::~MIMEHandlerCommandActuator()
{
}
 
 
status_t
MIMEHandlerCommandActuator::Archive(BMessage* into, bool deep) const
{
	status_t ret = CommandActuator::Archive(into, deep);
	into->AddString("mimeType", fMimeType.String());
	return ret;
}
 
 
filter_result
MIMEHandlerCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
	void** setAsyncData, BMessage* mouseMessage)
{
	if (IS_KEY_DOWN(keyMessage))
		// cause KeyEventAsync() to be called asynchronously
		*setAsyncData = (void*) true;
	return B_SKIP_MESSAGE;
}
 
 
void
MIMEHandlerCommandActuator::KeyEventAsync(const BMessage* keyMessage,
	void* asyncData)
{
	if (be_roster == NULL)
		return;
 
	BString string;
	status_t ret = be_roster->Launch(fMimeType.String());
	if ((ret != B_OK) && (ret != B_ALREADY_RUNNING)) {
		string << "Can't launch handler for ";
		string << ", no such MIME type exists. Please check your Shortcuts";
		string << " settings.";
		BAlert* alert = new BAlert("Shortcuts MIME launcher error",
			string.String(), "OK");
		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
		alert->Go(NULL);
	}
}
 
 
BArchivable* MIMEHandlerCommandActuator::Instantiate(BMessage* from)
{
	if (validate_instantiation(from, "MIMEHandlerCommandActuator"))
		return new MIMEHandlerCommandActuator(from);
	else
		return NULL;
}
 
 
//	#pragma mark - BeepCommandActuator
 
 
BeepCommandActuator::BeepCommandActuator(int32 argc, char** argv)
	:
	CommandActuator(argc, argv)
{
}
 
 
BeepCommandActuator::BeepCommandActuator(BMessage* from)
	:
	CommandActuator(from)
{
}
 
 
BeepCommandActuator::~BeepCommandActuator()
{
}
 
 
status_t
BeepCommandActuator::Archive(BMessage* into, bool deep) const
{
	return CommandActuator::Archive(into, deep);
}
 
 
BArchivable*
BeepCommandActuator::Instantiate(BMessage* from)
{
	if (validate_instantiation(from, "BeepCommandActuator"))
		return new BeepCommandActuator(from);
	else
		return NULL;
}
 
 
filter_result
BeepCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
	void** setAsyncData, BMessage* mouseMessage)
{
	if (IS_KEY_DOWN(keyMessage))
		beep();
 
	return B_SKIP_MESSAGE;
}
 
 
//	#pragma mark - MultiCommandActuator
 
 
MultiCommandActuator::MultiCommandActuator(BMessage* from)
	:
	CommandActuator(from)
{
	BMessage msg;
	for (int i = 0; from->FindMessage("subs", i, &msg) == B_OK; i++) {
		BArchivable* subObj = instantiate_object(&msg);
		if (subObj) {
			CommandActuator* ca = dynamic_cast < CommandActuator*>(subObj);
 
			if (ca)
				fSubActuators.AddItem(ca);
			else
				delete subObj;
		}
	}
}
 
 
MultiCommandActuator::MultiCommandActuator(int32 argc, char** argv)
	:
	CommandActuator(argc, argv)
{
	for (int i = 1; i < argc; i++) {
		CommandActuator* sub = CreateCommandActuator(argv[i]);
 
		if (sub)
			fSubActuators.AddItem(sub);
		else
			printf("Error creating subActuator from [%s]\n", argv[i]);
	}
}
 
 
MultiCommandActuator::~MultiCommandActuator()
{
	int numSubs = fSubActuators.CountItems();
	for (int i = 0; i < numSubs; i++)
		delete ((CommandActuator*) fSubActuators.ItemAt(i));
}
 
 
status_t
MultiCommandActuator::Archive(BMessage* into, bool deep) const
{
	status_t ret = CommandActuator::Archive(into, deep);
	if (ret != B_OK)
		return ret;
 
	int numSubs = fSubActuators.CountItems();
	for (int i = 0; i < numSubs; i++) {
		BMessage msg;
		ret = ((CommandActuator*)fSubActuators.ItemAt(i))->Archive(&msg, deep);
 
		if (ret != B_OK)
			return ret;
 
		into->AddMessage("subs", &msg);
	}
	return B_OK;
}
 
 
BArchivable*
MultiCommandActuator::Instantiate(BMessage* from)
{
	if (validate_instantiation(from, "MultiCommandActuator"))
		return new MultiCommandActuator(from);
	else
		return NULL;
}
 
 
filter_result
MultiCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
	void** asyncData, BMessage* mouseMessage)
{
	BList* aDataList = NULL; // demand-allocated
	filter_result res = B_SKIP_MESSAGE;
	int numSubs = fSubActuators.CountItems();
	for (int i = 0; i < numSubs; i++) {
		void* aData = NULL;
		status_t next = ((CommandActuator*)fSubActuators.ItemAt(i))->
			KeyEvent(keyMessage, outList, &aData, mouseMessage);
 
		if (next == B_DISPATCH_MESSAGE)
			// dispatch message if at least one sub wants it dispatched
			res = B_DISPATCH_MESSAGE;
 
		if (aData) {
			if (aDataList == NULL)
				*asyncData = aDataList = new BList;
 
			while (aDataList->CountItems() < i - 1)
				aDataList->AddItem(NULL);
			aDataList->AddItem(aData);
		}
	}
	return res;
}
 
 
void
MultiCommandActuator::KeyEventAsync(const BMessage* keyUpMsg, void* asyncData)
{
	BList* list = (BList*) asyncData;
	int numSubs = list->CountItems();
	for (int i = 0; i < numSubs; i++) {
		void* aData = list->ItemAt(i);
		if (aData)
			((CommandActuator*) fSubActuators.ItemAt(i))->
				KeyEventAsync(keyUpMsg, aData);
	}
	delete list;
}
 
 
//	#pragma mark - MoveMouseCommandActuator
 
 
MoveMouseCommandActuator::MoveMouseCommandActuator(BMessage* from)
	:
	CommandActuator(from)
{
	if (from->FindFloat("xPercent", &fXPercent) != B_OK)
		fXPercent = 0.0f;
 
	if (from->FindFloat("yPercent", &fYPercent) != B_OK)
		fYPercent = 0.0f;
 
	if (from->FindFloat("xPixels", &fXPixels) != B_OK)
		fXPixels = 0;
 
	if (from->FindFloat("yPixels", &fYPixels) != B_OK)
		fYPixels = 0;
}
 
 
MoveMouseCommandActuator::MoveMouseCommandActuator(int32 argc, char** argv)
	:
	CommandActuator(argc, argv),
	fXPercent(0.0f),
	fYPercent(0.0f),
	fXPixels(0),
	fYPixels(0)
{
	if (argc > 1)
		_ParseArg(argv[1], fXPercent, fXPixels);
 
	if (argc > 2)
		_ParseArg(argv[2], fYPercent, fYPixels);
}
 
 
MoveMouseCommandActuator::~MoveMouseCommandActuator()
{
}
 
 
status_t
MoveMouseCommandActuator::Archive(BMessage* into, bool deep) const
{
	status_t ret = CommandActuator::Archive(into, deep);
	into->AddFloat("xPercent", fXPercent);
	into->AddFloat("yPercent", fYPercent);
	into->AddFloat("xPixels", fXPixels);
	into->AddFloat("yPixels", fYPixels);
	return ret;
}
 
 
void
MoveMouseCommandActuator::CalculateCoords(float& setX, float& setY) const
{
	BScreen s;
	BRect frame = s.Frame();
	setX = (frame.Width() * fXPercent) + fXPixels;
	setY = (frame.Height() * fYPercent) + fYPixels;
}
 
 
BMessage*
MoveMouseCommandActuator::CreateMouseMessage(const BMessage* original,
	BPoint where, BList* outList) const
{
	// Force where into the screen space
	{
		BScreen screen;
		where.ConstrainTo(screen.Frame());
	}
 
	BMessage* newMessage = new BMessage(B_MOUSE_MOVED);
 
	newMessage->AddPoint("where", where);
 
	int32 buttons = 0;
	(void)original->FindInt32("buttons", &buttons);
 
	if (buttons == 0)
		buttons = 1;
 
	newMessage->AddInt32("buttons", buttons);
 
	// Trey sez you gotta keep then "when"'s increasing if you want click&drag
	// to work!
	const BMessage* lastMessage;
	int32 last = outList->CountItems() - 1;
 
	if (outList->CountItems() > 0)
		lastMessage = (const BMessage*)outList->ItemAt(last);
	else
		lastMessage = original;
 
	int64 when;
 
	if (lastMessage->FindInt64("when", &when) == B_OK) {
		when++;
		newMessage->RemoveName("when");
		newMessage->AddInt64("when", when);
	}
	return newMessage;
}
 
 
static bool IsNumeric(char c);
static bool IsNumeric(char c)
{
	return (((c >= '0') && (c <= '9')) || (c == '.') || (c == '-'));
}
 
 
// Parse a string of the form "10", "10%", "10+ 10%", or "10%+ 10"
void
MoveMouseCommandActuator::_ParseArg(const char* arg, float& setPercent,
	float& setPixels) const
{
	char* temp = new char[strlen(arg) + 1];
	strcpy(temp, arg);
 
	// Find the percent part, if any
	char* percent = strchr(temp, '%');
	if (percent) {
		// Rewind to one before the beginning of the number
		char* beginNum = percent - 1;
		while (beginNum >= temp) {
			char c = *beginNum;
			if (IsNumeric(c))
				beginNum--;
			else
				break;
		}
 
		// parse the number
		setPercent = atof(++beginNum)/100.0f;
 
		// Now white it out to ease finding the other #
		while (beginNum <= percent)
			*(beginNum++) = ' ';
	}
 
	// Find the pixel part, if any
	char* pixel = temp;
	while (!IsNumeric(*pixel)) {
		if (*pixel == '\0')
			break;
		pixel++;
	}
	setPixels = atof(pixel);
 delete [] temp;
}
 
 
//	#pragma mark - MoveMouseToCommandActuator
 
 
MoveMouseToCommandActuator::MoveMouseToCommandActuator(BMessage* from)
	:
	MoveMouseCommandActuator(from)
{
}
 
 
MoveMouseToCommandActuator::MoveMouseToCommandActuator(int32 argc, char** argv)
	:
	MoveMouseCommandActuator(argc, argv)
{
}
 
 
MoveMouseToCommandActuator::~MoveMouseToCommandActuator()
{
}
 
 
status_t
MoveMouseToCommandActuator::Archive(BMessage* into, bool deep) const
{
	return MoveMouseCommandActuator::Archive(into, deep);
}
 
 
BArchivable*
MoveMouseToCommandActuator::Instantiate(BMessage* from)
{
	if (validate_instantiation(from, "MoveMouseToCommandActuator"))
		return new MoveMouseToCommandActuator(from);
	else
		return NULL;
}
 
 
filter_result
MoveMouseToCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
	void** setAsyncData, BMessage* mouseMessage)
{
	if (IS_KEY_DOWN(keyMessage)) {
		float x, y;
		CalculateCoords(x, y);
		BPoint where(x, y);
		BMessage* newMessage = CreateMouseMessage(keyMessage, where, outList);
		*mouseMessage = *newMessage;
		outList->AddItem(newMessage);
 
		return B_DISPATCH_MESSAGE;
	}
 
	return B_SKIP_MESSAGE;
}
 
 
//	#pragma mark - MoveMouseByCommandActuator
 
 
MoveMouseByCommandActuator::MoveMouseByCommandActuator(BMessage* from)
	:
	MoveMouseCommandActuator(from)
{
}
 
 
MoveMouseByCommandActuator::MoveMouseByCommandActuator(int32 argc, char** argv)
	:
	MoveMouseCommandActuator(argc, argv)
{
}
 
 
MoveMouseByCommandActuator::~MoveMouseByCommandActuator()
{
}
 
 
status_t MoveMouseByCommandActuator::Archive(BMessage* into, bool deep) const
{
 status_t ret = MoveMouseCommandActuator::Archive(into, deep);
 return ret;
}
 
 
BArchivable*
MoveMouseByCommandActuator::Instantiate(BMessage* from)
{
	if (validate_instantiation(from, "MoveMouseByCommandActuator"))
		return new MoveMouseByCommandActuator(from);
	else
		return NULL;
}
 
 
filter_result
MoveMouseByCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
	void** setAsyncData, BMessage* mouseMessage)
{
	if (IS_KEY_DOWN(keyMessage)) {
		// Get the current mouse position
		BPoint where;
		if (mouseMessage->FindPoint("where", &where) == B_OK) {
			// Get the desired offset
			BPoint diff;
			CalculateCoords(diff.x, diff.y);
			where += diff;
			BMessage* newMessage = CreateMouseMessage(keyMessage, where, outList);
			*mouseMessage = *newMessage;
			outList->AddItem(newMessage);
			return B_DISPATCH_MESSAGE;
		}
	}
	return B_SKIP_MESSAGE;
}
 
 
//	#pragma mark - SendMessageCommandActuator
 
 
SendMessageCommandActuator::SendMessageCommandActuator(int32 argc, char** argv)
	:
	CommandActuator(argc, argv),
	fSignature((argc > 1) ? argv[1] : "")
{
	// Parse the what code. It may be in any of the following formats:
	// 12356 (int)
	// 'HELO' (chars enclosed in single quotes)
	// 0x12ab3c (hex)
 
	if (argc > 2) {
		const char* whatStr = argv[2];
		if ((whatStr[0] == '\'')
			&& (strlen(whatStr) == 6)
			&& (whatStr[5] == '\'')) {
			// Translate the characters into the uint32 they stand for.
			// Note that we must do this in a byte-endian-independant fashion
			// (no casting!)
			fSendMessage.what = 0;
			uint32 mult = 1;
			for (int i = 0; i < 4; i++) {
				fSendMessage.what += ((uint32)(whatStr[4 - i]))* mult;
				mult <<= 8;
			}
		} else if (strncmp(whatStr, "0x", 2) == 0)
			// translate hex string to decimal
			fSendMessage.what = strtoul(&whatStr[2], NULL, 16);
		else
			fSendMessage.what = atoi(whatStr);
	} else
		fSendMessage.what = 0;
 
	for (int i = 3; i < argc; i++) {
		type_code tc = B_BOOL_TYPE;// default type when no value is present
		const char* arg = argv[i];
		BString argString(arg);
		const char* equals = strchr(arg, '=');
		const char* value = "true";// default if no value is present
 
		if (equals) {
			tc = B_STRING_TYPE;// default type when value is present
			value = equals + 1;
			const char* colon = strchr(arg, ':');
			if (colon > equals)
				colon = NULL;// colons after the equals sign don't count
 
			if (colon) {
				const char* typeStr = colon + 1;
				if (strncasecmp(typeStr, "string", 6) == 0)
					tc = B_STRING_TYPE;
				else if (strncasecmp(typeStr, "int8", 4) == 0)
					tc = B_INT8_TYPE;
				else if (strncasecmp(typeStr, "int16", 5) == 0)
					tc = B_INT16_TYPE;
				else if (strncasecmp(typeStr, "int32", 5) == 0)
					tc = B_INT32_TYPE;
				else if (strncasecmp(typeStr, "int64", 5) == 0)
					tc = B_INT64_TYPE;
				else if (strncasecmp(typeStr, "bool", 4) == 0)
					tc = B_BOOL_TYPE;
				else if (strncasecmp(typeStr, "float", 5) == 0)
					tc = B_FLOAT_TYPE;
				else if (strncasecmp(typeStr, "double", 6) == 0)
					tc = B_DOUBLE_TYPE;
				else if (strncasecmp(typeStr, "point", 5) == 0)
					tc = B_POINT_TYPE;
				else if (strncasecmp(typeStr, "rect", 4) == 0)
					tc = B_RECT_TYPE;
 
				// remove the colon and stuff
				argString = argString.Truncate(colon - arg);
			} else
				// remove the equals and arg
				argString = argString.Truncate(equals - arg);
		}
 
		switch(tc) {
			case B_STRING_TYPE:
				fSendMessage.AddString(argString.String(), value);
				break;
 
			case B_INT8_TYPE:
				fSendMessage.AddInt8(argString.String(), (int8)atoi(value));
				break;
 
			case B_INT16_TYPE:
				fSendMessage.AddInt16(argString.String(), (int16)atoi(value));
				break;
 
			case B_INT32_TYPE:
				fSendMessage.AddInt32(argString.String(), (int32)atoi(value));
				break;
 
			case B_INT64_TYPE:
				fSendMessage.AddInt64(argString.String(), (int64)atoi(value));
				break;
 
			case B_BOOL_TYPE:
				fSendMessage.AddBool(argString.String(), ((value[0] == 't')
					|| (value[0] == 'T')));
				break;
 
			case B_FLOAT_TYPE:
				fSendMessage.AddFloat(argString.String(), atof(value));
				break;
 
			case B_DOUBLE_TYPE:
				fSendMessage.AddDouble(argString.String(), (double)atof(value));
				break;
 
			case B_POINT_TYPE:
			{
				float pts[2] = {0.0f, 0.0f};
				_ParseFloatArgs(pts, 2, value);
				fSendMessage.AddPoint(argString.String(), BPoint(pts[0], pts[1]));
				break;
			}
 
			case B_RECT_TYPE:
			{
				float pts[4] = {0.0f, 0.0f, 0.0f, 0.0f};
				_ParseFloatArgs(pts, 4, value);
				fSendMessage.AddRect(argString.String(),
					BRect(pts[0], pts[1], pts[2], pts[3]));
				break;
			}
		}
	}
}
 
 
void
SendMessageCommandActuator::_ParseFloatArgs(float* args, int maxArgs,
	const char* str) const
{
	const char* next = str;
	for (int i = 0; i < maxArgs; i++) {
		args[i] = atof(next);
		next = strchr(next, ',');
		if (next) next++;
		else break;
	}
}
 
 
SendMessageCommandActuator::SendMessageCommandActuator(BMessage* from)
	:
	CommandActuator(from)
{
	const char* temp;
 
	if (from->FindString("signature", 0, &temp) == B_OK)
		fSignature = temp;
 
	(void) from->FindMessage("sendmsg", &fSendMessage);
}
 
 
SendMessageCommandActuator::~SendMessageCommandActuator()
{
}
 
 
status_t
SendMessageCommandActuator::Archive(BMessage* into, bool deep) const
{
	status_t ret = CommandActuator::Archive(into, deep);
	into->AddString("signature", fSignature.String());
	into->AddMessage("sendmsg", &fSendMessage);
	return ret;
}
 
 
filter_result
SendMessageCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
	void** setAsyncData, BMessage* mouseMessage)
{
	if (IS_KEY_DOWN(keyMessage))
		// cause KeyEventAsync() to be called asynchronously
		*setAsyncData = (void*) true;
 
	return B_SKIP_MESSAGE;
}
 
 
void
SendMessageCommandActuator::KeyEventAsync(const BMessage* keyMessage,
	void* asyncData)
{
	if (be_roster == NULL)
		return;
 
	BString string;
	if (fSignature.Length() == 0) {
		string << "SendMessage: Target application signature not specified";
		BAlert* alert = new BAlert("Shortcuts SendMessage error",
			string.String(), "OK");
		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
		alert->Go(NULL);
	} else {
		status_t result = B_OK;
		BMessenger messenger(fSignature.String(), -1, &result);
		if (result == B_OK)
			messenger.SendMessage(&fSendMessage);
	}
}
 
 
BArchivable*
SendMessageCommandActuator::Instantiate(BMessage* from)
{
	if (validate_instantiation(from, "SendMessageCommandActuator"))
		return new SendMessageCommandActuator(from);
	else
		return NULL;
}

V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.

V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.

V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.