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