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