xref: /haiku/src/kits/interface/Window.cpp (revision b2acee1cb986b696adfad7daabfe9279949a3e54)
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('H', B_COMMAND_KEY | B_CONTROL_KEY,
2926 		new BMessage(B_HIDE_APPLICATION), NULL);
2927 	AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY,
2928 		new BMessage(_SEND_TO_FRONT_), NULL);
2929 	AddShortcut('B', B_COMMAND_KEY | B_CONTROL_KEY,
2930 		new BMessage(_SEND_BEHIND_), NULL);
2931 
2932 	// We set the default pulse rate, but we don't start the pulse
2933 	fPulseRate = 500000;
2934 	fPulseRunner = NULL;
2935 
2936 	fIsFilePanel = false;
2937 
2938 	fMenuSem = -1;
2939 
2940 	fMinimized = false;
2941 
2942 	fMaxZoomHeight = 32768.0;
2943 	fMaxZoomWidth = 32768.0;
2944 	fMinHeight = 0.0;
2945 	fMinWidth = 0.0;
2946 	fMaxHeight = 32768.0;
2947 	fMaxWidth = 32768.0;
2948 
2949 	fLastViewToken = B_NULL_TOKEN;
2950 
2951 	// TODO: other initializations!
2952 	fOffscreen = false;
2953 
2954 	// Create the server-side window
2955 
2956 	port_id receivePort = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY,
2957 		"w<app_server");
2958 	if (receivePort < B_OK) {
2959 		// TODO: huh?
2960 		debugger("Could not create BWindow's receive port, used for "
2961 				 "interacting with the app_server!");
2962 		delete this;
2963 		return;
2964 	}
2965 
2966 	STRACE(("BWindow::InitData(): contacting app_server...\n"));
2967 
2968 	// let app_server know that a window has been created.
2969 	fLink = new(std::nothrow) BPrivate::PortLink(
2970 		BApplication::Private::ServerLink()->SenderPort(), receivePort);
2971 	if (fLink == NULL) {
2972 		// Zombie!
2973 		return;
2974 	}
2975 
2976 	{
2977 		BPrivate::AppServerLink lockLink;
2978 			// we're talking to the server application using our own
2979 			// communication channel (fLink) - we better make sure no one
2980 			// interferes by locking that channel (which AppServerLink does
2981 			// implicetly)
2982 
2983 		if (bitmapToken < 0) {
2984 			fLink->StartMessage(AS_CREATE_WINDOW);
2985 		} else {
2986 			fLink->StartMessage(AS_CREATE_OFFSCREEN_WINDOW);
2987 			fLink->Attach<int32>(bitmapToken);
2988 			fOffscreen = true;
2989 		}
2990 
2991 		fLink->Attach<BRect>(fFrame);
2992 		fLink->Attach<uint32>((uint32)fLook);
2993 		fLink->Attach<uint32>((uint32)fFeel);
2994 		fLink->Attach<uint32>(fFlags);
2995 		fLink->Attach<uint32>(workspace);
2996 		fLink->Attach<int32>(_get_object_token_(this));
2997 		fLink->Attach<port_id>(receivePort);
2998 		fLink->Attach<port_id>(fMsgPort);
2999 		fLink->AttachString(title);
3000 
3001 		port_id sendPort;
3002 		int32 code;
3003 		if (fLink->FlushWithReply(code) == B_OK
3004 			&& code == B_OK
3005 			&& fLink->Read<port_id>(&sendPort) == B_OK) {
3006 			// read the frame size and its limits that were really
3007 			// enforced on the server side
3008 
3009 			fLink->Read<BRect>(&fFrame);
3010 			fLink->Read<float>(&fMinWidth);
3011 			fLink->Read<float>(&fMaxWidth);
3012 			fLink->Read<float>(&fMinHeight);
3013 			fLink->Read<float>(&fMaxHeight);
3014 
3015 			fMaxZoomWidth = fMaxWidth;
3016 			fMaxZoomHeight = fMaxHeight;
3017 		} else
3018 			sendPort = -1;
3019 
3020 		// Redirect our link to the new window connection
3021 		fLink->SetSenderPort(sendPort);
3022 		STRACE(("Server says that our send port is %ld\n", sendPort));
3023 	}
3024 
3025 	STRACE(("Window locked?: %s\n", IsLocked() ? "True" : "False"));
3026 
3027 	_CreateTopView();
3028 }
3029 
3030 
3031 //! Rename the handler and its thread
3032 void
3033 BWindow::_SetName(const char* title)
3034 {
3035 	if (title == NULL)
3036 		title = "";
3037 
3038 	// we will change BWindow's thread name to "w>window title"
3039 
3040 	char threadName[B_OS_NAME_LENGTH];
3041 	strcpy(threadName, "w>");
3042 #ifdef __HAIKU__
3043 	strlcat(threadName, title, B_OS_NAME_LENGTH);
3044 #else
3045 	int32 length = strlen(title);
3046 	length = min_c(length, B_OS_NAME_LENGTH - 3);
3047 	memcpy(threadName + 2, title, length);
3048 	threadName[length + 2] = '\0';
3049 #endif
3050 
3051 	// change the handler's name
3052 	SetName(threadName);
3053 
3054 	// if the message loop has been started...
3055 	if (Thread() >= B_OK)
3056 		rename_thread(Thread(), threadName);
3057 }
3058 
3059 
3060 //!	Reads all pending messages from the window port and put them into the queue.
3061 void
3062 BWindow::_DequeueAll()
3063 {
3064 	//	Get message count from port
3065 	int32 count = port_count(fMsgPort);
3066 
3067 	for (int32 i = 0; i < count; i++) {
3068 		BMessage* message = MessageFromPort(0);
3069 		if (message != NULL)
3070 			fDirectTarget->Queue()->AddMessage(message);
3071 	}
3072 }
3073 
3074 
3075 /*!	This here is an almost complete code duplication to BLooper::task_looper()
3076 	but with some important differences:
3077 	 a)	it uses the _DetermineTarget() method to tell what the later target of
3078 		a message will be, if no explicit target is supplied.
3079 	 b)	it calls _UnpackMessage() and _SanitizeMessage() to duplicate the message
3080 		to all of its intended targets, and to add all fields the target would
3081 		expect in such a message.
3082 
3083 	This is important because the app_server sends all input events to the
3084 	preferred handler, and expects them to be correctly distributed to their
3085 	intended targets.
3086 */
3087 void
3088 BWindow::task_looper()
3089 {
3090 	STRACE(("info: BWindow::task_looper() started.\n"));
3091 
3092 	// Check that looper is locked (should be)
3093 	AssertLocked();
3094 	Unlock();
3095 
3096 	if (IsLocked())
3097 		debugger("window must not be locked!");
3098 
3099 	while (!fTerminating) {
3100 		// Did we get a message?
3101 		BMessage* msg = MessageFromPort();
3102 		if (msg)
3103 			_AddMessagePriv(msg);
3104 
3105 		//	Get message count from port
3106 		int32 msgCount = port_count(fMsgPort);
3107 		for (int32 i = 0; i < msgCount; ++i) {
3108 			// Read 'count' messages from port (so we will not block)
3109 			// We use zero as our timeout since we know there is stuff there
3110 			msg = MessageFromPort(0);
3111 			// Add messages to queue
3112 			if (msg)
3113 				_AddMessagePriv(msg);
3114 		}
3115 
3116 		bool dispatchNextMessage = true;
3117 		while (!fTerminating && dispatchNextMessage) {
3118 			// Get next message from queue (assign to fLastMessage after
3119 			// locking)
3120 			BMessage* message = fDirectTarget->Queue()->NextMessage();
3121 
3122 			// Lock the looper
3123 			if (!Lock()) {
3124 				delete message;
3125 				break;
3126 			}
3127 
3128 			fLastMessage = message;
3129 
3130 			if (fLastMessage == NULL) {
3131 				// No more messages: Unlock the looper and terminate the
3132 				// dispatch loop.
3133 				dispatchNextMessage = false;
3134 			} else {
3135 				// Get the target handler
3136 				BMessage::Private messagePrivate(fLastMessage);
3137 				bool usePreferred = messagePrivate.UsePreferredTarget();
3138 				BHandler* handler = NULL;
3139 				bool dropMessage = false;
3140 
3141 				if (usePreferred) {
3142 					handler = PreferredHandler();
3143 					if (handler == NULL)
3144 						handler = this;
3145 				} else {
3146 					gDefaultTokens.GetToken(messagePrivate.GetTarget(),
3147 						B_HANDLER_TOKEN, (void**)&handler);
3148 
3149 					// if this handler doesn't belong to us, we drop the message
3150 					if (handler != NULL && handler->Looper() != this) {
3151 						dropMessage = true;
3152 						handler = NULL;
3153 					}
3154 				}
3155 
3156 				if ((handler == NULL && !dropMessage) || usePreferred)
3157 					handler = _DetermineTarget(fLastMessage, handler);
3158 
3159 				unpack_cookie cookie;
3160 				while (_UnpackMessage(cookie, &fLastMessage, &handler, &usePreferred)) {
3161 					// if there is no target handler, the message is dropped
3162 					if (handler != NULL) {
3163 						_SanitizeMessage(fLastMessage, handler, usePreferred);
3164 
3165 						// Is this a scripting message?
3166 						if (fLastMessage->HasSpecifiers()) {
3167 							int32 index = 0;
3168 							// Make sure the current specifier is kosher
3169 							if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
3170 								handler = resolve_specifier(handler, fLastMessage);
3171 						}
3172 
3173 						if (handler != NULL)
3174 							handler = _TopLevelFilter(fLastMessage, handler);
3175 
3176 						if (handler != NULL)
3177 							DispatchMessage(fLastMessage, handler);
3178 					}
3179 
3180 					// Delete the current message
3181 					delete fLastMessage;
3182 					fLastMessage = NULL;
3183 				}
3184 			}
3185 
3186 			if (fTerminating) {
3187 				// we leave the looper locked when we quit
3188 				return;
3189 			}
3190 
3191 			Unlock();
3192 
3193 			// Are any messages on the port?
3194 			if (port_count(fMsgPort) > 0) {
3195 				// Do outer loop
3196 				dispatchNextMessage = false;
3197 			}
3198 		}
3199 	}
3200 }
3201 
3202 
3203 window_type
3204 BWindow::_ComposeType(window_look look, window_feel feel) const
3205 {
3206 	switch (feel) {
3207 		case B_NORMAL_WINDOW_FEEL:
3208 			switch (look) {
3209 				case B_TITLED_WINDOW_LOOK:
3210 					return B_TITLED_WINDOW;
3211 
3212 				case B_DOCUMENT_WINDOW_LOOK:
3213 					return B_DOCUMENT_WINDOW;
3214 
3215 				case B_BORDERED_WINDOW_LOOK:
3216 					return B_BORDERED_WINDOW;
3217 
3218 				default:
3219 					return B_UNTYPED_WINDOW;
3220 			}
3221 			break;
3222 
3223 		case B_MODAL_APP_WINDOW_FEEL:
3224 			if (look == B_MODAL_WINDOW_LOOK)
3225 				return B_MODAL_WINDOW;
3226 			break;
3227 
3228 		case B_FLOATING_APP_WINDOW_FEEL:
3229 			if (look == B_FLOATING_WINDOW_LOOK)
3230 				return B_FLOATING_WINDOW;
3231 			break;
3232 
3233 		default:
3234 			return B_UNTYPED_WINDOW;
3235 	}
3236 
3237 	return B_UNTYPED_WINDOW;
3238 }
3239 
3240 
3241 void
3242 BWindow::_DecomposeType(window_type type, window_look* _look,
3243 	window_feel* _feel) const
3244 {
3245 	switch (type) {
3246 		case B_DOCUMENT_WINDOW:
3247 			*_look = B_DOCUMENT_WINDOW_LOOK;
3248 			*_feel = B_NORMAL_WINDOW_FEEL;
3249 			break;
3250 
3251 		case B_MODAL_WINDOW:
3252 			*_look = B_MODAL_WINDOW_LOOK;
3253 			*_feel = B_MODAL_APP_WINDOW_FEEL;
3254 			break;
3255 
3256 		case B_FLOATING_WINDOW:
3257 			*_look = B_FLOATING_WINDOW_LOOK;
3258 			*_feel = B_FLOATING_APP_WINDOW_FEEL;
3259 			break;
3260 
3261 		case B_BORDERED_WINDOW:
3262 			*_look = B_BORDERED_WINDOW_LOOK;
3263 			*_feel = B_NORMAL_WINDOW_FEEL;
3264 			break;
3265 
3266 		case B_TITLED_WINDOW:
3267 		case B_UNTYPED_WINDOW:
3268 		default:
3269 			*_look = B_TITLED_WINDOW_LOOK;
3270 			*_feel = B_NORMAL_WINDOW_FEEL;
3271 			break;
3272 	}
3273 }
3274 
3275 
3276 void
3277 BWindow::_CreateTopView()
3278 {
3279 	STRACE(("_CreateTopView(): enter\n"));
3280 
3281 	BRect frame = fFrame.OffsetToCopy(B_ORIGIN);
3282 	// TODO: what to do here about std::nothrow?
3283 	fTopView = new BView(frame, "fTopView", B_FOLLOW_ALL, B_WILL_DRAW);
3284 	fTopView->fTopLevelView = true;
3285 
3286 	//inhibit check_lock()
3287 	fLastViewToken = _get_object_token_(fTopView);
3288 
3289 	// set fTopView's owner, add it to window's eligible handler list
3290 	// and also set its next handler to be this window.
3291 
3292 	STRACE(("Calling setowner fTopView = %p this = %p.\n",
3293 		fTopView, this));
3294 
3295 	fTopView->_SetOwner(this);
3296 
3297 	// we can't use AddChild() because this is the top view
3298 	fTopView->_CreateSelf();
3299 	STRACE(("BuildTopView ended\n"));
3300 }
3301 
3302 
3303 /*!
3304 	Resizes the top view to match the window size. This will also
3305 	adapt the size of all its child views as needed.
3306 	This method has to be called whenever the frame of the window
3307 	changes.
3308 */
3309 void
3310 BWindow::_AdoptResize()
3311 {
3312 	// Resize views according to their resize modes - this
3313 	// saves us some server communication, as the server
3314 	// does the same with our views on its side.
3315 
3316 	int32 deltaWidth = (int32)(fFrame.Width() - fTopView->Bounds().Width());
3317 	int32 deltaHeight = (int32)(fFrame.Height() - fTopView->Bounds().Height());
3318 	if (deltaWidth == 0 && deltaHeight == 0)
3319 		return;
3320 
3321 	fTopView->_ResizeBy(deltaWidth, deltaHeight);
3322 }
3323 
3324 
3325 void
3326 BWindow::_SetFocus(BView* focusView, bool notifyInputServer)
3327 {
3328 	if (fFocus == focusView)
3329 		return;
3330 
3331 	// we notify the input server if we are passing focus
3332 	// from a view which has the B_INPUT_METHOD_AWARE to a one
3333 	// which does not, or vice-versa
3334 	if (notifyInputServer && fActive) {
3335 		bool inputMethodAware = false;
3336 		if (focusView)
3337 			inputMethodAware = focusView->Flags() & B_INPUT_METHOD_AWARE;
3338 		BMessage msg(inputMethodAware ? IS_FOCUS_IM_AWARE_VIEW : IS_UNFOCUS_IM_AWARE_VIEW);
3339 		BMessenger messenger(focusView);
3340 		BMessage reply;
3341 		if (focusView)
3342 			msg.AddMessenger("view", messenger);
3343 		_control_input_server_(&msg, &reply);
3344 	}
3345 
3346 	fFocus = focusView;
3347 	SetPreferredHandler(focusView);
3348 }
3349 
3350 
3351 /*!
3352 	\brief Determines the target of a message received for the
3353 		focus view.
3354 */
3355 BHandler*
3356 BWindow::_DetermineTarget(BMessage* message, BHandler* target)
3357 {
3358 	if (target == NULL)
3359 		target = this;
3360 
3361 	switch (message->what) {
3362 		case B_KEY_DOWN:
3363 		case B_KEY_UP:
3364 		{
3365 			// if we have a default button, it might want to hear
3366 			// about pressing the <enter> key
3367 			const int32 kNonLockModifierKeys = B_SHIFT_KEY | B_COMMAND_KEY
3368 				| B_CONTROL_KEY | B_OPTION_KEY | B_MENU_KEY;
3369 			int32 rawChar;
3370 			if (DefaultButton() != NULL
3371 				&& message->FindInt32("raw_char", &rawChar) == B_OK
3372 				&& rawChar == B_ENTER
3373 				&& (modifiers() & kNonLockModifierKeys) == 0)
3374 				return DefaultButton();
3375 
3376 			// supposed to fall through
3377 		}
3378 		case B_UNMAPPED_KEY_DOWN:
3379 		case B_UNMAPPED_KEY_UP:
3380 		case B_MODIFIERS_CHANGED:
3381 			// these messages should be dispatched by the focus view
3382 			if (CurrentFocus() != NULL)
3383 				return CurrentFocus();
3384 			break;
3385 
3386 		case B_MOUSE_DOWN:
3387 		case B_MOUSE_UP:
3388 		case B_MOUSE_MOVED:
3389 		case B_MOUSE_WHEEL_CHANGED:
3390 		case B_MOUSE_IDLE:
3391 			// is there a token of the view that is currently under the mouse?
3392 			int32 token;
3393 			if (message->FindInt32("_view_token", &token) == B_OK) {
3394 				BView* view = _FindView(token);
3395 				if (view != NULL)
3396 					return view;
3397 			}
3398 
3399 			// if there is no valid token in the message, we try our
3400 			// luck with the last target, if available
3401 			if (fLastMouseMovedView != NULL)
3402 				return fLastMouseMovedView;
3403 			break;
3404 
3405 		case B_PULSE:
3406 		case B_QUIT_REQUESTED:
3407 			// TODO: test whether R5 will let BView dispatch these messages
3408 			return this;
3409 
3410 		case _MESSAGE_DROPPED_:
3411 			if (fLastMouseMovedView != NULL)
3412 				return fLastMouseMovedView;
3413 			break;
3414 
3415 		default:
3416 			break;
3417 	}
3418 
3419 	return target;
3420 }
3421 
3422 
3423 /*!	\brief Determines whether or not this message has targeted the focus view.
3424 
3425 	This will return \c false only if the message did not go to the preferred
3426 	handler, or if the packed message does not contain address the focus view
3427 	at all.
3428 */
3429 bool
3430 BWindow::_IsFocusMessage(BMessage* message)
3431 {
3432 	BMessage::Private messagePrivate(message);
3433 	if (!messagePrivate.UsePreferredTarget())
3434 		return false;
3435 
3436 	bool feedFocus;
3437 	if (message->HasInt32("_token")
3438 		&& (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus))
3439 		return false;
3440 
3441 	return true;
3442 }
3443 
3444 
3445 /*!	\brief Distributes the message to its intended targets. This is done for
3446 		all messages that should go to the preferred handler.
3447 
3448 	Returns \c true in case the message should still be dispatched
3449 */
3450 bool
3451 BWindow::_UnpackMessage(unpack_cookie& cookie, BMessage** _message,
3452 	BHandler** _target, bool* _usePreferred)
3453 {
3454 	if (cookie.message == NULL)
3455 		return false;
3456 
3457 	if (cookie.index == 0 && !cookie.tokens_scanned) {
3458 		// We were called the first time for this message
3459 
3460 		if (!*_usePreferred) {
3461 			// only consider messages targeted at the preferred handler
3462 			cookie.message = NULL;
3463 			return true;
3464 		}
3465 
3466 		// initialize our cookie
3467 		cookie.message = *_message;
3468 		cookie.focus = *_target;
3469 
3470 		if (cookie.focus != NULL)
3471 			cookie.focus_token = _get_object_token_(*_target);
3472 
3473 		if (fLastMouseMovedView != NULL && cookie.message->what == B_MOUSE_MOVED)
3474 			cookie.last_view_token = _get_object_token_(fLastMouseMovedView);
3475 
3476 		*_usePreferred = false;
3477 	}
3478 
3479 	_DequeueAll();
3480 
3481 	// distribute the message to all targets specified in the
3482 	// message directly (but not to the focus view)
3483 
3484 	for (int32 token; !cookie.tokens_scanned
3485 			&& cookie.message->FindInt32("_token", cookie.index, &token)
3486 				== B_OK;
3487 			cookie.index++) {
3488 		// focus view is preferred and should get its message directly
3489 		if (token == cookie.focus_token) {
3490 			cookie.found_focus = true;
3491 			continue;
3492 		}
3493 		if (token == cookie.last_view_token)
3494 			continue;
3495 
3496 		BView* target = _FindView(token);
3497 		if (target == NULL)
3498 			continue;
3499 
3500 		*_message = new BMessage(*cookie.message);
3501 		// the secondary copies of the message should not be treated as focus
3502 		// messages, otherwise there will be unintended side effects, i.e.
3503 		// keyboard shortcuts getting processed multiple times.
3504 		(*_message)->RemoveName("_feed_focus");
3505 		*_target = target;
3506 		cookie.index++;
3507 		return true;
3508 	}
3509 
3510 	cookie.tokens_scanned = true;
3511 
3512 	// if there is a last mouse moved view, and the new focus is
3513 	// different, the previous view wants to get its B_EXITED_VIEW
3514 	// message
3515 	if (cookie.last_view_token != B_NULL_TOKEN && fLastMouseMovedView != NULL
3516 		&& fLastMouseMovedView != cookie.focus) {
3517 		*_message = new BMessage(*cookie.message);
3518 		*_target = fLastMouseMovedView;
3519 		cookie.last_view_token = B_NULL_TOKEN;
3520 		return true;
3521 	}
3522 
3523 	bool dispatchToFocus = true;
3524 
3525 	// check if the focus token is still valid (could have been removed in the mean time)
3526 	BHandler* handler;
3527 	if (gDefaultTokens.GetToken(cookie.focus_token, B_HANDLER_TOKEN, (void**)&handler) != B_OK
3528 		|| handler->Looper() != this)
3529 		dispatchToFocus = false;
3530 
3531 	if (dispatchToFocus && cookie.index > 0) {
3532 		// should this message still be dispatched by the focus view?
3533 		bool feedFocus;
3534 		if (!cookie.found_focus
3535 			&& (cookie.message->FindBool("_feed_focus", &feedFocus) != B_OK
3536 				|| feedFocus == false))
3537 			dispatchToFocus = false;
3538 	}
3539 
3540 	if (!dispatchToFocus) {
3541 		delete cookie.message;
3542 		cookie.message = NULL;
3543 		return false;
3544 	}
3545 
3546 	*_message = cookie.message;
3547 	*_target = cookie.focus;
3548 	*_usePreferred = true;
3549 	cookie.message = NULL;
3550 	return true;
3551 }
3552 
3553 
3554 /*!	Some messages don't get to the window in a shape an application should see.
3555 	This method is supposed to give a message the last grinding before
3556 	it's acceptable for the receiving application.
3557 */
3558 void
3559 BWindow::_SanitizeMessage(BMessage* message, BHandler* target, bool usePreferred)
3560 {
3561 	if (target == NULL)
3562 		return;
3563 
3564 	switch (message->what) {
3565 		case B_MOUSE_MOVED:
3566 		case B_MOUSE_UP:
3567 		case B_MOUSE_DOWN:
3568 		{
3569 			BPoint where;
3570 			if (message->FindPoint("screen_where", &where) != B_OK)
3571 				break;
3572 
3573 			BView* view = dynamic_cast<BView*>(target);
3574 
3575 			if (view == NULL || message->what == B_MOUSE_MOVED) {
3576 				// add local window coordinates, only
3577 				// for regular mouse moved messages
3578 				message->AddPoint("where", ConvertFromScreen(where));
3579 			}
3580 
3581 			if (view != NULL) {
3582 				// add local view coordinates
3583 				BPoint viewWhere = view->ConvertFromScreen(where);
3584 				if (message->what != B_MOUSE_MOVED) {
3585 					// Yep, the meaning of "where" is different
3586 					// for regular mouse moved messages versus
3587 					// mouse up/down!
3588 					message->AddPoint("where", viewWhere);
3589 				}
3590 				message->AddPoint("be:view_where", viewWhere);
3591 
3592 				if (message->what == B_MOUSE_MOVED) {
3593 					// is there a token of the view that is currently under
3594 					// the mouse?
3595 					BView* viewUnderMouse = NULL;
3596 					int32 token;
3597 					if (message->FindInt32("_view_token", &token) == B_OK)
3598 						viewUnderMouse = _FindView(token);
3599 
3600 					// add transit information
3601 					uint32 transit
3602 						= _TransitForMouseMoved(view, viewUnderMouse);
3603 					message->AddInt32("be:transit", transit);
3604 
3605 					if (usePreferred)
3606 						fLastMouseMovedView = viewUnderMouse;
3607 				}
3608 			}
3609 			break;
3610 		}
3611 
3612 		case B_MOUSE_IDLE:
3613 		{
3614 			// App Server sends screen coordinates, convert the point to
3615 			// local view coordinates, then add the point in be:view_where
3616 			BPoint where;
3617 			if (message->FindPoint("screen_where", &where) != B_OK)
3618 				break;
3619 
3620 			BView* view = dynamic_cast<BView*>(target);
3621 			if (view != NULL) {
3622 				// add local view coordinates
3623 				message->AddPoint("be:view_where",
3624 					view->ConvertFromScreen(where));
3625 			}
3626 			break;
3627 		}
3628 
3629 		case _MESSAGE_DROPPED_:
3630 		{
3631 			uint32 originalWhat;
3632 			if (message->FindInt32("_original_what",
3633 					(int32*)&originalWhat) == B_OK) {
3634 				message->what = originalWhat;
3635 				message->RemoveName("_original_what");
3636 			}
3637 			break;
3638 		}
3639 	}
3640 }
3641 
3642 
3643 /*!
3644 	This is called by BView::GetMouse() when a B_MOUSE_MOVED message
3645 	is removed from the queue.
3646 	It allows the window to update the last mouse moved view, and
3647 	let it decide if this message should be kept. It will also remove
3648 	the message from the queue.
3649 	You need to hold the message queue lock when calling this method!
3650 
3651 	\return true if this message can be used to get the mouse data from,
3652 	\return false if this is not meant for the public.
3653 */
3654 bool
3655 BWindow::_StealMouseMessage(BMessage* message, bool& deleteMessage)
3656 {
3657 	BMessage::Private messagePrivate(message);
3658 	if (!messagePrivate.UsePreferredTarget()) {
3659 		// this message is targeted at a specific handler, so we should
3660 		// not steal it
3661 		return false;
3662 	}
3663 
3664 	int32 token;
3665 	if (message->FindInt32("_token", 0, &token) == B_OK) {
3666 		// This message has other targets, so we can't remove it;
3667 		// just prevent it from being sent to the preferred handler
3668 		// again (if it should have gotten it at all).
3669 		bool feedFocus;
3670 		if (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus)
3671 			return false;
3672 
3673 		message->RemoveName("_feed_focus");
3674 		deleteMessage = false;
3675 	} else {
3676 		deleteMessage = true;
3677 
3678 		if (message->what == B_MOUSE_MOVED) {
3679 			// We need to update the last mouse moved view, as this message
3680 			// won't make it to _SanitizeMessage() anymore.
3681 			BView* viewUnderMouse = NULL;
3682 			int32 token;
3683 			if (message->FindInt32("_view_token", &token) == B_OK)
3684 				viewUnderMouse = _FindView(token);
3685 
3686 			// Don't remove important transit messages!
3687 			uint32 transit = _TransitForMouseMoved(fLastMouseMovedView,
3688 				viewUnderMouse);
3689 			if (transit == B_ENTERED_VIEW || transit == B_EXITED_VIEW)
3690 				deleteMessage = false;
3691 		}
3692 
3693 		if (deleteMessage) {
3694 			// The message is only thought for the preferred handler, so we
3695 			// can just remove it.
3696 			MessageQueue()->RemoveMessage(message);
3697 		}
3698 	}
3699 
3700 	return true;
3701 }
3702 
3703 
3704 uint32
3705 BWindow::_TransitForMouseMoved(BView* view, BView* viewUnderMouse) const
3706 {
3707 	uint32 transit;
3708 	if (viewUnderMouse == view) {
3709 		// the mouse is over the target view
3710 		if (fLastMouseMovedView != view)
3711 			transit = B_ENTERED_VIEW;
3712 		else
3713 			transit = B_INSIDE_VIEW;
3714 	} else {
3715 		// the mouse is not over the target view
3716 		if (view == fLastMouseMovedView)
3717 			transit = B_EXITED_VIEW;
3718 		else
3719 			transit = B_OUTSIDE_VIEW;
3720 	}
3721 	return transit;
3722 }
3723 
3724 
3725 /*!	Forwards the key to the switcher
3726 */
3727 void
3728 BWindow::_Switcher(int32 rawKey, uint32 modifiers, bool repeat)
3729 {
3730 	// only send the first key press, no repeats
3731 	if (repeat)
3732 		return;
3733 
3734 	BMessenger deskbar(kDeskbarSignature);
3735 	if (!deskbar.IsValid()) {
3736 		// TODO: have some kind of fallback-handling in case the Deskbar is
3737 		// not available?
3738 		return;
3739 	}
3740 
3741 	BMessage message('TASK');
3742 	message.AddInt32("key", rawKey);
3743 	message.AddInt32("modifiers", modifiers);
3744 	message.AddInt64("when", system_time());
3745 	message.AddInt32("team", Team());
3746 	deskbar.SendMessage(&message);
3747 }
3748 
3749 
3750 /*!	Handles keyboard input before it gets forwarded to the target handler.
3751 	This includes shortcut evaluation, keyboard navigation, etc.
3752 
3753 	\return handled if true, the event was already handled, and will not
3754 		be forwarded to the target handler.
3755 
3756 	TODO: must also convert the incoming key to the font encoding of the target
3757 */
3758 bool
3759 BWindow::_HandleKeyDown(BMessage* event)
3760 {
3761 	// Only handle special functions when the event targeted the active focus
3762 	// view
3763 	if (!_IsFocusMessage(event))
3764 		return false;
3765 
3766 	const char* bytes = NULL;
3767 	if (event->FindString("bytes", &bytes) != B_OK)
3768 		return false;
3769 
3770 	char key = bytes[0];
3771 
3772 	uint32 modifiers;
3773 	if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK)
3774 		modifiers = 0;
3775 
3776 	// handle BMenuBar key
3777 	if (key == B_ESCAPE && (modifiers & B_COMMAND_KEY) != 0
3778 		&& fKeyMenuBar != NULL) {
3779 		fKeyMenuBar->StartMenuBar(0, true, false, NULL);
3780 		return true;
3781 	}
3782 
3783 	// Keyboard navigation through views
3784 	// (B_OPTION_KEY makes BTextViews and friends navigable, even in editing
3785 	// mode)
3786 	if (key == B_TAB && (modifiers & B_OPTION_KEY) != 0) {
3787 		_KeyboardNavigation();
3788 		return true;
3789 	}
3790 
3791 	int32 rawKey;
3792 	event->FindInt32("key", &rawKey);
3793 
3794 	// Deskbar's Switcher
3795 	if ((key == B_TAB || rawKey == 0x11) && (modifiers & B_CONTROL_KEY) != 0) {
3796 		_Switcher(rawKey, modifiers, event->HasInt32("be:key_repeat"));
3797 		return true;
3798 	}
3799 
3800 	// Optionally close window when the escape key is pressed
3801 	if (key == B_ESCAPE && (Flags() & B_CLOSE_ON_ESCAPE) != 0) {
3802 		BMessage message(B_QUIT_REQUESTED);
3803 		message.AddBool("shortcut", true);
3804 
3805 		PostMessage(&message);
3806 		return true;
3807 	}
3808 
3809 	// PrtScr key takes a screenshot
3810 	if (key == B_FUNCTION_KEY && rawKey == B_PRINT_KEY) {
3811 		// With no modifier keys the best way to get a screenshot is by
3812 		// calling the screenshot CLI
3813 		if (modifiers == 0) {
3814 			be_roster->Launch("application/x-vnd.haiku-screenshot-cli");
3815 			return true;
3816 		}
3817 
3818 		// Prepare a message based on the modifier keys pressed and launch the
3819 		// screenshot GUI
3820 		BMessage message(B_ARGV_RECEIVED);
3821 		int32 argc = 1;
3822 		message.AddString("argv", "Screenshot");
3823 		if ((modifiers & B_CONTROL_KEY) != 0) {
3824 			argc++;
3825 			message.AddString("argv", "--clipboard");
3826 		}
3827 		if ((modifiers & B_SHIFT_KEY) != 0) {
3828 			argc++;
3829 			message.AddString("argv", "--silent");
3830 		}
3831 		message.AddInt32("argc", argc);
3832 		be_roster->Launch("application/x-vnd.haiku-screenshot", &message);
3833 		return true;
3834 	}
3835 
3836 	// Handle shortcuts
3837 	if ((modifiers & B_COMMAND_KEY) != 0) {
3838 		// Command+q has been pressed, so, we will quit
3839 		// the shortcut mechanism doesn't allow handlers outside the window
3840 		if (!fNoQuitShortcut && (key == 'Q' || key == 'q')) {
3841 			BMessage message(B_QUIT_REQUESTED);
3842 			message.AddBool("shortcut", true);
3843 
3844 			be_app->PostMessage(&message);
3845 			// eat the event
3846 			return true;
3847 		}
3848 
3849 		// Send Command+Left and Command+Right to textview if it has focus
3850 		if (key == B_LEFT_ARROW || key == B_RIGHT_ARROW) {
3851 			// check key before doing expensive dynamic_cast
3852 			BTextView* textView = dynamic_cast<BTextView*>(CurrentFocus());
3853 			if (textView != NULL) {
3854 				textView->KeyDown(bytes, modifiers);
3855 				// eat the event
3856 				return true;
3857 			}
3858 		}
3859 
3860 		// Pretend that the user opened a menu, to give the subclass a
3861 		// chance to update it's menus. This may install new shortcuts,
3862 		// which is why we have to call it here, before trying to find
3863 		// a shortcut for the given key.
3864 		MenusBeginning();
3865 
3866 		Shortcut* shortcut = _FindShortcut(key, modifiers);
3867 		if (shortcut != NULL) {
3868 			// TODO: would be nice to move this functionality to
3869 			//	a Shortcut::Invoke() method - but since BMenu::InvokeItem()
3870 			//	(and BMenuItem::Invoke()) are private, I didn't want
3871 			//	to mess with them (BMenuItem::Invoke() is public in
3872 			//	Dano/Zeta, though, maybe we should just follow their
3873 			//	example)
3874 			if (shortcut->MenuItem() != NULL) {
3875 				BMenu* menu = shortcut->MenuItem()->Menu();
3876 				if (menu != NULL)
3877 					MenuPrivate(menu).InvokeItem(shortcut->MenuItem(), true);
3878 			} else {
3879 				BHandler* target = shortcut->Target();
3880 				if (target == NULL)
3881 					target = CurrentFocus();
3882 
3883 				if (shortcut->Message() != NULL) {
3884 					BMessage message(*shortcut->Message());
3885 
3886 					if (message.ReplaceInt64("when", system_time()) != B_OK)
3887 						message.AddInt64("when", system_time());
3888 					if (message.ReplaceBool("shortcut", true) != B_OK)
3889 						message.AddBool("shortcut", true);
3890 
3891 					PostMessage(&message, target);
3892 				}
3893 			}
3894 		}
3895 
3896 		MenusEnded();
3897 
3898 		// we always eat the event if the command key was pressed
3899 		return true;
3900 	}
3901 
3902 	// TODO: convert keys to the encoding of the target view
3903 
3904 	return false;
3905 }
3906 
3907 
3908 bool
3909 BWindow::_HandleUnmappedKeyDown(BMessage* event)
3910 {
3911 	// Only handle special functions when the event targeted the active focus
3912 	// view
3913 	if (!_IsFocusMessage(event))
3914 		return false;
3915 
3916 	uint32 modifiers;
3917 	int32 rawKey;
3918 	if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK
3919 		|| event->FindInt32("key", &rawKey))
3920 		return false;
3921 
3922 	// Deskbar's Switcher
3923 	if (rawKey == 0x11 && (modifiers & B_CONTROL_KEY) != 0) {
3924 		_Switcher(rawKey, modifiers, event->HasInt32("be:key_repeat"));
3925 		return true;
3926 	}
3927 
3928 	return false;
3929 }
3930 
3931 
3932 void
3933 BWindow::_KeyboardNavigation()
3934 {
3935 	BMessage* message = CurrentMessage();
3936 	if (message == NULL)
3937 		return;
3938 
3939 	const char* bytes;
3940 	uint32 modifiers;
3941 	if (message->FindString("bytes", &bytes) != B_OK || bytes[0] != B_TAB)
3942 		return;
3943 
3944 	message->FindInt32("modifiers", (int32*)&modifiers);
3945 
3946 	BView* nextFocus;
3947 	int32 jumpGroups = (modifiers & B_OPTION_KEY) != 0
3948 		? B_NAVIGABLE_JUMP : B_NAVIGABLE;
3949 	if (modifiers & B_SHIFT_KEY)
3950 		nextFocus = _FindPreviousNavigable(fFocus, jumpGroups);
3951 	else
3952 		nextFocus = _FindNextNavigable(fFocus, jumpGroups);
3953 
3954 	if (nextFocus != NULL && nextFocus != fFocus)
3955 		nextFocus->MakeFocus(true);
3956 }
3957 
3958 
3959 /*!
3960 	\brief Return the position of the window centered horizontally to the passed
3961            in \a frame and vertically 3/4 from the top of \a frame.
3962 
3963 	If the window is on the borders
3964 
3965 	\param width The width of the window.
3966 	\param height The height of the window.
3967 	\param frame The \a frame to center the window in.
3968 
3969 	\return The new window position.
3970 */
3971 BPoint
3972 BWindow::AlertPosition(const BRect& frame)
3973 {
3974 	float width = Bounds().Width();
3975 	float height = Bounds().Height();
3976 
3977 	BPoint point(frame.left + (frame.Width() / 2.0f) - (width / 2.0f),
3978 		frame.top + (frame.Height() / 4.0f) - ceil(height / 3.0f));
3979 
3980 	BRect screenFrame = BScreen(this).Frame();
3981 	if (frame == screenFrame) {
3982 		// reference frame is screen frame, skip the below adjustments
3983 		return point;
3984 	}
3985 
3986 	float borderWidth;
3987 	float tabHeight;
3988 	_GetDecoratorSize(&borderWidth, &tabHeight);
3989 
3990 	// clip the x position within the horizontal edges of the screen
3991 	if (point.x < screenFrame.left + borderWidth)
3992 		point.x = screenFrame.left + borderWidth;
3993 	else if (point.x + width > screenFrame.right - borderWidth)
3994 		point.x = screenFrame.right - borderWidth - width;
3995 
3996 	// lower the window down if it is covering the window tab
3997 	float tabPosition = frame.LeftTop().y + tabHeight + borderWidth;
3998 	if (point.y < tabPosition)
3999 		point.y = tabPosition;
4000 
4001 	// clip the y position within the vertical edges of the screen
4002 	if (point.y < screenFrame.top + borderWidth)
4003 		point.y = screenFrame.top + borderWidth;
4004 	else if (point.y + height > screenFrame.bottom - borderWidth)
4005 		point.y = screenFrame.bottom - borderWidth - height;
4006 
4007 	return point;
4008 }
4009 
4010 
4011 BMessage*
4012 BWindow::ConvertToMessage(void* raw, int32 code)
4013 {
4014 	return BLooper::ConvertToMessage(raw, code);
4015 }
4016 
4017 
4018 BWindow::Shortcut*
4019 BWindow::_FindShortcut(uint32 key, uint32 modifiers)
4020 {
4021 	int32 count = fShortcuts.CountItems();
4022 
4023 	key = Shortcut::PrepareKey(key);
4024 	modifiers = Shortcut::PrepareModifiers(modifiers);
4025 
4026 	for (int32 index = 0; index < count; index++) {
4027 		Shortcut* shortcut = (Shortcut*)fShortcuts.ItemAt(index);
4028 
4029 		if (shortcut->Matches(key, modifiers))
4030 			return shortcut;
4031 	}
4032 
4033 	return NULL;
4034 }
4035 
4036 
4037 BView*
4038 BWindow::_FindView(int32 token)
4039 {
4040 	BHandler* handler;
4041 	if (gDefaultTokens.GetToken(token, B_HANDLER_TOKEN,
4042 			(void**)&handler) != B_OK) {
4043 		return NULL;
4044 	}
4045 
4046 	// the view must belong to us in order to be found by this method
4047 	BView* view = dynamic_cast<BView*>(handler);
4048 	if (view != NULL && view->Window() == this)
4049 		return view;
4050 
4051 	return NULL;
4052 }
4053 
4054 
4055 BView*
4056 BWindow::_FindView(BView* view, BPoint point) const
4057 {
4058 	// point is assumed to be already in view's coordinates
4059 	if (!view->IsHidden(view) && view->Bounds().Contains(point)) {
4060 		if (view->fFirstChild == NULL)
4061 			return view;
4062 		else {
4063 			BView* child = view->fFirstChild;
4064 			while (child != NULL) {
4065 				BPoint childPoint = point - child->Frame().LeftTop();
4066 				BView* subView  = _FindView(child, childPoint);
4067 				if (subView != NULL)
4068 					return subView;
4069 
4070 				child = child->fNextSibling;
4071 			}
4072 		}
4073 		return view;
4074 	}
4075 	return NULL;
4076 }
4077 
4078 
4079 BView*
4080 BWindow::_FindNextNavigable(BView* focus, uint32 flags)
4081 {
4082 	if (focus == NULL)
4083 		focus = fTopView;
4084 
4085 	BView* nextFocus = focus;
4086 
4087 	// Search the tree for views that accept focus (depth search)
4088 	while (true) {
4089 		if (nextFocus->fFirstChild)
4090 			nextFocus = nextFocus->fFirstChild;
4091 		else if (nextFocus->fNextSibling)
4092 			nextFocus = nextFocus->fNextSibling;
4093 		else {
4094 			// go to the nearest parent with a next sibling
4095 			while (!nextFocus->fNextSibling && nextFocus->fParent) {
4096 				nextFocus = nextFocus->fParent;
4097 			}
4098 
4099 			if (nextFocus == fTopView) {
4100 				// if we started with the top view, we traversed the whole tree already
4101 				if (nextFocus == focus)
4102 					return NULL;
4103 
4104 				nextFocus = nextFocus->fFirstChild;
4105 			} else
4106 				nextFocus = nextFocus->fNextSibling;
4107 		}
4108 
4109 		if (nextFocus == focus || nextFocus == NULL) {
4110 			// When we get here it means that the hole tree has been
4111 			// searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
4112 			return NULL;
4113 		}
4114 
4115 		if (!nextFocus->IsHidden() && (nextFocus->Flags() & flags) != 0)
4116 			return nextFocus;
4117 	}
4118 }
4119 
4120 
4121 BView*
4122 BWindow::_FindPreviousNavigable(BView* focus, uint32 flags)
4123 {
4124 	if (focus == NULL)
4125 		focus = fTopView;
4126 
4127 	BView* previousFocus = focus;
4128 
4129 	// Search the tree for the previous view that accept focus
4130 	while (true) {
4131 		if (previousFocus->fPreviousSibling) {
4132 			// find the last child in the previous sibling
4133 			previousFocus = _LastViewChild(previousFocus->fPreviousSibling);
4134 		} else {
4135 			previousFocus = previousFocus->fParent;
4136 			if (previousFocus == fTopView)
4137 				previousFocus = _LastViewChild(fTopView);
4138 		}
4139 
4140 		if (previousFocus == focus || previousFocus == NULL) {
4141 			// When we get here it means that the hole tree has been
4142 			// searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
4143 			return NULL;
4144 		}
4145 
4146 		if (!previousFocus->IsHidden() && (previousFocus->Flags() & flags) != 0)
4147 			return previousFocus;
4148 	}
4149 }
4150 
4151 
4152 /*!
4153 	Returns the last child in a view hierarchy.
4154 	Needed only by _FindPreviousNavigable().
4155 */
4156 BView*
4157 BWindow::_LastViewChild(BView* parent)
4158 {
4159 	while (true) {
4160 		BView* last = parent->fFirstChild;
4161 		if (last == NULL)
4162 			return parent;
4163 
4164 		while (last->fNextSibling) {
4165 			last = last->fNextSibling;
4166 		}
4167 
4168 		parent = last;
4169 	}
4170 }
4171 
4172 
4173 void
4174 BWindow::SetIsFilePanel(bool isFilePanel)
4175 {
4176 	fIsFilePanel = isFilePanel;
4177 }
4178 
4179 
4180 bool
4181 BWindow::IsFilePanel() const
4182 {
4183 	return fIsFilePanel;
4184 }
4185 
4186 
4187 void
4188 BWindow::_GetDecoratorSize(float* _borderWidth, float* _tabHeight) const
4189 {
4190 	// fallback in case retrieving the decorator settings fails
4191 	// (highly unlikely)
4192 	float borderWidth = 5.0;
4193 	float tabHeight = 21.0;
4194 
4195 	BMessage settings;
4196 	if (GetDecoratorSettings(&settings) == B_OK) {
4197 		BRect tabRect;
4198 		if (settings.FindRect("tab frame", &tabRect) == B_OK)
4199 			tabHeight = tabRect.Height();
4200 		settings.FindFloat("border width", &borderWidth);
4201 	} else {
4202 		// probably no-border window look
4203 		if (fLook == B_NO_BORDER_WINDOW_LOOK) {
4204 			borderWidth = 0.0;
4205 			tabHeight = 0.0;
4206 		}
4207 		// else use fall-back values from above
4208 	}
4209 
4210 	if (_borderWidth != NULL)
4211 		*_borderWidth = borderWidth;
4212 	if (_tabHeight != NULL)
4213 		*_tabHeight = tabHeight;
4214 }
4215 
4216 
4217 void
4218 BWindow::_SendShowOrHideMessage()
4219 {
4220 	fLink->StartMessage(AS_SHOW_OR_HIDE_WINDOW);
4221 	fLink->Attach<int32>(fShowLevel);
4222 	fLink->Flush();
4223 }
4224 
4225 
4226 //	#pragma mark - C++ binary compatibility kludge
4227 
4228 
4229 extern "C" void
4230 _ReservedWindow1__7BWindow(BWindow* window, BLayout* layout)
4231 {
4232 	// SetLayout()
4233 	perform_data_set_layout data;
4234 	data.layout = layout;
4235 	window->Perform(PERFORM_CODE_SET_LAYOUT, &data);
4236 }
4237 
4238 
4239 void BWindow::_ReservedWindow2() {}
4240 void BWindow::_ReservedWindow3() {}
4241 void BWindow::_ReservedWindow4() {}
4242 void BWindow::_ReservedWindow5() {}
4243 void BWindow::_ReservedWindow6() {}
4244 void BWindow::_ReservedWindow7() {}
4245 void BWindow::_ReservedWindow8() {}
4246 
4247