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