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