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