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