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