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