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