Ticket #559: Window.2.cpp

File Window.2.cpp, 67.6 KB (added by g.zachrisson@…, 18 years ago)

UPDATED patched Window.cpp

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