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