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