xref: /haiku/src/kits/interface/Window.cpp (revision d9cebac2b77547b7064f22497514eecd2d047160)
1 /*
2  * Copyright 2001-2007, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Adrian Oanca <adioanca@cotty.iren.ro>
7  *		Axel Dörfler, axeld@pinc-software.de
8  *		Stephan Aßmus, <superstippi@gmx.de>
9  */
10 
11 
12 #include <Application.h>
13 #include <Autolock.h>
14 #include <Button.h>
15 #include <MenuBar.h>
16 #include <MenuItem.h>
17 #include <MessageQueue.h>
18 #include <MessageRunner.h>
19 #include <PropertyInfo.h>
20 #include <Roster.h>
21 #include <Screen.h>
22 #include <String.h>
23 #include <Window.h>
24 
25 #include <input_globals.h>
26 #include <AppMisc.h>
27 #include <ApplicationPrivate.h>
28 #include <AppServerLink.h>
29 #include <DirectMessageTarget.h>
30 #include <InputServerTypes.h>
31 #include <MenuPrivate.h>
32 #include <MessagePrivate.h>
33 #include <PortLink.h>
34 #include <ServerProtocol.h>
35 #include <TokenSpace.h>
36 #include <tracker_private.h>
37 
38 #include <ctype.h>
39 #include <stdio.h>
40 #include <math.h>
41 
42 
43 //#define DEBUG_WIN
44 #ifdef DEBUG_WIN
45 #	define STRACE(x) printf x
46 #else
47 #	define STRACE(x) ;
48 #endif
49 
50 #define B_HIDE_APPLICATION '_AHD'
51 	// if we ever move this to a public namespace, we should also move the
52 	// handling of this message into BApplication
53 
54 void do_minimize_team(BRect zoomRect, team_id team, bool zoom);
55 
56 struct BWindow::unpack_cookie {
57 	unpack_cookie();
58 
59 	BMessage*	message;
60 	int32		index;
61 	BHandler*	focus;
62 	int32		focus_token;
63 	int32		last_view_token;
64 	bool		found_focus;
65 	bool		tokens_scanned;
66 };
67 
68 class BWindow::Shortcut {
69 	public:
70 		Shortcut(uint32 key, uint32 modifiers, BMenuItem* item);
71 		Shortcut(uint32 key, uint32 modifiers, BMessage* message, BHandler* target);
72 		~Shortcut();
73 
74 		bool Matches(uint32 key, uint32 modifiers) const;
75 
76 		BMenuItem* MenuItem() const { return fMenuItem; }
77 		BMessage* Message() const { return fMessage; }
78 		BHandler* Target() const { return fTarget; }
79 
80 		static uint32 AllowedModifiers();
81 		static uint32 PrepareKey(uint32 key);
82 		static uint32 PrepareModifiers(uint32 modifiers);
83 
84 	private:
85 		uint32		fKey;
86 		uint32		fModifiers;
87 		BMenuItem*	fMenuItem;
88 		BMessage*	fMessage;
89 		BHandler*	fTarget;
90 };
91 
92 
93 using BPrivate::gDefaultTokens;
94 
95 static property_info sWindowPropInfo[] = {
96 	{
97 		"Active", { B_GET_PROPERTY, B_SET_PROPERTY },
98 		{ B_DIRECT_SPECIFIER }, NULL, 0, { B_BOOL_TYPE }
99 	},
100 
101 	{
102 		"Feel", { B_GET_PROPERTY, B_SET_PROPERTY },
103 		{ B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE }
104 	},
105 
106 	{
107 		"Flags", { B_GET_PROPERTY, B_SET_PROPERTY },
108 		{ B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE }
109 	},
110 
111 	{
112 		"Frame", { B_GET_PROPERTY, B_SET_PROPERTY },
113 		{ B_DIRECT_SPECIFIER }, NULL, 0, { B_RECT_TYPE }
114 	},
115 
116 	{
117 		"Hidden", { B_GET_PROPERTY, B_SET_PROPERTY },
118 		{ B_DIRECT_SPECIFIER }, NULL, 0, { B_BOOL_TYPE }
119 	},
120 
121 	{
122 		"Look", { B_GET_PROPERTY, B_SET_PROPERTY },
123 		{ B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE }
124 	},
125 
126 	{
127 		"Title", { B_GET_PROPERTY, B_SET_PROPERTY },
128 		{ B_DIRECT_SPECIFIER }, NULL, 0, { B_STRING_TYPE }
129 	},
130 
131 	{
132 		"Workspaces", { B_GET_PROPERTY, B_SET_PROPERTY },
133 		{ B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE }
134 	},
135 
136 	{
137 		"MenuBar", {},
138 		{ B_DIRECT_SPECIFIER }, NULL, 0, {}
139 	},
140 
141 	{
142 		"View", { B_COUNT_PROPERTIES },
143 		{ B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE }
144 	},
145 
146 	{
147 		"View", {}, {}, NULL, 0, {}
148 	},
149 
150 	{
151 		"Minimize", { B_GET_PROPERTY, B_SET_PROPERTY },
152 		{ B_DIRECT_SPECIFIER }, NULL, 0, { B_BOOL_TYPE }
153 	},
154 
155 	{}
156 };
157 
158 static value_info sWindowValueInfo[] = {
159 	{
160 		"MoveTo", 'WDMT', B_COMMAND_KIND,
161 		"Moves to the position in the BPoint data"
162 	},
163 
164 	{
165 		"MoveBy", 'WDMB', B_COMMAND_KIND,
166 		"Moves by the offsets in the BPoint data"
167 	},
168 
169 	{
170 		"ResizeTo", 'WDRT', B_COMMAND_KIND,
171 		"Resize to the size in the BPoint data"
172 	},
173 
174 	{
175 		"ResizeBy", 'WDRB', B_COMMAND_KIND,
176 		"Resize by the offsets in the BPoint data"
177 	},
178 
179 	{}
180 };
181 
182 
183 void
184 _set_menu_sem_(BWindow *window, sem_id sem)
185 {
186 	if (window != NULL)
187 		window->fMenuSem = sem;
188 }
189 
190 
191 //	#pragma mark -
192 
193 
194 BWindow::unpack_cookie::unpack_cookie()
195 	:
196 	message((BMessage*)~0UL),
197 		// message == NULL is our exit condition
198 	index(0),
199 	focus_token(B_NULL_TOKEN),
200 	last_view_token(B_NULL_TOKEN),
201 	found_focus(false),
202 	tokens_scanned(false)
203 {
204 }
205 
206 
207 //	#pragma mark -
208 
209 
210 BWindow::Shortcut::Shortcut(uint32 key, uint32 modifiers, BMenuItem* item)
211 	:
212 	fKey(PrepareKey(key)),
213 	fModifiers(PrepareModifiers(modifiers)),
214 	fMenuItem(item),
215 	fMessage(NULL),
216 	fTarget(NULL)
217 {
218 }
219 
220 
221 BWindow::Shortcut::Shortcut(uint32 key, uint32 modifiers, BMessage* message,
222 	BHandler* target)
223 	:
224 	fKey(PrepareKey(key)),
225 	fModifiers(PrepareModifiers(modifiers)),
226 	fMenuItem(NULL),
227 	fMessage(message),
228 	fTarget(target)
229 {
230 }
231 
232 
233 BWindow::Shortcut::~Shortcut()
234 {
235 	// we own the message, if any
236 	delete fMessage;
237 }
238 
239 
240 bool
241 BWindow::Shortcut::Matches(uint32 key, uint32 modifiers) const
242 {
243 	return fKey == key && fModifiers == modifiers;
244 }
245 
246 
247 /*static*/
248 uint32
249 BWindow::Shortcut::AllowedModifiers()
250 {
251 	return B_COMMAND_KEY | B_OPTION_KEY | B_SHIFT_KEY
252 		| B_CONTROL_KEY | B_MENU_KEY;
253 }
254 
255 
256 /*static*/
257 uint32
258 BWindow::Shortcut::PrepareModifiers(uint32 modifiers)
259 {
260 	return (modifiers & AllowedModifiers()) | B_COMMAND_KEY;
261 }
262 
263 
264 /*static*/
265 uint32
266 BWindow::Shortcut::PrepareKey(uint32 key)
267 {
268 	return tolower(key);
269 		// TODO: support unicode and/or more intelligent key mapping
270 }
271 
272 
273 //	#pragma mark -
274 
275 
276 BWindow::BWindow(BRect frame, const char* title, window_type type,
277 		uint32 flags, uint32 workspace)
278 	: BLooper(title, B_DISPLAY_PRIORITY)
279 {
280 	window_look look;
281 	window_feel feel;
282 	_DecomposeType(type, &look, &feel);
283 
284 	_InitData(frame, title, look, feel, flags, workspace);
285 }
286 
287 
288 BWindow::BWindow(BRect frame, const char* title, window_look look,
289 		window_feel feel, uint32 flags, uint32 workspace)
290 	: BLooper(title, B_DISPLAY_PRIORITY)
291 {
292 	_InitData(frame, title, look, feel, flags, workspace);
293 }
294 
295 
296 BWindow::BWindow(BMessage* data)
297 	: BLooper(data)
298 {
299 	data->FindRect("_frame", &fFrame);
300 
301 	const char *title;
302 	data->FindString("_title", &title);
303 
304 	window_look look;
305 	data->FindInt32("_wlook", (int32 *)&look);
306 
307 	window_feel feel;
308 	data->FindInt32("_wfeel", (int32 *)&feel);
309 
310 	if (data->FindInt32("_flags", (int32 *)&fFlags) != B_OK)
311 		fFlags = 0;
312 
313 	uint32 workspaces;
314 	data->FindInt32("_wspace", (int32 *)&workspaces);
315 
316 	uint32 type;
317 	if (data->FindInt32("_type", (int32*)&type) == B_OK)
318 		_DecomposeType((window_type)type, &fLook, &fFeel);
319 
320 		// connect to app_server and initialize data
321 	_InitData(fFrame, title, look, feel, fFlags, workspaces);
322 
323 	if (data->FindFloat("_zoom", 0, &fMaxZoomWidth) == B_OK
324 		&& data->FindFloat("_zoom", 1, &fMaxZoomHeight) == B_OK)
325 		SetZoomLimits(fMaxZoomWidth, fMaxZoomHeight);
326 
327 	if (data->FindFloat("_sizel", 0, &fMinWidth) == B_OK
328 		&& data->FindFloat("_sizel", 1, &fMinHeight) == B_OK
329 		&& data->FindFloat("_sizel", 2, &fMaxWidth) == B_OK
330 		&& data->FindFloat("_sizel", 3, &fMaxHeight) == B_OK)
331 		SetSizeLimits(fMinWidth, fMaxWidth,
332 			fMinHeight, fMaxHeight);
333 
334 	if (data->FindInt64("_pulse", &fPulseRate) == B_OK)
335 		SetPulseRate(fPulseRate);
336 
337 	BMessage msg;
338 	int32 i = 0;
339 	while (data->FindMessage("_views", i++, &msg) == B_OK) {
340 		BArchivable *obj = instantiate_object(&msg);
341 		BView *child = dynamic_cast<BView *>(obj);
342 		if (child)
343 			AddChild(child);
344 	}
345 }
346 
347 
348 BWindow::BWindow(BRect frame, int32 bitmapToken)
349 	: BLooper("offscreen bitmap")
350 {
351 	// TODO: Implement for real
352 	_DecomposeType(B_UNTYPED_WINDOW, &fLook, &fFeel);
353 	_InitData(frame, "offscreen", fLook, fFeel, 0, 0, bitmapToken);
354 }
355 
356 
357 BWindow::~BWindow()
358 {
359 	if (BMenu *menu = dynamic_cast<BMenu *>(fFocus))
360 		menu->QuitTracking();
361 
362 	// The BWindow is locked when the destructor is called,
363 	// we need to unlock because the menubar thread tries
364 	// to post a message, which will deadlock otherwise.
365 	// TODO: I replaced Unlock() with UnlockFully() because the window
366 	// was kept locked after that in case it was closed using ALT-W.
367 	// There might be an extra Lock() somewhere in the quitting path...
368 	UnlockFully();
369 
370 	// Wait if a menu is still tracking
371 	if (fMenuSem > 0) {
372 		while (acquire_sem(fMenuSem) == B_INTERRUPTED)
373 			;
374 	}
375 
376 	Lock();
377 
378 	fTopView->RemoveSelf();
379 	delete fTopView;
380 
381 	// remove all remaining shortcuts
382 	int32 shortCutCount = fShortcuts.CountItems();
383 	for (int32 i = 0; i < shortCutCount; i++) {
384 		delete (Shortcut*)fShortcuts.ItemAtFast(i);
385 	}
386 
387 	// TODO: release other dynamically-allocated objects
388 	free(fTitle);
389 
390 	// disable pulsing
391 	SetPulseRate(0);
392 
393 	// tell app_server about our demise
394 	fLink->StartMessage(AS_DELETE_WINDOW);
395 	// sync with the server so that for example
396 	// a BBitmap can be sure that there are no
397 	// more pending messages that are executed
398 	// after the bitmap is deleted (which uses
399 	// a different link and server side thread)
400 	int32 code;
401 	fLink->FlushWithReply(code);
402 
403 	// the sender port belongs to the app_server
404 	delete_port(fLink->ReceiverPort());
405 	delete fLink;
406 }
407 
408 
409 BArchivable *
410 BWindow::Instantiate(BMessage *data)
411 {
412 	if (!validate_instantiation(data , "BWindow"))
413 		return NULL;
414 
415 	return new BWindow(data);
416 }
417 
418 
419 status_t
420 BWindow::Archive(BMessage* data, bool deep) const
421 {
422 	status_t ret = BLooper::Archive(data, deep);
423 
424 	if (ret == B_OK)
425 		ret = data->AddRect("_frame", fFrame);
426 	if (ret == B_OK)
427 		ret = data->AddString("_title", fTitle);
428 	if (ret == B_OK)
429 		ret = data->AddInt32("_wlook", fLook);
430 	if (ret == B_OK)
431 		ret = data->AddInt32("_wfeel", fFeel);
432 	if (ret == B_OK && fFlags != 0)
433 		ret = data->AddInt32("_flags", fFlags);
434 	if (ret == B_OK)
435 		ret = data->AddInt32("_wspace", (uint32)Workspaces());
436 
437 	if (ret == B_OK && !_ComposeType(fLook, fFeel))
438 		ret = data->AddInt32("_type", (uint32)Type());
439 
440 	if (fMaxZoomWidth != 32768.0 || fMaxZoomHeight != 32768.0) {
441 		if (ret == B_OK)
442 			ret = data->AddFloat("_zoom", fMaxZoomWidth);
443 		if (ret == B_OK)
444 			ret = data->AddFloat("_zoom", fMaxZoomHeight);
445 	}
446 
447 	if (fMinWidth != 0.0 || fMinHeight != 0.0
448 		|| fMaxWidth != 32768.0 || fMaxHeight != 32768.0) {
449 		if (ret == B_OK)
450 			ret = data->AddFloat("_sizel", fMinWidth);
451 		if (ret == B_OK)
452 			ret = data->AddFloat("_sizel", fMinHeight);
453 		if (ret == B_OK)
454 			ret = data->AddFloat("_sizel", fMaxWidth);
455 		if (ret == B_OK)
456 			ret = data->AddFloat("_sizel", fMaxHeight);
457 	}
458 
459 	if (ret == B_OK && fPulseRate != 500000)
460 		data->AddInt64("_pulse", fPulseRate);
461 
462 	if (ret == B_OK && deep) {
463 		int32 noOfViews = CountChildren();
464 		for (int32 i = 0; i < noOfViews; i++){
465 			BMessage childArchive;
466 			ret = ChildAt(i)->Archive(&childArchive, true);
467 			if (ret == B_OK)
468 				ret = data->AddMessage("_views", &childArchive);
469 			if (ret != B_OK)
470 				break;
471 		}
472 	}
473 
474 	return ret;
475 }
476 
477 
478 void
479 BWindow::Quit()
480 {
481 	if (!IsLocked()) {
482 		const char *name = Name();
483 		if (!name)
484 			name = "no-name";
485 
486 		printf("ERROR - you must Lock a looper before calling Quit(), "
487 			   "team=%ld, looper=%s\n", Team(), name);
488 	}
489 
490 	// Try to lock
491 	if (!Lock()){
492 		// We're toast already
493 		return;
494 	}
495 
496 	while (!IsHidden())	{
497 		Hide();
498 	}
499 
500 	if (fFlags & B_QUIT_ON_WINDOW_CLOSE)
501 		be_app->PostMessage(B_QUIT_REQUESTED);
502 
503 	BLooper::Quit();
504 }
505 
506 
507 void
508 BWindow::AddChild(BView *child, BView *before)
509 {
510 	BAutolock locker(this);
511 	if (locker.IsLocked())
512 		fTopView->AddChild(child, before);
513 }
514 
515 
516 bool
517 BWindow::RemoveChild(BView *child)
518 {
519 	BAutolock locker(this);
520 	if (!locker.IsLocked())
521 		return false;
522 
523 	return fTopView->RemoveChild(child);
524 }
525 
526 
527 int32
528 BWindow::CountChildren() const
529 {
530 	BAutolock locker(const_cast<BWindow*>(this));
531 	if (!locker.IsLocked())
532 		return 0;
533 
534 	return fTopView->CountChildren();
535 }
536 
537 
538 BView *
539 BWindow::ChildAt(int32 index) const
540 {
541 	BAutolock locker(const_cast<BWindow*>(this));
542 	if (!locker.IsLocked())
543 		return NULL;
544 
545 	return fTopView->ChildAt(index);
546 }
547 
548 
549 void
550 BWindow::Minimize(bool minimize)
551 {
552 	if (IsModal() || IsFloating() || fMinimized == minimize || !Lock())
553 		return;
554 
555 	fMinimized = minimize;
556 
557 	fLink->StartMessage(AS_MINIMIZE_WINDOW);
558 	fLink->Attach<bool>(minimize);
559 	fLink->Attach<int32>(fShowLevel);
560 	fLink->Flush();
561 
562 	Unlock();
563 }
564 
565 
566 status_t
567 BWindow::SendBehind(const BWindow *window)
568 {
569 	if (!window || !Lock())
570 		return B_ERROR;
571 
572 	fLink->StartMessage(AS_SEND_BEHIND);
573 	fLink->Attach<int32>(_get_object_token_(window));
574 	fLink->Attach<team_id>(Team());
575 
576 	status_t status = B_ERROR;
577 	fLink->FlushWithReply(status);
578 
579 	Unlock();
580 
581 	return status;
582 }
583 
584 
585 void
586 BWindow::Flush() const
587 {
588 	if (const_cast<BWindow *>(this)->Lock()) {
589 		fLink->Flush();
590 		const_cast<BWindow *>(this)->Unlock();
591 	}
592 }
593 
594 
595 void
596 BWindow::Sync() const
597 {
598 	if (!const_cast<BWindow*>(this)->Lock())
599 		return;
600 
601 	fLink->StartMessage(AS_SYNC);
602 
603 	// waiting for the reply is the actual syncing
604 	int32 code;
605 	fLink->FlushWithReply(code);
606 
607 	const_cast<BWindow*>(this)->Unlock();
608 }
609 
610 
611 void
612 BWindow::DisableUpdates()
613 {
614 	if (Lock()) {
615 		fLink->StartMessage(AS_DISABLE_UPDATES);
616 		fLink->Flush();
617 		Unlock();
618 	}
619 }
620 
621 
622 void
623 BWindow::EnableUpdates()
624 {
625 	if (Lock()) {
626 		fLink->StartMessage(AS_ENABLE_UPDATES);
627 		fLink->Flush();
628 		Unlock();
629 	}
630 }
631 
632 
633 void
634 BWindow::BeginViewTransaction()
635 {
636 	if (Lock()) {
637 		if (fInTransaction) {
638 			Unlock();
639 			return;
640 		}
641 		fInTransaction = true;
642 
643 		Unlock();
644 	}
645 }
646 
647 
648 void
649 BWindow::EndViewTransaction()
650 {
651 	if (Lock()) {
652 		if (!fInTransaction) {
653 			Unlock();
654 			return;
655 		}
656 		fLink->Flush();
657 		fInTransaction = false;
658 
659 		Unlock();
660 	}
661 }
662 
663 
664 bool
665 BWindow::IsFront() const
666 {
667 	BAutolock locker(const_cast<BWindow*>(this));
668 	if (!locker.IsLocked())
669 		return false;
670 
671 	fLink->StartMessage(AS_IS_FRONT_WINDOW);
672 
673 	status_t status;
674 	if (fLink->FlushWithReply(status) == B_OK)
675 		return status >= B_OK;
676 
677 	return false;
678 }
679 
680 
681 void
682 BWindow::MessageReceived(BMessage *msg)
683 {
684 	if (!msg->HasSpecifiers()) {
685 		if (msg->what == B_KEY_DOWN)
686 			_KeyboardNavigation();
687 
688 		return BLooper::MessageReceived(msg);
689 	}
690 
691 	BMessage replyMsg(B_REPLY);
692 	bool handled = false;
693 
694 	BMessage specifier;
695 	int32 what;
696 	const char *prop;
697 	int32 index;
698 
699 	if (msg->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK)
700 		return BLooper::MessageReceived(msg);
701 
702 	BPropertyInfo propertyInfo(sWindowPropInfo);
703 	switch (propertyInfo.FindMatch(msg, index, &specifier, what, prop)) {
704 		case 0:
705 			if (msg->what == B_GET_PROPERTY) {
706 				replyMsg.AddBool("result", IsActive());
707 				handled = true;
708 			} else if (msg->what == B_SET_PROPERTY) {
709 				bool newActive;
710 				if (msg->FindBool("data", &newActive) == B_OK) {
711 					Activate(newActive);
712 					handled = true;
713 				}
714 			}
715 			break;
716 		case 1:
717 			if (msg->what == B_GET_PROPERTY) {
718 				replyMsg.AddInt32("result", (uint32)Feel());
719 				handled = true;
720 			} else {
721 				uint32 newFeel;
722 				if (msg->FindInt32("data", (int32 *)&newFeel) == B_OK) {
723 					SetFeel((window_feel)newFeel);
724 					handled = true;
725 				}
726 			}
727 			break;
728 		case 2:
729 			if (msg->what == B_GET_PROPERTY) {
730 				replyMsg.AddInt32("result", Flags());
731 				handled = true;
732 			} else {
733 				uint32 newFlags;
734 				if (msg->FindInt32("data", (int32 *)&newFlags) == B_OK) {
735 					SetFlags(newFlags);
736 					handled = true;
737 				}
738 			}
739 			break;
740 		case 3:
741 			if (msg->what == B_GET_PROPERTY) {
742 				replyMsg.AddRect("result", Frame());
743 				handled = true;
744 			} else {
745 				BRect newFrame;
746 				if (msg->FindRect("data", &newFrame) == B_OK) {
747 					MoveTo(newFrame.LeftTop());
748 					ResizeTo(newFrame.Width(), newFrame.Height());
749 					handled = true;
750 				}
751 			}
752 			break;
753 		case 4:
754 			if (msg->what == B_GET_PROPERTY) {
755 				replyMsg.AddBool("result", IsHidden());
756 				handled = true;
757 			} else {
758 				bool hide;
759 				if (msg->FindBool("data", &hide) == B_OK) {
760 					if (hide) {
761 						if (!IsHidden())
762 							Hide();
763 					} else if (IsHidden())
764 						Show();
765 					handled = true;
766 				}
767 			}
768 			break;
769 		case 5:
770 			if (msg->what == B_GET_PROPERTY) {
771 				replyMsg.AddInt32("result", (uint32)Look());
772 				handled = true;
773 			} else {
774 				uint32 newLook;
775 				if (msg->FindInt32("data", (int32 *)&newLook) == B_OK) {
776 					SetLook((window_look)newLook);
777 					handled = true;
778 				}
779 			}
780 			break;
781 		case 6:
782 			if (msg->what == B_GET_PROPERTY) {
783 				replyMsg.AddString("result", Title());
784 				handled = true;
785 			} else {
786 				const char *newTitle = NULL;
787 				if (msg->FindString("data", &newTitle) == B_OK) {
788 					SetTitle(newTitle);
789 					handled = true;
790 				}
791 			}
792 			break;
793 		case 7:
794 			if (msg->what == B_GET_PROPERTY) {
795 				replyMsg.AddInt32( "result", Workspaces());
796 				handled = true;
797 			} else {
798 				uint32 newWorkspaces;
799 				if (msg->FindInt32("data", (int32 *)&newWorkspaces) == B_OK) {
800 					SetWorkspaces(newWorkspaces);
801 					handled = true;
802 				}
803 			}
804 			break;
805 		case 11:
806 			if (msg->what == B_GET_PROPERTY) {
807 				replyMsg.AddBool("result", IsMinimized());
808 				handled = true;
809 			} else {
810 				bool minimize;
811 				if (msg->FindBool("data", &minimize) == B_OK) {
812 					Minimize(minimize);
813 					handled = true;
814 				}
815 			}
816 			break;
817 		default:
818 			return BLooper::MessageReceived(msg);
819 	}
820 
821 	if (handled) {
822 		if (msg->what == B_SET_PROPERTY)
823 			replyMsg.AddInt32("error", B_OK);
824 	} else {
825 		replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD;
826 		replyMsg.AddInt32("error", B_BAD_SCRIPT_SYNTAX);
827 		replyMsg.AddString("message", "Didn't understand the specifier(s)");
828 	}
829 	msg->SendReply(&replyMsg);
830 }
831 
832 
833 void
834 BWindow::DispatchMessage(BMessage *msg, BHandler *target)
835 {
836 	if (!msg)
837 		return;
838 
839 	switch (msg->what) {
840 		case B_ZOOM:
841 			Zoom();
842 			break;
843 
844 		case B_MINIMIZE:
845 		{
846 			bool minimize;
847 			if (msg->FindBool("minimize", &minimize) == B_OK)
848 				Minimize(minimize);
849 			break;
850 		}
851 
852 		case B_HIDE_APPLICATION:
853 		{
854 			// Hide all applications with the same signature
855 			// (ie. those that are part of the same group to be consistent
856 			// to what the Deskbar shows you).
857 			app_info info;
858 			be_app->GetAppInfo(&info);
859 
860 			BList list;
861 			be_roster->GetAppList(info.signature, &list);
862 
863 			for (int32 i = 0; i < list.CountItems(); i++) {
864 				do_minimize_team(BRect(), (team_id)list.ItemAt(i), false);
865 			}
866 			break;
867 		}
868 
869 		case B_WINDOW_RESIZED:
870 		{
871 			int32 width, height;
872 			if (msg->FindInt32("width", &width) == B_OK
873 				&& msg->FindInt32("height", &height) == B_OK) {
874 				// combine with pending resize notifications
875 				BMessage* pendingMessage;
876 				while ((pendingMessage = MessageQueue()->FindMessage(B_WINDOW_RESIZED, 0))) {
877 					if (pendingMessage != msg) {
878 						int32 nextWidth;
879 						if (pendingMessage->FindInt32("width", &nextWidth) == B_OK)
880 							width = nextWidth;
881 
882 						int32 nextHeight;
883 						if (pendingMessage->FindInt32("height", &nextHeight) == B_OK)
884 							height = nextHeight;
885 
886 						MessageQueue()->RemoveMessage(pendingMessage);
887 						delete pendingMessage;
888 							// this deletes the first *additional* message
889 							// fCurrentMessage is safe
890 					} else {
891 						MessageQueue()->RemoveMessage(pendingMessage);
892 					}
893 				}
894 				if (width != fFrame.Width() || height != fFrame.Height()) {
895 					// NOTE: we might have already handled the resize
896 					// in an _UPDATE_ message
897 					fFrame.right = fFrame.left + width;
898 					fFrame.bottom = fFrame.top + height;
899 
900 					_AdoptResize();
901 //					FrameResized(width, height);
902 				}
903 // call hook function anyways
904 // TODO: When a window is resized programmatically,
905 // it receives this message, and maybe it is wise to
906 // keep the asynchronous nature of this process to
907 // not risk breaking any apps.
908 FrameResized(width, height);
909 			}
910 			break;
911 		}
912 
913 		case B_WINDOW_MOVED:
914 		{
915 			BPoint origin;
916 			if (msg->FindPoint("where", &origin) == B_OK) {
917 				if (fFrame.LeftTop() != origin) {
918 					// NOTE: we might have already handled the move
919 					// in an _UPDATE_ message
920 					fFrame.OffsetTo(origin);
921 
922 //					FrameMoved(origin);
923 				}
924 // call hook function anyways
925 // TODO: When a window is moved programmatically,
926 // it receives this message, and maybe it is wise to
927 // keep the asynchronous nature of this process to
928 // not risk breaking any apps.
929 FrameMoved(origin);
930 			}
931 			break;
932 		}
933 
934 		case B_WINDOW_ACTIVATED:
935 			if (target == this) {
936 				bool active;
937 				if (msg->FindBool("active", &active) == B_OK
938 					&& active != fActive) {
939 					fActive = active;
940 
941 					WindowActivated(active);
942 
943 					// call hook function 'WindowActivated(bool)' for all
944 					// views attached to this window.
945 					fTopView->_Activate(active);
946 
947 					// we notify the input server if we are gaining or losing focus
948 					// from a view which has the B_INPUT_METHOD_AWARE on a window
949 					// (de)activation
950 					bool inputMethodAware = false;
951 					if (fFocus)
952 						inputMethodAware = fFocus->Flags() & B_INPUT_METHOD_AWARE;
953 					BMessage msg(active && inputMethodAware ? IS_FOCUS_IM_AWARE_VIEW : IS_UNFOCUS_IM_AWARE_VIEW);
954 					BMessenger messenger(fFocus);
955 					BMessage reply;
956 					if (fFocus)
957 						msg.AddMessenger("view", messenger);
958 					_control_input_server_(&msg, &reply);
959 				}
960 			} else
961 				target->MessageReceived(msg);
962 			break;
963 
964 		case B_SCREEN_CHANGED:
965 			if (target == this) {
966 				BRect frame;
967 				uint32 mode;
968 				if (msg->FindRect("frame", &frame) == B_OK
969 					&& msg->FindInt32("mode", (int32 *)&mode) == B_OK)
970 					ScreenChanged(frame, (color_space)mode);
971 			} else
972 				target->MessageReceived(msg);
973 			break;
974 
975 		case B_WORKSPACE_ACTIVATED:
976 			if (target == this) {
977 				uint32 workspace;
978 				bool active;
979 				if (msg->FindInt32("workspace", (int32 *)&workspace) == B_OK
980 					&& msg->FindBool("active", &active) == B_OK)
981 					WorkspaceActivated(workspace, active);
982 			} else
983 				target->MessageReceived(msg);
984 			break;
985 
986 		case B_WORKSPACES_CHANGED:
987 			if (target == this) {
988 				uint32 oldWorkspace, newWorkspace;
989 				if (msg->FindInt32("old", (int32 *)&oldWorkspace) == B_OK
990 					&& msg->FindInt32("new", (int32 *)&newWorkspace) == B_OK)
991 					WorkspacesChanged(oldWorkspace, newWorkspace);
992 			} else
993 				target->MessageReceived(msg);
994 			break;
995 
996 		case B_INVALIDATE:
997 		{
998 			if (BView* view = dynamic_cast<BView*>(target)) {
999 				BRect rect;
1000 				if (msg->FindRect("be:area", &rect) == B_OK)
1001 					view->Invalidate(rect);
1002 				else
1003 					view->Invalidate();
1004 			} else
1005 				target->MessageReceived(msg);
1006 			break;
1007 		}
1008 
1009 		case B_KEY_DOWN:
1010 		{
1011 			if (!_HandleKeyDown(msg)) {
1012 				if (BView* view = dynamic_cast<BView*>(target)) {
1013 					// TODO: cannot use "string" here if we support having different
1014 					//	font encoding per view (it's supposed to be converted by
1015 					//	_HandleKeyDown() one day)
1016 					const char *string = NULL;
1017 					if (msg->FindString("bytes", &string) == B_OK)
1018 						view->KeyDown(string, strlen(string));
1019 				} else
1020 					target->MessageReceived(msg);
1021 			}
1022 			break;
1023 		}
1024 
1025 		case B_KEY_UP:
1026 		{
1027 			const char *string = NULL;
1028 			msg->FindString("bytes", &string);
1029 
1030 			// TODO: same as above
1031 			if (BView* view = dynamic_cast<BView*>(target))
1032 				view->KeyUp(string, strlen(string));
1033 			else
1034 				target->MessageReceived(msg);
1035 			break;
1036 		}
1037 
1038 		case B_MOUSE_DOWN:
1039 		{
1040 			BView *view = dynamic_cast<BView *>(target);
1041 
1042 			// Close an eventually opened menu, if this click targets the
1043 			// preferred handler, and unless the target is the menu itself
1044 			BMenu *menu = dynamic_cast<BMenu *>(fFocus);
1045 			if (menu != NULL && menu != view && PreferredHandler() == target
1046 				&& menu->State() != MENU_STATE_CLOSED) {
1047 				menu->QuitTracking();
1048 				return;
1049 			}
1050 
1051 			if (view != NULL) {
1052 				BPoint where;
1053 				msg->FindPoint("be:view_where", &where);
1054 				view->MouseDown(where);
1055 			} else
1056 				target->MessageReceived(msg);
1057 
1058 			break;
1059 		}
1060 
1061 		case B_MOUSE_UP:
1062 		{
1063 			if (BView *view = dynamic_cast<BView *>(target)) {
1064 				BPoint where;
1065 				msg->FindPoint("be:view_where", &where);
1066 				view->fMouseEventOptions = 0;
1067 				view->MouseUp(where);
1068 			} else
1069 				target->MessageReceived(msg);
1070 
1071 			break;
1072 		}
1073 
1074 		case B_MOUSE_MOVED:
1075 		{
1076 			if (BView *view = dynamic_cast<BView *>(target)) {
1077 				if (((view->fEventOptions | view->fMouseEventOptions)
1078 						& B_NO_POINTER_HISTORY) != 0) {
1079 					// filter out older mouse moved messages in the queue
1080 					_DequeueAll();
1081 					BMessageQueue *queue = MessageQueue();
1082 					queue->Lock();
1083 
1084 					BMessage *moved;
1085 					for (int32 i = 0; (moved = queue->FindMessage(i)) != NULL; i++) {
1086 						if (moved != msg && moved->what == B_MOUSE_MOVED) {
1087 							queue->Unlock();
1088 							return;
1089 						}
1090 					}
1091 					queue->Unlock();
1092 				}
1093 
1094 				BPoint where;
1095 				uint32 buttons;
1096 				uint32 transit;
1097 				msg->FindPoint("be:view_where", &where);
1098 				msg->FindInt32("buttons", (int32*)&buttons);
1099 				msg->FindInt32("be:transit", (int32*)&transit);
1100 
1101 #if 0
1102 				bigtime_t when;
1103 				if (msg->FindInt64("when", (int64*)&when) < B_OK)
1104 					printf("BWindow B_MOUSE_MOVED no when\n");
1105 				else if (system_time() - when > 5000)
1106 					printf("BWindow B_MOUSE_MOVED lagging behind\n");
1107 #endif
1108 
1109 				BMessage* dragMessage = NULL;
1110 				if (msg->HasMessage("be:drag_message")) {
1111 					dragMessage = new BMessage();
1112 					if (msg->FindMessage("be:drag_message", dragMessage) != B_OK) {
1113 						delete dragMessage;
1114 						dragMessage = NULL;
1115 					}
1116 				}
1117 
1118 				view->MouseMoved(where, transit, dragMessage);
1119 				delete dragMessage;
1120 			} else
1121 				target->MessageReceived(msg);
1122 
1123 			break;
1124 		}
1125 
1126 		case B_PULSE:
1127 			if (target == this && fPulseRunner) {
1128 				fTopView->_Pulse();
1129 				fLink->Flush();
1130 			} else
1131 				target->MessageReceived(msg);
1132 			break;
1133 
1134 		case _UPDATE_:
1135 		{
1136 //bigtime_t now = system_time();
1137 //bigtime_t drawTime = 0;
1138 			STRACE(("info:BWindow handling _UPDATE_.\n"));
1139 			BRect updateRect;
1140 
1141 			fLink->StartMessage(AS_BEGIN_UPDATE);
1142 			fInTransaction = true;
1143 
1144 			int32 code;
1145 			if (fLink->FlushWithReply(code) == B_OK
1146 				&& code == B_OK) {
1147 				// read current window position and size first,
1148 				// the update rect is in screen coordinates...
1149 				// so we need to be up to date
1150 				BPoint origin;
1151 				fLink->Read<BPoint>(&origin);
1152 				float width;
1153 				float height;
1154 				fLink->Read<float>(&width);
1155 				fLink->Read<float>(&height);
1156 				if (origin != fFrame.LeftTop()) {
1157 					// TODO: remove code duplicatation with
1158 					// B_WINDOW_MOVED case...
1159 					//printf("window position was not up to date\n");
1160 					fFrame.OffsetTo(origin);
1161 					FrameMoved(origin);
1162 				}
1163 				if (width != fFrame.Width() || height != fFrame.Height()) {
1164 					// TODO: remove code duplicatation with
1165 					// B_WINDOW_RESIZED case...
1166 					//printf("window size was not up to date\n");
1167 					fFrame.right = fFrame.left + width;
1168 					fFrame.bottom = fFrame.top + height;
1169 
1170 					_AdoptResize();
1171 					FrameResized(width, height);
1172 				}
1173 
1174 				// read culmulated update rect (is in screen coords)
1175 				fLink->Read<BRect>(&updateRect);
1176 
1177 				// read tokens for views that need to be drawn
1178 				// NOTE: we need to read the tokens completely
1179 				// first, or other calls would likely mess up the
1180 				// data in the link.
1181 				BList tokens(20);
1182 				int32 token;
1183 				status_t error = fLink->Read<int32>(&token);
1184 				while (error >= B_OK && token != B_NULL_TOKEN) {
1185 					tokens.AddItem((void*)token);
1186 					error = fLink->Read<int32>(&token);
1187 				}
1188 				// draw
1189 				int32 count = tokens.CountItems();
1190 				for (int32 i = 0; i < count; i++) {
1191 //bigtime_t drawStart = system_time();
1192 					if (BView* view = _FindView((int32)tokens.ItemAtFast(i)))
1193 						view->_Draw(updateRect);
1194 					else
1195 						printf("_UPDATE_ - didn't find view by token: %ld\n", (int32)tokens.ItemAtFast(i));
1196 //drawTime += system_time() - drawStart;
1197 				}
1198 				// TODO: the tokens are actually hirachically sorted,
1199 				// so traversing the list in revers and calling
1200 				// child->DrawAfterChildren would actually work correctly,
1201 				// only that drawing outside a view is not yet supported
1202 				// in the app_server.
1203 //printf("  %ld views drawn, total Draw() time: %lld\n", count, drawTime);
1204 			}
1205 
1206 			fLink->StartMessage(AS_END_UPDATE);
1207 			fLink->Flush();
1208 			fInTransaction = false;
1209 
1210 //printf("BWindow(%s) - UPDATE took %lld usecs\n", Title(), system_time() - now);
1211 			break;
1212 		}
1213 
1214 		case _MENUS_DONE_:
1215 			MenusEnded();
1216 			break;
1217 
1218 		// These two are obviously some kind of old scripting messages
1219 		// this is NOT an app_server message and we have to be cautious
1220 		case B_WINDOW_MOVE_BY:
1221 		{
1222 			BPoint offset;
1223 			if (msg->FindPoint("data", &offset) == B_OK)
1224 				MoveBy(offset.x, offset.y);
1225 			else
1226 				msg->SendReply(B_MESSAGE_NOT_UNDERSTOOD);
1227 			break;
1228 		}
1229 
1230 		// this is NOT an app_server message and we have to be cautious
1231 		case B_WINDOW_MOVE_TO:
1232 		{
1233 			BPoint origin;
1234 			if (msg->FindPoint("data", &origin) == B_OK)
1235 				MoveTo(origin);
1236 			else
1237 				msg->SendReply(B_MESSAGE_NOT_UNDERSTOOD);
1238 			break;
1239 		}
1240 
1241 		case B_LAYOUT_WINDOW:
1242 		{
1243 			if (fFlags & B_AUTO_UPDATE_SIZE_LIMITS) {
1244 				// Get min/max constraints of the top view and enforce window
1245 				// size limits respectively.
1246 				BSize minSize = fTopView->MinSize();
1247 				BSize maxSize = fTopView->MaxSize();
1248 				SetSizeLimits(minSize.width, maxSize.width,
1249 					minSize.height, maxSize.height);
1250 			}
1251 
1252 			// do the actual layout
1253 			fTopView->Layout(false);
1254 
1255 			break;
1256 		}
1257 
1258 		default:
1259 			BLooper::DispatchMessage(msg, target);
1260 			break;
1261 	}
1262 }
1263 
1264 
1265 void
1266 BWindow::FrameMoved(BPoint new_position)
1267 {
1268 	// does nothing
1269 	// Hook function
1270 }
1271 
1272 
1273 void
1274 BWindow::FrameResized(float new_width, float new_height)
1275 {
1276 	// does nothing
1277 	// Hook function
1278 }
1279 
1280 
1281 void
1282 BWindow::WorkspacesChanged(uint32 old_ws, uint32 new_ws)
1283 {
1284 	// does nothing
1285 	// Hook function
1286 }
1287 
1288 
1289 void
1290 BWindow::WorkspaceActivated(int32 ws, bool state)
1291 {
1292 	// does nothing
1293 	// Hook function
1294 }
1295 
1296 
1297 void
1298 BWindow::MenusBeginning()
1299 {
1300 	// does nothing
1301 	// Hook function
1302 }
1303 
1304 
1305 void
1306 BWindow::MenusEnded()
1307 {
1308 	// does nothing
1309 	// Hook function
1310 }
1311 
1312 
1313 void
1314 BWindow::SetSizeLimits(float minWidth, float maxWidth,
1315 	float minHeight, float maxHeight)
1316 {
1317 	if (minWidth > maxWidth || minHeight > maxHeight)
1318 		return;
1319 
1320 	if (!Lock())
1321 		return;
1322 
1323 	fLink->StartMessage(AS_SET_SIZE_LIMITS);
1324 	fLink->Attach<float>(minWidth);
1325 	fLink->Attach<float>(maxWidth);
1326 	fLink->Attach<float>(minHeight);
1327 	fLink->Attach<float>(maxHeight);
1328 
1329 	int32 code;
1330 	if (fLink->FlushWithReply(code) == B_OK
1331 		&& code == B_OK) {
1332 		// read the values that were really enforced on
1333 		// the server side (the window frame could have
1334 		// been changed, too)
1335 		fLink->Read<BRect>(&fFrame);
1336 		fLink->Read<float>(&fMinWidth);
1337 		fLink->Read<float>(&fMaxWidth);
1338 		fLink->Read<float>(&fMinHeight);
1339 		fLink->Read<float>(&fMaxHeight);
1340 
1341 		_AdoptResize();
1342 			// TODO: the same has to be done for SetLook() (that can alter
1343 			//		the size limits, and hence, the size of the window
1344 	}
1345 	Unlock();
1346 }
1347 
1348 
1349 void
1350 BWindow::GetSizeLimits(float *minWidth, float *maxWidth,
1351 	float *minHeight, float *maxHeight)
1352 {
1353 	// TODO: What about locking?!?
1354 	*minHeight = fMinHeight;
1355 	*minWidth = fMinWidth;
1356 	*maxHeight = fMaxHeight;
1357 	*maxWidth = fMaxWidth;
1358 }
1359 
1360 
1361 status_t
1362 BWindow::SetDecoratorSettings(const BMessage& settings)
1363 {
1364 	// flatten the given settings into a buffer and send
1365 	// it to the app_server to apply the settings to the
1366 	// decorator
1367 
1368 	int32 size = settings.FlattenedSize();
1369 	char buffer[size];
1370 	status_t ret = settings.Flatten(buffer, size);
1371 
1372 	if (ret < B_OK)
1373 		return ret;
1374 
1375 	if (!Lock())
1376 		return B_ERROR;
1377 
1378 	ret = fLink->StartMessage(AS_SET_DECORATOR_SETTINGS);
1379 
1380 	if (ret == B_OK)
1381 		ret = fLink->Attach<int32>(size);
1382 
1383 	if (ret == B_OK)
1384 		ret = fLink->Attach(buffer, size);
1385 
1386 	if (ret == B_OK)
1387 		ret = fLink->Flush();
1388 
1389 	Unlock();
1390 
1391 	return ret;
1392 }
1393 
1394 
1395 status_t
1396 BWindow::GetDecoratorSettings(BMessage* settings) const
1397 {
1398 	// read a flattened settings message from the app_server
1399 	// and put it into settings
1400 
1401 	if (!const_cast<BWindow*>(this)->Lock())
1402 		return B_ERROR;
1403 
1404 	status_t ret = fLink->StartMessage(AS_GET_DECORATOR_SETTINGS);
1405 
1406 	if (ret == B_OK) {
1407 		int32 code;
1408 		ret = fLink->FlushWithReply(code);
1409 		if (ret == B_OK && code != B_OK)
1410 			ret = code;
1411 	}
1412 
1413 	if (ret == B_OK) {
1414 		int32 size;
1415 		ret = fLink->Read<int32>(&size);
1416 		if (ret == B_OK) {
1417 			char buffer[size];
1418 			ret = fLink->Read(buffer, size);
1419 			if (ret == B_OK) {
1420 				ret = settings->Unflatten(buffer);
1421 			}
1422 		}
1423 	}
1424 
1425 	const_cast<BWindow*>(this)->Unlock();
1426 
1427 	return ret;
1428 }
1429 
1430 
1431 void
1432 BWindow::SetZoomLimits(float maxWidth, float maxHeight)
1433 {
1434 	// TODO: What about locking?!?
1435 	if (maxWidth > fMaxWidth)
1436 		maxWidth = fMaxWidth;
1437 	else
1438 		fMaxZoomWidth = maxWidth;
1439 
1440 	if (maxHeight > fMaxHeight)
1441 		maxHeight = fMaxHeight;
1442 	else
1443 		fMaxZoomHeight = maxHeight;
1444 }
1445 
1446 
1447 void
1448 BWindow::Zoom(BPoint leftTop, float width, float height)
1449 {
1450 	// the default implementation of this hook function
1451 	// just does the obvious:
1452 	MoveTo(leftTop);
1453 	ResizeTo(width, height);
1454 }
1455 
1456 
1457 void
1458 BWindow::Zoom()
1459 {
1460 	// TODO: What about locking?!?
1461 	/*
1462 		from BeBook:
1463 		However, if the window's rectangle already matches these "zoom" dimensions
1464 		(give or take a few pixels), Zoom() passes the window's previous
1465 		("non-zoomed") size and location. (??????)
1466 	*/
1467 
1468 	/* From BeBook:
1469 		The dimensions that non-virtual Zoom() passes to hook Zoom() are deduced from
1470 		the smallest of three rectangles:
1471 	*/
1472 
1473 	// TODO: make more elaborate (figure out this window's
1474 	// tab height and border width... maybe ask app_server)
1475 	float borderWidth = 5.0;
1476 	float tabHeight = 26.0;
1477 
1478 	// 1) the rectangle defined by SetZoomLimits(),
1479 	float zoomedWidth = fMaxZoomWidth;
1480 	float zoomedHeight = fMaxZoomHeight;
1481 
1482 	// 2) the rectangle defined by SetSizeLimits()
1483 	if (fMaxWidth < zoomedWidth)
1484 		zoomedWidth = fMaxWidth;
1485 	if (fMaxHeight < zoomedHeight)
1486 		zoomedHeight = fMaxHeight;
1487 
1488 	// 3) the screen rectangle
1489 	BScreen screen(this);
1490 	float screenWidth = screen.Frame().Width() - 2 * borderWidth;
1491 	float screenHeight = screen.Frame().Height() - (borderWidth + tabHeight);
1492 	if (screenWidth < zoomedWidth)
1493 		zoomedWidth = screenWidth;
1494 	if (screenHeight < zoomedHeight)
1495 		zoomedHeight = screenHeight;
1496 
1497 	BPoint zoomedLeftTop = screen.Frame().LeftTop() + BPoint(borderWidth, tabHeight);
1498 
1499 	// UN-ZOOM:
1500 	if (fPreviousFrame.IsValid()
1501 		// NOTE: don't check for fFrame.LeftTop() == zoomedLeftTop
1502 		// -> makes it easier on the user to get a window back into place
1503 		&& fFrame.Width() == zoomedWidth
1504 		&& fFrame.Height() == zoomedHeight) {
1505 		// already zoomed!
1506 		Zoom(fPreviousFrame.LeftTop(), fPreviousFrame.Width(), fPreviousFrame.Height());
1507 		return;
1508 	}
1509 
1510 	// ZOOM:
1511 
1512 	// remember fFrame for later "unzooming"
1513 	fPreviousFrame = fFrame;
1514 
1515 	Zoom(zoomedLeftTop, zoomedWidth, zoomedHeight);
1516 }
1517 
1518 
1519 void
1520 BWindow::ScreenChanged(BRect screen_size, color_space depth)
1521 {
1522 	// Hook function
1523 }
1524 
1525 
1526 void
1527 BWindow::SetPulseRate(bigtime_t rate)
1528 {
1529 	// TODO: What about locking?!?
1530 	if (rate < 0 || (rate == fPulseRate && !(rate == 0 ^ fPulseRunner == NULL)))
1531 		return;
1532 
1533 	fPulseRate = rate;
1534 
1535 	if (rate > 0) {
1536 		if (fPulseRunner == NULL) {
1537 			BMessage message(B_PULSE);
1538 			fPulseRunner = new BMessageRunner(BMessenger(this),
1539 				&message, rate);
1540 		} else {
1541 			fPulseRunner->SetInterval(rate);
1542 		}
1543 	} else {
1544 		// rate == 0
1545 		delete fPulseRunner;
1546 		fPulseRunner = NULL;
1547 	}
1548 }
1549 
1550 
1551 bigtime_t
1552 BWindow::PulseRate() const
1553 {
1554 	return fPulseRate;
1555 }
1556 
1557 
1558 void
1559 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMenuItem *item)
1560 {
1561 	Shortcut* shortcut = new Shortcut(key, modifiers, item);
1562 
1563 	// removes the shortcut if it already exists!
1564 	RemoveShortcut(key, modifiers);
1565 
1566 	fShortcuts.AddItem(shortcut);
1567 }
1568 
1569 
1570 void
1571 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage *message)
1572 {
1573 	AddShortcut(key, modifiers, message, this);
1574 }
1575 
1576 
1577 void
1578 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage *message, BHandler *target)
1579 {
1580 	if (message == NULL)
1581 		return;
1582 
1583 	Shortcut* shortcut = new Shortcut(key, modifiers, message, target);
1584 
1585 	// removes the shortcut if it already exists!
1586 	RemoveShortcut(key, modifiers);
1587 
1588 	fShortcuts.AddItem(shortcut);
1589 }
1590 
1591 
1592 void
1593 BWindow::RemoveShortcut(uint32 key, uint32 modifiers)
1594 {
1595 	Shortcut* shortcut = _FindShortcut(key, modifiers);
1596 	if (shortcut != NULL) {
1597 		fShortcuts.RemoveItem(shortcut);
1598 		delete shortcut;
1599 	} else if ((key == 'q' || key == 'Q') && modifiers == B_COMMAND_KEY) {
1600 		// the quit shortcut is a fake shortcut
1601 		fNoQuitShortcut = true;
1602 	}
1603 }
1604 
1605 
1606 BButton *
1607 BWindow::DefaultButton() const
1608 {
1609 	// TODO: What about locking?!?
1610 	return fDefaultButton;
1611 }
1612 
1613 
1614 void
1615 BWindow::SetDefaultButton(BButton *button)
1616 {
1617 	// TODO: What about locking?!?
1618 	if (fDefaultButton == button)
1619 		return;
1620 
1621 	if (fDefaultButton != NULL) {
1622 		// tell old button it's no longer the default one
1623 		BButton *oldDefault = fDefaultButton;
1624 		oldDefault->MakeDefault(false);
1625 		oldDefault->Invalidate();
1626 	}
1627 
1628 	fDefaultButton = button;
1629 
1630 	if (button != NULL) {
1631 		// notify new default button
1632 		fDefaultButton->MakeDefault(true);
1633 		fDefaultButton->Invalidate();
1634 	}
1635 }
1636 
1637 
1638 bool
1639 BWindow::NeedsUpdate() const
1640 {
1641 	if (!const_cast<BWindow *>(this)->Lock())
1642 		return false;
1643 
1644 	fLink->StartMessage(AS_NEEDS_UPDATE);
1645 
1646 	int32 code = B_ERROR;
1647 	fLink->FlushWithReply(code);
1648 
1649 	const_cast<BWindow *>(this)->Unlock();
1650 
1651 	return code == B_OK;
1652 }
1653 
1654 
1655 void
1656 BWindow::UpdateIfNeeded()
1657 {
1658 	// works only from the window thread
1659 	if (find_thread(NULL) != Thread())
1660 		return;
1661 
1662 	// if the queue is already locked we are called recursivly
1663 	// from our own dispatched update message
1664 	if (((const BMessageQueue *)MessageQueue())->IsLocked())
1665 		return;
1666 
1667 	if (!Lock())
1668 		return;
1669 
1670 	// make sure all requests that would cause an update have
1671 	// arrived at the server
1672 	Sync();
1673 
1674 	// Since we're blocking the event loop, we need to retrieve
1675 	// all messages that are pending on the port.
1676 	_DequeueAll();
1677 
1678 	BMessageQueue *queue = MessageQueue();
1679 	queue->Lock();
1680 
1681 	// First process and remove any _UPDATE_ message in the queue
1682 	// With the current design, there can only be one at a time
1683 
1684 	BMessage *msg;
1685 	for (int32 i = 0; (msg = queue->FindMessage(i)) != NULL; i++) {
1686 		if (msg->what == _UPDATE_) {
1687 			BWindow::DispatchMessage(msg, this);
1688 				// we need to make sure that no overridden method is called
1689 				// here; for BWindow::DispatchMessage() we now exactly what
1690 				// will happen
1691 			queue->RemoveMessage(msg);
1692 			delete msg;
1693 			break;
1694 			// NOTE: "i" would have to be decreased if there were
1695 			// multiple _UPDATE_ messages and we would not break!
1696 		}
1697 	}
1698 
1699 	queue->Unlock();
1700 	Unlock();
1701 }
1702 
1703 
1704 BView *
1705 BWindow::FindView(const char *viewName) const
1706 {
1707 	BAutolock locker(const_cast<BWindow*>(this));
1708 	if (!locker.IsLocked())
1709 		return NULL;
1710 
1711 	return fTopView->FindView(viewName);
1712 }
1713 
1714 
1715 BView *
1716 BWindow::FindView(BPoint point) const
1717 {
1718 	BAutolock locker(const_cast<BWindow*>(this));
1719 	if (!locker.IsLocked())
1720 		return NULL;
1721 
1722 	// point is assumed to be in window coordinates,
1723 	// fTopView has same bounds as window
1724 	return _FindView(fTopView, point);
1725 }
1726 
1727 
1728 BView *
1729 BWindow::CurrentFocus() const
1730 {
1731 	return fFocus;
1732 }
1733 
1734 
1735 void
1736 BWindow::Activate(bool active)
1737 {
1738 	if (!Lock())
1739 		return;
1740 
1741 	if (!IsHidden()) {
1742 		fLink->StartMessage(AS_ACTIVATE_WINDOW);
1743 		fLink->Attach<bool>(active);
1744 		fLink->Flush();
1745 	}
1746 
1747 	Unlock();
1748 }
1749 
1750 
1751 void
1752 BWindow::WindowActivated(bool state)
1753 {
1754 	// hook function
1755 	// does nothing
1756 }
1757 
1758 
1759 void
1760 BWindow::ConvertToScreen(BPoint *point) const
1761 {
1762 	point->x += fFrame.left;
1763 	point->y += fFrame.top;
1764 }
1765 
1766 
1767 BPoint
1768 BWindow::ConvertToScreen(BPoint point) const
1769 {
1770 	return point + fFrame.LeftTop();
1771 }
1772 
1773 
1774 void
1775 BWindow::ConvertFromScreen(BPoint *point) const
1776 {
1777 	point->x -= fFrame.left;
1778 	point->y -= fFrame.top;
1779 }
1780 
1781 
1782 BPoint
1783 BWindow::ConvertFromScreen(BPoint point) const
1784 {
1785 	return point - fFrame.LeftTop();
1786 }
1787 
1788 
1789 void
1790 BWindow::ConvertToScreen(BRect *rect) const
1791 {
1792 	rect->OffsetBy(fFrame.LeftTop());
1793 }
1794 
1795 
1796 BRect
1797 BWindow::ConvertToScreen(BRect rect) const
1798 {
1799 	return rect.OffsetByCopy(fFrame.LeftTop());
1800 }
1801 
1802 
1803 void
1804 BWindow::ConvertFromScreen(BRect* rect) const
1805 {
1806 	rect->OffsetBy(-fFrame.left, -fFrame.top);
1807 }
1808 
1809 
1810 BRect
1811 BWindow::ConvertFromScreen(BRect rect) const
1812 {
1813 	return rect.OffsetByCopy(-fFrame.left, -fFrame.top);
1814 }
1815 
1816 
1817 bool
1818 BWindow::IsMinimized() const
1819 {
1820 	// Hiding takes precendence over minimization!!!
1821 	if (IsHidden())
1822 		return false;
1823 
1824 	return fMinimized;
1825 }
1826 
1827 
1828 BRect
1829 BWindow::Bounds() const
1830 {
1831 	return BRect(0, 0, fFrame.Width(), fFrame.Height());
1832 }
1833 
1834 
1835 BRect
1836 BWindow::Frame() const
1837 {
1838 	return fFrame;
1839 }
1840 
1841 
1842 const char *
1843 BWindow::Title() const
1844 {
1845 	return fTitle;
1846 }
1847 
1848 
1849 void
1850 BWindow::SetTitle(const char *title)
1851 {
1852 	if (title == NULL)
1853 		title = "";
1854 
1855 	free(fTitle);
1856 	fTitle = strdup(title);
1857 
1858 	_SetName(title);
1859 
1860 	// we notify the app_server so we can actually see the change
1861 	if (Lock()) {
1862 		fLink->StartMessage(AS_SET_WINDOW_TITLE);
1863 		fLink->AttachString(fTitle);
1864 		fLink->Flush();
1865 		Unlock();
1866 	}
1867 }
1868 
1869 
1870 bool
1871 BWindow::IsActive() const
1872 {
1873 	return fActive;
1874 }
1875 
1876 
1877 void
1878 BWindow::SetKeyMenuBar(BMenuBar *bar)
1879 {
1880 	fKeyMenuBar = bar;
1881 }
1882 
1883 
1884 BMenuBar *
1885 BWindow::KeyMenuBar() const
1886 {
1887 	return fKeyMenuBar;
1888 }
1889 
1890 
1891 bool
1892 BWindow::IsModal() const
1893 {
1894 	return fFeel == B_MODAL_SUBSET_WINDOW_FEEL
1895 		|| fFeel == B_MODAL_APP_WINDOW_FEEL
1896 		|| fFeel == B_MODAL_ALL_WINDOW_FEEL;
1897 }
1898 
1899 
1900 bool
1901 BWindow::IsFloating() const
1902 {
1903 	return fFeel == B_FLOATING_SUBSET_WINDOW_FEEL
1904 		|| fFeel == B_FLOATING_APP_WINDOW_FEEL
1905 		|| fFeel == B_FLOATING_ALL_WINDOW_FEEL;
1906 }
1907 
1908 
1909 status_t
1910 BWindow::AddToSubset(BWindow *window)
1911 {
1912 	if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL
1913 		|| (fFeel != B_MODAL_SUBSET_WINDOW_FEEL
1914 			&& fFeel != B_FLOATING_SUBSET_WINDOW_FEEL))
1915 		return B_BAD_VALUE;
1916 
1917 	if (!Lock())
1918 		return B_ERROR;
1919 
1920 	status_t status = B_ERROR;
1921 	fLink->StartMessage(AS_ADD_TO_SUBSET);
1922 	fLink->Attach<int32>(_get_object_token_(window));
1923 	fLink->FlushWithReply(status);
1924 
1925 	Unlock();
1926 
1927 	return status;
1928 }
1929 
1930 
1931 status_t
1932 BWindow::RemoveFromSubset(BWindow *window)
1933 {
1934 	if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL
1935 		|| (fFeel != B_MODAL_SUBSET_WINDOW_FEEL
1936 			&& fFeel != B_FLOATING_SUBSET_WINDOW_FEEL))
1937 		return B_BAD_VALUE;
1938 
1939 	if (!Lock())
1940 		return B_ERROR;
1941 
1942 	status_t status = B_ERROR;
1943 	fLink->StartMessage(AS_REMOVE_FROM_SUBSET);
1944 	fLink->Attach<int32>(_get_object_token_(window));
1945 	fLink->FlushWithReply(status);
1946 
1947 	Unlock();
1948 
1949 	return status;
1950 }
1951 
1952 
1953 status_t
1954 BWindow::Perform(perform_code d, void *arg)
1955 {
1956 	return BLooper::Perform(d, arg);
1957 }
1958 
1959 
1960 status_t
1961 BWindow::SetType(window_type type)
1962 {
1963 	window_look look;
1964 	window_feel feel;
1965 	_DecomposeType(type, &look, &feel);
1966 
1967 	status_t status = SetLook(look);
1968 	if (status == B_OK)
1969 		status = SetFeel(feel);
1970 
1971 	return status;
1972 }
1973 
1974 
1975 window_type
1976 BWindow::Type() const
1977 {
1978 	return _ComposeType(fLook, fFeel);
1979 }
1980 
1981 
1982 status_t
1983 BWindow::SetLook(window_look look)
1984 {
1985 	BAutolock locker(this);
1986 	if (!locker.IsLocked())
1987 		return B_BAD_VALUE;
1988 
1989 	fLink->StartMessage(AS_SET_LOOK);
1990 	fLink->Attach<int32>((int32)look);
1991 
1992 	status_t status = B_ERROR;
1993 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
1994 		fLook = look;
1995 
1996 	// TODO: this could have changed the window size, and thus, we
1997 	//	need to get it from the server (and call _AdoptResize()).
1998 
1999 	return status;
2000 }
2001 
2002 
2003 window_look
2004 BWindow::Look() const
2005 {
2006 	return fLook;
2007 }
2008 
2009 
2010 status_t
2011 BWindow::SetFeel(window_feel feel)
2012 {
2013 	BAutolock locker(this);
2014 	if (!locker.IsLocked())
2015 		return B_BAD_VALUE;
2016 
2017 	fLink->StartMessage(AS_SET_FEEL);
2018 	fLink->Attach<int32>((int32)feel);
2019 
2020 	status_t status = B_ERROR;
2021 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2022 		fFeel = feel;
2023 
2024 	return status;
2025 }
2026 
2027 
2028 window_feel
2029 BWindow::Feel() const
2030 {
2031 	return fFeel;
2032 }
2033 
2034 
2035 status_t
2036 BWindow::SetFlags(uint32 flags)
2037 {
2038 	BAutolock locker(this);
2039 	if (!locker.IsLocked())
2040 		return B_BAD_VALUE;
2041 
2042 	fLink->StartMessage(AS_SET_FLAGS);
2043 	fLink->Attach<uint32>(flags);
2044 
2045 	int32 status = B_ERROR;
2046 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2047 		fFlags = flags;
2048 
2049 	return status;
2050 }
2051 
2052 
2053 uint32
2054 BWindow::Flags() const
2055 {
2056 	return fFlags;
2057 }
2058 
2059 
2060 status_t
2061 BWindow::SetWindowAlignment(window_alignment mode,
2062 	int32 h, int32 hOffset, int32 width, int32 widthOffset,
2063 	int32 v, int32 vOffset, int32 height, int32 heightOffset)
2064 {
2065 	if ((mode & (B_BYTE_ALIGNMENT | B_PIXEL_ALIGNMENT)) == 0
2066 		|| (hOffset >= 0 && hOffset <= h)
2067 		|| (vOffset >= 0 && vOffset <= v)
2068 		|| (widthOffset >= 0 && widthOffset <= width)
2069 		|| (heightOffset >= 0 && heightOffset <= height))
2070 		return B_BAD_VALUE;
2071 
2072 	// TODO: test if hOffset = 0 and set it to 1 if true.
2073 
2074 	if (!Lock())
2075 		return B_ERROR;
2076 
2077 	fLink->StartMessage(AS_SET_ALIGNMENT);
2078 	fLink->Attach<int32>((int32)mode);
2079 	fLink->Attach<int32>(h);
2080 	fLink->Attach<int32>(hOffset);
2081 	fLink->Attach<int32>(width);
2082 	fLink->Attach<int32>(widthOffset);
2083 	fLink->Attach<int32>(v);
2084 	fLink->Attach<int32>(vOffset);
2085 	fLink->Attach<int32>(height);
2086 	fLink->Attach<int32>(heightOffset);
2087 
2088 	status_t status = B_ERROR;
2089 	fLink->FlushWithReply(status);
2090 
2091 	Unlock();
2092 
2093 	return status;
2094 }
2095 
2096 
2097 status_t
2098 BWindow::GetWindowAlignment(window_alignment *mode,
2099 	int32 *h, int32 *hOffset, int32 *width, int32 *widthOffset,
2100 	int32 *v, int32 *vOffset, int32 *height, int32 *heightOffset) const
2101 {
2102 	if (!const_cast<BWindow *>(this)->Lock())
2103 		return B_ERROR;
2104 
2105 	fLink->StartMessage(AS_GET_ALIGNMENT);
2106 
2107 	status_t status;
2108 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK) {
2109 		fLink->Read<int32>((int32 *)mode);
2110 		fLink->Read<int32>(h);
2111 		fLink->Read<int32>(hOffset);
2112 		fLink->Read<int32>(width);
2113 		fLink->Read<int32>(widthOffset);
2114 		fLink->Read<int32>(v);
2115 		fLink->Read<int32>(hOffset);
2116 		fLink->Read<int32>(height);
2117 		fLink->Read<int32>(heightOffset);
2118 	}
2119 
2120 	const_cast<BWindow *>(this)->Unlock();
2121 	return status;
2122 }
2123 
2124 
2125 uint32
2126 BWindow::Workspaces() const
2127 {
2128 	if (!const_cast<BWindow *>(this)->Lock())
2129 		return 0;
2130 
2131 	uint32 workspaces = 0;
2132 
2133 	fLink->StartMessage(AS_GET_WORKSPACES);
2134 
2135 	status_t status;
2136 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2137 		fLink->Read<uint32>(&workspaces);
2138 
2139 	const_cast<BWindow *>(this)->Unlock();
2140 	return workspaces;
2141 }
2142 
2143 
2144 void
2145 BWindow::SetWorkspaces(uint32 workspaces)
2146 {
2147 	// TODO: don't forget about Tracker's background window.
2148 	if (fFeel != B_NORMAL_WINDOW_FEEL)
2149 		return;
2150 
2151 	if (Lock()) {
2152 		fLink->StartMessage(AS_SET_WORKSPACES);
2153 		fLink->Attach<uint32>(workspaces);
2154 		fLink->Flush();
2155 		Unlock();
2156 	}
2157 }
2158 
2159 
2160 BView *
2161 BWindow::LastMouseMovedView() const
2162 {
2163 	return fLastMouseMovedView;
2164 }
2165 
2166 
2167 void
2168 BWindow::MoveBy(float dx, float dy)
2169 {
2170 	if ((dx == 0.0 && dy == 0.0) || !Lock())
2171 		return;
2172 
2173 	fLink->StartMessage(AS_WINDOW_MOVE);
2174 	fLink->Attach<float>(dx);
2175 	fLink->Attach<float>(dy);
2176 
2177 	status_t status;
2178 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2179 		fFrame.OffsetBy(dx, dy);
2180 
2181 	Unlock();
2182 }
2183 
2184 
2185 void
2186 BWindow::MoveTo(BPoint point)
2187 {
2188 	if (!Lock())
2189 		return;
2190 
2191 	point.x = roundf(point.x);
2192 	point.y = roundf(point.y);
2193 
2194 	if (fFrame.left != point.x || fFrame.top != point.y) {
2195 		float xOffset = point.x - fFrame.left;
2196 		float yOffset = point.y - fFrame.top;
2197 
2198 		MoveBy(xOffset, yOffset);
2199 	}
2200 
2201 	Unlock();
2202 }
2203 
2204 
2205 void
2206 BWindow::MoveTo(float x, float y)
2207 {
2208 	MoveTo(BPoint(x, y));
2209 }
2210 
2211 
2212 void
2213 BWindow::ResizeBy(float dx, float dy)
2214 {
2215 	if (!Lock())
2216 		return;
2217 
2218 	dx = roundf(dx);
2219 	dy = roundf(dy);
2220 
2221 	// stay in minimum & maximum frame limits
2222 	if (fFrame.Width() + dx < fMinWidth)
2223 		dx = fMinWidth - fFrame.Width();
2224 	if (fFrame.Width() + dx > fMaxWidth)
2225 		dx = fMaxWidth - fFrame.Width();
2226 	if (fFrame.Height() + dy < fMinHeight)
2227 		dy = fMinHeight - fFrame.Height();
2228 	if (fFrame.Height() + dy > fMaxHeight)
2229 		dy = fMaxHeight - fFrame.Height();
2230 
2231 	if (dx != 0.0 || dy != 0.0) {
2232 		fLink->StartMessage(AS_WINDOW_RESIZE);
2233 		fLink->Attach<float>(dx);
2234 		fLink->Attach<float>(dy);
2235 
2236 		status_t status;
2237 		if (fLink->FlushWithReply(status) == B_OK && status == B_OK) {
2238 			fFrame.SetRightBottom(fFrame.RightBottom() + BPoint(dx, dy));
2239 			_AdoptResize();
2240 		}
2241 	}
2242 	Unlock();
2243 }
2244 
2245 
2246 void
2247 BWindow::ResizeTo(float width, float height)
2248 {
2249 	if (Lock()) {
2250 		ResizeBy(width - fFrame.Width(), height - fFrame.Height());
2251 		Unlock();
2252 	}
2253 }
2254 
2255 
2256 void
2257 BWindow::Show()
2258 {
2259 	if (!fRunCalled) {
2260 		// this is the fist time Show() is called, which implicitly runs the looper
2261 		if (fLink->SenderPort() < B_OK) {
2262 			// We don't have valid app_server connection; there is no point
2263 			// in starting our looper
2264 			fThread = B_ERROR;
2265 			return;
2266 		} else
2267 			Run();
2268 	}
2269 
2270 	if (Lock()) {
2271 		fShowLevel++;
2272 
2273 		if (fShowLevel == 1) {
2274 			STRACE(("BWindow(%s): sending AS_SHOW_WINDOW message...\n", Name()));
2275 			fLink->StartMessage(AS_SHOW_WINDOW);
2276 			fLink->Flush();
2277 		}
2278 
2279 		Unlock();
2280 	}
2281 }
2282 
2283 
2284 void
2285 BWindow::Hide()
2286 {
2287 	if (!Lock())
2288 		return;
2289 
2290 	if (--fShowLevel == 0) {
2291 		fLink->StartMessage(AS_HIDE_WINDOW);
2292 		fLink->Flush();
2293 	}
2294 
2295 	Unlock();
2296 }
2297 
2298 
2299 bool
2300 BWindow::IsHidden() const
2301 {
2302 	return fShowLevel <= 0;
2303 }
2304 
2305 
2306 bool
2307 BWindow::QuitRequested()
2308 {
2309 	return BLooper::QuitRequested();
2310 }
2311 
2312 
2313 thread_id
2314 BWindow::Run()
2315 {
2316 	return BLooper::Run();
2317 }
2318 
2319 
2320 void
2321 BWindow::SetLayout(BLayout* layout)
2322 {
2323 	fTopView->SetLayout(layout);
2324 }
2325 
2326 
2327 BLayout*
2328 BWindow::GetLayout() const
2329 {
2330 	return fTopView->GetLayout();
2331 }
2332 
2333 
2334 void
2335 BWindow::InvalidateLayout(bool descendants)
2336 {
2337 	fTopView->InvalidateLayout(descendants);
2338 }
2339 
2340 
2341 status_t
2342 BWindow::GetSupportedSuites(BMessage *data)
2343 {
2344 	if (data == NULL)
2345 		return B_BAD_VALUE;
2346 
2347 	status_t status = data->AddString("suites", "suite/vnd.Be-window");
2348 	if (status == B_OK) {
2349 		BPropertyInfo propertyInfo(sWindowPropInfo, sWindowValueInfo);
2350 
2351 		status = data->AddFlat("messages", &propertyInfo);
2352 		if (status == B_OK)
2353 			status = BLooper::GetSupportedSuites(data);
2354 	}
2355 
2356 	return status;
2357 }
2358 
2359 
2360 BHandler *
2361 BWindow::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier,
2362 	int32 what,	const char *property)
2363 {
2364 	if (msg->what == B_WINDOW_MOVE_BY
2365 		|| msg->what == B_WINDOW_MOVE_TO)
2366 		return this;
2367 
2368 	BPropertyInfo propertyInfo(sWindowPropInfo);
2369 	if (propertyInfo.FindMatch(msg, index, specifier, what, property) >= 0) {
2370 		if (!strcmp(property, "View")) {
2371 			// we will NOT pop the current specifier
2372 			return fTopView;
2373 		} else if (!strcmp(property, "MenuBar")) {
2374 			if (fKeyMenuBar) {
2375 				msg->PopSpecifier();
2376 				return fKeyMenuBar;
2377 			} else {
2378 				BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
2379 				replyMsg.AddInt32("error", B_NAME_NOT_FOUND);
2380 				replyMsg.AddString("message", "This window doesn't have a main MenuBar");
2381 				msg->SendReply(&replyMsg);
2382 				return NULL;
2383 			}
2384 		} else
2385 			return this;
2386 	}
2387 
2388 	return BLooper::ResolveSpecifier(msg, index, specifier, what, property);
2389 }
2390 
2391 
2392 //	#pragma mark - Private Methods
2393 
2394 
2395 void
2396 BWindow::_InitData(BRect frame, const char* title, window_look look,
2397 	window_feel feel, uint32 flags,	uint32 workspace, int32 bitmapToken)
2398 {
2399 	STRACE(("BWindow::InitData()\n"));
2400 
2401 	if (be_app == NULL) {
2402 		debugger("You need a valid BApplication object before interacting with the app_server");
2403 		return;
2404 	}
2405 
2406 	frame.left = roundf(frame.left);
2407 	frame.top = roundf(frame.top);
2408 	frame.right = roundf(frame.right);
2409 	frame.bottom = roundf(frame.bottom);
2410 
2411 	fFrame = frame;
2412 
2413 	if (title == NULL)
2414 		title = "";
2415 
2416 	fTitle = strdup(title);
2417 
2418 	_SetName(title);
2419 
2420 	fFeel = feel;
2421 	fLook = look;
2422 	fFlags = flags;
2423 
2424 	fInTransaction = false;
2425 	fActive = false;
2426 	fShowLevel = 0;
2427 
2428 	fTopView = NULL;
2429 	fFocus = NULL;
2430 	fLastMouseMovedView	= NULL;
2431 	fKeyMenuBar = NULL;
2432 	fDefaultButton = NULL;
2433 
2434 	// Shortcut 'Q' is handled in _HandleKeyDown() directly, as its message
2435 	// get sent to the application, and not one of our handlers.
2436 	// It is only installed for non-modal windows, though.
2437 	fNoQuitShortcut = IsModal();
2438 
2439 	if ((fFlags & B_NOT_CLOSABLE) == 0 && !IsModal()) {
2440 		// Modal windows default to non-closable, but you can add the shortcut manually,
2441 		// if a different behaviour is wanted
2442 		AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED));
2443 	}
2444 
2445 	AddShortcut('X', B_COMMAND_KEY, new BMessage(B_CUT), NULL);
2446 	AddShortcut('C', B_COMMAND_KEY, new BMessage(B_COPY), NULL);
2447 	AddShortcut('V', B_COMMAND_KEY, new BMessage(B_PASTE), NULL);
2448 	AddShortcut('A', B_COMMAND_KEY, new BMessage(B_SELECT_ALL), NULL);
2449 
2450 	// Window modifier keys
2451 	AddShortcut('M', B_COMMAND_KEY | B_CONTROL_KEY,
2452 		new BMessage(B_MINIMIZE), NULL);
2453 	AddShortcut('Z', B_COMMAND_KEY | B_CONTROL_KEY,
2454 		new BMessage(B_ZOOM), NULL);
2455 	AddShortcut('H', B_COMMAND_KEY | B_CONTROL_KEY,
2456 		new BMessage(B_HIDE_APPLICATION), NULL);
2457 
2458 	// We set the default pulse rate, but we don't start the pulse
2459 	fPulseRate = 500000;
2460 	fPulseRunner = NULL;
2461 
2462 	// TODO:  see if you can use 'fViewsNeedPulse'
2463 
2464 	fIsFilePanel = false;
2465 
2466 	// TODO: see WHEN is this used!
2467 	fMaskActivated = false;
2468 
2469 	// TODO: see WHEN is this used!
2470 	fWaitingForMenu = false;
2471 	fMenuSem = -1;
2472 
2473 	fMinimized = false;
2474 
2475 	fMaxZoomHeight = 32768.0;
2476 	fMaxZoomWidth = 32768.0;
2477 	fMinHeight = 0.0;
2478 	fMinWidth = 0.0;
2479 	fMaxHeight = 32768.0;
2480 	fMaxWidth = 32768.0;
2481 
2482 	fLastViewToken = B_NULL_TOKEN;
2483 
2484 	// TODO: other initializations!
2485 
2486 	// Create the server-side window
2487 
2488 	port_id receivePort = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY, "w<app_server");
2489 	if (receivePort < B_OK) {
2490 		// TODO: huh?
2491 		debugger("Could not create BWindow's receive port, used for interacting with the app_server!");
2492 		delete this;
2493 		return;
2494 	}
2495 
2496 	STRACE(("BWindow::InitData(): contacting app_server...\n"));
2497 
2498 	// let app_server know that a window has been created.
2499 	fLink = new BPrivate::PortLink(
2500 		BApplication::Private::ServerLink()->SenderPort(), receivePort);
2501 
2502 	{
2503 		BPrivate::AppServerLink lockLink;
2504 			// we're talking to the server application using our own
2505 			// communication channel (fLink) - we better make sure no one
2506 			// interferes by locking that channel (which AppServerLink does
2507 			// implicetly)
2508 
2509 		if (bitmapToken < 0) {
2510 			fLink->StartMessage(AS_CREATE_WINDOW);
2511 		} else {
2512 			fLink->StartMessage(AS_CREATE_OFFSCREEN_WINDOW);
2513 			fLink->Attach<int32>(bitmapToken);
2514 		}
2515 
2516 		fLink->Attach<BRect>(fFrame);
2517 		fLink->Attach<uint32>((uint32)fLook);
2518 		fLink->Attach<uint32>((uint32)fFeel);
2519 		fLink->Attach<uint32>(fFlags);
2520 		fLink->Attach<uint32>(workspace);
2521 		fLink->Attach<int32>(_get_object_token_(this));
2522 		fLink->Attach<port_id>(receivePort);
2523 		fLink->Attach<port_id>(fMsgPort);
2524 		fLink->AttachString(title);
2525 
2526 		port_id sendPort;
2527 		int32 code;
2528 		if (fLink->FlushWithReply(code) == B_OK
2529 			&& code == B_OK
2530 			&& fLink->Read<port_id>(&sendPort) == B_OK) {
2531 			// read the frame size and its limits that were really
2532 			// enforced on the server side
2533 
2534 			fLink->Read<BRect>(&fFrame);
2535 			fLink->Read<float>(&fMinWidth);
2536 			fLink->Read<float>(&fMaxWidth);
2537 			fLink->Read<float>(&fMinHeight);
2538 			fLink->Read<float>(&fMaxHeight);
2539 
2540 			fMaxZoomWidth = fMaxWidth;
2541 			fMaxZoomHeight = fMaxHeight;
2542 		} else
2543 			sendPort = -1;
2544 
2545 		// Redirect our link to the new window connection
2546 		fLink->SetSenderPort(sendPort);
2547 	}
2548 
2549 	STRACE(("Server says that our send port is %ld\n", sendPort));
2550 	STRACE(("Window locked?: %s\n", IsLocked() ? "True" : "False"));
2551 
2552 	_CreateTopView();
2553 }
2554 
2555 
2556 //! Rename the handler and its thread
2557 void
2558 BWindow::_SetName(const char *title)
2559 {
2560 	if (title == NULL)
2561 		title = "";
2562 
2563 	// we will change BWindow's thread name to "w>window title"
2564 
2565 	char threadName[B_OS_NAME_LENGTH];
2566 	strcpy(threadName, "w>");
2567 #ifdef __HAIKU__
2568 	strlcat(threadName, title, B_OS_NAME_LENGTH);
2569 #else
2570 	int32 length = strlen(title);
2571 	length = min_c(length, B_OS_NAME_LENGTH - 3);
2572 	memcpy(threadName + 2, title, length);
2573 	threadName[length + 2] = '\0';
2574 #endif
2575 
2576 	// change the handler's name
2577 	SetName(threadName);
2578 
2579 	// if the message loop has been started...
2580 	if (Thread() >= B_OK)
2581 		rename_thread(Thread(), threadName);
2582 }
2583 
2584 
2585 //!	Reads all pending messages from the window port and put them into the queue.
2586 void
2587 BWindow::_DequeueAll()
2588 {
2589 	//	Get message count from port
2590 	int32 count = port_count(fMsgPort);
2591 
2592 	for (int32 i = 0; i < count; i++) {
2593 		BMessage *message = MessageFromPort(0);
2594 		if (message != NULL)
2595 			fDirectTarget->Queue()->AddMessage(message);
2596 	}
2597 }
2598 
2599 
2600 /*!	This here is an almost complete code duplication to BLooper::task_looper()
2601 	but with some important differences:
2602 	 a)	it uses the _DetermineTarget() method to tell what the later target of
2603 		a message will be, if no explicit target is supplied.
2604 	 b)	it calls _UnpackMessage() and _SanitizeMessage() to duplicate the message
2605 	 	to all of its intended targets, and to add all fields the target would
2606 	 	expect in such a message.
2607 
2608 	This is important because the app_server sends all input events to the
2609 	preferred handler, and expects them to be correctly distributed to their
2610 	intended targets.
2611 */
2612 void
2613 BWindow::task_looper()
2614 {
2615 	STRACE(("info: BWindow::task_looper() started.\n"));
2616 
2617 	// Check that looper is locked (should be)
2618 	AssertLocked();
2619 	Unlock();
2620 
2621 	if (IsLocked())
2622 		debugger("window must not be locked!");
2623 
2624 	while (!fTerminating) {
2625 		// Did we get a message?
2626 		BMessage* msg = MessageFromPort();
2627 		if (msg)
2628 			_AddMessagePriv(msg);
2629 
2630 		//	Get message count from port
2631 		int32 msgCount = port_count(fMsgPort);
2632 		for (int32 i = 0; i < msgCount; ++i) {
2633 			// Read 'count' messages from port (so we will not block)
2634 			// We use zero as our timeout since we know there is stuff there
2635 			msg = MessageFromPort(0);
2636 			// Add messages to queue
2637 			if (msg)
2638 				_AddMessagePriv(msg);
2639 		}
2640 
2641 		bool dispatchNextMessage = true;
2642 		while (!fTerminating && dispatchNextMessage) {
2643 			// Get next message from queue (assign to fLastMessage)
2644 			fLastMessage = fDirectTarget->Queue()->NextMessage();
2645 
2646 			// Lock the looper
2647 			if (!Lock())
2648 				break;
2649 
2650 			if (!fLastMessage) {
2651 				// No more messages: Unlock the looper and terminate the
2652 				// dispatch loop.
2653 				dispatchNextMessage = false;
2654 			} else {
2655 				// Get the target handler
2656 				BMessage::Private messagePrivate(fLastMessage);
2657 				bool usePreferred = messagePrivate.UsePreferredTarget();
2658 				BHandler *handler = NULL;
2659 				bool dropMessage = false;
2660 
2661 				if (usePreferred) {
2662 					handler = PreferredHandler();
2663 					if (handler == NULL)
2664 						handler = this;
2665 				} else {
2666 					gDefaultTokens.GetToken(messagePrivate.GetTarget(),
2667 						B_HANDLER_TOKEN, (void **)&handler);
2668 
2669 					// if this handler doesn't belong to us, we drop the message
2670 					if (handler != NULL && handler->Looper() != this) {
2671 						dropMessage = true;
2672 						handler = NULL;
2673 					}
2674 				}
2675 
2676 				if ((handler == NULL && !dropMessage) || usePreferred)
2677 					handler = _DetermineTarget(fLastMessage, handler);
2678 
2679 				unpack_cookie cookie;
2680 				while (_UnpackMessage(cookie, &fLastMessage, &handler, &usePreferred)) {
2681 					// if there is no target handler, the message is dropped
2682 					if (handler != NULL) {
2683 						_SanitizeMessage(fLastMessage, handler, usePreferred);
2684 
2685 						// Is this a scripting message?
2686 						if (fLastMessage->HasSpecifiers()) {
2687 							int32 index = 0;
2688 							// Make sure the current specifier is kosher
2689 							if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
2690 								handler = resolve_specifier(handler, fLastMessage);
2691 						}
2692 
2693 						if (handler != NULL)
2694 							handler = _TopLevelFilter(fLastMessage, handler);
2695 
2696 						if (handler != NULL)
2697 							DispatchMessage(fLastMessage, handler);
2698 					}
2699 
2700 					// Delete the current message
2701 					delete fLastMessage;
2702 					fLastMessage = NULL;
2703 				}
2704 			}
2705 
2706 			if (fTerminating) {
2707 				// we leave the looper locked when we quit
2708 				return;
2709 			}
2710 
2711 			Unlock();
2712 
2713 			// Are any messages on the port?
2714 			if (port_count(fMsgPort) > 0) {
2715 				// Do outer loop
2716 				dispatchNextMessage = false;
2717 			}
2718 		}
2719 	}
2720 }
2721 
2722 
2723 window_type
2724 BWindow::_ComposeType(window_look look, window_feel feel) const
2725 {
2726 	switch (feel) {
2727 		case B_NORMAL_WINDOW_FEEL:
2728 			switch (look) {
2729 				case B_TITLED_WINDOW_LOOK:
2730 					return B_TITLED_WINDOW;
2731 
2732 				case B_DOCUMENT_WINDOW_LOOK:
2733 					return B_DOCUMENT_WINDOW;
2734 
2735 				case B_BORDERED_WINDOW_LOOK:
2736 					return B_BORDERED_WINDOW;
2737 
2738 				default:
2739 					return B_UNTYPED_WINDOW;
2740 			}
2741 			break;
2742 
2743 		case B_MODAL_APP_WINDOW_FEEL:
2744 			if (look == B_MODAL_WINDOW_LOOK)
2745 				return B_MODAL_WINDOW;
2746 			break;
2747 
2748 		case B_FLOATING_APP_WINDOW_FEEL:
2749 			if (look == B_FLOATING_WINDOW_LOOK)
2750 				return B_FLOATING_WINDOW;
2751 			break;
2752 
2753 		default:
2754 			return B_UNTYPED_WINDOW;
2755 	}
2756 
2757 	return B_UNTYPED_WINDOW;
2758 }
2759 
2760 
2761 void
2762 BWindow::_DecomposeType(window_type type, window_look *_look,
2763 	window_feel *_feel) const
2764 {
2765 	switch (type) {
2766 		case B_DOCUMENT_WINDOW:
2767 			*_look = B_DOCUMENT_WINDOW_LOOK;
2768 			*_feel = B_NORMAL_WINDOW_FEEL;
2769 			break;
2770 
2771 		case B_MODAL_WINDOW:
2772 			*_look = B_MODAL_WINDOW_LOOK;
2773 			*_feel = B_MODAL_APP_WINDOW_FEEL;
2774 			break;
2775 
2776 		case B_FLOATING_WINDOW:
2777 			*_look = B_FLOATING_WINDOW_LOOK;
2778 			*_feel = B_FLOATING_APP_WINDOW_FEEL;
2779 			break;
2780 
2781 		case B_BORDERED_WINDOW:
2782 			*_look = B_BORDERED_WINDOW_LOOK;
2783 			*_feel = B_NORMAL_WINDOW_FEEL;
2784 			break;
2785 
2786 		case B_TITLED_WINDOW:
2787 		case B_UNTYPED_WINDOW:
2788 		default:
2789 			*_look = B_TITLED_WINDOW_LOOK;
2790 			*_feel = B_NORMAL_WINDOW_FEEL;
2791 			break;
2792 	}
2793 }
2794 
2795 
2796 void
2797 BWindow::_CreateTopView()
2798 {
2799 	STRACE(("_CreateTopView(): enter\n"));
2800 
2801 	BRect frame = fFrame.OffsetToCopy(B_ORIGIN);
2802 	fTopView = new BView(frame, "fTopView",
2803 		B_FOLLOW_ALL, B_WILL_DRAW);
2804 	fTopView->fTopLevelView = true;
2805 
2806 	//inhibit check_lock()
2807 	fLastViewToken = _get_object_token_(fTopView);
2808 
2809 	// set fTopView's owner, add it to window's eligible handler list
2810 	// and also set its next handler to be this window.
2811 
2812 	STRACE(("Calling setowner fTopView = %p this = %p.\n",
2813 		fTopView, this));
2814 
2815 	fTopView->_SetOwner(this);
2816 
2817 	// we can't use AddChild() because this is the top view
2818   	fTopView->_CreateSelf();
2819 
2820 	STRACE(("BuildTopView ended\n"));
2821 }
2822 
2823 
2824 /*!
2825 	Resizes the top view to match the window size. This will also
2826 	adapt the size of all its child views as needed.
2827 	This method has to be called whenever the frame of the window
2828 	changes.
2829 */
2830 void
2831 BWindow::_AdoptResize()
2832 {
2833 	// Resize views according to their resize modes - this
2834 	// saves us some server communication, as the server
2835 	// does the same with our views on its side.
2836 
2837 	int32 deltaWidth = (int32)(fFrame.Width() - fTopView->Bounds().Width());
2838 	int32 deltaHeight = (int32)(fFrame.Height() - fTopView->Bounds().Height());
2839 	if (deltaWidth == 0 && deltaHeight == 0)
2840 		return;
2841 
2842 	fTopView->_ResizeBy(deltaWidth, deltaHeight);
2843 }
2844 
2845 
2846 void
2847 BWindow::_SetFocus(BView *focusView, bool notifyInputServer)
2848 {
2849 	if (fFocus == focusView)
2850 		return;
2851 
2852 	// we notify the input server if we are passing focus
2853 	// from a view which has the B_INPUT_METHOD_AWARE to a one
2854 	// which does not, or vice-versa
2855 	if (notifyInputServer) {
2856 		bool inputMethodAware = false;
2857 		if (focusView)
2858 			inputMethodAware = focusView->Flags() & B_INPUT_METHOD_AWARE;
2859 		BMessage msg(inputMethodAware ? IS_FOCUS_IM_AWARE_VIEW : IS_UNFOCUS_IM_AWARE_VIEW);
2860 		BMessenger messenger(focusView);
2861 		BMessage reply;
2862 		if (focusView)
2863 			msg.AddMessenger("view", messenger);
2864 		_control_input_server_(&msg, &reply);
2865 	}
2866 
2867 	fFocus = focusView;
2868 	SetPreferredHandler(focusView);
2869 }
2870 
2871 
2872 /*!
2873 	\brief Determines the target of a message received for the
2874 		focus view.
2875 */
2876 BHandler *
2877 BWindow::_DetermineTarget(BMessage *message, BHandler *target)
2878 {
2879 	if (target == NULL)
2880 		target = this;
2881 
2882 	switch (message->what) {
2883 		case B_KEY_DOWN:
2884 		case B_KEY_UP:
2885 		{
2886 			// if we have a default button, it might want to hear
2887 			// about pressing the <enter> key
2888 			int32 rawChar;
2889 			if (DefaultButton() != NULL
2890 				&& message->FindInt32("raw_char", &rawChar) == B_OK
2891 				&& rawChar == B_ENTER)
2892 				return DefaultButton();
2893 
2894 			// supposed to fall through
2895 		}
2896 		case B_UNMAPPED_KEY_DOWN:
2897 		case B_UNMAPPED_KEY_UP:
2898 		case B_MODIFIERS_CHANGED:
2899 			// these messages should be dispatched by the focus view
2900 			if (CurrentFocus() != NULL)
2901 				return CurrentFocus();
2902 			break;
2903 
2904 		case B_MOUSE_DOWN:
2905 		case B_MOUSE_UP:
2906 		case B_MOUSE_MOVED:
2907 		case B_MOUSE_WHEEL_CHANGED:
2908 			// is there a token of the view that is currently under the mouse?
2909 			int32 token;
2910 			if (message->FindInt32("_view_token", &token) == B_OK) {
2911 				BView* view = _FindView(token);
2912 				if (view != NULL)
2913 					return view;
2914 			}
2915 
2916 			// if there is no valid token in the message, we try our
2917 			// luck with the last target, if available
2918 			if (fLastMouseMovedView != NULL)
2919 				return fLastMouseMovedView;
2920 			break;
2921 
2922 		case B_PULSE:
2923 		case B_QUIT_REQUESTED:
2924 			// TODO: test wether R5 will let BView dispatch these messages
2925 			return this;
2926 
2927 		case _MESSAGE_DROPPED_:
2928 			if (fLastMouseMovedView != NULL)
2929 				return fLastMouseMovedView;
2930 			break;
2931 
2932 		default:
2933 			break;
2934 	}
2935 
2936 	return target;
2937 }
2938 
2939 
2940 /*!
2941 	\brief Distributes the message to its intended targets. This is done for
2942 		all messages that should go to the preferred handler.
2943 
2944 	Returns \c true in case the message should still be dispatched
2945 */
2946 bool
2947 BWindow::_UnpackMessage(unpack_cookie& cookie, BMessage** _message, BHandler** _target,
2948 	bool* _usePreferred)
2949 {
2950 	if (cookie.message == NULL)
2951 		return false;
2952 
2953 	if (cookie.index == 0 && !cookie.tokens_scanned) {
2954 		// We were called the first time for this message
2955 
2956 		if (!*_usePreferred) {
2957 			// only consider messages targeted at the preferred handler
2958 			cookie.message = NULL;
2959 			return true;
2960 		}
2961 
2962 		// initialize our cookie
2963 		cookie.message = *_message;
2964 		cookie.focus = *_target;
2965 
2966 		if (cookie.focus != NULL)
2967 			cookie.focus_token = _get_object_token_(*_target);
2968 
2969 		if (fLastMouseMovedView != NULL && cookie.message->what == B_MOUSE_MOVED)
2970 			cookie.last_view_token = _get_object_token_(fLastMouseMovedView);
2971 
2972 		*_usePreferred = false;
2973 	}
2974 
2975 	_DequeueAll();
2976 
2977 	// distribute the message to all targets specified in the
2978 	// message directly (but not to the focus view)
2979 
2980 	for (int32 token; !cookie.tokens_scanned
2981 			&& cookie.message->FindInt32("_token", cookie.index, &token) == B_OK;
2982 			cookie.index++) {
2983 		// focus view is preferred and should get its message directly
2984 		if (token == cookie.focus_token) {
2985 			cookie.found_focus = true;
2986 			continue;
2987 		}
2988 		if (token == cookie.last_view_token)
2989 			continue;
2990 
2991 		BView* target = _FindView(token);
2992 		if (target == NULL)
2993 			continue;
2994 
2995 		*_message = new BMessage(*cookie.message);
2996 		*_target = target;
2997 		cookie.index++;
2998 		return true;
2999 	}
3000 
3001 	cookie.tokens_scanned = true;
3002 
3003 	// if there is a last mouse moved view, and the new focus is
3004 	// different, the previous view wants to get its B_EXITED_VIEW
3005 	// message
3006 	if (cookie.last_view_token != B_NULL_TOKEN && fLastMouseMovedView != NULL
3007 		&& fLastMouseMovedView != cookie.focus) {
3008 		*_message = new BMessage(*cookie.message);
3009 		*_target = fLastMouseMovedView;
3010 		cookie.last_view_token = B_NULL_TOKEN;
3011 		return true;
3012 	}
3013 
3014 	bool dispatchToFocus = true;
3015 
3016 	// check if the focus token is still valid (could have been removed in the mean time)
3017 	BHandler* handler;
3018 	if (gDefaultTokens.GetToken(cookie.focus_token, B_HANDLER_TOKEN, (void**)&handler) != B_OK
3019 		|| handler->Looper() != this)
3020 		dispatchToFocus = false;
3021 
3022 	if (dispatchToFocus && cookie.index > 0) {
3023 		// should this message still be dispatched by the focus view?
3024 		bool feedFocus;
3025 		if (!cookie.found_focus
3026 			&& (cookie.message->FindBool("_feed_focus", &feedFocus) != B_OK
3027 				|| feedFocus == false))
3028 			dispatchToFocus = false;
3029 	}
3030 
3031 	if (!dispatchToFocus) {
3032 		delete cookie.message;
3033 		cookie.message = NULL;
3034 		return false;
3035 	}
3036 
3037 	*_message = cookie.message;
3038 	*_target = cookie.focus;
3039 	*_usePreferred = true;
3040 	cookie.message = NULL;
3041 	return true;
3042 }
3043 
3044 
3045 /*!
3046 	Some messages don't get to the window in a shape an application should see.
3047 	This method is supposed to give a message the last grinding before
3048 	it's acceptable for the receiving application.
3049 */
3050 void
3051 BWindow::_SanitizeMessage(BMessage* message, BHandler* target, bool usePreferred)
3052 {
3053 	if (target == NULL)
3054 		return;
3055 
3056 	switch (message->what) {
3057 		case B_MOUSE_MOVED:
3058 		case B_MOUSE_UP:
3059 		case B_MOUSE_DOWN:
3060 		{
3061 			BPoint where;
3062 			if (message->FindPoint("screen_where", &where) != B_OK)
3063 				break;
3064 
3065 			// add local window coordinates
3066 			message->AddPoint("where", ConvertFromScreen(where));
3067 
3068 			BView* view = dynamic_cast<BView*>(target);
3069 			if (view != NULL) {
3070 				// add local view coordinates
3071 				message->AddPoint("be:view_where", view->ConvertFromScreen(where));
3072 
3073 				if (message->what == B_MOUSE_MOVED) {
3074 					// is there a token of the view that is currently under the mouse?
3075 					BView* viewUnderMouse = NULL;
3076 					int32 token;
3077 					if (message->FindInt32("_view_token", &token) == B_OK)
3078 						viewUnderMouse = _FindView(token);
3079 
3080 					// add transit information
3081 					int32 transit;
3082 					if (viewUnderMouse == view) {
3083 						// the mouse is over the target view
3084 						if (fLastMouseMovedView != view)
3085 							transit = B_ENTERED_VIEW;
3086 						else
3087 							transit = B_INSIDE_VIEW;
3088 					} else {
3089 						// the mouse is not over the target view
3090 						if (view == fLastMouseMovedView)
3091 							transit = B_EXITED_VIEW;
3092 						else
3093 							transit = B_OUTSIDE_VIEW;
3094 					}
3095 
3096 					message->AddInt32("be:transit", transit);
3097 
3098 					if (usePreferred || viewUnderMouse == NULL)
3099 						fLastMouseMovedView = viewUnderMouse;
3100 				}
3101 			}
3102 			break;
3103 		}
3104 
3105 		case _MESSAGE_DROPPED_:
3106 		{
3107 			uint32 originalWhat;
3108 			if (message->FindInt32("_original_what", (int32*)&originalWhat) == B_OK) {
3109 				message->what = originalWhat;
3110 				message->RemoveName("_original_what");
3111 			}
3112 			break;
3113 		}
3114 	}
3115 }
3116 
3117 
3118 /*!
3119 	This is called by BView::GetMouse() when a B_MOUSE_MOVED message
3120 	is removed from the queue.
3121 	It allows the window to update the last mouse moved view, and
3122 	let it decide if this message should be kept. It will also remove
3123 	the message from the queue.
3124 	You need to hold the message queue lock when calling this method!
3125 
3126 	\return true if this message can be used to get the mouse data from,
3127 	\return false if this is not meant for the public.
3128 */
3129 bool
3130 BWindow::_StealMouseMessage(BMessage* message, bool& deleteMessage)
3131 {
3132 	BMessage::Private messagePrivate(message);
3133 	if (!messagePrivate.UsePreferredTarget()) {
3134 		// this message is targeted at a specific handler, so we should
3135 		// not steal it
3136 		return false;
3137 	}
3138 
3139 	int32 token;
3140 	if (message->FindInt32("_token", 0, &token) == B_OK) {
3141 		// This message has other targets, so we can't remove it;
3142 		// just prevent it from being sent to the preferred handler
3143 		// again (if it should have gotten it at all).
3144 		bool feedFocus;
3145 		if (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus)
3146 			return false;
3147 
3148 		message->RemoveName("_feed_focus");
3149 		deleteMessage = false;
3150 	} else {
3151 		// The message is only thought for the preferred handler, so we
3152 		// can just remove it.
3153 		MessageQueue()->RemoveMessage(message);
3154 		deleteMessage = true;
3155 
3156 		if (message->what == B_MOUSE_MOVED) {
3157 			// We need to update the last mouse moved view, as this message
3158 			// won't make it to _SanitizeMessage() anymore
3159 			BView* viewUnderMouse = NULL;
3160 			int32 token;
3161 			if (message->FindInt32("_view_token", &token) == B_OK)
3162 				viewUnderMouse = _FindView(token);
3163 
3164 			fLastMouseMovedView = viewUnderMouse;
3165 		}
3166 	}
3167 
3168 	return true;
3169 }
3170 
3171 
3172 /*!
3173 	Handles keyboard input before it gets forwarded to the target handler.
3174 	This includes shortcut evaluation, keyboard navigation, etc.
3175 
3176 	\return handled if true, the event was already handled, and will not
3177 		be forwarded to the target handler.
3178 
3179 	TODO: must also convert the incoming key to the font encoding of the target
3180 */
3181 bool
3182 BWindow::_HandleKeyDown(BMessage* event)
3183 {
3184 	const char *string = NULL;
3185 	if (event->FindString("bytes", &string) != B_OK)
3186 		return false;
3187 
3188 	char key = string[0];
3189 
3190 	uint32 modifiers;
3191 	if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK)
3192 		modifiers = 0;
3193 
3194 	// handle BMenuBar key
3195 	if (key == B_ESCAPE && (modifiers & B_COMMAND_KEY) != 0
3196 		&& fKeyMenuBar) {
3197 		// TODO: ask Marc about 'fWaitingForMenu' member!
3198 
3199 		// fWaitingForMenu = true;
3200 		fKeyMenuBar->StartMenuBar(0, true, false, NULL);
3201 		return true;
3202 	}
3203 
3204 	// Keyboard navigation through views
3205 	// (B_OPTION_KEY makes BTextViews and friends navigable, even in editing mode)
3206 	if (key == B_TAB && (modifiers & (B_COMMAND_KEY | B_OPTION_KEY)) != 0) {
3207 		_KeyboardNavigation();
3208 		return true;
3209 	}
3210 
3211 	// Deskbar's Switcher
3212 	if (key == B_TAB && (modifiers & B_CONTROL_KEY) != 0) {
3213 		BMessenger deskbar(kDeskbarSignature);
3214 		int32 rawKey;
3215 		if (event->FindInt32("key", &rawKey) == B_OK
3216 			&& !event->HasInt32("be:key_repeat")
3217 			&& deskbar.IsValid()) {
3218 			// only send the first key press, no repeats
3219 			BMessage message('TASK');
3220 			message.AddInt32("key", rawKey);
3221 			message.AddInt32("modifiers", modifiers);
3222 			message.AddInt64("when", system_time());
3223 			message.AddInt32("team", Team());
3224 			deskbar.SendMessage(&message);
3225 		}
3226 		return true;
3227 	}
3228 
3229 	// Optionally close window when the escape key is pressed
3230 	if (key == B_ESCAPE && (Flags() & B_CLOSE_ON_ESCAPE) != 0) {
3231 		BMessage message(B_QUIT_REQUESTED);
3232 		message.AddBool("shortcut", true);
3233 
3234 		PostMessage(&message);
3235 		return true;
3236 	}
3237 
3238 	// Handle shortcuts
3239 	if ((modifiers & B_COMMAND_KEY) != 0) {
3240 		// Command+q has been pressed, so, we will quit
3241 		// the shortcut mechanism doesn't allow handlers outside the window
3242 		if (!fNoQuitShortcut && (key == 'Q' || key == 'q')) {
3243 			BMessage message(B_QUIT_REQUESTED);
3244 			message.AddBool("shortcut", true);
3245 
3246 			be_app->PostMessage(&message);
3247 			return true;
3248 		}
3249 
3250 		MenusBeginning();
3251 
3252 		Shortcut* shortcut = _FindShortcut(key, modifiers);
3253 		if (shortcut != NULL) {
3254 			// TODO: would be nice to move this functionality to
3255 			//	a Shortcut::Invoke() method - but since BMenu::InvokeItem()
3256 			//	(and BMenuItem::Invoke()) are private, I didn't want
3257 			//	to mess with them (BMenuItem::Invoke() is public in
3258 			//	Dano/Zeta, though, maybe we should just follow their
3259 			//	example)
3260 			if (shortcut->MenuItem() != NULL) {
3261 				BMenu* menu = shortcut->MenuItem()->Menu();
3262 				if (menu != NULL)
3263 					menu->InvokeItem(shortcut->MenuItem(), true);
3264 			} else {
3265 				BHandler* target = shortcut->Target();
3266 				if (target == NULL)
3267 					target = CurrentFocus();
3268 
3269 				if (shortcut->Message() != NULL) {
3270 					BMessage message(*shortcut->Message());
3271 
3272 					if (message.ReplaceInt64("when", system_time()) != B_OK)
3273 						message.AddInt64("when", system_time());
3274 					if (message.ReplaceBool("shortcut", true) != B_OK)
3275 						message.AddBool("shortcut", true);
3276 
3277 					PostMessage(&message, target);
3278 				}
3279 			}
3280 		}
3281 
3282 		MenusEnded();
3283 
3284 		// we always eat the event if the command key was pressed
3285 		return true;
3286 	}
3287 
3288 	// TODO: convert keys to the encoding of the target view
3289 
3290 	return false;
3291 }
3292 
3293 
3294 void
3295 BWindow::_KeyboardNavigation()
3296 {
3297 	BMessage *message = CurrentMessage();
3298 	if (message == NULL)
3299 		return;
3300 
3301 	const char *bytes;
3302 	uint32 modifiers;
3303 	if (message->FindString("bytes", &bytes) != B_OK
3304 		|| bytes[0] != B_TAB)
3305 		return;
3306 
3307 	message->FindInt32("modifiers", (int32*)&modifiers);
3308 
3309 	BView *nextFocus;
3310 	int32 jumpGroups = modifiers & B_CONTROL_KEY ? B_NAVIGABLE_JUMP : B_NAVIGABLE;
3311 	if (modifiers & B_SHIFT_KEY)
3312 		nextFocus = _FindPreviousNavigable(fFocus, jumpGroups);
3313 	else
3314 		nextFocus = _FindNextNavigable(fFocus, jumpGroups);
3315 
3316 	if (nextFocus && nextFocus != fFocus) {
3317 		nextFocus->MakeFocus(true);
3318 	}
3319 }
3320 
3321 
3322 BMessage *
3323 BWindow::ConvertToMessage(void *raw, int32 code)
3324 {
3325 	return BLooper::ConvertToMessage(raw, code);
3326 }
3327 
3328 
3329 BWindow::Shortcut *
3330 BWindow::_FindShortcut(uint32 key, uint32 modifiers)
3331 {
3332 	int32 count = fShortcuts.CountItems();
3333 
3334 	key = Shortcut::PrepareKey(key);
3335 	modifiers = Shortcut::PrepareModifiers(modifiers);
3336 
3337 	for (int32 index = 0; index < count; index++) {
3338 		Shortcut *shortcut = (Shortcut *)fShortcuts.ItemAt(index);
3339 
3340 		if (shortcut->Matches(key, modifiers))
3341 			return shortcut;
3342 	}
3343 
3344 	return NULL;
3345 }
3346 
3347 
3348 BView *
3349 BWindow::_FindView(int32 token)
3350 {
3351 	BHandler* handler;
3352 	if (gDefaultTokens.GetToken(token, B_HANDLER_TOKEN, (void**)&handler) != B_OK)
3353 		return NULL;
3354 
3355 	// the view must belong to us in order to be found by this method
3356 	BView* view = dynamic_cast<BView*>(handler);
3357 	if (view != NULL && view->Window() == this)
3358 		return view;
3359 
3360 	return NULL;
3361 }
3362 
3363 
3364 BView*
3365 BWindow::_FindView(BView* view, BPoint point) const
3366 {
3367 	// point is assumed to be already in view's coordinates
3368 	// TODO: since BView::Bounds() potentially queries the app_server anyway,
3369 	// we could just let the app_server answer this query directly.
3370 	if (view->Bounds().Contains(point) && !view->fFirstChild)
3371 		return view;
3372 
3373 	BView* child = view->fFirstChild;
3374 
3375 	while (child != NULL) {
3376 		BPoint childPoint = point - child->LeftTop();
3377 		if ((view = _FindView(child, childPoint)) != NULL)
3378 			return view;
3379 
3380 		child = child->fNextSibling;
3381 	}
3382 
3383 	return NULL;
3384 }
3385 
3386 
3387 BView*
3388 BWindow::_FindNextNavigable(BView* focus, uint32 flags)
3389 {
3390 	if (focus == NULL)
3391 		focus = fTopView;
3392 
3393 	BView* nextFocus = focus;
3394 
3395 	// Search the tree for views that accept focus (depth search)
3396 	while (true) {
3397 		if (nextFocus->fFirstChild)
3398 			nextFocus = nextFocus->fFirstChild;
3399 		else if (nextFocus->fNextSibling)
3400 			nextFocus = nextFocus->fNextSibling;
3401 		else {
3402 			// go to the nearest parent with a next sibling
3403 			while (!nextFocus->fNextSibling && nextFocus->fParent) {
3404 				nextFocus = nextFocus->fParent;
3405 			}
3406 
3407 			if (nextFocus == fTopView) {
3408 				// if we started with the top view, we traversed the whole tree already
3409 				if (nextFocus == focus)
3410 					return NULL;
3411 
3412 				nextFocus = nextFocus->fFirstChild;
3413 			} else
3414 				nextFocus = nextFocus->fNextSibling;
3415 		}
3416 
3417 		if (nextFocus == focus || nextFocus == NULL) {
3418 			// When we get here it means that the hole tree has been
3419 			// searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
3420 			return NULL;
3421 		}
3422 
3423 		if (!nextFocus->IsHidden() && (nextFocus->Flags() & flags) != 0)
3424 			return nextFocus;
3425 	}
3426 }
3427 
3428 
3429 BView *
3430 BWindow::_FindPreviousNavigable(BView* focus, uint32 flags)
3431 {
3432 	if (focus == NULL)
3433 		focus = fTopView;
3434 
3435 	BView* previousFocus = focus;
3436 
3437 	// Search the tree for the previous view that accept focus
3438 	while (true) {
3439 		if (previousFocus->fPreviousSibling) {
3440 			// find the last child in the previous sibling
3441 			previousFocus = _LastViewChild(previousFocus->fPreviousSibling);
3442 		} else {
3443 			previousFocus = previousFocus->fParent;
3444 			if (previousFocus == fTopView)
3445 				previousFocus = _LastViewChild(fTopView);
3446 		}
3447 
3448 		if (previousFocus == focus || previousFocus == NULL) {
3449 			// When we get here it means that the hole tree has been
3450 			// searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
3451 			return NULL;
3452 		}
3453 
3454 		if (!previousFocus->IsHidden() && (previousFocus->Flags() & flags) != 0)
3455 			return previousFocus;
3456 	}
3457 }
3458 
3459 
3460 /*!
3461 	Returns the last child in a view hierarchy.
3462 	Needed only by _FindPreviousNavigable().
3463 */
3464 BView *
3465 BWindow::_LastViewChild(BView *parent)
3466 {
3467 	while (true) {
3468 		BView *last = parent->fFirstChild;
3469 		if (last == NULL)
3470 			return parent;
3471 
3472 		while (last->fNextSibling) {
3473 			last = last->fNextSibling;
3474 		}
3475 
3476 		parent = last;
3477 	}
3478 }
3479 
3480 
3481 void
3482 BWindow::SetIsFilePanel(bool isFilePanel)
3483 {
3484 	fIsFilePanel = isFilePanel;
3485 }
3486 
3487 
3488 bool
3489 BWindow::IsFilePanel() const
3490 {
3491 	return fIsFilePanel;
3492 }
3493 
3494 
3495 //------------------------------------------------------------------------------
3496 // Virtual reserved Functions
3497 
3498 void BWindow::_ReservedWindow1() {}
3499 void BWindow::_ReservedWindow2() {}
3500 void BWindow::_ReservedWindow3() {}
3501 void BWindow::_ReservedWindow4() {}
3502 void BWindow::_ReservedWindow5() {}
3503 void BWindow::_ReservedWindow6() {}
3504 void BWindow::_ReservedWindow7() {}
3505 void BWindow::_ReservedWindow8() {}
3506 
3507 void
3508 BWindow::PrintToStream() const
3509 {
3510 	printf("BWindow '%s' data:\
3511 		Title			= %s\
3512 		Token			= %ld\
3513 		InTransaction 	= %s\
3514 		Active 			= %s\
3515 		fShowLevel		= %d\
3516 		Flags			= %lx\
3517 		send_port		= %ld\
3518 		receive_port	= %ld\
3519 		fTopView name	= %s\
3520 		focus view name	= %s\
3521 		lastMouseMoved	= %s\
3522 		fLink			= %p\
3523 		KeyMenuBar name	= %s\
3524 		DefaultButton	= %s\
3525 		# of shortcuts	= %ld",
3526 		Name(), fTitle,
3527 		_get_object_token_(this),
3528 		fInTransaction == true ? "yes" : "no",
3529 		fActive == true ? "yes" : "no",
3530 		fShowLevel,
3531 		fFlags,
3532 		fLink->SenderPort(),
3533 		fLink->ReceiverPort(),
3534 		fTopView != NULL ? fTopView->Name() : "NULL",
3535 		fFocus != NULL ? fFocus->Name() : "NULL",
3536 		fLastMouseMovedView != NULL ? fLastMouseMovedView->Name() : "NULL",
3537 		fLink,
3538 		fKeyMenuBar != NULL ? fKeyMenuBar->Name() : "NULL",
3539 		fDefaultButton != NULL ? fDefaultButton->Name() : "NULL",
3540 		fShortcuts.CountItems());
3541 /*
3542 	for( int32 i=0; i<accelList.CountItems(); i++){
3543 		_BCmdKey	*key = (_BCmdKey*)accelList.ItemAt(i);
3544 		printf("\tShortCut %ld: char %s\n\t\t message: \n", i, (key->key > 127)?"ASCII":"UNICODE");
3545 		key->message->PrintToStream();
3546 	}
3547 */
3548 	printf("\
3549 		topViewToken	= %ld\
3550 		isFilePanel		= %s\
3551 		MaskActivated	= %s\
3552 		pulseRate		= %lld\
3553 		waitingForMenu	= %s\
3554 		minimized		= %s\
3555 		Menu semaphore	= %ld\
3556 		maxZoomHeight	= %f\
3557 		maxZoomWidth	= %f\
3558 		minWindHeight	= %f\
3559 		minWindWidth	= %f\
3560 		maxWindHeight	= %f\
3561 		maxWindWidth	= %f\
3562 		frame			= ( %f, %f, %f, %f )\
3563 		look			= %d\
3564 		feel			= %d\
3565 		lastViewToken	= %ld\
3566 		pulseRunner		= %s\n",
3567 		fTopViewToken,
3568 		fIsFilePanel==true?"Yes":"No",
3569 		fMaskActivated==true?"Yes":"No",
3570 		fPulseRate,
3571 		fWaitingForMenu==true?"Yes":"No",
3572 		fMinimized==true?"Yes":"No",
3573 		fMenuSem,
3574 		fMaxZoomHeight,
3575 		fMaxZoomWidth,
3576 		fMinHeight,
3577 		fMinWidth,
3578 		fMaxHeight,
3579 		fMaxWidth,
3580 		fFrame.left, fFrame.top, fFrame.right, fFrame.bottom,
3581 		(int16)fLook,
3582 		(int16)fFeel,
3583 		fLastViewToken,
3584 		fPulseRunner != NULL ? "In place" : "NULL");
3585 }
3586 
3587 /*
3588 TODO list:
3589 	*) test arguments for SetWindowAligment
3590 	*) call hook functions: MenusBeginning, MenusEnded. Add menu activation code.
3591 */
3592 
3593