xref: /haiku/src/kits/interface/Window.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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=%" B_PRId32 ", 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)(addr_t)list.ItemAt(i),
1033 					false);
1034 			}
1035 			break;
1036 		}
1037 
1038 		case B_WINDOW_RESIZED:
1039 		{
1040 			int32 width, height;
1041 			if (msg->FindInt32("width", &width) == B_OK
1042 				&& msg->FindInt32("height", &height) == B_OK) {
1043 				// combine with pending resize notifications
1044 				BMessage* pendingMessage;
1045 				while ((pendingMessage = MessageQueue()->FindMessage(B_WINDOW_RESIZED, 0))) {
1046 					int32 nextWidth;
1047 					if (pendingMessage->FindInt32("width", &nextWidth) == B_OK)
1048 						width = nextWidth;
1049 
1050 					int32 nextHeight;
1051 					if (pendingMessage->FindInt32("height", &nextHeight) == B_OK)
1052 						height = nextHeight;
1053 
1054 					MessageQueue()->RemoveMessage(pendingMessage);
1055 					delete pendingMessage;
1056 						// this deletes the first *additional* message
1057 						// fCurrentMessage is safe
1058 				}
1059 				if (width != fFrame.Width() || height != fFrame.Height()) {
1060 					// NOTE: we might have already handled the resize
1061 					// in an _UPDATE_ message
1062 					fFrame.right = fFrame.left + width;
1063 					fFrame.bottom = fFrame.top + height;
1064 
1065 					_AdoptResize();
1066 //					FrameResized(width, height);
1067 				}
1068 // call hook function anyways
1069 // TODO: When a window is resized programmatically,
1070 // it receives this message, and maybe it is wise to
1071 // keep the asynchronous nature of this process to
1072 // not risk breaking any apps.
1073 FrameResized(width, height);
1074 			}
1075 			break;
1076 		}
1077 
1078 		case B_WINDOW_MOVED:
1079 		{
1080 			BPoint origin;
1081 			if (msg->FindPoint("where", &origin) == B_OK) {
1082 				if (fFrame.LeftTop() != origin) {
1083 					// NOTE: we might have already handled the move
1084 					// in an _UPDATE_ message
1085 					fFrame.OffsetTo(origin);
1086 
1087 //					FrameMoved(origin);
1088 				}
1089 // call hook function anyways
1090 // TODO: When a window is moved programmatically,
1091 // it receives this message, and maybe it is wise to
1092 // keep the asynchronous nature of this process to
1093 // not risk breaking any apps.
1094 FrameMoved(origin);
1095 			}
1096 			break;
1097 		}
1098 
1099 		case B_WINDOW_ACTIVATED:
1100 			if (target != this) {
1101 				target->MessageReceived(msg);
1102 				break;
1103 			}
1104 
1105 			bool active;
1106 			if (msg->FindBool("active", &active) != B_OK)
1107 				break;
1108 
1109 			// find latest activation message
1110 
1111 			while (true) {
1112 				BMessage* pendingMessage = MessageQueue()->FindMessage(
1113 					B_WINDOW_ACTIVATED, 0);
1114 				if (pendingMessage == NULL)
1115 					break;
1116 
1117 				bool nextActive;
1118 				if (pendingMessage->FindBool("active", &nextActive) == B_OK)
1119 					active = nextActive;
1120 
1121 				MessageQueue()->RemoveMessage(pendingMessage);
1122 				delete pendingMessage;
1123 			}
1124 
1125 			if (active != fActive) {
1126 				fActive = active;
1127 
1128 				WindowActivated(active);
1129 
1130 				// call hook function 'WindowActivated(bool)' for all
1131 				// views attached to this window.
1132 				fTopView->_Activate(active);
1133 
1134 				// we notify the input server if we are gaining or losing focus
1135 				// from a view which has the B_INPUT_METHOD_AWARE on a window
1136 				// activation
1137 				if (!active)
1138 					break;
1139 				bool inputMethodAware = false;
1140 				if (fFocus)
1141 					inputMethodAware = fFocus->Flags() & B_INPUT_METHOD_AWARE;
1142 				BMessage msg(inputMethodAware ? IS_FOCUS_IM_AWARE_VIEW : IS_UNFOCUS_IM_AWARE_VIEW);
1143 				BMessenger messenger(fFocus);
1144 				BMessage reply;
1145 				if (fFocus)
1146 					msg.AddMessenger("view", messenger);
1147 				_control_input_server_(&msg, &reply);
1148 			}
1149 			break;
1150 
1151 		case B_SCREEN_CHANGED:
1152 			if (target == this) {
1153 				BRect frame;
1154 				uint32 mode;
1155 				if (msg->FindRect("frame", &frame) == B_OK
1156 					&& msg->FindInt32("mode", (int32*)&mode) == B_OK)
1157 					ScreenChanged(frame, (color_space)mode);
1158 			} else
1159 				target->MessageReceived(msg);
1160 			break;
1161 
1162 		case B_WORKSPACE_ACTIVATED:
1163 			if (target == this) {
1164 				uint32 workspace;
1165 				bool active;
1166 				if (msg->FindInt32("workspace", (int32*)&workspace) == B_OK
1167 					&& msg->FindBool("active", &active) == B_OK)
1168 					WorkspaceActivated(workspace, active);
1169 			} else
1170 				target->MessageReceived(msg);
1171 			break;
1172 
1173 		case B_WORKSPACES_CHANGED:
1174 			if (target == this) {
1175 				uint32 oldWorkspace, newWorkspace;
1176 				if (msg->FindInt32("old", (int32*)&oldWorkspace) == B_OK
1177 					&& msg->FindInt32("new", (int32*)&newWorkspace) == B_OK)
1178 					WorkspacesChanged(oldWorkspace, newWorkspace);
1179 			} else
1180 				target->MessageReceived(msg);
1181 			break;
1182 
1183 		case B_INVALIDATE:
1184 		{
1185 			if (BView* view = dynamic_cast<BView*>(target)) {
1186 				BRect rect;
1187 				if (msg->FindRect("be:area", &rect) == B_OK)
1188 					view->Invalidate(rect);
1189 				else
1190 					view->Invalidate();
1191 			} else
1192 				target->MessageReceived(msg);
1193 			break;
1194 		}
1195 
1196 		case B_KEY_DOWN:
1197 		{
1198 			if (!_HandleKeyDown(msg)) {
1199 				if (BView* view = dynamic_cast<BView*>(target)) {
1200 					// TODO: cannot use "string" here if we support having
1201 					// different font encoding per view (it's supposed to be
1202 					// converted by _HandleKeyDown() one day)
1203 					const char* string;
1204 					ssize_t bytes;
1205 					if (msg->FindData("bytes", B_STRING_TYPE,
1206 						(const void**)&string, &bytes) == B_OK) {
1207 						view->KeyDown(string, bytes - 1);
1208 					}
1209 				} else
1210 					target->MessageReceived(msg);
1211 			}
1212 			break;
1213 		}
1214 
1215 		case B_KEY_UP:
1216 		{
1217 			// TODO: same as above
1218 			if (BView* view = dynamic_cast<BView*>(target)) {
1219 				const char* string;
1220 				ssize_t bytes;
1221 				if (msg->FindData("bytes", B_STRING_TYPE,
1222 					(const void**)&string, &bytes) == B_OK) {
1223 					view->KeyUp(string, bytes - 1);
1224 				}
1225 			} else
1226 				target->MessageReceived(msg);
1227 			break;
1228 		}
1229 
1230 		case B_UNMAPPED_KEY_DOWN:
1231 		{
1232 			if (!_HandleUnmappedKeyDown(msg))
1233 				target->MessageReceived(msg);
1234 			break;
1235 		}
1236 
1237 		case B_MOUSE_DOWN:
1238 		{
1239 			BView* view = dynamic_cast<BView*>(target);
1240 
1241 			if (view != NULL) {
1242 				BPoint where;
1243 				msg->FindPoint("be:view_where", &where);
1244 				view->MouseDown(where);
1245 			} else
1246 				target->MessageReceived(msg);
1247 
1248 			break;
1249 		}
1250 
1251 		case B_MOUSE_UP:
1252 		{
1253 			if (BView* view = dynamic_cast<BView*>(target)) {
1254 				BPoint where;
1255 				msg->FindPoint("be:view_where", &where);
1256 				view->fMouseEventOptions = 0;
1257 				view->MouseUp(where);
1258 			} else
1259 				target->MessageReceived(msg);
1260 
1261 			break;
1262 		}
1263 
1264 		case B_MOUSE_MOVED:
1265 		{
1266 			if (BView* view = dynamic_cast<BView*>(target)) {
1267 				uint32 eventOptions = view->fEventOptions
1268 					| view->fMouseEventOptions;
1269 				bool noHistory = eventOptions & B_NO_POINTER_HISTORY;
1270 				bool dropIfLate = !(eventOptions & B_FULL_POINTER_HISTORY);
1271 
1272 				bigtime_t eventTime;
1273 				if (msg->FindInt64("when", (int64*)&eventTime) < B_OK)
1274 					eventTime = system_time();
1275 
1276 				uint32 transit;
1277 				msg->FindInt32("be:transit", (int32*)&transit);
1278 				// don't drop late messages with these important transit values
1279 				if (transit == B_ENTERED_VIEW || transit == B_EXITED_VIEW)
1280 					dropIfLate = false;
1281 
1282 				// TODO: The dropping code may have the following problem:
1283 				// On slower computers, 20ms may just be to abitious a delay.
1284 				// There, we might constantly check the message queue for a
1285 				// newer message, not find any, and still use the only but
1286 				// later than 20ms message, which of course makes the whole
1287 				// thing later than need be. An adaptive delay would be
1288 				// kind of neat, but would probably use additional BWindow
1289 				// members to count the successful versus fruitless queue
1290 				// searches and the delay value itself or something similar.
1291 
1292 				if (noHistory
1293 					|| (dropIfLate && (system_time() - eventTime > 20000))) {
1294 					// filter out older mouse moved messages in the queue
1295 					_DequeueAll();
1296 					BMessageQueue* queue = MessageQueue();
1297 					queue->Lock();
1298 
1299 					BMessage* moved;
1300 					for (int32 i = 0; (moved = queue->FindMessage(i)) != NULL;
1301 							i++) {
1302 						if (moved != msg && moved->what == B_MOUSE_MOVED) {
1303 							// there is a newer mouse moved message in the
1304 							// queue, just ignore the current one, the newer one
1305 							// will be handled here eventually
1306 							queue->Unlock();
1307 							return;
1308 						}
1309 					}
1310 					queue->Unlock();
1311 				}
1312 
1313 				BPoint where;
1314 				uint32 buttons;
1315 				msg->FindPoint("be:view_where", &where);
1316 				msg->FindInt32("buttons", (int32*)&buttons);
1317 
1318 				delete fIdleMouseRunner;
1319 
1320 				if (transit != B_EXITED_VIEW && transit != B_OUTSIDE_VIEW) {
1321 					// Start new idle runner
1322 					BMessage idle(B_MOUSE_IDLE);
1323 					idle.AddPoint("be:view_where", where);
1324 					fIdleMouseRunner = new BMessageRunner(
1325 						BMessenger(NULL, this), &idle,
1326 						BToolTipManager::Manager()->ShowDelay(), 1);
1327 				} else {
1328 					fIdleMouseRunner = NULL;
1329 					if (dynamic_cast<BPrivate::ToolTipWindow*>(this) == NULL)
1330 						BToolTipManager::Manager()->HideTip();
1331 				}
1332 
1333 				BMessage* dragMessage = NULL;
1334 				if (msg->HasMessage("be:drag_message")) {
1335 					dragMessage = new BMessage();
1336 					if (msg->FindMessage("be:drag_message", dragMessage)
1337 							!= B_OK) {
1338 						delete dragMessage;
1339 						dragMessage = NULL;
1340 					}
1341 				}
1342 
1343 				view->MouseMoved(where, transit, dragMessage);
1344 				delete dragMessage;
1345 			} else
1346 				target->MessageReceived(msg);
1347 
1348 			break;
1349 		}
1350 
1351 		case B_PULSE:
1352 			if (target == this && fPulseRunner) {
1353 				fTopView->_Pulse();
1354 				fLink->Flush();
1355 			} else
1356 				target->MessageReceived(msg);
1357 			break;
1358 
1359 		case _UPDATE_:
1360 		{
1361 //bigtime_t now = system_time();
1362 //bigtime_t drawTime = 0;
1363 			STRACE(("info:BWindow handling _UPDATE_.\n"));
1364 
1365 			fLink->StartMessage(AS_BEGIN_UPDATE);
1366 			fInTransaction = true;
1367 
1368 			int32 code;
1369 			if (fLink->FlushWithReply(code) == B_OK
1370 				&& code == B_OK) {
1371 				// read current window position and size first,
1372 				// the update rect is in screen coordinates...
1373 				// so we need to be up to date
1374 				BPoint origin;
1375 				fLink->Read<BPoint>(&origin);
1376 				float width;
1377 				float height;
1378 				fLink->Read<float>(&width);
1379 				fLink->Read<float>(&height);
1380 				if (origin != fFrame.LeftTop()) {
1381 					// TODO: remove code duplicatation with
1382 					// B_WINDOW_MOVED case...
1383 					//printf("window position was not up to date\n");
1384 					fFrame.OffsetTo(origin);
1385 					FrameMoved(origin);
1386 				}
1387 				if (width != fFrame.Width() || height != fFrame.Height()) {
1388 					// TODO: remove code duplicatation with
1389 					// B_WINDOW_RESIZED case...
1390 					//printf("window size was not up to date\n");
1391 					fFrame.right = fFrame.left + width;
1392 					fFrame.bottom = fFrame.top + height;
1393 
1394 					_AdoptResize();
1395 					FrameResized(width, height);
1396 				}
1397 
1398 				// read tokens for views that need to be drawn
1399 				// NOTE: we need to read the tokens completely
1400 				// first, we cannot draw views in between reading
1401 				// the tokens, since other communication would likely
1402 				// mess up the data in the link.
1403 				struct ViewUpdateInfo {
1404 					int32 token;
1405 					BRect updateRect;
1406 				};
1407 				BList infos(20);
1408 				while (true) {
1409 					// read next token and create/add ViewUpdateInfo
1410 					int32 token;
1411 					status_t error = fLink->Read<int32>(&token);
1412 					if (error < B_OK || token == B_NULL_TOKEN)
1413 						break;
1414 					ViewUpdateInfo* info = new(std::nothrow) ViewUpdateInfo;
1415 					if (info == NULL || !infos.AddItem(info)) {
1416 						delete info;
1417 						break;
1418 					}
1419 					info->token = token;
1420 					// read culmulated update rect (is in screen coords)
1421 					error = fLink->Read<BRect>(&(info->updateRect));
1422 					if (error < B_OK)
1423 						break;
1424 				}
1425 				// draw
1426 				int32 count = infos.CountItems();
1427 				for (int32 i = 0; i < count; i++) {
1428 //bigtime_t drawStart = system_time();
1429 					ViewUpdateInfo* info
1430 						= (ViewUpdateInfo*)infos.ItemAtFast(i);
1431 					if (BView* view = _FindView(info->token))
1432 						view->_Draw(info->updateRect);
1433 					else {
1434 						printf("_UPDATE_ - didn't find view by token: %"
1435 							B_PRId32 "\n", info->token);
1436 					}
1437 //drawTime += system_time() - drawStart;
1438 				}
1439 				// NOTE: The tokens are actually hirachically sorted,
1440 				// so traversing the list in revers and calling
1441 				// child->_DrawAfterChildren() actually works like intended.
1442 				for (int32 i = count - 1; i >= 0; i--) {
1443 					ViewUpdateInfo* info
1444 						= (ViewUpdateInfo*)infos.ItemAtFast(i);
1445 					if (BView* view = _FindView(info->token))
1446 						view->_DrawAfterChildren(info->updateRect);
1447 					delete info;
1448 				}
1449 
1450 //printf("  %ld views drawn, total Draw() time: %lld\n", count, drawTime);
1451 			}
1452 
1453 			fLink->StartMessage(AS_END_UPDATE);
1454 			fLink->Flush();
1455 			fInTransaction = false;
1456 			fUpdateRequested = false;
1457 
1458 //printf("BWindow(%s) - UPDATE took %lld usecs\n", Title(), system_time() - now);
1459 			break;
1460 		}
1461 
1462 		case _MENUS_DONE_:
1463 			MenusEnded();
1464 			break;
1465 
1466 		// These two are obviously some kind of old scripting messages
1467 		// this is NOT an app_server message and we have to be cautious
1468 		case B_WINDOW_MOVE_BY:
1469 		{
1470 			BPoint offset;
1471 			if (msg->FindPoint("data", &offset) == B_OK)
1472 				MoveBy(offset.x, offset.y);
1473 			else
1474 				msg->SendReply(B_MESSAGE_NOT_UNDERSTOOD);
1475 			break;
1476 		}
1477 
1478 		// this is NOT an app_server message and we have to be cautious
1479 		case B_WINDOW_MOVE_TO:
1480 		{
1481 			BPoint origin;
1482 			if (msg->FindPoint("data", &origin) == B_OK)
1483 				MoveTo(origin);
1484 			else
1485 				msg->SendReply(B_MESSAGE_NOT_UNDERSTOOD);
1486 			break;
1487 		}
1488 
1489 		case B_LAYOUT_WINDOW:
1490 		{
1491 			Layout(false);
1492 			break;
1493 		}
1494 
1495 		default:
1496 			BLooper::DispatchMessage(msg, target);
1497 			break;
1498 	}
1499 }
1500 
1501 
1502 void
1503 BWindow::FrameMoved(BPoint new_position)
1504 {
1505 	// does nothing
1506 	// Hook function
1507 }
1508 
1509 
1510 void
1511 BWindow::FrameResized(float new_width, float new_height)
1512 {
1513 	// does nothing
1514 	// Hook function
1515 }
1516 
1517 
1518 void
1519 BWindow::WorkspacesChanged(uint32 old_ws, uint32 new_ws)
1520 {
1521 	// does nothing
1522 	// Hook function
1523 }
1524 
1525 
1526 void
1527 BWindow::WorkspaceActivated(int32 ws, bool state)
1528 {
1529 	// does nothing
1530 	// Hook function
1531 }
1532 
1533 
1534 void
1535 BWindow::MenusBeginning()
1536 {
1537 	// does nothing
1538 	// Hook function
1539 }
1540 
1541 
1542 void
1543 BWindow::MenusEnded()
1544 {
1545 	// does nothing
1546 	// Hook function
1547 }
1548 
1549 
1550 void
1551 BWindow::SetSizeLimits(float minWidth, float maxWidth,
1552 	float minHeight, float maxHeight)
1553 {
1554 	if (minWidth > maxWidth || minHeight > maxHeight)
1555 		return;
1556 
1557 	if (!Lock())
1558 		return;
1559 
1560 	fLink->StartMessage(AS_SET_SIZE_LIMITS);
1561 	fLink->Attach<float>(minWidth);
1562 	fLink->Attach<float>(maxWidth);
1563 	fLink->Attach<float>(minHeight);
1564 	fLink->Attach<float>(maxHeight);
1565 
1566 	int32 code;
1567 	if (fLink->FlushWithReply(code) == B_OK
1568 		&& code == B_OK) {
1569 		// read the values that were really enforced on
1570 		// the server side (the window frame could have
1571 		// been changed, too)
1572 		fLink->Read<BRect>(&fFrame);
1573 		fLink->Read<float>(&fMinWidth);
1574 		fLink->Read<float>(&fMaxWidth);
1575 		fLink->Read<float>(&fMinHeight);
1576 		fLink->Read<float>(&fMaxHeight);
1577 
1578 		_AdoptResize();
1579 			// TODO: the same has to be done for SetLook() (that can alter
1580 			//		the size limits, and hence, the size of the window
1581 	}
1582 	Unlock();
1583 }
1584 
1585 
1586 void
1587 BWindow::GetSizeLimits(float* _minWidth, float* _maxWidth, float* _minHeight,
1588 	float* _maxHeight)
1589 {
1590 	// TODO: What about locking?!?
1591 	if (_minHeight != NULL)
1592 		*_minHeight = fMinHeight;
1593 	if (_minWidth != NULL)
1594 		*_minWidth = fMinWidth;
1595 	if (_maxHeight != NULL)
1596 		*_maxHeight = fMaxHeight;
1597 	if (_maxWidth != NULL)
1598 		*_maxWidth = fMaxWidth;
1599 }
1600 
1601 
1602 /*!	Updates the window's size limits from the minimum and maximum sizes of its
1603 	top view.
1604 
1605 	Is a no-op, unless the \c B_AUTO_UPDATE_SIZE_LIMITS window flag is set.
1606 
1607 	The method is called automatically after a layout invalidation. Since it is
1608 	invoked asynchronously, calling this method manually is necessary, if it is
1609 	desired to adjust the limits (and as a possible side effect the window size)
1610 	earlier (e.g. before the first Show()).
1611 */
1612 void
1613 BWindow::UpdateSizeLimits()
1614 {
1615 	if ((fFlags & B_AUTO_UPDATE_SIZE_LIMITS) != 0) {
1616 		// Get min/max constraints of the top view and enforce window
1617 		// size limits respectively.
1618 		BSize minSize = fTopView->MinSize();
1619 		BSize maxSize = fTopView->MaxSize();
1620 		SetSizeLimits(minSize.width, maxSize.width,
1621 			minSize.height, maxSize.height);
1622 	}
1623 }
1624 
1625 
1626 status_t
1627 BWindow::SetDecoratorSettings(const BMessage& settings)
1628 {
1629 	// flatten the given settings into a buffer and send
1630 	// it to the app_server to apply the settings to the
1631 	// decorator
1632 
1633 	int32 size = settings.FlattenedSize();
1634 	char buffer[size];
1635 	status_t status = settings.Flatten(buffer, size);
1636 	if (status != B_OK)
1637 		return status;
1638 
1639 	if (!Lock())
1640 		return B_ERROR;
1641 
1642 	status = fLink->StartMessage(AS_SET_DECORATOR_SETTINGS);
1643 
1644 	if (status == B_OK)
1645 		status = fLink->Attach<int32>(size);
1646 
1647 	if (status == B_OK)
1648 		status = fLink->Attach(buffer, size);
1649 
1650 	if (status == B_OK)
1651 		status = fLink->Flush();
1652 
1653 	Unlock();
1654 
1655 	return status;
1656 }
1657 
1658 
1659 status_t
1660 BWindow::GetDecoratorSettings(BMessage* settings) const
1661 {
1662 	// read a flattened settings message from the app_server
1663 	// and put it into settings
1664 
1665 	if (!const_cast<BWindow*>(this)->Lock())
1666 		return B_ERROR;
1667 
1668 	status_t status = fLink->StartMessage(AS_GET_DECORATOR_SETTINGS);
1669 
1670 	if (status == B_OK) {
1671 		int32 code;
1672 		status = fLink->FlushWithReply(code);
1673 		if (status == B_OK && code != B_OK)
1674 			status = code;
1675 	}
1676 
1677 	if (status == B_OK) {
1678 		int32 size;
1679 		status = fLink->Read<int32>(&size);
1680 		if (status == B_OK) {
1681 			char buffer[size];
1682 			status = fLink->Read(buffer, size);
1683 			if (status == B_OK) {
1684 				status = settings->Unflatten(buffer);
1685 			}
1686 		}
1687 	}
1688 
1689 	const_cast<BWindow*>(this)->Unlock();
1690 
1691 	return status;
1692 }
1693 
1694 
1695 void
1696 BWindow::SetZoomLimits(float maxWidth, float maxHeight)
1697 {
1698 	// TODO: What about locking?!?
1699 	if (maxWidth > fMaxWidth)
1700 		maxWidth = fMaxWidth;
1701 	else
1702 		fMaxZoomWidth = maxWidth;
1703 
1704 	if (maxHeight > fMaxHeight)
1705 		maxHeight = fMaxHeight;
1706 	else
1707 		fMaxZoomHeight = maxHeight;
1708 }
1709 
1710 
1711 void
1712 BWindow::Zoom(BPoint leftTop, float width, float height)
1713 {
1714 	// the default implementation of this hook function
1715 	// just does the obvious:
1716 	MoveTo(leftTop);
1717 	ResizeTo(width, height);
1718 }
1719 
1720 
1721 void
1722 BWindow::Zoom()
1723 {
1724 	// TODO: What about locking?!?
1725 
1726 	// From BeBook:
1727 	// The dimensions that non-virtual Zoom() passes to hook Zoom() are deduced
1728 	// from the smallest of three rectangles:
1729 
1730 	float borderWidth;
1731 	float tabHeight;
1732 	_GetDecoratorSize(&borderWidth, &tabHeight);
1733 
1734 	// 1) the rectangle defined by SetZoomLimits(),
1735 	float zoomedWidth = fMaxZoomWidth;
1736 	float zoomedHeight = fMaxZoomHeight;
1737 
1738 	// 2) the rectangle defined by SetSizeLimits()
1739 	if (fMaxWidth < zoomedWidth)
1740 		zoomedWidth = fMaxWidth;
1741 	if (fMaxHeight < zoomedHeight)
1742 		zoomedHeight = fMaxHeight;
1743 
1744 	// 3) the screen rectangle
1745 	BScreen screen(this);
1746 	// TODO: Broken for tab on left side windows...
1747 	float screenWidth = screen.Frame().Width() - 2 * borderWidth;
1748 	float screenHeight = screen.Frame().Height() - (2 * borderWidth + tabHeight);
1749 	if (screenWidth < zoomedWidth)
1750 		zoomedWidth = screenWidth;
1751 	if (screenHeight < zoomedHeight)
1752 		zoomedHeight = screenHeight;
1753 
1754 	BPoint zoomedLeftTop = screen.Frame().LeftTop() + BPoint(borderWidth,
1755 		tabHeight + borderWidth);
1756 	// Center if window cannot be made full screen
1757 	if (screenWidth > zoomedWidth)
1758 		zoomedLeftTop.x += (screenWidth - zoomedWidth) / 2;
1759 	if (screenHeight > zoomedHeight)
1760 		zoomedLeftTop.y += (screenHeight - zoomedHeight) / 2;
1761 
1762 	// Un-Zoom
1763 
1764 	if (fPreviousFrame.IsValid()
1765 		// NOTE: don't check for fFrame.LeftTop() == zoomedLeftTop
1766 		// -> makes it easier on the user to get a window back into place
1767 		&& fFrame.Width() == zoomedWidth && fFrame.Height() == zoomedHeight) {
1768 		// already zoomed!
1769 		Zoom(fPreviousFrame.LeftTop(), fPreviousFrame.Width(),
1770 			fPreviousFrame.Height());
1771 		return;
1772 	}
1773 
1774 	// Zoom
1775 
1776 	// remember fFrame for later "unzooming"
1777 	fPreviousFrame = fFrame;
1778 
1779 	Zoom(zoomedLeftTop, zoomedWidth, zoomedHeight);
1780 }
1781 
1782 
1783 void
1784 BWindow::ScreenChanged(BRect screen_size, color_space depth)
1785 {
1786 	// Hook function
1787 }
1788 
1789 
1790 void
1791 BWindow::SetPulseRate(bigtime_t rate)
1792 {
1793 	// TODO: What about locking?!?
1794 	if (rate < 0
1795 		|| (rate == fPulseRate && !((rate == 0) ^ (fPulseRunner == NULL))))
1796 		return;
1797 
1798 	fPulseRate = rate;
1799 
1800 	if (rate > 0) {
1801 		if (fPulseRunner == NULL) {
1802 			BMessage message(B_PULSE);
1803 			fPulseRunner = new(std::nothrow) BMessageRunner(BMessenger(this),
1804 				&message, rate);
1805 		} else {
1806 			fPulseRunner->SetInterval(rate);
1807 		}
1808 	} else {
1809 		// rate == 0
1810 		delete fPulseRunner;
1811 		fPulseRunner = NULL;
1812 	}
1813 }
1814 
1815 
1816 bigtime_t
1817 BWindow::PulseRate() const
1818 {
1819 	return fPulseRate;
1820 }
1821 
1822 
1823 void
1824 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMenuItem* item)
1825 {
1826 	Shortcut* shortcut = new(std::nothrow) Shortcut(key, modifiers, item);
1827 	if (shortcut == NULL)
1828 		return;
1829 
1830 	// removes the shortcut if it already exists!
1831 	RemoveShortcut(key, modifiers);
1832 
1833 	fShortcuts.AddItem(shortcut);
1834 }
1835 
1836 
1837 void
1838 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage* message)
1839 {
1840 	AddShortcut(key, modifiers, message, this);
1841 }
1842 
1843 
1844 void
1845 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage* message,
1846 	BHandler* target)
1847 {
1848 	if (message == NULL)
1849 		return;
1850 
1851 	Shortcut* shortcut = new(std::nothrow) Shortcut(key, modifiers, message,
1852 		target);
1853 	if (shortcut == NULL)
1854 		return;
1855 
1856 	// removes the shortcut if it already exists!
1857 	RemoveShortcut(key, modifiers);
1858 
1859 	fShortcuts.AddItem(shortcut);
1860 }
1861 
1862 
1863 bool
1864 BWindow::HasShortcut(uint32 key, uint32 modifiers)
1865 {
1866 	return _FindShortcut(key, modifiers) != NULL;
1867 }
1868 
1869 
1870 void
1871 BWindow::RemoveShortcut(uint32 key, uint32 modifiers)
1872 {
1873 	Shortcut* shortcut = _FindShortcut(key, modifiers);
1874 	if (shortcut != NULL) {
1875 		fShortcuts.RemoveItem(shortcut);
1876 		delete shortcut;
1877 	} else if ((key == 'q' || key == 'Q') && modifiers == B_COMMAND_KEY) {
1878 		// the quit shortcut is a fake shortcut
1879 		fNoQuitShortcut = true;
1880 	}
1881 }
1882 
1883 
1884 BButton*
1885 BWindow::DefaultButton() const
1886 {
1887 	// TODO: What about locking?!?
1888 	return fDefaultButton;
1889 }
1890 
1891 
1892 void
1893 BWindow::SetDefaultButton(BButton* button)
1894 {
1895 	// TODO: What about locking?!?
1896 	if (fDefaultButton == button)
1897 		return;
1898 
1899 	if (fDefaultButton != NULL) {
1900 		// tell old button it's no longer the default one
1901 		BButton* oldDefault = fDefaultButton;
1902 		oldDefault->MakeDefault(false);
1903 		oldDefault->Invalidate();
1904 	}
1905 
1906 	fDefaultButton = button;
1907 
1908 	if (button != NULL) {
1909 		// notify new default button
1910 		fDefaultButton->MakeDefault(true);
1911 		fDefaultButton->Invalidate();
1912 	}
1913 }
1914 
1915 
1916 bool
1917 BWindow::NeedsUpdate() const
1918 {
1919 	if (!const_cast<BWindow*>(this)->Lock())
1920 		return false;
1921 
1922 	fLink->StartMessage(AS_NEEDS_UPDATE);
1923 
1924 	int32 code = B_ERROR;
1925 	fLink->FlushWithReply(code);
1926 
1927 	const_cast<BWindow*>(this)->Unlock();
1928 
1929 	return code == B_OK;
1930 }
1931 
1932 
1933 void
1934 BWindow::UpdateIfNeeded()
1935 {
1936 	// works only from the window thread
1937 	if (find_thread(NULL) != Thread())
1938 		return;
1939 
1940 	// if the queue is already locked we are called recursivly
1941 	// from our own dispatched update message
1942 	if (((const BMessageQueue*)MessageQueue())->IsLocked())
1943 		return;
1944 
1945 	if (!Lock())
1946 		return;
1947 
1948 	// make sure all requests that would cause an update have
1949 	// arrived at the server
1950 	Sync();
1951 
1952 	// Since we're blocking the event loop, we need to retrieve
1953 	// all messages that are pending on the port.
1954 	_DequeueAll();
1955 
1956 	BMessageQueue* queue = MessageQueue();
1957 
1958 	// First process and remove any _UPDATE_ message in the queue
1959 	// With the current design, there can only be one at a time
1960 
1961 	while (true) {
1962 		queue->Lock();
1963 
1964 		BMessage* message = queue->FindMessage(_UPDATE_, 0);
1965 		queue->RemoveMessage(message);
1966 
1967 		queue->Unlock();
1968 
1969 		if (message == NULL)
1970 			break;
1971 
1972 		BWindow::DispatchMessage(message, this);
1973 		delete message;
1974 	}
1975 
1976 	Unlock();
1977 }
1978 
1979 
1980 BView*
1981 BWindow::FindView(const char* viewName) const
1982 {
1983 	BAutolock locker(const_cast<BWindow*>(this));
1984 	if (!locker.IsLocked())
1985 		return NULL;
1986 
1987 	return fTopView->FindView(viewName);
1988 }
1989 
1990 
1991 BView*
1992 BWindow::FindView(BPoint point) const
1993 {
1994 	BAutolock locker(const_cast<BWindow*>(this));
1995 	if (!locker.IsLocked())
1996 		return NULL;
1997 
1998 	// point is assumed to be in window coordinates,
1999 	// fTopView has same bounds as window
2000 	return _FindView(fTopView, point);
2001 }
2002 
2003 
2004 BView*
2005 BWindow::CurrentFocus() const
2006 {
2007 	return fFocus;
2008 }
2009 
2010 
2011 void
2012 BWindow::Activate(bool active)
2013 {
2014 	if (!Lock())
2015 		return;
2016 
2017 	if (!IsHidden()) {
2018 		fMinimized = false;
2019 			// activating a window will also unminimize it
2020 
2021 		fLink->StartMessage(AS_ACTIVATE_WINDOW);
2022 		fLink->Attach<bool>(active);
2023 		fLink->Flush();
2024 	}
2025 
2026 	Unlock();
2027 }
2028 
2029 
2030 void
2031 BWindow::WindowActivated(bool state)
2032 {
2033 	// hook function
2034 	// does nothing
2035 }
2036 
2037 
2038 void
2039 BWindow::ConvertToScreen(BPoint* point) const
2040 {
2041 	point->x += fFrame.left;
2042 	point->y += fFrame.top;
2043 }
2044 
2045 
2046 BPoint
2047 BWindow::ConvertToScreen(BPoint point) const
2048 {
2049 	return point + fFrame.LeftTop();
2050 }
2051 
2052 
2053 void
2054 BWindow::ConvertFromScreen(BPoint* point) const
2055 {
2056 	point->x -= fFrame.left;
2057 	point->y -= fFrame.top;
2058 }
2059 
2060 
2061 BPoint
2062 BWindow::ConvertFromScreen(BPoint point) const
2063 {
2064 	return point - fFrame.LeftTop();
2065 }
2066 
2067 
2068 void
2069 BWindow::ConvertToScreen(BRect* rect) const
2070 {
2071 	rect->OffsetBy(fFrame.LeftTop());
2072 }
2073 
2074 
2075 BRect
2076 BWindow::ConvertToScreen(BRect rect) const
2077 {
2078 	return rect.OffsetByCopy(fFrame.LeftTop());
2079 }
2080 
2081 
2082 void
2083 BWindow::ConvertFromScreen(BRect* rect) const
2084 {
2085 	rect->OffsetBy(-fFrame.left, -fFrame.top);
2086 }
2087 
2088 
2089 BRect
2090 BWindow::ConvertFromScreen(BRect rect) const
2091 {
2092 	return rect.OffsetByCopy(-fFrame.left, -fFrame.top);
2093 }
2094 
2095 
2096 bool
2097 BWindow::IsMinimized() const
2098 {
2099 	BAutolock locker(const_cast<BWindow*>(this));
2100 	if (!locker.IsLocked())
2101 		return false;
2102 
2103 	return fMinimized;
2104 }
2105 
2106 
2107 BRect
2108 BWindow::Bounds() const
2109 {
2110 	return BRect(0, 0, fFrame.Width(), fFrame.Height());
2111 }
2112 
2113 
2114 BRect
2115 BWindow::Frame() const
2116 {
2117 	return fFrame;
2118 }
2119 
2120 
2121 BRect
2122 BWindow::DecoratorFrame() const
2123 {
2124 	BRect decoratorFrame(Frame());
2125 	BRect tabRect(0, 0, 0, 0);
2126 
2127 	float borderWidth = 5.0;
2128 
2129 	BMessage settings;
2130 	if (GetDecoratorSettings(&settings) == B_OK) {
2131 		settings.FindRect("tab frame", &tabRect);
2132 		settings.FindFloat("border width", &borderWidth);
2133 	} else {
2134 		// probably no-border window look
2135 		if (fLook == B_NO_BORDER_WINDOW_LOOK)
2136 			borderWidth = 0.f;
2137 		else if (fLook == B_BORDERED_WINDOW_LOOK)
2138 			borderWidth = 1.f;
2139 		// else use fall-back values from above
2140 	}
2141 
2142 	if (fLook == kLeftTitledWindowLook) {
2143 		decoratorFrame.top -= borderWidth;
2144 		decoratorFrame.left -= borderWidth + tabRect.Width();
2145 		decoratorFrame.right += borderWidth;
2146 		decoratorFrame.bottom += borderWidth;
2147 	} else {
2148 		decoratorFrame.top -= borderWidth + tabRect.Height();
2149 		decoratorFrame.left -= borderWidth;
2150 		decoratorFrame.right += borderWidth;
2151 		decoratorFrame.bottom += borderWidth;
2152 	}
2153 
2154 	return decoratorFrame;
2155 }
2156 
2157 
2158 BSize
2159 BWindow::Size() const
2160 {
2161 	return BSize(fFrame.Width(), fFrame.Height());
2162 }
2163 
2164 
2165 const char*
2166 BWindow::Title() const
2167 {
2168 	return fTitle;
2169 }
2170 
2171 
2172 void
2173 BWindow::SetTitle(const char* title)
2174 {
2175 	if (title == NULL)
2176 		title = "";
2177 
2178 	free(fTitle);
2179 	fTitle = strdup(title);
2180 
2181 	_SetName(title);
2182 
2183 	// we notify the app_server so we can actually see the change
2184 	if (Lock()) {
2185 		fLink->StartMessage(AS_SET_WINDOW_TITLE);
2186 		fLink->AttachString(fTitle);
2187 		fLink->Flush();
2188 		Unlock();
2189 	}
2190 }
2191 
2192 
2193 bool
2194 BWindow::IsActive() const
2195 {
2196 	return fActive;
2197 }
2198 
2199 
2200 void
2201 BWindow::SetKeyMenuBar(BMenuBar* bar)
2202 {
2203 	fKeyMenuBar = bar;
2204 }
2205 
2206 
2207 BMenuBar*
2208 BWindow::KeyMenuBar() const
2209 {
2210 	return fKeyMenuBar;
2211 }
2212 
2213 
2214 bool
2215 BWindow::IsModal() const
2216 {
2217 	return fFeel == B_MODAL_SUBSET_WINDOW_FEEL
2218 		|| fFeel == B_MODAL_APP_WINDOW_FEEL
2219 		|| fFeel == B_MODAL_ALL_WINDOW_FEEL
2220 		|| fFeel == kMenuWindowFeel;
2221 }
2222 
2223 
2224 bool
2225 BWindow::IsFloating() const
2226 {
2227 	return fFeel == B_FLOATING_SUBSET_WINDOW_FEEL
2228 		|| fFeel == B_FLOATING_APP_WINDOW_FEEL
2229 		|| fFeel == B_FLOATING_ALL_WINDOW_FEEL;
2230 }
2231 
2232 
2233 status_t
2234 BWindow::AddToSubset(BWindow* window)
2235 {
2236 	if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL
2237 		|| (fFeel != B_MODAL_SUBSET_WINDOW_FEEL
2238 			&& fFeel != B_FLOATING_SUBSET_WINDOW_FEEL))
2239 		return B_BAD_VALUE;
2240 
2241 	if (!Lock())
2242 		return B_ERROR;
2243 
2244 	status_t status = B_ERROR;
2245 	fLink->StartMessage(AS_ADD_TO_SUBSET);
2246 	fLink->Attach<int32>(_get_object_token_(window));
2247 	fLink->FlushWithReply(status);
2248 
2249 	Unlock();
2250 
2251 	return status;
2252 }
2253 
2254 
2255 status_t
2256 BWindow::RemoveFromSubset(BWindow* window)
2257 {
2258 	if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL
2259 		|| (fFeel != B_MODAL_SUBSET_WINDOW_FEEL
2260 			&& fFeel != B_FLOATING_SUBSET_WINDOW_FEEL))
2261 		return B_BAD_VALUE;
2262 
2263 	if (!Lock())
2264 		return B_ERROR;
2265 
2266 	status_t status = B_ERROR;
2267 	fLink->StartMessage(AS_REMOVE_FROM_SUBSET);
2268 	fLink->Attach<int32>(_get_object_token_(window));
2269 	fLink->FlushWithReply(status);
2270 
2271 	Unlock();
2272 
2273 	return status;
2274 }
2275 
2276 
2277 status_t
2278 BWindow::Perform(perform_code code, void* _data)
2279 {
2280 	switch (code) {
2281 		case PERFORM_CODE_SET_LAYOUT:
2282 		{
2283 			perform_data_set_layout* data = (perform_data_set_layout*)_data;
2284 			BWindow::SetLayout(data->layout);
2285 			return B_OK;
2286 }
2287 	}
2288 
2289 	return BLooper::Perform(code, _data);
2290 }
2291 
2292 
2293 status_t
2294 BWindow::SetType(window_type type)
2295 {
2296 	window_look look;
2297 	window_feel feel;
2298 	_DecomposeType(type, &look, &feel);
2299 
2300 	status_t status = SetLook(look);
2301 	if (status == B_OK)
2302 		status = SetFeel(feel);
2303 
2304 	return status;
2305 }
2306 
2307 
2308 window_type
2309 BWindow::Type() const
2310 {
2311 	return _ComposeType(fLook, fFeel);
2312 }
2313 
2314 
2315 status_t
2316 BWindow::SetLook(window_look look)
2317 {
2318 	BAutolock locker(this);
2319 	if (!locker.IsLocked())
2320 		return B_BAD_VALUE;
2321 
2322 	fLink->StartMessage(AS_SET_LOOK);
2323 	fLink->Attach<int32>((int32)look);
2324 
2325 	status_t status = B_ERROR;
2326 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2327 		fLook = look;
2328 
2329 	// TODO: this could have changed the window size, and thus, we
2330 	//	need to get it from the server (and call _AdoptResize()).
2331 
2332 	return status;
2333 }
2334 
2335 
2336 window_look
2337 BWindow::Look() const
2338 {
2339 	return fLook;
2340 }
2341 
2342 
2343 status_t
2344 BWindow::SetFeel(window_feel feel)
2345 {
2346 	BAutolock locker(this);
2347 	if (!locker.IsLocked())
2348 		return B_BAD_VALUE;
2349 
2350 	fLink->StartMessage(AS_SET_FEEL);
2351 	fLink->Attach<int32>((int32)feel);
2352 
2353 	status_t status = B_ERROR;
2354 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2355 		fFeel = feel;
2356 
2357 	return status;
2358 }
2359 
2360 
2361 window_feel
2362 BWindow::Feel() const
2363 {
2364 	return fFeel;
2365 }
2366 
2367 
2368 status_t
2369 BWindow::SetFlags(uint32 flags)
2370 {
2371 	BAutolock locker(this);
2372 	if (!locker.IsLocked())
2373 		return B_BAD_VALUE;
2374 
2375 	fLink->StartMessage(AS_SET_FLAGS);
2376 	fLink->Attach<uint32>(flags);
2377 
2378 	int32 status = B_ERROR;
2379 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2380 		fFlags = flags;
2381 
2382 	return status;
2383 }
2384 
2385 
2386 uint32
2387 BWindow::Flags() const
2388 {
2389 	return fFlags;
2390 }
2391 
2392 
2393 status_t
2394 BWindow::SetWindowAlignment(window_alignment mode,
2395 	int32 h, int32 hOffset, int32 width, int32 widthOffset,
2396 	int32 v, int32 vOffset, int32 height, int32 heightOffset)
2397 {
2398 	if ((mode & (B_BYTE_ALIGNMENT | B_PIXEL_ALIGNMENT)) == 0
2399 		|| (hOffset >= 0 && hOffset <= h)
2400 		|| (vOffset >= 0 && vOffset <= v)
2401 		|| (widthOffset >= 0 && widthOffset <= width)
2402 		|| (heightOffset >= 0 && heightOffset <= height))
2403 		return B_BAD_VALUE;
2404 
2405 	// TODO: test if hOffset = 0 and set it to 1 if true.
2406 
2407 	if (!Lock())
2408 		return B_ERROR;
2409 
2410 	fLink->StartMessage(AS_SET_ALIGNMENT);
2411 	fLink->Attach<int32>((int32)mode);
2412 	fLink->Attach<int32>(h);
2413 	fLink->Attach<int32>(hOffset);
2414 	fLink->Attach<int32>(width);
2415 	fLink->Attach<int32>(widthOffset);
2416 	fLink->Attach<int32>(v);
2417 	fLink->Attach<int32>(vOffset);
2418 	fLink->Attach<int32>(height);
2419 	fLink->Attach<int32>(heightOffset);
2420 
2421 	status_t status = B_ERROR;
2422 	fLink->FlushWithReply(status);
2423 
2424 	Unlock();
2425 
2426 	return status;
2427 }
2428 
2429 
2430 status_t
2431 BWindow::GetWindowAlignment(window_alignment* mode,
2432 	int32* h, int32* hOffset, int32* width, int32* widthOffset,
2433 	int32* v, int32* vOffset, int32* height, int32* heightOffset) const
2434 {
2435 	if (!const_cast<BWindow*>(this)->Lock())
2436 		return B_ERROR;
2437 
2438 	fLink->StartMessage(AS_GET_ALIGNMENT);
2439 
2440 	status_t status;
2441 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK) {
2442 		fLink->Read<int32>((int32*)mode);
2443 		fLink->Read<int32>(h);
2444 		fLink->Read<int32>(hOffset);
2445 		fLink->Read<int32>(width);
2446 		fLink->Read<int32>(widthOffset);
2447 		fLink->Read<int32>(v);
2448 		fLink->Read<int32>(hOffset);
2449 		fLink->Read<int32>(height);
2450 		fLink->Read<int32>(heightOffset);
2451 	}
2452 
2453 	const_cast<BWindow*>(this)->Unlock();
2454 	return status;
2455 }
2456 
2457 
2458 uint32
2459 BWindow::Workspaces() const
2460 {
2461 	if (!const_cast<BWindow*>(this)->Lock())
2462 		return 0;
2463 
2464 	uint32 workspaces = 0;
2465 
2466 	fLink->StartMessage(AS_GET_WORKSPACES);
2467 
2468 	status_t status;
2469 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2470 		fLink->Read<uint32>(&workspaces);
2471 
2472 	const_cast<BWindow*>(this)->Unlock();
2473 	return workspaces;
2474 }
2475 
2476 
2477 void
2478 BWindow::SetWorkspaces(uint32 workspaces)
2479 {
2480 	// TODO: don't forget about Tracker's background window.
2481 	if (fFeel != B_NORMAL_WINDOW_FEEL)
2482 		return;
2483 
2484 	if (Lock()) {
2485 		fLink->StartMessage(AS_SET_WORKSPACES);
2486 		fLink->Attach<uint32>(workspaces);
2487 		fLink->Flush();
2488 		Unlock();
2489 	}
2490 }
2491 
2492 
2493 BView*
2494 BWindow::LastMouseMovedView() const
2495 {
2496 	return fLastMouseMovedView;
2497 }
2498 
2499 
2500 void
2501 BWindow::MoveBy(float dx, float dy)
2502 {
2503 	if ((dx != 0.0f || dy != 0.0f) && Lock()) {
2504 		MoveTo(fFrame.left + dx, fFrame.top + dy);
2505 		Unlock();
2506 	}
2507 }
2508 
2509 
2510 void
2511 BWindow::MoveTo(BPoint point)
2512 {
2513 	MoveTo(point.x, point.y);
2514 }
2515 
2516 
2517 void
2518 BWindow::MoveTo(float x, float y)
2519 {
2520 	if (!Lock())
2521 		return;
2522 
2523 	x = roundf(x);
2524 	y = roundf(y);
2525 
2526 	if (fFrame.left != x || fFrame.top != y) {
2527 		fLink->StartMessage(AS_WINDOW_MOVE);
2528 		fLink->Attach<float>(x);
2529 		fLink->Attach<float>(y);
2530 
2531 		status_t status;
2532 		if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2533 			fFrame.OffsetTo(x, y);
2534 	}
2535 
2536 	Unlock();
2537 }
2538 
2539 
2540 void
2541 BWindow::ResizeBy(float dx, float dy)
2542 {
2543 	if (Lock()) {
2544 		ResizeTo(fFrame.Width() + dx, fFrame.Height() + dy);
2545 		Unlock();
2546 	}
2547 }
2548 
2549 
2550 void
2551 BWindow::ResizeTo(float width, float height)
2552 {
2553 	if (!Lock())
2554 		return;
2555 
2556 	width = roundf(width);
2557 	height = roundf(height);
2558 
2559 	// stay in minimum & maximum frame limits
2560 	if (width < fMinWidth)
2561 		width = fMinWidth;
2562 	else if (width > fMaxWidth)
2563 		width = fMaxWidth;
2564 
2565 	if (height < fMinHeight)
2566 		height = fMinHeight;
2567 	else if (height > fMaxHeight)
2568 		height = fMaxHeight;
2569 
2570 	if (width != fFrame.Width() || height != fFrame.Height()) {
2571 		fLink->StartMessage(AS_WINDOW_RESIZE);
2572 		fLink->Attach<float>(width);
2573 		fLink->Attach<float>(height);
2574 
2575 		status_t status;
2576 		if (fLink->FlushWithReply(status) == B_OK && status == B_OK) {
2577 			fFrame.right = fFrame.left + width;
2578 			fFrame.bottom = fFrame.top + height;
2579 			_AdoptResize();
2580 		}
2581 	}
2582 
2583 	Unlock();
2584 }
2585 
2586 
2587 void
2588 BWindow::CenterIn(const BRect& rect)
2589 {
2590 	// Set size limits now if needed
2591 	UpdateSizeLimits();
2592 
2593 	MoveTo(BLayoutUtils::AlignInFrame(rect, Size(),
2594 		BAlignment(B_ALIGN_HORIZONTAL_CENTER,
2595 			B_ALIGN_VERTICAL_CENTER)).LeftTop());
2596 }
2597 
2598 
2599 void
2600 BWindow::CenterOnScreen()
2601 {
2602 	BScreen screen(this);
2603 	CenterIn(screen.Frame());
2604 }
2605 
2606 
2607 void
2608 BWindow::Show()
2609 {
2610 	bool runCalled = true;
2611 	if (Lock()) {
2612 		fShowLevel--;
2613 
2614 		_SendShowOrHideMessage();
2615 
2616 		runCalled = fRunCalled;
2617 
2618 		Unlock();
2619 	}
2620 
2621 	if (!runCalled) {
2622 		// This is the fist time Show() is called, which implicitly runs the
2623 		// looper. NOTE: The window is still locked if it has not been
2624 		// run yet, so accessing members is safe.
2625 		if (fLink->SenderPort() < B_OK) {
2626 			// We don't have valid app_server connection; there is no point
2627 			// in starting our looper
2628 			fThread = B_ERROR;
2629 			return;
2630 		} else
2631 			Run();
2632 	}
2633 }
2634 
2635 
2636 void
2637 BWindow::Hide()
2638 {
2639 	if (Lock()) {
2640 		// If we are minimized and are about to be hidden, unminimize
2641 		if (IsMinimized() && fShowLevel == 0)
2642 			Minimize(false);
2643 
2644 		fShowLevel++;
2645 
2646 		_SendShowOrHideMessage();
2647 
2648 		Unlock();
2649 	}
2650 }
2651 
2652 
2653 bool
2654 BWindow::IsHidden() const
2655 {
2656 	return fShowLevel > 0;
2657 }
2658 
2659 
2660 bool
2661 BWindow::QuitRequested()
2662 {
2663 	return BLooper::QuitRequested();
2664 }
2665 
2666 
2667 thread_id
2668 BWindow::Run()
2669 {
2670 	return BLooper::Run();
2671 }
2672 
2673 
2674 void
2675 BWindow::SetLayout(BLayout* layout)
2676 {
2677 	fTopView->SetLayout(layout);
2678 }
2679 
2680 
2681 BLayout*
2682 BWindow::GetLayout() const
2683 {
2684 	return fTopView->GetLayout();
2685 }
2686 
2687 
2688 void
2689 BWindow::InvalidateLayout(bool descendants)
2690 {
2691 	fTopView->InvalidateLayout(descendants);
2692 }
2693 
2694 
2695 void
2696 BWindow::Layout(bool force)
2697 {
2698 	UpdateSizeLimits();
2699 
2700 	// Do the actual layout
2701 	fTopView->Layout(force);
2702 }
2703 
2704 
2705 status_t
2706 BWindow::GetSupportedSuites(BMessage* data)
2707 {
2708 	if (data == NULL)
2709 		return B_BAD_VALUE;
2710 
2711 	status_t status = data->AddString("suites", "suite/vnd.Be-window");
2712 	if (status == B_OK) {
2713 		BPropertyInfo propertyInfo(sWindowPropInfo, sWindowValueInfo);
2714 
2715 		status = data->AddFlat("messages", &propertyInfo);
2716 		if (status == B_OK)
2717 			status = BLooper::GetSupportedSuites(data);
2718 	}
2719 
2720 	return status;
2721 }
2722 
2723 
2724 BHandler*
2725 BWindow::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier,
2726 	int32 what,	const char* property)
2727 {
2728 	if (msg->what == B_WINDOW_MOVE_BY
2729 		|| msg->what == B_WINDOW_MOVE_TO)
2730 		return this;
2731 
2732 	BPropertyInfo propertyInfo(sWindowPropInfo);
2733 	if (propertyInfo.FindMatch(msg, index, specifier, what, property) >= 0) {
2734 		if (!strcmp(property, "View")) {
2735 			// we will NOT pop the current specifier
2736 			return fTopView;
2737 		} else if (!strcmp(property, "MenuBar")) {
2738 			if (fKeyMenuBar) {
2739 				msg->PopSpecifier();
2740 				return fKeyMenuBar;
2741 			} else {
2742 				BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
2743 				replyMsg.AddInt32("error", B_NAME_NOT_FOUND);
2744 				replyMsg.AddString("message",
2745 					"This window doesn't have a main MenuBar");
2746 				msg->SendReply(&replyMsg);
2747 				return NULL;
2748 			}
2749 		} else
2750 			return this;
2751 	}
2752 
2753 	return BLooper::ResolveSpecifier(msg, index, specifier, what, property);
2754 }
2755 
2756 
2757 //	#pragma mark - Private Methods
2758 
2759 
2760 void
2761 BWindow::_InitData(BRect frame, const char* title, window_look look,
2762 	window_feel feel, uint32 flags,	uint32 workspace, int32 bitmapToken)
2763 {
2764 	STRACE(("BWindow::InitData()\n"));
2765 
2766 	if (be_app == NULL) {
2767 		debugger("You need a valid BApplication object before interacting with "
2768 			"the app_server");
2769 		return;
2770 	}
2771 
2772 	frame.left = roundf(frame.left);
2773 	frame.top = roundf(frame.top);
2774 	frame.right = roundf(frame.right);
2775 	frame.bottom = roundf(frame.bottom);
2776 
2777 	fFrame = frame;
2778 
2779 	if (title == NULL)
2780 		title = "";
2781 
2782 	fTitle = strdup(title);
2783 
2784 	_SetName(title);
2785 
2786 	fFeel = feel;
2787 	fLook = look;
2788 	fFlags = flags | B_ASYNCHRONOUS_CONTROLS;
2789 
2790 	fInTransaction = bitmapToken >= 0;
2791 	fUpdateRequested = false;
2792 	fActive = false;
2793 	fShowLevel = 1;
2794 
2795 	fTopView = NULL;
2796 	fFocus = NULL;
2797 	fLastMouseMovedView	= NULL;
2798 	fIdleMouseRunner = NULL;
2799 	fKeyMenuBar = NULL;
2800 	fDefaultButton = NULL;
2801 
2802 	// Shortcut 'Q' is handled in _HandleKeyDown() directly, as its message
2803 	// get sent to the application, and not one of our handlers.
2804 	// It is only installed for non-modal windows, though.
2805 	fNoQuitShortcut = IsModal();
2806 
2807 	if ((fFlags & B_NOT_CLOSABLE) == 0 && !IsModal()) {
2808 		// Modal windows default to non-closable, but you can add the shortcut manually,
2809 		// if a different behaviour is wanted
2810 		AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED));
2811 	}
2812 
2813 	AddShortcut('X', B_COMMAND_KEY, new BMessage(B_CUT), NULL);
2814 	AddShortcut('C', B_COMMAND_KEY, new BMessage(B_COPY), NULL);
2815 	AddShortcut('V', B_COMMAND_KEY, new BMessage(B_PASTE), NULL);
2816 	AddShortcut('A', B_COMMAND_KEY, new BMessage(B_SELECT_ALL), NULL);
2817 
2818 	// Window modifier keys
2819 	AddShortcut('M', B_COMMAND_KEY | B_CONTROL_KEY,
2820 		new BMessage(_MINIMIZE_), NULL);
2821 	AddShortcut('Z', B_COMMAND_KEY | B_CONTROL_KEY,
2822 		new BMessage(_ZOOM_), NULL);
2823 	AddShortcut('H', B_COMMAND_KEY | B_CONTROL_KEY,
2824 		new BMessage(B_HIDE_APPLICATION), NULL);
2825 	AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY,
2826 		new BMessage(_SEND_TO_FRONT_), NULL);
2827 	AddShortcut('B', B_COMMAND_KEY | B_CONTROL_KEY,
2828 		new BMessage(_SEND_BEHIND_), NULL);
2829 
2830 	// Workspace modifier keys
2831 	BMessage* message;
2832 	message = new BMessage(_SWITCH_WORKSPACE_);
2833 	message->AddInt32("delta_x", -1);
2834 	AddShortcut(B_LEFT_ARROW, B_COMMAND_KEY | B_CONTROL_KEY, message, NULL);
2835 
2836 	message = new BMessage(_SWITCH_WORKSPACE_);
2837 	message->AddInt32("delta_x", 1);
2838 	AddShortcut(B_RIGHT_ARROW, B_COMMAND_KEY | B_CONTROL_KEY, message, NULL);
2839 
2840 	message = new BMessage(_SWITCH_WORKSPACE_);
2841 	message->AddInt32("delta_y", -1);
2842 	AddShortcut(B_UP_ARROW, B_COMMAND_KEY | B_CONTROL_KEY, message, NULL);
2843 
2844 	message = new BMessage(_SWITCH_WORKSPACE_);
2845 	message->AddInt32("delta_y", 1);
2846 	AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_CONTROL_KEY, message, NULL);
2847 
2848 	message = new BMessage(_SWITCH_WORKSPACE_);
2849 	message->AddBool("take_me_there", true);
2850 	message->AddInt32("delta_x", -1);
2851 	AddShortcut(B_LEFT_ARROW, B_COMMAND_KEY | B_CONTROL_KEY | B_SHIFT_KEY, message, NULL);
2852 
2853 	message = new BMessage(_SWITCH_WORKSPACE_);
2854 	message->AddBool("take_me_there", true);
2855 	message->AddInt32("delta_x", 1);
2856 	AddShortcut(B_RIGHT_ARROW, B_COMMAND_KEY | B_CONTROL_KEY | B_SHIFT_KEY, message, NULL);
2857 
2858 	message = new BMessage(_SWITCH_WORKSPACE_);
2859 	message->AddBool("take_me_there", true);
2860 	message->AddInt32("delta_y", -1);
2861 	AddShortcut(B_UP_ARROW, B_COMMAND_KEY | B_CONTROL_KEY | B_SHIFT_KEY, message, NULL);
2862 
2863 	message = new BMessage(_SWITCH_WORKSPACE_);
2864 	message->AddBool("take_me_there", true);
2865 	message->AddInt32("delta_y", 1);
2866 	AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_CONTROL_KEY | B_SHIFT_KEY, message, NULL);
2867 
2868 	// We set the default pulse rate, but we don't start the pulse
2869 	fPulseRate = 500000;
2870 	fPulseRunner = NULL;
2871 
2872 	fIsFilePanel = false;
2873 
2874 	fMenuSem = -1;
2875 
2876 	fMinimized = false;
2877 
2878 	fMaxZoomHeight = 32768.0;
2879 	fMaxZoomWidth = 32768.0;
2880 	fMinHeight = 0.0;
2881 	fMinWidth = 0.0;
2882 	fMaxHeight = 32768.0;
2883 	fMaxWidth = 32768.0;
2884 
2885 	fLastViewToken = B_NULL_TOKEN;
2886 
2887 	// TODO: other initializations!
2888 	fOffscreen = false;
2889 
2890 	// Create the server-side window
2891 
2892 	port_id receivePort = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY, "w<app_server");
2893 	if (receivePort < B_OK) {
2894 		// TODO: huh?
2895 		debugger("Could not create BWindow's receive port, used for interacting with the app_server!");
2896 		delete this;
2897 		return;
2898 	}
2899 
2900 	STRACE(("BWindow::InitData(): contacting app_server...\n"));
2901 
2902 	// let app_server know that a window has been created.
2903 	fLink = new(std::nothrow) BPrivate::PortLink(
2904 		BApplication::Private::ServerLink()->SenderPort(), receivePort);
2905 	if (fLink == NULL) {
2906 		// Zombie!
2907 		return;
2908 	}
2909 
2910 	{
2911 		BPrivate::AppServerLink lockLink;
2912 			// we're talking to the server application using our own
2913 			// communication channel (fLink) - we better make sure no one
2914 			// interferes by locking that channel (which AppServerLink does
2915 			// implicetly)
2916 
2917 		if (bitmapToken < 0) {
2918 			fLink->StartMessage(AS_CREATE_WINDOW);
2919 		} else {
2920 			fLink->StartMessage(AS_CREATE_OFFSCREEN_WINDOW);
2921 			fLink->Attach<int32>(bitmapToken);
2922 			fOffscreen = true;
2923 		}
2924 
2925 		fLink->Attach<BRect>(fFrame);
2926 		fLink->Attach<uint32>((uint32)fLook);
2927 		fLink->Attach<uint32>((uint32)fFeel);
2928 		fLink->Attach<uint32>(fFlags);
2929 		fLink->Attach<uint32>(workspace);
2930 		fLink->Attach<int32>(_get_object_token_(this));
2931 		fLink->Attach<port_id>(receivePort);
2932 		fLink->Attach<port_id>(fMsgPort);
2933 		fLink->AttachString(title);
2934 
2935 		port_id sendPort;
2936 		int32 code;
2937 		if (fLink->FlushWithReply(code) == B_OK
2938 			&& code == B_OK
2939 			&& fLink->Read<port_id>(&sendPort) == B_OK) {
2940 			// read the frame size and its limits that were really
2941 			// enforced on the server side
2942 
2943 			fLink->Read<BRect>(&fFrame);
2944 			fLink->Read<float>(&fMinWidth);
2945 			fLink->Read<float>(&fMaxWidth);
2946 			fLink->Read<float>(&fMinHeight);
2947 			fLink->Read<float>(&fMaxHeight);
2948 
2949 			fMaxZoomWidth = fMaxWidth;
2950 			fMaxZoomHeight = fMaxHeight;
2951 		} else
2952 			sendPort = -1;
2953 
2954 		// Redirect our link to the new window connection
2955 		fLink->SetSenderPort(sendPort);
2956 	}
2957 
2958 	STRACE(("Server says that our send port is %ld\n", sendPort));
2959 	STRACE(("Window locked?: %s\n", IsLocked() ? "True" : "False"));
2960 
2961 	_CreateTopView();
2962 }
2963 
2964 
2965 //! Rename the handler and its thread
2966 void
2967 BWindow::_SetName(const char* title)
2968 {
2969 	if (title == NULL)
2970 		title = "";
2971 
2972 	// we will change BWindow's thread name to "w>window title"
2973 
2974 	char threadName[B_OS_NAME_LENGTH];
2975 	strcpy(threadName, "w>");
2976 #ifdef __HAIKU__
2977 	strlcat(threadName, title, B_OS_NAME_LENGTH);
2978 #else
2979 	int32 length = strlen(title);
2980 	length = min_c(length, B_OS_NAME_LENGTH - 3);
2981 	memcpy(threadName + 2, title, length);
2982 	threadName[length + 2] = '\0';
2983 #endif
2984 
2985 	// change the handler's name
2986 	SetName(threadName);
2987 
2988 	// if the message loop has been started...
2989 	if (Thread() >= B_OK)
2990 		rename_thread(Thread(), threadName);
2991 }
2992 
2993 
2994 //!	Reads all pending messages from the window port and put them into the queue.
2995 void
2996 BWindow::_DequeueAll()
2997 {
2998 	//	Get message count from port
2999 	int32 count = port_count(fMsgPort);
3000 
3001 	for (int32 i = 0; i < count; i++) {
3002 		BMessage* message = MessageFromPort(0);
3003 		if (message != NULL)
3004 			fDirectTarget->Queue()->AddMessage(message);
3005 	}
3006 }
3007 
3008 
3009 /*!	This here is an almost complete code duplication to BLooper::task_looper()
3010 	but with some important differences:
3011 	 a)	it uses the _DetermineTarget() method to tell what the later target of
3012 		a message will be, if no explicit target is supplied.
3013 	 b)	it calls _UnpackMessage() and _SanitizeMessage() to duplicate the message
3014 		to all of its intended targets, and to add all fields the target would
3015 		expect in such a message.
3016 
3017 	This is important because the app_server sends all input events to the
3018 	preferred handler, and expects them to be correctly distributed to their
3019 	intended targets.
3020 */
3021 void
3022 BWindow::task_looper()
3023 {
3024 	STRACE(("info: BWindow::task_looper() started.\n"));
3025 
3026 	// Check that looper is locked (should be)
3027 	AssertLocked();
3028 	Unlock();
3029 
3030 	if (IsLocked())
3031 		debugger("window must not be locked!");
3032 
3033 	while (!fTerminating) {
3034 		// Did we get a message?
3035 		BMessage* msg = MessageFromPort();
3036 		if (msg)
3037 			_AddMessagePriv(msg);
3038 
3039 		//	Get message count from port
3040 		int32 msgCount = port_count(fMsgPort);
3041 		for (int32 i = 0; i < msgCount; ++i) {
3042 			// Read 'count' messages from port (so we will not block)
3043 			// We use zero as our timeout since we know there is stuff there
3044 			msg = MessageFromPort(0);
3045 			// Add messages to queue
3046 			if (msg)
3047 				_AddMessagePriv(msg);
3048 		}
3049 
3050 		bool dispatchNextMessage = true;
3051 		while (!fTerminating && dispatchNextMessage) {
3052 			// Get next message from queue (assign to fLastMessage)
3053 			fLastMessage = fDirectTarget->Queue()->NextMessage();
3054 
3055 			// Lock the looper
3056 			if (!Lock())
3057 				break;
3058 
3059 			if (!fLastMessage) {
3060 				// No more messages: Unlock the looper and terminate the
3061 				// dispatch loop.
3062 				dispatchNextMessage = false;
3063 			} else {
3064 				// Get the target handler
3065 				BMessage::Private messagePrivate(fLastMessage);
3066 				bool usePreferred = messagePrivate.UsePreferredTarget();
3067 				BHandler* handler = NULL;
3068 				bool dropMessage = false;
3069 
3070 				if (usePreferred) {
3071 					handler = PreferredHandler();
3072 					if (handler == NULL)
3073 						handler = this;
3074 				} else {
3075 					gDefaultTokens.GetToken(messagePrivate.GetTarget(),
3076 						B_HANDLER_TOKEN, (void**)&handler);
3077 
3078 					// if this handler doesn't belong to us, we drop the message
3079 					if (handler != NULL && handler->Looper() != this) {
3080 						dropMessage = true;
3081 						handler = NULL;
3082 					}
3083 				}
3084 
3085 				if ((handler == NULL && !dropMessage) || usePreferred)
3086 					handler = _DetermineTarget(fLastMessage, handler);
3087 
3088 				unpack_cookie cookie;
3089 				while (_UnpackMessage(cookie, &fLastMessage, &handler, &usePreferred)) {
3090 					// if there is no target handler, the message is dropped
3091 					if (handler != NULL) {
3092 						_SanitizeMessage(fLastMessage, handler, usePreferred);
3093 
3094 						// Is this a scripting message?
3095 						if (fLastMessage->HasSpecifiers()) {
3096 							int32 index = 0;
3097 							// Make sure the current specifier is kosher
3098 							if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
3099 								handler = resolve_specifier(handler, fLastMessage);
3100 						}
3101 
3102 						if (handler != NULL)
3103 							handler = _TopLevelFilter(fLastMessage, handler);
3104 
3105 						if (handler != NULL)
3106 							DispatchMessage(fLastMessage, handler);
3107 					}
3108 
3109 					// Delete the current message
3110 					delete fLastMessage;
3111 					fLastMessage = NULL;
3112 				}
3113 			}
3114 
3115 			if (fTerminating) {
3116 				// we leave the looper locked when we quit
3117 				return;
3118 			}
3119 
3120 			Unlock();
3121 
3122 			// Are any messages on the port?
3123 			if (port_count(fMsgPort) > 0) {
3124 				// Do outer loop
3125 				dispatchNextMessage = false;
3126 			}
3127 		}
3128 	}
3129 }
3130 
3131 
3132 window_type
3133 BWindow::_ComposeType(window_look look, window_feel feel) const
3134 {
3135 	switch (feel) {
3136 		case B_NORMAL_WINDOW_FEEL:
3137 			switch (look) {
3138 				case B_TITLED_WINDOW_LOOK:
3139 					return B_TITLED_WINDOW;
3140 
3141 				case B_DOCUMENT_WINDOW_LOOK:
3142 					return B_DOCUMENT_WINDOW;
3143 
3144 				case B_BORDERED_WINDOW_LOOK:
3145 					return B_BORDERED_WINDOW;
3146 
3147 				default:
3148 					return B_UNTYPED_WINDOW;
3149 			}
3150 			break;
3151 
3152 		case B_MODAL_APP_WINDOW_FEEL:
3153 			if (look == B_MODAL_WINDOW_LOOK)
3154 				return B_MODAL_WINDOW;
3155 			break;
3156 
3157 		case B_FLOATING_APP_WINDOW_FEEL:
3158 			if (look == B_FLOATING_WINDOW_LOOK)
3159 				return B_FLOATING_WINDOW;
3160 			break;
3161 
3162 		default:
3163 			return B_UNTYPED_WINDOW;
3164 	}
3165 
3166 	return B_UNTYPED_WINDOW;
3167 }
3168 
3169 
3170 void
3171 BWindow::_DecomposeType(window_type type, window_look* _look,
3172 	window_feel* _feel) const
3173 {
3174 	switch (type) {
3175 		case B_DOCUMENT_WINDOW:
3176 			*_look = B_DOCUMENT_WINDOW_LOOK;
3177 			*_feel = B_NORMAL_WINDOW_FEEL;
3178 			break;
3179 
3180 		case B_MODAL_WINDOW:
3181 			*_look = B_MODAL_WINDOW_LOOK;
3182 			*_feel = B_MODAL_APP_WINDOW_FEEL;
3183 			break;
3184 
3185 		case B_FLOATING_WINDOW:
3186 			*_look = B_FLOATING_WINDOW_LOOK;
3187 			*_feel = B_FLOATING_APP_WINDOW_FEEL;
3188 			break;
3189 
3190 		case B_BORDERED_WINDOW:
3191 			*_look = B_BORDERED_WINDOW_LOOK;
3192 			*_feel = B_NORMAL_WINDOW_FEEL;
3193 			break;
3194 
3195 		case B_TITLED_WINDOW:
3196 		case B_UNTYPED_WINDOW:
3197 		default:
3198 			*_look = B_TITLED_WINDOW_LOOK;
3199 			*_feel = B_NORMAL_WINDOW_FEEL;
3200 			break;
3201 	}
3202 }
3203 
3204 
3205 void
3206 BWindow::_CreateTopView()
3207 {
3208 	STRACE(("_CreateTopView(): enter\n"));
3209 
3210 	BRect frame = fFrame.OffsetToCopy(B_ORIGIN);
3211 	// TODO: what to do here about std::nothrow?
3212 	fTopView = new BView(frame, "fTopView",
3213 		B_FOLLOW_ALL, B_WILL_DRAW);
3214 	fTopView->fTopLevelView = true;
3215 
3216 	//inhibit check_lock()
3217 	fLastViewToken = _get_object_token_(fTopView);
3218 
3219 	// set fTopView's owner, add it to window's eligible handler list
3220 	// and also set its next handler to be this window.
3221 
3222 	STRACE(("Calling setowner fTopView = %p this = %p.\n",
3223 		fTopView, this));
3224 
3225 	fTopView->_SetOwner(this);
3226 
3227 	// we can't use AddChild() because this is the top view
3228 	fTopView->_CreateSelf();
3229 
3230 	STRACE(("BuildTopView ended\n"));
3231 }
3232 
3233 
3234 /*!
3235 	Resizes the top view to match the window size. This will also
3236 	adapt the size of all its child views as needed.
3237 	This method has to be called whenever the frame of the window
3238 	changes.
3239 */
3240 void
3241 BWindow::_AdoptResize()
3242 {
3243 	// Resize views according to their resize modes - this
3244 	// saves us some server communication, as the server
3245 	// does the same with our views on its side.
3246 
3247 	int32 deltaWidth = (int32)(fFrame.Width() - fTopView->Bounds().Width());
3248 	int32 deltaHeight = (int32)(fFrame.Height() - fTopView->Bounds().Height());
3249 	if (deltaWidth == 0 && deltaHeight == 0)
3250 		return;
3251 
3252 	fTopView->_ResizeBy(deltaWidth, deltaHeight);
3253 }
3254 
3255 
3256 void
3257 BWindow::_SetFocus(BView* focusView, bool notifyInputServer)
3258 {
3259 	if (fFocus == focusView)
3260 		return;
3261 
3262 	// we notify the input server if we are passing focus
3263 	// from a view which has the B_INPUT_METHOD_AWARE to a one
3264 	// which does not, or vice-versa
3265 	if (notifyInputServer && fActive) {
3266 		bool inputMethodAware = false;
3267 		if (focusView)
3268 			inputMethodAware = focusView->Flags() & B_INPUT_METHOD_AWARE;
3269 		BMessage msg(inputMethodAware ? IS_FOCUS_IM_AWARE_VIEW : IS_UNFOCUS_IM_AWARE_VIEW);
3270 		BMessenger messenger(focusView);
3271 		BMessage reply;
3272 		if (focusView)
3273 			msg.AddMessenger("view", messenger);
3274 		_control_input_server_(&msg, &reply);
3275 	}
3276 
3277 	fFocus = focusView;
3278 	SetPreferredHandler(focusView);
3279 }
3280 
3281 
3282 /*!
3283 	\brief Determines the target of a message received for the
3284 		focus view.
3285 */
3286 BHandler*
3287 BWindow::_DetermineTarget(BMessage* message, BHandler* target)
3288 {
3289 	if (target == NULL)
3290 		target = this;
3291 
3292 	switch (message->what) {
3293 		case B_KEY_DOWN:
3294 		case B_KEY_UP:
3295 		{
3296 			// if we have a default button, it might want to hear
3297 			// about pressing the <enter> key
3298 			int32 rawChar;
3299 			if (DefaultButton() != NULL
3300 				&& message->FindInt32("raw_char", &rawChar) == B_OK
3301 				&& rawChar == B_ENTER)
3302 				return DefaultButton();
3303 
3304 			// supposed to fall through
3305 		}
3306 		case B_UNMAPPED_KEY_DOWN:
3307 		case B_UNMAPPED_KEY_UP:
3308 		case B_MODIFIERS_CHANGED:
3309 			// these messages should be dispatched by the focus view
3310 			if (CurrentFocus() != NULL)
3311 				return CurrentFocus();
3312 			break;
3313 
3314 		case B_MOUSE_DOWN:
3315 		case B_MOUSE_UP:
3316 		case B_MOUSE_MOVED:
3317 		case B_MOUSE_WHEEL_CHANGED:
3318 		case B_MOUSE_IDLE:
3319 			// is there a token of the view that is currently under the mouse?
3320 			int32 token;
3321 			if (message->FindInt32("_view_token", &token) == B_OK) {
3322 				BView* view = _FindView(token);
3323 				if (view != NULL)
3324 					return view;
3325 			}
3326 
3327 			// if there is no valid token in the message, we try our
3328 			// luck with the last target, if available
3329 			if (fLastMouseMovedView != NULL)
3330 				return fLastMouseMovedView;
3331 			break;
3332 
3333 		case B_PULSE:
3334 		case B_QUIT_REQUESTED:
3335 			// TODO: test whether R5 will let BView dispatch these messages
3336 			return this;
3337 
3338 		case _MESSAGE_DROPPED_:
3339 			if (fLastMouseMovedView != NULL)
3340 				return fLastMouseMovedView;
3341 			break;
3342 
3343 		default:
3344 			break;
3345 	}
3346 
3347 	return target;
3348 }
3349 
3350 
3351 /*!	\brief Determines whether or not this message has targeted the focus view.
3352 
3353 	This will return \c false only if the message did not go to the preferred
3354 	handler, or if the packed message does not contain address the focus view
3355 	at all.
3356 */
3357 bool
3358 BWindow::_IsFocusMessage(BMessage* message)
3359 {
3360 	BMessage::Private messagePrivate(message);
3361 	if (!messagePrivate.UsePreferredTarget())
3362 		return false;
3363 
3364 	bool feedFocus;
3365 	if (message->HasInt32("_token")
3366 		&& (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus))
3367 		return false;
3368 
3369 	return true;
3370 }
3371 
3372 
3373 /*!	\brief Distributes the message to its intended targets. This is done for
3374 		all messages that should go to the preferred handler.
3375 
3376 	Returns \c true in case the message should still be dispatched
3377 */
3378 bool
3379 BWindow::_UnpackMessage(unpack_cookie& cookie, BMessage** _message,
3380 	BHandler** _target, bool* _usePreferred)
3381 {
3382 	if (cookie.message == NULL)
3383 		return false;
3384 
3385 	if (cookie.index == 0 && !cookie.tokens_scanned) {
3386 		// We were called the first time for this message
3387 
3388 		if (!*_usePreferred) {
3389 			// only consider messages targeted at the preferred handler
3390 			cookie.message = NULL;
3391 			return true;
3392 		}
3393 
3394 		// initialize our cookie
3395 		cookie.message = *_message;
3396 		cookie.focus = *_target;
3397 
3398 		if (cookie.focus != NULL)
3399 			cookie.focus_token = _get_object_token_(*_target);
3400 
3401 		if (fLastMouseMovedView != NULL && cookie.message->what == B_MOUSE_MOVED)
3402 			cookie.last_view_token = _get_object_token_(fLastMouseMovedView);
3403 
3404 		*_usePreferred = false;
3405 	}
3406 
3407 	_DequeueAll();
3408 
3409 	// distribute the message to all targets specified in the
3410 	// message directly (but not to the focus view)
3411 
3412 	for (int32 token; !cookie.tokens_scanned
3413 			&& cookie.message->FindInt32("_token", cookie.index, &token)
3414 				== B_OK;
3415 			cookie.index++) {
3416 		// focus view is preferred and should get its message directly
3417 		if (token == cookie.focus_token) {
3418 			cookie.found_focus = true;
3419 			continue;
3420 		}
3421 		if (token == cookie.last_view_token)
3422 			continue;
3423 
3424 		BView* target = _FindView(token);
3425 		if (target == NULL)
3426 			continue;
3427 
3428 		*_message = new BMessage(*cookie.message);
3429 		*_target = target;
3430 		cookie.index++;
3431 		return true;
3432 	}
3433 
3434 	cookie.tokens_scanned = true;
3435 
3436 	// if there is a last mouse moved view, and the new focus is
3437 	// different, the previous view wants to get its B_EXITED_VIEW
3438 	// message
3439 	if (cookie.last_view_token != B_NULL_TOKEN && fLastMouseMovedView != NULL
3440 		&& fLastMouseMovedView != cookie.focus) {
3441 		*_message = new BMessage(*cookie.message);
3442 		*_target = fLastMouseMovedView;
3443 		cookie.last_view_token = B_NULL_TOKEN;
3444 		return true;
3445 	}
3446 
3447 	bool dispatchToFocus = true;
3448 
3449 	// check if the focus token is still valid (could have been removed in the mean time)
3450 	BHandler* handler;
3451 	if (gDefaultTokens.GetToken(cookie.focus_token, B_HANDLER_TOKEN, (void**)&handler) != B_OK
3452 		|| handler->Looper() != this)
3453 		dispatchToFocus = false;
3454 
3455 	if (dispatchToFocus && cookie.index > 0) {
3456 		// should this message still be dispatched by the focus view?
3457 		bool feedFocus;
3458 		if (!cookie.found_focus
3459 			&& (cookie.message->FindBool("_feed_focus", &feedFocus) != B_OK
3460 				|| feedFocus == false))
3461 			dispatchToFocus = false;
3462 	}
3463 
3464 	if (!dispatchToFocus) {
3465 		delete cookie.message;
3466 		cookie.message = NULL;
3467 		return false;
3468 	}
3469 
3470 	*_message = cookie.message;
3471 	*_target = cookie.focus;
3472 	*_usePreferred = true;
3473 	cookie.message = NULL;
3474 	return true;
3475 }
3476 
3477 
3478 /*!	Some messages don't get to the window in a shape an application should see.
3479 	This method is supposed to give a message the last grinding before
3480 	it's acceptable for the receiving application.
3481 */
3482 void
3483 BWindow::_SanitizeMessage(BMessage* message, BHandler* target, bool usePreferred)
3484 {
3485 	if (target == NULL)
3486 		return;
3487 
3488 	switch (message->what) {
3489 		case B_MOUSE_MOVED:
3490 		case B_MOUSE_UP:
3491 		case B_MOUSE_DOWN:
3492 		{
3493 			BPoint where;
3494 			if (message->FindPoint("screen_where", &where) != B_OK)
3495 				break;
3496 
3497 			BView* view = dynamic_cast<BView*>(target);
3498 
3499 			if (!view || message->what == B_MOUSE_MOVED) {
3500 				// add local window coordinates, only
3501 				// for regular mouse moved messages
3502 				message->AddPoint("where", ConvertFromScreen(where));
3503 			}
3504 
3505 			if (view != NULL) {
3506 				// add local view coordinates
3507 				BPoint viewWhere = view->ConvertFromScreen(where);
3508 				if (message->what != B_MOUSE_MOVED) {
3509 					// Yep, the meaning of "where" is different
3510 					// for regular mouse moved messages versus
3511 					// mouse up/down!
3512 					message->AddPoint("where", viewWhere);
3513 				}
3514 				message->AddPoint("be:view_where", viewWhere);
3515 
3516 				if (message->what == B_MOUSE_MOVED) {
3517 					// is there a token of the view that is currently under
3518 					// the mouse?
3519 					BView* viewUnderMouse = NULL;
3520 					int32 token;
3521 					if (message->FindInt32("_view_token", &token) == B_OK)
3522 						viewUnderMouse = _FindView(token);
3523 
3524 					// add transit information
3525 					uint32 transit
3526 						= _TransitForMouseMoved(view, viewUnderMouse);
3527 					message->AddInt32("be:transit", transit);
3528 
3529 					if (usePreferred)
3530 						fLastMouseMovedView = viewUnderMouse;
3531 				}
3532 			}
3533 			break;
3534 		}
3535 
3536 		case _MESSAGE_DROPPED_:
3537 		{
3538 			uint32 originalWhat;
3539 			if (message->FindInt32("_original_what", (int32*)&originalWhat) == B_OK) {
3540 				message->what = originalWhat;
3541 				message->RemoveName("_original_what");
3542 			}
3543 			break;
3544 		}
3545 	}
3546 }
3547 
3548 
3549 /*!
3550 	This is called by BView::GetMouse() when a B_MOUSE_MOVED message
3551 	is removed from the queue.
3552 	It allows the window to update the last mouse moved view, and
3553 	let it decide if this message should be kept. It will also remove
3554 	the message from the queue.
3555 	You need to hold the message queue lock when calling this method!
3556 
3557 	\return true if this message can be used to get the mouse data from,
3558 	\return false if this is not meant for the public.
3559 */
3560 bool
3561 BWindow::_StealMouseMessage(BMessage* message, bool& deleteMessage)
3562 {
3563 	BMessage::Private messagePrivate(message);
3564 	if (!messagePrivate.UsePreferredTarget()) {
3565 		// this message is targeted at a specific handler, so we should
3566 		// not steal it
3567 		return false;
3568 	}
3569 
3570 	int32 token;
3571 	if (message->FindInt32("_token", 0, &token) == B_OK) {
3572 		// This message has other targets, so we can't remove it;
3573 		// just prevent it from being sent to the preferred handler
3574 		// again (if it should have gotten it at all).
3575 		bool feedFocus;
3576 		if (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus)
3577 			return false;
3578 
3579 		message->RemoveName("_feed_focus");
3580 		deleteMessage = false;
3581 	} else {
3582 		deleteMessage = true;
3583 
3584 		if (message->what == B_MOUSE_MOVED) {
3585 			// We need to update the last mouse moved view, as this message
3586 			// won't make it to _SanitizeMessage() anymore.
3587 			BView* viewUnderMouse = NULL;
3588 			int32 token;
3589 			if (message->FindInt32("_view_token", &token) == B_OK)
3590 				viewUnderMouse = _FindView(token);
3591 
3592 			// Don't remove important transit messages!
3593 			uint32 transit = _TransitForMouseMoved(fLastMouseMovedView,
3594 				viewUnderMouse);
3595 			if (transit == B_ENTERED_VIEW || transit == B_EXITED_VIEW)
3596 				deleteMessage = false;
3597 		}
3598 
3599 		if (deleteMessage) {
3600 			// The message is only thought for the preferred handler, so we
3601 			// can just remove it.
3602 			MessageQueue()->RemoveMessage(message);
3603 		}
3604 	}
3605 
3606 	return true;
3607 }
3608 
3609 
3610 uint32
3611 BWindow::_TransitForMouseMoved(BView* view, BView* viewUnderMouse) const
3612 {
3613 	uint32 transit;
3614 	if (viewUnderMouse == view) {
3615 		// the mouse is over the target view
3616 		if (fLastMouseMovedView != view)
3617 			transit = B_ENTERED_VIEW;
3618 		else
3619 			transit = B_INSIDE_VIEW;
3620 	} else {
3621 		// the mouse is not over the target view
3622 		if (view == fLastMouseMovedView)
3623 			transit = B_EXITED_VIEW;
3624 		else
3625 			transit = B_OUTSIDE_VIEW;
3626 	}
3627 	return transit;
3628 }
3629 
3630 
3631 /*!	Forwards the key to the switcher
3632 */
3633 void
3634 BWindow::_Switcher(int32 rawKey, uint32 modifiers, bool repeat)
3635 {
3636 	// only send the first key press, no repeats
3637 	if (repeat)
3638 		return;
3639 
3640 	BMessenger deskbar(kDeskbarSignature);
3641 	if (!deskbar.IsValid()) {
3642 		// TODO: have some kind of fallback-handling in case the Deskbar is
3643 		// not available?
3644 		return;
3645 	}
3646 
3647 	BMessage message('TASK');
3648 	message.AddInt32("key", rawKey);
3649 	message.AddInt32("modifiers", modifiers);
3650 	message.AddInt64("when", system_time());
3651 	message.AddInt32("team", Team());
3652 	deskbar.SendMessage(&message);
3653 }
3654 
3655 
3656 /*!	Handles keyboard input before it gets forwarded to the target handler.
3657 	This includes shortcut evaluation, keyboard navigation, etc.
3658 
3659 	\return handled if true, the event was already handled, and will not
3660 		be forwarded to the target handler.
3661 
3662 	TODO: must also convert the incoming key to the font encoding of the target
3663 */
3664 bool
3665 BWindow::_HandleKeyDown(BMessage* event)
3666 {
3667 	// Only handle special functions when the event targeted the active focus
3668 	// view
3669 	if (!_IsFocusMessage(event))
3670 		return false;
3671 
3672 	const char* string = NULL;
3673 	if (event->FindString("bytes", &string) != B_OK)
3674 		return false;
3675 
3676 	char key = string[0];
3677 
3678 	uint32 modifiers;
3679 	if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK)
3680 		modifiers = 0;
3681 
3682 	// handle BMenuBar key
3683 	if (key == B_ESCAPE && (modifiers & B_COMMAND_KEY) != 0 && fKeyMenuBar) {
3684 		fKeyMenuBar->StartMenuBar(0, true, false, NULL);
3685 		return true;
3686 	}
3687 
3688 	// Keyboard navigation through views
3689 	// (B_OPTION_KEY makes BTextViews and friends navigable, even in editing
3690 	// mode)
3691 	if (key == B_TAB && (modifiers & B_OPTION_KEY) != 0) {
3692 		_KeyboardNavigation();
3693 		return true;
3694 	}
3695 
3696 	int32 rawKey;
3697 	event->FindInt32("key", &rawKey);
3698 
3699 	// Deskbar's Switcher
3700 	if ((key == B_TAB || rawKey == 0x11) && (modifiers & B_CONTROL_KEY) != 0) {
3701 		_Switcher(rawKey, modifiers, event->HasInt32("be:key_repeat"));
3702 		return true;
3703 	}
3704 
3705 	// Optionally close window when the escape key is pressed
3706 	if (key == B_ESCAPE && (Flags() & B_CLOSE_ON_ESCAPE) != 0) {
3707 		BMessage message(B_QUIT_REQUESTED);
3708 		message.AddBool("shortcut", true);
3709 
3710 		PostMessage(&message);
3711 		return true;
3712 	}
3713 
3714 	// PrtScr key takes a screenshot
3715 	if (key == B_FUNCTION_KEY && rawKey == B_PRINT_KEY) {
3716 		// With no modifier keys the best way to get a screenshot is by
3717 		// calling the screenshot CLI
3718 		if (modifiers == 0) {
3719 			be_roster->Launch("application/x-vnd.haiku-screenshot-cli");
3720 			return true;
3721 		}
3722 
3723 		// Prepare a message based on the modifier keys pressed and launch the
3724 		// screenshot GUI
3725 		BMessage message(B_ARGV_RECEIVED);
3726 		int32 argc = 1;
3727 		message.AddString("argv", "Screenshot");
3728 		if ((modifiers & B_CONTROL_KEY) != 0) {
3729 			argc++;
3730 			message.AddString("argv", "--clipboard");
3731 		}
3732 		if ((modifiers & B_SHIFT_KEY) != 0) {
3733 			argc++;
3734 			message.AddString("argv", "--silent");
3735 		}
3736 		message.AddInt32("argc", argc);
3737 		be_roster->Launch("application/x-vnd.haiku-screenshot", &message);
3738 		return true;
3739 	}
3740 
3741 	// Handle shortcuts
3742 	if ((modifiers & B_COMMAND_KEY) != 0) {
3743 		// Command+q has been pressed, so, we will quit
3744 		// the shortcut mechanism doesn't allow handlers outside the window
3745 		if (!fNoQuitShortcut && (key == 'Q' || key == 'q')) {
3746 			BMessage message(B_QUIT_REQUESTED);
3747 			message.AddBool("shortcut", true);
3748 
3749 			be_app->PostMessage(&message);
3750 			// eat the event
3751 			return true;
3752 		}
3753 
3754 		// Pretend that the user opened a menu, to give the subclass a
3755 		// chance to update it's menus. This may install new shortcuts,
3756 		// which is why we have to call it here, before trying to find
3757 		// a shortcut for the given key.
3758 		MenusBeginning();
3759 
3760 		Shortcut* shortcut = _FindShortcut(key, modifiers);
3761 		if (shortcut != NULL) {
3762 			// TODO: would be nice to move this functionality to
3763 			//	a Shortcut::Invoke() method - but since BMenu::InvokeItem()
3764 			//	(and BMenuItem::Invoke()) are private, I didn't want
3765 			//	to mess with them (BMenuItem::Invoke() is public in
3766 			//	Dano/Zeta, though, maybe we should just follow their
3767 			//	example)
3768 			if (shortcut->MenuItem() != NULL) {
3769 				BMenu* menu = shortcut->MenuItem()->Menu();
3770 				if (menu != NULL)
3771 					MenuPrivate(menu).InvokeItem(shortcut->MenuItem(), true);
3772 			} else {
3773 				BHandler* target = shortcut->Target();
3774 				if (target == NULL)
3775 					target = CurrentFocus();
3776 
3777 				if (shortcut->Message() != NULL) {
3778 					BMessage message(*shortcut->Message());
3779 
3780 					if (message.ReplaceInt64("when", system_time()) != B_OK)
3781 						message.AddInt64("when", system_time());
3782 					if (message.ReplaceBool("shortcut", true) != B_OK)
3783 						message.AddBool("shortcut", true);
3784 
3785 					PostMessage(&message, target);
3786 				}
3787 			}
3788 		}
3789 
3790 		MenusEnded();
3791 
3792 		// we always eat the event if the command key was pressed
3793 		return true;
3794 	}
3795 
3796 	// TODO: convert keys to the encoding of the target view
3797 
3798 	return false;
3799 }
3800 
3801 
3802 bool
3803 BWindow::_HandleUnmappedKeyDown(BMessage* event)
3804 {
3805 	// Only handle special functions when the event targeted the active focus
3806 	// view
3807 	if (!_IsFocusMessage(event))
3808 		return false;
3809 
3810 	uint32 modifiers;
3811 	int32 rawKey;
3812 	if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK
3813 		|| event->FindInt32("key", &rawKey))
3814 		return false;
3815 
3816 	// Deskbar's Switcher
3817 	if (rawKey == 0x11 && (modifiers & B_CONTROL_KEY) != 0) {
3818 		_Switcher(rawKey, modifiers, event->HasInt32("be:key_repeat"));
3819 		return true;
3820 	}
3821 
3822 	return false;
3823 }
3824 
3825 
3826 void
3827 BWindow::_KeyboardNavigation()
3828 {
3829 	BMessage* message = CurrentMessage();
3830 	if (message == NULL)
3831 		return;
3832 
3833 	const char* bytes;
3834 	uint32 modifiers;
3835 	if (message->FindString("bytes", &bytes) != B_OK
3836 		|| bytes[0] != B_TAB)
3837 		return;
3838 
3839 	message->FindInt32("modifiers", (int32*)&modifiers);
3840 
3841 	BView* nextFocus;
3842 	int32 jumpGroups = (modifiers & B_OPTION_KEY) != 0
3843 		? B_NAVIGABLE_JUMP : B_NAVIGABLE;
3844 	if (modifiers & B_SHIFT_KEY)
3845 		nextFocus = _FindPreviousNavigable(fFocus, jumpGroups);
3846 	else
3847 		nextFocus = _FindNextNavigable(fFocus, jumpGroups);
3848 
3849 	if (nextFocus && nextFocus != fFocus) {
3850 		nextFocus->MakeFocus(true);
3851 	}
3852 }
3853 
3854 
3855 BMessage*
3856 BWindow::ConvertToMessage(void* raw, int32 code)
3857 {
3858 	return BLooper::ConvertToMessage(raw, code);
3859 }
3860 
3861 
3862 BWindow::Shortcut*
3863 BWindow::_FindShortcut(uint32 key, uint32 modifiers)
3864 {
3865 	int32 count = fShortcuts.CountItems();
3866 
3867 	key = Shortcut::PrepareKey(key);
3868 	modifiers = Shortcut::PrepareModifiers(modifiers);
3869 
3870 	for (int32 index = 0; index < count; index++) {
3871 		Shortcut* shortcut = (Shortcut*)fShortcuts.ItemAt(index);
3872 
3873 		if (shortcut->Matches(key, modifiers))
3874 			return shortcut;
3875 	}
3876 
3877 	return NULL;
3878 }
3879 
3880 
3881 BView*
3882 BWindow::_FindView(int32 token)
3883 {
3884 	BHandler* handler;
3885 	if (gDefaultTokens.GetToken(token, B_HANDLER_TOKEN,
3886 			(void**)&handler) != B_OK) {
3887 		return NULL;
3888 	}
3889 
3890 	// the view must belong to us in order to be found by this method
3891 	BView* view = dynamic_cast<BView*>(handler);
3892 	if (view != NULL && view->Window() == this)
3893 		return view;
3894 
3895 	return NULL;
3896 }
3897 
3898 
3899 BView*
3900 BWindow::_FindView(BView* view, BPoint point) const
3901 {
3902 	// point is assumed to be already in view's coordinates
3903 	if (!view->IsHidden() && view->Bounds().Contains(point)) {
3904 		if (!view->fFirstChild)
3905 			return view;
3906 		else {
3907 			BView* child = view->fFirstChild;
3908 			while (child != NULL) {
3909 				BPoint childPoint = point - child->Frame().LeftTop();
3910 				BView* subView  = _FindView(child, childPoint);
3911 				if (subView != NULL)
3912 					return subView;
3913 
3914 				child = child->fNextSibling;
3915 			}
3916 		}
3917 		return view;
3918 	}
3919 	return NULL;
3920 }
3921 
3922 
3923 BView*
3924 BWindow::_FindNextNavigable(BView* focus, uint32 flags)
3925 {
3926 	if (focus == NULL)
3927 		focus = fTopView;
3928 
3929 	BView* nextFocus = focus;
3930 
3931 	// Search the tree for views that accept focus (depth search)
3932 	while (true) {
3933 		if (nextFocus->fFirstChild)
3934 			nextFocus = nextFocus->fFirstChild;
3935 		else if (nextFocus->fNextSibling)
3936 			nextFocus = nextFocus->fNextSibling;
3937 		else {
3938 			// go to the nearest parent with a next sibling
3939 			while (!nextFocus->fNextSibling && nextFocus->fParent) {
3940 				nextFocus = nextFocus->fParent;
3941 			}
3942 
3943 			if (nextFocus == fTopView) {
3944 				// if we started with the top view, we traversed the whole tree already
3945 				if (nextFocus == focus)
3946 					return NULL;
3947 
3948 				nextFocus = nextFocus->fFirstChild;
3949 			} else
3950 				nextFocus = nextFocus->fNextSibling;
3951 		}
3952 
3953 		if (nextFocus == focus || nextFocus == NULL) {
3954 			// When we get here it means that the hole tree has been
3955 			// searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
3956 			return NULL;
3957 		}
3958 
3959 		if (!nextFocus->IsHidden() && (nextFocus->Flags() & flags) != 0)
3960 			return nextFocus;
3961 	}
3962 }
3963 
3964 
3965 BView*
3966 BWindow::_FindPreviousNavigable(BView* focus, uint32 flags)
3967 {
3968 	if (focus == NULL)
3969 		focus = fTopView;
3970 
3971 	BView* previousFocus = focus;
3972 
3973 	// Search the tree for the previous view that accept focus
3974 	while (true) {
3975 		if (previousFocus->fPreviousSibling) {
3976 			// find the last child in the previous sibling
3977 			previousFocus = _LastViewChild(previousFocus->fPreviousSibling);
3978 		} else {
3979 			previousFocus = previousFocus->fParent;
3980 			if (previousFocus == fTopView)
3981 				previousFocus = _LastViewChild(fTopView);
3982 		}
3983 
3984 		if (previousFocus == focus || previousFocus == NULL) {
3985 			// When we get here it means that the hole tree has been
3986 			// searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
3987 			return NULL;
3988 		}
3989 
3990 		if (!previousFocus->IsHidden() && (previousFocus->Flags() & flags) != 0)
3991 			return previousFocus;
3992 	}
3993 }
3994 
3995 
3996 /*!
3997 	Returns the last child in a view hierarchy.
3998 	Needed only by _FindPreviousNavigable().
3999 */
4000 BView*
4001 BWindow::_LastViewChild(BView* parent)
4002 {
4003 	while (true) {
4004 		BView* last = parent->fFirstChild;
4005 		if (last == NULL)
4006 			return parent;
4007 
4008 		while (last->fNextSibling) {
4009 			last = last->fNextSibling;
4010 		}
4011 
4012 		parent = last;
4013 	}
4014 }
4015 
4016 
4017 void
4018 BWindow::SetIsFilePanel(bool isFilePanel)
4019 {
4020 	fIsFilePanel = isFilePanel;
4021 }
4022 
4023 
4024 bool
4025 BWindow::IsFilePanel() const
4026 {
4027 	return fIsFilePanel;
4028 }
4029 
4030 
4031 void
4032 BWindow::_GetDecoratorSize(float* _borderWidth, float* _tabHeight) const
4033 {
4034 	// fallback in case retrieving the decorator settings fails
4035 	// (highly unlikely)
4036 	float borderWidth = 5.0;
4037 	float tabHeight = 21.0;
4038 
4039 	BMessage settings;
4040 	if (GetDecoratorSettings(&settings) == B_OK) {
4041 		BRect tabRect;
4042 		if (settings.FindRect("tab frame", &tabRect) == B_OK)
4043 			tabHeight = tabRect.Height();
4044 		settings.FindFloat("border width", &borderWidth);
4045 	} else {
4046 		// probably no-border window look
4047 		if (fLook == B_NO_BORDER_WINDOW_LOOK) {
4048 			borderWidth = 0.0;
4049 			tabHeight = 0.0;
4050 		}
4051 		// else use fall-back values from above
4052 	}
4053 
4054 	if (_borderWidth != NULL)
4055 		*_borderWidth = borderWidth;
4056 	if (_tabHeight != NULL)
4057 		*_tabHeight = tabHeight;
4058 }
4059 
4060 
4061 void
4062 BWindow::_SendShowOrHideMessage()
4063 {
4064 	fLink->StartMessage(AS_SHOW_OR_HIDE_WINDOW);
4065 	fLink->Attach<int32>(fShowLevel);
4066 	fLink->Flush();
4067 }
4068 
4069 
4070 //	#pragma mark - C++ binary compatibility kludge
4071 
4072 
4073 extern "C" void
4074 _ReservedWindow1__7BWindow(BWindow* window, BLayout* layout)
4075 {
4076 	// SetLayout()
4077 	perform_data_set_layout data;
4078 	data.layout = layout;
4079 	window->Perform(PERFORM_CODE_SET_LAYOUT, &data);
4080 }
4081 
4082 
4083 void BWindow::_ReservedWindow2() {}
4084 void BWindow::_ReservedWindow3() {}
4085 void BWindow::_ReservedWindow4() {}
4086 void BWindow::_ReservedWindow5() {}
4087 void BWindow::_ReservedWindow6() {}
4088 void BWindow::_ReservedWindow7() {}
4089 void BWindow::_ReservedWindow8() {}
4090 
4091