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