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