xref: /haiku/src/kits/interface/Window.cpp (revision c9060eb991e10e477ece52478d6743fc7691c143)
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 		fLink->StartMessage(AS_ACTIVATE_WINDOW);
1800 		fLink->Attach<bool>(active);
1801 		fLink->Flush();
1802 	}
1803 
1804 	Unlock();
1805 }
1806 
1807 
1808 void
1809 BWindow::WindowActivated(bool state)
1810 {
1811 	// hook function
1812 	// does nothing
1813 }
1814 
1815 
1816 void
1817 BWindow::ConvertToScreen(BPoint *point) const
1818 {
1819 	point->x += fFrame.left;
1820 	point->y += fFrame.top;
1821 }
1822 
1823 
1824 BPoint
1825 BWindow::ConvertToScreen(BPoint point) const
1826 {
1827 	return point + fFrame.LeftTop();
1828 }
1829 
1830 
1831 void
1832 BWindow::ConvertFromScreen(BPoint *point) const
1833 {
1834 	point->x -= fFrame.left;
1835 	point->y -= fFrame.top;
1836 }
1837 
1838 
1839 BPoint
1840 BWindow::ConvertFromScreen(BPoint point) const
1841 {
1842 	return point - fFrame.LeftTop();
1843 }
1844 
1845 
1846 void
1847 BWindow::ConvertToScreen(BRect *rect) const
1848 {
1849 	rect->OffsetBy(fFrame.LeftTop());
1850 }
1851 
1852 
1853 BRect
1854 BWindow::ConvertToScreen(BRect rect) const
1855 {
1856 	return rect.OffsetByCopy(fFrame.LeftTop());
1857 }
1858 
1859 
1860 void
1861 BWindow::ConvertFromScreen(BRect* rect) const
1862 {
1863 	rect->OffsetBy(-fFrame.left, -fFrame.top);
1864 }
1865 
1866 
1867 BRect
1868 BWindow::ConvertFromScreen(BRect rect) const
1869 {
1870 	return rect.OffsetByCopy(-fFrame.left, -fFrame.top);
1871 }
1872 
1873 
1874 bool
1875 BWindow::IsMinimized() const
1876 {
1877 	// Hiding takes precendence over minimization!!!
1878 	if (IsHidden())
1879 		return false;
1880 
1881 	return fMinimized;
1882 }
1883 
1884 
1885 BRect
1886 BWindow::Bounds() const
1887 {
1888 	return BRect(0, 0, fFrame.Width(), fFrame.Height());
1889 }
1890 
1891 
1892 BRect
1893 BWindow::Frame() const
1894 {
1895 	return fFrame;
1896 }
1897 
1898 
1899 const char *
1900 BWindow::Title() const
1901 {
1902 	return fTitle;
1903 }
1904 
1905 
1906 void
1907 BWindow::SetTitle(const char *title)
1908 {
1909 	if (title == NULL)
1910 		title = "";
1911 
1912 	free(fTitle);
1913 	fTitle = strdup(title);
1914 
1915 	_SetName(title);
1916 
1917 	// we notify the app_server so we can actually see the change
1918 	if (Lock()) {
1919 		fLink->StartMessage(AS_SET_WINDOW_TITLE);
1920 		fLink->AttachString(fTitle);
1921 		fLink->Flush();
1922 		Unlock();
1923 	}
1924 }
1925 
1926 
1927 bool
1928 BWindow::IsActive() const
1929 {
1930 	return fActive;
1931 }
1932 
1933 
1934 void
1935 BWindow::SetKeyMenuBar(BMenuBar *bar)
1936 {
1937 	fKeyMenuBar = bar;
1938 }
1939 
1940 
1941 BMenuBar *
1942 BWindow::KeyMenuBar() const
1943 {
1944 	return fKeyMenuBar;
1945 }
1946 
1947 
1948 bool
1949 BWindow::IsModal() const
1950 {
1951 	return fFeel == B_MODAL_SUBSET_WINDOW_FEEL
1952 		|| fFeel == B_MODAL_APP_WINDOW_FEEL
1953 		|| fFeel == B_MODAL_ALL_WINDOW_FEEL
1954 		|| fFeel == kMenuWindowFeel;
1955 }
1956 
1957 
1958 bool
1959 BWindow::IsFloating() const
1960 {
1961 	return fFeel == B_FLOATING_SUBSET_WINDOW_FEEL
1962 		|| fFeel == B_FLOATING_APP_WINDOW_FEEL
1963 		|| fFeel == B_FLOATING_ALL_WINDOW_FEEL;
1964 }
1965 
1966 
1967 status_t
1968 BWindow::AddToSubset(BWindow *window)
1969 {
1970 	if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL
1971 		|| (fFeel != B_MODAL_SUBSET_WINDOW_FEEL
1972 			&& fFeel != B_FLOATING_SUBSET_WINDOW_FEEL))
1973 		return B_BAD_VALUE;
1974 
1975 	if (!Lock())
1976 		return B_ERROR;
1977 
1978 	status_t status = B_ERROR;
1979 	fLink->StartMessage(AS_ADD_TO_SUBSET);
1980 	fLink->Attach<int32>(_get_object_token_(window));
1981 	fLink->FlushWithReply(status);
1982 
1983 	Unlock();
1984 
1985 	return status;
1986 }
1987 
1988 
1989 status_t
1990 BWindow::RemoveFromSubset(BWindow *window)
1991 {
1992 	if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL
1993 		|| (fFeel != B_MODAL_SUBSET_WINDOW_FEEL
1994 			&& fFeel != B_FLOATING_SUBSET_WINDOW_FEEL))
1995 		return B_BAD_VALUE;
1996 
1997 	if (!Lock())
1998 		return B_ERROR;
1999 
2000 	status_t status = B_ERROR;
2001 	fLink->StartMessage(AS_REMOVE_FROM_SUBSET);
2002 	fLink->Attach<int32>(_get_object_token_(window));
2003 	fLink->FlushWithReply(status);
2004 
2005 	Unlock();
2006 
2007 	return status;
2008 }
2009 
2010 
2011 status_t
2012 BWindow::Perform(perform_code d, void *arg)
2013 {
2014 	return BLooper::Perform(d, arg);
2015 }
2016 
2017 
2018 status_t
2019 BWindow::SetType(window_type type)
2020 {
2021 	window_look look;
2022 	window_feel feel;
2023 	_DecomposeType(type, &look, &feel);
2024 
2025 	status_t status = SetLook(look);
2026 	if (status == B_OK)
2027 		status = SetFeel(feel);
2028 
2029 	return status;
2030 }
2031 
2032 
2033 window_type
2034 BWindow::Type() const
2035 {
2036 	return _ComposeType(fLook, fFeel);
2037 }
2038 
2039 
2040 status_t
2041 BWindow::SetLook(window_look look)
2042 {
2043 	BAutolock locker(this);
2044 	if (!locker.IsLocked())
2045 		return B_BAD_VALUE;
2046 
2047 	fLink->StartMessage(AS_SET_LOOK);
2048 	fLink->Attach<int32>((int32)look);
2049 
2050 	status_t status = B_ERROR;
2051 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2052 		fLook = look;
2053 
2054 	// TODO: this could have changed the window size, and thus, we
2055 	//	need to get it from the server (and call _AdoptResize()).
2056 
2057 	return status;
2058 }
2059 
2060 
2061 window_look
2062 BWindow::Look() const
2063 {
2064 	return fLook;
2065 }
2066 
2067 
2068 status_t
2069 BWindow::SetFeel(window_feel feel)
2070 {
2071 	BAutolock locker(this);
2072 	if (!locker.IsLocked())
2073 		return B_BAD_VALUE;
2074 
2075 	fLink->StartMessage(AS_SET_FEEL);
2076 	fLink->Attach<int32>((int32)feel);
2077 
2078 	status_t status = B_ERROR;
2079 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2080 		fFeel = feel;
2081 
2082 	return status;
2083 }
2084 
2085 
2086 window_feel
2087 BWindow::Feel() const
2088 {
2089 	return fFeel;
2090 }
2091 
2092 
2093 status_t
2094 BWindow::SetFlags(uint32 flags)
2095 {
2096 	BAutolock locker(this);
2097 	if (!locker.IsLocked())
2098 		return B_BAD_VALUE;
2099 
2100 	fLink->StartMessage(AS_SET_FLAGS);
2101 	fLink->Attach<uint32>(flags);
2102 
2103 	int32 status = B_ERROR;
2104 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2105 		fFlags = flags;
2106 
2107 	return status;
2108 }
2109 
2110 
2111 uint32
2112 BWindow::Flags() const
2113 {
2114 	return fFlags;
2115 }
2116 
2117 
2118 status_t
2119 BWindow::SetWindowAlignment(window_alignment mode,
2120 	int32 h, int32 hOffset, int32 width, int32 widthOffset,
2121 	int32 v, int32 vOffset, int32 height, int32 heightOffset)
2122 {
2123 	if ((mode & (B_BYTE_ALIGNMENT | B_PIXEL_ALIGNMENT)) == 0
2124 		|| (hOffset >= 0 && hOffset <= h)
2125 		|| (vOffset >= 0 && vOffset <= v)
2126 		|| (widthOffset >= 0 && widthOffset <= width)
2127 		|| (heightOffset >= 0 && heightOffset <= height))
2128 		return B_BAD_VALUE;
2129 
2130 	// TODO: test if hOffset = 0 and set it to 1 if true.
2131 
2132 	if (!Lock())
2133 		return B_ERROR;
2134 
2135 	fLink->StartMessage(AS_SET_ALIGNMENT);
2136 	fLink->Attach<int32>((int32)mode);
2137 	fLink->Attach<int32>(h);
2138 	fLink->Attach<int32>(hOffset);
2139 	fLink->Attach<int32>(width);
2140 	fLink->Attach<int32>(widthOffset);
2141 	fLink->Attach<int32>(v);
2142 	fLink->Attach<int32>(vOffset);
2143 	fLink->Attach<int32>(height);
2144 	fLink->Attach<int32>(heightOffset);
2145 
2146 	status_t status = B_ERROR;
2147 	fLink->FlushWithReply(status);
2148 
2149 	Unlock();
2150 
2151 	return status;
2152 }
2153 
2154 
2155 status_t
2156 BWindow::GetWindowAlignment(window_alignment *mode,
2157 	int32 *h, int32 *hOffset, int32 *width, int32 *widthOffset,
2158 	int32 *v, int32 *vOffset, int32 *height, int32 *heightOffset) const
2159 {
2160 	if (!const_cast<BWindow *>(this)->Lock())
2161 		return B_ERROR;
2162 
2163 	fLink->StartMessage(AS_GET_ALIGNMENT);
2164 
2165 	status_t status;
2166 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK) {
2167 		fLink->Read<int32>((int32 *)mode);
2168 		fLink->Read<int32>(h);
2169 		fLink->Read<int32>(hOffset);
2170 		fLink->Read<int32>(width);
2171 		fLink->Read<int32>(widthOffset);
2172 		fLink->Read<int32>(v);
2173 		fLink->Read<int32>(hOffset);
2174 		fLink->Read<int32>(height);
2175 		fLink->Read<int32>(heightOffset);
2176 	}
2177 
2178 	const_cast<BWindow *>(this)->Unlock();
2179 	return status;
2180 }
2181 
2182 
2183 uint32
2184 BWindow::Workspaces() const
2185 {
2186 	if (!const_cast<BWindow *>(this)->Lock())
2187 		return 0;
2188 
2189 	uint32 workspaces = 0;
2190 
2191 	fLink->StartMessage(AS_GET_WORKSPACES);
2192 
2193 	status_t status;
2194 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2195 		fLink->Read<uint32>(&workspaces);
2196 
2197 	const_cast<BWindow *>(this)->Unlock();
2198 	return workspaces;
2199 }
2200 
2201 
2202 void
2203 BWindow::SetWorkspaces(uint32 workspaces)
2204 {
2205 	// TODO: don't forget about Tracker's background window.
2206 	if (fFeel != B_NORMAL_WINDOW_FEEL)
2207 		return;
2208 
2209 	if (Lock()) {
2210 		fLink->StartMessage(AS_SET_WORKSPACES);
2211 		fLink->Attach<uint32>(workspaces);
2212 		fLink->Flush();
2213 		Unlock();
2214 	}
2215 }
2216 
2217 
2218 BView *
2219 BWindow::LastMouseMovedView() const
2220 {
2221 	return fLastMouseMovedView;
2222 }
2223 
2224 
2225 void
2226 BWindow::MoveBy(float dx, float dy)
2227 {
2228 	if ((dx == 0.0 && dy == 0.0) || !Lock())
2229 		return;
2230 
2231 	fLink->StartMessage(AS_WINDOW_MOVE);
2232 	fLink->Attach<float>(dx);
2233 	fLink->Attach<float>(dy);
2234 
2235 	status_t status;
2236 	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2237 		fFrame.OffsetBy(dx, dy);
2238 
2239 	Unlock();
2240 }
2241 
2242 
2243 void
2244 BWindow::MoveTo(BPoint point)
2245 {
2246 	if (!Lock())
2247 		return;
2248 
2249 	point.x = roundf(point.x);
2250 	point.y = roundf(point.y);
2251 
2252 	if (fFrame.left != point.x || fFrame.top != point.y) {
2253 		float xOffset = point.x - fFrame.left;
2254 		float yOffset = point.y - fFrame.top;
2255 
2256 		MoveBy(xOffset, yOffset);
2257 	}
2258 
2259 	Unlock();
2260 }
2261 
2262 
2263 void
2264 BWindow::MoveTo(float x, float y)
2265 {
2266 	MoveTo(BPoint(x, y));
2267 }
2268 
2269 
2270 void
2271 BWindow::ResizeBy(float dx, float dy)
2272 {
2273 	if (!Lock())
2274 		return;
2275 
2276 	dx = roundf(dx);
2277 	dy = roundf(dy);
2278 
2279 	// stay in minimum & maximum frame limits
2280 	if (fFrame.Width() + dx < fMinWidth)
2281 		dx = fMinWidth - fFrame.Width();
2282 	if (fFrame.Width() + dx > fMaxWidth)
2283 		dx = fMaxWidth - fFrame.Width();
2284 	if (fFrame.Height() + dy < fMinHeight)
2285 		dy = fMinHeight - fFrame.Height();
2286 	if (fFrame.Height() + dy > fMaxHeight)
2287 		dy = fMaxHeight - fFrame.Height();
2288 
2289 	if (dx != 0.0 || dy != 0.0) {
2290 		fLink->StartMessage(AS_WINDOW_RESIZE);
2291 		fLink->Attach<float>(dx);
2292 		fLink->Attach<float>(dy);
2293 
2294 		status_t status;
2295 		if (fLink->FlushWithReply(status) == B_OK && status == B_OK) {
2296 			fFrame.SetRightBottom(fFrame.RightBottom() + BPoint(dx, dy));
2297 			_AdoptResize();
2298 		}
2299 	}
2300 	Unlock();
2301 }
2302 
2303 
2304 void
2305 BWindow::ResizeTo(float width, float height)
2306 {
2307 	if (Lock()) {
2308 		ResizeBy(width - fFrame.Width(), height - fFrame.Height());
2309 		Unlock();
2310 	}
2311 }
2312 
2313 
2314 void
2315 BWindow::Show()
2316 {
2317 	if (!fRunCalled) {
2318 		// this is the fist time Show() is called, which implicitly runs the looper
2319 		if (fLink->SenderPort() < B_OK) {
2320 			// We don't have valid app_server connection; there is no point
2321 			// in starting our looper
2322 			fThread = B_ERROR;
2323 			return;
2324 		} else
2325 			Run();
2326 	}
2327 
2328 	if (Lock()) {
2329 		fShowLevel++;
2330 
2331 		if (fShowLevel == 1) {
2332 			STRACE(("BWindow(%s): sending AS_SHOW_WINDOW message...\n", Name()));
2333 			fLink->StartMessage(AS_SHOW_WINDOW);
2334 			fLink->Flush();
2335 		}
2336 
2337 		Unlock();
2338 	}
2339 }
2340 
2341 
2342 void
2343 BWindow::Hide()
2344 {
2345 	if (!Lock())
2346 		return;
2347 
2348 	if (--fShowLevel == 0) {
2349 		fLink->StartMessage(AS_HIDE_WINDOW);
2350 		fLink->Flush();
2351 	}
2352 
2353 	Unlock();
2354 }
2355 
2356 
2357 bool
2358 BWindow::IsHidden() const
2359 {
2360 	return fShowLevel <= 0;
2361 }
2362 
2363 
2364 bool
2365 BWindow::QuitRequested()
2366 {
2367 	return BLooper::QuitRequested();
2368 }
2369 
2370 
2371 thread_id
2372 BWindow::Run()
2373 {
2374 	return BLooper::Run();
2375 }
2376 
2377 
2378 void
2379 BWindow::SetLayout(BLayout* layout)
2380 {
2381 	fTopView->SetLayout(layout);
2382 }
2383 
2384 
2385 BLayout*
2386 BWindow::GetLayout() const
2387 {
2388 	return fTopView->GetLayout();
2389 }
2390 
2391 
2392 void
2393 BWindow::InvalidateLayout(bool descendants)
2394 {
2395 	fTopView->InvalidateLayout(descendants);
2396 }
2397 
2398 
2399 status_t
2400 BWindow::GetSupportedSuites(BMessage *data)
2401 {
2402 	if (data == NULL)
2403 		return B_BAD_VALUE;
2404 
2405 	status_t status = data->AddString("suites", "suite/vnd.Be-window");
2406 	if (status == B_OK) {
2407 		BPropertyInfo propertyInfo(sWindowPropInfo, sWindowValueInfo);
2408 
2409 		status = data->AddFlat("messages", &propertyInfo);
2410 		if (status == B_OK)
2411 			status = BLooper::GetSupportedSuites(data);
2412 	}
2413 
2414 	return status;
2415 }
2416 
2417 
2418 BHandler *
2419 BWindow::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier,
2420 	int32 what,	const char *property)
2421 {
2422 	if (msg->what == B_WINDOW_MOVE_BY
2423 		|| msg->what == B_WINDOW_MOVE_TO)
2424 		return this;
2425 
2426 	BPropertyInfo propertyInfo(sWindowPropInfo);
2427 	if (propertyInfo.FindMatch(msg, index, specifier, what, property) >= 0) {
2428 		if (!strcmp(property, "View")) {
2429 			// we will NOT pop the current specifier
2430 			return fTopView;
2431 		} else if (!strcmp(property, "MenuBar")) {
2432 			if (fKeyMenuBar) {
2433 				msg->PopSpecifier();
2434 				return fKeyMenuBar;
2435 			} else {
2436 				BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
2437 				replyMsg.AddInt32("error", B_NAME_NOT_FOUND);
2438 				replyMsg.AddString("message", "This window doesn't have a main MenuBar");
2439 				msg->SendReply(&replyMsg);
2440 				return NULL;
2441 			}
2442 		} else
2443 			return this;
2444 	}
2445 
2446 	return BLooper::ResolveSpecifier(msg, index, specifier, what, property);
2447 }
2448 
2449 
2450 //	#pragma mark - Private Methods
2451 
2452 
2453 void
2454 BWindow::_InitData(BRect frame, const char* title, window_look look,
2455 	window_feel feel, uint32 flags,	uint32 workspace, int32 bitmapToken)
2456 {
2457 	STRACE(("BWindow::InitData()\n"));
2458 
2459 	if (be_app == NULL) {
2460 		debugger("You need a valid BApplication object before interacting with the app_server");
2461 		return;
2462 	}
2463 
2464 	frame.left = roundf(frame.left);
2465 	frame.top = roundf(frame.top);
2466 	frame.right = roundf(frame.right);
2467 	frame.bottom = roundf(frame.bottom);
2468 
2469 	fFrame = frame;
2470 
2471 	if (title == NULL)
2472 		title = "";
2473 
2474 	fTitle = strdup(title);
2475 
2476 	_SetName(title);
2477 
2478 	fFeel = feel;
2479 	fLook = look;
2480 	fFlags = flags | B_ASYNCHRONOUS_CONTROLS;
2481 
2482 	fInTransaction = false;
2483 	fUpdateRequested = false;
2484 	fActive = false;
2485 	fShowLevel = 0;
2486 
2487 	fTopView = NULL;
2488 	fFocus = NULL;
2489 	fLastMouseMovedView	= NULL;
2490 	fKeyMenuBar = NULL;
2491 	fDefaultButton = NULL;
2492 
2493 	// Shortcut 'Q' is handled in _HandleKeyDown() directly, as its message
2494 	// get sent to the application, and not one of our handlers.
2495 	// It is only installed for non-modal windows, though.
2496 	fNoQuitShortcut = IsModal();
2497 
2498 	if ((fFlags & B_NOT_CLOSABLE) == 0 && !IsModal()) {
2499 		// Modal windows default to non-closable, but you can add the shortcut manually,
2500 		// if a different behaviour is wanted
2501 		AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED));
2502 	}
2503 
2504 	AddShortcut('X', B_COMMAND_KEY, new BMessage(B_CUT), NULL);
2505 	AddShortcut('C', B_COMMAND_KEY, new BMessage(B_COPY), NULL);
2506 	AddShortcut('V', B_COMMAND_KEY, new BMessage(B_PASTE), NULL);
2507 	AddShortcut('A', B_COMMAND_KEY, new BMessage(B_SELECT_ALL), NULL);
2508 
2509 	// Window modifier keys
2510 	AddShortcut('M', B_COMMAND_KEY | B_CONTROL_KEY,
2511 		new BMessage(B_MINIMIZE), NULL);
2512 	AddShortcut('Z', B_COMMAND_KEY | B_CONTROL_KEY,
2513 		new BMessage(B_ZOOM), NULL);
2514 	AddShortcut('H', B_COMMAND_KEY | B_CONTROL_KEY,
2515 		new BMessage(B_HIDE_APPLICATION), NULL);
2516 
2517 	// We set the default pulse rate, but we don't start the pulse
2518 	fPulseRate = 500000;
2519 	fPulseRunner = NULL;
2520 
2521 	fIsFilePanel = false;
2522 
2523 	fMenuSem = -1;
2524 
2525 	fMinimized = false;
2526 
2527 	fMaxZoomHeight = 32768.0;
2528 	fMaxZoomWidth = 32768.0;
2529 	fMinHeight = 0.0;
2530 	fMinWidth = 0.0;
2531 	fMaxHeight = 32768.0;
2532 	fMaxWidth = 32768.0;
2533 
2534 	fLastViewToken = B_NULL_TOKEN;
2535 
2536 	// TODO: other initializations!
2537 
2538 	// Create the server-side window
2539 
2540 	port_id receivePort = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY, "w<app_server");
2541 	if (receivePort < B_OK) {
2542 		// TODO: huh?
2543 		debugger("Could not create BWindow's receive port, used for interacting with the app_server!");
2544 		delete this;
2545 		return;
2546 	}
2547 
2548 	STRACE(("BWindow::InitData(): contacting app_server...\n"));
2549 
2550 	// let app_server know that a window has been created.
2551 	fLink = new BPrivate::PortLink(
2552 		BApplication::Private::ServerLink()->SenderPort(), receivePort);
2553 
2554 	{
2555 		BPrivate::AppServerLink lockLink;
2556 			// we're talking to the server application using our own
2557 			// communication channel (fLink) - we better make sure no one
2558 			// interferes by locking that channel (which AppServerLink does
2559 			// implicetly)
2560 
2561 		if (bitmapToken < 0) {
2562 			fLink->StartMessage(AS_CREATE_WINDOW);
2563 		} else {
2564 			fLink->StartMessage(AS_CREATE_OFFSCREEN_WINDOW);
2565 			fLink->Attach<int32>(bitmapToken);
2566 		}
2567 
2568 		fLink->Attach<BRect>(fFrame);
2569 		fLink->Attach<uint32>((uint32)fLook);
2570 		fLink->Attach<uint32>((uint32)fFeel);
2571 		fLink->Attach<uint32>(fFlags);
2572 		fLink->Attach<uint32>(workspace);
2573 		fLink->Attach<int32>(_get_object_token_(this));
2574 		fLink->Attach<port_id>(receivePort);
2575 		fLink->Attach<port_id>(fMsgPort);
2576 		fLink->AttachString(title);
2577 
2578 		port_id sendPort;
2579 		int32 code;
2580 		if (fLink->FlushWithReply(code) == B_OK
2581 			&& code == B_OK
2582 			&& fLink->Read<port_id>(&sendPort) == B_OK) {
2583 			// read the frame size and its limits that were really
2584 			// enforced on the server side
2585 
2586 			fLink->Read<BRect>(&fFrame);
2587 			fLink->Read<float>(&fMinWidth);
2588 			fLink->Read<float>(&fMaxWidth);
2589 			fLink->Read<float>(&fMinHeight);
2590 			fLink->Read<float>(&fMaxHeight);
2591 
2592 			fMaxZoomWidth = fMaxWidth;
2593 			fMaxZoomHeight = fMaxHeight;
2594 		} else
2595 			sendPort = -1;
2596 
2597 		// Redirect our link to the new window connection
2598 		fLink->SetSenderPort(sendPort);
2599 	}
2600 
2601 	STRACE(("Server says that our send port is %ld\n", sendPort));
2602 	STRACE(("Window locked?: %s\n", IsLocked() ? "True" : "False"));
2603 
2604 	_CreateTopView();
2605 }
2606 
2607 
2608 //! Rename the handler and its thread
2609 void
2610 BWindow::_SetName(const char *title)
2611 {
2612 	if (title == NULL)
2613 		title = "";
2614 
2615 	// we will change BWindow's thread name to "w>window title"
2616 
2617 	char threadName[B_OS_NAME_LENGTH];
2618 	strcpy(threadName, "w>");
2619 #ifdef __HAIKU__
2620 	strlcat(threadName, title, B_OS_NAME_LENGTH);
2621 #else
2622 	int32 length = strlen(title);
2623 	length = min_c(length, B_OS_NAME_LENGTH - 3);
2624 	memcpy(threadName + 2, title, length);
2625 	threadName[length + 2] = '\0';
2626 #endif
2627 
2628 	// change the handler's name
2629 	SetName(threadName);
2630 
2631 	// if the message loop has been started...
2632 	if (Thread() >= B_OK)
2633 		rename_thread(Thread(), threadName);
2634 }
2635 
2636 
2637 //!	Reads all pending messages from the window port and put them into the queue.
2638 void
2639 BWindow::_DequeueAll()
2640 {
2641 	//	Get message count from port
2642 	int32 count = port_count(fMsgPort);
2643 
2644 	for (int32 i = 0; i < count; i++) {
2645 		BMessage *message = MessageFromPort(0);
2646 		if (message != NULL)
2647 			fDirectTarget->Queue()->AddMessage(message);
2648 	}
2649 }
2650 
2651 
2652 /*!	This here is an almost complete code duplication to BLooper::task_looper()
2653 	but with some important differences:
2654 	 a)	it uses the _DetermineTarget() method to tell what the later target of
2655 		a message will be, if no explicit target is supplied.
2656 	 b)	it calls _UnpackMessage() and _SanitizeMessage() to duplicate the message
2657 	 	to all of its intended targets, and to add all fields the target would
2658 	 	expect in such a message.
2659 
2660 	This is important because the app_server sends all input events to the
2661 	preferred handler, and expects them to be correctly distributed to their
2662 	intended targets.
2663 */
2664 void
2665 BWindow::task_looper()
2666 {
2667 	STRACE(("info: BWindow::task_looper() started.\n"));
2668 
2669 	// Check that looper is locked (should be)
2670 	AssertLocked();
2671 	Unlock();
2672 
2673 	if (IsLocked())
2674 		debugger("window must not be locked!");
2675 
2676 	while (!fTerminating) {
2677 		// Did we get a message?
2678 		BMessage* msg = MessageFromPort();
2679 		if (msg)
2680 			_AddMessagePriv(msg);
2681 
2682 		//	Get message count from port
2683 		int32 msgCount = port_count(fMsgPort);
2684 		for (int32 i = 0; i < msgCount; ++i) {
2685 			// Read 'count' messages from port (so we will not block)
2686 			// We use zero as our timeout since we know there is stuff there
2687 			msg = MessageFromPort(0);
2688 			// Add messages to queue
2689 			if (msg)
2690 				_AddMessagePriv(msg);
2691 		}
2692 
2693 		bool dispatchNextMessage = true;
2694 		while (!fTerminating && dispatchNextMessage) {
2695 			// Get next message from queue (assign to fLastMessage)
2696 			fLastMessage = fDirectTarget->Queue()->NextMessage();
2697 
2698 			// Lock the looper
2699 			if (!Lock())
2700 				break;
2701 
2702 			if (!fLastMessage) {
2703 				// No more messages: Unlock the looper and terminate the
2704 				// dispatch loop.
2705 				dispatchNextMessage = false;
2706 			} else {
2707 				// Get the target handler
2708 				BMessage::Private messagePrivate(fLastMessage);
2709 				bool usePreferred = messagePrivate.UsePreferredTarget();
2710 				BHandler *handler = NULL;
2711 				bool dropMessage = false;
2712 
2713 				if (usePreferred) {
2714 					handler = PreferredHandler();
2715 					if (handler == NULL)
2716 						handler = this;
2717 				} else {
2718 					gDefaultTokens.GetToken(messagePrivate.GetTarget(),
2719 						B_HANDLER_TOKEN, (void **)&handler);
2720 
2721 					// if this handler doesn't belong to us, we drop the message
2722 					if (handler != NULL && handler->Looper() != this) {
2723 						dropMessage = true;
2724 						handler = NULL;
2725 					}
2726 				}
2727 
2728 				if ((handler == NULL && !dropMessage) || usePreferred)
2729 					handler = _DetermineTarget(fLastMessage, handler);
2730 
2731 				unpack_cookie cookie;
2732 				while (_UnpackMessage(cookie, &fLastMessage, &handler, &usePreferred)) {
2733 					// if there is no target handler, the message is dropped
2734 					if (handler != NULL) {
2735 						_SanitizeMessage(fLastMessage, handler, usePreferred);
2736 
2737 						// Is this a scripting message?
2738 						if (fLastMessage->HasSpecifiers()) {
2739 							int32 index = 0;
2740 							// Make sure the current specifier is kosher
2741 							if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
2742 								handler = resolve_specifier(handler, fLastMessage);
2743 						}
2744 
2745 						if (handler != NULL)
2746 							handler = _TopLevelFilter(fLastMessage, handler);
2747 
2748 						if (handler != NULL)
2749 							DispatchMessage(fLastMessage, handler);
2750 					}
2751 
2752 					// Delete the current message
2753 					delete fLastMessage;
2754 					fLastMessage = NULL;
2755 				}
2756 			}
2757 
2758 			if (fTerminating) {
2759 				// we leave the looper locked when we quit
2760 				return;
2761 			}
2762 
2763 			Unlock();
2764 
2765 			// Are any messages on the port?
2766 			if (port_count(fMsgPort) > 0) {
2767 				// Do outer loop
2768 				dispatchNextMessage = false;
2769 			}
2770 		}
2771 	}
2772 }
2773 
2774 
2775 window_type
2776 BWindow::_ComposeType(window_look look, window_feel feel) const
2777 {
2778 	switch (feel) {
2779 		case B_NORMAL_WINDOW_FEEL:
2780 			switch (look) {
2781 				case B_TITLED_WINDOW_LOOK:
2782 					return B_TITLED_WINDOW;
2783 
2784 				case B_DOCUMENT_WINDOW_LOOK:
2785 					return B_DOCUMENT_WINDOW;
2786 
2787 				case B_BORDERED_WINDOW_LOOK:
2788 					return B_BORDERED_WINDOW;
2789 
2790 				default:
2791 					return B_UNTYPED_WINDOW;
2792 			}
2793 			break;
2794 
2795 		case B_MODAL_APP_WINDOW_FEEL:
2796 			if (look == B_MODAL_WINDOW_LOOK)
2797 				return B_MODAL_WINDOW;
2798 			break;
2799 
2800 		case B_FLOATING_APP_WINDOW_FEEL:
2801 			if (look == B_FLOATING_WINDOW_LOOK)
2802 				return B_FLOATING_WINDOW;
2803 			break;
2804 
2805 		default:
2806 			return B_UNTYPED_WINDOW;
2807 	}
2808 
2809 	return B_UNTYPED_WINDOW;
2810 }
2811 
2812 
2813 void
2814 BWindow::_DecomposeType(window_type type, window_look *_look,
2815 	window_feel *_feel) const
2816 {
2817 	switch (type) {
2818 		case B_DOCUMENT_WINDOW:
2819 			*_look = B_DOCUMENT_WINDOW_LOOK;
2820 			*_feel = B_NORMAL_WINDOW_FEEL;
2821 			break;
2822 
2823 		case B_MODAL_WINDOW:
2824 			*_look = B_MODAL_WINDOW_LOOK;
2825 			*_feel = B_MODAL_APP_WINDOW_FEEL;
2826 			break;
2827 
2828 		case B_FLOATING_WINDOW:
2829 			*_look = B_FLOATING_WINDOW_LOOK;
2830 			*_feel = B_FLOATING_APP_WINDOW_FEEL;
2831 			break;
2832 
2833 		case B_BORDERED_WINDOW:
2834 			*_look = B_BORDERED_WINDOW_LOOK;
2835 			*_feel = B_NORMAL_WINDOW_FEEL;
2836 			break;
2837 
2838 		case B_TITLED_WINDOW:
2839 		case B_UNTYPED_WINDOW:
2840 		default:
2841 			*_look = B_TITLED_WINDOW_LOOK;
2842 			*_feel = B_NORMAL_WINDOW_FEEL;
2843 			break;
2844 	}
2845 }
2846 
2847 
2848 void
2849 BWindow::_CreateTopView()
2850 {
2851 	STRACE(("_CreateTopView(): enter\n"));
2852 
2853 	BRect frame = fFrame.OffsetToCopy(B_ORIGIN);
2854 	fTopView = new BView(frame, "fTopView",
2855 		B_FOLLOW_ALL, B_WILL_DRAW);
2856 	fTopView->fTopLevelView = true;
2857 
2858 	//inhibit check_lock()
2859 	fLastViewToken = _get_object_token_(fTopView);
2860 
2861 	// set fTopView's owner, add it to window's eligible handler list
2862 	// and also set its next handler to be this window.
2863 
2864 	STRACE(("Calling setowner fTopView = %p this = %p.\n",
2865 		fTopView, this));
2866 
2867 	fTopView->_SetOwner(this);
2868 
2869 	// we can't use AddChild() because this is the top view
2870   	fTopView->_CreateSelf();
2871 
2872 	STRACE(("BuildTopView ended\n"));
2873 }
2874 
2875 
2876 /*!
2877 	Resizes the top view to match the window size. This will also
2878 	adapt the size of all its child views as needed.
2879 	This method has to be called whenever the frame of the window
2880 	changes.
2881 */
2882 void
2883 BWindow::_AdoptResize()
2884 {
2885 	// Resize views according to their resize modes - this
2886 	// saves us some server communication, as the server
2887 	// does the same with our views on its side.
2888 
2889 	int32 deltaWidth = (int32)(fFrame.Width() - fTopView->Bounds().Width());
2890 	int32 deltaHeight = (int32)(fFrame.Height() - fTopView->Bounds().Height());
2891 	if (deltaWidth == 0 && deltaHeight == 0)
2892 		return;
2893 
2894 	fTopView->_ResizeBy(deltaWidth, deltaHeight);
2895 }
2896 
2897 
2898 void
2899 BWindow::_SetFocus(BView *focusView, bool notifyInputServer)
2900 {
2901 	if (fFocus == focusView)
2902 		return;
2903 
2904 	// we notify the input server if we are passing focus
2905 	// from a view which has the B_INPUT_METHOD_AWARE to a one
2906 	// which does not, or vice-versa
2907 	if (notifyInputServer) {
2908 		bool inputMethodAware = false;
2909 		if (focusView)
2910 			inputMethodAware = focusView->Flags() & B_INPUT_METHOD_AWARE;
2911 		BMessage msg(inputMethodAware ? IS_FOCUS_IM_AWARE_VIEW : IS_UNFOCUS_IM_AWARE_VIEW);
2912 		BMessenger messenger(focusView);
2913 		BMessage reply;
2914 		if (focusView)
2915 			msg.AddMessenger("view", messenger);
2916 		_control_input_server_(&msg, &reply);
2917 	}
2918 
2919 	fFocus = focusView;
2920 	SetPreferredHandler(focusView);
2921 }
2922 
2923 
2924 /*!
2925 	\brief Determines the target of a message received for the
2926 		focus view.
2927 */
2928 BHandler *
2929 BWindow::_DetermineTarget(BMessage *message, BHandler *target)
2930 {
2931 	if (target == NULL)
2932 		target = this;
2933 
2934 	switch (message->what) {
2935 		case B_KEY_DOWN:
2936 		case B_KEY_UP:
2937 		{
2938 			// if we have a default button, it might want to hear
2939 			// about pressing the <enter> key
2940 			int32 rawChar;
2941 			if (DefaultButton() != NULL
2942 				&& message->FindInt32("raw_char", &rawChar) == B_OK
2943 				&& rawChar == B_ENTER)
2944 				return DefaultButton();
2945 
2946 			// supposed to fall through
2947 		}
2948 		case B_UNMAPPED_KEY_DOWN:
2949 		case B_UNMAPPED_KEY_UP:
2950 		case B_MODIFIERS_CHANGED:
2951 			// these messages should be dispatched by the focus view
2952 			if (CurrentFocus() != NULL)
2953 				return CurrentFocus();
2954 			break;
2955 
2956 		case B_MOUSE_DOWN:
2957 		case B_MOUSE_UP:
2958 		case B_MOUSE_MOVED:
2959 		case B_MOUSE_WHEEL_CHANGED:
2960 			// is there a token of the view that is currently under the mouse?
2961 			int32 token;
2962 			if (message->FindInt32("_view_token", &token) == B_OK) {
2963 				BView* view = _FindView(token);
2964 				if (view != NULL)
2965 					return view;
2966 			}
2967 
2968 			// if there is no valid token in the message, we try our
2969 			// luck with the last target, if available
2970 			if (fLastMouseMovedView != NULL)
2971 				return fLastMouseMovedView;
2972 			break;
2973 
2974 		case B_PULSE:
2975 		case B_QUIT_REQUESTED:
2976 			// TODO: test wether R5 will let BView dispatch these messages
2977 			return this;
2978 
2979 		case _MESSAGE_DROPPED_:
2980 			if (fLastMouseMovedView != NULL)
2981 				return fLastMouseMovedView;
2982 			break;
2983 
2984 		default:
2985 			break;
2986 	}
2987 
2988 	return target;
2989 }
2990 
2991 
2992 /*!
2993 	\brief Distributes the message to its intended targets. This is done for
2994 		all messages that should go to the preferred handler.
2995 
2996 	Returns \c true in case the message should still be dispatched
2997 */
2998 bool
2999 BWindow::_UnpackMessage(unpack_cookie& cookie, BMessage** _message, BHandler** _target,
3000 	bool* _usePreferred)
3001 {
3002 	if (cookie.message == NULL)
3003 		return false;
3004 
3005 	if (cookie.index == 0 && !cookie.tokens_scanned) {
3006 		// We were called the first time for this message
3007 
3008 		if (!*_usePreferred) {
3009 			// only consider messages targeted at the preferred handler
3010 			cookie.message = NULL;
3011 			return true;
3012 		}
3013 
3014 		// initialize our cookie
3015 		cookie.message = *_message;
3016 		cookie.focus = *_target;
3017 
3018 		if (cookie.focus != NULL)
3019 			cookie.focus_token = _get_object_token_(*_target);
3020 
3021 		if (fLastMouseMovedView != NULL && cookie.message->what == B_MOUSE_MOVED)
3022 			cookie.last_view_token = _get_object_token_(fLastMouseMovedView);
3023 
3024 		*_usePreferred = false;
3025 	}
3026 
3027 	_DequeueAll();
3028 
3029 	// distribute the message to all targets specified in the
3030 	// message directly (but not to the focus view)
3031 
3032 	for (int32 token; !cookie.tokens_scanned
3033 			&& cookie.message->FindInt32("_token", cookie.index, &token) == B_OK;
3034 			cookie.index++) {
3035 		// focus view is preferred and should get its message directly
3036 		if (token == cookie.focus_token) {
3037 			cookie.found_focus = true;
3038 			continue;
3039 		}
3040 		if (token == cookie.last_view_token)
3041 			continue;
3042 
3043 		BView* target = _FindView(token);
3044 		if (target == NULL)
3045 			continue;
3046 
3047 		*_message = new BMessage(*cookie.message);
3048 		*_target = target;
3049 		cookie.index++;
3050 		return true;
3051 	}
3052 
3053 	cookie.tokens_scanned = true;
3054 
3055 	// if there is a last mouse moved view, and the new focus is
3056 	// different, the previous view wants to get its B_EXITED_VIEW
3057 	// message
3058 	if (cookie.last_view_token != B_NULL_TOKEN && fLastMouseMovedView != NULL
3059 		&& fLastMouseMovedView != cookie.focus) {
3060 		*_message = new BMessage(*cookie.message);
3061 		*_target = fLastMouseMovedView;
3062 		cookie.last_view_token = B_NULL_TOKEN;
3063 		return true;
3064 	}
3065 
3066 	bool dispatchToFocus = true;
3067 
3068 	// check if the focus token is still valid (could have been removed in the mean time)
3069 	BHandler* handler;
3070 	if (gDefaultTokens.GetToken(cookie.focus_token, B_HANDLER_TOKEN, (void**)&handler) != B_OK
3071 		|| handler->Looper() != this)
3072 		dispatchToFocus = false;
3073 
3074 	if (dispatchToFocus && cookie.index > 0) {
3075 		// should this message still be dispatched by the focus view?
3076 		bool feedFocus;
3077 		if (!cookie.found_focus
3078 			&& (cookie.message->FindBool("_feed_focus", &feedFocus) != B_OK
3079 				|| feedFocus == false))
3080 			dispatchToFocus = false;
3081 	}
3082 
3083 	if (!dispatchToFocus) {
3084 		delete cookie.message;
3085 		cookie.message = NULL;
3086 		return false;
3087 	}
3088 
3089 	*_message = cookie.message;
3090 	*_target = cookie.focus;
3091 	*_usePreferred = true;
3092 	cookie.message = NULL;
3093 	return true;
3094 }
3095 
3096 
3097 /*!	Some messages don't get to the window in a shape an application should see.
3098 	This method is supposed to give a message the last grinding before
3099 	it's acceptable for the receiving application.
3100 */
3101 void
3102 BWindow::_SanitizeMessage(BMessage* message, BHandler* target, bool usePreferred)
3103 {
3104 	if (target == NULL)
3105 		return;
3106 
3107 	switch (message->what) {
3108 		case B_MOUSE_MOVED:
3109 		case B_MOUSE_UP:
3110 		case B_MOUSE_DOWN:
3111 		{
3112 			BPoint where;
3113 			if (message->FindPoint("screen_where", &where) != B_OK)
3114 				break;
3115 
3116 			BView* view = dynamic_cast<BView*>(target);
3117 			if (view != NULL) {
3118 				// add local view coordinates
3119 				message->AddPoint("be:view_where",
3120 					view->ConvertFromScreen(where));
3121 				message->AddPoint("where", view->ConvertFromScreen(where));
3122 
3123 				if (message->what == B_MOUSE_MOVED) {
3124 					// is there a token of the view that is currently under the mouse?
3125 					BView* viewUnderMouse = NULL;
3126 					int32 token;
3127 					if (message->FindInt32("_view_token", &token) == B_OK)
3128 						viewUnderMouse = _FindView(token);
3129 
3130 					// add transit information
3131 					int32 transit;
3132 					if (viewUnderMouse == view) {
3133 						// the mouse is over the target view
3134 						if (fLastMouseMovedView != view)
3135 							transit = B_ENTERED_VIEW;
3136 						else
3137 							transit = B_INSIDE_VIEW;
3138 					} else {
3139 						// the mouse is not over the target view
3140 						if (view == fLastMouseMovedView)
3141 							transit = B_EXITED_VIEW;
3142 						else
3143 							transit = B_OUTSIDE_VIEW;
3144 					}
3145 
3146 					message->AddInt32("be:transit", transit);
3147 
3148 					if (usePreferred || viewUnderMouse == NULL)
3149 						fLastMouseMovedView = viewUnderMouse;
3150 				}
3151 			} else {
3152 				// add local window coordinates
3153 				message->AddPoint("where", ConvertFromScreen(where));
3154 			}
3155 			break;
3156 		}
3157 
3158 		case _MESSAGE_DROPPED_:
3159 		{
3160 			uint32 originalWhat;
3161 			if (message->FindInt32("_original_what", (int32*)&originalWhat) == B_OK) {
3162 				message->what = originalWhat;
3163 				message->RemoveName("_original_what");
3164 			}
3165 			break;
3166 		}
3167 	}
3168 }
3169 
3170 
3171 /*!
3172 	This is called by BView::GetMouse() when a B_MOUSE_MOVED message
3173 	is removed from the queue.
3174 	It allows the window to update the last mouse moved view, and
3175 	let it decide if this message should be kept. It will also remove
3176 	the message from the queue.
3177 	You need to hold the message queue lock when calling this method!
3178 
3179 	\return true if this message can be used to get the mouse data from,
3180 	\return false if this is not meant for the public.
3181 */
3182 bool
3183 BWindow::_StealMouseMessage(BMessage* message, bool& deleteMessage)
3184 {
3185 	BMessage::Private messagePrivate(message);
3186 	if (!messagePrivate.UsePreferredTarget()) {
3187 		// this message is targeted at a specific handler, so we should
3188 		// not steal it
3189 		return false;
3190 	}
3191 
3192 	int32 token;
3193 	if (message->FindInt32("_token", 0, &token) == B_OK) {
3194 		// This message has other targets, so we can't remove it;
3195 		// just prevent it from being sent to the preferred handler
3196 		// again (if it should have gotten it at all).
3197 		bool feedFocus;
3198 		if (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus)
3199 			return false;
3200 
3201 		message->RemoveName("_feed_focus");
3202 		deleteMessage = false;
3203 	} else {
3204 		// The message is only thought for the preferred handler, so we
3205 		// can just remove it.
3206 		MessageQueue()->RemoveMessage(message);
3207 		deleteMessage = true;
3208 
3209 		if (message->what == B_MOUSE_MOVED) {
3210 			// We need to update the last mouse moved view, as this message
3211 			// won't make it to _SanitizeMessage() anymore
3212 			BView* viewUnderMouse = NULL;
3213 			int32 token;
3214 			if (message->FindInt32("_view_token", &token) == B_OK)
3215 				viewUnderMouse = _FindView(token);
3216 
3217 			fLastMouseMovedView = viewUnderMouse;
3218 		}
3219 	}
3220 
3221 	return true;
3222 }
3223 
3224 
3225 /*!
3226 	Handles keyboard input before it gets forwarded to the target handler.
3227 	This includes shortcut evaluation, keyboard navigation, etc.
3228 
3229 	\return handled if true, the event was already handled, and will not
3230 		be forwarded to the target handler.
3231 
3232 	TODO: must also convert the incoming key to the font encoding of the target
3233 */
3234 bool
3235 BWindow::_HandleKeyDown(BMessage* event)
3236 {
3237 	const char *string = NULL;
3238 	if (event->FindString("bytes", &string) != B_OK)
3239 		return false;
3240 
3241 	char key = string[0];
3242 
3243 	uint32 modifiers;
3244 	if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK)
3245 		modifiers = 0;
3246 
3247 	// handle BMenuBar key
3248 	if (key == B_ESCAPE && (modifiers & B_COMMAND_KEY) != 0 && fKeyMenuBar) {
3249 		fKeyMenuBar->StartMenuBar(0, true, false, NULL);
3250 		return true;
3251 	}
3252 
3253 	// Keyboard navigation through views
3254 	// (B_OPTION_KEY makes BTextViews and friends navigable, even in editing mode)
3255 	if (key == B_TAB && (modifiers & (B_COMMAND_KEY | B_OPTION_KEY)) != 0) {
3256 		_KeyboardNavigation();
3257 		return true;
3258 	}
3259 
3260 	// Deskbar's Switcher
3261 	if (key == B_TAB && (modifiers & B_CONTROL_KEY) != 0) {
3262 		BMessenger deskbar(kDeskbarSignature);
3263 		int32 rawKey;
3264 		if (event->FindInt32("key", &rawKey) == B_OK
3265 			&& !event->HasInt32("be:key_repeat")
3266 			&& deskbar.IsValid()) {
3267 			// only send the first key press, no repeats
3268 			BMessage message('TASK');
3269 			message.AddInt32("key", rawKey);
3270 			message.AddInt32("modifiers", modifiers);
3271 			message.AddInt64("when", system_time());
3272 			message.AddInt32("team", Team());
3273 			deskbar.SendMessage(&message);
3274 		}
3275 		return true;
3276 	}
3277 
3278 	// Optionally close window when the escape key is pressed
3279 	if (key == B_ESCAPE && (Flags() & B_CLOSE_ON_ESCAPE) != 0) {
3280 		BMessage message(B_QUIT_REQUESTED);
3281 		message.AddBool("shortcut", true);
3282 
3283 		PostMessage(&message);
3284 		return true;
3285 	}
3286 
3287 	if (key == B_FUNCTION_KEY) {
3288 		// Check for Print Screen
3289 		int32 rawKey;
3290 		if (event->FindInt32("key", &rawKey) == B_OK && rawKey == B_PRINT_KEY) {
3291 			// Get filename
3292 			BPath homePath;
3293 
3294 			if (find_directory(B_USER_DIRECTORY, &homePath) != B_OK) {
3295 				fprintf(stderr, "failed to find user home directory\n");
3296 				return true;
3297 			}
3298 
3299 			BPath path;
3300 			BEntry entry;
3301 			int32 index = 1;
3302 			do {
3303 				char filename[32];
3304 				sprintf(filename, "screen%ld.png", index++);
3305 				path = homePath;
3306 				path.Append(filename);
3307 				entry.SetTo(path.Path());
3308 			} while (entry.Exists());
3309 
3310 			// Get the screen bitmap
3311 			BScreen screen(this);
3312 			BBitmap* screenDump;
3313 			screen.GetBitmap(&screenDump, false);
3314 
3315 			// Dump to PNG
3316 			SaveToPNG(path.Path(), screen.Frame(), screenDump->ColorSpace(),
3317 				screenDump->Bits(),
3318 				screenDump->BitsLength(),
3319 				screenDump->BytesPerRow());
3320 
3321 			// Free the bitmap allocated by BScreen.GetBitmap
3322 			delete screenDump;
3323 
3324 			return true;
3325 		}
3326 	}
3327 
3328 	// Handle shortcuts
3329 	if ((modifiers & B_COMMAND_KEY) != 0) {
3330 		// Command+q has been pressed, so, we will quit
3331 		// the shortcut mechanism doesn't allow handlers outside the window
3332 		if (!fNoQuitShortcut && (key == 'Q' || key == 'q')) {
3333 			BMessage message(B_QUIT_REQUESTED);
3334 			message.AddBool("shortcut", true);
3335 
3336 			be_app->PostMessage(&message);
3337 			return true;
3338 		}
3339 
3340 		MenusBeginning();
3341 
3342 		Shortcut* shortcut = _FindShortcut(key, modifiers);
3343 		if (shortcut != NULL) {
3344 			// TODO: would be nice to move this functionality to
3345 			//	a Shortcut::Invoke() method - but since BMenu::InvokeItem()
3346 			//	(and BMenuItem::Invoke()) are private, I didn't want
3347 			//	to mess with them (BMenuItem::Invoke() is public in
3348 			//	Dano/Zeta, though, maybe we should just follow their
3349 			//	example)
3350 			if (shortcut->MenuItem() != NULL) {
3351 				BMenu* menu = shortcut->MenuItem()->Menu();
3352 				if (menu != NULL) {
3353 					MenuPrivate(menu).InvokeItem(shortcut->MenuItem(),
3354 												true);
3355 				}
3356 			} else {
3357 				BHandler* target = shortcut->Target();
3358 				if (target == NULL)
3359 					target = CurrentFocus();
3360 
3361 				if (shortcut->Message() != NULL) {
3362 					BMessage message(*shortcut->Message());
3363 
3364 					if (message.ReplaceInt64("when", system_time()) != B_OK)
3365 						message.AddInt64("when", system_time());
3366 					if (message.ReplaceBool("shortcut", true) != B_OK)
3367 						message.AddBool("shortcut", true);
3368 
3369 					PostMessage(&message, target);
3370 				}
3371 			}
3372 		}
3373 
3374 		MenusEnded();
3375 
3376 		// we always eat the event if the command key was pressed
3377 		return true;
3378 	}
3379 
3380 	// TODO: convert keys to the encoding of the target view
3381 
3382 	return false;
3383 }
3384 
3385 
3386 void
3387 BWindow::_KeyboardNavigation()
3388 {
3389 	BMessage *message = CurrentMessage();
3390 	if (message == NULL)
3391 		return;
3392 
3393 	const char *bytes;
3394 	uint32 modifiers;
3395 	if (message->FindString("bytes", &bytes) != B_OK
3396 		|| bytes[0] != B_TAB)
3397 		return;
3398 
3399 	message->FindInt32("modifiers", (int32*)&modifiers);
3400 
3401 	BView *nextFocus;
3402 	int32 jumpGroups = modifiers & B_CONTROL_KEY ? B_NAVIGABLE_JUMP : B_NAVIGABLE;
3403 	if (modifiers & B_SHIFT_KEY)
3404 		nextFocus = _FindPreviousNavigable(fFocus, jumpGroups);
3405 	else
3406 		nextFocus = _FindNextNavigable(fFocus, jumpGroups);
3407 
3408 	if (nextFocus && nextFocus != fFocus) {
3409 		nextFocus->MakeFocus(true);
3410 	}
3411 }
3412 
3413 
3414 BMessage *
3415 BWindow::ConvertToMessage(void *raw, int32 code)
3416 {
3417 	return BLooper::ConvertToMessage(raw, code);
3418 }
3419 
3420 
3421 BWindow::Shortcut *
3422 BWindow::_FindShortcut(uint32 key, uint32 modifiers)
3423 {
3424 	int32 count = fShortcuts.CountItems();
3425 
3426 	key = Shortcut::PrepareKey(key);
3427 	modifiers = Shortcut::PrepareModifiers(modifiers);
3428 
3429 	for (int32 index = 0; index < count; index++) {
3430 		Shortcut *shortcut = (Shortcut *)fShortcuts.ItemAt(index);
3431 
3432 		if (shortcut->Matches(key, modifiers))
3433 			return shortcut;
3434 	}
3435 
3436 	return NULL;
3437 }
3438 
3439 
3440 BView *
3441 BWindow::_FindView(int32 token)
3442 {
3443 	BHandler* handler;
3444 	if (gDefaultTokens.GetToken(token, B_HANDLER_TOKEN, (void**)&handler) != B_OK)
3445 		return NULL;
3446 
3447 	// the view must belong to us in order to be found by this method
3448 	BView* view = dynamic_cast<BView*>(handler);
3449 	if (view != NULL && view->Window() == this)
3450 		return view;
3451 
3452 	return NULL;
3453 }
3454 
3455 
3456 BView*
3457 BWindow::_FindView(BView* view, BPoint point) const
3458 {
3459 	// point is assumed to be already in view's coordinates
3460 	// TODO: since BView::Bounds() potentially queries the app_server anyway,
3461 	// we could just let the app_server answer this query directly.
3462 	if (view->Bounds().Contains(point) && !view->fFirstChild)
3463 		return view;
3464 
3465 	BView* child = view->fFirstChild;
3466 
3467 	while (child != NULL) {
3468 		BPoint childPoint = point - child->LeftTop();
3469 		if ((view = _FindView(child, childPoint)) != NULL)
3470 			return view;
3471 
3472 		child = child->fNextSibling;
3473 	}
3474 
3475 	return NULL;
3476 }
3477 
3478 
3479 BView*
3480 BWindow::_FindNextNavigable(BView* focus, uint32 flags)
3481 {
3482 	if (focus == NULL)
3483 		focus = fTopView;
3484 
3485 	BView* nextFocus = focus;
3486 
3487 	// Search the tree for views that accept focus (depth search)
3488 	while (true) {
3489 		if (nextFocus->fFirstChild)
3490 			nextFocus = nextFocus->fFirstChild;
3491 		else if (nextFocus->fNextSibling)
3492 			nextFocus = nextFocus->fNextSibling;
3493 		else {
3494 			// go to the nearest parent with a next sibling
3495 			while (!nextFocus->fNextSibling && nextFocus->fParent) {
3496 				nextFocus = nextFocus->fParent;
3497 			}
3498 
3499 			if (nextFocus == fTopView) {
3500 				// if we started with the top view, we traversed the whole tree already
3501 				if (nextFocus == focus)
3502 					return NULL;
3503 
3504 				nextFocus = nextFocus->fFirstChild;
3505 			} else
3506 				nextFocus = nextFocus->fNextSibling;
3507 		}
3508 
3509 		if (nextFocus == focus || nextFocus == NULL) {
3510 			// When we get here it means that the hole tree has been
3511 			// searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
3512 			return NULL;
3513 		}
3514 
3515 		if (!nextFocus->IsHidden() && (nextFocus->Flags() & flags) != 0)
3516 			return nextFocus;
3517 	}
3518 }
3519 
3520 
3521 BView *
3522 BWindow::_FindPreviousNavigable(BView* focus, uint32 flags)
3523 {
3524 	if (focus == NULL)
3525 		focus = fTopView;
3526 
3527 	BView* previousFocus = focus;
3528 
3529 	// Search the tree for the previous view that accept focus
3530 	while (true) {
3531 		if (previousFocus->fPreviousSibling) {
3532 			// find the last child in the previous sibling
3533 			previousFocus = _LastViewChild(previousFocus->fPreviousSibling);
3534 		} else {
3535 			previousFocus = previousFocus->fParent;
3536 			if (previousFocus == fTopView)
3537 				previousFocus = _LastViewChild(fTopView);
3538 		}
3539 
3540 		if (previousFocus == focus || previousFocus == NULL) {
3541 			// When we get here it means that the hole tree has been
3542 			// searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
3543 			return NULL;
3544 		}
3545 
3546 		if (!previousFocus->IsHidden() && (previousFocus->Flags() & flags) != 0)
3547 			return previousFocus;
3548 	}
3549 }
3550 
3551 
3552 /*!
3553 	Returns the last child in a view hierarchy.
3554 	Needed only by _FindPreviousNavigable().
3555 */
3556 BView *
3557 BWindow::_LastViewChild(BView *parent)
3558 {
3559 	while (true) {
3560 		BView *last = parent->fFirstChild;
3561 		if (last == NULL)
3562 			return parent;
3563 
3564 		while (last->fNextSibling) {
3565 			last = last->fNextSibling;
3566 		}
3567 
3568 		parent = last;
3569 	}
3570 }
3571 
3572 
3573 void
3574 BWindow::SetIsFilePanel(bool isFilePanel)
3575 {
3576 	fIsFilePanel = isFilePanel;
3577 }
3578 
3579 
3580 bool
3581 BWindow::IsFilePanel() const
3582 {
3583 	return fIsFilePanel;
3584 }
3585 
3586 
3587 //	#pragma mark - C++ binary compatibility kludge
3588 
3589 
3590 extern "C" void
3591 _ReservedWindow1__7BWindow()
3592 {
3593 	// SetLayout()
3594 }
3595 
3596 
3597 void BWindow::_ReservedWindow2() {}
3598 void BWindow::_ReservedWindow3() {}
3599 void BWindow::_ReservedWindow4() {}
3600 void BWindow::_ReservedWindow5() {}
3601 void BWindow::_ReservedWindow6() {}
3602 void BWindow::_ReservedWindow7() {}
3603 void BWindow::_ReservedWindow8() {}
3604 
3605