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