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