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