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