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