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