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