xref: /haiku/src/apps/autoraise/AutoRaiseIcon.cpp (revision 1f0635d2277dcd0818dc7f539c1cb1b296f6444b)
1 #include <BeBuild.h>
2 
3 #include "AutoRaiseIcon.h"
4 #include <stdlib.h>
5 #include <DataIO.h>
6 #include <Screen.h>
7 #include <View.h>
8 #include <Debug.h>
9 
10 
11 extern "C" _EXPORT BView *instantiate_deskbar_item(void)
12 {
13 	puts("Instanciating AutoRaise TrayView...");
14 	return (new TrayView);
15 }
16 
17 
18 status_t removeFromDeskbar(void *)
19 {
20 	BDeskbar db;
21 	if (db.RemoveItem(APP_NAME) != B_OK) {
22 		printf("Unable to remove AutoRaise from BDeskbar\n");
23 	}
24 
25 	return B_OK;
26 }
27 
28 //**************************************************
29 
30 ConfigMenu::ConfigMenu(TrayView *tv, bool useMag)
31 	:BPopUpMenu("config_popup", false, false){
32 
33 	BMenu *tmpm;
34 	BMenuItem *tmpi;
35 	BMessage *msg;
36 
37 	AutoRaiseSettings *s = tv->Settings();
38 
39 	SetFont(be_plain_font);
40 
41 
42 	BMenuItem *active = new BMenuItem("Active", new BMessage(MSG_TOGGLE_ACTIVE));
43 	active->SetMarked(s->Active());
44 	AddItem(active);
45 
46 	tmpm = new BMenu("Mode");
47 	tmpm->SetFont(be_plain_font);
48 
49 	msg = new BMessage(MSG_SET_MODE);
50 	msg->AddInt32(AR_MODE, Mode_All);
51 	tmpi = new BMenuItem("Default (all windows)", msg);
52 	tmpi->SetMarked(s->Mode() == Mode_All);
53 	tmpm->AddItem(tmpi);
54 
55 	msg = new BMessage(MSG_SET_MODE);
56 	msg->AddInt32(AR_MODE, Mode_DeskbarOver);
57 	tmpi = new BMenuItem("Deskbar only (over its area)", msg);
58 	tmpi->SetMarked(s->Mode() == Mode_DeskbarOver);
59 	tmpm->AddItem(tmpi);
60 
61 	msg = new BMessage(MSG_SET_MODE);
62 	msg->AddInt32(AR_MODE, Mode_DeskbarTouch);
63 	tmpi = new BMenuItem("Deskbar only (touch)", msg);
64 	tmpi->SetMarked(s->Mode() == Mode_DeskbarTouch);
65 	tmpm->AddItem(tmpi);
66 
67 
68 	tmpm->SetTargetForItems(tv);
69 	BMenuItem *modem = new BMenuItem(tmpm);
70 	modem->SetEnabled(s->Active());
71 	AddItem(modem);
72 
73 	tmpm = new BMenu("Inactive behaviour");
74 	tmpm->SetFont(be_plain_font);
75 
76 	msg = new BMessage(MSG_SET_BEHAVIOUR);
77 	msg->AddInt32(AR_BEHAVIOUR, B_NORMAL_MOUSE);
78 	tmpi = new BMenuItem("Normal", msg);
79 	tmpi->SetMarked(tv->fNormalMM == B_NORMAL_MOUSE);
80 	tmpm->AddItem(tmpi);
81 
82 	msg = new BMessage(MSG_SET_BEHAVIOUR);
83 	msg->AddInt32(AR_BEHAVIOUR, B_FOCUS_FOLLOWS_MOUSE);
84 	tmpi = new BMenuItem("Focus follows mouse", msg);
85 	tmpi->SetMarked(tv->fNormalMM == B_FOCUS_FOLLOWS_MOUSE);
86 	tmpm->AddItem(tmpi);
87 
88 	msg = new BMessage(MSG_SET_BEHAVIOUR);
89 	msg->AddInt32(AR_BEHAVIOUR, B_WARP_FOCUS_FOLLOWS_MOUSE);
90 	tmpi = new BMenuItem("Warping (ffm)", msg);
91 	tmpi->SetMarked(tv->fNormalMM == (mode_mouse)B_WARP_FOCUS_FOLLOWS_MOUSE);
92 	tmpm->AddItem(tmpi);
93 
94 	msg = new BMessage(MSG_SET_BEHAVIOUR);
95 	msg->AddInt32(AR_BEHAVIOUR, B_INSTANT_WARP_FOCUS_FOLLOWS_MOUSE);
96 	tmpi = new BMenuItem("Instant warping (ffm)", msg);
97 	tmpi->SetMarked(tv->fNormalMM == (mode_mouse)B_INSTANT_WARP_FOCUS_FOLLOWS_MOUSE);
98 	tmpm->AddItem(tmpi);
99 
100 	tmpm->SetTargetForItems(tv);
101 	BMenuItem *behavm = new BMenuItem(tmpm);
102 	AddItem(behavm);
103 
104 
105 	tmpm = new BMenu("Delay");
106 	tmpm->SetFont(be_plain_font);
107 
108 	msg = new BMessage(MSG_SET_DELAY);
109 	msg->AddInt64(AR_DELAY, 100000LL);
110 	tmpi = new BMenuItem("0.1 s", msg);
111 	tmpi->SetMarked(tv->raise_delay == 100000LL);
112 	tmpm->AddItem(tmpi);
113 
114 	msg = new BMessage(MSG_SET_DELAY);
115 	msg->AddInt64(AR_DELAY, 200000LL);
116 	tmpi = new BMenuItem("0.2 s", msg);
117 	tmpi->SetMarked(tv->raise_delay == 200000LL);
118 	tmpm->AddItem(tmpi);
119 
120 	msg = new BMessage(MSG_SET_DELAY);
121 	msg->AddInt64(AR_DELAY, 500000LL);
122 	tmpi = new BMenuItem("0.5 s", msg);
123 	tmpi->SetMarked(tv->raise_delay == 500000LL);
124 	tmpm->AddItem(tmpi);
125 
126 	msg = new BMessage(MSG_SET_DELAY);
127 	msg->AddInt64(AR_DELAY, 1000000LL);
128 	tmpi = new BMenuItem("1 s", msg);
129 	tmpi->SetMarked(tv->raise_delay == 1000000LL);
130 	tmpm->AddItem(tmpi);
131 
132 	msg = new BMessage(MSG_SET_DELAY);
133 	msg->AddInt64(AR_DELAY, 2000000LL);
134 	tmpi = new BMenuItem("2 s", msg);
135 	tmpi->SetMarked(tv->raise_delay == 2000000LL);
136 	tmpm->AddItem(tmpi);
137 
138 	msg = new BMessage(MSG_SET_DELAY);
139 	msg->AddInt64(AR_DELAY, 3000000LL);
140 	tmpi = new BMenuItem("3 s", msg);
141 	tmpi->SetMarked(tv->raise_delay == 3000000LL);
142 	tmpm->AddItem(tmpi);
143 
144 	msg = new BMessage(MSG_SET_DELAY);
145 	msg->AddInt64(AR_DELAY, 4000000LL);
146 	tmpi = new BMenuItem("4 s", msg);
147 	tmpi->SetMarked(tv->raise_delay == 4000000LL);
148 	tmpm->AddItem(tmpi);
149 
150 	msg = new BMessage(MSG_SET_DELAY);
151 	msg->AddInt64(AR_DELAY, 5000000LL);
152 	tmpi = new BMenuItem("5 s", msg);
153 	tmpi->SetMarked(tv->raise_delay == 5000000LL);
154 	tmpm->AddItem(tmpi);
155 
156 	tmpm->SetTargetForItems(tv);
157 	BMenuItem *delaym = new BMenuItem(tmpm);
158 	delaym->SetEnabled(s->Active());
159 
160 	AddItem(delaym);
161 
162 	AddSeparatorItem();
163 //	AddItem(new BMenuItem("Settings...", new BMessage(OPEN_SETTINGS)));
164 
165 	AddItem(new BMenuItem("About " APP_NAME B_UTF8_ELLIPSIS,
166 		new BMessage(B_ABOUT_REQUESTED)));
167 	AddItem(new BMenuItem("Remove from tray", new BMessage(REMOVE_FROM_TRAY)));
168 
169 	SetTargetForItems(tv);
170 	SetAsyncAutoDestruct(true);
171 }
172 
173 ConfigMenu::~ConfigMenu() {}
174 
175 //************************************************
176 
177 TrayView::TrayView()
178 	:BView(BRect(0, 0, B_MINI_ICON, B_MINI_ICON -1), "AutoRaise", B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW){
179 	_init(); 	//Initialization common to both constructors
180 }
181 
182 //Rehydratable constructor
183 TrayView::TrayView(BMessage *mdArchive):BView(mdArchive){
184 	_init();		//As above
185 }
186 
187 void TrayView::GetPreferredSize(float *w, float *h)
188 {
189 	*w = B_MINI_ICON;
190 	*h = B_MINI_ICON - 1;
191 }
192 
193 void TrayView::_init()
194 {
195 	thread_info ti;
196 	status_t err;
197 
198 	watching = false;
199 	_settings = new AutoRaiseSettings;
200 
201 	_appPath = _settings->AppPath();
202 
203 	raise_delay = _settings->Delay();
204 	current_window = 0;
205 	polling_delay = 100000;
206 	fPollerSem = create_sem(0, "AutoRaise poller sync");
207 	last_raiser_thread = 0;
208 	fNormalMM = mouse_mode();
209 
210 	_activeIcon = NULL;
211 	_inactiveIcon = NULL;
212 
213 	get_thread_info(find_thread(NULL), &ti);
214 	fDeskbarTeam = ti.team;
215 
216 	resume_thread(poller_thread = spawn_thread(poller, "AutoRaise desktop "
217 		"poller", B_NORMAL_PRIORITY, (void *)this));
218 
219 	//determine paths to icon files based on app path in settings file
220 
221 	BResources res;
222 	BFile theapp(&_appPath, B_READ_ONLY);
223 	if ((err = res.SetTo(&theapp)) != B_OK) {
224 
225 		printf("Unable to find the app to get the resources !!!\n");
226 //		removeFromDeskbar(NULL);
227 //		delete _settings;
228 //		return;
229 	}
230 
231 	size_t bmsz;
232 	char *p;
233 
234 	p = (char *)res.LoadResource('MICN', ACTIVE_ICON, &bmsz);
235 	_activeIcon = new BBitmap(BRect(0, 0, B_MINI_ICON-1, B_MINI_ICON -1),
236 		B_CMAP8);
237 	if (!p)
238 		puts("ERROR loading active icon");
239 	else
240 		_activeIcon->SetBits(p, B_MINI_ICON*B_MINI_ICON, 0, B_CMAP8);
241 
242 	p = (char *)res.LoadResource('MICN', INACTIVE_ICON, &bmsz);
243 	_inactiveIcon = new BBitmap(BRect(0, 0, B_MINI_ICON-1, B_MINI_ICON -1),
244 		B_CMAP8);
245 	if (!p)
246 		puts("ERROR loading inactive icon");
247 	else
248 		_inactiveIcon->SetBits(p, B_MINI_ICON*B_MINI_ICON, 0, B_CMAP8);
249 
250 
251 	SetDrawingMode(B_OP_ALPHA);
252 	SetFlags(Flags() | B_WILL_DRAW);
253 
254 	// begin watching if we want
255 	// (doesn't work here, better do it in AttachedToWindow())
256 }
257 
258 TrayView::~TrayView(){
259 	status_t ret;
260 
261 	if (watching) {
262 		set_mouse_mode(fNormalMM);
263 		watching = false;
264 	}
265 	delete_sem(fPollerSem);
266 	wait_for_thread(poller_thread, &ret);
267 	if (_activeIcon) delete _activeIcon;
268 	if (_inactiveIcon) delete _inactiveIcon;
269 	if (_settings) delete _settings;
270 
271 	return;
272 }
273 
274 //Dehydrate into a message (called by the DeskBar)
275 status_t TrayView::Archive(BMessage *data, bool deep) const {
276 //	BEntry appentry(&_appPath, true);
277 //	BPath appPath(&appentry);
278 	status_t error=BView::Archive(data, deep);
279 	data->AddString("add_on", APP_SIG);
280 //	data->AddFlat("_appPath", (BFlattenable *) &_appPath);
281 	data->AddRef("_appPath", &_appPath);
282 
283 	return error;
284 }
285 
286 //Rehydrate the View from a given message (called by the DeskBar)
287 TrayView *TrayView::Instantiate(BMessage *data) {
288 
289 	if (!validate_instantiation(data, "TrayView")) {
290 		return NULL;
291 	}
292 
293 	return (new TrayView(data));
294 }
295 
296 void TrayView::AttachedToWindow() {
297 	if(Parent())
298 		SetViewColor(Parent()->ViewColor());
299 	if (_settings->Active()) {
300 		fNormalMM = mouse_mode();
301 		set_mouse_mode(B_FOCUS_FOLLOWS_MOUSE);
302 		release_sem(fPollerSem);
303 		watching = true;
304 	}
305 }
306 
307 void TrayView::Draw(BRect updaterect) {
308 	BRect bnds(Bounds());
309 
310 	if (Parent()) SetHighColor(Parent()->ViewColor());
311 	else SetHighColor(189, 186, 189, 255);
312 	FillRect(bnds);
313 
314 	if (_settings->Active())
315 	{
316 		if (_activeIcon) DrawBitmap(_activeIcon);
317 	}
318 	else
319 	{
320 		if (_inactiveIcon) DrawBitmap(_inactiveIcon);
321 	}
322 }
323 
324 void TrayView::MouseDown(BPoint where) {
325 	BWindow *window = Window();	/*To handle the MouseDown message*/
326 	if (!window)	/*Check for proper instantiation*/
327 		return;
328 
329 	BMessage *mouseMsg = window->CurrentMessage();
330 	if (!mouseMsg)	/*Check for existence*/
331 		return;
332 
333 	if (mouseMsg->what == B_MOUSE_DOWN) {
334 		/*Variables for storing the button pressed / modifying key*/
335 		uint32 	buttons = 0;
336 		uint32  modifiers = 0;
337 
338 		/*Get the button pressed*/
339 		mouseMsg->FindInt32("buttons", (int32 *) &buttons);
340 		/*Get modifier key (if any)*/
341 		mouseMsg->FindInt32("modifiers", (int32 *) &modifiers);
342 
343 		/*Now perform action*/
344 		switch(buttons) {
345 			case B_PRIMARY_MOUSE_BUTTON:
346 			{
347 				SetActive(!_settings->Active());
348 
349 				break;
350 			}
351 			case B_SECONDARY_MOUSE_BUTTON:
352 			{
353 				ConvertToScreen(&where);
354 
355 				//menu will delete itself (see constructor of ConfigMenu),
356 				//so all we're concerned about is calling Go() asynchronously
357 				ConfigMenu *menu = new ConfigMenu(this, false);
358 				menu->Go(where, true, true, ConvertToScreen(Bounds()), true);
359 
360 				break;
361 			}
362 		}
363 	}
364 }
365 
366 
367 int32 fronter(void *arg)
368 {
369 	TrayView *tv = (TrayView *)arg;
370 	int32 ws = current_workspace();
371 	volatile int32 tok = tv->current_window;
372 	sem_id sem = tv->fPollerSem;
373 
374 	snooze(tv->raise_delay);
375 
376 	if (acquire_sem(sem) != B_OK)
377 		return B_OK; // this really needs a better locking model...
378 	if (ws != current_workspace())
379 		goto end; // don't touch windows if we changed workspace
380 	if (tv->last_raiser_thread != find_thread(NULL))
381 		goto end; // seems a newer one has been spawn, exit
382 PRINT(("tok = %ld cw = %ld\n", tok, tv->current_window));
383 	if (tok == tv->current_window) {
384 		bool doZoom = false;
385 		BRect zoomRect(0.0f, 0.0f, 10.0f, 10.0f);
386 		do_window_action(tok, B_BRING_TO_FRONT, zoomRect, doZoom);
387 	}
388 
389 	end:
390 	release_sem(sem);
391 	return B_OK;
392 }
393 
394 
395 int32 poller(void *arg)
396 {
397 	TrayView *tv = (TrayView *)arg;
398 	volatile int32 tok = tv->current_window;
399 	int32 *tl = NULL;
400 	int32 i, tlc;
401 	window_info *wi = NULL;
402 
403 	int pass=0;
404 	BPoint mouse;
405 	uint32 buttons;
406 
407 	while (acquire_sem(tv->fPollerSem) == B_OK) {
408 		release_sem(tv->fPollerSem);
409 		pass++;
410 		BLooper *l = tv->Looper();
411 		if (!l || l->LockWithTimeout(500000) != B_OK)
412 			continue;
413 		tv->GetMouse(&mouse, &buttons);
414 		tv->ConvertToScreen(&mouse);
415 		tv->Looper()->Unlock();
416 		if (buttons) // we don't want to interfere when the user is moving a window or something...
417 			goto zzz;
418 
419 		tl = get_token_list(-1, &tlc);
420 		for (i=0; i<tlc; i++) {
421 			wi = get_window_info(tl[i]);
422 			if (wi) {
423 				if (wi->layer < 3) // we hit the desktop or a window not on this WS
424 					goto zzz;
425 				if ((wi->window_left > wi->window_right) || (wi->window_top > wi->window_bottom))
426 					goto zzz; // invalid window ?
427 /*
428 printf("if (!%s && (%li, %li)isin(%li)(%li, %li, %li, %li) && (%li != %li) ", wi->is_mini?"true":"false",
429 	(long)mouse.x, (long)mouse.y, i, wi->window_left, wi->window_right,
430 	wi->window_top, wi->window_bottom, wi-server_token, tok);
431 */
432 
433 
434 				if ((!wi->is_mini)
435 						&& (((long)mouse.x) > wi->window_left) && (((long)mouse.x) < wi->window_right)
436 						&& (((long)mouse.y) > wi->window_top) && (((long)mouse.y) < wi->window_bottom)) {
437 //((tv->_settings->Mode() != Mode_DeskbarOver) || (wi->team == tv->fDeskbarTeam))
438 
439 					if ((tv->_settings->Mode() == Mode_All) &&
440 					(wi->server_token == tv->current_window))
441 						goto zzz; // already raised
442 
443 					if ((tv->_settings->Mode() == Mode_All) || (wi->team == tv->fDeskbarTeam)) {
444 						tv->current_window = wi->server_token;
445 						tok = wi->server_token;
446 						resume_thread(tv->last_raiser_thread = spawn_thread(fronter, "fronter", B_NORMAL_PRIORITY, (void *)tv));
447 						goto zzz;
448 					} else if (tv->_settings->Mode() == Mode_DeskbarTouch) // give up, before we find Deskbar under it
449 						goto zzz;
450 				}
451 				free(wi);
452 				wi=NULL;
453 			} else
454 				goto zzz;
455 		}
456 	zzz:
457 //		puts("");
458 		if (wi) free(wi);
459 		wi = NULL;
460 		if (tl) free(tl);
461 		tl = NULL;
462 		snooze(tv->polling_delay);
463 	}
464 	return B_OK;
465 }
466 
467 
468 void TrayView::MessageReceived(BMessage* message)
469 {
470 	BMessenger msgr;
471 
472 	BAlert *alert;
473 	bigtime_t delay;
474 	int32 mode;
475 
476 	switch(message->what)
477 	{
478 		case MSG_TOGGLE_ACTIVE:
479 			SetActive(!_settings->Active());
480 			break;
481 		case MSG_SET_ACTIVE:
482 			SetActive(true);
483 			break;
484 		case MSG_SET_INACTIVE:
485 			SetActive(false);
486 			break;
487 		case MSG_SET_DELAY:
488 			delay = DEFAULT_DELAY;
489 			message->FindInt64(AR_DELAY, &delay);
490 			raise_delay = delay;
491 			_settings->SetDelay(delay);
492 			break;
493 		case MSG_SET_MODE:
494 			mode = Mode_All;
495 			message->FindInt32(AR_MODE, &mode);
496 			_settings->SetMode(mode);
497 			break;
498 		case MSG_SET_BEHAVIOUR:
499 		{
500 			message->FindInt32(AR_BEHAVIOUR, &mode);
501 			bool wasactive = _settings->Active();
502 			if (wasactive)
503 				SetActive(false);
504 			fNormalMM = (mode_mouse)mode;
505 			set_mouse_mode(fNormalMM);
506 			if (wasactive)
507 				SetActive(true);
508 			break;
509 		}
510 		case REMOVE_FROM_TRAY:
511 		{
512 			thread_id tid = spawn_thread(removeFromDeskbar, "RemoveFromDeskbar", B_NORMAL_PRIORITY, (void*)this);
513 			if (tid != 0) resume_thread(tid);
514 
515 			break;
516 		}
517 		case B_ABOUT_REQUESTED:
518 			alert = new BAlert("about box", "AutoRaise, (c) 2002, mmu_man\nEnjoy :-)", "OK", NULL, NULL,
519 				B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_INFO_ALERT);
520 			alert->SetShortcut(0, B_ENTER);
521 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
522 			alert->Go(NULL); // use asynchronous version
523 			break;
524 		case OPEN_SETTINGS:
525 
526 			break;
527 
528 		default:
529 			BView::MessageReceived(message);
530 	}
531 }
532 
533 AutoRaiseSettings *TrayView::Settings() const
534 {
535 	return _settings;
536 }
537 
538 void TrayView::SetActive(bool st)
539 {
540 	_settings->SetActive(st);
541 	if (_settings->Active())
542 	{
543 		if (!watching) {
544 			fNormalMM = mouse_mode();
545 			set_mouse_mode(B_FOCUS_FOLLOWS_MOUSE);
546 			release_sem(fPollerSem);
547 			watching = true;
548 		}
549 	}
550 	else
551 	{
552 		if (watching) {
553 			acquire_sem(fPollerSem);
554 			set_mouse_mode(fNormalMM);
555 			watching = false;
556 		}
557 	}
558 	Invalidate();
559 }
560 
561