xref: /haiku/src/kits/interface/Window.cpp (revision 1b6bc2675fe3691538c8764ab016593f3b06ca53)
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
280 		| B_CONTROL_KEY | 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() || fMinimized == minimize || !Lock())
593 		return;
594 
595 	fMinimized = minimize;
596 
597 	fLink->StartMessage(AS_MINIMIZE_WINDOW);
598 	fLink->Attach<bool>(minimize);
599 	fLink->Attach<int32>(fShowLevel);
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 			if (fShowLevel >= 1) {
774 				fLink->StartMessage(AS_SHOW_WINDOW);
775 				fLink->Flush();
776 			}
777 		}
778 
779 		return BLooper::MessageReceived(msg);
780 	}
781 
782 	BMessage replyMsg(B_REPLY);
783 	bool handled = false;
784 
785 	BMessage specifier;
786 	int32 what;
787 	const char* prop;
788 	int32 index;
789 
790 	if (msg->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK)
791 		return BLooper::MessageReceived(msg);
792 
793 	BPropertyInfo propertyInfo(sWindowPropInfo);
794 	switch (propertyInfo.FindMatch(msg, index, &specifier, what, prop)) {
795 		case 0:
796 			if (msg->what == B_GET_PROPERTY) {
797 				replyMsg.AddBool("result", IsActive());
798 				handled = true;
799 			} else if (msg->what == B_SET_PROPERTY) {
800 				bool newActive;
801 				if (msg->FindBool("data", &newActive) == B_OK) {
802 					Activate(newActive);
803 					handled = true;
804 				}
805 			}
806 			break;
807 		case 1:
808 			if (msg->what == B_GET_PROPERTY) {
809 				replyMsg.AddInt32("result", (uint32)Feel());
810 				handled = true;
811 			} else {
812 				uint32 newFeel;
813 				if (msg->FindInt32("data", (int32*)&newFeel) == B_OK) {
814 					SetFeel((window_feel)newFeel);
815 					handled = true;
816 				}
817 			}
818 			break;
819 		case 2:
820 			if (msg->what == B_GET_PROPERTY) {
821 				replyMsg.AddInt32("result", Flags());
822 				handled = true;
823 			} else {
824 				uint32 newFlags;
825 				if (msg->FindInt32("data", (int32*)&newFlags) == B_OK) {
826 					SetFlags(newFlags);
827 					handled = true;
828 				}
829 			}
830 			break;
831 		case 3:
832 			if (msg->what == B_GET_PROPERTY) {
833 				replyMsg.AddRect("result", Frame());
834 				handled = true;
835 			} else {
836 				BRect newFrame;
837 				if (msg->FindRect("data", &newFrame) == B_OK) {
838 					MoveTo(newFrame.LeftTop());
839 					ResizeTo(newFrame.Width(), newFrame.Height());
840 					handled = true;
841 				}
842 			}
843 			break;
844 		case 4:
845 			if (msg->what == B_GET_PROPERTY) {
846 				replyMsg.AddBool("result", IsHidden());
847 				handled = true;
848 			} else {
849 				bool hide;
850 				if (msg->FindBool("data", &hide) == B_OK) {
851 					if (hide) {
852 						if (!IsHidden())
853 							Hide();
854 					} else if (IsHidden())
855 						Show();
856 					handled = true;
857 				}
858 			}
859 			break;
860 		case 5:
861 			if (msg->what == B_GET_PROPERTY) {
862 				replyMsg.AddInt32("result", (uint32)Look());
863 				handled = true;
864 			} else {
865 				uint32 newLook;
866 				if (msg->FindInt32("data", (int32*)&newLook) == B_OK) {
867 					SetLook((window_look)newLook);
868 					handled = true;
869 				}
870 			}
871 			break;
872 		case 6:
873 			if (msg->what == B_GET_PROPERTY) {
874 				replyMsg.AddString("result", Title());
875 				handled = true;
876 			} else {
877 				const char* newTitle = NULL;
878 				if (msg->FindString("data", &newTitle) == B_OK) {
879 					SetTitle(newTitle);
880 					handled = true;
881 				}
882 			}
883 			break;
884 		case 7:
885 			if (msg->what == B_GET_PROPERTY) {
886 				replyMsg.AddInt32( "result", Workspaces());
887 				handled = true;
888 			} else {
889 				uint32 newWorkspaces;
890 				if (msg->FindInt32("data", (int32*)&newWorkspaces) == B_OK) {
891 					SetWorkspaces(newWorkspaces);
892 					handled = true;
893 				}
894 			}
895 			break;
896 		case 11:
897 			if (msg->what == B_GET_PROPERTY) {
898 				replyMsg.AddBool("result", IsMinimized());
899 				handled = true;
900 			} else {
901 				bool minimize;
902 				if (msg->FindBool("data", &minimize) == B_OK) {
903 					Minimize(minimize);
904 					handled = true;
905 				}
906 			}
907 			break;
908 		case 12:
909 			if (msg->what == B_GET_PROPERTY) {
910 				BMessage settings;
911 				if (GetDecoratorSettings(&settings) == B_OK) {
912 					BRect frame;
913 					if (settings.FindRect("tab frame", &frame) == B_OK) {
914 						replyMsg.AddRect("result", frame);
915 						handled = true;
916 					}
917 				}
918 			}
919 			break;
920 		default:
921 			return BLooper::MessageReceived(msg);
922 	}
923 
924 	if (handled) {
925 		if (msg->what == B_SET_PROPERTY)
926 			replyMsg.AddInt32("error", B_OK);
927 	} else {
928 		replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD;
929 		replyMsg.AddInt32("error", B_BAD_SCRIPT_SYNTAX);
930 		replyMsg.AddString("message", "Didn't understand the specifier(s)");
931 	}
932 	msg->SendReply(&replyMsg);
933 }
934 
935 
936 void
937 BWindow::DispatchMessage(BMessage* msg, BHandler* target)
938 {
939 	if (!msg)
940 		return;
941 
942 	switch (msg->what) {
943 		case B_ZOOM:
944 			Zoom();
945 			break;
946 
947 		case _MINIMIZE_:
948 			// Used by the minimize shortcut
949 			if ((Flags() & B_NOT_MINIMIZABLE) == 0)
950 				Minimize(true);
951 			break;
952 
953 		case _ZOOM_:
954 			// Used by the zoom shortcut
955 			if ((Flags() & B_NOT_ZOOMABLE) == 0)
956 				Zoom();
957 			break;
958 
959 		case _SEND_BEHIND_:
960 			SendBehind(NULL);
961 			break;
962 
963 		case _SEND_TO_FRONT_:
964 			Activate();
965 			break;
966 
967 		case _SWITCH_WORKSPACE_:
968 		{
969 			int32 deltaX = 0;
970 			msg->FindInt32("delta_x", &deltaX);
971 			int32 deltaY = 0;
972 			msg->FindInt32("delta_y", &deltaY);
973 			bool takeMeThere = false;
974 			msg->FindBool("take_me_there", &takeMeThere);
975 
976 			if (deltaX == 0 && deltaY == 0)
977 				break;
978 
979 			BPrivate::AppServerLink link;
980 			link.StartMessage(AS_GET_WORKSPACE_LAYOUT);
981 
982 			status_t status;
983 			int32 columns;
984 			int32 rows;
985 			if (link.FlushWithReply(status) != B_OK || status != B_OK)
986 				break;
987 
988 			link.Read<int32>(&columns);
989 			link.Read<int32>(&rows);
990 
991 			int32 current = current_workspace();
992 
993 			int32 nextColumn = current % columns + deltaX;
994 			int32 nextRow = current / columns + deltaY;
995 			if (nextColumn >= columns)
996 				nextColumn = columns - 1;
997 			else if (nextColumn < 0)
998 				nextColumn = 0;
999 			if (nextRow >= rows)
1000 				nextRow = rows - 1;
1001 			else if (nextRow < 0)
1002 				nextRow = 0;
1003 
1004 			int32 next = nextColumn + nextRow * columns;
1005 			if (next != current) {
1006 				BPrivate::AppServerLink link;
1007 				link.StartMessage(AS_ACTIVATE_WORKSPACE);
1008 				link.Attach<int32>(next);
1009 				link.Attach<bool>(takeMeThere);
1010 				link.Flush();
1011 			}
1012 			break;
1013 		}
1014 
1015 		case B_MINIMIZE:
1016 		{
1017 			bool minimize;
1018 			if (msg->FindBool("minimize", &minimize) == B_OK)
1019 				Minimize(minimize);
1020 			break;
1021 		}
1022 
1023 		case B_HIDE_APPLICATION:
1024 		{
1025 			// Hide all applications with the same signature
1026 			// (ie. those that are part of the same group to be consistent
1027 			// to what the Deskbar shows you).
1028 			app_info info;
1029 			be_app->GetAppInfo(&info);
1030 
1031 			BList list;
1032 			be_roster->GetAppList(info.signature, &list);
1033 
1034 			for (int32 i = 0; i < list.CountItems(); i++) {
1035 				do_minimize_team(BRect(), (team_id)list.ItemAt(i), false);
1036 			}
1037 			break;
1038 		}
1039 
1040 		case B_WINDOW_RESIZED:
1041 		{
1042 			int32 width, height;
1043 			if (msg->FindInt32("width", &width) == B_OK
1044 				&& msg->FindInt32("height", &height) == B_OK) {
1045 				// combine with pending resize notifications
1046 				BMessage* pendingMessage;
1047 				while ((pendingMessage = MessageQueue()->FindMessage(B_WINDOW_RESIZED, 0))) {
1048 					int32 nextWidth;
1049 					if (pendingMessage->FindInt32("width", &nextWidth) == B_OK)
1050 						width = nextWidth;
1051 
1052 					int32 nextHeight;
1053 					if (pendingMessage->FindInt32("height", &nextHeight) == B_OK)
1054 						height = nextHeight;
1055 
1056 					MessageQueue()->RemoveMessage(pendingMessage);
1057 					delete pendingMessage;
1058 						// this deletes the first *additional* message
1059 						// fCurrentMessage is safe
1060 				}
1061 				if (width != fFrame.Width() || height != fFrame.Height()) {
1062 					// NOTE: we might have already handled the resize
1063 					// in an _UPDATE_ message
1064 					fFrame.right = fFrame.left + width;
1065 					fFrame.bottom = fFrame.top + height;
1066 
1067 					_AdoptResize();
1068 //					FrameResized(width, height);
1069 				}
1070 // call hook function anyways
1071 // TODO: When a window is resized programmatically,
1072 // it receives this message, and maybe it is wise to
1073 // keep the asynchronous nature of this process to
1074 // not risk breaking any apps.
1075 FrameResized(width, height);
1076 			}
1077 			break;
1078 		}
1079 
1080 		case B_WINDOW_MOVED:
1081 		{
1082 			BPoint origin;
1083 			if (msg->FindPoint("where", &origin) == B_OK) {
1084 				if (fFrame.LeftTop() != origin) {
1085 					// NOTE: we might have already handled the move
1086 					// in an _UPDATE_ message
1087 					fFrame.OffsetTo(origin);
1088 
1089 //					FrameMoved(origin);
1090 				}
1091 // call hook function anyways
1092 // TODO: When a window is moved programmatically,
1093 // it receives this message, and maybe it is wise to
1094 // keep the asynchronous nature of this process to
1095 // not risk breaking any apps.
1096 FrameMoved(origin);
1097 			}
1098 			break;
1099 		}
1100 
1101 		case B_WINDOW_ACTIVATED:
1102 			if (target != this) {
1103 				target->MessageReceived(msg);
1104 				break;
1105 			}
1106 
1107 			bool active;
1108 			if (msg->FindBool("active", &active) != B_OK)
1109 				break;
1110 
1111 			// find latest activation message
1112 
1113 			while (true) {
1114 				BMessage* pendingMessage = MessageQueue()->FindMessage(
1115 					B_WINDOW_ACTIVATED, 0);
1116 				if (pendingMessage == NULL)
1117 					break;
1118 
1119 				bool nextActive;
1120 				if (pendingMessage->FindBool("active", &nextActive) == B_OK)
1121 					active = nextActive;
1122 
1123 				MessageQueue()->RemoveMessage(pendingMessage);
1124 				delete pendingMessage;
1125 			}
1126 
1127 			if (active != fActive) {
1128 				fActive = active;
1129 
1130 				WindowActivated(active);
1131 
1132 				// call hook function 'WindowActivated(bool)' for all
1133 				// views attached to this window.
1134 				fTopView->_Activate(active);
1135 
1136 				// we notify the input server if we are gaining or losing focus
1137 				// from a view which has the B_INPUT_METHOD_AWARE on a window
1138 				// activation
1139 				if (!active)
1140 					break;
1141 				bool inputMethodAware = false;
1142 				if (fFocus)
1143 					inputMethodAware = fFocus->Flags() & B_INPUT_METHOD_AWARE;
1144 				BMessage msg(inputMethodAware ? IS_FOCUS_IM_AWARE_VIEW : IS_UNFOCUS_IM_AWARE_VIEW);
1145 				BMessenger messenger(fFocus);
1146 				BMessage reply;
1147 				if (fFocus)
1148 					msg.AddMessenger("view", messenger);
1149 				_control_input_server_(&msg, &reply);
1150 			}
1151 			break;
1152 
1153 		case B_SCREEN_CHANGED:
1154 			if (target == this) {
1155 				BRect frame;
1156 				uint32 mode;
1157 				if (msg->FindRect("frame", &frame) == B_OK
1158 					&& msg->FindInt32("mode", (int32*)&mode) == B_OK)
1159 					ScreenChanged(frame, (color_space)mode);
1160 			} else
1161 				target->MessageReceived(msg);
1162 			break;
1163 
1164 		case B_WORKSPACE_ACTIVATED:
1165 			if (target == this) {
1166 				uint32 workspace;
1167 				bool active;
1168 				if (msg->FindInt32("workspace", (int32*)&workspace) == B_OK
1169 					&& msg->FindBool("active", &active) == B_OK)
1170 					WorkspaceActivated(workspace, active);
1171 			} else
1172 				target->MessageReceived(msg);
1173 			break;
1174 
1175 		case B_WORKSPACES_CHANGED:
1176 			if (target == this) {
1177 				uint32 oldWorkspace, newWorkspace;
1178 				if (msg->FindInt32("old", (int32*)&oldWorkspace) == B_OK
1179 					&& msg->FindInt32("new", (int32*)&newWorkspace) == B_OK)
1180 					WorkspacesChanged(oldWorkspace, newWorkspace);
1181 			} else
1182 				target->MessageReceived(msg);
1183 			break;
1184 
1185 		case B_INVALIDATE:
1186 		{
1187 			if (BView* view = dynamic_cast<BView*>(target)) {
1188 				BRect rect;
1189 				if (msg->FindRect("be:area", &rect) == B_OK)
1190 					view->Invalidate(rect);
1191 				else
1192 					view->Invalidate();
1193 			} else
1194 				target->MessageReceived(msg);
1195 			break;
1196 		}
1197 
1198 		case B_KEY_DOWN:
1199 		{
1200 			if (!_HandleKeyDown(msg)) {
1201 				if (BView* view = dynamic_cast<BView*>(target)) {
1202 					// TODO: cannot use "string" here if we support having
1203 					// different font encoding per view (it's supposed to be
1204 					// converted by _HandleKeyDown() one day)
1205 					const char* string;
1206 					ssize_t bytes;
1207 					if (msg->FindData("bytes", B_STRING_TYPE,
1208 						(const void**)&string, &bytes) == B_OK) {
1209 						view->KeyDown(string, bytes - 1);
1210 					}
1211 				} else
1212 					target->MessageReceived(msg);
1213 			}
1214 			break;
1215 		}
1216 
1217 		case B_KEY_UP:
1218 		{
1219 			// TODO: same as above
1220 			if (BView* view = dynamic_cast<BView*>(target)) {
1221 				const char* string;
1222 				ssize_t bytes;
1223 				if (msg->FindData("bytes", B_STRING_TYPE,
1224 					(const void**)&string, &bytes) == B_OK) {
1225 					view->KeyUp(string, bytes - 1);
1226 				}
1227 			} else
1228 				target->MessageReceived(msg);
1229 			break;
1230 		}
1231 
1232 		case B_UNMAPPED_KEY_DOWN:
1233 		{
1234 			if (!_HandleUnmappedKeyDown(msg))
1235 				target->MessageReceived(msg);
1236 			break;
1237 		}
1238 
1239 		case B_MOUSE_DOWN:
1240 		{
1241 			BView* view = dynamic_cast<BView*>(target);
1242 
1243 			if (view != NULL) {
1244 				BPoint where;
1245 				msg->FindPoint("be:view_where", &where);
1246 				view->MouseDown(where);
1247 			} else
1248 				target->MessageReceived(msg);
1249 
1250 			break;
1251 		}
1252 
1253 		case B_MOUSE_UP:
1254 		{
1255 			if (BView* view = dynamic_cast<BView*>(target)) {
1256 				BPoint where;
1257 				msg->FindPoint("be:view_where", &where);
1258 				view->fMouseEventOptions = 0;
1259 				view->MouseUp(where);
1260 			} else
1261 				target->MessageReceived(msg);
1262 
1263 			break;
1264 		}
1265 
1266 		case B_MOUSE_MOVED:
1267 		{
1268 			if (BView* view = dynamic_cast<BView*>(target)) {
1269 				uint32 eventOptions = view->fEventOptions
1270 					| view->fMouseEventOptions;
1271 				bool noHistory = eventOptions & B_NO_POINTER_HISTORY;
1272 				bool dropIfLate = !(eventOptions & B_FULL_POINTER_HISTORY);
1273 
1274 				bigtime_t eventTime;
1275 				if (msg->FindInt64("when", (int64*)&eventTime) < B_OK)
1276 					eventTime = system_time();
1277 
1278 				uint32 transit;
1279 				msg->FindInt32("be:transit", (int32*)&transit);
1280 				// don't drop late messages with these important transit values
1281 				if (transit == B_ENTERED_VIEW || transit == B_EXITED_VIEW)
1282 					dropIfLate = false;
1283 
1284 				// TODO: The dropping code may have the following problem:
1285 				// On slower computers, 20ms may just be to abitious a delay.
1286 				// There, we might constantly check the message queue for a
1287 				// newer message, not find any, and still use the only but
1288 				// later than 20ms message, which of course makes the whole
1289 				// thing later than need be. An adaptive delay would be
1290 				// kind of neat, but would probably use additional BWindow
1291 				// members to count the successful versus fruitless queue
1292 				// searches and the delay value itself or something similar.
1293 
1294 				if (noHistory
1295 					|| (dropIfLate && (system_time() - eventTime > 20000))) {
1296 					// filter out older mouse moved messages in the queue
1297 					_DequeueAll();
1298 					BMessageQueue* queue = MessageQueue();
1299 					queue->Lock();
1300 
1301 					BMessage* moved;
1302 					for (int32 i = 0; (moved = queue->FindMessage(i)) != NULL;
1303 							i++) {
1304 						if (moved != msg && moved->what == B_MOUSE_MOVED) {
1305 							// there is a newer mouse moved message in the
1306 							// queue, just ignore the current one, the newer one
1307 							// will be handled here eventually
1308 							queue->Unlock();
1309 							return;
1310 						}
1311 					}
1312 					queue->Unlock();
1313 				}
1314 
1315 				BPoint where;
1316 				uint32 buttons;
1317 				msg->FindPoint("be:view_where", &where);
1318 				msg->FindInt32("buttons", (int32*)&buttons);
1319 
1320 				delete fIdleMouseRunner;
1321 
1322 				if (transit != B_EXITED_VIEW && transit != B_OUTSIDE_VIEW) {
1323 					// Start new idle runner
1324 					BMessage idle(B_MOUSE_IDLE);
1325 					idle.AddPoint("be:view_where", where);
1326 					fIdleMouseRunner = new BMessageRunner(
1327 						BMessenger(NULL, this), &idle,
1328 						BToolTipManager::Manager()->ShowDelay(), 1);
1329 				} else {
1330 					fIdleMouseRunner = NULL;
1331 					if (dynamic_cast<BPrivate::ToolTipWindow*>(this) == NULL)
1332 						BToolTipManager::Manager()->HideTip();
1333 				}
1334 
1335 				BMessage* dragMessage = NULL;
1336 				if (msg->HasMessage("be:drag_message")) {
1337 					dragMessage = new BMessage();
1338 					if (msg->FindMessage("be:drag_message", dragMessage)
1339 							!= B_OK) {
1340 						delete dragMessage;
1341 						dragMessage = NULL;
1342 					}
1343 				}
1344 
1345 				view->MouseMoved(where, transit, dragMessage);
1346 				delete dragMessage;
1347 			} else
1348 				target->MessageReceived(msg);
1349 
1350 			break;
1351 		}
1352 
1353 		case B_PULSE:
1354 			if (target == this && fPulseRunner) {
1355 				fTopView->_Pulse();
1356 				fLink->Flush();
1357 			} else
1358 				target->MessageReceived(msg);
1359 			break;
1360 
1361 		case _UPDATE_:
1362 		{
1363 //bigtime_t now = system_time();
1364 //bigtime_t drawTime = 0;
1365 			STRACE(("info:BWindow handling _UPDATE_.\n"));
1366 
1367 			fLink->StartMessage(AS_BEGIN_UPDATE);
1368 			fInTransaction = true;
1369 
1370 			int32 code;
1371 			if (fLink->FlushWithReply(code) == B_OK
1372 				&& code == B_OK) {
1373 				// read current window position and size first,
1374 				// the update rect is in screen coordinates...
1375 				// so we need to be up to date
1376 				BPoint origin;
1377 				fLink->Read<BPoint>(&origin);
1378 				float width;
1379 				float height;
1380 				fLink->Read<float>(&width);
1381 				fLink->Read<float>(&height);
1382 				if (origin != fFrame.LeftTop()) {
1383 					// TODO: remove code duplicatation with
1384 					// B_WINDOW_MOVED case...
1385 					//printf("window position was not up to date\n");
1386 					fFrame.OffsetTo(origin);
1387 					FrameMoved(origin);
1388 				}
1389 				if (width != fFrame.Width() || height != fFrame.Height()) {
1390 					// TODO: remove code duplicatation with
1391 					// B_WINDOW_RESIZED case...
1392 					//printf("window size was not up to date\n");
1393 					fFrame.right = fFrame.left + width;
1394 					fFrame.bottom = fFrame.top + height;
1395 
1396 					_AdoptResize();
1397 					FrameResized(width, height);
1398 				}
1399 
1400 				// read tokens for views that need to be drawn
1401 				// NOTE: we need to read the tokens completely
1402 				// first, we cannot draw views in between reading
1403 				// the tokens, since other communication would likely
1404 				// mess up the data in the link.
1405 				struct ViewUpdateInfo {
1406 					int32 token;
1407 					BRect updateRect;
1408 				};
1409 				BList infos(20);
1410 				while (true) {
1411 					// read next token and create/add ViewUpdateInfo
1412 					int32 token;
1413 					status_t error = fLink->Read<int32>(&token);
1414 					if (error < B_OK || token == B_NULL_TOKEN)
1415 						break;
1416 					ViewUpdateInfo* info = new(std::nothrow) ViewUpdateInfo;
1417 					if (info == NULL || !infos.AddItem(info)) {
1418 						delete info;
1419 						break;
1420 					}
1421 					info->token = token;
1422 					// read culmulated update rect (is in screen coords)
1423 					error = fLink->Read<BRect>(&(info->updateRect));
1424 					if (error < B_OK)
1425 						break;
1426 				}
1427 				// draw
1428 				int32 count = infos.CountItems();
1429 				for (int32 i = 0; i < count; i++) {
1430 //bigtime_t drawStart = system_time();
1431 					ViewUpdateInfo* info
1432 						= (ViewUpdateInfo*)infos.ItemAtFast(i);
1433 					if (BView* view = _FindView(info->token))
1434 						view->_Draw(info->updateRect);
1435 					else {
1436 						printf("_UPDATE_ - didn't find view by token: %ld\n",
1437 							info->token);
1438 					}
1439 //drawTime += system_time() - drawStart;
1440 				}
1441 				// NOTE: The tokens are actually hirachically sorted,
1442 				// so traversing the list in revers and calling
1443 				// child->_DrawAfterChildren() actually works like intended.
1444 				for (int32 i = count - 1; i >= 0; i--) {
1445 					ViewUpdateInfo* info
1446 						= (ViewUpdateInfo*)infos.ItemAtFast(i);
1447 					if (BView* view = _FindView(info->token))
1448 						view->_DrawAfterChildren(info->updateRect);
1449 					delete info;
1450 				}
1451 
1452 //printf("  %ld views drawn, total Draw() time: %lld\n", count, drawTime);
1453 			}
1454 
1455 			fLink->StartMessage(AS_END_UPDATE);
1456 			fLink->Flush();
1457 			fInTransaction = false;
1458 			fUpdateRequested = false;
1459 
1460 //printf("BWindow(%s) - UPDATE took %lld usecs\n", Title(), system_time() - now);
1461 			break;
1462 		}
1463 
1464 		case _MENUS_DONE_:
1465 			MenusEnded();
1466 			break;
1467 
1468 		// These two are obviously some kind of old scripting messages
1469 		// this is NOT an app_server message and we have to be cautious
1470 		case B_WINDOW_MOVE_BY:
1471 		{
1472 			BPoint offset;
1473 			if (msg->FindPoint("data", &offset) == B_OK)
1474 				MoveBy(offset.x, offset.y);
1475 			else
1476 				msg->SendReply(B_MESSAGE_NOT_UNDERSTOOD);
1477 			break;
1478 		}
1479 
1480 		// this is NOT an app_server message and we have to be cautious
1481 		case B_WINDOW_MOVE_TO:
1482 		{
1483 			BPoint origin;
1484 			if (msg->FindPoint("data", &origin) == B_OK)
1485 				MoveTo(origin);
1486 			else
1487 				msg->SendReply(B_MESSAGE_NOT_UNDERSTOOD);
1488 			break;
1489 		}
1490 
1491 		case B_LAYOUT_WINDOW:
1492 		{
1493 			Layout(false);
1494 			break;
1495 		}
1496 
1497 		default:
1498 			BLooper::DispatchMessage(msg, target);
1499 			break;
1500 	}
1501 }
1502 
1503 
1504 void
1505 BWindow::FrameMoved(BPoint new_position)
1506 {
1507 	// does nothing
1508 	// Hook function
1509 }
1510 
1511 
1512 void
1513 BWindow::FrameResized(float new_width, float new_height)
1514 {
1515 	// does nothing
1516 	// Hook function
1517 }
1518 
1519 
1520 void
1521 BWindow::WorkspacesChanged(uint32 old_ws, uint32 new_ws)
1522 {
1523 	// does nothing
1524 	// Hook function
1525 }
1526 
1527 
1528 void
1529 BWindow::WorkspaceActivated(int32 ws, bool state)
1530 {
1531 	// does nothing
1532 	// Hook function
1533 }
1534 
1535 
1536 void
1537 BWindow::MenusBeginning()
1538 {
1539 	// does nothing
1540 	// Hook function
1541 }
1542 
1543 
1544 void
1545 BWindow::MenusEnded()
1546 {
1547 	// does nothing
1548 	// Hook function
1549 }
1550 
1551 
1552 void
1553 BWindow::SetSizeLimits(float minWidth, float maxWidth,
1554 	float minHeight, float maxHeight)
1555 {
1556 	if (minWidth > maxWidth || minHeight > maxHeight)
1557 		return;
1558 
1559 	if (!Lock())
1560 		return;
1561 
1562 	fLink->StartMessage(AS_SET_SIZE_LIMITS);
1563 	fLink->Attach<float>(minWidth);
1564 	fLink->Attach<float>(maxWidth);
1565 	fLink->Attach<float>(minHeight);
1566 	fLink->Attach<float>(maxHeight);
1567 
1568 	int32 code;
1569 	if (fLink->FlushWithReply(code) == B_OK
1570 		&& code == B_OK) {
1571 		// read the values that were really enforced on
1572 		// the server side (the window frame could have
1573 		// been changed, too)
1574 		fLink->Read<BRect>(&fFrame);
1575 		fLink->Read<float>(&fMinWidth);
1576 		fLink->Read<float>(&fMaxWidth);
1577 		fLink->Read<float>(&fMinHeight);
1578 		fLink->Read<float>(&fMaxHeight);
1579 
1580 		_AdoptResize();
1581 			// TODO: the same has to be done for SetLook() (that can alter
1582 			//		the size limits, and hence, the size of the window
1583 	}
1584 	Unlock();
1585 }
1586 
1587 
1588 void
1589 BWindow::GetSizeLimits(float* _minWidth, float* _maxWidth, float* _minHeight,
1590 	float* _maxHeight)
1591 {
1592 	// TODO: What about locking?!?
1593 	if (_minHeight != NULL)
1594 		*_minHeight = fMinHeight;
1595 	if (_minWidth != NULL)
1596 		*_minWidth = fMinWidth;
1597 	if (_maxHeight != NULL)
1598 		*_maxHeight = fMaxHeight;
1599 	if (_maxWidth != NULL)
1600 		*_maxWidth = fMaxWidth;
1601 }
1602 
1603 
1604 /*!	Updates the window's size limits from the minimum and maximum sizes of its
1605 	top view.
1606 
1607 	Is a no-op, unless the \c B_AUTO_UPDATE_SIZE_LIMITS window flag is set.
1608 
1609 	The method is called automatically after a layout invalidation. Since it is
1610 	invoked asynchronously, calling this method manually is necessary, if it is
1611 	desired to adjust the limits (and as a possible side effect the window size)
1612 	earlier (e.g. before the first Show()).
1613 */
1614 void
1615 BWindow::UpdateSizeLimits()
1616 {
1617 	if ((fFlags & B_AUTO_UPDATE_SIZE_LIMITS) != 0) {
1618 		// Get min/max constraints of the top view and enforce window
1619 		// size limits respectively.
1620 		BSize minSize = fTopView->MinSize();
1621 		BSize maxSize = fTopView->MaxSize();
1622 		SetSizeLimits(minSize.width, maxSize.width,
1623 			minSize.height, maxSize.height);
1624 	}
1625 }
1626 
1627 
1628 status_t
1629 BWindow::SetDecoratorSettings(const BMessage& settings)
1630 {
1631 	// flatten the given settings into a buffer and send
1632 	// it to the app_server to apply the settings to the
1633 	// decorator
1634 
1635 	int32 size = settings.FlattenedSize();
1636 	char buffer[size];
1637 	status_t status = settings.Flatten(buffer, size);
1638 	if (status != B_OK)
1639 		return status;
1640 
1641 	if (!Lock())
1642 		return B_ERROR;
1643 
1644 	status = fLink->StartMessage(AS_SET_DECORATOR_SETTINGS);
1645 
1646 	if (status == B_OK)
1647 		status = fLink->Attach<int32>(size);
1648 
1649 	if (status == B_OK)
1650 		status = fLink->Attach(buffer, size);
1651 
1652 	if (status == B_OK)
1653 		status = fLink->Flush();
1654 
1655 	Unlock();
1656 
1657 	return status;
1658 }
1659 
1660 
1661 status_t
1662 BWindow::GetDecoratorSettings(BMessage* settings) const
1663 {
1664 	// read a flattened settings message from the app_server
1665 	// and put it into settings
1666 
1667 	if (!const_cast<BWindow*>(this)->Lock())
1668 		return B_ERROR;
1669 
1670 	status_t status = fLink->StartMessage(AS_GET_DECORATOR_SETTINGS);
1671 
1672 	if (status == B_OK) {
1673 		int32 code;
1674 		status = fLink->FlushWithReply(code);
1675 		if (status == B_OK && code != B_OK)
1676 			status = code;
1677 	}
1678 
1679 	if (status == B_OK) {
1680 		int32 size;
1681 		status = fLink->Read<int32>(&size);
1682 		if (status == B_OK) {
1683 			char buffer[size];
1684 			status = fLink->Read(buffer, size);
1685 			if (status == B_OK) {
1686 				status = settings->Unflatten(buffer);
1687 			}
1688 		}
1689 	}
1690 
1691 	const_cast<BWindow*>(this)->Unlock();
1692 
1693 	return status;
1694 }
1695 
1696 
1697 void
1698 BWindow::SetZoomLimits(float maxWidth, float maxHeight)
1699 {
1700 	// TODO: What about locking?!?
1701 	if (maxWidth > fMaxWidth)
1702 		maxWidth = fMaxWidth;
1703 	else
1704 		fMaxZoomWidth = maxWidth;
1705 
1706 	if (maxHeight > fMaxHeight)
1707 		maxHeight = fMaxHeight;
1708 	else
1709 		fMaxZoomHeight = maxHeight;
1710 }
1711 
1712 
1713 void
1714 BWindow::Zoom(BPoint leftTop, float width, float height)
1715 {
1716 	// the default implementation of this hook function
1717 	// just does the obvious:
1718 	MoveTo(leftTop);
1719 	ResizeTo(width, height);
1720 }
1721 
1722 
1723 void
1724 BWindow::Zoom()
1725 {
1726 	// TODO: What about locking?!?
1727 
1728 	// From BeBook:
1729 	// The dimensions that non-virtual Zoom() passes to hook Zoom() are deduced
1730 	// from the smallest of three rectangles:
1731 
1732 	float borderWidth;
1733 	float tabHeight;
1734 	_GetDecoratorSize(&borderWidth, &tabHeight);
1735 
1736 	// 1) the rectangle defined by SetZoomLimits(),
1737 	float zoomedWidth = fMaxZoomWidth;
1738 	float zoomedHeight = fMaxZoomHeight;
1739 
1740 	// 2) the rectangle defined by SetSizeLimits()
1741 	if (fMaxWidth < zoomedWidth)
1742 		zoomedWidth = fMaxWidth;
1743 	if (fMaxHeight < zoomedHeight)
1744 		zoomedHeight = fMaxHeight;
1745 
1746 	// 3) the screen rectangle
1747 	BScreen screen(this);
1748 	// TODO: Broken for tab on left side windows...
1749 	float screenWidth = screen.Frame().Width() - 2 * borderWidth;
1750 	float screenHeight = screen.Frame().Height() - (2 * borderWidth + tabHeight);
1751 	if (screenWidth < zoomedWidth)
1752 		zoomedWidth = screenWidth;
1753 	if (screenHeight < zoomedHeight)
1754 		zoomedHeight = screenHeight;
1755 
1756 	BPoint zoomedLeftTop = screen.Frame().LeftTop() + BPoint(borderWidth,
1757 		tabHeight + borderWidth);
1758 	// Center if window cannot be made full screen
1759 	if (screenWidth > zoomedWidth)
1760 		zoomedLeftTop.x += (screenWidth - zoomedWidth) / 2;
1761 	if (screenHeight > zoomedHeight)
1762 		zoomedLeftTop.y += (screenHeight - zoomedHeight) / 2;
1763 
1764 	// Un-Zoom
1765 
1766 	if (fPreviousFrame.IsValid()
1767 		// NOTE: don't check for fFrame.LeftTop() == zoomedLeftTop
1768 		// -> makes it easier on the user to get a window back into place
1769 		&& fFrame.Width() == zoomedWidth && fFrame.Height() == zoomedHeight) {
1770 		// already zoomed!
1771 		Zoom(fPreviousFrame.LeftTop(), fPreviousFrame.Width(),
1772 			fPreviousFrame.Height());
1773 		return;
1774 	}
1775 
1776 	// Zoom
1777 
1778 	// remember fFrame for later "unzooming"
1779 	fPreviousFrame = fFrame;
1780 
1781 	Zoom(zoomedLeftTop, zoomedWidth, zoomedHeight);
1782 }
1783 
1784 
1785 void
1786 BWindow::ScreenChanged(BRect screen_size, color_space depth)
1787 {
1788 	// Hook function
1789 }
1790 
1791 
1792 void
1793 BWindow::SetPulseRate(bigtime_t rate)
1794 {
1795 	// TODO: What about locking?!?
1796 	if (rate < 0
1797 		|| (rate == fPulseRate && !((rate == 0) ^ (fPulseRunner == NULL))))
1798 		return;
1799 
1800 	fPulseRate = rate;
1801 
1802 	if (rate > 0) {
1803 		if (fPulseRunner == NULL) {
1804 			BMessage message(B_PULSE);
1805 			fPulseRunner = new(std::nothrow) BMessageRunner(BMessenger(this),
1806 				&message, rate);
1807 		} else {
1808 			fPulseRunner->SetInterval(rate);
1809 		}
1810 	} else {
1811 		// rate == 0
1812 		delete fPulseRunner;
1813 		fPulseRunner = NULL;
1814 	}
1815 }
1816 
1817 
1818 bigtime_t
1819 BWindow::PulseRate() const
1820 {
1821 	return fPulseRate;
1822 }
1823 
1824 
1825 void
1826 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMenuItem* item)
1827 {
1828 	Shortcut* shortcut = new(std::nothrow) Shortcut(key, modifiers, item);
1829 	if (shortcut == NULL)
1830 		return;
1831 
1832 	// removes the shortcut if it already exists!
1833 	RemoveShortcut(key, modifiers);
1834 
1835 	fShortcuts.AddItem(shortcut);
1836 }
1837 
1838 
1839 void
1840 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage* message)
1841 {
1842 	AddShortcut(key, modifiers, message, this);
1843 }
1844 
1845 
1846 void
1847 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage* message,
1848 	BHandler* target)
1849 {
1850 	if (message == NULL)
1851 		return;
1852 
1853 	Shortcut* shortcut = new(std::nothrow) Shortcut(key, modifiers, message,
1854 		target);
1855 	if (shortcut == NULL)
1856 		return;
1857 
1858 	// removes the shortcut if it already exists!
1859 	RemoveShortcut(key, modifiers);
1860 
1861 	fShortcuts.AddItem(shortcut);
1862 }
1863 
1864 
1865 void
1866 BWindow::RemoveShortcut(uint32 key, uint32 modifiers)
1867 {
1868 	Shortcut* shortcut = _FindShortcut(key, modifiers);
1869 	if (shortcut != NULL) {
1870 		fShortcuts.RemoveItem(shortcut);
1871 		delete shortcut;
1872 	} else if ((key == 'q' || key == 'Q') && modifiers == B_COMMAND_KEY) {
1873 		// the quit shortcut is a fake shortcut
1874 		fNoQuitShortcut = true;
1875 	}
1876 }
1877 
1878 
1879 BButton*
1880 BWindow::DefaultButton() const
1881 {
1882 	// TODO: What about locking?!?
1883 	return fDefaultButton;
1884 }
1885 
1886 
1887 void
1888 BWindow::SetDefaultButton(BButton* button)
1889 {
1890 	// TODO: What about locking?!?
1891 	if (fDefaultButton == button)
1892 		return;
1893 
1894 	if (fDefaultButton != NULL) {
1895 		// tell old button it's no longer the default one
1896 		BButton* oldDefault = fDefaultButton;
1897 		oldDefault->MakeDefault(false);
1898 		oldDefault->Invalidate();
1899 	}
1900 
1901 	fDefaultButton = button;
1902 
1903 	if (button != NULL) {
1904 		// notify new default button
1905 		fDefaultButton->MakeDefault(true);
1906 		fDefaultButton->Invalidate();
1907 	}
1908 }
1909 
1910 
1911 bool
1912 BWindow::NeedsUpdate() const
1913 {
1914 	if (!const_cast<BWindow*>(this)->Lock())
1915 		return false;
1916 
1917 	fLink->StartMessage(AS_NEEDS_UPDATE);
1918 
1919 	int32 code = B_ERROR;
1920 	fLink->FlushWithReply(code);
1921 
1922 	const_cast<BWindow*>(this)->Unlock();
1923 
1924 	return code == B_OK;
1925 }
1926 
1927 
1928 void
1929 BWindow::UpdateIfNeeded()
1930 {
1931 	// works only from the window thread
1932 	if (find_thread(NULL) != Thread())
1933 		return;
1934 
1935 	// if the queue is already locked we are called recursivly
1936 	// from our own dispatched update message
1937 	if (((const BMessageQueue*)MessageQueue())->IsLocked())
1938 		return;
1939 
1940 	if (!Lock())
1941 		return;
1942 
1943 	// make sure all requests that would cause an update have
1944 	// arrived at the server
1945 	Sync();
1946 
1947 	// Since we're blocking the event loop, we need to retrieve
1948 	// all messages that are pending on the port.
1949 	_DequeueAll();
1950 
1951 	BMessageQueue* queue = MessageQueue();
1952 
1953 	// First process and remove any _UPDATE_ message in the queue
1954 	// With the current design, there can only be one at a time
1955 
1956 	while (true) {
1957 		queue->Lock();
1958 
1959 		BMessage* message = queue->FindMessage(_UPDATE_, 0);
1960 		queue->RemoveMessage(message);
1961 
1962 		queue->Unlock();
1963 
1964 		if (message == NULL)
1965 			break;
1966 
1967 		BWindow::DispatchMessage(message, this);
1968 		delete message;
1969 	}
1970 
1971 	Unlock();
1972 }
1973 
1974 
1975 BView*
1976 BWindow::FindView(const char* viewName) const
1977 {
1978 	BAutolock locker(const_cast<BWindow*>(this));
1979 	if (!locker.IsLocked())
1980 		return NULL;
1981 
1982 	return fTopView->FindView(viewName);
1983 }
1984 
1985 
1986 BView*
1987 BWindow::FindView(BPoint point) const
1988 {
1989 	BAutolock locker(const_cast<BWindow*>(this));
1990 	if (!locker.IsLocked())
1991 		return NULL;
1992 
1993 	// point is assumed to be in window coordinates,
1994 	// fTopView has same bounds as window
1995 	return _FindView(fTopView, point);
1996 }
1997 
1998 
1999 BView*
2000 BWindow::CurrentFocus() const
2001 {
2002 	return fFocus;
2003 }
2004 
2005 
2006 void
2007 BWindow::Activate(bool active)
2008 {
2009 	if (!Lock())
2010 		return;
2011 
2012 	if (!IsHidden()) {
2013 		fMinimized = false;
2014 			// activating a window will also unminimize it
2015 
2016 		fLink->StartMessage(AS_ACTIVATE_WINDOW);
2017 		fLink->Attach<bool>(active);
2018 		fLink->Flush();
2019 	}
2020 
2021 	Unlock();
2022 }
2023 
2024 
2025 void
2026 BWindow::WindowActivated(bool state)
2027 {
2028 	// hook function
2029 	// does nothing
2030 }
2031 
2032 
2033 void
2034 BWindow::ConvertToScreen(BPoint* point) const
2035 {
2036 	point->x += fFrame.left;
2037 	point->y += fFrame.top;
2038 }
2039 
2040 
2041 BPoint
2042 BWindow::ConvertToScreen(BPoint point) const
2043 {
2044 	return point + fFrame.LeftTop();
2045 }
2046 
2047 
2048 void
2049 BWindow::ConvertFromScreen(BPoint* point) const
2050 {
2051 	point->x -= fFrame.left;
2052 	point->y -= fFrame.top;
2053 }
2054 
2055 
2056 BPoint
2057 BWindow::ConvertFromScreen(BPoint point) const
2058 {
2059 	return point - fFrame.LeftTop();
2060 }
2061 
2062 
2063 void
2064 BWindow::ConvertToScreen(BRect* rect) const
2065 {
2066 	rect->OffsetBy(fFrame.LeftTop());
2067 }
2068 
2069 
2070 BRect
2071 BWindow::ConvertToScreen(BRect rect) const
2072 {
2073 	return rect.OffsetByCopy(fFrame.LeftTop());
2074 }
2075 
2076 
2077 void
2078 BWindow::ConvertFromScreen(BRect* rect) const
2079 {
2080 	rect->OffsetBy(-fFrame.left, -fFrame.top);
2081 }
2082 
2083 
2084 BRect
2085 BWindow::ConvertFromScreen(BRect rect) const
2086 {
2087 	return rect.OffsetByCopy(-fFrame.left, -fFrame.top);
2088 }
2089 
2090 
2091 bool
2092 BWindow::IsMinimized() const
2093 {
2094 	BAutolock locker(const_cast<BWindow*>(this));
2095 	if (!locker.IsLocked())
2096 		return false;
2097 
2098 	// Hiding takes precendence over minimization!!!
2099 	if (IsHidden())
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 		if (fShowLevel == 1) {
2614 			fLink->StartMessage(AS_SHOW_WINDOW);
2615 			fLink->Flush();
2616 		}
2617 
2618 		runCalled = fRunCalled;
2619 
2620 		Unlock();
2621 	}
2622 
2623 	if (!runCalled) {
2624 		// This is the fist time Show() is called, which implicitly runs the
2625 		// looper. NOTE: The window is still locked if it has not been
2626 		// run yet, so accessing members is safe.
2627 		if (fLink->SenderPort() < B_OK) {
2628 			// We don't have valid app_server connection; there is no point
2629 			// in starting our looper
2630 			fThread = B_ERROR;
2631 			return;
2632 		} else
2633 			Run();
2634 	}
2635 }
2636 
2637 
2638 void
2639 BWindow::Hide()
2640 {
2641 	if (!Lock())
2642 		return;
2643 
2644 	fShowLevel--;
2645 
2646 	if (fShowLevel == 0) {
2647 		fLink->StartMessage(AS_HIDE_WINDOW);
2648 		fLink->Flush();
2649 	}
2650 
2651 	Unlock();
2652 }
2653 
2654 
2655 bool
2656 BWindow::IsHidden() const
2657 {
2658 	return fShowLevel <= 0;
2659 }
2660 
2661 
2662 bool
2663 BWindow::QuitRequested()
2664 {
2665 	return BLooper::QuitRequested();
2666 }
2667 
2668 
2669 thread_id
2670 BWindow::Run()
2671 {
2672 	return BLooper::Run();
2673 }
2674 
2675 
2676 void
2677 BWindow::SetLayout(BLayout* layout)
2678 {
2679 	fTopView->SetLayout(layout);
2680 }
2681 
2682 
2683 BLayout*
2684 BWindow::GetLayout() const
2685 {
2686 	return fTopView->GetLayout();
2687 }
2688 
2689 
2690 void
2691 BWindow::InvalidateLayout(bool descendants)
2692 {
2693 	fTopView->InvalidateLayout(descendants);
2694 }
2695 
2696 
2697 void
2698 BWindow::Layout(bool force)
2699 {
2700 	UpdateSizeLimits();
2701 
2702 	// Do the actual layout
2703 	fTopView->Layout(force);
2704 }
2705 
2706 
2707 status_t
2708 BWindow::GetSupportedSuites(BMessage* data)
2709 {
2710 	if (data == NULL)
2711 		return B_BAD_VALUE;
2712 
2713 	status_t status = data->AddString("suites", "suite/vnd.Be-window");
2714 	if (status == B_OK) {
2715 		BPropertyInfo propertyInfo(sWindowPropInfo, sWindowValueInfo);
2716 
2717 		status = data->AddFlat("messages", &propertyInfo);
2718 		if (status == B_OK)
2719 			status = BLooper::GetSupportedSuites(data);
2720 	}
2721 
2722 	return status;
2723 }
2724 
2725 
2726 BHandler*
2727 BWindow::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier,
2728 	int32 what,	const char* property)
2729 {
2730 	if (msg->what == B_WINDOW_MOVE_BY
2731 		|| msg->what == B_WINDOW_MOVE_TO)
2732 		return this;
2733 
2734 	BPropertyInfo propertyInfo(sWindowPropInfo);
2735 	if (propertyInfo.FindMatch(msg, index, specifier, what, property) >= 0) {
2736 		if (!strcmp(property, "View")) {
2737 			// we will NOT pop the current specifier
2738 			return fTopView;
2739 		} else if (!strcmp(property, "MenuBar")) {
2740 			if (fKeyMenuBar) {
2741 				msg->PopSpecifier();
2742 				return fKeyMenuBar;
2743 			} else {
2744 				BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
2745 				replyMsg.AddInt32("error", B_NAME_NOT_FOUND);
2746 				replyMsg.AddString("message",
2747 					"This window doesn't have a main MenuBar");
2748 				msg->SendReply(&replyMsg);
2749 				return NULL;
2750 			}
2751 		} else
2752 			return this;
2753 	}
2754 
2755 	return BLooper::ResolveSpecifier(msg, index, specifier, what, property);
2756 }
2757 
2758 
2759 //	#pragma mark - Private Methods
2760 
2761 
2762 void
2763 BWindow::_InitData(BRect frame, const char* title, window_look look,
2764 	window_feel feel, uint32 flags,	uint32 workspace, int32 bitmapToken)
2765 {
2766 	STRACE(("BWindow::InitData()\n"));
2767 
2768 	if (be_app == NULL) {
2769 		debugger("You need a valid BApplication object before interacting with "
2770 			"the app_server");
2771 		return;
2772 	}
2773 
2774 	frame.left = roundf(frame.left);
2775 	frame.top = roundf(frame.top);
2776 	frame.right = roundf(frame.right);
2777 	frame.bottom = roundf(frame.bottom);
2778 
2779 	fFrame = frame;
2780 
2781 	if (title == NULL)
2782 		title = "";
2783 
2784 	fTitle = strdup(title);
2785 
2786 	_SetName(title);
2787 
2788 	fFeel = feel;
2789 	fLook = look;
2790 	fFlags = flags | B_ASYNCHRONOUS_CONTROLS;
2791 
2792 	fInTransaction = bitmapToken >= 0;
2793 	fUpdateRequested = false;
2794 	fActive = false;
2795 	fShowLevel = 0;
2796 
2797 	fTopView = NULL;
2798 	fFocus = NULL;
2799 	fLastMouseMovedView	= NULL;
2800 	fIdleMouseRunner = NULL;
2801 	fKeyMenuBar = NULL;
2802 	fDefaultButton = NULL;
2803 
2804 	// Shortcut 'Q' is handled in _HandleKeyDown() directly, as its message
2805 	// get sent to the application, and not one of our handlers.
2806 	// It is only installed for non-modal windows, though.
2807 	fNoQuitShortcut = IsModal();
2808 
2809 	if ((fFlags & B_NOT_CLOSABLE) == 0 && !IsModal()) {
2810 		// Modal windows default to non-closable, but you can add the shortcut manually,
2811 		// if a different behaviour is wanted
2812 		AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED));
2813 	}
2814 
2815 	AddShortcut('X', B_COMMAND_KEY, new BMessage(B_CUT), NULL);
2816 	AddShortcut('C', B_COMMAND_KEY, new BMessage(B_COPY), NULL);
2817 	AddShortcut('V', B_COMMAND_KEY, new BMessage(B_PASTE), NULL);
2818 	AddShortcut('A', B_COMMAND_KEY, new BMessage(B_SELECT_ALL), NULL);
2819 
2820 	// Window modifier keys
2821 	AddShortcut('M', B_COMMAND_KEY | B_CONTROL_KEY,
2822 		new BMessage(_MINIMIZE_), NULL);
2823 	AddShortcut('Z', B_COMMAND_KEY | B_CONTROL_KEY,
2824 		new BMessage(_ZOOM_), NULL);
2825 	AddShortcut('H', B_COMMAND_KEY | B_CONTROL_KEY,
2826 		new BMessage(B_HIDE_APPLICATION), NULL);
2827 	AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY,
2828 		new BMessage(_SEND_TO_FRONT_), NULL);
2829 	AddShortcut('B', B_COMMAND_KEY | B_CONTROL_KEY,
2830 		new BMessage(_SEND_BEHIND_), NULL);
2831 
2832 	// Workspace modifier keys
2833 	BMessage* message;
2834 	message = new BMessage(_SWITCH_WORKSPACE_);
2835 	message->AddInt32("delta_x", -1);
2836 	AddShortcut(B_LEFT_ARROW, B_COMMAND_KEY | B_CONTROL_KEY, message, NULL);
2837 
2838 	message = new BMessage(_SWITCH_WORKSPACE_);
2839 	message->AddInt32("delta_x", 1);
2840 	AddShortcut(B_RIGHT_ARROW, B_COMMAND_KEY | B_CONTROL_KEY, message, NULL);
2841 
2842 	message = new BMessage(_SWITCH_WORKSPACE_);
2843 	message->AddInt32("delta_y", -1);
2844 	AddShortcut(B_UP_ARROW, B_COMMAND_KEY | B_CONTROL_KEY, message, NULL);
2845 
2846 	message = new BMessage(_SWITCH_WORKSPACE_);
2847 	message->AddInt32("delta_y", 1);
2848 	AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_CONTROL_KEY, message, NULL);
2849 
2850 	message = new BMessage(_SWITCH_WORKSPACE_);
2851 	message->AddBool("take_me_there", true);
2852 	message->AddInt32("delta_x", -1);
2853 	AddShortcut(B_LEFT_ARROW, B_COMMAND_KEY | B_CONTROL_KEY | B_SHIFT_KEY, message, NULL);
2854 
2855 	message = new BMessage(_SWITCH_WORKSPACE_);
2856 	message->AddBool("take_me_there", true);
2857 	message->AddInt32("delta_x", 1);
2858 	AddShortcut(B_RIGHT_ARROW, B_COMMAND_KEY | B_CONTROL_KEY | B_SHIFT_KEY, message, NULL);
2859 
2860 	message = new BMessage(_SWITCH_WORKSPACE_);
2861 	message->AddBool("take_me_there", true);
2862 	message->AddInt32("delta_y", -1);
2863 	AddShortcut(B_UP_ARROW, B_COMMAND_KEY | B_CONTROL_KEY | B_SHIFT_KEY, message, NULL);
2864 
2865 	message = new BMessage(_SWITCH_WORKSPACE_);
2866 	message->AddBool("take_me_there", true);
2867 	message->AddInt32("delta_y", 1);
2868 	AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_CONTROL_KEY | B_SHIFT_KEY, message, NULL);
2869 
2870 	// We set the default pulse rate, but we don't start the pulse
2871 	fPulseRate = 500000;
2872 	fPulseRunner = NULL;
2873 
2874 	fIsFilePanel = false;
2875 
2876 	fMenuSem = -1;
2877 
2878 	fMinimized = false;
2879 
2880 	fMaxZoomHeight = 32768.0;
2881 	fMaxZoomWidth = 32768.0;
2882 	fMinHeight = 0.0;
2883 	fMinWidth = 0.0;
2884 	fMaxHeight = 32768.0;
2885 	fMaxWidth = 32768.0;
2886 
2887 	fLastViewToken = B_NULL_TOKEN;
2888 
2889 	// TODO: other initializations!
2890 	fOffscreen = false;
2891 
2892 	// Create the server-side window
2893 
2894 	port_id receivePort = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY, "w<app_server");
2895 	if (receivePort < B_OK) {
2896 		// TODO: huh?
2897 		debugger("Could not create BWindow's receive port, used for interacting with the app_server!");
2898 		delete this;
2899 		return;
2900 	}
2901 
2902 	STRACE(("BWindow::InitData(): contacting app_server...\n"));
2903 
2904 	// let app_server know that a window has been created.
2905 	fLink = new(std::nothrow) BPrivate::PortLink(
2906 		BApplication::Private::ServerLink()->SenderPort(), receivePort);
2907 	if (fLink == NULL) {
2908 		// Zombie!
2909 		return;
2910 	}
2911 
2912 	{
2913 		BPrivate::AppServerLink lockLink;
2914 			// we're talking to the server application using our own
2915 			// communication channel (fLink) - we better make sure no one
2916 			// interferes by locking that channel (which AppServerLink does
2917 			// implicetly)
2918 
2919 		if (bitmapToken < 0) {
2920 			fLink->StartMessage(AS_CREATE_WINDOW);
2921 		} else {
2922 			fLink->StartMessage(AS_CREATE_OFFSCREEN_WINDOW);
2923 			fLink->Attach<int32>(bitmapToken);
2924 			fOffscreen = true;
2925 		}
2926 
2927 		fLink->Attach<BRect>(fFrame);
2928 		fLink->Attach<uint32>((uint32)fLook);
2929 		fLink->Attach<uint32>((uint32)fFeel);
2930 		fLink->Attach<uint32>(fFlags);
2931 		fLink->Attach<uint32>(workspace);
2932 		fLink->Attach<int32>(_get_object_token_(this));
2933 		fLink->Attach<port_id>(receivePort);
2934 		fLink->Attach<port_id>(fMsgPort);
2935 		fLink->AttachString(title);
2936 
2937 		port_id sendPort;
2938 		int32 code;
2939 		if (fLink->FlushWithReply(code) == B_OK
2940 			&& code == B_OK
2941 			&& fLink->Read<port_id>(&sendPort) == B_OK) {
2942 			// read the frame size and its limits that were really
2943 			// enforced on the server side
2944 
2945 			fLink->Read<BRect>(&fFrame);
2946 			fLink->Read<float>(&fMinWidth);
2947 			fLink->Read<float>(&fMaxWidth);
2948 			fLink->Read<float>(&fMinHeight);
2949 			fLink->Read<float>(&fMaxHeight);
2950 
2951 			fMaxZoomWidth = fMaxWidth;
2952 			fMaxZoomHeight = fMaxHeight;
2953 		} else
2954 			sendPort = -1;
2955 
2956 		// Redirect our link to the new window connection
2957 		fLink->SetSenderPort(sendPort);
2958 	}
2959 
2960 	STRACE(("Server says that our send port is %ld\n", sendPort));
2961 	STRACE(("Window locked?: %s\n", IsLocked() ? "True" : "False"));
2962 
2963 	_CreateTopView();
2964 }
2965 
2966 
2967 //! Rename the handler and its thread
2968 void
2969 BWindow::_SetName(const char* title)
2970 {
2971 	if (title == NULL)
2972 		title = "";
2973 
2974 	// we will change BWindow's thread name to "w>window title"
2975 
2976 	char threadName[B_OS_NAME_LENGTH];
2977 	strcpy(threadName, "w>");
2978 #ifdef __HAIKU__
2979 	strlcat(threadName, title, B_OS_NAME_LENGTH);
2980 #else
2981 	int32 length = strlen(title);
2982 	length = min_c(length, B_OS_NAME_LENGTH - 3);
2983 	memcpy(threadName + 2, title, length);
2984 	threadName[length + 2] = '\0';
2985 #endif
2986 
2987 	// change the handler's name
2988 	SetName(threadName);
2989 
2990 	// if the message loop has been started...
2991 	if (Thread() >= B_OK)
2992 		rename_thread(Thread(), threadName);
2993 }
2994 
2995 
2996 //!	Reads all pending messages from the window port and put them into the queue.
2997 void
2998 BWindow::_DequeueAll()
2999 {
3000 	//	Get message count from port
3001 	int32 count = port_count(fMsgPort);
3002 
3003 	for (int32 i = 0; i < count; i++) {
3004 		BMessage* message = MessageFromPort(0);
3005 		if (message != NULL)
3006 			fDirectTarget->Queue()->AddMessage(message);
3007 	}
3008 }
3009 
3010 
3011 /*!	This here is an almost complete code duplication to BLooper::task_looper()
3012 	but with some important differences:
3013 	 a)	it uses the _DetermineTarget() method to tell what the later target of
3014 		a message will be, if no explicit target is supplied.
3015 	 b)	it calls _UnpackMessage() and _SanitizeMessage() to duplicate the message
3016 		to all of its intended targets, and to add all fields the target would
3017 		expect in such a message.
3018 
3019 	This is important because the app_server sends all input events to the
3020 	preferred handler, and expects them to be correctly distributed to their
3021 	intended targets.
3022 */
3023 void
3024 BWindow::task_looper()
3025 {
3026 	STRACE(("info: BWindow::task_looper() started.\n"));
3027 
3028 	// Check that looper is locked (should be)
3029 	AssertLocked();
3030 	Unlock();
3031 
3032 	if (IsLocked())
3033 		debugger("window must not be locked!");
3034 
3035 	while (!fTerminating) {
3036 		// Did we get a message?
3037 		BMessage* msg = MessageFromPort();
3038 		if (msg)
3039 			_AddMessagePriv(msg);
3040 
3041 		//	Get message count from port
3042 		int32 msgCount = port_count(fMsgPort);
3043 		for (int32 i = 0; i < msgCount; ++i) {
3044 			// Read 'count' messages from port (so we will not block)
3045 			// We use zero as our timeout since we know there is stuff there
3046 			msg = MessageFromPort(0);
3047 			// Add messages to queue
3048 			if (msg)
3049 				_AddMessagePriv(msg);
3050 		}
3051 
3052 		bool dispatchNextMessage = true;
3053 		while (!fTerminating && dispatchNextMessage) {
3054 			// Get next message from queue (assign to fLastMessage)
3055 			fLastMessage = fDirectTarget->Queue()->NextMessage();
3056 
3057 			// Lock the looper
3058 			if (!Lock())
3059 				break;
3060 
3061 			if (!fLastMessage) {
3062 				// No more messages: Unlock the looper and terminate the
3063 				// dispatch loop.
3064 				dispatchNextMessage = false;
3065 			} else {
3066 				// Get the target handler
3067 				BMessage::Private messagePrivate(fLastMessage);
3068 				bool usePreferred = messagePrivate.UsePreferredTarget();
3069 				BHandler* handler = NULL;
3070 				bool dropMessage = false;
3071 
3072 				if (usePreferred) {
3073 					handler = PreferredHandler();
3074 					if (handler == NULL)
3075 						handler = this;
3076 				} else {
3077 					gDefaultTokens.GetToken(messagePrivate.GetTarget(),
3078 						B_HANDLER_TOKEN, (void**)&handler);
3079 
3080 					// if this handler doesn't belong to us, we drop the message
3081 					if (handler != NULL && handler->Looper() != this) {
3082 						dropMessage = true;
3083 						handler = NULL;
3084 					}
3085 				}
3086 
3087 				if ((handler == NULL && !dropMessage) || usePreferred)
3088 					handler = _DetermineTarget(fLastMessage, handler);
3089 
3090 				unpack_cookie cookie;
3091 				while (_UnpackMessage(cookie, &fLastMessage, &handler, &usePreferred)) {
3092 					// if there is no target handler, the message is dropped
3093 					if (handler != NULL) {
3094 						_SanitizeMessage(fLastMessage, handler, usePreferred);
3095 
3096 						// Is this a scripting message?
3097 						if (fLastMessage->HasSpecifiers()) {
3098 							int32 index = 0;
3099 							// Make sure the current specifier is kosher
3100 							if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
3101 								handler = resolve_specifier(handler, fLastMessage);
3102 						}
3103 
3104 						if (handler != NULL)
3105 							handler = _TopLevelFilter(fLastMessage, handler);
3106 
3107 						if (handler != NULL)
3108 							DispatchMessage(fLastMessage, handler);
3109 					}
3110 
3111 					// Delete the current message
3112 					delete fLastMessage;
3113 					fLastMessage = NULL;
3114 				}
3115 			}
3116 
3117 			if (fTerminating) {
3118 				// we leave the looper locked when we quit
3119 				return;
3120 			}
3121 
3122 			Unlock();
3123 
3124 			// Are any messages on the port?
3125 			if (port_count(fMsgPort) > 0) {
3126 				// Do outer loop
3127 				dispatchNextMessage = false;
3128 			}
3129 		}
3130 	}
3131 }
3132 
3133 
3134 window_type
3135 BWindow::_ComposeType(window_look look, window_feel feel) const
3136 {
3137 	switch (feel) {
3138 		case B_NORMAL_WINDOW_FEEL:
3139 			switch (look) {
3140 				case B_TITLED_WINDOW_LOOK:
3141 					return B_TITLED_WINDOW;
3142 
3143 				case B_DOCUMENT_WINDOW_LOOK:
3144 					return B_DOCUMENT_WINDOW;
3145 
3146 				case B_BORDERED_WINDOW_LOOK:
3147 					return B_BORDERED_WINDOW;
3148 
3149 				default:
3150 					return B_UNTYPED_WINDOW;
3151 			}
3152 			break;
3153 
3154 		case B_MODAL_APP_WINDOW_FEEL:
3155 			if (look == B_MODAL_WINDOW_LOOK)
3156 				return B_MODAL_WINDOW;
3157 			break;
3158 
3159 		case B_FLOATING_APP_WINDOW_FEEL:
3160 			if (look == B_FLOATING_WINDOW_LOOK)
3161 				return B_FLOATING_WINDOW;
3162 			break;
3163 
3164 		default:
3165 			return B_UNTYPED_WINDOW;
3166 	}
3167 
3168 	return B_UNTYPED_WINDOW;
3169 }
3170 
3171 
3172 void
3173 BWindow::_DecomposeType(window_type type, window_look* _look,
3174 	window_feel* _feel) const
3175 {
3176 	switch (type) {
3177 		case B_DOCUMENT_WINDOW:
3178 			*_look = B_DOCUMENT_WINDOW_LOOK;
3179 			*_feel = B_NORMAL_WINDOW_FEEL;
3180 			break;
3181 
3182 		case B_MODAL_WINDOW:
3183 			*_look = B_MODAL_WINDOW_LOOK;
3184 			*_feel = B_MODAL_APP_WINDOW_FEEL;
3185 			break;
3186 
3187 		case B_FLOATING_WINDOW:
3188 			*_look = B_FLOATING_WINDOW_LOOK;
3189 			*_feel = B_FLOATING_APP_WINDOW_FEEL;
3190 			break;
3191 
3192 		case B_BORDERED_WINDOW:
3193 			*_look = B_BORDERED_WINDOW_LOOK;
3194 			*_feel = B_NORMAL_WINDOW_FEEL;
3195 			break;
3196 
3197 		case B_TITLED_WINDOW:
3198 		case B_UNTYPED_WINDOW:
3199 		default:
3200 			*_look = B_TITLED_WINDOW_LOOK;
3201 			*_feel = B_NORMAL_WINDOW_FEEL;
3202 			break;
3203 	}
3204 }
3205 
3206 
3207 void
3208 BWindow::_CreateTopView()
3209 {
3210 	STRACE(("_CreateTopView(): enter\n"));
3211 
3212 	BRect frame = fFrame.OffsetToCopy(B_ORIGIN);
3213 	// TODO: what to do here about std::nothrow?
3214 	fTopView = new BView(frame, "fTopView",
3215 		B_FOLLOW_ALL, B_WILL_DRAW);
3216 	fTopView->fTopLevelView = true;
3217 
3218 	//inhibit check_lock()
3219 	fLastViewToken = _get_object_token_(fTopView);
3220 
3221 	// set fTopView's owner, add it to window's eligible handler list
3222 	// and also set its next handler to be this window.
3223 
3224 	STRACE(("Calling setowner fTopView = %p this = %p.\n",
3225 		fTopView, this));
3226 
3227 	fTopView->_SetOwner(this);
3228 
3229 	// we can't use AddChild() because this is the top view
3230 	fTopView->_CreateSelf();
3231 
3232 	STRACE(("BuildTopView ended\n"));
3233 }
3234 
3235 
3236 /*!
3237 	Resizes the top view to match the window size. This will also
3238 	adapt the size of all its child views as needed.
3239 	This method has to be called whenever the frame of the window
3240 	changes.
3241 */
3242 void
3243 BWindow::_AdoptResize()
3244 {
3245 	// Resize views according to their resize modes - this
3246 	// saves us some server communication, as the server
3247 	// does the same with our views on its side.
3248 
3249 	int32 deltaWidth = (int32)(fFrame.Width() - fTopView->Bounds().Width());
3250 	int32 deltaHeight = (int32)(fFrame.Height() - fTopView->Bounds().Height());
3251 	if (deltaWidth == 0 && deltaHeight == 0)
3252 		return;
3253 
3254 	fTopView->_ResizeBy(deltaWidth, deltaHeight);
3255 }
3256 
3257 
3258 void
3259 BWindow::_SetFocus(BView* focusView, bool notifyInputServer)
3260 {
3261 	if (fFocus == focusView)
3262 		return;
3263 
3264 	// we notify the input server if we are passing focus
3265 	// from a view which has the B_INPUT_METHOD_AWARE to a one
3266 	// which does not, or vice-versa
3267 	if (notifyInputServer && fActive) {
3268 		bool inputMethodAware = false;
3269 		if (focusView)
3270 			inputMethodAware = focusView->Flags() & B_INPUT_METHOD_AWARE;
3271 		BMessage msg(inputMethodAware ? IS_FOCUS_IM_AWARE_VIEW : IS_UNFOCUS_IM_AWARE_VIEW);
3272 		BMessenger messenger(focusView);
3273 		BMessage reply;
3274 		if (focusView)
3275 			msg.AddMessenger("view", messenger);
3276 		_control_input_server_(&msg, &reply);
3277 	}
3278 
3279 	fFocus = focusView;
3280 	SetPreferredHandler(focusView);
3281 }
3282 
3283 
3284 /*!
3285 	\brief Determines the target of a message received for the
3286 		focus view.
3287 */
3288 BHandler*
3289 BWindow::_DetermineTarget(BMessage* message, BHandler* target)
3290 {
3291 	if (target == NULL)
3292 		target = this;
3293 
3294 	switch (message->what) {
3295 		case B_KEY_DOWN:
3296 		case B_KEY_UP:
3297 		{
3298 			// if we have a default button, it might want to hear
3299 			// about pressing the <enter> key
3300 			int32 rawChar;
3301 			if (DefaultButton() != NULL
3302 				&& message->FindInt32("raw_char", &rawChar) == B_OK
3303 				&& rawChar == B_ENTER)
3304 				return DefaultButton();
3305 
3306 			// supposed to fall through
3307 		}
3308 		case B_UNMAPPED_KEY_DOWN:
3309 		case B_UNMAPPED_KEY_UP:
3310 		case B_MODIFIERS_CHANGED:
3311 			// these messages should be dispatched by the focus view
3312 			if (CurrentFocus() != NULL)
3313 				return CurrentFocus();
3314 			break;
3315 
3316 		case B_MOUSE_DOWN:
3317 		case B_MOUSE_UP:
3318 		case B_MOUSE_MOVED:
3319 		case B_MOUSE_WHEEL_CHANGED:
3320 		case B_MOUSE_IDLE:
3321 			// is there a token of the view that is currently under the mouse?
3322 			int32 token;
3323 			if (message->FindInt32("_view_token", &token) == B_OK) {
3324 				BView* view = _FindView(token);
3325 				if (view != NULL)
3326 					return view;
3327 			}
3328 
3329 			// if there is no valid token in the message, we try our
3330 			// luck with the last target, if available
3331 			if (fLastMouseMovedView != NULL)
3332 				return fLastMouseMovedView;
3333 			break;
3334 
3335 		case B_PULSE:
3336 		case B_QUIT_REQUESTED:
3337 			// TODO: test whether R5 will let BView dispatch these messages
3338 			return this;
3339 
3340 		case _MESSAGE_DROPPED_:
3341 			if (fLastMouseMovedView != NULL)
3342 				return fLastMouseMovedView;
3343 			break;
3344 
3345 		default:
3346 			break;
3347 	}
3348 
3349 	return target;
3350 }
3351 
3352 
3353 /*!	\brief Determines whether or not this message has targeted the focus view.
3354 
3355 	This will return \c false only if the message did not go to the preferred
3356 	handler, or if the packed message does not contain address the focus view
3357 	at all.
3358 */
3359 bool
3360 BWindow::_IsFocusMessage(BMessage* message)
3361 {
3362 	BMessage::Private messagePrivate(message);
3363 	if (!messagePrivate.UsePreferredTarget())
3364 		return false;
3365 
3366 	bool feedFocus;
3367 	if (message->HasInt32("_token")
3368 		&& (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus))
3369 		return false;
3370 
3371 	return true;
3372 }
3373 
3374 
3375 /*!	\brief Distributes the message to its intended targets. This is done for
3376 		all messages that should go to the preferred handler.
3377 
3378 	Returns \c true in case the message should still be dispatched
3379 */
3380 bool
3381 BWindow::_UnpackMessage(unpack_cookie& cookie, BMessage** _message,
3382 	BHandler** _target, bool* _usePreferred)
3383 {
3384 	if (cookie.message == NULL)
3385 		return false;
3386 
3387 	if (cookie.index == 0 && !cookie.tokens_scanned) {
3388 		// We were called the first time for this message
3389 
3390 		if (!*_usePreferred) {
3391 			// only consider messages targeted at the preferred handler
3392 			cookie.message = NULL;
3393 			return true;
3394 		}
3395 
3396 		// initialize our cookie
3397 		cookie.message = *_message;
3398 		cookie.focus = *_target;
3399 
3400 		if (cookie.focus != NULL)
3401 			cookie.focus_token = _get_object_token_(*_target);
3402 
3403 		if (fLastMouseMovedView != NULL && cookie.message->what == B_MOUSE_MOVED)
3404 			cookie.last_view_token = _get_object_token_(fLastMouseMovedView);
3405 
3406 		*_usePreferred = false;
3407 	}
3408 
3409 	_DequeueAll();
3410 
3411 	// distribute the message to all targets specified in the
3412 	// message directly (but not to the focus view)
3413 
3414 	for (int32 token; !cookie.tokens_scanned
3415 			&& cookie.message->FindInt32("_token", cookie.index, &token)
3416 				== B_OK;
3417 			cookie.index++) {
3418 		// focus view is preferred and should get its message directly
3419 		if (token == cookie.focus_token) {
3420 			cookie.found_focus = true;
3421 			continue;
3422 		}
3423 		if (token == cookie.last_view_token)
3424 			continue;
3425 
3426 		BView* target = _FindView(token);
3427 		if (target == NULL)
3428 			continue;
3429 
3430 		*_message = new BMessage(*cookie.message);
3431 		*_target = target;
3432 		cookie.index++;
3433 		return true;
3434 	}
3435 
3436 	cookie.tokens_scanned = true;
3437 
3438 	// if there is a last mouse moved view, and the new focus is
3439 	// different, the previous view wants to get its B_EXITED_VIEW
3440 	// message
3441 	if (cookie.last_view_token != B_NULL_TOKEN && fLastMouseMovedView != NULL
3442 		&& fLastMouseMovedView != cookie.focus) {
3443 		*_message = new BMessage(*cookie.message);
3444 		*_target = fLastMouseMovedView;
3445 		cookie.last_view_token = B_NULL_TOKEN;
3446 		return true;
3447 	}
3448 
3449 	bool dispatchToFocus = true;
3450 
3451 	// check if the focus token is still valid (could have been removed in the mean time)
3452 	BHandler* handler;
3453 	if (gDefaultTokens.GetToken(cookie.focus_token, B_HANDLER_TOKEN, (void**)&handler) != B_OK
3454 		|| handler->Looper() != this)
3455 		dispatchToFocus = false;
3456 
3457 	if (dispatchToFocus && cookie.index > 0) {
3458 		// should this message still be dispatched by the focus view?
3459 		bool feedFocus;
3460 		if (!cookie.found_focus
3461 			&& (cookie.message->FindBool("_feed_focus", &feedFocus) != B_OK
3462 				|| feedFocus == false))
3463 			dispatchToFocus = false;
3464 	}
3465 
3466 	if (!dispatchToFocus) {
3467 		delete cookie.message;
3468 		cookie.message = NULL;
3469 		return false;
3470 	}
3471 
3472 	*_message = cookie.message;
3473 	*_target = cookie.focus;
3474 	*_usePreferred = true;
3475 	cookie.message = NULL;
3476 	return true;
3477 }
3478 
3479 
3480 /*!	Some messages don't get to the window in a shape an application should see.
3481 	This method is supposed to give a message the last grinding before
3482 	it's acceptable for the receiving application.
3483 */
3484 void
3485 BWindow::_SanitizeMessage(BMessage* message, BHandler* target, bool usePreferred)
3486 {
3487 	if (target == NULL)
3488 		return;
3489 
3490 	switch (message->what) {
3491 		case B_MOUSE_MOVED:
3492 		case B_MOUSE_UP:
3493 		case B_MOUSE_DOWN:
3494 		{
3495 			BPoint where;
3496 			if (message->FindPoint("screen_where", &where) != B_OK)
3497 				break;
3498 
3499 			BView* view = dynamic_cast<BView*>(target);
3500 
3501 			if (!view || message->what == B_MOUSE_MOVED) {
3502 				// add local window coordinates, only
3503 				// for regular mouse moved messages
3504 				message->AddPoint("where", ConvertFromScreen(where));
3505 			}
3506 
3507 			if (view != NULL) {
3508 				// add local view coordinates
3509 				BPoint viewWhere = view->ConvertFromScreen(where);
3510 				if (message->what != B_MOUSE_MOVED) {
3511 					// Yep, the meaning of "where" is different
3512 					// for regular mouse moved messages versus
3513 					// mouse up/down!
3514 					message->AddPoint("where", viewWhere);
3515 				}
3516 				message->AddPoint("be:view_where", viewWhere);
3517 
3518 				if (message->what == B_MOUSE_MOVED) {
3519 					// is there a token of the view that is currently under
3520 					// the mouse?
3521 					BView* viewUnderMouse = NULL;
3522 					int32 token;
3523 					if (message->FindInt32("_view_token", &token) == B_OK)
3524 						viewUnderMouse = _FindView(token);
3525 
3526 					// add transit information
3527 					uint32 transit
3528 						= _TransitForMouseMoved(view, viewUnderMouse);
3529 					message->AddInt32("be:transit", transit);
3530 
3531 					if (usePreferred)
3532 						fLastMouseMovedView = viewUnderMouse;
3533 				}
3534 			}
3535 			break;
3536 		}
3537 
3538 		case _MESSAGE_DROPPED_:
3539 		{
3540 			uint32 originalWhat;
3541 			if (message->FindInt32("_original_what", (int32*)&originalWhat) == B_OK) {
3542 				message->what = originalWhat;
3543 				message->RemoveName("_original_what");
3544 			}
3545 			break;
3546 		}
3547 	}
3548 }
3549 
3550 
3551 /*!
3552 	This is called by BView::GetMouse() when a B_MOUSE_MOVED message
3553 	is removed from the queue.
3554 	It allows the window to update the last mouse moved view, and
3555 	let it decide if this message should be kept. It will also remove
3556 	the message from the queue.
3557 	You need to hold the message queue lock when calling this method!
3558 
3559 	\return true if this message can be used to get the mouse data from,
3560 	\return false if this is not meant for the public.
3561 */
3562 bool
3563 BWindow::_StealMouseMessage(BMessage* message, bool& deleteMessage)
3564 {
3565 	BMessage::Private messagePrivate(message);
3566 	if (!messagePrivate.UsePreferredTarget()) {
3567 		// this message is targeted at a specific handler, so we should
3568 		// not steal it
3569 		return false;
3570 	}
3571 
3572 	int32 token;
3573 	if (message->FindInt32("_token", 0, &token) == B_OK) {
3574 		// This message has other targets, so we can't remove it;
3575 		// just prevent it from being sent to the preferred handler
3576 		// again (if it should have gotten it at all).
3577 		bool feedFocus;
3578 		if (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus)
3579 			return false;
3580 
3581 		message->RemoveName("_feed_focus");
3582 		deleteMessage = false;
3583 	} else {
3584 		deleteMessage = true;
3585 
3586 		if (message->what == B_MOUSE_MOVED) {
3587 			// We need to update the last mouse moved view, as this message
3588 			// won't make it to _SanitizeMessage() anymore.
3589 			BView* viewUnderMouse = NULL;
3590 			int32 token;
3591 			if (message->FindInt32("_view_token", &token) == B_OK)
3592 				viewUnderMouse = _FindView(token);
3593 
3594 			// Don't remove important transit messages!
3595 			uint32 transit = _TransitForMouseMoved(fLastMouseMovedView,
3596 				viewUnderMouse);
3597 			if (transit == B_ENTERED_VIEW || transit == B_EXITED_VIEW)
3598 				deleteMessage = false;
3599 		}
3600 
3601 		if (deleteMessage) {
3602 			// The message is only thought for the preferred handler, so we
3603 			// can just remove it.
3604 			MessageQueue()->RemoveMessage(message);
3605 		}
3606 	}
3607 
3608 	return true;
3609 }
3610 
3611 
3612 uint32
3613 BWindow::_TransitForMouseMoved(BView* view, BView* viewUnderMouse) const
3614 {
3615 	uint32 transit;
3616 	if (viewUnderMouse == view) {
3617 		// the mouse is over the target view
3618 		if (fLastMouseMovedView != view)
3619 			transit = B_ENTERED_VIEW;
3620 		else
3621 			transit = B_INSIDE_VIEW;
3622 	} else {
3623 		// the mouse is not over the target view
3624 		if (view == fLastMouseMovedView)
3625 			transit = B_EXITED_VIEW;
3626 		else
3627 			transit = B_OUTSIDE_VIEW;
3628 	}
3629 	return transit;
3630 }
3631 
3632 
3633 /*!	Forwards the key to the switcher
3634 */
3635 void
3636 BWindow::_Switcher(int32 rawKey, uint32 modifiers, bool repeat)
3637 {
3638 	// only send the first key press, no repeats
3639 	if (repeat)
3640 		return;
3641 
3642 	BMessenger deskbar(kDeskbarSignature);
3643 	if (!deskbar.IsValid()) {
3644 		// TODO: have some kind of fallback-handling in case the Deskbar is
3645 		// not available?
3646 		return;
3647 	}
3648 
3649 	BMessage message('TASK');
3650 	message.AddInt32("key", rawKey);
3651 	message.AddInt32("modifiers", modifiers);
3652 	message.AddInt64("when", system_time());
3653 	message.AddInt32("team", Team());
3654 	deskbar.SendMessage(&message);
3655 }
3656 
3657 
3658 /*!	Handles keyboard input before it gets forwarded to the target handler.
3659 	This includes shortcut evaluation, keyboard navigation, etc.
3660 
3661 	\return handled if true, the event was already handled, and will not
3662 		be forwarded to the target handler.
3663 
3664 	TODO: must also convert the incoming key to the font encoding of the target
3665 */
3666 bool
3667 BWindow::_HandleKeyDown(BMessage* event)
3668 {
3669 	// Only handle special functions when the event targeted the active focus
3670 	// view
3671 	if (!_IsFocusMessage(event))
3672 		return false;
3673 
3674 	const char* string = NULL;
3675 	if (event->FindString("bytes", &string) != B_OK)
3676 		return false;
3677 
3678 	char key = string[0];
3679 
3680 	uint32 modifiers;
3681 	if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK)
3682 		modifiers = 0;
3683 
3684 	// handle BMenuBar key
3685 	if (key == B_ESCAPE && (modifiers & B_COMMAND_KEY) != 0 && fKeyMenuBar) {
3686 		fKeyMenuBar->StartMenuBar(0, true, false, NULL);
3687 		return true;
3688 	}
3689 
3690 	// Keyboard navigation through views
3691 	// (B_OPTION_KEY makes BTextViews and friends navigable, even in editing
3692 	// mode)
3693 	if (key == B_TAB && (modifiers & B_OPTION_KEY) != 0) {
3694 		_KeyboardNavigation();
3695 		return true;
3696 	}
3697 
3698 	int32 rawKey;
3699 	event->FindInt32("key", &rawKey);
3700 
3701 	// Deskbar's Switcher
3702 	if ((key == B_TAB || rawKey == 0x11) && (modifiers & B_CONTROL_KEY) != 0) {
3703 		_Switcher(rawKey, modifiers, event->HasInt32("be:key_repeat"));
3704 		return true;
3705 	}
3706 
3707 	// Optionally close window when the escape key is pressed
3708 	if (key == B_ESCAPE && (Flags() & B_CLOSE_ON_ESCAPE) != 0) {
3709 		BMessage message(B_QUIT_REQUESTED);
3710 		message.AddBool("shortcut", true);
3711 
3712 		PostMessage(&message);
3713 		return true;
3714 	}
3715 
3716 	// PrtScr key takes a screenshot
3717 	if (key == B_FUNCTION_KEY && rawKey == B_PRINT_KEY) {
3718 		// With no modifier keys the best way to get a screenshot is by
3719 		// calling the screenshot CLI
3720 		if (modifiers == 0) {
3721 			be_roster->Launch("application/x-vnd.haiku-screenshot-cli");
3722 			return true;
3723 		}
3724 
3725 		// Prepare a message based on the modifier keys pressed and launch the
3726 		// screenshot GUI
3727 		BMessage message(B_ARGV_RECEIVED);
3728 		int32 argc = 1;
3729 		message.AddString("argv", "Screenshot");
3730 		if ((modifiers & B_CONTROL_KEY) != 0) {
3731 			argc++;
3732 			message.AddString("argv", "--clipboard");
3733 		}
3734 		if ((modifiers & B_SHIFT_KEY) != 0) {
3735 			argc++;
3736 			message.AddString("argv", "--silent");
3737 		}
3738 		message.AddInt32("argc", argc);
3739 		be_roster->Launch("application/x-vnd.haiku-screenshot", &message);
3740 		return true;
3741 	}
3742 
3743 	// Handle shortcuts
3744 	if ((modifiers & B_COMMAND_KEY) != 0) {
3745 		// Command+q has been pressed, so, we will quit
3746 		// the shortcut mechanism doesn't allow handlers outside the window
3747 		if (!fNoQuitShortcut && (key == 'Q' || key == 'q')) {
3748 			BMessage message(B_QUIT_REQUESTED);
3749 			message.AddBool("shortcut", true);
3750 
3751 			be_app->PostMessage(&message);
3752 			return true;
3753 		}
3754 
3755 		// Pretend that the user opened a menu, to give the subclass a
3756 		// chance to update it's menus. This may install new shortcuts,
3757 		// which is why we have to call it here, before trying to find
3758 		// a shortcut for the given key.
3759 		MenusBeginning();
3760 
3761 		Shortcut* shortcut = _FindShortcut(key, modifiers);
3762 		if (shortcut != NULL) {
3763 			// TODO: would be nice to move this functionality to
3764 			//	a Shortcut::Invoke() method - but since BMenu::InvokeItem()
3765 			//	(and BMenuItem::Invoke()) are private, I didn't want
3766 			//	to mess with them (BMenuItem::Invoke() is public in
3767 			//	Dano/Zeta, though, maybe we should just follow their
3768 			//	example)
3769 			if (shortcut->MenuItem() != NULL) {
3770 				BMenu* menu = shortcut->MenuItem()->Menu();
3771 				if (menu != NULL)
3772 					MenuPrivate(menu).InvokeItem(shortcut->MenuItem(), true);
3773 			} else {
3774 				BHandler* target = shortcut->Target();
3775 				if (target == NULL)
3776 					target = CurrentFocus();
3777 
3778 				if (shortcut->Message() != NULL) {
3779 					BMessage message(*shortcut->Message());
3780 
3781 					if (message.ReplaceInt64("when", system_time()) != B_OK)
3782 						message.AddInt64("when", system_time());
3783 					if (message.ReplaceBool("shortcut", true) != B_OK)
3784 						message.AddBool("shortcut", true);
3785 
3786 					PostMessage(&message, target);
3787 				}
3788 			}
3789 		}
3790 
3791 		MenusEnded();
3792 
3793 		// we always eat the event if the command key was pressed
3794 		return true;
3795 	}
3796 
3797 	// TODO: convert keys to the encoding of the target view
3798 
3799 	return false;
3800 }
3801 
3802 
3803 bool
3804 BWindow::_HandleUnmappedKeyDown(BMessage* event)
3805 {
3806 	// Only handle special functions when the event targeted the active focus
3807 	// view
3808 	if (!_IsFocusMessage(event))
3809 		return false;
3810 
3811 	uint32 modifiers;
3812 	int32 rawKey;
3813 	if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK
3814 		|| event->FindInt32("key", &rawKey))
3815 		return false;
3816 
3817 	// Deskbar's Switcher
3818 	if (rawKey == 0x11 && (modifiers & B_CONTROL_KEY) != 0) {
3819 		_Switcher(rawKey, modifiers, event->HasInt32("be:key_repeat"));
3820 		return true;
3821 	}
3822 
3823 	return false;
3824 }
3825 
3826 
3827 void
3828 BWindow::_KeyboardNavigation()
3829 {
3830 	BMessage* message = CurrentMessage();
3831 	if (message == NULL)
3832 		return;
3833 
3834 	const char* bytes;
3835 	uint32 modifiers;
3836 	if (message->FindString("bytes", &bytes) != B_OK
3837 		|| bytes[0] != B_TAB)
3838 		return;
3839 
3840 	message->FindInt32("modifiers", (int32*)&modifiers);
3841 
3842 	BView* nextFocus;
3843 	int32 jumpGroups = (modifiers & B_OPTION_KEY) != 0
3844 		? B_NAVIGABLE_JUMP : B_NAVIGABLE;
3845 	if (modifiers & B_SHIFT_KEY)
3846 		nextFocus = _FindPreviousNavigable(fFocus, jumpGroups);
3847 	else
3848 		nextFocus = _FindNextNavigable(fFocus, jumpGroups);
3849 
3850 	if (nextFocus && nextFocus != fFocus) {
3851 		nextFocus->MakeFocus(true);
3852 	}
3853 }
3854 
3855 
3856 BMessage*
3857 BWindow::ConvertToMessage(void* raw, int32 code)
3858 {
3859 	return BLooper::ConvertToMessage(raw, code);
3860 }
3861 
3862 
3863 BWindow::Shortcut*
3864 BWindow::_FindShortcut(uint32 key, uint32 modifiers)
3865 {
3866 	int32 count = fShortcuts.CountItems();
3867 
3868 	key = Shortcut::PrepareKey(key);
3869 	modifiers = Shortcut::PrepareModifiers(modifiers);
3870 
3871 	for (int32 index = 0; index < count; index++) {
3872 		Shortcut* shortcut = (Shortcut*)fShortcuts.ItemAt(index);
3873 
3874 		if (shortcut->Matches(key, modifiers))
3875 			return shortcut;
3876 	}
3877 
3878 	return NULL;
3879 }
3880 
3881 
3882 BView*
3883 BWindow::_FindView(int32 token)
3884 {
3885 	BHandler* handler;
3886 	if (gDefaultTokens.GetToken(token, B_HANDLER_TOKEN,
3887 			(void**)&handler) != B_OK) {
3888 		return NULL;
3889 	}
3890 
3891 	// the view must belong to us in order to be found by this method
3892 	BView* view = dynamic_cast<BView*>(handler);
3893 	if (view != NULL && view->Window() == this)
3894 		return view;
3895 
3896 	return NULL;
3897 }
3898 
3899 
3900 BView*
3901 BWindow::_FindView(BView* view, BPoint point) const
3902 {
3903 	// point is assumed to be already in view's coordinates
3904 	if (!view->IsHidden() && view->Bounds().Contains(point)) {
3905 		if (!view->fFirstChild)
3906 			return view;
3907 		else {
3908 			BView* child = view->fFirstChild;
3909 			while (child != NULL) {
3910 				BPoint childPoint = point - child->Frame().LeftTop();
3911 				BView* subView  = _FindView(child, childPoint);
3912 				if (subView != NULL)
3913 					return subView;
3914 
3915 				child = child->fNextSibling;
3916 			}
3917 		}
3918 		return view;
3919 	}
3920 	return NULL;
3921 }
3922 
3923 
3924 BView*
3925 BWindow::_FindNextNavigable(BView* focus, uint32 flags)
3926 {
3927 	if (focus == NULL)
3928 		focus = fTopView;
3929 
3930 	BView* nextFocus = focus;
3931 
3932 	// Search the tree for views that accept focus (depth search)
3933 	while (true) {
3934 		if (nextFocus->fFirstChild)
3935 			nextFocus = nextFocus->fFirstChild;
3936 		else if (nextFocus->fNextSibling)
3937 			nextFocus = nextFocus->fNextSibling;
3938 		else {
3939 			// go to the nearest parent with a next sibling
3940 			while (!nextFocus->fNextSibling && nextFocus->fParent) {
3941 				nextFocus = nextFocus->fParent;
3942 			}
3943 
3944 			if (nextFocus == fTopView) {
3945 				// if we started with the top view, we traversed the whole tree already
3946 				if (nextFocus == focus)
3947 					return NULL;
3948 
3949 				nextFocus = nextFocus->fFirstChild;
3950 			} else
3951 				nextFocus = nextFocus->fNextSibling;
3952 		}
3953 
3954 		if (nextFocus == focus || nextFocus == NULL) {
3955 			// When we get here it means that the hole tree has been
3956 			// searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
3957 			return NULL;
3958 		}
3959 
3960 		if (!nextFocus->IsHidden() && (nextFocus->Flags() & flags) != 0)
3961 			return nextFocus;
3962 	}
3963 }
3964 
3965 
3966 BView*
3967 BWindow::_FindPreviousNavigable(BView* focus, uint32 flags)
3968 {
3969 	if (focus == NULL)
3970 		focus = fTopView;
3971 
3972 	BView* previousFocus = focus;
3973 
3974 	// Search the tree for the previous view that accept focus
3975 	while (true) {
3976 		if (previousFocus->fPreviousSibling) {
3977 			// find the last child in the previous sibling
3978 			previousFocus = _LastViewChild(previousFocus->fPreviousSibling);
3979 		} else {
3980 			previousFocus = previousFocus->fParent;
3981 			if (previousFocus == fTopView)
3982 				previousFocus = _LastViewChild(fTopView);
3983 		}
3984 
3985 		if (previousFocus == focus || previousFocus == NULL) {
3986 			// When we get here it means that the hole tree has been
3987 			// searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
3988 			return NULL;
3989 		}
3990 
3991 		if (!previousFocus->IsHidden() && (previousFocus->Flags() & flags) != 0)
3992 			return previousFocus;
3993 	}
3994 }
3995 
3996 
3997 /*!
3998 	Returns the last child in a view hierarchy.
3999 	Needed only by _FindPreviousNavigable().
4000 */
4001 BView*
4002 BWindow::_LastViewChild(BView* parent)
4003 {
4004 	while (true) {
4005 		BView* last = parent->fFirstChild;
4006 		if (last == NULL)
4007 			return parent;
4008 
4009 		while (last->fNextSibling) {
4010 			last = last->fNextSibling;
4011 		}
4012 
4013 		parent = last;
4014 	}
4015 }
4016 
4017 
4018 void
4019 BWindow::SetIsFilePanel(bool isFilePanel)
4020 {
4021 	fIsFilePanel = isFilePanel;
4022 }
4023 
4024 
4025 bool
4026 BWindow::IsFilePanel() const
4027 {
4028 	return fIsFilePanel;
4029 }
4030 
4031 
4032 void
4033 BWindow::_GetDecoratorSize(float* _borderWidth, float* _tabHeight) const
4034 {
4035 	// fallback in case retrieving the decorator settings fails
4036 	// (highly unlikely)
4037 	float borderWidth = 5.0;
4038 	float tabHeight = 21.0;
4039 
4040 	BMessage settings;
4041 	if (GetDecoratorSettings(&settings) == B_OK) {
4042 		BRect tabRect;
4043 		if (settings.FindRect("tab frame", &tabRect) == B_OK)
4044 			tabHeight = tabRect.Height();
4045 		settings.FindFloat("border width", &borderWidth);
4046 	} else {
4047 		// probably no-border window look
4048 		if (fLook == B_NO_BORDER_WINDOW_LOOK) {
4049 			borderWidth = 0.0;
4050 			tabHeight = 0.0;
4051 		}
4052 		// else use fall-back values from above
4053 	}
4054 
4055 	if (_borderWidth != NULL)
4056 		*_borderWidth = borderWidth;
4057 	if (_tabHeight != NULL)
4058 		*_tabHeight = tabHeight;
4059 }
4060 
4061 
4062 //	#pragma mark - C++ binary compatibility kludge
4063 
4064 
4065 extern "C" void
4066 _ReservedWindow1__7BWindow(BWindow* window, BLayout* layout)
4067 {
4068 	// SetLayout()
4069 	perform_data_set_layout data;
4070 	data.layout = layout;
4071 	window->Perform(PERFORM_CODE_SET_LAYOUT, &data);
4072 }
4073 
4074 
4075 void BWindow::_ReservedWindow2() {}
4076 void BWindow::_ReservedWindow3() {}
4077 void BWindow::_ReservedWindow4() {}
4078 void BWindow::_ReservedWindow5() {}
4079 void BWindow::_ReservedWindow6() {}
4080 void BWindow::_ReservedWindow7() {}
4081 void BWindow::_ReservedWindow8() {}
4082 
4083