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