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
76void do_minimize_team(BRect zoomRect, team_id team, bool zoom);
77
78
79struct 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
92class BWindow::Shortcut {
93public:
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
110private:
111			uint32			fKey;
112			uint32			fModifiers;
113			BMenuItem*		fMenuItem;
114			BMessage*		fMessage;
115			BHandler*		fTarget;
116};
117
118
119using BPrivate::gDefaultTokens;
120using BPrivate::MenuPrivate;
121
122static 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
190static 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
215void
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
226BWindow::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
242BWindow::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
253BWindow::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
265BWindow::Shortcut::~Shortcut()
266{
267	// we own the message, if any
268	delete fMessage;
269}
270
271
272bool
273BWindow::Shortcut::Matches(uint32 key, uint32 modifiers) const
274{
275	return fKey == key && fModifiers == modifiers;
276}
277
278
279/*static*/
280uint32
281BWindow::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*/
289uint32
290BWindow::Shortcut::PrepareModifiers(uint32 modifiers)
291{
292	return (modifiers & AllowedModifiers()) | B_COMMAND_KEY;
293}
294
295
296/*static*/
297uint32
298BWindow::Shortcut::PrepareKey(uint32 key)
299{
300	return BUnicodeChar::ToLower(key);
301}
302
303
304//	#pragma mark - BWindow
305
306
307BWindow::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
320BWindow::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
329BWindow::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
381BWindow::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
390BWindow::~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
443BArchivable*
444BWindow::Instantiate(BMessage* data)
445{
446	if (!validate_instantiation(data, "BWindow"))
447		return NULL;
448
449	return new(std::nothrow) BWindow(data);
450}
451
452
453status_t
454BWindow::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
512void
513BWindow::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
541void
542BWindow::AddChild(BView* child, BView* before)
543{
544	BAutolock locker(this);
545	if (locker.IsLocked())
546		fTopView->AddChild(child, before);
547}
548
549
550void
551BWindow::AddChild(BLayoutItem* child)
552{
553	BAutolock locker(this);
554	if (locker.IsLocked())
555		fTopView->AddChild(child);
556}
557
558
559bool
560BWindow::RemoveChild(BView* child)
561{
562	BAutolock locker(this);
563	if (!locker.IsLocked())
564		return false;
565
566	return fTopView->RemoveChild(child);
567}
568
569
570int32
571BWindow::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
581BView*
582BWindow::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
592void
593BWindow::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
609status_t
610BWindow::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
628void
629BWindow::Flush() const
630{
631	if (const_cast<BWindow*>(this)->Lock()) {
632		fLink->Flush();
633		const_cast<BWindow*>(this)->Unlock();
634	}
635}
636
637
638void
639BWindow::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
654void
655BWindow::DisableUpdates()
656{
657	if (Lock()) {
658		fLink->StartMessage(AS_DISABLE_UPDATES);
659		fLink->Flush();
660		Unlock();
661	}
662}
663
664
665void
666BWindow::EnableUpdates()
667{
668	if (Lock()) {
669		fLink->StartMessage(AS_ENABLE_UPDATES);
670		fLink->Flush();
671		Unlock();
672	}
673}
674
675
676void
677BWindow::BeginViewTransaction()
678{
679	if (Lock()) {
680		fInTransaction = true;
681		Unlock();
682	}
683}
684
685
686void
687BWindow::EndViewTransaction()
688{
689	if (Lock()) {
690		if (fInTransaction)
691			fLink->Flush();
692		fInTransaction = false;
693		Unlock();
694	}
695}
696
697
698bool
699BWindow::InViewTransaction() const
700{
701	BAutolock locker(const_cast<BWindow*>(this));
702	return fInTransaction;
703}
704
705
706bool
707BWindow::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
723void
724BWindow::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
937void
938BWindow::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.
1032FrameResized(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.
1053FrameMoved(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_KEY_DOWN:
1152			if (!_HandleKeyDown(message))
1153				target->MessageReceived(message);
1154			break;
1155
1156		case B_UNMAPPED_KEY_DOWN:
1157			if (!_HandleUnmappedKeyDown(message))
1158				target->MessageReceived(message);
1159			break;
1160
1161		case B_PULSE:
1162			if (target == this && fPulseRunner) {
1163				fTopView->_Pulse();
1164				fLink->Flush();
1165			} else
1166				target->MessageReceived(message);
1167			break;
1168
1169		case _UPDATE_:
1170		{
1171//bigtime_t now = system_time();
1172//bigtime_t drawTime = 0;
1173			STRACE(("info:BWindow handling _UPDATE_.\n"));
1174
1175			fLink->StartMessage(AS_BEGIN_UPDATE);
1176			fInTransaction = true;
1177
1178			int32 code;
1179			if (fLink->FlushWithReply(code) == B_OK
1180				&& code == B_OK) {
1181				// read current window position and size first,
1182				// the update rect is in screen coordinates...
1183				// so we need to be up to date
1184				BPoint origin;
1185				fLink->Read<BPoint>(&origin);
1186				float width;
1187				float height;
1188				fLink->Read<float>(&width);
1189				fLink->Read<float>(&height);
1190				if (origin != fFrame.LeftTop()) {
1191					// TODO: remove code duplicatation with
1192					// B_WINDOW_MOVED case...
1193					//printf("window position was not up to date\n");
1194					fFrame.OffsetTo(origin);
1195					FrameMoved(origin);
1196				}
1197				if (width != fFrame.Width() || height != fFrame.Height()) {
1198					// TODO: remove code duplicatation with
1199					// B_WINDOW_RESIZED case...
1200					//printf("window size was not up to date\n");
1201					fFrame.right = fFrame.left + width;
1202					fFrame.bottom = fFrame.top + height;
1203
1204					_AdoptResize();
1205					FrameResized(width, height);
1206				}
1207
1208				// read tokens for views that need to be drawn
1209				// NOTE: we need to read the tokens completely
1210				// first, we cannot draw views in between reading
1211				// the tokens, since other communication would likely
1212				// mess up the data in the link.
1213				struct ViewUpdateInfo {
1214					int32 token;
1215					BRect updateRect;
1216				};
1217				BList infos(20);
1218				while (true) {
1219					// read next token and create/add ViewUpdateInfo
1220					int32 token;
1221					status_t error = fLink->Read<int32>(&token);
1222					if (error < B_OK || token == B_NULL_TOKEN)
1223						break;
1224					ViewUpdateInfo* info = new(std::nothrow) ViewUpdateInfo;
1225					if (info == NULL || !infos.AddItem(info)) {
1226						delete info;
1227						break;
1228					}
1229					info->token = token;
1230					// read culmulated update rect (is in screen coords)
1231					error = fLink->Read<BRect>(&(info->updateRect));
1232					if (error < B_OK)
1233						break;
1234				}
1235				// draw
1236				int32 count = infos.CountItems();
1237				for (int32 i = 0; i < count; i++) {
1238//bigtime_t drawStart = system_time();
1239					ViewUpdateInfo* info
1240						= (ViewUpdateInfo*)infos.ItemAtFast(i);
1241					if (BView* view = _FindView(info->token))
1242						view->_Draw(info->updateRect);
1243					else {
1244						printf("_UPDATE_ - didn't find view by token: %"
1245							B_PRId32 "\n", info->token);
1246					}
1247//drawTime += system_time() - drawStart;
1248				}
1249				// NOTE: The tokens are actually hirachically sorted,
1250				// so traversing the list in revers and calling
1251				// child->_DrawAfterChildren() actually works like intended.
1252				for (int32 i = count - 1; i >= 0; i--) {
1253					ViewUpdateInfo* info
1254						= (ViewUpdateInfo*)infos.ItemAtFast(i);
1255					if (BView* view = _FindView(info->token))
1256						view->_DrawAfterChildren(info->updateRect);
1257					delete info;
1258				}
1259
1260//printf("  %ld views drawn, total Draw() time: %lld\n", count, drawTime);
1261			}
1262
1263			fLink->StartMessage(AS_END_UPDATE);
1264			fLink->Flush();
1265			fInTransaction = false;
1266			fUpdateRequested = false;
1267
1268//printf("BWindow(%s) - UPDATE took %lld usecs\n", Title(), system_time() - now);
1269			break;
1270		}
1271
1272		case _MENUS_DONE_:
1273			MenusEnded();
1274			break;
1275
1276		// These two are obviously some kind of old scripting messages
1277		// this is NOT an app_server message and we have to be cautious
1278		case B_WINDOW_MOVE_BY:
1279		{
1280			BPoint offset;
1281			if (message->FindPoint("data", &offset) == B_OK)
1282				MoveBy(offset.x, offset.y);
1283			else
1284				message->SendReply(B_MESSAGE_NOT_UNDERSTOOD);
1285			break;
1286		}
1287
1288		// this is NOT an app_server message and we have to be cautious
1289		case B_WINDOW_MOVE_TO:
1290		{
1291			BPoint origin;
1292			if (message->FindPoint("data", &origin) == B_OK)
1293				MoveTo(origin);
1294			else
1295				message->SendReply(B_MESSAGE_NOT_UNDERSTOOD);
1296			break;
1297		}
1298
1299		case B_LAYOUT_WINDOW:
1300		{
1301			Layout(false);
1302			break;
1303		}
1304
1305		case B_COLORS_UPDATED:
1306		{
1307			fTopView->_ColorsUpdated(message);
1308			target->MessageReceived(message);
1309			break;
1310		}
1311
1312		case B_FONTS_UPDATED:
1313		{
1314			fTopView->_FontsUpdated(message);
1315			target->MessageReceived(message);
1316			break;
1317		}
1318
1319		default:
1320			BLooper::DispatchMessage(message, target);
1321			break;
1322	}
1323}
1324
1325
1326void
1327BWindow::FrameMoved(BPoint newPosition)
1328{
1329	// does nothing
1330	// Hook function
1331}
1332
1333
1334void
1335BWindow::FrameResized(float newWidth, float newHeight)
1336{
1337	// does nothing
1338	// Hook function
1339}
1340
1341
1342void
1343BWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
1344{
1345	// does nothing
1346	// Hook function
1347}
1348
1349
1350void
1351BWindow::WorkspaceActivated(int32 workspace, bool state)
1352{
1353	// does nothing
1354	// Hook function
1355}
1356
1357
1358void
1359BWindow::MenusBeginning()
1360{
1361	// does nothing
1362	// Hook function
1363}
1364
1365
1366void
1367BWindow::MenusEnded()
1368{
1369	// does nothing
1370	// Hook function
1371}
1372
1373
1374void
1375BWindow::SetSizeLimits(float minWidth, float maxWidth,
1376	float minHeight, float maxHeight)
1377{
1378	if (minWidth > maxWidth || minHeight > maxHeight)
1379		return;
1380
1381	if (!Lock())
1382		return;
1383
1384	fLink->StartMessage(AS_SET_SIZE_LIMITS);
1385	fLink->Attach<float>(minWidth);
1386	fLink->Attach<float>(maxWidth);
1387	fLink->Attach<float>(minHeight);
1388	fLink->Attach<float>(maxHeight);
1389
1390	int32 code;
1391	if (fLink->FlushWithReply(code) == B_OK
1392		&& code == B_OK) {
1393		// read the values that were really enforced on
1394		// the server side (the window frame could have
1395		// been changed, too)
1396		fLink->Read<BRect>(&fFrame);
1397		fLink->Read<float>(&fMinWidth);
1398		fLink->Read<float>(&fMaxWidth);
1399		fLink->Read<float>(&fMinHeight);
1400		fLink->Read<float>(&fMaxHeight);
1401
1402		_AdoptResize();
1403			// TODO: the same has to be done for SetLook() (that can alter
1404			//		the size limits, and hence, the size of the window
1405	}
1406	Unlock();
1407}
1408
1409
1410void
1411BWindow::GetSizeLimits(float* _minWidth, float* _maxWidth, float* _minHeight,
1412	float* _maxHeight)
1413{
1414	// TODO: What about locking?!?
1415	if (_minHeight != NULL)
1416		*_minHeight = fMinHeight;
1417	if (_minWidth != NULL)
1418		*_minWidth = fMinWidth;
1419	if (_maxHeight != NULL)
1420		*_maxHeight = fMaxHeight;
1421	if (_maxWidth != NULL)
1422		*_maxWidth = fMaxWidth;
1423}
1424
1425
1426void
1427BWindow::UpdateSizeLimits()
1428{
1429	BAutolock locker(this);
1430
1431	if ((fFlags & B_AUTO_UPDATE_SIZE_LIMITS) != 0) {
1432		// Get min/max constraints of the top view and enforce window
1433		// size limits respectively.
1434		BSize minSize = fTopView->MinSize();
1435		BSize maxSize = fTopView->MaxSize();
1436		SetSizeLimits(minSize.width, maxSize.width,
1437			minSize.height, maxSize.height);
1438	}
1439}
1440
1441
1442status_t
1443BWindow::SetDecoratorSettings(const BMessage& settings)
1444{
1445	// flatten the given settings into a buffer and send
1446	// it to the app_server to apply the settings to the
1447	// decorator
1448
1449	int32 size = settings.FlattenedSize();
1450	char buffer[size];
1451	status_t status = settings.Flatten(buffer, size);
1452	if (status != B_OK)
1453		return status;
1454
1455	if (!Lock())
1456		return B_ERROR;
1457
1458	status = fLink->StartMessage(AS_SET_DECORATOR_SETTINGS);
1459
1460	if (status == B_OK)
1461		status = fLink->Attach<int32>(size);
1462
1463	if (status == B_OK)
1464		status = fLink->Attach(buffer, size);
1465
1466	if (status == B_OK)
1467		status = fLink->Flush();
1468
1469	Unlock();
1470
1471	return status;
1472}
1473
1474
1475status_t
1476BWindow::GetDecoratorSettings(BMessage* settings) const
1477{
1478	// read a flattened settings message from the app_server
1479	// and put it into settings
1480
1481	if (!const_cast<BWindow*>(this)->Lock())
1482		return B_ERROR;
1483
1484	status_t status = fLink->StartMessage(AS_GET_DECORATOR_SETTINGS);
1485
1486	if (status == B_OK) {
1487		int32 code;
1488		status = fLink->FlushWithReply(code);
1489		if (status == B_OK && code != B_OK)
1490			status = code;
1491	}
1492
1493	if (status == B_OK) {
1494		int32 size;
1495		status = fLink->Read<int32>(&size);
1496		if (status == B_OK) {
1497			char buffer[size];
1498			status = fLink->Read(buffer, size);
1499			if (status == B_OK) {
1500				status = settings->Unflatten(buffer);
1501			}
1502		}
1503	}
1504
1505	const_cast<BWindow*>(this)->Unlock();
1506
1507	return status;
1508}
1509
1510
1511void
1512BWindow::SetZoomLimits(float maxWidth, float maxHeight)
1513{
1514	// TODO: What about locking?!?
1515	if (maxWidth > fMaxWidth)
1516		maxWidth = fMaxWidth;
1517	else
1518		fMaxZoomWidth = maxWidth;
1519
1520	if (maxHeight > fMaxHeight)
1521		maxHeight = fMaxHeight;
1522	else
1523		fMaxZoomHeight = maxHeight;
1524}
1525
1526
1527void
1528BWindow::Zoom(BPoint origin, float width, float height)
1529{
1530	// the default implementation of this hook function
1531	// just does the obvious:
1532	MoveTo(origin);
1533	ResizeTo(width, height);
1534}
1535
1536
1537void
1538BWindow::Zoom()
1539{
1540	// TODO: What about locking?!?
1541
1542	// From BeBook:
1543	// The dimensions that non-virtual Zoom() passes to hook Zoom() are deduced
1544	// from the smallest of three rectangles:
1545
1546	// 1) the rectangle defined by SetZoomLimits() and,
1547	// 2) the rectangle defined by SetSizeLimits()
1548	float maxZoomWidth = std::min(fMaxZoomWidth, fMaxWidth);
1549	float maxZoomHeight = std::min(fMaxZoomHeight, fMaxHeight);
1550
1551	// 3) the screen rectangle
1552	BRect screenFrame = (BScreen(this)).Frame();
1553	maxZoomWidth = std::min(maxZoomWidth, screenFrame.Width());
1554	maxZoomHeight = std::min(maxZoomHeight, screenFrame.Height());
1555
1556	BRect zoomArea = screenFrame; // starts at screen size
1557
1558	BDeskbar deskbar;
1559	BRect deskbarFrame = deskbar.Frame();
1560	bool isShiftDown = (modifiers() & B_SHIFT_KEY) != 0;
1561	if (!isShiftDown && !deskbar.IsAutoHide()) {
1562		// remove area taken up by Deskbar unless hidden or shift is held down
1563		switch (deskbar.Location()) {
1564			case B_DESKBAR_TOP:
1565				zoomArea.top = deskbarFrame.bottom + 2;
1566				break;
1567
1568			case B_DESKBAR_BOTTOM:
1569				zoomArea.bottom = deskbarFrame.top - 2;
1570				break;
1571
1572			// in vertical mode, only if not always on top and not auto-raise
1573			case B_DESKBAR_LEFT_TOP:
1574			case B_DESKBAR_LEFT_BOTTOM:
1575				if (!deskbar.IsAlwaysOnTop() && !deskbar.IsAutoRaise())
1576					zoomArea.left = deskbarFrame.right + 2;
1577				break;
1578
1579			default:
1580			case B_DESKBAR_RIGHT_TOP:
1581			case B_DESKBAR_RIGHT_BOTTOM:
1582				if (!deskbar.IsAlwaysOnTop() && !deskbar.IsAutoRaise())
1583					zoomArea.right = deskbarFrame.left - 2;
1584				break;
1585		}
1586	}
1587
1588	// TODO: Broken for tab on left side windows...
1589	float borderWidth;
1590	float tabHeight;
1591	_GetDecoratorSize(&borderWidth, &tabHeight);
1592
1593	// remove the area taken up by the tab and border
1594	zoomArea.left += borderWidth;
1595	zoomArea.top += borderWidth + tabHeight;
1596	zoomArea.right -= borderWidth;
1597	zoomArea.bottom -= borderWidth;
1598
1599	// inset towards center vertically first to see if there will be room
1600	// above or below Deskbar
1601	if (zoomArea.Height() > maxZoomHeight)
1602		zoomArea.InsetBy(0, roundf((zoomArea.Height() - maxZoomHeight) / 2));
1603
1604	if (zoomArea.top > deskbarFrame.bottom
1605		|| zoomArea.bottom < deskbarFrame.top) {
1606		// there is room above or below Deskbar, start from screen width
1607		// minus borders instead of desktop width minus borders
1608		zoomArea.left = screenFrame.left + borderWidth;
1609		zoomArea.right = screenFrame.right - borderWidth;
1610	}
1611
1612	// inset towards center
1613	if (zoomArea.Width() > maxZoomWidth)
1614		zoomArea.InsetBy(roundf((zoomArea.Width() - maxZoomWidth) / 2), 0);
1615
1616	// Un-Zoom
1617
1618	if (fPreviousFrame.IsValid()
1619		// NOTE: don't check for fFrame.LeftTop() == zoomArea.LeftTop()
1620		// -> makes it easier on the user to get a window back into place
1621		&& fFrame.Width() == zoomArea.Width()
1622		&& fFrame.Height() == zoomArea.Height()) {
1623		// already zoomed!
1624		Zoom(fPreviousFrame.LeftTop(), fPreviousFrame.Width(),
1625			fPreviousFrame.Height());
1626		return;
1627	}
1628
1629	// Zoom
1630
1631	// remember fFrame for later "unzooming"
1632	fPreviousFrame = fFrame;
1633
1634	Zoom(zoomArea.LeftTop(), zoomArea.Width(), zoomArea.Height());
1635}
1636
1637
1638void
1639BWindow::ScreenChanged(BRect screenSize, color_space depth)
1640{
1641	// Hook function
1642}
1643
1644
1645void
1646BWindow::SetPulseRate(bigtime_t rate)
1647{
1648	// TODO: What about locking?!?
1649	if (rate < 0
1650		|| (rate == fPulseRate && !((rate == 0) ^ (fPulseRunner == NULL))))
1651		return;
1652
1653	fPulseRate = rate;
1654
1655	if (rate > 0) {
1656		if (fPulseRunner == NULL) {
1657			BMessage message(B_PULSE);
1658			fPulseRunner = new(std::nothrow) BMessageRunner(BMessenger(this),
1659				&message, rate);
1660		} else {
1661			fPulseRunner->SetInterval(rate);
1662		}
1663	} else {
1664		// rate == 0
1665		delete fPulseRunner;
1666		fPulseRunner = NULL;
1667	}
1668}
1669
1670
1671bigtime_t
1672BWindow::PulseRate() const
1673{
1674	return fPulseRate;
1675}
1676
1677
1678void
1679BWindow::AddShortcut(uint32 key, uint32 modifiers, BMenuItem* item)
1680{
1681	Shortcut* shortcut = new(std::nothrow) Shortcut(key, modifiers, item);
1682	if (shortcut == NULL)
1683		return;
1684
1685	// removes the shortcut if it already exists!
1686	RemoveShortcut(key, modifiers);
1687
1688	fShortcuts.AddItem(shortcut);
1689}
1690
1691
1692void
1693BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage* message)
1694{
1695	AddShortcut(key, modifiers, message, this);
1696}
1697
1698
1699void
1700BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage* message,
1701	BHandler* target)
1702{
1703	if (message == NULL)
1704		return;
1705
1706	Shortcut* shortcut = new(std::nothrow) Shortcut(key, modifiers, message,
1707		target);
1708	if (shortcut == NULL)
1709		return;
1710
1711	// removes the shortcut if it already exists!
1712	RemoveShortcut(key, modifiers);
1713
1714	fShortcuts.AddItem(shortcut);
1715}
1716
1717
1718bool
1719BWindow::HasShortcut(uint32 key, uint32 modifiers)
1720{
1721	return _FindShortcut(key, modifiers) != NULL;
1722}
1723
1724
1725void
1726BWindow::RemoveShortcut(uint32 key, uint32 modifiers)
1727{
1728	Shortcut* shortcut = _FindShortcut(key, modifiers);
1729	if (shortcut != NULL) {
1730		fShortcuts.RemoveItem(shortcut);
1731		delete shortcut;
1732	} else if ((key == 'q' || key == 'Q') && modifiers == B_COMMAND_KEY) {
1733		// the quit shortcut is a fake shortcut
1734		fNoQuitShortcut = true;
1735	}
1736}
1737
1738
1739BButton*
1740BWindow::DefaultButton() const
1741{
1742	// TODO: What about locking?!?
1743	return fDefaultButton;
1744}
1745
1746
1747void
1748BWindow::SetDefaultButton(BButton* button)
1749{
1750	// TODO: What about locking?!?
1751	if (fDefaultButton == button)
1752		return;
1753
1754	if (fDefaultButton != NULL) {
1755		// tell old button it's no longer the default one
1756		BButton* oldDefault = fDefaultButton;
1757		oldDefault->MakeDefault(false);
1758		oldDefault->Invalidate();
1759	}
1760
1761	fDefaultButton = button;
1762
1763	if (button != NULL) {
1764		// notify new default button
1765		fDefaultButton->MakeDefault(true);
1766		fDefaultButton->Invalidate();
1767	}
1768}
1769
1770
1771bool
1772BWindow::NeedsUpdate() const
1773{
1774	if (!const_cast<BWindow*>(this)->Lock())
1775		return false;
1776
1777	fLink->StartMessage(AS_NEEDS_UPDATE);
1778
1779	int32 code = B_ERROR;
1780	fLink->FlushWithReply(code);
1781
1782	const_cast<BWindow*>(this)->Unlock();
1783
1784	return code == B_OK;
1785}
1786
1787
1788void
1789BWindow::UpdateIfNeeded()
1790{
1791	// works only from the window thread
1792	if (find_thread(NULL) != Thread())
1793		return;
1794
1795	// if the queue is already locked we are called recursivly
1796	// from our own dispatched update message
1797	if (((const BMessageQueue*)MessageQueue())->IsLocked())
1798		return;
1799
1800	if (!Lock())
1801		return;
1802
1803	// make sure all requests that would cause an update have
1804	// arrived at the server
1805	Sync();
1806
1807	// Since we're blocking the event loop, we need to retrieve
1808	// all messages that are pending on the port.
1809	_DequeueAll();
1810
1811	BMessageQueue* queue = MessageQueue();
1812
1813	// First process and remove any _UPDATE_ message in the queue
1814	// With the current design, there can only be one at a time
1815
1816	while (true) {
1817		queue->Lock();
1818
1819		BMessage* message = queue->FindMessage(_UPDATE_, 0);
1820		queue->RemoveMessage(message);
1821
1822		queue->Unlock();
1823
1824		if (message == NULL)
1825			break;
1826
1827		BWindow::DispatchMessage(message, this);
1828		delete message;
1829	}
1830
1831	Unlock();
1832}
1833
1834
1835BView*
1836BWindow::FindView(const char* viewName) const
1837{
1838	BAutolock locker(const_cast<BWindow*>(this));
1839	if (!locker.IsLocked())
1840		return NULL;
1841
1842	return fTopView->FindView(viewName);
1843}
1844
1845
1846BView*
1847BWindow::FindView(BPoint point) const
1848{
1849	BAutolock locker(const_cast<BWindow*>(this));
1850	if (!locker.IsLocked())
1851		return NULL;
1852
1853	// point is assumed to be in window coordinates,
1854	// fTopView has same bounds as window
1855	return _FindView(fTopView, point);
1856}
1857
1858
1859BView*
1860BWindow::CurrentFocus() const
1861{
1862	return fFocus;
1863}
1864
1865
1866void
1867BWindow::Activate(bool active)
1868{
1869	if (!Lock())
1870		return;
1871
1872	if (!IsHidden()) {
1873		fMinimized = false;
1874			// activating a window will also unminimize it
1875
1876		fLink->StartMessage(AS_ACTIVATE_WINDOW);
1877		fLink->Attach<bool>(active);
1878		fLink->Flush();
1879	}
1880
1881	Unlock();
1882}
1883
1884
1885void
1886BWindow::WindowActivated(bool focus)
1887{
1888	// hook function
1889	// does nothing
1890}
1891
1892
1893void
1894BWindow::ConvertToScreen(BPoint* point) const
1895{
1896	point->x += fFrame.left;
1897	point->y += fFrame.top;
1898}
1899
1900
1901BPoint
1902BWindow::ConvertToScreen(BPoint point) const
1903{
1904	return point + fFrame.LeftTop();
1905}
1906
1907
1908void
1909BWindow::ConvertFromScreen(BPoint* point) const
1910{
1911	point->x -= fFrame.left;
1912	point->y -= fFrame.top;
1913}
1914
1915
1916BPoint
1917BWindow::ConvertFromScreen(BPoint point) const
1918{
1919	return point - fFrame.LeftTop();
1920}
1921
1922
1923void
1924BWindow::ConvertToScreen(BRect* rect) const
1925{
1926	rect->OffsetBy(fFrame.LeftTop());
1927}
1928
1929
1930BRect
1931BWindow::ConvertToScreen(BRect rect) const
1932{
1933	return rect.OffsetByCopy(fFrame.LeftTop());
1934}
1935
1936
1937void
1938BWindow::ConvertFromScreen(BRect* rect) const
1939{
1940	rect->OffsetBy(-fFrame.left, -fFrame.top);
1941}
1942
1943
1944BRect
1945BWindow::ConvertFromScreen(BRect rect) const
1946{
1947	return rect.OffsetByCopy(-fFrame.left, -fFrame.top);
1948}
1949
1950
1951bool
1952BWindow::IsMinimized() const
1953{
1954	BAutolock locker(const_cast<BWindow*>(this));
1955	if (!locker.IsLocked())
1956		return false;
1957
1958	return fMinimized;
1959}
1960
1961
1962BRect
1963BWindow::Bounds() const
1964{
1965	return BRect(0, 0, fFrame.Width(), fFrame.Height());
1966}
1967
1968
1969BRect
1970BWindow::Frame() const
1971{
1972	return fFrame;
1973}
1974
1975
1976BRect
1977BWindow::DecoratorFrame() const
1978{
1979	BRect decoratorFrame(Frame());
1980	BRect tabRect(0, 0, 0, 0);
1981
1982	float borderWidth = 5.0;
1983
1984	BMessage settings;
1985	if (GetDecoratorSettings(&settings) == B_OK) {
1986		settings.FindRect("tab frame", &tabRect);
1987		settings.FindFloat("border width", &borderWidth);
1988	} else {
1989		// probably no-border window look
1990		if (fLook == B_NO_BORDER_WINDOW_LOOK)
1991			borderWidth = 0.f;
1992		else if (fLook == B_BORDERED_WINDOW_LOOK)
1993			borderWidth = 1.f;
1994		// else use fall-back values from above
1995	}
1996
1997	if (fLook == kLeftTitledWindowLook) {
1998		decoratorFrame.top -= borderWidth;
1999		decoratorFrame.left -= borderWidth + tabRect.Width();
2000		decoratorFrame.right += borderWidth;
2001		decoratorFrame.bottom += borderWidth;
2002	} else {
2003		decoratorFrame.top -= borderWidth + tabRect.Height();
2004		decoratorFrame.left -= borderWidth;
2005		decoratorFrame.right += borderWidth;
2006		decoratorFrame.bottom += borderWidth;
2007	}
2008
2009	return decoratorFrame;
2010}
2011
2012
2013BSize
2014BWindow::Size() const
2015{
2016	return BSize(fFrame.Width(), fFrame.Height());
2017}
2018
2019
2020const char*
2021BWindow::Title() const
2022{
2023	return fTitle;
2024}
2025
2026
2027void
2028BWindow::SetTitle(const char* title)
2029{
2030	if (title == NULL)
2031		title = "";
2032
2033	free(fTitle);
2034	fTitle = strdup(title);
2035
2036	_SetName(title);
2037
2038	// we notify the app_server so we can actually see the change
2039	if (Lock()) {
2040		fLink->StartMessage(AS_SET_WINDOW_TITLE);
2041		fLink->AttachString(fTitle);
2042		fLink->Flush();
2043		Unlock();
2044	}
2045}
2046
2047
2048bool
2049BWindow::IsActive() const
2050{
2051	return fActive;
2052}
2053
2054
2055void
2056BWindow::SetKeyMenuBar(BMenuBar* bar)
2057{
2058	fKeyMenuBar = bar;
2059}
2060
2061
2062BMenuBar*
2063BWindow::KeyMenuBar() const
2064{
2065	return fKeyMenuBar;
2066}
2067
2068
2069bool
2070BWindow::IsModal() const
2071{
2072	return fFeel == B_MODAL_SUBSET_WINDOW_FEEL
2073		|| fFeel == B_MODAL_APP_WINDOW_FEEL
2074		|| fFeel == B_MODAL_ALL_WINDOW_FEEL
2075		|| fFeel == kMenuWindowFeel;
2076}
2077
2078
2079bool
2080BWindow::IsFloating() const
2081{
2082	return fFeel == B_FLOATING_SUBSET_WINDOW_FEEL
2083		|| fFeel == B_FLOATING_APP_WINDOW_FEEL
2084		|| fFeel == B_FLOATING_ALL_WINDOW_FEEL;
2085}
2086
2087
2088status_t
2089BWindow::AddToSubset(BWindow* window)
2090{
2091	if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL
2092		|| (fFeel != B_MODAL_SUBSET_WINDOW_FEEL
2093			&& fFeel != B_FLOATING_SUBSET_WINDOW_FEEL))
2094		return B_BAD_VALUE;
2095
2096	if (!Lock())
2097		return B_ERROR;
2098
2099	status_t status = B_ERROR;
2100	fLink->StartMessage(AS_ADD_TO_SUBSET);
2101	fLink->Attach<int32>(_get_object_token_(window));
2102	fLink->FlushWithReply(status);
2103
2104	Unlock();
2105
2106	return status;
2107}
2108
2109
2110status_t
2111BWindow::RemoveFromSubset(BWindow* window)
2112{
2113	if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL
2114		|| (fFeel != B_MODAL_SUBSET_WINDOW_FEEL
2115			&& fFeel != B_FLOATING_SUBSET_WINDOW_FEEL))
2116		return B_BAD_VALUE;
2117
2118	if (!Lock())
2119		return B_ERROR;
2120
2121	status_t status = B_ERROR;
2122	fLink->StartMessage(AS_REMOVE_FROM_SUBSET);
2123	fLink->Attach<int32>(_get_object_token_(window));
2124	fLink->FlushWithReply(status);
2125
2126	Unlock();
2127
2128	return status;
2129}
2130
2131
2132status_t
2133BWindow::Perform(perform_code code, void* _data)
2134{
2135	switch (code) {
2136		case PERFORM_CODE_SET_LAYOUT:
2137		{
2138			perform_data_set_layout* data = (perform_data_set_layout*)_data;
2139			BWindow::SetLayout(data->layout);
2140			return B_OK;
2141}
2142	}
2143
2144	return BLooper::Perform(code, _data);
2145}
2146
2147
2148status_t
2149BWindow::SetType(window_type type)
2150{
2151	window_look look;
2152	window_feel feel;
2153	_DecomposeType(type, &look, &feel);
2154
2155	status_t status = SetLook(look);
2156	if (status == B_OK)
2157		status = SetFeel(feel);
2158
2159	return status;
2160}
2161
2162
2163window_type
2164BWindow::Type() const
2165{
2166	return _ComposeType(fLook, fFeel);
2167}
2168
2169
2170status_t
2171BWindow::SetLook(window_look look)
2172{
2173	BAutolock locker(this);
2174	if (!locker.IsLocked())
2175		return B_BAD_VALUE;
2176
2177	fLink->StartMessage(AS_SET_LOOK);
2178	fLink->Attach<int32>((int32)look);
2179
2180	status_t status = B_ERROR;
2181	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2182		fLook = look;
2183
2184	// TODO: this could have changed the window size, and thus, we
2185	//	need to get it from the server (and call _AdoptResize()).
2186
2187	return status;
2188}
2189
2190
2191window_look
2192BWindow::Look() const
2193{
2194	return fLook;
2195}
2196
2197
2198status_t
2199BWindow::SetFeel(window_feel feel)
2200{
2201	BAutolock locker(this);
2202	if (!locker.IsLocked())
2203		return B_BAD_VALUE;
2204
2205	fLink->StartMessage(AS_SET_FEEL);
2206	fLink->Attach<int32>((int32)feel);
2207
2208	status_t status = B_ERROR;
2209	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2210		fFeel = feel;
2211
2212	return status;
2213}
2214
2215
2216window_feel
2217BWindow::Feel() const
2218{
2219	return fFeel;
2220}
2221
2222
2223status_t
2224BWindow::SetFlags(uint32 flags)
2225{
2226	BAutolock locker(this);
2227	if (!locker.IsLocked())
2228		return B_BAD_VALUE;
2229
2230	fLink->StartMessage(AS_SET_FLAGS);
2231	fLink->Attach<uint32>(flags);
2232
2233	int32 status = B_ERROR;
2234	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2235		fFlags = flags;
2236
2237	return status;
2238}
2239
2240
2241uint32
2242BWindow::Flags() const
2243{
2244	return fFlags;
2245}
2246
2247
2248status_t
2249BWindow::SetWindowAlignment(window_alignment mode,
2250	int32 h, int32 hOffset, int32 width, int32 widthOffset,
2251	int32 v, int32 vOffset, int32 height, int32 heightOffset)
2252{
2253	if ((mode & (B_BYTE_ALIGNMENT | B_PIXEL_ALIGNMENT)) == 0
2254		|| (hOffset >= 0 && hOffset <= h)
2255		|| (vOffset >= 0 && vOffset <= v)
2256		|| (widthOffset >= 0 && widthOffset <= width)
2257		|| (heightOffset >= 0 && heightOffset <= height))
2258		return B_BAD_VALUE;
2259
2260	// TODO: test if hOffset = 0 and set it to 1 if true.
2261
2262	if (!Lock())
2263		return B_ERROR;
2264
2265	fLink->StartMessage(AS_SET_ALIGNMENT);
2266	fLink->Attach<int32>((int32)mode);
2267	fLink->Attach<int32>(h);
2268	fLink->Attach<int32>(hOffset);
2269	fLink->Attach<int32>(width);
2270	fLink->Attach<int32>(widthOffset);
2271	fLink->Attach<int32>(v);
2272	fLink->Attach<int32>(vOffset);
2273	fLink->Attach<int32>(height);
2274	fLink->Attach<int32>(heightOffset);
2275
2276	status_t status = B_ERROR;
2277	fLink->FlushWithReply(status);
2278
2279	Unlock();
2280
2281	return status;
2282}
2283
2284
2285status_t
2286BWindow::GetWindowAlignment(window_alignment* mode,
2287	int32* h, int32* hOffset, int32* width, int32* widthOffset,
2288	int32* v, int32* vOffset, int32* height, int32* heightOffset) const
2289{
2290	if (!const_cast<BWindow*>(this)->Lock())
2291		return B_ERROR;
2292
2293	fLink->StartMessage(AS_GET_ALIGNMENT);
2294
2295	status_t status;
2296	if (fLink->FlushWithReply(status) == B_OK && status == B_OK) {
2297		fLink->Read<int32>((int32*)mode);
2298		fLink->Read<int32>(h);
2299		fLink->Read<int32>(hOffset);
2300		fLink->Read<int32>(width);
2301		fLink->Read<int32>(widthOffset);
2302		fLink->Read<int32>(v);
2303		fLink->Read<int32>(hOffset);
2304		fLink->Read<int32>(height);
2305		fLink->Read<int32>(heightOffset);
2306	}
2307
2308	const_cast<BWindow*>(this)->Unlock();
2309	return status;
2310}
2311
2312
2313uint32
2314BWindow::Workspaces() const
2315{
2316	if (!const_cast<BWindow*>(this)->Lock())
2317		return 0;
2318
2319	uint32 workspaces = 0;
2320
2321	fLink->StartMessage(AS_GET_WORKSPACES);
2322
2323	status_t status;
2324	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2325		fLink->Read<uint32>(&workspaces);
2326
2327	const_cast<BWindow*>(this)->Unlock();
2328	return workspaces;
2329}
2330
2331
2332void
2333BWindow::SetWorkspaces(uint32 workspaces)
2334{
2335	// TODO: don't forget about Tracker's background window.
2336	if (fFeel != B_NORMAL_WINDOW_FEEL)
2337		return;
2338
2339	if (Lock()) {
2340		fLink->StartMessage(AS_SET_WORKSPACES);
2341		fLink->Attach<uint32>(workspaces);
2342		fLink->Flush();
2343		Unlock();
2344	}
2345}
2346
2347
2348BView*
2349BWindow::LastMouseMovedView() const
2350{
2351	return fLastMouseMovedView;
2352}
2353
2354
2355void
2356BWindow::MoveBy(float dx, float dy)
2357{
2358	if ((dx != 0.0f || dy != 0.0f) && Lock()) {
2359		MoveTo(fFrame.left + dx, fFrame.top + dy);
2360		Unlock();
2361	}
2362}
2363
2364
2365void
2366BWindow::MoveTo(BPoint point)
2367{
2368	MoveTo(point.x, point.y);
2369}
2370
2371
2372void
2373BWindow::MoveTo(float x, float y)
2374{
2375	if (!Lock())
2376		return;
2377
2378	x = roundf(x);
2379	y = roundf(y);
2380
2381	if (fFrame.left != x || fFrame.top != y) {
2382		fLink->StartMessage(AS_WINDOW_MOVE);
2383		fLink->Attach<float>(x);
2384		fLink->Attach<float>(y);
2385
2386		status_t status;
2387		if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2388			fFrame.OffsetTo(x, y);
2389	}
2390
2391	Unlock();
2392}
2393
2394
2395void
2396BWindow::ResizeBy(float dx, float dy)
2397{
2398	if (Lock()) {
2399		ResizeTo(fFrame.Width() + dx, fFrame.Height() + dy);
2400		Unlock();
2401	}
2402}
2403
2404
2405void
2406BWindow::ResizeTo(float width, float height)
2407{
2408	if (!Lock())
2409		return;
2410
2411	width = roundf(width);
2412	height = roundf(height);
2413
2414	// stay in minimum & maximum frame limits
2415	if (width < fMinWidth)
2416		width = fMinWidth;
2417	else if (width > fMaxWidth)
2418		width = fMaxWidth;
2419
2420	if (height < fMinHeight)
2421		height = fMinHeight;
2422	else if (height > fMaxHeight)
2423		height = fMaxHeight;
2424
2425	if (width != fFrame.Width() || height != fFrame.Height()) {
2426		fLink->StartMessage(AS_WINDOW_RESIZE);
2427		fLink->Attach<float>(width);
2428		fLink->Attach<float>(height);
2429
2430		status_t status;
2431		if (fLink->FlushWithReply(status) == B_OK && status == B_OK) {
2432			fFrame.right = fFrame.left + width;
2433			fFrame.bottom = fFrame.top + height;
2434			_AdoptResize();
2435		}
2436	}
2437
2438	Unlock();
2439}
2440
2441
2442void
2443BWindow::ResizeToPreferred()
2444{
2445	BAutolock locker(this);
2446	Layout(false);
2447
2448	float width = fTopView->PreferredSize().width;
2449	width = std::min(width, fTopView->MaxSize().width);
2450	width = std::max(width, fTopView->MinSize().width);
2451
2452	float height = fTopView->PreferredSize().height;
2453	height = std::min(width, fTopView->MaxSize().height);
2454	height = std::max(width, fTopView->MinSize().height);
2455
2456	if (GetLayout()->HasHeightForWidth())
2457		GetLayout()->GetHeightForWidth(width, NULL, NULL, &height);
2458
2459	ResizeTo(width, height);
2460}
2461
2462
2463void
2464BWindow::CenterIn(const BRect& rect)
2465{
2466	BAutolock locker(this);
2467
2468	// Set size limits now if needed
2469	UpdateSizeLimits();
2470
2471	MoveTo(BLayoutUtils::AlignInFrame(rect, Size(),
2472		BAlignment(B_ALIGN_HORIZONTAL_CENTER,
2473			B_ALIGN_VERTICAL_CENTER)).LeftTop());
2474	MoveOnScreen(B_DO_NOT_RESIZE_TO_FIT | B_MOVE_IF_PARTIALLY_OFFSCREEN);
2475}
2476
2477
2478void
2479BWindow::CenterOnScreen()
2480{
2481	CenterIn(BScreen(this).Frame());
2482}
2483
2484
2485// Centers the window on the screen with the passed in id.
2486void
2487BWindow::CenterOnScreen(screen_id id)
2488{
2489	CenterIn(BScreen(id).Frame());
2490}
2491
2492
2493void
2494BWindow::MoveOnScreen(uint32 flags)
2495{
2496	// Set size limits now if needed
2497	UpdateSizeLimits();
2498
2499	BRect screenFrame = BScreen(this).Frame();
2500	BRect frame = Frame();
2501
2502	float borderWidth;
2503	float tabHeight;
2504	_GetDecoratorSize(&borderWidth, &tabHeight);
2505
2506	frame.InsetBy(-borderWidth, -borderWidth);
2507	frame.top -= tabHeight;
2508
2509	if ((flags & B_DO_NOT_RESIZE_TO_FIT) == 0) {
2510		// Make sure the window fits on the screen
2511		if (frame.Width() > screenFrame.Width())
2512			frame.right -= frame.Width() - screenFrame.Width();
2513		if (frame.Height() > screenFrame.Height())
2514			frame.bottom -= frame.Height() - screenFrame.Height();
2515
2516		BRect innerFrame = frame;
2517		innerFrame.top += tabHeight;
2518		innerFrame.InsetBy(borderWidth, borderWidth);
2519		ResizeTo(innerFrame.Width(), innerFrame.Height());
2520	}
2521
2522	if (((flags & B_MOVE_IF_PARTIALLY_OFFSCREEN) == 0
2523			&& !screenFrame.Contains(frame))
2524		|| !frame.Intersects(screenFrame)) {
2525		// Off and away
2526		CenterOnScreen();
2527		return;
2528	}
2529
2530	// Move such that the upper left corner, and most of the window
2531	// will be visible.
2532	float left = frame.left;
2533	if (left < screenFrame.left)
2534		left = screenFrame.left;
2535	else if (frame.right > screenFrame.right)
2536		left = std::max(0.f, screenFrame.right - frame.Width());
2537
2538	float top = frame.top;
2539	if (top < screenFrame.top)
2540		top = screenFrame.top;
2541	else if (frame.bottom > screenFrame.bottom)
2542		top = std::max(0.f, screenFrame.bottom - frame.Height());
2543
2544	if (top != frame.top || left != frame.left)
2545		MoveTo(left + borderWidth, top + tabHeight + borderWidth);
2546}
2547
2548
2549void
2550BWindow::Show()
2551{
2552	bool runCalled = true;
2553	if (Lock()) {
2554		fShowLevel--;
2555
2556		_SendShowOrHideMessage();
2557
2558		runCalled = fRunCalled;
2559
2560		Unlock();
2561	}
2562
2563	if (!runCalled) {
2564		// This is the fist time Show() is called, which implicitly runs the
2565		// looper. NOTE: The window is still locked if it has not been
2566		// run yet, so accessing members is safe.
2567		if (fLink->SenderPort() < B_OK) {
2568			// We don't have valid app_server connection; there is no point
2569			// in starting our looper
2570			fThread = B_ERROR;
2571			return;
2572		} else
2573			Run();
2574	}
2575}
2576
2577
2578void
2579BWindow::Hide()
2580{
2581	if (Lock()) {
2582		// If we are minimized and are about to be hidden, unminimize
2583		if (IsMinimized() && fShowLevel == 0)
2584			Minimize(false);
2585
2586		fShowLevel++;
2587
2588		_SendShowOrHideMessage();
2589
2590		Unlock();
2591	}
2592}
2593
2594
2595bool
2596BWindow::IsHidden() const
2597{
2598	return fShowLevel > 0;
2599}
2600
2601
2602bool
2603BWindow::QuitRequested()
2604{
2605	return BLooper::QuitRequested();
2606}
2607
2608
2609thread_id
2610BWindow::Run()
2611{
2612	return BLooper::Run();
2613}
2614
2615
2616void
2617BWindow::SetLayout(BLayout* layout)
2618{
2619	// Adopt layout's colors for fTopView
2620	if (layout != NULL)
2621		fTopView->AdoptViewColors(layout->View());
2622
2623	fTopView->SetLayout(layout);
2624}
2625
2626
2627BLayout*
2628BWindow::GetLayout() const
2629{
2630	return fTopView->GetLayout();
2631}
2632
2633
2634void
2635BWindow::InvalidateLayout(bool descendants)
2636{
2637	fTopView->InvalidateLayout(descendants);
2638}
2639
2640
2641void
2642BWindow::Layout(bool force)
2643{
2644	UpdateSizeLimits();
2645
2646	// Do the actual layout
2647	fTopView->Layout(force);
2648}
2649
2650
2651bool
2652BWindow::IsOffscreenWindow() const
2653{
2654	return fOffscreen;
2655}
2656
2657
2658status_t
2659BWindow::GetSupportedSuites(BMessage* data)
2660{
2661	if (data == NULL)
2662		return B_BAD_VALUE;
2663
2664	status_t status = data->AddString("suites", "suite/vnd.Be-window");
2665	if (status == B_OK) {
2666		BPropertyInfo propertyInfo(sWindowPropInfo, sWindowValueInfo);
2667
2668		status = data->AddFlat("messages", &propertyInfo);
2669		if (status == B_OK)
2670			status = BLooper::GetSupportedSuites(data);
2671	}
2672
2673	return status;
2674}
2675
2676
2677BHandler*
2678BWindow::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
2679	int32 what, const char* property)
2680{
2681	if (message->what == B_WINDOW_MOVE_BY
2682		|| message->what == B_WINDOW_MOVE_TO)
2683		return this;
2684
2685	BPropertyInfo propertyInfo(sWindowPropInfo);
2686	if (propertyInfo.FindMatch(message, index, specifier, what, property) >= 0) {
2687		if (strcmp(property, "View") == 0) {
2688			// we will NOT pop the current specifier
2689			return fTopView;
2690		} else if (strcmp(property, "MenuBar") == 0) {
2691			if (fKeyMenuBar) {
2692				message->PopSpecifier();
2693				return fKeyMenuBar;
2694			} else {
2695				BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
2696				replyMsg.AddInt32("error", B_NAME_NOT_FOUND);
2697				replyMsg.AddString("message",
2698					"This window doesn't have a main MenuBar");
2699				message->SendReply(&replyMsg);
2700				return NULL;
2701			}
2702		} else
2703			return this;
2704	}
2705
2706	return BLooper::ResolveSpecifier(message, index, specifier, what, property);
2707}
2708
2709
2710//	#pragma mark - Private Methods
2711
2712
2713void
2714BWindow::_InitData(BRect frame, const char* title, window_look look,
2715	window_feel feel, uint32 flags,	uint32 workspace, int32 bitmapToken)
2716{
2717	STRACE(("BWindow::InitData()\n"));
2718
2719	if (be_app == NULL) {
2720		debugger("You need a valid BApplication object before interacting with "
2721			"the app_server");
2722		return;
2723	}
2724
2725	frame.left = roundf(frame.left);
2726	frame.top = roundf(frame.top);
2727	frame.right = roundf(frame.right);
2728	frame.bottom = roundf(frame.bottom);
2729
2730	fFrame = frame;
2731
2732	if (title == NULL)
2733		title = "";
2734
2735	fTitle = strdup(title);
2736
2737	_SetName(title);
2738
2739	fFeel = feel;
2740	fLook = look;
2741	fFlags = flags | B_ASYNCHRONOUS_CONTROLS;
2742
2743	fInTransaction = bitmapToken >= 0;
2744	fUpdateRequested = false;
2745	fActive = false;
2746	fShowLevel = 1;
2747
2748	fTopView = NULL;
2749	fFocus = NULL;
2750	fLastMouseMovedView	= NULL;
2751	fKeyMenuBar = NULL;
2752	fDefaultButton = NULL;
2753
2754	// Shortcut 'Q' is handled in _HandleKeyDown() directly, as its message
2755	// get sent to the application, and not one of our handlers.
2756	// It is only installed for non-modal windows, though.
2757	fNoQuitShortcut = IsModal();
2758
2759	if ((fFlags & B_NOT_CLOSABLE) == 0 && !IsModal()) {
2760		// Modal windows default to non-closable, but you can add the
2761		// shortcut manually, if a different behaviour is wanted
2762		AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED));
2763	}
2764
2765	// Edit modifier keys
2766
2767	AddShortcut('X', B_COMMAND_KEY, new BMessage(B_CUT), NULL);
2768	AddShortcut('C', B_COMMAND_KEY, new BMessage(B_COPY), NULL);
2769	AddShortcut('V', B_COMMAND_KEY, new BMessage(B_PASTE), NULL);
2770	AddShortcut('A', B_COMMAND_KEY, new BMessage(B_SELECT_ALL), NULL);
2771
2772	// Window modifier keys
2773
2774	AddShortcut('M', B_COMMAND_KEY | B_CONTROL_KEY,
2775		new BMessage(_MINIMIZE_), NULL);
2776	AddShortcut('Z', B_COMMAND_KEY | B_CONTROL_KEY,
2777		new BMessage(_ZOOM_), NULL);
2778	AddShortcut('Z', B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY,
2779		new BMessage(_ZOOM_), NULL);
2780	AddShortcut('H', B_COMMAND_KEY | B_CONTROL_KEY,
2781		new BMessage(B_HIDE_APPLICATION), NULL);
2782	AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY,
2783		new BMessage(_SEND_TO_FRONT_), NULL);
2784	AddShortcut('B', B_COMMAND_KEY | B_CONTROL_KEY,
2785		new BMessage(_SEND_BEHIND_), NULL);
2786
2787	// We set the default pulse rate, but we don't start the pulse
2788	fPulseRate = 500000;
2789	fPulseRunner = NULL;
2790
2791	fIsFilePanel = false;
2792
2793	fMenuSem = -1;
2794
2795	fMinimized = false;
2796
2797	fMaxZoomHeight = 32768.0;
2798	fMaxZoomWidth = 32768.0;
2799	fMinHeight = 0.0;
2800	fMinWidth = 0.0;
2801	fMaxHeight = 32768.0;
2802	fMaxWidth = 32768.0;
2803
2804	fLastViewToken = B_NULL_TOKEN;
2805
2806	// TODO: other initializations!
2807	fOffscreen = false;
2808
2809	// Create the server-side window
2810
2811	port_id receivePort = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY,
2812		"w<app_server");
2813	if (receivePort < B_OK) {
2814		// TODO: huh?
2815		debugger("Could not create BWindow's receive port, used for "
2816				 "interacting with the app_server!");
2817		delete this;
2818		return;
2819	}
2820
2821	STRACE(("BWindow::InitData(): contacting app_server...\n"));
2822
2823	// let app_server know that a window has been created.
2824	fLink = new(std::nothrow) BPrivate::PortLink(
2825		BApplication::Private::ServerLink()->SenderPort(), receivePort);
2826	if (fLink == NULL) {
2827		// Zombie!
2828		return;
2829	}
2830
2831	{
2832		BPrivate::AppServerLink lockLink;
2833			// we're talking to the server application using our own
2834			// communication channel (fLink) - we better make sure no one
2835			// interferes by locking that channel (which AppServerLink does
2836			// implicetly)
2837
2838		if (bitmapToken < 0) {
2839			fLink->StartMessage(AS_CREATE_WINDOW);
2840		} else {
2841			fLink->StartMessage(AS_CREATE_OFFSCREEN_WINDOW);
2842			fLink->Attach<int32>(bitmapToken);
2843			fOffscreen = true;
2844		}
2845
2846		fLink->Attach<BRect>(fFrame);
2847		fLink->Attach<uint32>((uint32)fLook);
2848		fLink->Attach<uint32>((uint32)fFeel);
2849		fLink->Attach<uint32>(fFlags);
2850		fLink->Attach<uint32>(workspace);
2851		fLink->Attach<int32>(_get_object_token_(this));
2852		fLink->Attach<port_id>(receivePort);
2853		fLink->Attach<port_id>(fMsgPort);
2854		fLink->AttachString(title);
2855
2856		port_id sendPort;
2857		int32 code;
2858		if (fLink->FlushWithReply(code) == B_OK
2859			&& code == B_OK
2860			&& fLink->Read<port_id>(&sendPort) == B_OK) {
2861			// read the frame size and its limits that were really
2862			// enforced on the server side
2863
2864			fLink->Read<BRect>(&fFrame);
2865			fLink->Read<float>(&fMinWidth);
2866			fLink->Read<float>(&fMaxWidth);
2867			fLink->Read<float>(&fMinHeight);
2868			fLink->Read<float>(&fMaxHeight);
2869
2870			fMaxZoomWidth = fMaxWidth;
2871			fMaxZoomHeight = fMaxHeight;
2872		} else
2873			sendPort = -1;
2874
2875		// Redirect our link to the new window connection
2876		fLink->SetSenderPort(sendPort);
2877		STRACE(("Server says that our send port is %ld\n", sendPort));
2878	}
2879
2880	STRACE(("Window locked?: %s\n", IsLocked() ? "True" : "False"));
2881
2882	_CreateTopView();
2883}
2884
2885
2886//! Rename the handler and its thread
2887void
2888BWindow::_SetName(const char* title)
2889{
2890	if (title == NULL)
2891		title = "";
2892
2893	// we will change BWindow's thread name to "w>window title"
2894
2895	char threadName[B_OS_NAME_LENGTH];
2896	strcpy(threadName, "w>");
2897#ifdef __HAIKU__
2898	strlcat(threadName, title, B_OS_NAME_LENGTH);
2899#else
2900	int32 length = strlen(title);
2901	length = min_c(length, B_OS_NAME_LENGTH - 3);
2902	memcpy(threadName + 2, title, length);
2903	threadName[length + 2] = '\0';
2904#endif
2905
2906	// change the handler's name
2907	SetName(threadName);
2908
2909	// if the message loop has been started...
2910	if (Thread() >= B_OK)
2911		rename_thread(Thread(), threadName);
2912}
2913
2914
2915//!	Reads all pending messages from the window port and put them into the queue.
2916void
2917BWindow::_DequeueAll()
2918{
2919	//	Get message count from port
2920	int32 count = port_count(fMsgPort);
2921
2922	for (int32 i = 0; i < count; i++) {
2923		BMessage* message = MessageFromPort(0);
2924		if (message != NULL)
2925			fDirectTarget->Queue()->AddMessage(message);
2926	}
2927}
2928
2929
2930/*!	This here is an almost complete code duplication to BLooper::task_looper()
2931	but with some important differences:
2932	 a)	it uses the _DetermineTarget() method to tell what the later target of
2933		a message will be, if no explicit target is supplied.
2934	 b)	it calls _UnpackMessage() and _SanitizeMessage() to duplicate the message
2935		to all of its intended targets, and to add all fields the target would
2936		expect in such a message.
2937
2938	This is important because the app_server sends all input events to the
2939	preferred handler, and expects them to be correctly distributed to their
2940	intended targets.
2941*/
2942void
2943BWindow::task_looper()
2944{
2945	STRACE(("info: BWindow::task_looper() started.\n"));
2946
2947	// Check that looper is locked (should be)
2948	AssertLocked();
2949	Unlock();
2950
2951	if (IsLocked())
2952		debugger("window must not be locked!");
2953
2954	while (!fTerminating) {
2955		// Did we get a message?
2956		BMessage* msg = MessageFromPort();
2957		if (msg)
2958			_AddMessagePriv(msg);
2959
2960		//	Get message count from port
2961		int32 msgCount = port_count(fMsgPort);
2962		for (int32 i = 0; i < msgCount; ++i) {
2963			// Read 'count' messages from port (so we will not block)
2964			// We use zero as our timeout since we know there is stuff there
2965			msg = MessageFromPort(0);
2966			// Add messages to queue
2967			if (msg)
2968				_AddMessagePriv(msg);
2969		}
2970
2971		bool dispatchNextMessage = true;
2972		while (!fTerminating && dispatchNextMessage) {
2973			// Get next message from queue (assign to fLastMessage after
2974			// locking)
2975			BMessage* message = fDirectTarget->Queue()->NextMessage();
2976
2977			// Lock the looper
2978			if (!Lock()) {
2979				delete message;
2980				break;
2981			}
2982
2983			fLastMessage = message;
2984
2985			if (fLastMessage == NULL) {
2986				// No more messages: Unlock the looper and terminate the
2987				// dispatch loop.
2988				dispatchNextMessage = false;
2989			} else {
2990				// Get the target handler
2991				BMessage::Private messagePrivate(fLastMessage);
2992				bool usePreferred = messagePrivate.UsePreferredTarget();
2993				BHandler* handler = NULL;
2994				bool dropMessage = false;
2995
2996				if (usePreferred) {
2997					handler = PreferredHandler();
2998					if (handler == NULL)
2999						handler = this;
3000				} else {
3001					gDefaultTokens.GetToken(messagePrivate.GetTarget(),
3002						B_HANDLER_TOKEN, (void**)&handler);
3003
3004					// if this handler doesn't belong to us, we drop the message
3005					if (handler != NULL && handler->Looper() != this) {
3006						dropMessage = true;
3007						handler = NULL;
3008					}
3009				}
3010
3011				if ((handler == NULL && !dropMessage) || usePreferred)
3012					handler = _DetermineTarget(fLastMessage, handler);
3013
3014				unpack_cookie cookie;
3015				while (_UnpackMessage(cookie, &fLastMessage, &handler, &usePreferred)) {
3016					// if there is no target handler, the message is dropped
3017					if (handler != NULL) {
3018						_SanitizeMessage(fLastMessage, handler, usePreferred);
3019
3020						// Is this a scripting message?
3021						if (fLastMessage->HasSpecifiers()) {
3022							int32 index = 0;
3023							// Make sure the current specifier is kosher
3024							if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
3025								handler = resolve_specifier(handler, fLastMessage);
3026						}
3027
3028						if (handler != NULL)
3029							handler = _TopLevelFilter(fLastMessage, handler);
3030
3031						if (handler != NULL)
3032							DispatchMessage(fLastMessage, handler);
3033					}
3034
3035					// Delete the current message
3036					delete fLastMessage;
3037					fLastMessage = NULL;
3038				}
3039			}
3040
3041			if (fTerminating) {
3042				// we leave the looper locked when we quit
3043				return;
3044			}
3045
3046			Unlock();
3047
3048			// Are any messages on the port?
3049			if (port_count(fMsgPort) > 0) {
3050				// Do outer loop
3051				dispatchNextMessage = false;
3052			}
3053		}
3054	}
3055}
3056
3057
3058window_type
3059BWindow::_ComposeType(window_look look, window_feel feel) const
3060{
3061	switch (feel) {
3062		case B_NORMAL_WINDOW_FEEL:
3063			switch (look) {
3064				case B_TITLED_WINDOW_LOOK:
3065					return B_TITLED_WINDOW;
3066
3067				case B_DOCUMENT_WINDOW_LOOK:
3068					return B_DOCUMENT_WINDOW;
3069
3070				case B_BORDERED_WINDOW_LOOK:
3071					return B_BORDERED_WINDOW;
3072
3073				default:
3074					return B_UNTYPED_WINDOW;
3075			}
3076			break;
3077
3078		case B_MODAL_APP_WINDOW_FEEL:
3079			if (look == B_MODAL_WINDOW_LOOK)
3080				return B_MODAL_WINDOW;
3081			break;
3082
3083		case B_FLOATING_APP_WINDOW_FEEL:
3084			if (look == B_FLOATING_WINDOW_LOOK)
3085				return B_FLOATING_WINDOW;
3086			break;
3087
3088		default:
3089			return B_UNTYPED_WINDOW;
3090	}
3091
3092	return B_UNTYPED_WINDOW;
3093}
3094
3095
3096void
3097BWindow::_DecomposeType(window_type type, window_look* _look,
3098	window_feel* _feel) const
3099{
3100	switch (type) {
3101		case B_DOCUMENT_WINDOW:
3102			*_look = B_DOCUMENT_WINDOW_LOOK;
3103			*_feel = B_NORMAL_WINDOW_FEEL;
3104			break;
3105
3106		case B_MODAL_WINDOW:
3107			*_look = B_MODAL_WINDOW_LOOK;
3108			*_feel = B_MODAL_APP_WINDOW_FEEL;
3109			break;
3110
3111		case B_FLOATING_WINDOW:
3112			*_look = B_FLOATING_WINDOW_LOOK;
3113			*_feel = B_FLOATING_APP_WINDOW_FEEL;
3114			break;
3115
3116		case B_BORDERED_WINDOW:
3117			*_look = B_BORDERED_WINDOW_LOOK;
3118			*_feel = B_NORMAL_WINDOW_FEEL;
3119			break;
3120
3121		case B_TITLED_WINDOW:
3122		case B_UNTYPED_WINDOW:
3123		default:
3124			*_look = B_TITLED_WINDOW_LOOK;
3125			*_feel = B_NORMAL_WINDOW_FEEL;
3126			break;
3127	}
3128}
3129
3130
3131void
3132BWindow::_CreateTopView()
3133{
3134	STRACE(("_CreateTopView(): enter\n"));
3135
3136	BRect frame = fFrame.OffsetToCopy(B_ORIGIN);
3137	// TODO: what to do here about std::nothrow?
3138	fTopView = new BView(frame, "fTopView", B_FOLLOW_ALL, B_WILL_DRAW);
3139	fTopView->fTopLevelView = true;
3140
3141	//inhibit check_lock()
3142	fLastViewToken = _get_object_token_(fTopView);
3143
3144	// set fTopView's owner, add it to window's eligible handler list
3145	// and also set its next handler to be this window.
3146
3147	STRACE(("Calling setowner fTopView = %p this = %p.\n",
3148		fTopView, this));
3149
3150	fTopView->_SetOwner(this);
3151
3152	// we can't use AddChild() because this is the top view
3153	fTopView->_CreateSelf();
3154	STRACE(("BuildTopView ended\n"));
3155}
3156
3157
3158/*!
3159	Resizes the top view to match the window size. This will also
3160	adapt the size of all its child views as needed.
3161	This method has to be called whenever the frame of the window
3162	changes.
3163*/
3164void
3165BWindow::_AdoptResize()
3166{
3167	// Resize views according to their resize modes - this
3168	// saves us some server communication, as the server
3169	// does the same with our views on its side.
3170
3171	int32 deltaWidth = (int32)(fFrame.Width() - fTopView->Bounds().Width());
3172	int32 deltaHeight = (int32)(fFrame.Height() - fTopView->Bounds().Height());
3173	if (deltaWidth == 0 && deltaHeight == 0)
3174		return;
3175
3176	fTopView->_ResizeBy(deltaWidth, deltaHeight);
3177}
3178
3179
3180void
3181BWindow::_SetFocus(BView* focusView, bool notifyInputServer)
3182{
3183	if (fFocus == focusView)
3184		return;
3185
3186	// we notify the input server if we are passing focus
3187	// from a view which has the B_INPUT_METHOD_AWARE to a one
3188	// which does not, or vice-versa
3189	if (notifyInputServer && fActive) {
3190		bool inputMethodAware = false;
3191		if (focusView)
3192			inputMethodAware = focusView->Flags() & B_INPUT_METHOD_AWARE;
3193		BMessage msg(inputMethodAware ? IS_FOCUS_IM_AWARE_VIEW : IS_UNFOCUS_IM_AWARE_VIEW);
3194		BMessenger messenger(focusView);
3195		BMessage reply;
3196		if (focusView)
3197			msg.AddMessenger("view", messenger);
3198		_control_input_server_(&msg, &reply);
3199	}
3200
3201	fFocus = focusView;
3202	SetPreferredHandler(focusView);
3203}
3204
3205
3206/*!
3207	\brief Determines the target of a message received for the
3208		focus view.
3209*/
3210BHandler*
3211BWindow::_DetermineTarget(BMessage* message, BHandler* target)
3212{
3213	if (target == NULL)
3214		target = this;
3215
3216	switch (message->what) {
3217		case B_KEY_DOWN:
3218		case B_KEY_UP:
3219		{
3220			// if we have a default button, it might want to hear
3221			// about pressing the <enter> key
3222			const int32 kNonLockModifierKeys = B_SHIFT_KEY | B_COMMAND_KEY
3223				| B_CONTROL_KEY | B_OPTION_KEY | B_MENU_KEY;
3224			int32 rawChar;
3225			if (DefaultButton() != NULL
3226				&& message->FindInt32("raw_char", &rawChar) == B_OK
3227				&& rawChar == B_ENTER
3228				&& (modifiers() & kNonLockModifierKeys) == 0)
3229				return DefaultButton();
3230
3231			// supposed to fall through
3232		}
3233		case B_UNMAPPED_KEY_DOWN:
3234		case B_UNMAPPED_KEY_UP:
3235		case B_MODIFIERS_CHANGED:
3236			// these messages should be dispatched by the focus view
3237			if (CurrentFocus() != NULL)
3238				return CurrentFocus();
3239			break;
3240
3241		case B_MOUSE_DOWN:
3242		case B_MOUSE_UP:
3243		case B_MOUSE_MOVED:
3244		case B_MOUSE_WHEEL_CHANGED:
3245		case B_MOUSE_IDLE:
3246			// is there a token of the view that is currently under the mouse?
3247			int32 token;
3248			if (message->FindInt32("_view_token", &token) == B_OK) {
3249				BView* view = _FindView(token);
3250				if (view != NULL)
3251					return view;
3252			}
3253
3254			// if there is no valid token in the message, we try our
3255			// luck with the last target, if available
3256			if (fLastMouseMovedView != NULL)
3257				return fLastMouseMovedView;
3258			break;
3259
3260		case B_PULSE:
3261		case B_QUIT_REQUESTED:
3262			// TODO: test whether R5 will let BView dispatch these messages
3263			return this;
3264
3265		case _MESSAGE_DROPPED_:
3266			if (fLastMouseMovedView != NULL)
3267				return fLastMouseMovedView;
3268			break;
3269
3270		default:
3271			break;
3272	}
3273
3274	return target;
3275}
3276
3277
3278/*!	\brief Determines whether or not this message has targeted the focus view.
3279
3280	This will return \c false only if the message did not go to the preferred
3281	handler, or if the packed message does not contain address the focus view
3282	at all.
3283*/
3284bool
3285BWindow::_IsFocusMessage(BMessage* message)
3286{
3287	BMessage::Private messagePrivate(message);
3288	if (!messagePrivate.UsePreferredTarget())
3289		return false;
3290
3291	bool feedFocus;
3292	if (message->HasInt32("_token")
3293		&& (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus))
3294		return false;
3295
3296	return true;
3297}
3298
3299
3300/*!	\brief Distributes the message to its intended targets. This is done for
3301		all messages that should go to the preferred handler.
3302
3303	Returns \c true in case the message should still be dispatched
3304*/
3305bool
3306BWindow::_UnpackMessage(unpack_cookie& cookie, BMessage** _message,
3307	BHandler** _target, bool* _usePreferred)
3308{
3309	if (cookie.message == NULL)
3310		return false;
3311
3312	if (cookie.index == 0 && !cookie.tokens_scanned) {
3313		// We were called the first time for this message
3314
3315		if (!*_usePreferred) {
3316			// only consider messages targeted at the preferred handler
3317			cookie.message = NULL;
3318			return true;
3319		}
3320
3321		// initialize our cookie
3322		cookie.message = *_message;
3323		cookie.focus = *_target;
3324
3325		if (cookie.focus != NULL)
3326			cookie.focus_token = _get_object_token_(*_target);
3327
3328		if (fLastMouseMovedView != NULL && cookie.message->what == B_MOUSE_MOVED)
3329			cookie.last_view_token = _get_object_token_(fLastMouseMovedView);
3330
3331		*_usePreferred = false;
3332	}
3333
3334	_DequeueAll();
3335
3336	// distribute the message to all targets specified in the
3337	// message directly (but not to the focus view)
3338
3339	for (int32 token; !cookie.tokens_scanned
3340			&& cookie.message->FindInt32("_token", cookie.index, &token)
3341				== B_OK;
3342			cookie.index++) {
3343		// focus view is preferred and should get its message directly
3344		if (token == cookie.focus_token) {
3345			cookie.found_focus = true;
3346			continue;
3347		}
3348		if (token == cookie.last_view_token)
3349			continue;
3350
3351		BView* target = _FindView(token);
3352		if (target == NULL)
3353			continue;
3354
3355		*_message = new BMessage(*cookie.message);
3356		// the secondary copies of the message should not be treated as focus
3357		// messages, otherwise there will be unintended side effects, i.e.
3358		// keyboard shortcuts getting processed multiple times.
3359		(*_message)->RemoveName("_feed_focus");
3360		*_target = target;
3361		cookie.index++;
3362		return true;
3363	}
3364
3365	cookie.tokens_scanned = true;
3366
3367	// if there is a last mouse moved view, and the new focus is
3368	// different, the previous view wants to get its B_EXITED_VIEW
3369	// message
3370	if (cookie.last_view_token != B_NULL_TOKEN && fLastMouseMovedView != NULL
3371		&& fLastMouseMovedView != cookie.focus) {
3372		*_message = new BMessage(*cookie.message);
3373		*_target = fLastMouseMovedView;
3374		cookie.last_view_token = B_NULL_TOKEN;
3375		return true;
3376	}
3377
3378	bool dispatchToFocus = true;
3379
3380	// check if the focus token is still valid (could have been removed in the mean time)
3381	BHandler* handler;
3382	if (gDefaultTokens.GetToken(cookie.focus_token, B_HANDLER_TOKEN, (void**)&handler) != B_OK
3383		|| handler->Looper() != this)
3384		dispatchToFocus = false;
3385
3386	if (dispatchToFocus && cookie.index > 0) {
3387		// should this message still be dispatched by the focus view?
3388		bool feedFocus;
3389		if (!cookie.found_focus
3390			&& (cookie.message->FindBool("_feed_focus", &feedFocus) != B_OK
3391				|| feedFocus == false))
3392			dispatchToFocus = false;
3393	}
3394
3395	if (!dispatchToFocus) {
3396		delete cookie.message;
3397		cookie.message = NULL;
3398		return false;
3399	}
3400
3401	*_message = cookie.message;
3402	*_target = cookie.focus;
3403	*_usePreferred = true;
3404	cookie.message = NULL;
3405	return true;
3406}
3407
3408
3409/*!	Some messages don't get to the window in a shape an application should see.
3410	This method is supposed to give a message the last grinding before
3411	it's acceptable for the receiving application.
3412*/
3413void
3414BWindow::_SanitizeMessage(BMessage* message, BHandler* target, bool usePreferred)
3415{
3416	if (target == NULL)
3417		return;
3418
3419	switch (message->what) {
3420		case B_MOUSE_MOVED:
3421		case B_MOUSE_UP:
3422		case B_MOUSE_DOWN:
3423		{
3424			BPoint where;
3425			if (message->FindPoint("screen_where", &where) != B_OK)
3426				break;
3427
3428			BView* view = dynamic_cast<BView*>(target);
3429
3430			if (view == NULL || message->what == B_MOUSE_MOVED) {
3431				// add local window coordinates, only
3432				// for regular mouse moved messages
3433				message->AddPoint("where", ConvertFromScreen(where));
3434			}
3435
3436			if (view != NULL) {
3437				// add local view coordinates
3438				BPoint viewWhere = view->ConvertFromScreen(where);
3439				if (message->what != B_MOUSE_MOVED) {
3440					// Yep, the meaning of "where" is different
3441					// for regular mouse moved messages versus
3442					// mouse up/down!
3443					message->AddPoint("where", viewWhere);
3444				}
3445				message->AddPoint("be:view_where", viewWhere);
3446
3447				if (message->what == B_MOUSE_MOVED) {
3448					// is there a token of the view that is currently under
3449					// the mouse?
3450					BView* viewUnderMouse = NULL;
3451					int32 token;
3452					if (message->FindInt32("_view_token", &token) == B_OK)
3453						viewUnderMouse = _FindView(token);
3454
3455					// add transit information
3456					uint32 transit
3457						= _TransitForMouseMoved(view, viewUnderMouse);
3458					message->AddInt32("be:transit", transit);
3459
3460					if (usePreferred)
3461						fLastMouseMovedView = viewUnderMouse;
3462				}
3463			}
3464			break;
3465		}
3466
3467		case B_MOUSE_IDLE:
3468		{
3469			// App Server sends screen coordinates, convert the point to
3470			// local view coordinates, then add the point in be:view_where
3471			BPoint where;
3472			if (message->FindPoint("screen_where", &where) != B_OK)
3473				break;
3474
3475			BView* view = dynamic_cast<BView*>(target);
3476			if (view != NULL) {
3477				// add local view coordinates
3478				message->AddPoint("be:view_where",
3479					view->ConvertFromScreen(where));
3480			}
3481			break;
3482		}
3483
3484		case _MESSAGE_DROPPED_:
3485		{
3486			uint32 originalWhat;
3487			if (message->FindInt32("_original_what",
3488					(int32*)&originalWhat) == B_OK) {
3489				message->what = originalWhat;
3490				message->RemoveName("_original_what");
3491			}
3492			break;
3493		}
3494	}
3495}
3496
3497
3498/*!
3499	This is called by BView::GetMouse() when a B_MOUSE_MOVED message
3500	is removed from the queue.
3501	It allows the window to update the last mouse moved view, and
3502	let it decide if this message should be kept. It will also remove
3503	the message from the queue.
3504	You need to hold the message queue lock when calling this method!
3505
3506	\return true if this message can be used to get the mouse data from,
3507	\return false if this is not meant for the public.
3508*/
3509bool
3510BWindow::_StealMouseMessage(BMessage* message, bool& deleteMessage)
3511{
3512	BMessage::Private messagePrivate(message);
3513	if (!messagePrivate.UsePreferredTarget()) {
3514		// this message is targeted at a specific handler, so we should
3515		// not steal it
3516		return false;
3517	}
3518
3519	int32 token;
3520	if (message->FindInt32("_token", 0, &token) == B_OK) {
3521		// This message has other targets, so we can't remove it;
3522		// just prevent it from being sent to the preferred handler
3523		// again (if it should have gotten it at all).
3524		bool feedFocus;
3525		if (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus)
3526			return false;
3527
3528		message->RemoveName("_feed_focus");
3529		deleteMessage = false;
3530	} else {
3531		deleteMessage = true;
3532
3533		if (message->what == B_MOUSE_MOVED) {
3534			// We need to update the last mouse moved view, as this message
3535			// won't make it to _SanitizeMessage() anymore.
3536			BView* viewUnderMouse = NULL;
3537			int32 token;
3538			if (message->FindInt32("_view_token", &token) == B_OK)
3539				viewUnderMouse = _FindView(token);
3540
3541			// Don't remove important transit messages!
3542			uint32 transit = _TransitForMouseMoved(fLastMouseMovedView,
3543				viewUnderMouse);
3544			if (transit == B_ENTERED_VIEW || transit == B_EXITED_VIEW)
3545				deleteMessage = false;
3546		}
3547
3548		if (deleteMessage) {
3549			// The message is only thought for the preferred handler, so we
3550			// can just remove it.
3551			MessageQueue()->RemoveMessage(message);
3552		}
3553	}
3554
3555	return true;
3556}
3557
3558
3559uint32
3560BWindow::_TransitForMouseMoved(BView* view, BView* viewUnderMouse) const
3561{
3562	uint32 transit;
3563	if (viewUnderMouse == view) {
3564		// the mouse is over the target view
3565		if (fLastMouseMovedView != view)
3566			transit = B_ENTERED_VIEW;
3567		else
3568			transit = B_INSIDE_VIEW;
3569	} else {
3570		// the mouse is not over the target view
3571		if (view == fLastMouseMovedView)
3572			transit = B_EXITED_VIEW;
3573		else
3574			transit = B_OUTSIDE_VIEW;
3575	}
3576	return transit;
3577}
3578
3579
3580/*!	Forwards the key to the switcher
3581*/
3582void
3583BWindow::_Switcher(int32 rawKey, uint32 modifiers, bool repeat)
3584{
3585	// only send the first key press, no repeats
3586	if (repeat)
3587		return;
3588
3589	BMessenger deskbar(kDeskbarSignature);
3590	if (!deskbar.IsValid()) {
3591		// TODO: have some kind of fallback-handling in case the Deskbar is
3592		// not available?
3593		return;
3594	}
3595
3596	BMessage message('TASK');
3597	message.AddInt32("key", rawKey);
3598	message.AddInt32("modifiers", modifiers);
3599	message.AddInt64("when", system_time());
3600	message.AddInt32("team", Team());
3601	deskbar.SendMessage(&message);
3602}
3603
3604
3605/*!	Handles keyboard input before it gets forwarded to the target handler.
3606	This includes shortcut evaluation, keyboard navigation, etc.
3607
3608	\return handled if true, the event was already handled, and will not
3609		be forwarded to the target handler.
3610
3611	TODO: must also convert the incoming key to the font encoding of the target
3612*/
3613bool
3614BWindow::_HandleKeyDown(BMessage* event)
3615{
3616	// Only handle special functions when the event targeted the active focus
3617	// view
3618	if (!_IsFocusMessage(event))
3619		return false;
3620
3621	const char* bytes = NULL;
3622	if (event->FindString("bytes", &bytes) != B_OK)
3623		return false;
3624
3625	char key = bytes[0];
3626
3627	uint32 modifiers;
3628	if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK)
3629		modifiers = 0;
3630
3631	// handle BMenuBar key
3632	if (key == B_ESCAPE && (modifiers & B_COMMAND_KEY) != 0
3633		&& fKeyMenuBar != NULL) {
3634		fKeyMenuBar->StartMenuBar(0, true, false, NULL);
3635		return true;
3636	}
3637
3638	// Keyboard navigation through views
3639	// (B_OPTION_KEY makes BTextViews and friends navigable, even in editing
3640	// mode)
3641	if (key == B_TAB && (modifiers & B_OPTION_KEY) != 0) {
3642		_KeyboardNavigation();
3643		return true;
3644	}
3645
3646	int32 rawKey;
3647	event->FindInt32("key", &rawKey);
3648
3649	// Deskbar's Switcher
3650	if ((key == B_TAB || rawKey == 0x11) && (modifiers & B_CONTROL_KEY) != 0) {
3651		_Switcher(rawKey, modifiers, event->HasInt32("be:key_repeat"));
3652		return true;
3653	}
3654
3655	// Optionally close window when the escape key is pressed
3656	if (key == B_ESCAPE && (Flags() & B_CLOSE_ON_ESCAPE) != 0) {
3657		BMessage message(B_QUIT_REQUESTED);
3658		message.AddBool("shortcut", true);
3659
3660		PostMessage(&message);
3661		return true;
3662	}
3663
3664	// PrtScr key takes a screenshot
3665	if (key == B_FUNCTION_KEY && rawKey == B_PRINT_KEY) {
3666		// With no modifier keys the best way to get a screenshot is by
3667		// calling the screenshot CLI
3668		if (modifiers == 0) {
3669			be_roster->Launch("application/x-vnd.haiku-screenshot-cli");
3670			return true;
3671		}
3672
3673		// Prepare a message based on the modifier keys pressed and launch the
3674		// screenshot GUI
3675		BMessage message(B_ARGV_RECEIVED);
3676		int32 argc = 1;
3677		message.AddString("argv", "Screenshot");
3678		if ((modifiers & B_CONTROL_KEY) != 0) {
3679			argc++;
3680			message.AddString("argv", "--clipboard");
3681		}
3682		if ((modifiers & B_SHIFT_KEY) != 0) {
3683			argc++;
3684			message.AddString("argv", "--silent");
3685		}
3686		message.AddInt32("argc", argc);
3687		be_roster->Launch("application/x-vnd.haiku-screenshot", &message);
3688		return true;
3689	}
3690
3691	// Handle shortcuts
3692	if ((modifiers & B_COMMAND_KEY) != 0) {
3693		// Command+q has been pressed, so, we will quit
3694		// the shortcut mechanism doesn't allow handlers outside the window
3695		if (!fNoQuitShortcut && (key == 'Q' || key == 'q')) {
3696			BMessage message(B_QUIT_REQUESTED);
3697			message.AddBool("shortcut", true);
3698
3699			be_app->PostMessage(&message);
3700			// eat the event
3701			return true;
3702		}
3703
3704		// Send Command+Left and Command+Right to textview if it has focus
3705		if (key == B_LEFT_ARROW || key == B_RIGHT_ARROW) {
3706			// check key before doing expensive dynamic_cast
3707			BTextView* textView = dynamic_cast<BTextView*>(CurrentFocus());
3708			if (textView != NULL) {
3709				textView->KeyDown(bytes, modifiers);
3710				// eat the event
3711				return true;
3712			}
3713		}
3714
3715		// Pretend that the user opened a menu, to give the subclass a
3716		// chance to update it's menus. This may install new shortcuts,
3717		// which is why we have to call it here, before trying to find
3718		// a shortcut for the given key.
3719		MenusBeginning();
3720
3721		Shortcut* shortcut = _FindShortcut(key, modifiers);
3722		if (shortcut != NULL) {
3723			// TODO: would be nice to move this functionality to
3724			//	a Shortcut::Invoke() method - but since BMenu::InvokeItem()
3725			//	(and BMenuItem::Invoke()) are private, I didn't want
3726			//	to mess with them (BMenuItem::Invoke() is public in
3727			//	Dano/Zeta, though, maybe we should just follow their
3728			//	example)
3729			if (shortcut->MenuItem() != NULL) {
3730				BMenu* menu = shortcut->MenuItem()->Menu();
3731				if (menu != NULL)
3732					MenuPrivate(menu).InvokeItem(shortcut->MenuItem(), true);
3733			} else {
3734				BHandler* target = shortcut->Target();
3735				if (target == NULL)
3736					target = CurrentFocus();
3737
3738				if (shortcut->Message() != NULL) {
3739					BMessage message(*shortcut->Message());
3740
3741					if (message.ReplaceInt64("when", system_time()) != B_OK)
3742						message.AddInt64("when", system_time());
3743					if (message.ReplaceBool("shortcut", true) != B_OK)
3744						message.AddBool("shortcut", true);
3745
3746					PostMessage(&message, target);
3747				}
3748			}
3749		}
3750
3751		MenusEnded();
3752
3753		// we always eat the event if the command key was pressed
3754		return true;
3755	}
3756
3757	// TODO: convert keys to the encoding of the target view
3758
3759	return false;
3760}
3761
3762
3763bool
3764BWindow::_HandleUnmappedKeyDown(BMessage* event)
3765{
3766	// Only handle special functions when the event targeted the active focus
3767	// view
3768	if (!_IsFocusMessage(event))
3769		return false;
3770
3771	uint32 modifiers;
3772	int32 rawKey;
3773	if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK
3774		|| event->FindInt32("key", &rawKey))
3775		return false;
3776
3777	// Deskbar's Switcher
3778	if (rawKey == 0x11 && (modifiers & B_CONTROL_KEY) != 0) {
3779		_Switcher(rawKey, modifiers, event->HasInt32("be:key_repeat"));
3780		return true;
3781	}
3782
3783	return false;
3784}
3785
3786
3787void
3788BWindow::_KeyboardNavigation()
3789{
3790	BMessage* message = CurrentMessage();
3791	if (message == NULL)
3792		return;
3793
3794	const char* bytes;
3795	uint32 modifiers;
3796	if (message->FindString("bytes", &bytes) != B_OK || bytes[0] != B_TAB)
3797		return;
3798
3799	message->FindInt32("modifiers", (int32*)&modifiers);
3800
3801	BView* nextFocus;
3802	int32 jumpGroups = (modifiers & B_OPTION_KEY) != 0
3803		? B_NAVIGABLE_JUMP : B_NAVIGABLE;
3804	if (modifiers & B_SHIFT_KEY)
3805		nextFocus = _FindPreviousNavigable(fFocus, jumpGroups);
3806	else
3807		nextFocus = _FindNextNavigable(fFocus, jumpGroups);
3808
3809	if (nextFocus != NULL && nextFocus != fFocus)
3810		nextFocus->MakeFocus(true);
3811}
3812
3813
3814/*!
3815	\brief Return the position of the window centered horizontally to the passed
3816           in \a frame and vertically 3/4 from the top of \a frame.
3817
3818	If the window is on the borders
3819
3820	\param width The width of the window.
3821	\param height The height of the window.
3822	\param frame The \a frame to center the window in.
3823
3824	\return The new window position.
3825*/
3826BPoint
3827BWindow::AlertPosition(const BRect& frame)
3828{
3829	float width = Bounds().Width();
3830	float height = Bounds().Height();
3831
3832	BPoint point(frame.left + (frame.Width() / 2.0f) - (width / 2.0f),
3833		frame.top + (frame.Height() / 4.0f) - ceil(height / 3.0f));
3834
3835	BRect screenFrame = BScreen(this).Frame();
3836	if (frame == screenFrame) {
3837		// reference frame is screen frame, skip the below adjustments
3838		return point;
3839	}
3840
3841	float borderWidth;
3842	float tabHeight;
3843	_GetDecoratorSize(&borderWidth, &tabHeight);
3844
3845	// clip the x position within the horizontal edges of the screen
3846	if (point.x < screenFrame.left + borderWidth)
3847		point.x = screenFrame.left + borderWidth;
3848	else if (point.x + width > screenFrame.right - borderWidth)
3849		point.x = screenFrame.right - borderWidth - width;
3850
3851	// lower the window down if it is covering the window tab
3852	float tabPosition = frame.LeftTop().y + tabHeight + borderWidth;
3853	if (point.y < tabPosition)
3854		point.y = tabPosition;
3855
3856	// clip the y position within the vertical edges of the screen
3857	if (point.y < screenFrame.top + borderWidth)
3858		point.y = screenFrame.top + borderWidth;
3859	else if (point.y + height > screenFrame.bottom - borderWidth)
3860		point.y = screenFrame.bottom - borderWidth - height;
3861
3862	return point;
3863}
3864
3865
3866BMessage*
3867BWindow::ConvertToMessage(void* raw, int32 code)
3868{
3869	return BLooper::ConvertToMessage(raw, code);
3870}
3871
3872
3873BWindow::Shortcut*
3874BWindow::_FindShortcut(uint32 key, uint32 modifiers)
3875{
3876	int32 count = fShortcuts.CountItems();
3877
3878	key = Shortcut::PrepareKey(key);
3879	modifiers = Shortcut::PrepareModifiers(modifiers);
3880
3881	for (int32 index = 0; index < count; index++) {
3882		Shortcut* shortcut = (Shortcut*)fShortcuts.ItemAt(index);
3883
3884		if (shortcut->Matches(key, modifiers))
3885			return shortcut;
3886	}
3887
3888	return NULL;
3889}
3890
3891
3892BView*
3893BWindow::_FindView(int32 token)
3894{
3895	BHandler* handler;
3896	if (gDefaultTokens.GetToken(token, B_HANDLER_TOKEN,
3897			(void**)&handler) != B_OK) {
3898		return NULL;
3899	}
3900
3901	// the view must belong to us in order to be found by this method
3902	BView* view = dynamic_cast<BView*>(handler);
3903	if (view != NULL && view->Window() == this)
3904		return view;
3905
3906	return NULL;
3907}
3908
3909
3910BView*
3911BWindow::_FindView(BView* view, BPoint point) const
3912{
3913	// point is assumed to be already in view's coordinates
3914	if (!view->IsHidden(view) && view->Bounds().Contains(point)) {
3915		if (view->fFirstChild == NULL)
3916			return view;
3917		else {
3918			BView* child = view->fFirstChild;
3919			while (child != NULL) {
3920				BPoint childPoint = point - child->Frame().LeftTop();
3921				BView* subView  = _FindView(child, childPoint);
3922				if (subView != NULL)
3923					return subView;
3924
3925				child = child->fNextSibling;
3926			}
3927		}
3928		return view;
3929	}
3930	return NULL;
3931}
3932
3933
3934BView*
3935BWindow::_FindNextNavigable(BView* focus, uint32 flags)
3936{
3937	if (focus == NULL)
3938		focus = fTopView;
3939
3940	BView* nextFocus = focus;
3941
3942	// Search the tree for views that accept focus (depth search)
3943	while (true) {
3944		if (nextFocus->fFirstChild)
3945			nextFocus = nextFocus->fFirstChild;
3946		else if (nextFocus->fNextSibling)
3947			nextFocus = nextFocus->fNextSibling;
3948		else {
3949			// go to the nearest parent with a next sibling
3950			while (!nextFocus->fNextSibling && nextFocus->fParent) {
3951				nextFocus = nextFocus->fParent;
3952			}
3953
3954			if (nextFocus == fTopView) {
3955				// if we started with the top view, we traversed the whole tree already
3956				if (nextFocus == focus)
3957					return NULL;
3958
3959				nextFocus = nextFocus->fFirstChild;
3960			} else
3961				nextFocus = nextFocus->fNextSibling;
3962		}
3963
3964		if (nextFocus == focus || nextFocus == NULL) {
3965			// When we get here it means that the hole tree has been
3966			// searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
3967			return NULL;
3968		}
3969
3970		if (!nextFocus->IsHidden() && (nextFocus->Flags() & flags) != 0)
3971			return nextFocus;
3972	}
3973}
3974
3975
3976BView*
3977BWindow::_FindPreviousNavigable(BView* focus, uint32 flags)
3978{
3979	if (focus == NULL)
3980		focus = fTopView;
3981
3982	BView* previousFocus = focus;
3983
3984	// Search the tree for the previous view that accept focus
3985	while (true) {
3986		if (previousFocus->fPreviousSibling) {
3987			// find the last child in the previous sibling
3988			previousFocus = _LastViewChild(previousFocus->fPreviousSibling);
3989		} else {
3990			previousFocus = previousFocus->fParent;
3991			if (previousFocus == fTopView)
3992				previousFocus = _LastViewChild(fTopView);
3993		}
3994
3995		if (previousFocus == focus || previousFocus == NULL) {
3996			// When we get here it means that the hole tree has been
3997			// searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
3998			return NULL;
3999		}
4000
4001		if (!previousFocus->IsHidden() && (previousFocus->Flags() & flags) != 0)
4002			return previousFocus;
4003	}
4004}
4005
4006
4007/*!
4008	Returns the last child in a view hierarchy.
4009	Needed only by _FindPreviousNavigable().
4010*/
4011BView*
4012BWindow::_LastViewChild(BView* parent)
4013{
4014	while (true) {
4015		BView* last = parent->fFirstChild;
4016		if (last == NULL)
4017			return parent;
4018
4019		while (last->fNextSibling) {
4020			last = last->fNextSibling;
4021		}
4022
4023		parent = last;
4024	}
4025}
4026
4027
4028void
4029BWindow::SetIsFilePanel(bool isFilePanel)
4030{
4031	fIsFilePanel = isFilePanel;
4032}
4033
4034
4035bool
4036BWindow::IsFilePanel() const
4037{
4038	return fIsFilePanel;
4039}
4040
4041
4042void
4043BWindow::_GetDecoratorSize(float* _borderWidth, float* _tabHeight) const
4044{
4045	// fallback in case retrieving the decorator settings fails
4046	// (highly unlikely)
4047	float borderWidth = 5.0;
4048	float tabHeight = 21.0;
4049
4050	BMessage settings;
4051	if (GetDecoratorSettings(&settings) == B_OK) {
4052		BRect tabRect;
4053		if (settings.FindRect("tab frame", &tabRect) == B_OK)
4054			tabHeight = tabRect.Height();
4055		settings.FindFloat("border width", &borderWidth);
4056	} else {
4057		// probably no-border window look
4058		if (fLook == B_NO_BORDER_WINDOW_LOOK) {
4059			borderWidth = 0.0;
4060			tabHeight = 0.0;
4061		}
4062		// else use fall-back values from above
4063	}
4064
4065	if (_borderWidth != NULL)
4066		*_borderWidth = borderWidth;
4067	if (_tabHeight != NULL)
4068		*_tabHeight = tabHeight;
4069}
4070
4071
4072void
4073BWindow::_SendShowOrHideMessage()
4074{
4075	fLink->StartMessage(AS_SHOW_OR_HIDE_WINDOW);
4076	fLink->Attach<int32>(fShowLevel);
4077	fLink->Flush();
4078}
4079
4080
4081//	#pragma mark - C++ binary compatibility kludge
4082
4083
4084extern "C" void
4085_ReservedWindow1__7BWindow(BWindow* window, BLayout* layout)
4086{
4087	// SetLayout()
4088	perform_data_set_layout data;
4089	data.layout = layout;
4090	window->Perform(PERFORM_CODE_SET_LAYOUT, &data);
4091}
4092
4093
4094void BWindow::_ReservedWindow2() {}
4095void BWindow::_ReservedWindow3() {}
4096void BWindow::_ReservedWindow4() {}
4097void BWindow::_ReservedWindow5() {}
4098void BWindow::_ReservedWindow6() {}
4099void BWindow::_ReservedWindow7() {}
4100void BWindow::_ReservedWindow8() {}
4101
4102