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