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