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