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