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