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