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