1/*
2 * Copyright 2004-2009, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT license.
4 *
5 * Authors:
6 *		J��r��me Duval
7 */
8
9
10#include "MethodReplicant.h"
11
12#include <new>
13#include <string.h>
14
15#include <Alert.h>
16#include <AppDefs.h>
17#include <Debug.h>
18#include <Dragger.h>
19#include <Bitmap.h>
20#include <MenuItem.h>
21#include <Message.h>
22#include <Messenger.h>
23#include <PopUpMenu.h>
24
25#include "remote_icon.h"
26
27#include "InputServerTypes.h"
28
29#ifdef DEBUG
30#	define CALLED() PRINT(("CALLED %s \n", __PRETTY_FUNCTION__));
31#else
32#	define CALLED()
33#endif
34
35
36MethodReplicant::MethodReplicant(const char* signature)
37	:
38	BView(BRect(0, 0, 15, 15), REPLICANT_CTL_NAME, B_FOLLOW_ALL, B_WILL_DRAW),
39	fMenu("", false, false)
40{
41	// Background Bitmap
42	fSegments = new BBitmap(BRect(0, 0, kRemoteWidth - 1, kRemoteHeight - 1),
43		kRemoteColorSpace);
44	fSegments->SetBits(kRemoteBits, kRemoteWidth * kRemoteHeight, 0,
45		kRemoteColorSpace);
46
47	ASSERT(signature != NULL);
48	fSignature = strdup(signature);
49
50	fMenu.SetFont(be_plain_font);
51	fMenu.SetRadioMode(true);
52}
53
54
55MethodReplicant::MethodReplicant(BMessage* message)
56	:
57	BView(message),
58	fMenu("", false, false)
59{
60	// Background Bitmap
61	fSegments = new BBitmap(BRect(0, 0, kRemoteWidth - 1, kRemoteHeight - 1),
62		kRemoteColorSpace);
63	fSegments->SetBits(kRemoteBits, kRemoteWidth * kRemoteHeight, 0,
64		kRemoteColorSpace);
65
66	const char* signature = NULL;
67	message->FindString("add_on", &signature);
68	ASSERT(signature != NULL);
69	fSignature = strdup(signature);
70
71	fMenu.SetFont(be_plain_font);
72	fMenu.SetRadioMode(true);
73}
74
75
76MethodReplicant::~MethodReplicant()
77{
78	delete fSegments;
79	free(fSignature);
80}
81
82
83// archiving overrides
84MethodReplicant*
85MethodReplicant::Instantiate(BMessage* data)
86{
87	CALLED();
88	if (!validate_instantiation(data, REPLICANT_CTL_NAME))
89		return NULL;
90	return new(std::nothrow) MethodReplicant(data);
91}
92
93
94status_t
95MethodReplicant::Archive(BMessage* data, bool deep) const
96{
97	BView::Archive(data, deep);
98
99	data->AddString("add_on", fSignature);
100	return B_NO_ERROR;
101}
102
103
104void
105MethodReplicant::AttachedToWindow()
106{
107	CALLED();
108
109	SetViewColor(Parent()->ViewColor());
110
111	BMessenger messenger(this);
112	BMessage msg(IS_METHOD_REGISTER);
113	msg.AddMessenger("address", messenger);
114
115	BMessenger inputMessenger(fSignature);
116	if (inputMessenger.SendMessage(&msg) != B_OK)
117		printf("error when contacting input_server\n");
118}
119
120
121void
122MethodReplicant::MessageReceived(BMessage* message)
123{
124	PRINT(("%s what:%c%c%c%c\n", __PRETTY_FUNCTION__, (char)(message->what >> 24),
125		(char)(message->what >> 16), (char)(message->what >> 8), (char)message->what));
126	PRINT_OBJECT(*message);
127
128	switch (message->what) {
129		case B_ABOUT_REQUESTED:
130		{
131			BAlert* alert = new BAlert("About Method Replicant",
132				"Method Replicant (Replicant)\n"
133				"  Brought to you by J��r��me DUVAL.\n\n"
134				"Haiku, 2004-2009", "OK");
135			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
136			alert->Go();
137			break;
138		}
139		case IS_UPDATE_NAME:
140			UpdateMethodName(message);
141			break;
142		case IS_UPDATE_ICON:
143			UpdateMethodIcon(message);
144			break;
145		case IS_UPDATE_MENU:
146			UpdateMethodMenu(message);
147			break;
148		case IS_UPDATE_METHOD:
149			UpdateMethod(message);
150			break;
151		case IS_ADD_METHOD:
152			AddMethod(message);
153			break;
154		case IS_REMOVE_METHOD:
155			RemoveMethod(message);
156			break;
157
158		default:
159			BView::MessageReceived(message);
160			break;
161	}
162}
163
164
165void
166MethodReplicant::Draw(BRect rect)
167{
168	BView::Draw(rect);
169
170	SetDrawingMode(B_OP_OVER);
171	DrawBitmap(fSegments);
172}
173
174
175void
176MethodReplicant::MouseDown(BPoint point)
177{
178	CALLED();
179	uint32 mouseButtons;
180	BPoint where;
181	GetMouse(&where, &mouseButtons, true);
182
183	where = ConvertToScreen(point);
184
185	fMenu.SetTargetForItems(this);
186	BMenuItem* item = fMenu.Go(where, true, true,
187		BRect(where - BPoint(4, 4), where + BPoint(4, 4)));
188
189	if (dynamic_cast<MethodMenuItem*>(item) != NULL) {
190		BMessage msg(IS_SET_METHOD);
191		msg.AddInt32("cookie", ((MethodMenuItem*)item)->Cookie());
192		BMessenger messenger(fSignature);
193		messenger.SendMessage(&msg);
194	}
195}
196
197
198void
199MethodReplicant::MouseUp(BPoint point)
200{
201	/* don't Quit() ! thanks for FFM users */
202}
203
204
205void
206MethodReplicant::UpdateMethod(BMessage* message)
207{
208	CALLED();
209	int32 cookie;
210	if (message->FindInt32("cookie", &cookie) != B_OK) {
211		fprintf(stderr, "can't find cookie in message\n");
212		return;
213	}
214
215	MethodMenuItem* item = FindItemByCookie(cookie);
216	if (item == NULL) {
217		fprintf(stderr, "can't find item with cookie %" B_PRIx32 "\n", cookie);
218		return;
219	}
220	item->SetMarked(true);
221
222	fSegments->SetBits(item->Icon(), kRemoteWidth * kRemoteHeight, 0,
223		kRemoteColorSpace);
224	Invalidate();
225}
226
227
228void
229MethodReplicant::UpdateMethodIcon(BMessage* message)
230{
231	CALLED();
232	int32 cookie;
233	if (message->FindInt32("cookie", &cookie) != B_OK) {
234		fprintf(stderr, "can't find cookie in message\n");
235		return;
236	}
237
238	const uchar* data;
239	ssize_t numBytes;
240	if (message->FindData("icon", B_ANY_TYPE, (const void**)&data, &numBytes)
241			!= B_OK) {
242		fprintf(stderr, "can't find icon in message\n");
243		return;
244	}
245
246	MethodMenuItem* item = FindItemByCookie(cookie);
247	if (item == NULL) {
248		fprintf(stderr, "can't find item with cookie %" B_PRIx32 "\n", cookie);
249		return;
250	}
251
252	item->SetIcon(data);
253}
254
255
256void
257MethodReplicant::UpdateMethodMenu(BMessage* message)
258{
259	CALLED();
260	int32 cookie;
261	if (message->FindInt32("cookie", &cookie) != B_OK) {
262		fprintf(stderr, "can't find cookie in message\n");
263		return;
264	}
265
266	BMessage msg;
267	if (message->FindMessage("menu", &msg) != B_OK) {
268		fprintf(stderr, "can't find menu in message\n");
269		return;
270	}
271	PRINT_OBJECT(msg);
272
273	BMessenger messenger;
274	if (message->FindMessenger("target", &messenger) != B_OK) {
275		fprintf(stderr, "can't find target in message\n");
276		return;
277	}
278
279	BMenu* menu = (BMenu*)BMenu::Instantiate(&msg);
280	if (menu == NULL) {
281		PRINT(("can't instantiate menu\n"));
282	} else
283		menu->SetTargetForItems(messenger);
284
285	MethodMenuItem* item = FindItemByCookie(cookie);
286	if (item == NULL) {
287		fprintf(stderr, "can't find item with cookie %" B_PRIx32 "\n", cookie);
288		return;
289	}
290	int32 index = fMenu.IndexOf(item);
291
292	MethodMenuItem* item2 = NULL;
293	if (menu) {
294		item2 = new MethodMenuItem(cookie, item->Label(), item->Icon(),
295			menu, messenger);
296	} else
297		item2 = new MethodMenuItem(cookie, item->Label(), item->Icon());
298
299	item = (MethodMenuItem*)fMenu.RemoveItem(index);
300	fMenu.AddItem(item2, index);
301	item2->SetMarked(item->IsMarked());
302	delete item;
303}
304
305
306void
307MethodReplicant::UpdateMethodName(BMessage* message)
308{
309	CALLED();
310	int32 cookie;
311	if (message->FindInt32("cookie", &cookie) != B_OK) {
312		fprintf(stderr, "can't find cookie in message\n");
313		return;
314	}
315
316	const char* name;
317	if (message->FindString("name", &name) != B_OK) {
318		fprintf(stderr, "can't find name in message\n");
319		return;
320	}
321
322	MethodMenuItem* item = FindItemByCookie(cookie);
323	if (item == NULL) {
324		fprintf(stderr, "can't find item with cookie %" B_PRIx32 "\n", cookie);
325		return;
326	}
327
328	item->SetName(name);
329}
330
331
332MethodMenuItem*
333MethodReplicant::FindItemByCookie(int32 cookie)
334{
335	for (int32 i = 0; i < fMenu.CountItems(); i++) {
336		MethodMenuItem* item = (MethodMenuItem*)fMenu.ItemAt(i);
337		PRINT(("cookie : 0x%" B_PRIx32 "\n", item->Cookie()));
338		if (item->Cookie() == cookie)
339			return item;
340	}
341
342	return NULL;
343}
344
345
346void
347MethodReplicant::AddMethod(BMessage* message)
348{
349	CALLED();
350	int32 cookie;
351	if (message->FindInt32("cookie", &cookie) != B_OK) {
352		fprintf(stderr, "can't find cookie in message\n");
353		return;
354	}
355
356	const char* name;
357	if (message->FindString("name", &name) != B_OK) {
358		fprintf(stderr, "can't find name in message\n");
359		return;
360	}
361
362	const uchar* icon;
363	ssize_t numBytes;
364	if (message->FindData("icon", B_ANY_TYPE, (const void**)&icon, &numBytes)
365			!= B_OK) {
366		fprintf(stderr, "can't find icon in message\n");
367		return;
368	}
369
370	BMessage menuMsg;
371	if (message->FindMessage("menu", &menuMsg) != B_OK) {
372		fprintf(stderr, "can't find menu in message\n");
373		return;
374	}
375	PRINT_OBJECT(menuMsg);
376
377	BMessenger messenger;
378	if (message->FindMessenger("target", &messenger) != B_OK) {
379		fprintf(stderr, "can't find target in message\n");
380		return;
381	}
382
383	BMenu* menu = static_cast<BMenu*>(BMenu::Instantiate(&menuMsg));
384	if (menu == NULL) {
385		PRINT(("can't instantiate menu\n"));
386	} else
387		menu->SetTargetForItems(messenger);
388
389	MethodMenuItem* item = FindItemByCookie(cookie);
390	if (item != NULL) {
391		fprintf(stderr, "item with cookie %" B_PRIx32 " already exists\n", cookie);
392		return;
393	}
394
395	if (menu != NULL) {
396		item = new MethodMenuItem(cookie, name, icon, menu, messenger);
397	} else
398		item = new MethodMenuItem(cookie, name, icon);
399	fMenu.AddItem(item);
400	item->SetTarget(this);
401
402	if (fMenu.CountItems() == 1)
403		item->SetMarked(true);
404}
405
406
407void
408MethodReplicant::RemoveMethod(BMessage* message)
409{
410	CALLED();
411	int32 cookie;
412	if (message->FindInt32("cookie", &cookie) != B_OK) {
413		fprintf(stderr, "can't find cookie in message\n");
414		return;
415	}
416
417	MethodMenuItem* item = FindItemByCookie(cookie);
418	if (item == NULL) {
419		fprintf(stderr, "can't find item with cookie %" B_PRIx32 "\n", cookie);
420		return;
421	}
422	fMenu.RemoveItem(item);
423	delete item;
424}
425