1/*
2 * Copyright 1999-2009 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Jeremy Friesner
7 *		Fredrik Mod��en
8 */
9
10
11#include "CommandActuators.h"
12
13
14#include <stdio.h>
15#include <stdlib.h>
16#include <strings.h>
17
18#include <String.h>
19#include <Roster.h>
20#include <Alert.h>
21#include <Screen.h>
22#include <Rect.h>
23#include <View.h>
24#include <Directory.h>
25#include <Entry.h>
26#include <List.h>
27#include <Beep.h>
28
29
30#include "ParseCommandLine.h"
31#include "KeyInfos.h"
32
33#define IS_KEY_DOWN(msg) ((msg->what == B_KEY_DOWN) \
34	|| (msg->what == B_UNMAPPED_KEY_DOWN))
35
36
37// factory function
38CommandActuator*
39CreateCommandActuator(const char* command)
40{
41	CommandActuator* act = NULL;
42	int32 argc;
43	char** argv = ParseArgvFromString(command, argc);
44	if (command[0] == '*') {
45		if (argc > 0) {
46			char* c = argv[0] + 1;
47			if (strcmp(c, "InsertString") == 0)
48				act = new KeyStrokeSequenceCommandActuator(argc, argv);
49			else if (strcmp(c, "MoveMouse") == 0)
50				act = new MoveMouseByCommandActuator(argc, argv);
51			else if (strcmp(c, "MoveMouseTo") == 0)
52				act = new MoveMouseToCommandActuator(argc, argv);
53			else if (strcmp(c, "MouseButton") == 0)
54				act = new MouseButtonCommandActuator(argc, argv);
55			else if (strcmp(c, "LaunchHandler") == 0)
56				act = new MIMEHandlerCommandActuator(argc, argv);
57			else if (strcmp(c, "Multi") == 0)
58				act = new MultiCommandActuator(argc, argv);
59			else if (strcmp(c, "MouseDown") == 0)
60				act = new MouseDownCommandActuator(argc, argv);
61			else if (strcmp(c, "MouseUp") == 0)
62				act = new MouseUpCommandActuator(argc, argv);
63			else if (strcmp(c, "SendMessage") == 0)
64				act = new SendMessageCommandActuator(argc, argv);
65			else
66				act = new BeepCommandActuator(argc, argv);
67		}
68	} else
69		act = new LaunchCommandActuator(argc, argv);
70
71	FreeArgv(argv);
72	return act;
73}
74
75
76//	#pragma mark - CommandActuator
77
78
79CommandActuator::CommandActuator(int32 argc, char** argv)
80{
81}
82
83
84CommandActuator::CommandActuator(BMessage* from)
85	:
86	BArchivable(from)
87{
88}
89
90
91status_t
92CommandActuator::Archive(BMessage* into, bool deep) const
93{
94	status_t ret = BArchivable::Archive(into, deep);
95	return ret;
96}
97
98
99//	#pragma mark - LaunchCommandActuator
100
101
102LaunchCommandActuator::LaunchCommandActuator(int32 argc, char** argv)
103	:
104	CommandActuator(argc, argv),
105	fArgv(CloneArgv(argv)),
106	fArgc(argc)
107{
108}
109
110
111LaunchCommandActuator::LaunchCommandActuator(BMessage* from)
112	:
113	CommandActuator(from)
114{
115	BList argList;
116	const char* temp;
117	int idx = 0;
118	while (from->FindString("largv", idx++, &temp) == B_OK) {
119		if (temp) {
120			char* copy = new char[strlen(temp) + 1];
121			strcpy(copy, temp);
122			argList.AddItem(copy);
123		}
124	}
125
126	fArgc = argList.CountItems();
127	fArgv = new char*[fArgc+ 1];
128
129	for (int i = 0; i < fArgc; i++)
130		fArgv[i] = (char*) argList.ItemAt(i);
131
132	fArgv[fArgc] = NULL;// terminate the array
133}
134
135
136LaunchCommandActuator::~LaunchCommandActuator()
137{
138	FreeArgv(fArgv);
139}
140
141
142filter_result
143LaunchCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
144	void** setAsyncData, BMessage* mouseMessage)
145{
146	if (IS_KEY_DOWN(keyMessage)) {
147		// cause KeyEventAsync() to be called asynchronously
148		*setAsyncData = (void*) true;
149	}
150	return B_SKIP_MESSAGE;
151}
152
153
154status_t
155LaunchCommandActuator::Archive(BMessage* into, bool deep) const
156{
157	status_t ret = CommandActuator::Archive(into, deep);
158
159	for (int i = 0; i < fArgc; i++)
160		into->AddString("largv", fArgv[i]);
161
162	return ret;
163}
164
165
166BArchivable*
167LaunchCommandActuator::Instantiate(BMessage* from)
168{
169	if (validate_instantiation(from, "LaunchCommandActuator"))
170		return new LaunchCommandActuator(from);
171	else
172		return NULL;
173}
174
175
176void
177LaunchCommandActuator::KeyEventAsync(const BMessage* keyMessage,
178	void* asyncData)
179{
180	if (be_roster == NULL)
181		return;
182
183	status_t result = B_OK;
184	BString string;
185	if (fArgc < 1)
186		string << "You didn't specify a command for this hotkey.";
187	else if ((result = LaunchCommand(fArgv, fArgc)) != B_OK) {
188		string << "Can't launch " << fArgv[0];
189		string << ", no such file exists.";
190		string << " Please check your Shortcuts settings.";
191	}
192
193	if (fArgc < 1 || result != B_OK) {
194		BAlert* alert = new BAlert("Shortcuts launcher error",
195			string.String(), "OK");
196		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
197		alert->Go(NULL);
198	}
199}
200
201
202//	#pragma mark - MouseCommandActuator
203
204
205MouseCommandActuator::MouseCommandActuator(int32 argc, char** argv)
206	:
207	CommandActuator(argc, argv),
208	fWhichButtons(B_PRIMARY_MOUSE_BUTTON)
209{
210	if (argc > 1) {
211		fWhichButtons = 0;
212
213		for (int i = 1; i < argc; i++) {
214			int buttonNumber = atoi(argv[i]);
215
216			switch(buttonNumber) {
217				case 1:
218					fWhichButtons |= B_PRIMARY_MOUSE_BUTTON;
219				break;
220				case 2:
221					fWhichButtons |= B_SECONDARY_MOUSE_BUTTON;
222				break;
223				case 3:
224					fWhichButtons |= B_TERTIARY_MOUSE_BUTTON;
225				break;
226			}
227		}
228	}
229}
230
231
232MouseCommandActuator::MouseCommandActuator(BMessage* from)
233	:
234	CommandActuator(from),
235	fWhichButtons(B_PRIMARY_MOUSE_BUTTON)
236{
237	from->FindInt32("buttons", &fWhichButtons);
238}
239
240
241MouseCommandActuator::~MouseCommandActuator()
242{
243}
244
245
246status_t
247MouseCommandActuator::Archive(BMessage* into, bool deep) const
248{
249	status_t ret = CommandActuator::Archive(into, deep);
250	into->AddInt32("buttons", fWhichButtons);
251	return ret;
252}
253
254
255int32
256MouseCommandActuator::_GetWhichButtons() const
257{
258	return fWhichButtons;
259}
260
261
262void
263MouseCommandActuator::_GenerateMouseButtonEvent(bool mouseDown,
264	const BMessage* keyMessage, BList* outList, BMessage* mouseMessage)
265{
266	BMessage* fakeMouse = new BMessage(*mouseMessage);
267	fakeMouse->what = mouseDown ? B_MOUSE_DOWN : B_MOUSE_UP;
268
269	// Update the buttons to reflect which mouse buttons we are faking
270	fakeMouse->RemoveName("buttons");
271
272	if (mouseDown)
273		fakeMouse->AddInt32("buttons", fWhichButtons);
274
275	// Trey sez you gotta keep then "when"'s increasing if you want
276	// click & drag to work!
277	int64 when;
278
279	const BMessage* lastMessage;
280	if (outList->CountItems() > 0) {
281		int32 last = outList->CountItems() - 1;
282		lastMessage = (const BMessage*)outList->ItemAt(last);
283	} else
284		lastMessage = keyMessage;
285
286	if (lastMessage->FindInt64("when", &when) == B_OK) {
287		when++;
288		fakeMouse->RemoveName("when");
289		fakeMouse->AddInt64("when", when);
290	}
291
292	outList->AddItem(fakeMouse);
293}
294
295
296//	#pragma mark - MouseDownCommandActuator
297
298
299MouseDownCommandActuator::MouseDownCommandActuator(int32 argc, char** argv)
300	:
301	MouseCommandActuator(argc, argv)
302{
303}
304
305
306MouseDownCommandActuator::MouseDownCommandActuator(BMessage* from)
307	:
308	MouseCommandActuator(from)
309{
310}
311
312
313MouseDownCommandActuator::~MouseDownCommandActuator()
314{
315}
316
317
318filter_result
319MouseDownCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
320	void** setAsyncData, BMessage* mouseMessage)
321{
322	if (IS_KEY_DOWN(keyMessage))
323		_GenerateMouseButtonEvent(true, keyMessage, outList, mouseMessage);
324
325	return B_DISPATCH_MESSAGE;
326}
327
328
329status_t
330MouseDownCommandActuator::Archive(BMessage* into, bool deep) const
331{
332	return MouseCommandActuator::Archive(into, deep);
333}
334
335
336BArchivable*
337MouseDownCommandActuator::Instantiate(BMessage* from)
338{
339	if (validate_instantiation(from, "MouseDownCommandActuator"))
340		return new MouseDownCommandActuator(from);
341	else
342		return NULL;
343}
344
345
346//	#pragma mark - MouseUpCommandActuator
347
348
349MouseUpCommandActuator::MouseUpCommandActuator(int32 argc, char** argv)
350	:
351	MouseCommandActuator(argc, argv)
352{
353}
354
355
356MouseUpCommandActuator::MouseUpCommandActuator(BMessage* from)
357	:
358	MouseCommandActuator(from)
359{
360}
361
362
363MouseUpCommandActuator::~MouseUpCommandActuator()
364{
365}
366
367
368filter_result
369MouseUpCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
370	void** setAsyncData, BMessage* mouseMessage)
371{
372	if (IS_KEY_DOWN(keyMessage))
373		_GenerateMouseButtonEvent(false, keyMessage, outList, mouseMessage);
374
375	return B_DISPATCH_MESSAGE;
376}
377
378
379status_t
380MouseUpCommandActuator::Archive(BMessage* into, bool deep) const
381{
382	return MouseCommandActuator::Archive(into, deep);
383}
384
385
386BArchivable*
387MouseUpCommandActuator::Instantiate(BMessage* from)
388{
389	if (validate_instantiation(from, "MouseUpCommandActuator"))
390		return new MouseUpCommandActuator(from);
391	else
392		return NULL;
393}
394
395
396//	#pragma mark - MouseButtonCommandActuator
397
398
399MouseButtonCommandActuator::MouseButtonCommandActuator(int32 argc, char** argv)
400	:
401	MouseCommandActuator(argc, argv),
402	fKeyDown(false)
403{
404}
405
406
407MouseButtonCommandActuator::MouseButtonCommandActuator(BMessage* from)
408	:
409	MouseCommandActuator(from),
410	fKeyDown(false)
411{
412}
413
414
415MouseButtonCommandActuator::~MouseButtonCommandActuator()
416{
417}
418
419
420filter_result
421MouseButtonCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
422	void** setAsyncData, BMessage* mouseMessage)
423{
424	if (IS_KEY_DOWN(keyMessage) != fKeyDown) {
425		_GenerateMouseButtonEvent(IS_KEY_DOWN(keyMessage), keyMessage, outList,
426			mouseMessage);
427		fKeyDown = IS_KEY_DOWN(keyMessage);
428
429		return B_DISPATCH_MESSAGE;
430	} else {
431		// This will handle key-repeats, which we don't want turned into lots
432		// of B_MOUSE_DOWN messages.
433		return B_SKIP_MESSAGE;
434	}
435}
436
437
438status_t
439MouseButtonCommandActuator::Archive(BMessage* into, bool deep) const
440{
441	return MouseCommandActuator::Archive(into, deep);
442}
443
444
445BArchivable*
446MouseButtonCommandActuator::Instantiate(BMessage* from)
447{
448	if (validate_instantiation(from, "MouseButtonCommandActuator"))
449		return new MouseButtonCommandActuator(from);
450	else
451		return NULL;
452}
453
454
455//	#pragma mark - KeyStrokeSequenceCommandActuator
456
457
458KeyStrokeSequenceCommandActuator::KeyStrokeSequenceCommandActuator(int32 argc,
459	char** argv)
460	:
461	CommandActuator(argc, argv)
462{
463	for (int s = 1; s < argc; s++) {
464		fSequence.Append(argv[s]);
465		if (s < argc - 1)
466			fSequence.Append(" ");
467	}
468
469	// Find any insert-unicode-here sequences and replace them with spaces...
470	int32 nextStart;
471	while ((nextStart = fSequence.FindFirst("$$")) >= 0) {
472		int32 nextEnd = fSequence.FindFirst("$$", nextStart + 2);
473		if (nextEnd >= 0) {
474			uint32 customKey= 0;
475			int32 unicodeVal= 0;
476			uint32 customMods = 0;
477			BString sub;
478			fSequence.CopyInto(sub, nextStart + 2, nextEnd-(nextStart + 2));
479			sub.ToLower();
480
481			if ((sub.FindFirst('-') >= 0) || ((sub.Length() > 0)
482				&& ((sub.String()[0] < '0') || (sub.String()[0] > '9')))) {
483
484				const char* s = sub.String();
485				while (*s == '-') s++;// go past any initial dashes
486
487				bool lastWasDash = true;
488				while (*s) {
489					if (lastWasDash) {
490						if (strncmp(s, "shift",5) == 0)
491							customMods |=B_LEFT_SHIFT_KEY| B_SHIFT_KEY;
492						else if (strncmp(s, "leftsh", 6) == 0)
493							customMods |=B_LEFT_SHIFT_KEY| B_SHIFT_KEY;
494						else if (strncmp(s, "rightsh",7) == 0)
495							customMods |=B_RIGHT_SHIFT_KEY | B_SHIFT_KEY;
496						else if (strncmp(s, "alt",3) == 0)
497							customMods |=B_LEFT_COMMAND_KEY| B_COMMAND_KEY;
498						else if (strncmp(s, "leftalt",7) == 0)
499							customMods |=B_LEFT_COMMAND_KEY| B_COMMAND_KEY;
500						else if (strncmp(s, "rightalt", 8) == 0)
501							customMods |=B_RIGHT_COMMAND_KEY | B_COMMAND_KEY;
502						else if (strncmp(s, "com",3) == 0)
503							customMods |=B_LEFT_COMMAND_KEY| B_COMMAND_KEY;
504						else if (strncmp(s, "leftcom",7) == 0)
505							customMods |=B_LEFT_COMMAND_KEY| B_COMMAND_KEY;
506						else if (strncmp(s, "rightcom", 8) == 0)
507							customMods |=B_RIGHT_COMMAND_KEY | B_COMMAND_KEY;
508						else if (strncmp(s, "con",3) == 0)
509							customMods |=B_LEFT_CONTROL_KEY| B_CONTROL_KEY;
510						else if (strncmp(s, "leftcon",7) == 0)
511							customMods |=B_LEFT_CONTROL_KEY| B_CONTROL_KEY;
512						else if (strncmp(s, "rightcon", 8) == 0)
513							customMods |=B_RIGHT_CONTROL_KEY | B_CONTROL_KEY;
514						else if (strncmp(s, "win",3) == 0)
515							customMods |=B_LEFT_OPTION_KEY | B_OPTION_KEY;
516						else if (strncmp(s, "leftwin",7) == 0)
517							customMods |=B_LEFT_OPTION_KEY | B_OPTION_KEY;
518						else if (strncmp(s, "rightwin", 8) == 0)
519							customMods |=B_RIGHT_OPTION_KEY| B_OPTION_KEY;
520						else if (strncmp(s, "opt",3) == 0)
521							customMods |=B_LEFT_OPTION_KEY | B_OPTION_KEY;
522						else if (strncmp(s, "leftopt",7) == 0)
523							customMods |=B_LEFT_OPTION_KEY | B_OPTION_KEY;
524						else if (strncmp(s, "rightopt", 8) == 0)
525							customMods |=B_RIGHT_OPTION_KEY| B_OPTION_KEY;
526						else if (strncmp(s, "menu", 4) == 0)
527							customMods |=B_MENU_KEY;
528						else if (strncmp(s, "caps", 4) == 0)
529							customMods |=B_CAPS_LOCK;
530						else if (strncmp(s, "scroll", 6) == 0)
531							customMods |=B_SCROLL_LOCK;
532						else if (strncmp(s, "num",3) == 0)
533							customMods |=B_NUM_LOCK;
534						else if (customKey == 0) {
535							BString arg = s;
536							int32 dashIdx = arg.FindFirst('-');
537
538							if (dashIdx >= 0)
539								arg.Truncate(dashIdx);
540
541							uint32 key = (uint32)FindKeyCode(arg.String());
542
543							if (key > 0) {
544								customKey = key;
545								const char* u = GetKeyUTF8(key);
546
547								//Parse the UTF8 back into an int32
548								switch(strlen(u)) {
549									case 1:
550										unicodeVal = ((uint32)(u[0]&0x7F));
551										break;
552									case 2:
553										unicodeVal = ((uint32)(u[1]&0x3F)) |
554											(((uint32)(u[0]&0x1F)) << 6);
555										break;
556									case 3:
557										unicodeVal = ((uint32)(u[2]&0x3F)) |
558											(((uint32)(u[1]&0x3F)) << 6) |
559											(((uint32)(u[0]&0x0F)) << 12);
560										break;
561									default: unicodeVal = 0;
562										break;
563								}
564							}
565						}
566						lastWasDash = false;
567					} else
568						lastWasDash = (*s == '-');
569
570					s++;
571				}
572
573				// If we have a letter, try to make it the correct case
574				if ((unicodeVal >= 'A') && (unicodeVal <= 'Z')) {
575					if ((customMods & B_SHIFT_KEY) == 0)
576						unicodeVal += 'a'-'A';
577				} else if ((unicodeVal >= 'a') && (unicodeVal <= 'z')) {
578					if ((customMods & B_SHIFT_KEY) != 0)
579						unicodeVal -= 'a'-'A';
580				}
581			} else {
582				unicodeVal = strtol(&(fSequence.String())[nextStart + 2],
583					NULL, 0);
584				customMods = (uint32) -1;
585			}
586
587			if (unicodeVal == 0)
588				unicodeVal = ' ';
589
590			BString newString = fSequence;
591			newString.Truncate(nextStart);
592			fOverrides.AddItem((void*)(addr_t)unicodeVal);
593			fOverrideOffsets.AddItem((void*)(addr_t)newString.Length());
594			fOverrideModifiers.AddItem((void*)(addr_t)customMods);
595			fOverrideKeyCodes.AddItem((void*)(addr_t)customKey);
596			newString.Append((unicodeVal > 0
597				&& unicodeVal < 127) ? (char)unicodeVal : ' ', 1);
598			newString.Append(&fSequence.String()[nextEnd + 2]);
599			fSequence = newString;
600		} else
601			break;
602	}
603
604	_GenerateKeyCodes();
605}
606
607
608KeyStrokeSequenceCommandActuator::KeyStrokeSequenceCommandActuator(
609	BMessage* from)
610	:
611	CommandActuator(from)
612{
613	const char* sequence;
614	if (from->FindString("sequence", 0, &sequence) == B_OK)
615		fSequence = sequence;
616
617	int32 temp;
618	for (int32 i = 0; from->FindInt32("ooffsets", i, &temp) == B_OK; i++) {
619		fOverrideOffsets.AddItem((void*)(addr_t)temp);
620
621		if (from->FindInt32("overrides", i, &temp) != B_OK)
622			temp = ' ';
623
624		fOverrides.AddItem((void*)(addr_t)temp);
625
626		if (from->FindInt32("omods", i, &temp) != B_OK)
627			temp = -1;
628
629		fOverrideModifiers.AddItem((void*)(addr_t)temp);
630
631		if (from->FindInt32("okeys", i, &temp) != B_OK)
632			temp = 0;
633
634		fOverrideKeyCodes.AddItem((void*)(addr_t)temp);
635	}
636
637	_GenerateKeyCodes();
638}
639
640
641KeyStrokeSequenceCommandActuator::~KeyStrokeSequenceCommandActuator()
642{
643	delete[] fKeyCodes;
644	delete[] fModCodes;
645	delete[] fStates;
646}
647
648
649void
650KeyStrokeSequenceCommandActuator::_GenerateKeyCodes()
651{
652	int slen = fSequence.Length();
653	fKeyCodes = new int32[slen];
654	fModCodes = new int32[slen];
655	fStates = new uint8[slen * 16];
656
657	memset(fStates, 0, slen * 16);
658
659	key_map* map;
660	char* keys;
661	get_key_map(&map, &keys);
662	for (int i = 0; i < slen; i++) {
663		uint32 overrideKey = 0;
664		uint32 overrideMods = (uint32)-1;
665		for (int32 j = fOverrideOffsets.CountItems()-1; j >= 0; j--) {
666			if ((int32)(addr_t)fOverrideOffsets.ItemAt(j) == i) {
667				overrideKey= (uint32)(addr_t) fOverrideKeyCodes.ItemAt(j);
668				overrideMods = (uint32)(addr_t) fOverrideModifiers.ItemAt(j);
669				break;
670			}
671		}
672
673		uint8* states = &fStates[i * 16];
674		int32& modCode = fModCodes[i];
675		if (overrideKey == 0) {
676			// Gotta do reverse-lookups to find out the raw keycodes for a
677			// given character. Expensive--there oughtta be a better way to do
678			// this.
679			char next = fSequence.ByteAt(i);
680			int32 key = _LookupKeyCode(map, keys, map->normal_map, next,
681				states, modCode, 0);
682			if (key < 0) {
683				key = _LookupKeyCode(map, keys, map->shift_map, next, states,
684					modCode, B_LEFT_SHIFT_KEY | B_SHIFT_KEY);
685			}
686
687			if (key < 0) {
688				key = _LookupKeyCode(map, keys, map->caps_map, next, states,
689					modCode, B_CAPS_LOCK);
690			}
691
692			if (key < 0) {
693				key = _LookupKeyCode(map, keys, map->caps_shift_map, next,
694					states, modCode,
695					B_LEFT_SHIFT_KEY | B_SHIFT_KEY | B_CAPS_LOCK);
696			}
697
698			if (key < 0) {
699				key = _LookupKeyCode(map, keys, map->option_map, next, states,
700					modCode, B_LEFT_OPTION_KEY | B_OPTION_KEY);
701			}
702
703			if (key < 0) {
704				key = _LookupKeyCode(map, keys, map->option_shift_map, next,
705					states, modCode, B_LEFT_OPTION_KEY | B_OPTION_KEY
706						| B_LEFT_SHIFT_KEY | B_SHIFT_KEY);
707			}
708
709			if (key < 0) {
710				key = _LookupKeyCode(map, keys, map->option_caps_map, next,
711					states, modCode,
712					B_LEFT_OPTION_KEY | B_OPTION_KEY | B_CAPS_LOCK);
713			}
714
715			if (key < 0) {
716				key = _LookupKeyCode(map, keys, map->option_caps_shift_map,
717					next, states, modCode, B_LEFT_OPTION_KEY | B_OPTION_KEY
718						| B_CAPS_LOCK | B_LEFT_SHIFT_KEY | B_SHIFT_KEY);
719			}
720
721			if (key < 0) {
722				key = _LookupKeyCode(map, keys, map->control_map, next, states,
723					modCode, B_CONTROL_KEY);
724			}
725
726			fKeyCodes[i] = key >= 0 ? key : 0;
727		}
728
729		if (overrideMods != (uint32)-1) {
730			modCode = (int32)overrideMods;
731
732			// Clear any bits that might have been set by the lookups...
733			_SetStateBit(states, map->caps_key, false);
734			_SetStateBit(states, map->scroll_key, false);
735			_SetStateBit(states, map->num_key, false);
736			_SetStateBit(states, map->menu_key, false);
737			_SetStateBit(states, map->left_shift_key, false);
738			_SetStateBit(states, map->right_shift_key, false);
739			_SetStateBit(states, map->left_command_key, false);
740			_SetStateBit(states, map->right_command_key, false);
741			_SetStateBit(states, map->left_control_key, false);
742			_SetStateBit(states, map->right_control_key, false);
743			_SetStateBit(states, map->left_option_key, false);
744			_SetStateBit(states, map->right_option_key, false);
745
746			// And then set any bits that were specified in our override.
747			if (modCode & B_CAPS_LOCK)
748				_SetStateBit(states, map->caps_key);
749
750			if (modCode & B_SCROLL_LOCK)
751				_SetStateBit(states, map->scroll_key);
752
753			if (modCode & B_NUM_LOCK)
754				_SetStateBit(states, map->num_key);
755
756			if (modCode & B_MENU_KEY)
757				_SetStateBit(states, map->menu_key);
758
759			if (modCode & B_LEFT_SHIFT_KEY)
760				_SetStateBit(states, map->left_shift_key);
761
762			if (modCode & B_RIGHT_SHIFT_KEY)
763				_SetStateBit(states, map->right_shift_key);
764
765			if (modCode & B_LEFT_COMMAND_KEY)
766				_SetStateBit(states, map->left_command_key);
767
768			if (modCode & B_RIGHT_COMMAND_KEY)
769				_SetStateBit(states, map->right_command_key);
770
771			if (modCode & B_LEFT_CONTROL_KEY)
772				_SetStateBit(states, map->left_control_key);
773
774			if (modCode & B_RIGHT_CONTROL_KEY)
775				_SetStateBit(states, map->right_control_key);
776
777			if (modCode & B_LEFT_OPTION_KEY)
778				_SetStateBit(states, map->left_option_key);
779
780			if (modCode & B_RIGHT_OPTION_KEY)
781				_SetStateBit(states, map->right_option_key);
782		}
783
784		if (overrideKey > 0) {
785			if (overrideKey > 127) {
786				// invalid value?
787				overrideKey = 0;
788			}
789
790			fKeyCodes[i] = overrideKey;
791			_SetStateBit(states, overrideKey);
792		}
793	}
794
795	free(keys);
796	free(map);
797}
798
799
800int32
801KeyStrokeSequenceCommandActuator::_LookupKeyCode(key_map* map, char* keys,
802	int32 offsets[128], char c, uint8* setStates, int32& setModifier,
803	int32 setTo) const
804{
805	for (int i = 0; i < 128; i++) {
806		if (keys[offsets[i]+ 1] == c) {
807			_SetStateBit(setStates, i);
808
809			if (setTo & B_SHIFT_KEY)
810				_SetStateBit(setStates, map->left_shift_key);
811
812			if (setTo & B_OPTION_KEY)
813				_SetStateBit(setStates, map->left_option_key);
814
815			if (setTo & B_CONTROL_KEY)
816				_SetStateBit(setStates, map->left_control_key);
817
818			if (setTo & B_CAPS_LOCK)
819				_SetStateBit(setStates, map->caps_key);
820
821			setModifier = setTo;
822
823			return i;
824		}
825	}
826
827	return -1;
828}
829
830
831void
832KeyStrokeSequenceCommandActuator::_SetStateBit(uint8* setStates, uint32 key,
833	bool on) const
834{
835	if (on)
836		setStates[key / 8] |= (0x80 >> (key % 8));
837	else
838		setStates[key / 8] &= ~(0x80 >> (key % 8));
839}
840
841
842status_t
843KeyStrokeSequenceCommandActuator::Archive(BMessage* into, bool deep) const
844{
845	status_t result = CommandActuator::Archive(into, deep);
846	if (result != B_OK)
847		return result;
848
849	into->AddString("sequence", fSequence.String());
850	int32 numOverrides = fOverrideOffsets.CountItems();
851
852	status_t overridesResult = B_OK;
853	for (int32 i = 0; i < numOverrides; i++) {
854		result = into->AddInt32("ooffsets",
855			(int32)(addr_t)fOverrideOffsets.ItemAt(i));
856		if (result != B_OK)
857			overridesResult = B_ERROR;
858
859		result = into->AddInt32("overrides",
860			(int32)(addr_t)fOverrides.ItemAt(i));
861		if (result != B_OK)
862			overridesResult = B_ERROR;
863
864		result = into->AddInt32("omods",
865			(int32)(addr_t)fOverrideModifiers.ItemAt(i));
866		if (result != B_OK)
867			overridesResult = B_ERROR;
868
869		result = into->AddInt32("okeys",
870			(int32)(addr_t)fOverrideKeyCodes.ItemAt(i));
871	}
872
873	return overridesResult == B_ERROR ? B_ERROR : result;
874}
875
876
877filter_result
878KeyStrokeSequenceCommandActuator::KeyEvent(const BMessage* keyMessage,
879	BList* outList, void** setAsyncData, BMessage* mouseMessage)
880{
881	if (IS_KEY_DOWN(keyMessage)) {
882		BMessage temp(*keyMessage);
883		int numChars = fSequence.Length();
884		for (int i = 0; i < numChars; i++) {
885			char nextChar = fSequence.ByteAt(i);
886
887			temp.RemoveName("modifiers");
888			temp.AddInt32("modifiers", fModCodes[i]);
889			temp.RemoveName("key");
890			temp.AddInt32("key", fKeyCodes[i]);
891			temp.RemoveName("raw_char");
892			temp.AddInt32("raw_char", (int32) nextChar);
893			temp.RemoveName("byte");
894
895			int32 override = -1;
896			for (int32 j = fOverrideOffsets.CountItems()-1; j >= 0; j--) {
897				int32 offset = (int32)(addr_t) fOverrideOffsets.ItemAt(j);
898				if (offset == i) {
899					override = (int32)(addr_t) fOverrides.ItemAt(j);
900					break;
901				}
902			}
903
904			char t[4];
905			if (override >= 0) {
906				if (override < 0x80) {
907					// one-byte encoding
908					t[0] = (char) override;
909					t[1] = 0x00;
910				} else if (override < 0x800) {
911					// two-byte encoding
912					t[0] = 0xC0 | ((char)((override & 0x7C0)>>6));
913					t[1] = 0x80 | ((char)((override & 0x03F)>>0));
914					t[2] = 0x00;
915				} else {
916					// three-byte encoding
917					t[0] = 0xE0 | ((char)((override & 0xF000)>>12));
918					t[1] = 0x80 | ((char)((override & 0x0FC0)>>6));
919					t[2] = 0x80 | ((char)((override & 0x003F)>>0));
920					t[3] = 0x00;
921				}
922			} else {
923				t[0] = nextChar;
924				t[1] = 0x00;
925			}
926
927			temp.RemoveName("byte");
928
929			for (int m = 0; t[m] != 0x00; m++)
930				temp.AddInt8("byte", t[m]);
931
932			temp.RemoveName("states");
933			temp.AddData("states", B_UINT8_TYPE, &fStates[i * 16], 16, true, 16);
934			temp.RemoveName("bytes");
935			temp.AddString("bytes", t);
936			temp.what = B_KEY_DOWN;
937			outList->AddItem(new BMessage(temp));
938			temp.what = B_KEY_UP;
939			outList->AddItem(new BMessage(temp));
940		}
941
942		return B_DISPATCH_MESSAGE;
943	} else
944		return B_SKIP_MESSAGE;
945}
946
947
948BArchivable*
949KeyStrokeSequenceCommandActuator::Instantiate(BMessage* from)
950{
951	if (validate_instantiation(from, "KeyStrokeSequenceCommandActuator"))
952		return new KeyStrokeSequenceCommandActuator(from);
953	else
954		return NULL;
955}
956
957
958//	#pragma mark - MIMEHandlerCommandActuator
959
960
961MIMEHandlerCommandActuator::MIMEHandlerCommandActuator(int32 argc, char** argv)
962	:
963	CommandActuator(argc, argv),
964	fMimeType((argc > 1) ? argv[1] : "")
965{
966}
967
968
969MIMEHandlerCommandActuator::MIMEHandlerCommandActuator(BMessage* from)
970	:
971	CommandActuator(from)
972{
973	const char* temp;
974	if (from->FindString("mimeType", 0, &temp) == B_OK)
975		fMimeType = temp;
976}
977
978
979MIMEHandlerCommandActuator::~MIMEHandlerCommandActuator()
980{
981}
982
983
984status_t
985MIMEHandlerCommandActuator::Archive(BMessage* into, bool deep) const
986{
987	status_t ret = CommandActuator::Archive(into, deep);
988	into->AddString("mimeType", fMimeType.String());
989	return ret;
990}
991
992
993filter_result
994MIMEHandlerCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
995	void** setAsyncData, BMessage* mouseMessage)
996{
997	if (IS_KEY_DOWN(keyMessage))
998		// cause KeyEventAsync() to be called asynchronously
999		*setAsyncData = (void*) true;
1000	return B_SKIP_MESSAGE;
1001}
1002
1003
1004void
1005MIMEHandlerCommandActuator::KeyEventAsync(const BMessage* keyMessage,
1006	void* asyncData)
1007{
1008	if (be_roster == NULL)
1009		return;
1010
1011	BString string;
1012	status_t ret = be_roster->Launch(fMimeType.String());
1013	if ((ret != B_OK) && (ret != B_ALREADY_RUNNING)) {
1014		string << "Can't launch handler for ";
1015		string << ", no such MIME type exists. Please check your Shortcuts";
1016		string << " settings.";
1017		BAlert* alert = new BAlert("Shortcuts MIME launcher error",
1018			string.String(), "OK");
1019		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1020		alert->Go(NULL);
1021	}
1022}
1023
1024
1025BArchivable* MIMEHandlerCommandActuator::Instantiate(BMessage* from)
1026{
1027	if (validate_instantiation(from, "MIMEHandlerCommandActuator"))
1028		return new MIMEHandlerCommandActuator(from);
1029	else
1030		return NULL;
1031}
1032
1033
1034//	#pragma mark - BeepCommandActuator
1035
1036
1037BeepCommandActuator::BeepCommandActuator(int32 argc, char** argv)
1038	:
1039	CommandActuator(argc, argv)
1040{
1041}
1042
1043
1044BeepCommandActuator::BeepCommandActuator(BMessage* from)
1045	:
1046	CommandActuator(from)
1047{
1048}
1049
1050
1051BeepCommandActuator::~BeepCommandActuator()
1052{
1053}
1054
1055
1056status_t
1057BeepCommandActuator::Archive(BMessage* into, bool deep) const
1058{
1059	return CommandActuator::Archive(into, deep);
1060}
1061
1062
1063BArchivable*
1064BeepCommandActuator::Instantiate(BMessage* from)
1065{
1066	if (validate_instantiation(from, "BeepCommandActuator"))
1067		return new BeepCommandActuator(from);
1068	else
1069		return NULL;
1070}
1071
1072
1073filter_result
1074BeepCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
1075	void** setAsyncData, BMessage* mouseMessage)
1076{
1077	if (IS_KEY_DOWN(keyMessage))
1078		beep();
1079
1080	return B_SKIP_MESSAGE;
1081}
1082
1083
1084//	#pragma mark - MultiCommandActuator
1085
1086
1087MultiCommandActuator::MultiCommandActuator(BMessage* from)
1088	:
1089	CommandActuator(from)
1090{
1091	BMessage msg;
1092	for (int i = 0; from->FindMessage("subs", i, &msg) == B_OK; i++) {
1093		BArchivable* subObj = instantiate_object(&msg);
1094		if (subObj) {
1095			CommandActuator* ca = dynamic_cast < CommandActuator*>(subObj);
1096
1097			if (ca)
1098				fSubActuators.AddItem(ca);
1099			else
1100				delete subObj;
1101		}
1102	}
1103}
1104
1105
1106MultiCommandActuator::MultiCommandActuator(int32 argc, char** argv)
1107	:
1108	CommandActuator(argc, argv)
1109{
1110	for (int i = 1; i < argc; i++) {
1111		CommandActuator* sub = CreateCommandActuator(argv[i]);
1112
1113		if (sub)
1114			fSubActuators.AddItem(sub);
1115		else
1116			printf("Error creating subActuator from [%s]\n", argv[i]);
1117	}
1118}
1119
1120
1121MultiCommandActuator::~MultiCommandActuator()
1122{
1123	int numSubs = fSubActuators.CountItems();
1124	for (int i = 0; i < numSubs; i++)
1125		delete ((CommandActuator*) fSubActuators.ItemAt(i));
1126}
1127
1128
1129status_t
1130MultiCommandActuator::Archive(BMessage* into, bool deep) const
1131{
1132	status_t ret = CommandActuator::Archive(into, deep);
1133	if (ret != B_OK)
1134		return ret;
1135
1136	int numSubs = fSubActuators.CountItems();
1137	for (int i = 0; i < numSubs; i++) {
1138		BMessage msg;
1139		ret = ((CommandActuator*)fSubActuators.ItemAt(i))->Archive(&msg, deep);
1140
1141		if (ret != B_OK)
1142			return ret;
1143
1144		into->AddMessage("subs", &msg);
1145	}
1146	return B_OK;
1147}
1148
1149
1150BArchivable*
1151MultiCommandActuator::Instantiate(BMessage* from)
1152{
1153	if (validate_instantiation(from, "MultiCommandActuator"))
1154		return new MultiCommandActuator(from);
1155	else
1156		return NULL;
1157}
1158
1159
1160filter_result
1161MultiCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
1162	void** asyncData, BMessage* mouseMessage)
1163{
1164	BList* aDataList = NULL; // demand-allocated
1165	filter_result res = B_SKIP_MESSAGE;
1166	int numSubs = fSubActuators.CountItems();
1167	for (int i = 0; i < numSubs; i++) {
1168		void* aData = NULL;
1169		status_t next = ((CommandActuator*)fSubActuators.ItemAt(i))->
1170			KeyEvent(keyMessage, outList, &aData, mouseMessage);
1171
1172		if (next == B_DISPATCH_MESSAGE)
1173			// dispatch message if at least one sub wants it dispatched
1174			res = B_DISPATCH_MESSAGE;
1175
1176		if (aData) {
1177			if (aDataList == NULL)
1178				*asyncData = aDataList = new BList;
1179
1180			while (aDataList->CountItems() < i - 1)
1181				aDataList->AddItem(NULL);
1182			aDataList->AddItem(aData);
1183		}
1184	}
1185	return res;
1186}
1187
1188
1189void
1190MultiCommandActuator::KeyEventAsync(const BMessage* keyUpMsg, void* asyncData)
1191{
1192	BList* list = (BList*) asyncData;
1193	int numSubs = list->CountItems();
1194	for (int i = 0; i < numSubs; i++) {
1195		void* aData = list->ItemAt(i);
1196		if (aData)
1197			((CommandActuator*) fSubActuators.ItemAt(i))->
1198				KeyEventAsync(keyUpMsg, aData);
1199	}
1200	delete list;
1201}
1202
1203
1204//	#pragma mark - MoveMouseCommandActuator
1205
1206
1207MoveMouseCommandActuator::MoveMouseCommandActuator(BMessage* from)
1208	:
1209	CommandActuator(from)
1210{
1211	if (from->FindFloat("xPercent", &fXPercent) != B_OK)
1212		fXPercent = 0.0f;
1213
1214	if (from->FindFloat("yPercent", &fYPercent) != B_OK)
1215		fYPercent = 0.0f;
1216
1217	if (from->FindFloat("xPixels", &fXPixels) != B_OK)
1218		fXPixels = 0;
1219
1220	if (from->FindFloat("yPixels", &fYPixels) != B_OK)
1221		fYPixels = 0;
1222}
1223
1224
1225MoveMouseCommandActuator::MoveMouseCommandActuator(int32 argc, char** argv)
1226	:
1227	CommandActuator(argc, argv),
1228	fXPercent(0.0f),
1229	fYPercent(0.0f),
1230	fXPixels(0),
1231	fYPixels(0)
1232{
1233	if (argc > 1)
1234		_ParseArg(argv[1], fXPercent, fXPixels);
1235
1236	if (argc > 2)
1237		_ParseArg(argv[2], fYPercent, fYPixels);
1238}
1239
1240
1241MoveMouseCommandActuator::~MoveMouseCommandActuator()
1242{
1243}
1244
1245
1246status_t
1247MoveMouseCommandActuator::Archive(BMessage* into, bool deep) const
1248{
1249	status_t ret = CommandActuator::Archive(into, deep);
1250	into->AddFloat("xPercent", fXPercent);
1251	into->AddFloat("yPercent", fYPercent);
1252	into->AddFloat("xPixels", fXPixels);
1253	into->AddFloat("yPixels", fYPixels);
1254	return ret;
1255}
1256
1257
1258void
1259MoveMouseCommandActuator::CalculateCoords(float& setX, float& setY) const
1260{
1261	BScreen s;
1262	BRect frame = s.Frame();
1263	setX = (frame.Width() * fXPercent) + fXPixels;
1264	setY = (frame.Height() * fYPercent) + fYPixels;
1265}
1266
1267
1268BMessage*
1269MoveMouseCommandActuator::CreateMouseMessage(const BMessage* original,
1270	BPoint where, BList* outList) const
1271{
1272	// Force where into the screen space
1273	{
1274		BScreen screen;
1275		where.ConstrainTo(screen.Frame());
1276	}
1277
1278	BMessage* newMessage = new BMessage(B_MOUSE_MOVED);
1279
1280	newMessage->AddPoint("where", where);
1281
1282	int32 buttons = 0;
1283	(void)original->FindInt32("buttons", &buttons);
1284
1285	if (buttons == 0)
1286		buttons = 1;
1287
1288	newMessage->AddInt32("buttons", buttons);
1289
1290	// Trey sez you gotta keep then "when"'s increasing if you want click&drag
1291	// to work!
1292	const BMessage* lastMessage;
1293	int32 last = outList->CountItems() - 1;
1294
1295	if (outList->CountItems() > 0)
1296		lastMessage = (const BMessage*)outList->ItemAt(last);
1297	else
1298		lastMessage = original;
1299
1300	int64 when;
1301
1302	if (lastMessage->FindInt64("when", &when) == B_OK) {
1303		when++;
1304		newMessage->RemoveName("when");
1305		newMessage->AddInt64("when", when);
1306	}
1307	return newMessage;
1308}
1309
1310
1311static bool IsNumeric(char c);
1312static bool IsNumeric(char c)
1313{
1314	return (((c >= '0') && (c <= '9')) || (c == '.') || (c == '-'));
1315}
1316
1317
1318// Parse a string of the form "10", "10%", "10+ 10%", or "10%+ 10"
1319void
1320MoveMouseCommandActuator::_ParseArg(const char* arg, float& setPercent,
1321	float& setPixels) const
1322{
1323	char* temp = new char[strlen(arg) + 1];
1324	strcpy(temp, arg);
1325
1326	// Find the percent part, if any
1327	char* percent = strchr(temp, '%');
1328	if (percent) {
1329		// Rewind to one before the beginning of the number
1330		char* beginNum = percent - 1;
1331		while (beginNum >= temp) {
1332			char c = *beginNum;
1333			if (IsNumeric(c))
1334				beginNum--;
1335			else
1336				break;
1337		}
1338
1339		// parse the number
1340		setPercent = atof(++beginNum)/100.0f;
1341
1342		// Now white it out to ease finding the other #
1343		while (beginNum <= percent)
1344			*(beginNum++) = ' ';
1345	}
1346
1347	// Find the pixel part, if any
1348	char* pixel = temp;
1349	while (!IsNumeric(*pixel)) {
1350		if (*pixel == '\0')
1351			break;
1352		pixel++;
1353	}
1354	setPixels = atof(pixel);
1355 delete [] temp;
1356}
1357
1358
1359//	#pragma mark - MoveMouseToCommandActuator
1360
1361
1362MoveMouseToCommandActuator::MoveMouseToCommandActuator(BMessage* from)
1363	:
1364	MoveMouseCommandActuator(from)
1365{
1366}
1367
1368
1369MoveMouseToCommandActuator::MoveMouseToCommandActuator(int32 argc, char** argv)
1370	:
1371	MoveMouseCommandActuator(argc, argv)
1372{
1373}
1374
1375
1376MoveMouseToCommandActuator::~MoveMouseToCommandActuator()
1377{
1378}
1379
1380
1381status_t
1382MoveMouseToCommandActuator::Archive(BMessage* into, bool deep) const
1383{
1384	return MoveMouseCommandActuator::Archive(into, deep);
1385}
1386
1387
1388BArchivable*
1389MoveMouseToCommandActuator::Instantiate(BMessage* from)
1390{
1391	if (validate_instantiation(from, "MoveMouseToCommandActuator"))
1392		return new MoveMouseToCommandActuator(from);
1393	else
1394		return NULL;
1395}
1396
1397
1398filter_result
1399MoveMouseToCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
1400	void** setAsyncData, BMessage* mouseMessage)
1401{
1402	if (IS_KEY_DOWN(keyMessage)) {
1403		float x, y;
1404		CalculateCoords(x, y);
1405		BPoint where(x, y);
1406		BMessage* newMessage = CreateMouseMessage(keyMessage, where, outList);
1407		*mouseMessage = *newMessage;
1408		outList->AddItem(newMessage);
1409
1410		return B_DISPATCH_MESSAGE;
1411	}
1412
1413	return B_SKIP_MESSAGE;
1414}
1415
1416
1417//	#pragma mark - MoveMouseByCommandActuator
1418
1419
1420MoveMouseByCommandActuator::MoveMouseByCommandActuator(BMessage* from)
1421	:
1422	MoveMouseCommandActuator(from)
1423{
1424}
1425
1426
1427MoveMouseByCommandActuator::MoveMouseByCommandActuator(int32 argc, char** argv)
1428	:
1429	MoveMouseCommandActuator(argc, argv)
1430{
1431}
1432
1433
1434MoveMouseByCommandActuator::~MoveMouseByCommandActuator()
1435{
1436}
1437
1438
1439status_t MoveMouseByCommandActuator::Archive(BMessage* into, bool deep) const
1440{
1441 status_t ret = MoveMouseCommandActuator::Archive(into, deep);
1442 return ret;
1443}
1444
1445
1446BArchivable*
1447MoveMouseByCommandActuator::Instantiate(BMessage* from)
1448{
1449	if (validate_instantiation(from, "MoveMouseByCommandActuator"))
1450		return new MoveMouseByCommandActuator(from);
1451	else
1452		return NULL;
1453}
1454
1455
1456filter_result
1457MoveMouseByCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
1458	void** setAsyncData, BMessage* mouseMessage)
1459{
1460	if (IS_KEY_DOWN(keyMessage)) {
1461		// Get the current mouse position
1462		BPoint where;
1463		if (mouseMessage->FindPoint("where", &where) == B_OK) {
1464			// Get the desired offset
1465			BPoint diff;
1466			CalculateCoords(diff.x, diff.y);
1467			where += diff;
1468			BMessage* newMessage = CreateMouseMessage(keyMessage, where, outList);
1469			*mouseMessage = *newMessage;
1470			outList->AddItem(newMessage);
1471			return B_DISPATCH_MESSAGE;
1472		}
1473	}
1474	return B_SKIP_MESSAGE;
1475}
1476
1477
1478//	#pragma mark - SendMessageCommandActuator
1479
1480
1481SendMessageCommandActuator::SendMessageCommandActuator(int32 argc, char** argv)
1482	:
1483	CommandActuator(argc, argv),
1484	fSignature((argc > 1) ? argv[1] : "")
1485{
1486	// Parse the what code. It may be in any of the following formats:
1487	// 12356 (int)
1488	// 'HELO' (chars enclosed in single quotes)
1489	// 0x12ab3c (hex)
1490
1491	if (argc > 2) {
1492		const char* whatStr = argv[2];
1493		if ((whatStr[0] == '\'')
1494			&& (strlen(whatStr) == 6)
1495			&& (whatStr[5] == '\'')) {
1496			// Translate the characters into the uint32 they stand for.
1497			// Note that we must do this in a byte-endian-independant fashion
1498			// (no casting!)
1499			fSendMessage.what = 0;
1500			uint32 mult = 1;
1501			for (int i = 0; i < 4; i++) {
1502				fSendMessage.what += ((uint32)(whatStr[4 - i]))* mult;
1503				mult <<= 8;
1504			}
1505		} else if (strncmp(whatStr, "0x", 2) == 0)
1506			// translate hex string to decimal
1507			fSendMessage.what = strtoul(&whatStr[2], NULL, 16);
1508		else
1509			fSendMessage.what = atoi(whatStr);
1510	} else
1511		fSendMessage.what = 0;
1512
1513	for (int i = 3; i < argc; i++) {
1514		type_code tc = B_BOOL_TYPE;// default type when no value is present
1515		const char* arg = argv[i];
1516		BString argString(arg);
1517		const char* equals = strchr(arg, '=');
1518		const char* value = "true";// default if no value is present
1519
1520		if (equals) {
1521			tc = B_STRING_TYPE;// default type when value is present
1522			value = equals + 1;
1523			const char* colon = strchr(arg, ':');
1524			if (colon > equals)
1525				colon = NULL;// colons after the equals sign don't count
1526
1527			if (colon) {
1528				const char* typeStr = colon + 1;
1529				if (strncasecmp(typeStr, "string", 6) == 0)
1530					tc = B_STRING_TYPE;
1531				else if (strncasecmp(typeStr, "int8", 4) == 0)
1532					tc = B_INT8_TYPE;
1533				else if (strncasecmp(typeStr, "int16", 5) == 0)
1534					tc = B_INT16_TYPE;
1535				else if (strncasecmp(typeStr, "int32", 5) == 0)
1536					tc = B_INT32_TYPE;
1537				else if (strncasecmp(typeStr, "int64", 5) == 0)
1538					tc = B_INT64_TYPE;
1539				else if (strncasecmp(typeStr, "bool", 4) == 0)
1540					tc = B_BOOL_TYPE;
1541				else if (strncasecmp(typeStr, "float", 5) == 0)
1542					tc = B_FLOAT_TYPE;
1543				else if (strncasecmp(typeStr, "double", 6) == 0)
1544					tc = B_DOUBLE_TYPE;
1545				else if (strncasecmp(typeStr, "point", 5) == 0)
1546					tc = B_POINT_TYPE;
1547				else if (strncasecmp(typeStr, "rect", 4) == 0)
1548					tc = B_RECT_TYPE;
1549
1550				// remove the colon and stuff
1551				argString = argString.Truncate(colon - arg);
1552			} else
1553				// remove the equals and arg
1554				argString = argString.Truncate(equals - arg);
1555		}
1556
1557		switch(tc) {
1558			case B_STRING_TYPE:
1559				fSendMessage.AddString(argString.String(), value);
1560				break;
1561
1562			case B_INT8_TYPE:
1563				fSendMessage.AddInt8(argString.String(), (int8)atoi(value));
1564				break;
1565
1566			case B_INT16_TYPE:
1567				fSendMessage.AddInt16(argString.String(), (int16)atoi(value));
1568				break;
1569
1570			case B_INT32_TYPE:
1571				fSendMessage.AddInt32(argString.String(), (int32)atoi(value));
1572				break;
1573
1574			case B_INT64_TYPE:
1575				fSendMessage.AddInt64(argString.String(), (int64)atoi(value));
1576				break;
1577
1578			case B_BOOL_TYPE:
1579				fSendMessage.AddBool(argString.String(), ((value[0] == 't')
1580					|| (value[0] == 'T')));
1581				break;
1582
1583			case B_FLOAT_TYPE:
1584				fSendMessage.AddFloat(argString.String(), atof(value));
1585				break;
1586
1587			case B_DOUBLE_TYPE:
1588				fSendMessage.AddDouble(argString.String(), (double)atof(value));
1589				break;
1590
1591			case B_POINT_TYPE:
1592			{
1593				float pts[2] = {0.0f, 0.0f};
1594				_ParseFloatArgs(pts, 2, value);
1595				fSendMessage.AddPoint(argString.String(), BPoint(pts[0], pts[1]));
1596				break;
1597			}
1598
1599			case B_RECT_TYPE:
1600			{
1601				float pts[4] = {0.0f, 0.0f, 0.0f, 0.0f};
1602				_ParseFloatArgs(pts, 4, value);
1603				fSendMessage.AddRect(argString.String(),
1604					BRect(pts[0], pts[1], pts[2], pts[3]));
1605				break;
1606			}
1607		}
1608	}
1609}
1610
1611
1612void
1613SendMessageCommandActuator::_ParseFloatArgs(float* args, int maxArgs,
1614	const char* str) const
1615{
1616	const char* next = str;
1617	for (int i = 0; i < maxArgs; i++) {
1618		args[i] = atof(next);
1619		next = strchr(next, ',');
1620		if (next) next++;
1621		else break;
1622	}
1623}
1624
1625
1626SendMessageCommandActuator::SendMessageCommandActuator(BMessage* from)
1627	:
1628	CommandActuator(from)
1629{
1630	const char* temp;
1631
1632	if (from->FindString("signature", 0, &temp) == B_OK)
1633		fSignature = temp;
1634
1635	(void) from->FindMessage("sendmsg", &fSendMessage);
1636}
1637
1638
1639SendMessageCommandActuator::~SendMessageCommandActuator()
1640{
1641}
1642
1643
1644status_t
1645SendMessageCommandActuator::Archive(BMessage* into, bool deep) const
1646{
1647	status_t ret = CommandActuator::Archive(into, deep);
1648	into->AddString("signature", fSignature.String());
1649	into->AddMessage("sendmsg", &fSendMessage);
1650	return ret;
1651}
1652
1653
1654filter_result
1655SendMessageCommandActuator::KeyEvent(const BMessage* keyMessage, BList* outList,
1656	void** setAsyncData, BMessage* mouseMessage)
1657{
1658	if (IS_KEY_DOWN(keyMessage))
1659		// cause KeyEventAsync() to be called asynchronously
1660		*setAsyncData = (void*) true;
1661
1662	return B_SKIP_MESSAGE;
1663}
1664
1665
1666void
1667SendMessageCommandActuator::KeyEventAsync(const BMessage* keyMessage,
1668	void* asyncData)
1669{
1670	if (be_roster == NULL)
1671		return;
1672
1673	BString string;
1674	if (fSignature.Length() == 0) {
1675		string << "SendMessage: Target application signature not specified";
1676		BAlert* alert = new BAlert("Shortcuts SendMessage error",
1677			string.String(), "OK");
1678		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1679		alert->Go(NULL);
1680	} else {
1681		status_t result = B_OK;
1682		BMessenger messenger(fSignature.String(), -1, &result);
1683		if (result == B_OK)
1684			messenger.SendMessage(&fSendMessage);
1685	}
1686}
1687
1688
1689BArchivable*
1690SendMessageCommandActuator::Instantiate(BMessage* from)
1691{
1692	if (validate_instantiation(from, "SendMessageCommandActuator"))
1693		return new SendMessageCommandActuator(from);
1694	else
1695		return NULL;
1696}
1697