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