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