xref: /haiku/src/kits/interface/Window.cpp (revision 0c93c0a807b27096abbfad677436afb7d1712d4a)
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_KEY_DOWN:
966 		{
967 			if (!_HandleKeyDown(msg)) {
968 				if (BView* view = dynamic_cast<BView*>(target)) {
969 					// TODO: cannot use "string" here if we support having different
970 					//	font encoding per view (it's supposed to be converted by
971 					//	_HandleKeyDown() one day)
972 					const char *string = NULL;
973 					if (msg->FindString("bytes", &string) == B_OK)
974 						view->KeyDown(string, strlen(string));
975 				} else
976 					target->MessageReceived(msg);
977 			}
978 			break;
979 		}
980 
981 		case B_KEY_UP:
982 		{
983 			const char *string = NULL;
984 			msg->FindString("bytes", &string);
985 
986 			// TODO: same as above
987 			if (BView* view = dynamic_cast<BView*>(target))
988 				view->KeyUp(string, strlen(string));
989 			else
990 				target->MessageReceived(msg);
991 			break;
992 		}
993 
994 		case B_MOUSE_DOWN:
995 		{
996 			BView *view = dynamic_cast<BView *>(target);
997 
998 			// Close an eventually opened menu, unless the target is the menu itself
999 			BMenu *menu = dynamic_cast<BMenu *>(fFocus);
1000 			if (menu != NULL && menu != view)
1001 				menu->QuitTracking();
1002 
1003 			if (view != NULL) {
1004 				BPoint where;
1005 				msg->FindPoint("be:view_where", &where);
1006 				view->MouseDown(where);
1007 			} else
1008 				target->MessageReceived(msg);
1009 
1010 			break;
1011 		}
1012 
1013 		case B_MOUSE_UP:
1014 		{
1015 			if (BView *view = dynamic_cast<BView *>(target)) {
1016 				BPoint where;
1017 				msg->FindPoint("be:view_where", &where);
1018 				view->MouseUp(where);
1019 			} else
1020 				target->MessageReceived(msg);
1021 
1022 			break;
1023 		}
1024 
1025 		case B_MOUSE_MOVED:
1026 		{
1027 			if (BView *view = dynamic_cast<BView *>(target)) {
1028 				BPoint where;
1029 				uint32 buttons;
1030 				uint32 transit;
1031 				msg->FindPoint("be:view_where", &where);
1032 				msg->FindInt32("buttons", (int32*)&buttons);
1033 				msg->FindInt32("be:transit", (int32*)&transit);
1034 
1035 #if 0
1036 				bigtime_t when;
1037 				if (msg->FindInt64("when", (int64*)&when) < B_OK)
1038 					printf("BWindow B_MOUSE_MOVED no when\n");
1039 				else if (system_time() - when > 5000)
1040 					printf("BWindow B_MOUSE_MOVED lagging behind\n");
1041 #endif
1042 
1043 				BMessage* dragMessage = NULL;
1044 				if (msg->HasMessage("be:drag_message")) {
1045 					dragMessage = new BMessage();
1046 					if (msg->FindMessage("be:drag_message", dragMessage) != B_OK) {
1047 						delete dragMessage;
1048 						dragMessage = NULL;
1049 					}
1050 				}
1051 
1052 				view->MouseMoved(where, transit, dragMessage);
1053 				delete dragMessage;
1054 			} else
1055 				target->MessageReceived(msg);
1056 
1057 			break;
1058 		}
1059 
1060 		case B_PULSE:
1061 			if (target == this && fPulseRunner) {
1062 				fTopView->_Pulse();
1063 				fLink->Flush();
1064 			} else
1065 				target->MessageReceived(msg);
1066 			break;
1067 
1068 		case _UPDATE_:
1069 		{
1070 			//bigtime_t now = system_time();
1071 			STRACE(("info:BWindow handling _UPDATE_.\n"));
1072 			BRect updateRect;
1073 
1074 			fLink->StartMessage(AS_BEGIN_UPDATE);
1075 			fInTransaction = true;
1076 
1077 			int32 code;
1078 			if (fLink->FlushWithReply(code) == B_OK
1079 				&& code == B_OK) {
1080 				// read current window position and size first,
1081 				// the update rect is in screen coordinates...
1082 				// so we need to be up to date
1083 				BPoint origin;
1084 				fLink->Read<BPoint>(&origin);
1085 				float width;
1086 				float height;
1087 				fLink->Read<float>(&width);
1088 				fLink->Read<float>(&height);
1089 				if (origin != fFrame.LeftTop()) {
1090 					// TODO: remove code duplicatation with
1091 					// B_WINDOW_MOVED case...
1092 					//printf("window position was not up to date\n");
1093 					fFrame.OffsetTo(origin);
1094 					FrameMoved(origin);
1095 				}
1096 				if (width != fFrame.Width() || height != fFrame.Height()) {
1097 					// TODO: remove code duplicatation with
1098 					// B_WINDOW_RESIZED case...
1099 					//printf("window size was not up to date\n");
1100 					fFrame.right = fFrame.left + width;
1101 					fFrame.bottom = fFrame.top + height;
1102 
1103 					_AdoptResize();
1104 					FrameResized(width, height);
1105 				}
1106 
1107 				// read culmulated update rect (is in screen coords)
1108 				fLink->Read<BRect>(&updateRect);
1109 
1110 				// read tokens for views that need to be drawn
1111 				// NOTE: we need to read the tokens completely
1112 				// first, or other calls would likely mess up the
1113 				// data in the link.
1114 				BList tokens(20);
1115 				int32 token;
1116 				status_t error = fLink->Read<int32>(&token);
1117 				while (error >= B_OK && token != B_NULL_TOKEN) {
1118 					tokens.AddItem((void*)token);
1119 					error = fLink->Read<int32>(&token);
1120 				}
1121 				// draw
1122 				int32 count = tokens.CountItems();
1123 				for (int32 i = 0; i < count; i++) {
1124 					if (BView* view = _FindView((int32)tokens.ItemAtFast(i)))
1125 						view->_Draw(updateRect);
1126 					else
1127 						printf("_UPDATE_ - didn't find view by token: %ld\n", (int32)tokens.ItemAtFast(i));
1128 				}
1129 				// TODO: the tokens are actually hirachically sorted,
1130 				// so traversing the list in revers and calling
1131 				// child->DrawAfterChildren would actually work correctly,
1132 				// only that drawing outside a view is not yet supported
1133 				// in the app_server.
1134 			}
1135 
1136 			fLink->StartMessage(AS_END_UPDATE);
1137 			fLink->Flush();
1138 			fInTransaction = false;
1139 
1140 			//printf("BWindow(%s) - UPDATE took %lld usecs\n", Title(), system_time() - now);
1141 			break;
1142 		}
1143 
1144 		case _MENUS_DONE_:
1145 			MenusEnded();
1146 			break;
1147 
1148 		// These two are obviously some kind of old scripting messages
1149 		// this is NOT an app_server message and we have to be cautious
1150 		case B_WINDOW_MOVE_BY:
1151 		{
1152 			BPoint offset;
1153 			if (msg->FindPoint("data", &offset) == B_OK)
1154 				MoveBy(offset.x, offset.y);
1155 			else
1156 				msg->SendReply(B_MESSAGE_NOT_UNDERSTOOD);
1157 			break;
1158 		}
1159 
1160 		// this is NOT an app_server message and we have to be cautious
1161 		case B_WINDOW_MOVE_TO:
1162 		{
1163 			BPoint origin;
1164 			if (msg->FindPoint("data", &origin) == B_OK)
1165 				MoveTo(origin);
1166 			else
1167 				msg->SendReply(B_MESSAGE_NOT_UNDERSTOOD);
1168 			break;
1169 		}
1170 
1171 		case B_LAYOUT_WINDOW:
1172 		{
1173 			if (fFlags & B_AUTO_UPDATE_SIZE_LIMITS) {
1174 				// Get min/max constraints of the top view and enforce window
1175 				// size limits respectively.
1176 				BSize minSize = fTopView->MinSize();
1177 				BSize maxSize = fTopView->MaxSize();
1178 				SetSizeLimits(minSize.width, maxSize.width,
1179 					minSize.height, maxSize.height);
1180 			}
1181 
1182 			// do the actual layout
1183 			fTopView->Layout(false);
1184 
1185 			break;
1186 		}
1187 
1188 		default:
1189 			BLooper::DispatchMessage(msg, target);
1190 			break;
1191 	}
1192 }
1193 
1194 
1195 void
1196 BWindow::FrameMoved(BPoint new_position)
1197 {
1198 	// does nothing
1199 	// Hook function
1200 }
1201 
1202 
1203 void
1204 BWindow::FrameResized(float new_width, float new_height)
1205 {
1206 	// does nothing
1207 	// Hook function
1208 }
1209 
1210 
1211 void
1212 BWindow::WorkspacesChanged(uint32 old_ws, uint32 new_ws)
1213 {
1214 	// does nothing
1215 	// Hook function
1216 }
1217 
1218 
1219 void
1220 BWindow::WorkspaceActivated(int32 ws, bool state)
1221 {
1222 	// does nothing
1223 	// Hook function
1224 }
1225 
1226 
1227 void
1228 BWindow::MenusBeginning()
1229 {
1230 	// does nothing
1231 	// Hook function
1232 }
1233 
1234 
1235 void
1236 BWindow::MenusEnded()
1237 {
1238 	// does nothing
1239 	// Hook function
1240 }
1241 
1242 
1243 void
1244 BWindow::SetSizeLimits(float minWidth, float maxWidth,
1245 	float minHeight, float maxHeight)
1246 {
1247 	if (minWidth > maxWidth || minHeight > maxHeight)
1248 		return;
1249 
1250 	if (!Lock())
1251 		return;
1252 
1253 	fLink->StartMessage(AS_SET_SIZE_LIMITS);
1254 	fLink->Attach<float>(minWidth);
1255 	fLink->Attach<float>(maxWidth);
1256 	fLink->Attach<float>(minHeight);
1257 	fLink->Attach<float>(maxHeight);
1258 
1259 	int32 code;
1260 	if (fLink->FlushWithReply(code) == B_OK
1261 		&& code == B_OK) {
1262 		// read the values that were really enforced on
1263 		// the server side (the window frame could have
1264 		// been changed, too)
1265 		fLink->Read<BRect>(&fFrame);
1266 		fLink->Read<float>(&fMinWidth);
1267 		fLink->Read<float>(&fMaxWidth);
1268 		fLink->Read<float>(&fMinHeight);
1269 		fLink->Read<float>(&fMaxHeight);
1270 
1271 		_AdoptResize();
1272 			// TODO: the same has to be done for SetLook() (that can alter
1273 			//		the size limits, and hence, the size of the window
1274 	}
1275 	Unlock();
1276 }
1277 
1278 
1279 void
1280 BWindow::GetSizeLimits(float *minWidth, float *maxWidth,
1281 	float *minHeight, float *maxHeight)
1282 {
1283 	// TODO: What about locking?!?
1284 	*minHeight = fMinHeight;
1285 	*minWidth = fMinWidth;
1286 	*maxHeight = fMaxHeight;
1287 	*maxWidth = fMaxWidth;
1288 }
1289 
1290 
1291 status_t
1292 BWindow::SetDecoratorSettings(const BMessage& settings)
1293 {
1294 	// flatten the given settings into a buffer and send
1295 	// it to the app_server to apply the settings to the
1296 	// decorator
1297 
1298 	int32 size = settings.FlattenedSize();
1299 	char buffer[size];
1300 	status_t ret = settings.Flatten(buffer, size);
1301 
1302 	if (ret < B_OK)
1303 		return ret;
1304 
1305 	if (!Lock())
1306 		return B_ERROR;
1307 
1308 	ret = fLink->StartMessage(AS_SET_DECORATOR_SETTINGS);
1309 
1310 	if (ret == B_OK)
1311 		ret = fLink->Attach<int32>(size);
1312 
1313 	if (ret == B_OK)
1314 		ret = fLink->Attach(buffer, size);
1315 
1316 	if (ret == B_OK)
1317 		ret = fLink->Flush();
1318 
1319 	Unlock();
1320 
1321 	return ret;
1322 }
1323 
1324 
1325 status_t
1326 BWindow::GetDecoratorSettings(BMessage* settings) const
1327 {
1328 	// read a flattened settings message from the app_server
1329 	// and put it into settings
1330 
1331 	if (!const_cast<BWindow*>(this)->Lock())
1332 		return B_ERROR;
1333 
1334 	status_t ret = fLink->StartMessage(AS_GET_DECORATOR_SETTINGS);
1335 
1336 	if (ret == B_OK) {
1337 		int32 code;
1338 		ret = fLink->FlushWithReply(code);
1339 		if (ret == B_OK && code != B_OK)
1340 			ret = code;
1341 	}
1342 
1343 	if (ret == B_OK) {
1344 		int32 size;
1345 		ret = fLink->Read<int32>(&size);
1346 		if (ret == B_OK) {
1347 			char buffer[size];
1348 			ret = fLink->Read(buffer, size);
1349 			if (ret == B_OK) {
1350 				ret = settings->Unflatten(buffer);
1351 			}
1352 		}
1353 	}
1354 
1355 	const_cast<BWindow*>(this)->Unlock();
1356 
1357 	return ret;
1358 }
1359 
1360 
1361 void
1362 BWindow::SetZoomLimits(float maxWidth, float maxHeight)
1363 {
1364 	// TODO: What about locking?!?
1365 	if (maxWidth > fMaxWidth)
1366 		maxWidth = fMaxWidth;
1367 	else
1368 		fMaxZoomWidth = maxWidth;
1369 
1370 	if (maxHeight > fMaxHeight)
1371 		maxHeight = fMaxHeight;
1372 	else
1373 		fMaxZoomHeight = maxHeight;
1374 }
1375 
1376 
1377 void
1378 BWindow::Zoom(BPoint leftTop, float width, float height)
1379 {
1380 	// the default implementation of this hook function
1381 	// just does the obvious:
1382 	MoveTo(leftTop);
1383 	ResizeTo(width, height);
1384 }
1385 
1386 
1387 void
1388 BWindow::Zoom()
1389 {
1390 	// TODO: What about locking?!?
1391 	/*
1392 		from BeBook:
1393 		However, if the window's rectangle already matches these "zoom" dimensions
1394 		(give or take a few pixels), Zoom() passes the window's previous
1395 		("non-zoomed") size and location. (??????)
1396 	*/
1397 
1398 	/* From BeBook:
1399 		The dimensions that non-virtual Zoom() passes to hook Zoom() are deduced from
1400 		the smallest of three rectangles:
1401 	*/
1402 
1403 	// TODO: make more elaborate (figure out this window's
1404 	// tab height and border width... maybe ask app_server)
1405 	float borderWidth = 5.0;
1406 	float tabHeight = 26.0;
1407 
1408 	// 1) the rectangle defined by SetZoomLimits(),
1409 	float zoomedWidth = fMaxZoomWidth;
1410 	float zoomedHeight = fMaxZoomHeight;
1411 
1412 	// 2) the rectangle defined by SetSizeLimits()
1413 	if (fMaxWidth < zoomedWidth)
1414 		zoomedWidth = fMaxWidth;
1415 	if (fMaxHeight < zoomedHeight)
1416 		zoomedHeight = fMaxHeight;
1417 
1418 	// 3) the screen rectangle
1419 	BScreen screen(this);
1420 	float screenWidth = screen.Frame().Width() - 2 * borderWidth;
1421 	float screenHeight = screen.Frame().Height() - (borderWidth + tabHeight);
1422 	if (screenWidth < zoomedWidth)
1423 		zoomedWidth = screenWidth;
1424 	if (screenHeight < zoomedHeight)
1425 		zoomedHeight = screenHeight;
1426 
1427 	BPoint zoomedLeftTop = screen.Frame().LeftTop() + BPoint(borderWidth, tabHeight);
1428 
1429 	// UN-ZOOM:
1430 	if (fPreviousFrame.IsValid()
1431 		// NOTE: don't check for fFrame.LeftTop() == zoomedLeftTop
1432 		// -> makes it easier on the user to get a window back into place
1433 		&& fFrame.Width() == zoomedWidth
1434 		&& fFrame.Height() == zoomedHeight) {
1435 		// already zoomed!
1436 		Zoom(fPreviousFrame.LeftTop(), fPreviousFrame.Width(), fPreviousFrame.Height());
1437 		return;
1438 	}
1439 
1440 	// ZOOM:
1441 
1442 	// remember fFrame for later "unzooming"
1443 	fPreviousFrame = fFrame;
1444 
1445 	Zoom(zoomedLeftTop, zoomedWidth, zoomedHeight);
1446 }
1447 
1448 
1449 void
1450 BWindow::ScreenChanged(BRect screen_size, color_space depth)
1451 {
1452 	// Hook function
1453 }
1454 
1455 
1456 void
1457 BWindow::SetPulseRate(bigtime_t rate)
1458 {
1459 	// TODO: What about locking?!?
1460 	if (rate < 0 || (rate == fPulseRate && !(rate == 0 ^ fPulseRunner == NULL)))
1461 		return;
1462 
1463 	fPulseRate = rate;
1464 
1465 	if (rate > 0) {
1466 		if (fPulseRunner == NULL) {
1467 			BMessage message(B_PULSE);
1468 			fPulseRunner = new BMessageRunner(BMessenger(this),
1469 				&message, rate);
1470 		} else {
1471 			fPulseRunner->SetInterval(rate);
1472 		}
1473 	} else {
1474 		// rate == 0
1475 		delete fPulseRunner;
1476 		fPulseRunner = NULL;
1477 	}
1478 }
1479 
1480 
1481 bigtime_t
1482 BWindow::PulseRate() const
1483 {
1484 	return fPulseRate;
1485 }
1486 
1487 
1488 void
1489 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMenuItem *item)
1490 {
1491 	Shortcut* shortcut = new Shortcut(key, modifiers, item);
1492 
1493 	// removes the shortcut if it already exists!
1494 	RemoveShortcut(key, modifiers);
1495 
1496 	fShortcuts.AddItem(shortcut);
1497 }
1498 
1499 
1500 void
1501 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage *message)
1502 {
1503 	AddShortcut(key, modifiers, message, this);
1504 }
1505 
1506 
1507 void
1508 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage *message, BHandler *target)
1509 {
1510 	if (message == NULL)
1511 		return;
1512 
1513 	Shortcut* shortcut = new Shortcut(key, modifiers, message, target);
1514 
1515 	// removes the shortcut if it already exists!
1516 	RemoveShortcut(key, modifiers);
1517 
1518 	fShortcuts.AddItem(shortcut);
1519 }
1520 
1521 
1522 void
1523 BWindow::RemoveShortcut(uint32 key, uint32 modifiers)
1524 {
1525 	Shortcut* shortcut = _FindShortcut(key, modifiers);
1526 	if (shortcut != NULL) {
1527 		fShortcuts.RemoveItem(shortcut);
1528 		delete shortcut;
1529 	} else if ((key == 'q' || key == 'Q') && modifiers == B_COMMAND_KEY) {
1530 		// the quit shortcut is a fake shortcut
1531 		fNoQuitShortcut = true;
1532 	}
1533 }
1534 
1535 
1536 BButton *
1537 BWindow::DefaultButton() const
1538 {
1539 	// TODO: What about locking?!?
1540 	return fDefaultButton;
1541 }
1542 
1543 
1544 void
1545 BWindow::SetDefaultButton(BButton *button)
1546 {
1547 	// TODO: What about locking?!?
1548 	if (fDefaultButton == button)
1549 		return;
1550 
1551 	if (fDefaultButton != NULL) {
1552 		// tell old button it's no longer the default one
1553 		BButton *oldDefault = fDefaultButton;
1554 		oldDefault->MakeDefault(false);
1555 		oldDefault->Invalidate();
1556 	}
1557 
1558 	fDefaultButton = button;
1559 
1560 	if (button != NULL) {
1561 		// notify new default button
1562 		fDefaultButton->MakeDefault(true);
1563 		fDefaultButton->Invalidate();
1564 	}
1565 }
1566 
1567 
1568 bool
1569 BWindow::NeedsUpdate() const
1570 {
1571 	if (!const_cast<BWindow *>(this)->Lock())
1572 		return false;
1573 
1574 	fLink->StartMessage(AS_NEEDS_UPDATE);
1575 
1576 	int32 code = B_ERROR;
1577 	fLink->FlushWithReply(code);
1578 
1579 	const_cast<BWindow *>(this)->Unlock();
1580 
1581 	return code == B_OK;
1582 }
1583 
1584 
1585 void
1586 BWindow::UpdateIfNeeded()
1587 {
1588 	// works only from the window thread
1589 	if (find_thread(NULL) != Thread())
1590 		return;
1591 
1592 	// if the queue is already locked we are called recursivly
1593 	// from our own dispatched update message
1594 	if (((const BMessageQueue *)MessageQueue())->IsLocked())
1595 		return;
1596 
1597 	if (!Lock())
1598 		return;
1599 
1600 	// make sure all requests that would cause an update have
1601 	// arrived at the server
1602 	Sync();
1603 
1604 	// Since we're blocking the event loop, we need to retrieve
1605 	// all messages that are pending on the port.
1606 	_DequeueAll();
1607 
1608 	BMessageQueue *queue = MessageQueue();
1609 	queue->Lock();
1610 
1611 	// First process and remove any _UPDATE_ message in the queue
1612 	// With the current design, there can only be one at a time
1613 
1614 	BMessage *msg;
1615 	for (int32 i = 0; (msg = queue->FindMessage(i)) != NULL; i++) {
1616 		if (msg->what == _UPDATE_) {
1617 			BWindow::DispatchMessage(msg, this);
1618 				// we need to make sure that no overridden method is called
1619 				// here; for BWindow::DispatchMessage() we now exactly what
1620 				// will happen
1621 			queue->RemoveMessage(msg);
1622 			delete msg;
1623 			break;
1624 			// NOTE: "i" would have to be decreased if there were
1625 			// multiple _UPDATE_ messages and we would not break!
1626 		}
1627 	}
1628 
1629 	queue->Unlock();
1630 	Unlock();
1631 }
1632 
1633 
1634 BView *
1635 BWindow::FindView(const char *viewName) const
1636 {
1637 	BAutolock _(const_cast<BWindow*>(this));
1638 	return fTopView->FindView(viewName);
1639 }
1640 
1641 
1642 BView *
1643 BWindow::FindView(BPoint point) const
1644 {
1645 	BAutolock _(const_cast<BWindow*>(this));
1646 	return _FindView(fTopView, point);
1647 }
1648 
1649 
1650 BView *
1651 BWindow::CurrentFocus() const
1652 {
1653 	return fFocus;
1654 }
1655 
1656 
1657 void
1658 BWindow::Activate(bool active)
1659 {
1660 	if (!Lock())
1661 		return;
1662 
1663 	if (!IsHidden()) {
1664 		fLink->StartMessage(AS_ACTIVATE_WINDOW);
1665 		fLink->Attach<bool>(active);
1666 		fLink->Flush();
1667 	}
1668 
1669 	Unlock();
1670 }
1671 
1672 
1673 void
1674 BWindow::WindowActivated(bool state)
1675 {
1676 	// hook function
1677 	// does nothing
1678 }
1679 
1680 
1681 void
1682 BWindow::ConvertToScreen(BPoint *point) const
1683 {
1684 	point->x += fFrame.left;
1685 	point->y += fFrame.top;
1686 }
1687 
1688 
1689 BPoint
1690 BWindow::ConvertToScreen(BPoint point) const
1691 {
1692 	return point + fFrame.LeftTop();
1693 }
1694 
1695 
1696 void
1697 BWindow::ConvertFromScreen(BPoint *point) const
1698 {
1699 	point->x -= fFrame.left;
1700 	point->y -= fFrame.top;
1701 }
1702 
1703 
1704 BPoint
1705 BWindow::ConvertFromScreen(BPoint point) const
1706 {
1707 	return point - fFrame.LeftTop();
1708 }
1709 
1710 
1711 void
1712 BWindow::ConvertToScreen(BRect *rect) const
1713 {
1714 	rect->OffsetBy(fFrame.LeftTop());
1715 }
1716 
1717 
1718 BRect
1719 BWindow::ConvertToScreen(BRect rect) const
1720 {
1721 	return rect.OffsetByCopy(fFrame.LeftTop());
1722 }
1723 
1724 
1725 void
1726 BWindow::ConvertFromScreen(BRect* rect) const
1727 {
1728 	rect->OffsetBy(-fFrame.left, -fFrame.top);
1729 }
1730 
1731 
1732 BRect
1733 BWindow::ConvertFromScreen(BRect rect) const
1734 {
1735 	return rect.OffsetByCopy(-fFrame.left, -fFrame.top);
1736 }
1737 
1738 
1739 bool
1740 BWindow::IsMinimized() const
1741 {
1742 	// Hiding takes precendence over minimization!!!
1743 	if (IsHidden())
1744 		return false;
1745 
1746 	return fMinimized;
1747 }
1748 
1749 
1750 BRect
1751 BWindow::Bounds() const
1752 {
1753 	return BRect(0, 0, fFrame.Width(), fFrame.Height());
1754 }
1755 
1756 
1757 BRect
1758 BWindow::Frame() const
1759 {
1760 	return fFrame;
1761 }
1762 
1763 
1764 const char *
1765 BWindow::Title() const
1766 {
1767 	return fTitle;
1768 }
1769 
1770 
1771 void
1772 BWindow::SetTitle(const char *title)
1773 {
1774 	if (title == NULL)
1775 		title = "";
1776 
1777 	free(fTitle);
1778 	fTitle = strdup(title);
1779 
1780 	// we will change BWindow's thread name to "w>window title"
1781 
1782 	char threadName[B_OS_NAME_LENGTH];
1783 	strcpy(threadName, "w>");
1784 #ifdef __HAIKU__
1785 	strlcat(threadName, title, B_OS_NAME_LENGTH);
1786 #else
1787 	int32 length = strlen(title);
1788 	length = min_c(length, B_OS_NAME_LENGTH - 3);
1789 	memcpy(threadName + 2, title, length);
1790 	threadName[length + 2] = '\0';
1791 #endif
1792 
1793 	// change the handler's name
1794 	SetName(threadName);
1795 
1796 	// if the message loop has been started...
1797 	if (Thread() >= B_OK)
1798 		rename_thread(Thread(), threadName);
1799 
1800 	// we notify the app_server so we can actually see the change
1801 	if (Lock()) {
1802 		fLink->StartMessage(AS_SET_WINDOW_TITLE);
1803 		fLink->AttachString(fTitle);
1804 		fLink->Flush();
1805 		Unlock();
1806 	}
1807 }
1808 
1809 
1810 bool
1811 BWindow::IsActive() const
1812 {
1813 	return fActive;
1814 }
1815 
1816 
1817 void
1818 BWindow::SetKeyMenuBar(BMenuBar *bar)
1819 {
1820 	fKeyMenuBar = bar;
1821 }
1822 
1823 
1824 BMenuBar *
1825 BWindow::KeyMenuBar() const
1826 {
1827 	return fKeyMenuBar;
1828 }
1829 
1830 
1831 bool
1832 BWindow::IsModal() const
1833 {
1834 	return fFeel == B_MODAL_SUBSET_WINDOW_FEEL
1835 		|| fFeel == B_MODAL_APP_WINDOW_FEEL
1836 		|| fFeel == B_MODAL_ALL_WINDOW_FEEL;
1837 }
1838 
1839 
1840 bool
1841 BWindow::IsFloating() const
1842 {
1843 	return fFeel == B_FLOATING_SUBSET_WINDOW_FEEL
1844 		|| fFeel == B_FLOATING_APP_WINDOW_FEEL
1845 		|| fFeel == B_FLOATING_ALL_WINDOW_FEEL;
1846 }
1847 
1848 
1849 status_t
1850 BWindow::AddToSubset(BWindow *window)
1851 {
1852 	if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL
1853 		|| (fFeel != B_MODAL_SUBSET_WINDOW_FEEL
1854 			&& fFeel != B_FLOATING_SUBSET_WINDOW_FEEL))
1855 		return B_BAD_VALUE;
1856 
1857 	if (!Lock())
1858 		return B_ERROR;
1859 
1860 	status_t status = B_ERROR;
1861 	fLink->StartMessage(AS_ADD_TO_SUBSET);
1862 	fLink->Attach<int32>(_get_object_token_(window));
1863 	fLink->FlushWithReply(status);
1864 
1865 	Unlock();
1866 
1867 	return status;
1868 }
1869 
1870 
1871 status_t
1872 BWindow::RemoveFromSubset(BWindow *window)
1873 {
1874 	if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL
1875 		|| (fFeel != B_MODAL_SUBSET_WINDOW_FEEL
1876 			&& fFeel != B_FLOATING_SUBSET_WINDOW_FEEL))
1877 		return B_BAD_VALUE;
1878 
1879 	if (!Lock())
1880 		return B_ERROR;
1881 
1882 	status_t status = B_ERROR;
1883 	fLink->StartMessage(AS_REMOVE_FROM_SUBSET);
1884 	fLink->Attach<int32>(_get_object_token_(window));
1885 	fLink->FlushWithReply(status);
1886 
1887 	Unlock();
1888 
1889 	return status;
1890 }
1891 
1892 
1893 status_t
1894 BWindow::Perform(perform_code d, void *arg)
1895 {
1896 	return BLooper::Perform(d, arg);
1897 }
1898 
1899 
1900 status_t
1901 BWindow::SetType(window_type type)
1902 {
1903 	window_look look;
1904 	window_feel feel;
1905 	_DecomposeType(type, &look, &feel);
1906 
1907 	status_t status = SetLook(look);
1908 	if (status == B_OK)
1909 		status = SetFeel(feel);
1910 
1911 	return status;
1912 }
1913 
1914 
1915 window_type
1916 BWindow::Type() const
1917 {
1918 	return _ComposeType(fLook, fFeel);
1919 }
1920 
1921 
1922 status_t
1923 BWindow::SetLook(window_look look)
1924 {
1925 	BAutolock locker(this);
1926 
1927 	fLink->StartMessage(AS_SET_LOOK);
1928 	fLink->Attach<int32>((int32)look);
1929 
1930 	status_t status = B_ERROR;
1931 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
1932 		fLook = look;
1933 
1934 	// TODO: this could have changed the window size, and thus, we
1935 	//	need to get it from the server (and call _AdoptResize()).
1936 
1937 	return status;
1938 }
1939 
1940 
1941 window_look
1942 BWindow::Look() const
1943 {
1944 	return fLook;
1945 }
1946 
1947 
1948 status_t
1949 BWindow::SetFeel(window_feel feel)
1950 {
1951 	BAutolock locker(this);
1952 
1953 	fLink->StartMessage(AS_SET_FEEL);
1954 	fLink->Attach<int32>((int32)feel);
1955 
1956 	status_t status = B_ERROR;
1957 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
1958 		fFeel = feel;
1959 
1960 	return status;
1961 }
1962 
1963 
1964 window_feel
1965 BWindow::Feel() const
1966 {
1967 	return fFeel;
1968 }
1969 
1970 
1971 status_t
1972 BWindow::SetFlags(uint32 flags)
1973 {
1974 	BAutolock locker(this);
1975 
1976 	fLink->StartMessage(AS_SET_FLAGS);
1977 	fLink->Attach<uint32>(flags);
1978 
1979 	int32 status = B_ERROR;
1980 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
1981 		fFlags = flags;
1982 
1983 	return status;
1984 }
1985 
1986 
1987 uint32
1988 BWindow::Flags() const
1989 {
1990 	return fFlags;
1991 }
1992 
1993 
1994 status_t
1995 BWindow::SetWindowAlignment(window_alignment mode,
1996 	int32 h, int32 hOffset, int32 width, int32 widthOffset,
1997 	int32 v, int32 vOffset, int32 height, int32 heightOffset)
1998 {
1999 	if ((mode & (B_BYTE_ALIGNMENT | B_PIXEL_ALIGNMENT)) == 0
2000 		|| (hOffset >= 0 && hOffset <= h)
2001 		|| (vOffset >= 0 && vOffset <= v)
2002 		|| (widthOffset >= 0 && widthOffset <= width)
2003 		|| (heightOffset >= 0 && heightOffset <= height))
2004 		return B_BAD_VALUE;
2005 
2006 	// TODO: test if hOffset = 0 and set it to 1 if true.
2007 
2008 	if (!Lock())
2009 		return B_ERROR;
2010 
2011 	fLink->StartMessage(AS_SET_ALIGNMENT);
2012 	fLink->Attach<int32>((int32)mode);
2013 	fLink->Attach<int32>(h);
2014 	fLink->Attach<int32>(hOffset);
2015 	fLink->Attach<int32>(width);
2016 	fLink->Attach<int32>(widthOffset);
2017 	fLink->Attach<int32>(v);
2018 	fLink->Attach<int32>(vOffset);
2019 	fLink->Attach<int32>(height);
2020 	fLink->Attach<int32>(heightOffset);
2021 
2022 	status_t status = B_ERROR;
2023 	fLink->FlushWithReply(status);
2024 
2025 	Unlock();
2026 
2027 	return status;
2028 }
2029 
2030 
2031 status_t
2032 BWindow::GetWindowAlignment(window_alignment *mode,
2033 	int32 *h, int32 *hOffset, int32 *width, int32 *widthOffset,
2034 	int32 *v, int32 *vOffset, int32 *height, int32 *heightOffset) const
2035 {
2036 	if (!const_cast<BWindow *>(this)->Lock())
2037 		return B_ERROR;
2038 
2039 	fLink->StartMessage(AS_GET_ALIGNMENT);
2040 
2041 	status_t status;
2042 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK) {
2043 		fLink->Read<int32>((int32 *)mode);
2044 		fLink->Read<int32>(h);
2045 		fLink->Read<int32>(hOffset);
2046 		fLink->Read<int32>(width);
2047 		fLink->Read<int32>(widthOffset);
2048 		fLink->Read<int32>(v);
2049 		fLink->Read<int32>(hOffset);
2050 		fLink->Read<int32>(height);
2051 		fLink->Read<int32>(heightOffset);
2052 	}
2053 
2054 	const_cast<BWindow *>(this)->Unlock();
2055 	return status;
2056 }
2057 
2058 
2059 uint32
2060 BWindow::Workspaces() const
2061 {
2062 	if (!const_cast<BWindow *>(this)->Lock())
2063 		return 0;
2064 
2065 	uint32 workspaces = 0;
2066 
2067 	fLink->StartMessage(AS_GET_WORKSPACES);
2068 
2069 	status_t status;
2070 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2071 		fLink->Read<uint32>(&workspaces);
2072 
2073 	const_cast<BWindow *>(this)->Unlock();
2074 	return workspaces;
2075 }
2076 
2077 
2078 void
2079 BWindow::SetWorkspaces(uint32 workspaces)
2080 {
2081 	// TODO: don't forget about Tracker's background window.
2082 	if (fFeel != B_NORMAL_WINDOW_FEEL)
2083 		return;
2084 
2085 	if (Lock()) {
2086 		fLink->StartMessage(AS_SET_WORKSPACES);
2087 		fLink->Attach<uint32>(workspaces);
2088 		fLink->Flush();
2089 		Unlock();
2090 	}
2091 }
2092 
2093 
2094 BView *
2095 BWindow::LastMouseMovedView() const
2096 {
2097 	return fLastMouseMovedView;
2098 }
2099 
2100 
2101 void
2102 BWindow::MoveBy(float dx, float dy)
2103 {
2104 	if ((dx == 0.0 && dy == 0.0) || !Lock())
2105 		return;
2106 
2107 	fLink->StartMessage(AS_WINDOW_MOVE);
2108 	fLink->Attach<float>(dx);
2109 	fLink->Attach<float>(dy);
2110 
2111 	status_t status;
2112 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2113 		fFrame.OffsetBy(dx, dy);
2114 
2115 	Unlock();
2116 }
2117 
2118 
2119 void
2120 BWindow::MoveTo(BPoint point)
2121 {
2122 	if (!Lock())
2123 		return;
2124 
2125 	point.x = roundf(point.x);
2126 	point.y = roundf(point.y);
2127 
2128 	if (fFrame.left != point.x || fFrame.top != point.y) {
2129 		float xOffset = point.x - fFrame.left;
2130 		float yOffset = point.y - fFrame.top;
2131 
2132 		MoveBy(xOffset, yOffset);
2133 	}
2134 
2135 	Unlock();
2136 }
2137 
2138 
2139 void
2140 BWindow::MoveTo(float x, float y)
2141 {
2142 	MoveTo(BPoint(x, y));
2143 }
2144 
2145 
2146 void
2147 BWindow::ResizeBy(float dx, float dy)
2148 {
2149 	if (!Lock())
2150 		return;
2151 
2152 	dx = roundf(dx);
2153 	dy = roundf(dy);
2154 
2155 	// stay in minimum & maximum frame limits
2156 	if (fFrame.Width() + dx < fMinWidth)
2157 		dx = fMinWidth - fFrame.Width();
2158 	if (fFrame.Width() + dx > fMaxWidth)
2159 		dx = fMaxWidth - fFrame.Width();
2160 	if (fFrame.Height() + dy < fMinHeight)
2161 		dy = fMinHeight - fFrame.Height();
2162 	if (fFrame.Height() + dy > fMaxHeight)
2163 		dy = fMaxHeight - fFrame.Height();
2164 
2165 	if (dx != 0.0 || dy != 0.0) {
2166 		fLink->StartMessage(AS_WINDOW_RESIZE);
2167 		fLink->Attach<float>(dx);
2168 		fLink->Attach<float>(dy);
2169 
2170 		status_t status;
2171 		if (fLink->FlushWithReply(status) == B_OK && status == B_OK) {
2172 			fFrame.SetRightBottom(fFrame.RightBottom() + BPoint(dx, dy));
2173 			_AdoptResize();
2174 		}
2175 	}
2176 	Unlock();
2177 }
2178 
2179 
2180 void
2181 BWindow::ResizeTo(float width, float height)
2182 {
2183 	if (Lock()) {
2184 		ResizeBy(width - fFrame.Width(), height - fFrame.Height());
2185 		Unlock();
2186 	}
2187 }
2188 
2189 
2190 void
2191 BWindow::Show()
2192 {
2193 	if (!fRunCalled) {
2194 		// this is the fist time Show() is called, which implicetly runs the looper
2195 		if (fLink->SenderPort() < B_OK) {
2196 			// We don't have valid app_server connection; there is no point
2197 			// in starting our looper
2198 			fThread = B_ERROR;
2199 			return;
2200 		} else
2201 			Run();
2202 	}
2203 
2204 	if (Lock()) {
2205 		fShowLevel++;
2206 
2207 		if (fShowLevel == 1) {
2208 			STRACE(("BWindow(%s): sending AS_SHOW_WINDOW message...\n", Name()));
2209 			fLink->StartMessage(AS_SHOW_WINDOW);
2210 			fLink->Flush();
2211 		}
2212 
2213 		Unlock();
2214 	}
2215 }
2216 
2217 
2218 void
2219 BWindow::Hide()
2220 {
2221 	if (!Lock())
2222 		return;
2223 
2224 	if (--fShowLevel == 0) {
2225 		fLink->StartMessage(AS_HIDE_WINDOW);
2226 		fLink->Flush();
2227 	}
2228 
2229 	Unlock();
2230 }
2231 
2232 
2233 bool
2234 BWindow::IsHidden() const
2235 {
2236 	return fShowLevel <= 0;
2237 }
2238 
2239 
2240 bool
2241 BWindow::QuitRequested()
2242 {
2243 	return BLooper::QuitRequested();
2244 }
2245 
2246 
2247 thread_id
2248 BWindow::Run()
2249 {
2250 	return BLooper::Run();
2251 }
2252 
2253 
2254 void
2255 BWindow::SetLayout(BLayout* layout)
2256 {
2257 	fTopView->SetLayout(layout);
2258 }
2259 
2260 
2261 BLayout*
2262 BWindow::GetLayout() const
2263 {
2264 	return fTopView->GetLayout();
2265 }
2266 
2267 
2268 void
2269 BWindow::InvalidateLayout(bool descendants)
2270 {
2271 	fTopView->InvalidateLayout(descendants);
2272 }
2273 
2274 
2275 status_t
2276 BWindow::GetSupportedSuites(BMessage *data)
2277 {
2278 	if (data == NULL)
2279 		return B_BAD_VALUE;
2280 
2281 	status_t status = data->AddString("suites", "suite/vnd.Be-window");
2282 	if (status == B_OK) {
2283 		BPropertyInfo propertyInfo(sWindowPropInfo, sWindowValueInfo);
2284 
2285 		status = data->AddFlat("messages", &propertyInfo);
2286 		if (status == B_OK)
2287 			status = BLooper::GetSupportedSuites(data);
2288 	}
2289 
2290 	return status;
2291 }
2292 
2293 
2294 BHandler *
2295 BWindow::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier,
2296 	int32 what,	const char *property)
2297 {
2298 	if (msg->what == B_WINDOW_MOVE_BY
2299 		|| msg->what == B_WINDOW_MOVE_TO)
2300 		return this;
2301 
2302 	BPropertyInfo propertyInfo(sWindowPropInfo);
2303 	if (propertyInfo.FindMatch(msg, index, specifier, what, property) >= 0) {
2304 		if (!strcmp(property, "View")) {
2305 			// we will NOT pop the current specifier
2306 			return fTopView;
2307 		} else if (!strcmp(property, "MenuBar")) {
2308 			if (fKeyMenuBar) {
2309 				msg->PopSpecifier();
2310 				return fKeyMenuBar;
2311 			} else {
2312 				BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
2313 				replyMsg.AddInt32("error", B_NAME_NOT_FOUND);
2314 				replyMsg.AddString("message", "This window doesn't have a main MenuBar");
2315 				msg->SendReply(&replyMsg);
2316 				return NULL;
2317 			}
2318 		} else
2319 			return this;
2320 	}
2321 
2322 	return BLooper::ResolveSpecifier(msg, index, specifier, what, property);
2323 }
2324 
2325 
2326 //	#pragma mark - Private Methods
2327 
2328 
2329 void
2330 BWindow::_InitData(BRect frame, const char* title, window_look look,
2331 	window_feel feel, uint32 flags,	uint32 workspace, int32 bitmapToken)
2332 {
2333 	STRACE(("BWindow::InitData()\n"));
2334 
2335 	if (be_app == NULL) {
2336 		debugger("You need a valid BApplication object before interacting with the app_server");
2337 		return;
2338 	}
2339 
2340 	frame.left = roundf(frame.left);
2341 	frame.top = roundf(frame.top);
2342 	frame.right = roundf(frame.right);
2343 	frame.bottom = roundf(frame.bottom);
2344 
2345 	fFrame = frame;
2346 
2347 	if (title == NULL)
2348 		title = "";
2349 	fTitle = strdup(title);
2350 	SetName(title);
2351 
2352 	fFeel = feel;
2353 	fLook = look;
2354 	fFlags = flags;
2355 
2356 	fInTransaction = false;
2357 	fActive = false;
2358 	fShowLevel = 0;
2359 
2360 	fTopView = NULL;
2361 	fFocus = NULL;
2362 	fLastMouseMovedView	= NULL;
2363 	fKeyMenuBar = NULL;
2364 	fDefaultButton = NULL;
2365 
2366 	// Shortcut 'Q' is handled in _HandleKeyDown() directly, as its message
2367 	// get sent to the application, and not one of our handlers
2368 	fNoQuitShortcut = false;
2369 
2370 	if ((fFlags & B_NOT_CLOSABLE) == 0 && !IsModal()) {
2371 		// Modal windows default to non-closable, but you can add the shortcut manually,
2372 		// if a different behaviour is wanted
2373 		AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED));
2374 	}
2375 
2376 	AddShortcut('X', B_COMMAND_KEY, new BMessage(B_CUT), NULL);
2377 	AddShortcut('C', B_COMMAND_KEY, new BMessage(B_COPY), NULL);
2378 	AddShortcut('V', B_COMMAND_KEY, new BMessage(B_PASTE), NULL);
2379 	AddShortcut('A', B_COMMAND_KEY, new BMessage(B_SELECT_ALL), NULL);
2380 
2381 	// We set the default pulse rate, but we don't start the pulse
2382 	fPulseRate = 500000;
2383 	fPulseRunner = NULL;
2384 
2385 	// TODO:  see if you can use 'fViewsNeedPulse'
2386 
2387 	fIsFilePanel = false;
2388 
2389 	// TODO: see WHEN is this used!
2390 	fMaskActivated = false;
2391 
2392 	// TODO: see WHEN is this used!
2393 	fWaitingForMenu = false;
2394 	fMenuSem = -1;
2395 
2396 	fMinimized = false;
2397 
2398 	fMaxZoomHeight = 32768.0;
2399 	fMaxZoomWidth = 32768.0;
2400 	fMinHeight = 0.0;
2401 	fMinWidth = 0.0;
2402 	fMaxHeight = 32768.0;
2403 	fMaxWidth = 32768.0;
2404 
2405 	fLastViewToken = B_NULL_TOKEN;
2406 
2407 	// TODO: other initializations!
2408 
2409 	// Create the server-side window
2410 
2411 	port_id receivePort = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY, "w<app_server");
2412 	if (receivePort < B_OK) {
2413 		// TODO: huh?
2414 		debugger("Could not create BWindow's receive port, used for interacting with the app_server!");
2415 		delete this;
2416 		return;
2417 	}
2418 
2419 	STRACE(("BWindow::InitData(): contacting app_server...\n"));
2420 
2421 	// HERE we are in BApplication's thread, so for locking we use be_app variable
2422 	// we'll lock the be_app to be sure we're the only one writing at BApplication's server port
2423 	bool locked = false;
2424 	if (!be_app->IsLocked()) {
2425 		be_app->Lock();
2426 		locked = true;
2427 	}
2428 
2429 	// let app_server know that a window has been created.
2430 	fLink = new BPrivate::PortLink(
2431 		BApplication::Private::ServerLink()->SenderPort(), receivePort);
2432 
2433 	if (bitmapToken < 0) {
2434 		fLink->StartMessage(AS_CREATE_WINDOW);
2435 	} else {
2436 		fLink->StartMessage(AS_CREATE_OFFSCREEN_WINDOW);
2437 		fLink->Attach<int32>(bitmapToken);
2438 	}
2439 
2440 	fLink->Attach<BRect>(fFrame);
2441 	fLink->Attach<uint32>((uint32)fLook);
2442 	fLink->Attach<uint32>((uint32)fFeel);
2443 	fLink->Attach<uint32>(fFlags);
2444 	fLink->Attach<uint32>(workspace);
2445 	fLink->Attach<int32>(_get_object_token_(this));
2446 	fLink->Attach<port_id>(receivePort);
2447 	fLink->Attach<port_id>(fMsgPort);
2448 	fLink->AttachString(title);
2449 
2450 	port_id sendPort;
2451 	int32 code;
2452 	if (fLink->FlushWithReply(code) == B_OK
2453 		&& code == B_OK
2454 		&& fLink->Read<port_id>(&sendPort) == B_OK) {
2455 		// read the frame size and its limits that were really
2456 		// enforced on the server side
2457 
2458 		fLink->Read<BRect>(&fFrame);
2459 		fLink->Read<float>(&fMinWidth);
2460 		fLink->Read<float>(&fMaxWidth);
2461 		fLink->Read<float>(&fMinHeight);
2462 		fLink->Read<float>(&fMaxHeight);
2463 
2464 		fMaxZoomWidth = fMaxWidth;
2465 		fMaxZoomHeight = fMaxHeight;
2466 	} else
2467 		sendPort = -1;
2468 
2469 	// Redirect our link to the new window connection
2470 	fLink->SetSenderPort(sendPort);
2471 
2472 	if (locked)
2473 		be_app->Unlock();
2474 
2475 	STRACE(("Server says that our send port is %ld\n", sendPort));
2476 	STRACE(("Window locked?: %s\n", IsLocked() ? "True" : "False"));
2477 
2478 	_CreateTopView();
2479 }
2480 
2481 
2482 //!	Reads all pending messages from the window port and put them into the queue.
2483 void
2484 BWindow::_DequeueAll()
2485 {
2486 	//	Get message count from port
2487 	int32 count = port_count(fMsgPort);
2488 
2489 	for (int32 i = 0; i < count; i++) {
2490 		BMessage *message = MessageFromPort(0);
2491 		if (message != NULL)
2492 			fDirectTarget->Queue()->AddMessage(message);
2493 	}
2494 }
2495 
2496 
2497 /*!	This here is an almost complete code duplication to BLooper::task_looper()
2498 	but with some important differences:
2499 	 a)	it uses the _DetermineTarget() method to tell what the later target of
2500 		a message will be, if no explicit target is supplied.
2501 	 b)	it calls _UnpackMessage() and _SanitizeMessage() to duplicate the message
2502 	 	to all of its intended targets, and to add all fields the target would
2503 	 	expect in such a message.
2504 
2505 	This is important because the app_server sends all input events to the
2506 	preferred handler, and expects them to be correctly distributed to their
2507 	intended targets.
2508 */
2509 void
2510 BWindow::task_looper()
2511 {
2512 	STRACE(("info: BWindow::task_looper() started.\n"));
2513 
2514 	// Check that looper is locked (should be)
2515 	AssertLocked();
2516 	Unlock();
2517 
2518 	if (IsLocked())
2519 		debugger("window must not be locked!");
2520 
2521 	while (!fTerminating) {
2522 		// Did we get a message?
2523 		BMessage* msg = MessageFromPort();
2524 		if (msg)
2525 			_AddMessagePriv(msg);
2526 
2527 		//	Get message count from port
2528 		int32 msgCount = port_count(fMsgPort);
2529 		for (int32 i = 0; i < msgCount; ++i) {
2530 			// Read 'count' messages from port (so we will not block)
2531 			// We use zero as our timeout since we know there is stuff there
2532 			msg = MessageFromPort(0);
2533 			// Add messages to queue
2534 			if (msg)
2535 				_AddMessagePriv(msg);
2536 		}
2537 
2538 		bool dispatchNextMessage = true;
2539 		while (!fTerminating && dispatchNextMessage) {
2540 			// Get next message from queue (assign to fLastMessage)
2541 			fLastMessage = fDirectTarget->Queue()->NextMessage();
2542 
2543 			// Lock the looper
2544 			if (!Lock())
2545 				break;
2546 
2547 			if (!fLastMessage) {
2548 				// No more messages: Unlock the looper and terminate the
2549 				// dispatch loop.
2550 				dispatchNextMessage = false;
2551 			} else {
2552 				// Get the target handler
2553 				BMessage::Private messagePrivate(fLastMessage);
2554 				bool usePreferred = messagePrivate.UsePreferredTarget();
2555 				BHandler *handler = NULL;
2556 				bool dropMessage = false;
2557 
2558 				if (usePreferred) {
2559 					handler = PreferredHandler();
2560 					if (handler == NULL)
2561 						handler = this;
2562 				} else {
2563 					gDefaultTokens.GetToken(messagePrivate.GetTarget(),
2564 						B_HANDLER_TOKEN, (void **)&handler);
2565 
2566 					// if this handler doesn't belong to us, we drop the message
2567 					if (handler != NULL && handler->Looper() != this) {
2568 						dropMessage = true;
2569 						handler = NULL;
2570 					}
2571 				}
2572 
2573 				if ((handler == NULL && !dropMessage) || usePreferred)
2574 					handler = _DetermineTarget(fLastMessage, handler);
2575 
2576 				unpack_cookie cookie;
2577 				while (_UnpackMessage(cookie, &fLastMessage, &handler, &usePreferred)) {
2578 					// if there is no target handler, the message is dropped
2579 					if (handler != NULL) {
2580 						_SanitizeMessage(fLastMessage, handler, usePreferred);
2581 
2582 						// Is this a scripting message?
2583 						if (fLastMessage->HasSpecifiers()) {
2584 							int32 index = 0;
2585 							// Make sure the current specifier is kosher
2586 							if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
2587 								handler = resolve_specifier(handler, fLastMessage);
2588 						}
2589 
2590 						if (handler != NULL)
2591 							handler = _TopLevelFilter(fLastMessage, handler);
2592 
2593 						if (handler != NULL)
2594 							DispatchMessage(fLastMessage, handler);
2595 					}
2596 
2597 					// Delete the current message
2598 					delete fLastMessage;
2599 					fLastMessage = NULL;
2600 				}
2601 			}
2602 
2603 			if (fTerminating) {
2604 				// we leave the looper locked when we quit
2605 				return;
2606 			}
2607 
2608 			Unlock();
2609 
2610 			// Are any messages on the port?
2611 			if (port_count(fMsgPort) > 0) {
2612 				// Do outer loop
2613 				dispatchNextMessage = false;
2614 			}
2615 		}
2616 	}
2617 }
2618 
2619 
2620 window_type
2621 BWindow::_ComposeType(window_look look, window_feel feel) const
2622 {
2623 	switch (feel) {
2624 		case B_NORMAL_WINDOW_FEEL:
2625 			switch (look) {
2626 				case B_TITLED_WINDOW_LOOK:
2627 					return B_TITLED_WINDOW;
2628 
2629 				case B_DOCUMENT_WINDOW_LOOK:
2630 					return B_DOCUMENT_WINDOW;
2631 
2632 				case B_BORDERED_WINDOW_LOOK:
2633 					return B_BORDERED_WINDOW;
2634 
2635 				default:
2636 					return B_UNTYPED_WINDOW;
2637 			}
2638 			break;
2639 
2640 		case B_MODAL_APP_WINDOW_FEEL:
2641 			if (look == B_MODAL_WINDOW_LOOK)
2642 				return B_MODAL_WINDOW;
2643 			break;
2644 
2645 		case B_FLOATING_APP_WINDOW_FEEL:
2646 			if (look == B_FLOATING_WINDOW_LOOK)
2647 				return B_FLOATING_WINDOW;
2648 			break;
2649 
2650 		default:
2651 			return B_UNTYPED_WINDOW;
2652 	}
2653 
2654 	return B_UNTYPED_WINDOW;
2655 }
2656 
2657 
2658 void
2659 BWindow::_DecomposeType(window_type type, window_look *_look,
2660 	window_feel *_feel) const
2661 {
2662 	switch (type) {
2663 		case B_DOCUMENT_WINDOW:
2664 			*_look = B_DOCUMENT_WINDOW_LOOK;
2665 			*_feel = B_NORMAL_WINDOW_FEEL;
2666 			break;
2667 
2668 		case B_MODAL_WINDOW:
2669 			*_look = B_MODAL_WINDOW_LOOK;
2670 			*_feel = B_MODAL_APP_WINDOW_FEEL;
2671 			break;
2672 
2673 		case B_FLOATING_WINDOW:
2674 			*_look = B_FLOATING_WINDOW_LOOK;
2675 			*_feel = B_FLOATING_APP_WINDOW_FEEL;
2676 			break;
2677 
2678 		case B_BORDERED_WINDOW:
2679 			*_look = B_BORDERED_WINDOW_LOOK;
2680 			*_feel = B_NORMAL_WINDOW_FEEL;
2681 			break;
2682 
2683 		case B_TITLED_WINDOW:
2684 		case B_UNTYPED_WINDOW:
2685 		default:
2686 			*_look = B_TITLED_WINDOW_LOOK;
2687 			*_feel = B_NORMAL_WINDOW_FEEL;
2688 			break;
2689 	}
2690 }
2691 
2692 
2693 void
2694 BWindow::_CreateTopView()
2695 {
2696 	STRACE(("_CreateTopView(): enter\n"));
2697 
2698 	BRect frame = fFrame.OffsetToCopy(B_ORIGIN);
2699 	fTopView = new BView(frame, "fTopView",
2700 		B_FOLLOW_ALL, B_WILL_DRAW);
2701 	fTopView->fTopLevelView = true;
2702 
2703 	//inhibit check_lock()
2704 	fLastViewToken = _get_object_token_(fTopView);
2705 
2706 	// set fTopView's owner, add it to window's eligible handler list
2707 	// and also set its next handler to be this window.
2708 
2709 	STRACE(("Calling setowner fTopView = %p this = %p.\n",
2710 		fTopView, this));
2711 
2712 	fTopView->_SetOwner(this);
2713 
2714 	// we can't use AddChild() because this is the top view
2715   	fTopView->_CreateSelf();
2716 
2717 	STRACE(("BuildTopView ended\n"));
2718 }
2719 
2720 
2721 /*!
2722 	Resizes the top view to match the window size. This will also
2723 	adapt the size of all its child views as needed.
2724 	This method has to be called whenever the frame of the window
2725 	changes.
2726 */
2727 void
2728 BWindow::_AdoptResize()
2729 {
2730 	// Resize views according to their resize modes - this
2731 	// saves us some server communication, as the server
2732 	// does the same with our views on its side.
2733 
2734 	int32 deltaWidth = (int32)(fFrame.Width() - fTopView->Bounds().Width());
2735 	int32 deltaHeight = (int32)(fFrame.Height() - fTopView->Bounds().Height());
2736 	if (deltaWidth == 0 && deltaHeight == 0)
2737 		return;
2738 
2739 	fTopView->_ResizeBy(deltaWidth, deltaHeight);
2740 }
2741 
2742 
2743 void
2744 BWindow::_SetFocus(BView *focusView, bool notifyInputServer)
2745 {
2746 	if (fFocus == focusView)
2747 		return;
2748 
2749 	// we notify the input server if we are passing focus
2750 	// from a view which has the B_INPUT_METHOD_AWARE to a one
2751 	// which does not, or vice-versa
2752 	if (notifyInputServer) {
2753 		bool inputMethodAware = false;
2754 		if (focusView)
2755 			inputMethodAware = focusView->Flags() & B_INPUT_METHOD_AWARE;
2756 		BMessage msg(inputMethodAware ? IS_FOCUS_IM_AWARE_VIEW : IS_UNFOCUS_IM_AWARE_VIEW);
2757 		BMessenger messenger(focusView);
2758 		BMessage reply;
2759 		if (focusView)
2760 			msg.AddMessenger("view", messenger);
2761 		_control_input_server_(&msg, &reply);
2762 	}
2763 
2764 	fFocus = focusView;
2765 	SetPreferredHandler(focusView);
2766 }
2767 
2768 
2769 /*!
2770 	\brief Determines the target of a message received for the
2771 		focus view.
2772 */
2773 BHandler *
2774 BWindow::_DetermineTarget(BMessage *message, BHandler *target)
2775 {
2776 	if (target == NULL)
2777 		target = this;
2778 
2779 	switch (message->what) {
2780 		case B_KEY_DOWN:
2781 		case B_KEY_UP:
2782 		{
2783 			// if we have a default button, it might want to hear
2784 			// about pressing the <enter> key
2785 			int32 rawChar;
2786 			if (DefaultButton() != NULL
2787 				&& message->FindInt32("raw_char", &rawChar) == B_OK
2788 				&& rawChar == B_ENTER)
2789 				return DefaultButton();
2790 
2791 			// supposed to fall through
2792 		}
2793 		case B_UNMAPPED_KEY_DOWN:
2794 		case B_UNMAPPED_KEY_UP:
2795 		case B_MODIFIERS_CHANGED:
2796 			// these messages should be dispatched by the focus view
2797 			if (CurrentFocus() != NULL)
2798 				return CurrentFocus();
2799 			break;
2800 
2801 		case B_MOUSE_DOWN:
2802 		case B_MOUSE_UP:
2803 		case B_MOUSE_MOVED:
2804 		case B_MOUSE_WHEEL_CHANGED:
2805 			// is there a token of the view that is currently under the mouse?
2806 			int32 token;
2807 			if (message->FindInt32("_view_token", &token) == B_OK) {
2808 				BView* view = _FindView(token);
2809 				if (view != NULL)
2810 					return view;
2811 			}
2812 
2813 			// if there is no valid token in the message, we try our
2814 			// luck with the last target, if available
2815 			if (fLastMouseMovedView != NULL)
2816 				return fLastMouseMovedView;
2817 			break;
2818 
2819 		case B_PULSE:
2820 		case B_QUIT_REQUESTED:
2821 			// TODO: test wether R5 will let BView dispatch these messages
2822 			return this;
2823 
2824 		case _MESSAGE_DROPPED_:
2825 			if (fLastMouseMovedView != NULL)
2826 				return fLastMouseMovedView;
2827 			break;
2828 
2829 		default:
2830 			break;
2831 	}
2832 
2833 	return target;
2834 }
2835 
2836 
2837 /*!
2838 	\brief Distributes the message to its intended targets. This is done for
2839 		all messages that should go to the preferred handler.
2840 
2841 	Returns \c true in case the message should still be dispatched
2842 */
2843 bool
2844 BWindow::_UnpackMessage(unpack_cookie& cookie, BMessage** _message, BHandler** _target,
2845 	bool* _usePreferred)
2846 {
2847 	if (cookie.message == NULL)
2848 		return false;
2849 
2850 	if (cookie.index == 0 && !cookie.tokens_scanned) {
2851 		// We were called the first time for this message
2852 
2853 		if (!*_usePreferred) {
2854 			// only consider messages targeted at the preferred handler
2855 			cookie.message = NULL;
2856 			return true;
2857 		}
2858 
2859 		// initialize our cookie
2860 		cookie.message = *_message;
2861 		cookie.focus = *_target;
2862 
2863 		if (cookie.focus != NULL)
2864 			cookie.focus_token = _get_object_token_(*_target);
2865 
2866 		if (fLastMouseMovedView != NULL && cookie.message->what == B_MOUSE_MOVED)
2867 			cookie.last_view_token = _get_object_token_(fLastMouseMovedView);
2868 
2869 		*_usePreferred = false;
2870 	}
2871 
2872 	_DequeueAll();
2873 
2874 	// distribute the message to all targets specified in the
2875 	// message directly (but not to the focus view)
2876 
2877 	for (int32 token; !cookie.tokens_scanned
2878 			&& cookie.message->FindInt32("_token", cookie.index, &token) == B_OK;
2879 			cookie.index++) {
2880 		// focus view is preferred and should get its message directly
2881 		if (token == cookie.focus_token) {
2882 			cookie.found_focus = true;
2883 			continue;
2884 		}
2885 		if (token == cookie.last_view_token)
2886 			continue;
2887 
2888 		BView* target = _FindView(token);
2889 		if (target == NULL)
2890 			continue;
2891 
2892 		*_message = new BMessage(*cookie.message);
2893 		*_target = target;
2894 		cookie.index++;
2895 		return true;
2896 	}
2897 
2898 	cookie.tokens_scanned = true;
2899 
2900 	// if there is a last mouse moved view, and the new focus is
2901 	// different, the previous view wants to get its B_EXITED_VIEW
2902 	// message
2903 	if (cookie.last_view_token != B_NULL_TOKEN && fLastMouseMovedView != NULL
2904 		&& fLastMouseMovedView != cookie.focus) {
2905 		*_message = new BMessage(*cookie.message);
2906 		*_target = fLastMouseMovedView;
2907 		cookie.last_view_token = B_NULL_TOKEN;
2908 		return true;
2909 	}
2910 
2911 	bool dispatchToFocus = true;
2912 
2913 	// check if the focus token is still valid (could have been removed in the mean time)
2914 	BHandler* handler;
2915 	if (gDefaultTokens.GetToken(cookie.focus_token, B_HANDLER_TOKEN, (void**)&handler) != B_OK
2916 		|| handler->Looper() != this)
2917 		dispatchToFocus = false;
2918 
2919 	if (dispatchToFocus && cookie.index > 0) {
2920 		// should this message still be dispatched by the focus view?
2921 		bool feedFocus;
2922 		if (!cookie.found_focus
2923 			&& (cookie.message->FindBool("_feed_focus", &feedFocus) != B_OK
2924 				|| feedFocus == false))
2925 			dispatchToFocus = false;
2926 	}
2927 
2928 	if (!dispatchToFocus) {
2929 		delete cookie.message;
2930 		cookie.message = NULL;
2931 		return false;
2932 	}
2933 
2934 	*_message = cookie.message;
2935 	*_target = cookie.focus;
2936 	*_usePreferred = true;
2937 	cookie.message = NULL;
2938 	return true;
2939 }
2940 
2941 
2942 /*!
2943 	Some messages don't get to the window in a shape an application should see.
2944 	This method is supposed to give a message the last grinding before
2945 	it's acceptable for the receiving application.
2946 */
2947 void
2948 BWindow::_SanitizeMessage(BMessage* message, BHandler* target, bool usePreferred)
2949 {
2950 	if (target == NULL)
2951 		return;
2952 
2953 	switch (message->what) {
2954 		case B_MOUSE_MOVED:
2955 		case B_MOUSE_UP:
2956 		case B_MOUSE_DOWN:
2957 		{
2958 			BPoint where;
2959 			if (message->FindPoint("screen_where", &where) != B_OK)
2960 				break;
2961 
2962 			// add local window coordinates
2963 			message->AddPoint("where", ConvertFromScreen(where));
2964 
2965 			BView* view = dynamic_cast<BView*>(target);
2966 			if (view != NULL) {
2967 				// add local view coordinates
2968 				message->AddPoint("be:view_where", view->ConvertFromScreen(where));
2969 
2970 				if (message->what == B_MOUSE_MOVED) {
2971 					// is there a token of the view that is currently under the mouse?
2972 					BView* viewUnderMouse = NULL;
2973 					int32 token;
2974 					if (message->FindInt32("_view_token", &token) == B_OK)
2975 						viewUnderMouse = _FindView(token);
2976 
2977 					// add transit information
2978 					int32 transit;
2979 					if (viewUnderMouse == view) {
2980 						// the mouse is over the target view
2981 						if (fLastMouseMovedView != view)
2982 							transit = B_ENTERED_VIEW;
2983 						else
2984 							transit = B_INSIDE_VIEW;
2985 					} else {
2986 						// the mouse is not over the target view
2987 						if (view == fLastMouseMovedView)
2988 							transit = B_EXITED_VIEW;
2989 						else
2990 							transit = B_OUTSIDE_VIEW;
2991 					}
2992 
2993 					message->AddInt32("be:transit", transit);
2994 
2995 					if (usePreferred || viewUnderMouse == NULL)
2996 						fLastMouseMovedView = viewUnderMouse;
2997 				}
2998 			}
2999 			break;
3000 		}
3001 
3002 		case _MESSAGE_DROPPED_:
3003 		{
3004 			uint32 originalWhat;
3005 			if (message->FindInt32("_original_what", (int32*)&originalWhat) == B_OK) {
3006 				message->what = originalWhat;
3007 				message->RemoveName("_original_what");
3008 			}
3009 			break;
3010 		}
3011 	}
3012 }
3013 
3014 
3015 /*!
3016 	This is called by BView::GetMouse() when a B_MOUSE_MOVED message
3017 	is removed from the queue.
3018 	It allows the window to update the last mouse moved view, and
3019 	let it decide if this message should be kept. It will also remove
3020 	the message from the queue.
3021 	You need to hold the message queue lock when calling this method!
3022 
3023 	\return true if this message can be used to get the mouse data from,
3024 	\return false if this is not meant for the public.
3025 */
3026 bool
3027 BWindow::_StealMouseMessage(BMessage* message, bool& deleteMessage)
3028 {
3029 	BMessage::Private messagePrivate(message);
3030 	if (!messagePrivate.UsePreferredTarget()) {
3031 		// this message is targeted at a specific handler, so we should
3032 		// not steal it
3033 		return false;
3034 	}
3035 
3036 	int32 token;
3037 	if (message->FindInt32("_token", 0, &token) == B_OK) {
3038 		// This message has other targets, so we can't remove it;
3039 		// just prevent it from being sent to the preferred handler
3040 		// again (if it should have gotten it at all).
3041 		bool feedFocus;
3042 		if (message->FindBool("_feed_focus", &feedFocus) == B_OK && feedFocus)
3043 			return false;
3044 
3045 		message->RemoveName("_feed_focus");
3046 		deleteMessage = false;
3047 	} else {
3048 		// The message is only thought for the preferred handler, so we
3049 		// can just remove it.
3050 		MessageQueue()->RemoveMessage(message);
3051 		deleteMessage = true;
3052 
3053 		if (message->what == B_MOUSE_MOVED) {
3054 			// We need to update the last mouse moved view, as this message
3055 			// won't make it to _SanitizeMessage() anymore
3056 			BView* viewUnderMouse = NULL;
3057 			int32 token;
3058 			if (message->FindInt32("_view_token", &token) == B_OK)
3059 				viewUnderMouse = _FindView(token);
3060 
3061 			fLastMouseMovedView = viewUnderMouse;
3062 		}
3063 	}
3064 
3065 	return true;
3066 }
3067 
3068 
3069 /*!
3070 	Handles keyboard input before it gets forwarded to the target handler.
3071 	This includes shortcut evaluation, keyboard navigation, etc.
3072 
3073 	\return handled if true, the event was already handled, and will not
3074 		be forwarded to the target handler.
3075 
3076 	TODO: must also convert the incoming key to the font encoding of the target
3077 */
3078 bool
3079 BWindow::_HandleKeyDown(BMessage* event)
3080 {
3081 	const char *string = NULL;
3082 	if (event->FindString("bytes", &string) != B_OK)
3083 		return false;
3084 
3085 	char key = string[0];
3086 
3087 	uint32 modifiers;
3088 	if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK)
3089 		modifiers = 0;
3090 
3091 	// handle BMenuBar key
3092 	if (key == B_ESCAPE && (modifiers & B_COMMAND_KEY) != 0
3093 		&& fKeyMenuBar) {
3094 		// TODO: ask Marc about 'fWaitingForMenu' member!
3095 
3096 		// fWaitingForMenu = true;
3097 		fKeyMenuBar->StartMenuBar(0, true, false, NULL);
3098 		return true;
3099 	}
3100 
3101 	// Keyboard navigation through views
3102 	// (B_OPTION_KEY makes BTextViews and friends navigable, even in editing mode)
3103 	if (key == B_TAB && (modifiers & (B_COMMAND_KEY | B_OPTION_KEY)) != 0) {
3104 		_KeyboardNavigation();
3105 		return true;
3106 	}
3107 
3108 	// Deskbar's Switcher
3109 	if (key == B_TAB && (modifiers & B_CONTROL_KEY) != 0) {
3110 		BMessenger deskbar(kDeskbarSignature);
3111 		int32 rawKey;
3112 		if (event->FindInt32("key", &rawKey) == B_OK
3113 			&& !event->HasInt32("be:key_repeat")
3114 			&& deskbar.IsValid()) {
3115 			// only send the first key press, no repeats
3116 			BMessage message('TASK');
3117 			message.AddInt32("key", rawKey);
3118 			message.AddInt32("modifiers", modifiers);
3119 			message.AddInt64("when", system_time());
3120 			message.AddInt32("team", Team());
3121 			deskbar.SendMessage(&message);
3122 		}
3123 		return true;
3124 	}
3125 
3126 	// Handle shortcuts
3127 	if ((modifiers & B_COMMAND_KEY) != 0) {
3128 		// Command+q has been pressed, so, we will quit
3129 		// the shortcut mechanism doesn't allow handlers outside the window
3130 		if (!fNoQuitShortcut && (key == 'Q' || key == 'q')) {
3131 			BMessage message(B_QUIT_REQUESTED);
3132 			message.AddBool("shortcut", true);
3133 
3134 			be_app->PostMessage(&message);
3135 			return true;
3136 		}
3137 
3138 		MenusBeginning();
3139 
3140 		Shortcut* shortcut = _FindShortcut(key, modifiers);
3141 		if (shortcut != NULL) {
3142 			// TODO: would be nice to move this functionality to
3143 			//	a Shortcut::Invoke() method - but since BMenu::InvokeItem()
3144 			//	(and BMenuItem::Invoke()) are private, I didn't want
3145 			//	to mess with them (BMenuItem::Invoke() is public in
3146 			//	Dano/Zeta, though, maybe we should just follow their
3147 			//	example)
3148 			if (shortcut->MenuItem() != NULL) {
3149 				BMenu* menu = shortcut->MenuItem()->Menu();
3150 				if (menu != NULL)
3151 					menu->InvokeItem(shortcut->MenuItem(), true);
3152 			} else {
3153 				BHandler* target = shortcut->Target();
3154 				if (target == NULL)
3155 					target = CurrentFocus();
3156 
3157 				if (shortcut->Message() != NULL) {
3158 					BMessage message(*shortcut->Message());
3159 
3160 					if (message.ReplaceInt64("when", system_time()) != B_OK)
3161 						message.AddInt64("when", system_time());
3162 					if (message.ReplaceBool("shortcut", true) != B_OK)
3163 						message.AddBool("shortcut", true);
3164 
3165 					PostMessage(&message, target);
3166 				}
3167 			}
3168 		}
3169 
3170 		MenusEnded();
3171 
3172 		// we always eat the event if the command key was pressed
3173 		return true;
3174 	}
3175 
3176 	// TODO: convert keys to the encoding of the target view
3177 
3178 	return false;
3179 }
3180 
3181 
3182 void
3183 BWindow::_KeyboardNavigation()
3184 {
3185 	BMessage *message = CurrentMessage();
3186 	if (message == NULL)
3187 		return;
3188 
3189 	const char *bytes;
3190 	uint32 modifiers;
3191 	if (message->FindString("bytes", &bytes) != B_OK
3192 		|| bytes[0] != B_TAB)
3193 		return;
3194 
3195 	message->FindInt32("modifiers", (int32*)&modifiers);
3196 
3197 	BView *nextFocus;
3198 	int32 jumpGroups = modifiers & B_CONTROL_KEY ? B_NAVIGABLE_JUMP : B_NAVIGABLE;
3199 	if (modifiers & B_SHIFT_KEY)
3200 		nextFocus = _FindPreviousNavigable(fFocus, jumpGroups);
3201 	else
3202 		nextFocus = _FindNextNavigable(fFocus, jumpGroups);
3203 
3204 	if (nextFocus && nextFocus != fFocus) {
3205 		nextFocus->MakeFocus(true);
3206 	}
3207 }
3208 
3209 
3210 BMessage *
3211 BWindow::ConvertToMessage(void *raw, int32 code)
3212 {
3213 	return BLooper::ConvertToMessage(raw, code);
3214 }
3215 
3216 
3217 BWindow::Shortcut *
3218 BWindow::_FindShortcut(uint32 key, uint32 modifiers)
3219 {
3220 	int32 count = fShortcuts.CountItems();
3221 
3222 	key = Shortcut::PrepareKey(key);
3223 	modifiers = Shortcut::PrepareModifiers(modifiers);
3224 
3225 	for (int32 index = 0; index < count; index++) {
3226 		Shortcut *shortcut = (Shortcut *)fShortcuts.ItemAt(index);
3227 
3228 		if (shortcut->Matches(key, modifiers))
3229 			return shortcut;
3230 	}
3231 
3232 	return NULL;
3233 }
3234 
3235 
3236 BView *
3237 BWindow::_FindView(int32 token)
3238 {
3239 	BHandler* handler;
3240 	if (gDefaultTokens.GetToken(token, B_HANDLER_TOKEN, (void**)&handler) != B_OK)
3241 		return NULL;
3242 
3243 	// the view must belong to us in order to be found by this method
3244 	BView* view = dynamic_cast<BView*>(handler);
3245 	if (view != NULL && view->Window() == this)
3246 		return view;
3247 
3248 	return NULL;
3249 }
3250 
3251 
3252 BView *
3253 BWindow::_FindView(BView *view, BPoint point) const
3254 {
3255 	// TODO: this is totally broken (bounds vs. frame) - since
3256 	//	BView::Bounds() potentially queries the app_server
3257 	//	anyway, we could just let the app_server answer this
3258 	//	query directly.
3259 	if (view->Bounds().Contains(point) && !view->fFirstChild)
3260 		return view;
3261 
3262 	BView *child = view->fFirstChild;
3263 
3264 	while (child != NULL) {
3265 		if ((view = _FindView(child, point)) != NULL)
3266 			return view;
3267 
3268 		child = child->fNextSibling;
3269 	}
3270 
3271 	return NULL;
3272 }
3273 
3274 
3275 BView *
3276 BWindow::_FindNextNavigable(BView* focus, uint32 flags)
3277 {
3278 	if (focus == NULL)
3279 		focus = fTopView;
3280 
3281 	BView* nextFocus = focus;
3282 
3283 	// Search the tree for views that accept focus (depth search)
3284 	while (true) {
3285 		if (nextFocus->fFirstChild)
3286 			nextFocus = nextFocus->fFirstChild;
3287 		else if (nextFocus->fNextSibling)
3288 			nextFocus = nextFocus->fNextSibling;
3289 		else {
3290 			// go to the nearest parent with a next sibling
3291 			while (!nextFocus->fNextSibling && nextFocus->fParent) {
3292 				nextFocus = nextFocus->fParent;
3293 			}
3294 
3295 			if (nextFocus == fTopView) {
3296 				// if we started with the top view, we traversed the whole tree already
3297 				if (nextFocus == focus)
3298 					return NULL;
3299 
3300 				nextFocus = nextFocus->fFirstChild;
3301 			} else
3302 				nextFocus = nextFocus->fNextSibling;
3303 		}
3304 
3305 		if (nextFocus == focus || nextFocus == NULL) {
3306 			// When we get here it means that the hole tree has been
3307 			// searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
3308 			return NULL;
3309 		}
3310 
3311 		if (!nextFocus->IsHidden() && (nextFocus->Flags() & flags) != 0)
3312 			return nextFocus;
3313 	}
3314 }
3315 
3316 
3317 BView *
3318 BWindow::_FindPreviousNavigable(BView* focus, uint32 flags)
3319 {
3320 	if (focus == NULL)
3321 		focus = fTopView;
3322 
3323 	BView* previousFocus = focus;
3324 
3325 	// Search the tree for the previous view that accept focus
3326 	while (true) {
3327 		if (previousFocus->fPreviousSibling) {
3328 			// find the last child in the previous sibling
3329 			previousFocus = _LastViewChild(previousFocus->fPreviousSibling);
3330 		} else {
3331 			previousFocus = previousFocus->fParent;
3332 			if (previousFocus == fTopView)
3333 				previousFocus = _LastViewChild(fTopView);
3334 		}
3335 
3336 		if (previousFocus == focus || previousFocus == NULL) {
3337 			// When we get here it means that the hole tree has been
3338 			// searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
3339 			return NULL;
3340 		}
3341 
3342 		if (!previousFocus->IsHidden() && (previousFocus->Flags() & flags) != 0)
3343 			return previousFocus;
3344 	}
3345 }
3346 
3347 
3348 /*!
3349 	Returns the last child in a view hierarchy.
3350 	Needed only by _FindPreviousNavigable().
3351 */
3352 BView *
3353 BWindow::_LastViewChild(BView *parent)
3354 {
3355 	while (true) {
3356 		BView *last = parent->fFirstChild;
3357 		if (last == NULL)
3358 			return parent;
3359 
3360 		while (last->fNextSibling) {
3361 			last = last->fNextSibling;
3362 		}
3363 
3364 		parent = last;
3365 	}
3366 }
3367 
3368 
3369 void
3370 BWindow::SetIsFilePanel(bool isFilePanel)
3371 {
3372 	fIsFilePanel = isFilePanel;
3373 }
3374 
3375 
3376 bool
3377 BWindow::IsFilePanel() const
3378 {
3379 	return fIsFilePanel;
3380 }
3381 
3382 
3383 //------------------------------------------------------------------------------
3384 // Virtual reserved Functions
3385 
3386 void BWindow::_ReservedWindow1() {}
3387 void BWindow::_ReservedWindow2() {}
3388 void BWindow::_ReservedWindow3() {}
3389 void BWindow::_ReservedWindow4() {}
3390 void BWindow::_ReservedWindow5() {}
3391 void BWindow::_ReservedWindow6() {}
3392 void BWindow::_ReservedWindow7() {}
3393 void BWindow::_ReservedWindow8() {}
3394 
3395 void
3396 BWindow::PrintToStream() const
3397 {
3398 	printf("BWindow '%s' data:\
3399 		Title			= %s\
3400 		Token			= %ld\
3401 		InTransaction 	= %s\
3402 		Active 			= %s\
3403 		fShowLevel		= %d\
3404 		Flags			= %lx\
3405 		send_port		= %ld\
3406 		receive_port	= %ld\
3407 		fTopView name	= %s\
3408 		focus view name	= %s\
3409 		lastMouseMoved	= %s\
3410 		fLink			= %p\
3411 		KeyMenuBar name	= %s\
3412 		DefaultButton	= %s\
3413 		# of shortcuts	= %ld",
3414 		Name(), fTitle,
3415 		_get_object_token_(this),
3416 		fInTransaction == true ? "yes" : "no",
3417 		fActive == true ? "yes" : "no",
3418 		fShowLevel,
3419 		fFlags,
3420 		fLink->SenderPort(),
3421 		fLink->ReceiverPort(),
3422 		fTopView != NULL ? fTopView->Name() : "NULL",
3423 		fFocus != NULL ? fFocus->Name() : "NULL",
3424 		fLastMouseMovedView != NULL ? fLastMouseMovedView->Name() : "NULL",
3425 		fLink,
3426 		fKeyMenuBar != NULL ? fKeyMenuBar->Name() : "NULL",
3427 		fDefaultButton != NULL ? fDefaultButton->Name() : "NULL",
3428 		fShortcuts.CountItems());
3429 /*
3430 	for( int32 i=0; i<accelList.CountItems(); i++){
3431 		_BCmdKey	*key = (_BCmdKey*)accelList.ItemAt(i);
3432 		printf("\tShortCut %ld: char %s\n\t\t message: \n", i, (key->key > 127)?"ASCII":"UNICODE");
3433 		key->message->PrintToStream();
3434 	}
3435 */
3436 	printf("\
3437 		topViewToken	= %ld\
3438 		isFilePanel		= %s\
3439 		MaskActivated	= %s\
3440 		pulseRate		= %lld\
3441 		waitingForMenu	= %s\
3442 		minimized		= %s\
3443 		Menu semaphore	= %ld\
3444 		maxZoomHeight	= %f\
3445 		maxZoomWidth	= %f\
3446 		minWindHeight	= %f\
3447 		minWindWidth	= %f\
3448 		maxWindHeight	= %f\
3449 		maxWindWidth	= %f\
3450 		frame			= ( %f, %f, %f, %f )\
3451 		look			= %d\
3452 		feel			= %d\
3453 		lastViewToken	= %ld\
3454 		pulseRunner		= %s\n",
3455 		fTopViewToken,
3456 		fIsFilePanel==true?"Yes":"No",
3457 		fMaskActivated==true?"Yes":"No",
3458 		fPulseRate,
3459 		fWaitingForMenu==true?"Yes":"No",
3460 		fMinimized==true?"Yes":"No",
3461 		fMenuSem,
3462 		fMaxZoomHeight,
3463 		fMaxZoomWidth,
3464 		fMinHeight,
3465 		fMinWidth,
3466 		fMaxHeight,
3467 		fMaxWidth,
3468 		fFrame.left, fFrame.top, fFrame.right, fFrame.bottom,
3469 		(int16)fLook,
3470 		(int16)fFeel,
3471 		fLastViewToken,
3472 		fPulseRunner != NULL ? "In place" : "NULL");
3473 }
3474 
3475 /*
3476 TODO list:
3477 	*) test arguments for SetWindowAligment
3478 	*) call hook functions: MenusBeginning, MenusEnded. Add menu activation code.
3479 */
3480 
3481