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