home *** CD-ROM | disk | FTP | other *** search
/ C Programming Starter Kit 2.0 / SamsPublishing-CProgrammingStarterKit-v2.0-Win31.iso / bc45 / owlsrc.pak / WINDOW.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-07-24  |  66.9 KB  |  2,538 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows
  3. // (C) Copyright 1991, 1994 by Borland International, All Rights Reserved
  4. //
  5. //   Implementation of TWindow.  This defines the basic behavior of all
  6. //   Windows.
  7. //----------------------------------------------------------------------------
  8. #include <owl/owlpch.h>
  9. #include <owl/window.h>
  10. #include <owl/applicat.h>
  11. #include <owl/appdict.h>
  12. #include <owl/scroller.h>
  13. #include <owl/gdiobjec.h>
  14. #include <owl/menu.h>
  15. #include <owl/framewin.h>
  16. #include <stdlib.h>
  17.  
  18. DIAG_DECLARE_GROUP(OwlMsg);  // diagnostic group for message tracing
  19. DIAG_DEFINE_GROUP_INIT(OWL_INI, OwlWin, 1, 0);  // diagnostic group for windows
  20.  
  21. //
  22. // define to build registered Window's classname using the module name
  23. //
  24. //#define _OWL_BUILDUP_CLASSNAME
  25.  
  26. //
  27. // define to use rtti to create unique window ids for the message cache
  28. //
  29. #if defined(__BORLANDC__) && !defined(BI_NO_RTTI)
  30. # define OWL_RTTI_MSGCACHE
  31. # define TYPE_UNIQUE_UINT32(t)    reinterpret_cast<uint32>(typeid(t).tpp)
  32. #endif
  33.  
  34. //
  35. // Externs defined in owl.cpp
  36. //
  37. extern LRESULT CALLBACK __export InitWndProc(HWND, uint, WPARAM, LPARAM);
  38.  
  39. extern WNDPROC       CreateInstanceThunk(TWindow*);
  40. extern void          FreeInstanceThunk(WNDPROC);
  41. extern void _OWLFUNC SetCreationWindow(TWindow*);
  42. extern uint _OWLDATA GetWindowPtrMsgId;
  43.  
  44. //$---------------------------------------------------------------------------
  45.  
  46. DEFINE_RESPONSE_TABLE(TWindow)
  47.   EV_WM_SETCURSOR,
  48.   EV_WM_SIZE,
  49.   EV_WM_MOVE,
  50.   EV_WM_COMPAREITEM,
  51.   EV_WM_DELETEITEM,
  52.   EV_WM_DRAWITEM,
  53.   EV_WM_MEASUREITEM,
  54.   EV_WM_VSCROLL,
  55.   EV_WM_HSCROLL,
  56.   EV_WM_CHILDINVALID,
  57.   EV_WM_ERASEBKGND,
  58.   EV_WM_PAINT,
  59.   EV_WM_LBUTTONDOWN,
  60.   EV_WM_KILLFOCUS,
  61. #if defined(BI_PLAT_WIN32)
  62.   EV_MESSAGE(WM_CTLCOLORMSGBOX, EvWin32CtlColor),
  63.   EV_MESSAGE(WM_CTLCOLOREDIT, EvWin32CtlColor),
  64.   EV_MESSAGE(WM_CTLCOLORLISTBOX, EvWin32CtlColor),
  65.   EV_MESSAGE(WM_CTLCOLORBTN, EvWin32CtlColor),
  66.   EV_MESSAGE(WM_CTLCOLORDLG, EvWin32CtlColor),
  67.   EV_MESSAGE(WM_CTLCOLORSCROLLBAR, EvWin32CtlColor),
  68.   EV_MESSAGE(WM_CTLCOLORSTATIC, EvWin32CtlColor),
  69. #endif
  70.   EV_WM_CREATE,
  71.   EV_WM_CLOSE,
  72.   EV_WM_DESTROY,
  73.   EV_WM_NCDESTROY,
  74.   EV_COMMAND(CM_EXIT, CmExit),
  75.   EV_WM_QUERYENDSESSION,
  76.   EV_WM_SYSCOLORCHANGE,
  77.   EV_WM_INITMENUPOPUP,
  78. END_RESPONSE_TABLE;
  79.  
  80.  
  81. //$---------------------------------------------------------------------------
  82. //
  83. //
  84. TCommandEnabler::TCommandEnabler(uint id, HWND hWndReceiver)
  85.   : Id(id), HWndReceiver(hWndReceiver)
  86. {
  87.   Handled = false;
  88. }
  89.  
  90. void
  91. TCommandEnabler::Enable(bool)
  92. {
  93.   Handled = true;
  94. }
  95.  
  96. //$---------------------------------------------------------------------------
  97.  
  98. //
  99. // constructor for a TWindow.  if a parent window is passed, adds the TWindow
  100. // to its parent's list of children
  101. //
  102. TWindow::TWindow(TWindow*        parent,
  103.                  const char far* title,
  104.                  TModule*        module)
  105. {
  106.   Thunk = 0;  // remember that we still need to initialize
  107.   Init(parent, title, module);
  108. }
  109.  
  110. //$---------------------------------------------------------------------------
  111. //
  112. // constructor for a TWindow which is being used as an alias for a
  113. // non-OWL window.  This ctor is generally not used by derived classes
  114. //
  115. TWindow::TWindow(HWND hWnd, TModule* module)
  116. {
  117.   // Perform preliminary initialization
  118.   //
  119.   HWindow = hWnd;
  120.   Title = 0;
  121.   Thunk = 0;  // remember that we still need to initialize
  122.  
  123.   // If the receiver's parent is an OWL window then pass the window to Init()
  124.   // so the receiver can be added to the parent's list of children
  125.   //
  126.   HWND      hParent = GetParent();
  127.   TWindow*  parent = hParent ? GetWindowPtr(hParent) : 0;
  128.   Init(parent, module);
  129.  
  130.   // Thunk the window
  131.   //
  132.   SubclassWindowFunction();
  133.  
  134.   // Copy over the title, styles, the coordinates & the id
  135.   //
  136.   GetWindowTextTitle();
  137.   GetHWndState();
  138.  
  139.   // Keep track that this is an alias object & that it is already created
  140.   //
  141.   SetFlag(wfAlias | wfFullyCreated);
  142.  
  143.   // Unique id may have been set inadvertantly to TWindow by the above
  144.   // GetWindowTextTitle, et. al. Reset it just in case
  145.   //
  146.   SetUniqueId();
  147. }
  148.  
  149. //$---------------------------------------------------------------------------
  150. //
  151. // Protected constructor for use by immediate virtually derived classes.
  152. // Immediate derivitives must call Init() before constructions are done.
  153. //
  154. TWindow::TWindow()
  155. {
  156.   Thunk = 0;  // remember that we still need to initialize
  157. }
  158.  
  159. //
  160. // Normal initialization of a default constructed TWindow. Is ignored
  161. // if called more than once.
  162. //
  163. void
  164. TWindow::Init(TWindow*        parent,
  165.               const char far* title,
  166.               TModule*        module)
  167. {
  168.   if (!Thunk) {
  169.     Init(parent, module);
  170.     Title = strnewdup(title);
  171.     DefaultProc = 0;
  172.     HWindow = 0;
  173.     EnableAutoCreate();
  174.  
  175.     // initialize creation attributes
  176.     //
  177.     Attr.Style = WS_CHILD | WS_VISIBLE;
  178.     Attr.X = Attr.Y = Attr.W = Attr.H = 0;
  179.     Attr.ExStyle = 0;
  180.     Attr.Id = 0;
  181.   }
  182. }
  183.  
  184. //
  185. // Private initializer function that does the bulk of the work
  186. //
  187. void
  188. TWindow::Init(TWindow* parent, TModule* module)
  189. {
  190.   ZOrder = 0;
  191.   ChildList = 0;
  192.   Flags = wfDeleteOnClose;
  193.   TransferBuffer = 0;
  194.   Parent = parent;
  195.   Attr.Param = 0;
  196.   Attr.Menu = 0;
  197.   Attr.AccelTable = 0;
  198.   hAccel = 0;
  199.   Thunk = ::CreateInstanceThunk(this);
  200.   Scroller = 0;
  201.  
  202.   CursorModule = 0;
  203.   CursorResId = 0;
  204.   HCursor = 0;
  205.   BkgndColor = NoColor;
  206.  
  207.   if (Parent)
  208.     Parent->AddChild(this);
  209.   else
  210.     SiblingList = 0;
  211.  
  212.   // Use passed module if one, else get parent's. If no parent, use app
  213.   //
  214.   if (Parent) {
  215.     Application = Parent->GetApplication();
  216.     Module = module ? module : Parent->GetModule();
  217.   }
  218.   else {
  219.     Module = module ? module : 0;
  220.     Application = TYPESAFE_DOWNCAST(Module,TApplication);
  221.     if (!Application) {
  222.       Application = ::GetApplicationObject();
  223.       if (!Module)
  224.         Module = Application;
  225.     }
  226.     CHECK(Application);
  227.   }
  228.   SetUniqueId();
  229.  
  230.   TRACEX(OwlWin, 1, "TWindow constructed @" << (void*)this);
  231. }
  232.  
  233. //
  234. // installs the thunk as the window function and saves the old window function
  235. // in "DefaultProc"
  236. //
  237. void
  238. TWindow::SubclassWindowFunction()
  239. {
  240.   PRECONDITION(HWindow != 0);
  241. #if defined(BI_PLAT_WIN32)
  242.   uint32 processId;
  243.   ::GetWindowThreadProcessId(HWindow, &processId);
  244.   if (processId == ::GetCurrentProcessId()) {
  245. #else
  246.   if (::GetWindowTask(HWindow) == ::GetCurrentTask()) {
  247. #endif
  248.     DefaultProc = (WNDPROC)SetWindowLong(GWL_WNDPROC, uint32(GetThunk()));
  249.     CHECK(DefaultProc != GetThunk());  // can only be called once!
  250.   }
  251. }
  252.  
  253. //
  254. // Destructor for a TWindow: disposes of each window in its ChildList
  255. // and removes itself from a non-0 parent's list of children
  256. //
  257. // Destroys a still-associated MS-Windows interface element and frees
  258. // the instance thunk used for association of an MS-Windows element
  259. // to the TWindow
  260. //
  261. // Disposes of its Scroller if the TScroller object was constructed
  262. //
  263. static void shutDown(TWindow* win, void*) {
  264.   win->Destroy();
  265.   delete win;
  266. }
  267.  
  268. TWindow::~TWindow()
  269. {
  270.   // Flush the cache so that messages dont get dispatched to a
  271.   // already-destructed derived class
  272.   //
  273.   void CacheFlush(uint32 id);
  274.   CacheFlush(UniqueId);
  275.  
  276.   // ShutDown children first, so the Owl objects get a chance to clean up
  277.   //
  278.   ForEach(shutDown);
  279.  
  280.   // If the HWindow is still around, then destroy it or unlink from it as
  281.   // appropriate.
  282.   //
  283.   if (HWindow) {
  284.     // for aliases:
  285.     //  - we are destructing the C++ object but not destroying the MS window
  286.     //  - restore the original window function
  287.     //
  288.     if (IsFlagSet(wfAlias)) {
  289.       // May want to check WNDPROC against Thunk to see if its still us
  290.       // and not restore if it's not us...
  291.       //
  292.       WARNX(OwlWin, GetWindowLong(GWL_WNDPROC) != (uint32)GetThunk(), 0,
  293.             "Restoring old WndProc after foreign subclass of:" << *this);
  294.       SetWindowLong(GWL_WNDPROC, uint32(DefaultProc));
  295.     }
  296.     // for non-aliases:
  297.     //  - destroy the windows element
  298.     // this is not a normal condition and is a safety net only
  299.     //
  300.     else {
  301.       WARNX(OwlWin, HWindow, 0, "Destroying from TWindow::~TWindow: " << *this);
  302.       Destroy();
  303.     }
  304.   }
  305.  
  306.   if (Parent)
  307.     Parent->RemoveChild(this);
  308.  
  309.   if (IsFlagSet(wfMainWindow))
  310.     GetApplication()->MainWindow = 0;
  311.   if (Application)
  312.     Application->Uncondemn(this);
  313.  
  314.   // Delete menu id string, scroller, title, cursor & thunk
  315.   //
  316.   if (Attr.Menu.IsString())
  317.     delete [] (char far*)Attr.Menu;  // assume strnewdup was used
  318.  
  319.   // delete scroller if there is one, by casting up to base and relying on
  320.   // virtual dtor to avoid pulling in TScroller object code.
  321.   //
  322.   if (Scroller) {
  323.     delete (TStreamableBase*)Scroller;
  324.     Scroller = 0;
  325.   }
  326.  
  327.   if (HIWORD(Title))
  328.     delete [] Title;
  329.  
  330.   if (HCursor && CursorModule)
  331.     SetCursor(0, 0);
  332.  
  333.   ::FreeInstanceThunk(Thunk);
  334.   TRACEX(OwlWin, 1, "TWindow destructed @" << (void*)this);
  335. }
  336.  
  337. #if defined(BI_MULTI_THREAD)
  338. //
  339. // overrides TEventHandler::Dispatch() to handle multi-thread synchronization
  340. //
  341. LRESULT TWindow::Dispatch(TEventInfo& info, WPARAM wp, LPARAM lp)
  342. {
  343.   TApplication::TAppLock Lock(*GetApplication());
  344.   return TEventHandler::Dispatch(info, wp, lp);
  345. }
  346.  
  347. #endif
  348.  
  349. //
  350. //
  351. //
  352. bool
  353. TWindow::PreProcessMsg(MSG& msg)
  354. {
  355.   return hAccel ? ::TranslateAccelerator(HWindow, hAccel, &msg) : false;
  356. }
  357.  
  358. //
  359. //
  360. //
  361. bool
  362. TWindow::IdleAction(long /*idleCount*/)
  363. {
  364.   return false;  // we don't need any more time for now
  365. }
  366.  
  367. //
  368. // Respond to this message by broadcasting it to all children
  369. //
  370. void
  371. TWindow::EvSysColorChange()
  372. {
  373.   ChildBroadcastMessage(WM_SYSCOLORCHANGE);
  374. }
  375.  
  376. //
  377. // Removes the passed pointer to a child window from the linked list of
  378. // sibling windows which the TWindow's ChildList points to
  379. //
  380. void
  381. TWindow::RemoveChild(TWindow* child)
  382. {
  383.   if (child && ChildList) {
  384.     TWindow*  lastChild = ChildList;
  385.     TWindow*  nextChild = lastChild;
  386.  
  387.     while (nextChild->SiblingList != lastChild &&
  388.            nextChild->SiblingList != child) {
  389.       nextChild = nextChild->SiblingList;
  390.     }
  391.  
  392.     if (nextChild->SiblingList == child) {
  393.       if (nextChild->SiblingList == nextChild)
  394.         ChildList = 0;
  395.  
  396.       else {
  397.         if (nextChild->SiblingList == ChildList)
  398.           ChildList = nextChild;
  399.  
  400.         nextChild->SiblingList = nextChild->SiblingList->SiblingList;
  401.       }
  402.     }
  403.   }
  404. }
  405.  
  406. //
  407. // Reparents this window to a new owl parent window. Also sets the windows
  408. // parent (owner)
  409. //
  410. void
  411. TWindow::SetParent(TWindow* newParent)
  412. {
  413.   if (Parent != newParent) {
  414.     if (Parent)
  415.       Parent->RemoveChild(this);
  416.  
  417.     SiblingList = 0;
  418.  
  419.     Parent = newParent;
  420.  
  421.     if (Parent)
  422.       Parent->AddChild(this);
  423.   }
  424.   // tell Windows about the change too, if appropriate
  425.   //
  426.   if (HWindow && Parent && GetParent() != Parent->HWindow) {
  427.     if (newParent) {
  428.       if (newParent->HWindow)
  429.         ::SetParent(HWindow, newParent->HWindow);
  430.     }
  431.     else
  432.       ::SetParent(HWindow, 0);
  433.   }
  434. }
  435.  
  436. //
  437. // Default behavior for updating document title is to pass it to parent frame
  438. //
  439. bool
  440. TWindow::SetDocTitle(const char far* docname, int index)
  441. {
  442.   return Parent ? Parent->SetDocTitle(docname, index) : false;
  443. }
  444.  
  445. //
  446. // Wildcard check used for child id notifications
  447. //
  448. static bool wildcardCheck(TGenericTableEntry __RTFAR& entry,
  449.                           TEventHandler::TEventInfo& info) {
  450.   return entry.Id == info.Id && (entry.Msg == UINT_MAX || entry.Msg == info.Msg);
  451. }
  452.  
  453. //
  454. // handles default processing of events
  455. //
  456. // this includes continued processing of menu/accelerators and child Id
  457. // notifications
  458. //
  459. LRESULT
  460. TWindow::DefaultProcessing()
  461. {
  462.   TCurrentEvent& currentEvent = GetCurrentEvent();
  463.  
  464.   if (currentEvent.Message == WM_COMMAND_ENABLE) {
  465.     if (currentEvent.Win != this) {
  466.       TWindow*          receiver = Parent ? Parent : currentEvent.Win;
  467.       TCommandEnabler&  commandEnabler = *(TCommandEnabler*)currentEvent.LParam;
  468.       TEventInfo        eventInfo(WM_COMMAND_ENABLE, commandEnabler.Id);
  469.  
  470.       if (receiver->Find(eventInfo))
  471.         return receiver->Dispatch(eventInfo, 0, currentEvent.LParam);
  472.     }
  473.  
  474.     return 0;
  475.   }
  476.  
  477.   if (currentEvent.Message != WM_COMMAND)
  478.     return DefWindowProc(currentEvent.Message, currentEvent.WParam, currentEvent.LParam);
  479.  
  480.   //
  481.   // currentEvent.Message == WM_COMMAND
  482.   //
  483. #if defined(BI_PLAT_WIN16)
  484.   uint    notifyCode = HIWORD(currentEvent.LParam);
  485. #else
  486.   uint    notifyCode = HIWORD(currentEvent.WParam);
  487. #endif
  488.   uint    id = LOWORD(currentEvent.WParam);
  489.   HWND    hWndCtl = (HWND)(uint)currentEvent.LParam;
  490.  
  491.   if (currentEvent.Win == this) {
  492.     //
  493.     // menu command originally destined for the receiver. defer to the app
  494.     //
  495.     if (hWndCtl == 0) {
  496.       TEventInfo     eventInfo(0, id);
  497.       TApplication*  app = GetApplication();
  498.  
  499.       if (app->Find(eventInfo)) {
  500.         app->Dispatch(eventInfo, eventInfo.Id);
  501.         return 0;
  502.       }
  503.       WARNX(OwlMsg, notifyCode<=1, 0, "Unprocessed WM_COMMAND(id=" << id << ")");
  504.     }
  505.     //
  506.     // originally destined for the receiver and the receiver has called us so
  507.     // default processing can occur
  508.     //
  509.     // unpack the original parameters and call DefWindowProc()
  510.     //
  511.     return DefWindowProc(currentEvent.Message, currentEvent.WParam, currentEvent.LParam);
  512.   }
  513.   else {
  514.     TWindow*        receiver;
  515.     uint            wParam;
  516.     TEqualOperator  equal = 0;
  517.  
  518.     if (hWndCtl == 0) {
  519.       //
  520.       // menu/accelerator/push button
  521.       //
  522.       // either give the message to the receiver's parent or the original
  523.       // receiver of the message
  524.       //
  525.       receiver = Parent ? Parent : currentEvent.Win;
  526.  
  527.       //
  528.       // "notifyCode" is either 0 or 1 depending on whether this is from an
  529.       // accelerator; however, we want to explicitly look for a 0...
  530.       //
  531.       notifyCode = 0;
  532.  
  533.       wParam = id;  // pass along "id" in case the receiver wants it
  534.     }
  535.     else {
  536.       //
  537.       // child id notification sent to the child and the child has called us
  538.       //
  539.       // offer the parent an opportunity to handle the notification
  540.       //
  541.       // NOTE: use equal function that will do a wildcard search
  542.       //
  543.       equal = wildcardCheck;
  544.       receiver = currentEvent.Win;
  545.  
  546.       //
  547.       // the child window identifier shouldn't be 0, but if it is then it will
  548.       // look like we are searching for a WM_ message with value "notifyCode"
  549.       //
  550.       if (receiver->IsFlagSet(wfAlias) || id == 0)
  551.         return receiver->DefWindowProc(currentEvent.Message, currentEvent.WParam, currentEvent.LParam);
  552.  
  553.       wParam = notifyCode;  // pass notification code in case receiver wants it
  554.     }
  555.  
  556.     TEventInfo  eventInfo(notifyCode, id);
  557.  
  558.     if (receiver->Find(eventInfo, equal))
  559.       return receiver->Dispatch(eventInfo, wParam);
  560.  
  561.     else
  562.       return receiver->DefaultProcessing();
  563.   }
  564. }
  565.  
  566. //
  567. // processing for WM_COMMAND
  568. //
  569. LRESULT
  570. TWindow::EvCommand(uint id, HWND hWndCtl, uint notifyCode)
  571. {
  572.   TWindow*        receiver = this;
  573.   uint            wParam;
  574.   TEqualOperator  equal = 0;
  575.  
  576.   if (hWndCtl == 0) {
  577.     //
  578.     // menu/accelerator
  579.     //
  580.     // "notifyCode" is either 0 or 1 depending on whether this is from an
  581.     // accelerator; however, we want to explicitly look for a 0...
  582.     //
  583.     notifyCode = 0;
  584.  
  585.     //
  586.     // pass the "id" along in case the user wants it...
  587.     //
  588.     wParam = id;
  589.   }
  590.   else {
  591.     //
  592.     // child id notification
  593.     //
  594.     TWindow*  child = GetWindowPtr(hWndCtl);
  595.  
  596.     //
  597.     // pass the "notifyCode" along in case the user wants it...
  598.     //
  599.     wParam = notifyCode;
  600.  
  601.     if (child) {
  602.       //
  603.       // give the child first crack at the event
  604.       //
  605.       receiver = child;
  606.       id = UINT_MAX;  // special designator for child Id notifications that are
  607.                       // handled at the child
  608.     }
  609.     else {
  610.       //
  611.       // offer the parent an opportunity to handle the notification
  612.       //
  613.       // NOTE: use equal function that will do a wildcard search
  614.       //
  615.       equal = wildcardCheck;
  616.  
  617.       //
  618.       // the child window identifier shouldn't be 0, but if it is then it will
  619.       // look like we are searching for a WM_ message with value "notifyCode"
  620.       //
  621.       if (id == 0)
  622.         return DefaultProcessing();
  623.     }
  624.   }
  625.  
  626.   TEventInfo  eventInfo(notifyCode, id);
  627.  
  628.   if (receiver->Find(eventInfo, equal))
  629.     return receiver->Dispatch(eventInfo, wParam);
  630.  
  631.   else
  632.     return receiver->DefaultProcessing();
  633. }
  634.  
  635. //
  636. //
  637. //
  638. void
  639. TWindow::EvCommandEnable(TCommandEnabler& commandEnabler)
  640. {
  641.   TEventInfo  eventInfo(WM_COMMAND_ENABLE, commandEnabler.Id);
  642.  
  643.   if (Find(eventInfo))
  644.     Dispatch(eventInfo, 0, (LPARAM)&commandEnabler);
  645. }
  646.  
  647. //
  648. // Don't process for windows out of our window tree (esp. other apps)
  649. //
  650. void
  651. TWindow::RouteCommandEnable(HWND hInitCmdTarget, TCommandEnabler& commandEnabler)
  652. {
  653.   // Extra processing for commands & commandEnablers: send the command down the
  654.   // command chain if we are the original receiver
  655.   //
  656.   if (commandEnabler.IsReceiver(*this)) {
  657.     HWND  hCmdTarget = hInitCmdTarget;
  658.     while (hCmdTarget && hCmdTarget != *this) {
  659.       TWindow*  cmdTarget = GetWindowPtr(hCmdTarget);
  660.  
  661.       if (cmdTarget) {
  662.         cmdTarget->EvCommandEnable(commandEnabler);
  663.  
  664.         if (commandEnabler.GetHandled())
  665.           return;
  666.       }
  667.       hCmdTarget = ::GetParent(hCmdTarget);
  668.     }
  669.   }
  670.  
  671.   // Always call base handler
  672.   //
  673.   TWindow::EvCommandEnable(commandEnabler);
  674.  
  675.   // No one explicitly enabled/disabled the command via the enabler, so run up
  676.   // the command chain checking for any one who is going to handle the command
  677.   // itself; if not then disable it...
  678.   // Don't do this for command senders that don't actually generate commands,
  679.   // like popup menu items.
  680.   //
  681.   if (commandEnabler.IsReceiver(*this) && !commandEnabler.GetHandled()
  682.       && commandEnabler.SendsCommand()) {
  683.     bool        enable = false;
  684.     TEventInfo  eventInfo(0, commandEnabler.Id);
  685.  
  686.     HWND  hCmdTarget = hInitCmdTarget;
  687.     while (true) {
  688.       TWindow*  cmdTarget = GetWindowPtr(hCmdTarget);
  689.  
  690.       if (cmdTarget && cmdTarget->Find(eventInfo)) {
  691.         enable = true;  // command will be handled, leave sender alone
  692.         break;
  693.       }
  694.       if (!hCmdTarget || hCmdTarget == *this)
  695.         break;
  696.  
  697.       hCmdTarget = ::GetParent(hCmdTarget);
  698.     }
  699.  
  700.     if (!enable) {
  701.       // check if the app wants to handle it
  702.       //
  703.       TEventInfo    enableInfo(WM_COMMAND_ENABLE, commandEnabler.Id);
  704.       TApplication* app = GetApplication();
  705.       if (app->Find(enableInfo)) {
  706.         app->Dispatch(enableInfo, 0, (LPARAM)&commandEnabler);
  707.         if (commandEnabler.GetHandled())
  708.           return;
  709.       }
  710.       enable = app->Find(eventInfo);
  711.     }
  712.  
  713.     commandEnabler.Enable(enable);
  714.   }
  715. }
  716.  
  717. //
  718. // specifies default processing for an incoming message
  719. //
  720. // calls original window proc that we subclassed, using ::CallWindowProc to
  721. // make sure that registers get setup correctly.
  722. //
  723. LRESULT
  724. TWindow::DefWindowProc(uint message, WPARAM wParam, LPARAM lParam)
  725. {
  726.   CHECK(DefaultProc);
  727.   LRESULT result = ::CallWindowProc(DefaultProc, HWindow, message, wParam, lParam);
  728.   GetApplication()->ResumeThrow();
  729.   return result;
  730. }
  731.  
  732.  
  733. static const int     msgCacheSize = 31;
  734. struct TCacheEntry {
  735.   uint32                       UniqueId;
  736.   TGenericTableEntry  __RTFAR* Entry;
  737.   uint                         Msg;
  738.   int                          Delta;  // adjustment to "this" pointer
  739.  
  740.   void Set(uint32 uniqueId, uint msg, TGenericTableEntry __RTFAR* entry, int delta = 0) {
  741.     UniqueId = uniqueId;
  742.     Entry = entry;
  743.     Msg = msg;
  744.     Delta = delta;
  745.   }
  746.   bool  Hit(uint msg, uint32 uniqueId) {return msg == Msg && uniqueId == UniqueId;}
  747.  
  748.   static uint  Key(uint32 id, uint msg) {return (uint(id) ^ msg) % msgCacheSize;}
  749. };
  750.  
  751. uint32 TWindow::LastUniqueId = 0;
  752.  
  753. static TCacheEntry  msgCache[msgCacheSize];
  754. static bool         cacheEnabled = true;
  755.  
  756. void CacheFlush(uint32 id) {
  757.   for (int i = 0; i < msgCacheSize; i++)
  758.     if (msgCache[i].UniqueId == id)
  759.       msgCache[i].Msg = 0;
  760. }
  761.  
  762. //
  763. // If rtti is available, then use it get an id for this window that is unique
  764. // on a per-class basis.
  765. //
  766. // Without rtti, use a per-instance unique id. Less efficient, but safe without
  767. // rtti.
  768. //
  769. void
  770. TWindow::SetUniqueId()
  771. {
  772. #if defined(OWL_RTTI_MSGCACHE)
  773.   UniqueId = 0;
  774. #else
  775.   if (++LastUniqueId == 0) {
  776.     //
  777.     // numbers wrapped around. disable the cache to be safe...
  778.     //
  779.     LastUniqueId = 1;
  780.     cacheEnabled = false;
  781.   }
  782.   UniqueId = LastUniqueId;
  783. #endif
  784. }
  785.  
  786. //
  787. // if the message is WM_COMMAND calls EvCommand(); otherwise looks for a handler
  788. // in the message response table
  789. //
  790. LRESULT
  791. TWindow::WindowProc(uint msg, WPARAM wParam, LPARAM lParam)
  792. {
  793.   // Handle WM_COMMAND_ENABLE command enable msgs by directly calling the
  794.   // virtual EvCommandEnable
  795.   //
  796.   if (msg == WM_COMMAND_ENABLE) {
  797.     EvCommandEnable(*(TCommandEnabler*)lParam);
  798.     return 0;
  799.   }
  800.   // Handle WM_COMMAND command msgs by directly calling the
  801.   // virtual EvCommand
  802.   //
  803.   else if (msg == WM_COMMAND) {
  804. #if defined(BI_PLAT_WIN16)
  805.     return EvCommand(LOWORD(wParam), (HWND)(uint)lParam, HIWORD(lParam));
  806. #else
  807.     return EvCommand(LOWORD(wParam), (HWND)(uint)lParam, HIWORD(wParam));
  808. #endif
  809.   }
  810.  
  811.   // Handle all other messages by looking up and dispatching using the
  812.   // response tables
  813.   //
  814.   else {
  815. #if defined(OWL_RTTI_MSGCACHE)
  816.     if (!UniqueId)
  817.       UniqueId = TYPE_UNIQUE_UINT32(*this);
  818. #endif
  819.     uint        key = TCacheEntry::Key(UniqueId, msg);
  820.     TEventInfo  eventInfo(msg);
  821.  
  822.     // Check the cache. A cache hit may be an RT handler, or an RT miss.
  823.     //
  824.     if (cacheEnabled && msgCache[key].Hit(msg, UniqueId)) {
  825.       eventInfo.Entry = msgCache[key].Entry;
  826.       if (eventInfo.Entry) {
  827.         TRACEX(OwlMsg, 1, MsgName(msg) << "* => " << *this);
  828.         eventInfo.Object = (GENERIC*)(((char*)this) + msgCache[key].Delta);
  829.         return Dispatch(eventInfo, wParam, lParam);
  830.  
  831.       } // else fall out & do default below
  832.     }
  833.     // Perform the lookup on this window.
  834.     //
  835.     else if (Find(eventInfo)) {
  836.       TRACEX(OwlMsg, 1, MsgName(msg) << " => " << *this);
  837.       msgCache[key].Set(UniqueId, msg, eventInfo.Entry, int(eventInfo.Object) - int(this));
  838.       return Dispatch(eventInfo, wParam, lParam);
  839.     }
  840.     else  // not found
  841.       msgCache[key].Set(UniqueId, msg, 0);  // Cache no-handler entries too
  842.  
  843.     // Behavior for messages that have no handler. If this is the main window,
  844.     // then give the app a chance to handle the message. If not the main
  845.     // window, or if the app had no handler, just call DefWindowProc() to
  846.     // pass the message back to whomever we subclassed
  847.     //
  848.     if (IsFlagSet(wfMainWindow)) {
  849.       TEventInfo cmdEventInfo(msg, wParam);
  850.       if (GetApplication()->Find(cmdEventInfo))
  851.         return GetApplication()->Dispatch(cmdEventInfo, wParam, lParam);
  852.       if (GetApplication()->Find(eventInfo))
  853.         return GetApplication()->Dispatch(eventInfo, wParam, lParam);
  854.     }
  855.     return DefWindowProc(msg, wParam, lParam);
  856.   }
  857. }
  858.  
  859. //
  860. // Save current event and call this window's WindowProc. Handles exceptions,
  861. // error checking, & GetWindowPtr messages.
  862. //
  863. LRESULT
  864. TWindow::HandleMessage(uint msg, WPARAM wParam, LPARAM lParam)
  865. {
  866.   // save event away in "CurrentEvent"
  867.   //
  868.   TCurrentEvent& currentEvent = GetCurrentEvent();
  869.   TCurrentEvent  saveEvent = currentEvent;  // for nested calls
  870.   currentEvent.Win = this;
  871.   currentEvent.Message = msg;
  872.   currentEvent.WParam = wParam;
  873.   currentEvent.LParam = lParam;
  874.  
  875.   // call window object's WindowProc virtual function
  876.   //
  877.   LRESULT  result;
  878.   result = WindowProc(msg, wParam, lParam);
  879.   currentEvent = saveEvent;  // restore previous event to current event
  880.   return result;
  881. }
  882.  
  883. //
  884. // Window function called for normal, initialized, OWL windows via a
  885. // per-window thunk. 'this' ptr is setup in register(s) by the thunk before
  886. // StdWndProc is called.
  887. //
  888. #if defined(BI_PLAT_WIN16)
  889. LRESULT
  890. FAR PASCAL __export
  891. StdWndProc(HWND, uint msg, WPARAM wParam, LPARAM lParam)
  892. {
  893.   return ((TWindow*)MK_FP(_ES,_BX))->ReceiveMessage(msg, wParam, lParam);
  894. }
  895. #else
  896. LRESULT
  897. FAR PASCAL _stdcall
  898. StdWndProc(HWND, uint msg, WPARAM wParam, LPARAM lParam)
  899. {
  900.   return ((TWindow*)_EAX)->ReceiveMessage(msg, wParam, lParam);
  901. }
  902. #endif
  903.  
  904. //
  905. // First member function that receives messages from windows thru the
  906. // thunk & then StdWndProc. Deals with exception suspension for non-NT
  907. // environments.
  908. //
  909. LRESULT
  910. TWindow::ReceiveMessage(uint msg, WPARAM wParam, LPARAM lParam)
  911. {
  912.   // if it's a "GetWindowPtr" message, then return pointer to this window
  913.   //
  914.   if (msg == ::GetWindowPtrMsgId && (!lParam || lParam == LPARAM(Application)))
  915.     return LRESULT(this);
  916.  
  917. #if defined(BI_PLAT_WIN32)
  918.   static bool exceptionOK =
  919.                 !(::GetVersion()&0x80000000) || (::GetVersion()&0xFF) >= 4;
  920.   if (exceptionOK)
  921.     return HandleMessage(msg, wParam, lParam);
  922. #endif
  923.   TRY {
  924.     return HandleMessage(msg, wParam, lParam);
  925.   }
  926.   CATCH( (TXOwl& x) {
  927.     GetApplication()->SuspendThrow(x);
  928.   })
  929.   CATCH( (xalloc& x) {
  930.     GetApplication()->SuspendThrow(x);
  931.   })
  932.   CATCH( (xmsg& x) {
  933.     GetApplication()->SuspendThrow(x);
  934.   })
  935.   CATCH( (Bad_cast& x) {
  936.     GetApplication()->SuspendThrow(TApplication::xsBadCast);
  937.   })
  938.   CATCH( (Bad_typeid& x) {
  939.     GetApplication()->SuspendThrow(TApplication::xsBadTypeid);
  940.   })
  941.   CATCH( (...) {
  942.     GetApplication()->SuspendThrow(TApplication::xsUnknown);
  943.   })
  944.   ENDCATCH
  945.  
  946.   // When exceptions are disabled CATCH is defined as empty, so the only code
  947.   // is the TRY block, making this return unreachable
  948.   //
  949. # pragma warn -rch
  950.   return msg == WM_CREATE ? -1 : 0;  // default value returned when exception caught
  951. # pragma warn .rch
  952. }
  953.  
  954. //
  955. // Determine the object pointer by sending a GetWindowPtrMsgId message to the
  956. // window.  When the first StdWndProc() in the subclass chain receives this
  957. // message it returns a pointer to the object (via the thunking mechanism).
  958. // If app is non-zero, then the process makes sure that the corresponding
  959. // TWindow is returned.
  960. //
  961. TWindow* _OWLFUNC GetWindowPtr(HWND hWnd, const TApplication* app)
  962. {
  963.   if (!hWnd /* && ::IsWindow(hWnd) */)  // could also check handle validity
  964.     return 0;
  965.  
  966. #if defined(BI_DATA_NEAR)
  967.   LPARAM param2 = app ? LPARAM(app) : 0;
  968. #else
  969.   LPARAM param2 = LPARAM(app);
  970. #endif
  971.  
  972.   // Avoid SendMessage to cutdown the overhead & message spy tool flooding
  973.   //
  974.   // Under Win16, need to fallback to SendMessage when the given hWnd is owned
  975.   // by another task. Using CallWindowProc on it would result in a bad SS
  976.   // setup when it received the message.
  977.   //
  978.   // Under Win32 don't even try if it is not our process. Some Win32's will
  979.   // return a wndProc that crashes.
  980.   //
  981. #if defined(BI_PLAT_WIN16)
  982.   if (::GetWindowTask(hWnd) != ::GetCurrentTask())
  983.     return (TWindow*)::SendMessage(hWnd, GetWindowPtrMsgId, 0, param2);
  984. #else
  985.   uint32 processId;
  986.   ::GetWindowThreadProcessId(hWnd, &processId);
  987.   if (processId != ::GetCurrentProcessId())
  988.     return 0;
  989. #endif
  990.  
  991.   WNDPROC wndProc = (WNDPROC)::GetWindowLong(hWnd, GWL_WNDPROC);
  992.   if (!wndProc)
  993.     return 0;
  994.   return (TWindow*)::CallWindowProc(wndProc, hWnd, GetWindowPtrMsgId, 0, param2);
  995. }
  996.  
  997. //
  998. // response method for an incoming WM_CREATE message
  999. //
  1000. // Call virtual SetupWindow to give derived classes a chance to set things up
  1001. // now that we are created & have an HWindow
  1002. //
  1003. int
  1004. TWindow::EvCreate(CREATESTRUCT far&)
  1005. {
  1006.   SetupWindow();
  1007.   SetFlag(wfFullyCreated);
  1008.   return (int)DefaultProcessing();
  1009. }
  1010.  
  1011. //
  1012. // regular windows never hold focus child handles--just say no.
  1013. //
  1014. bool
  1015. TWindow::HoldFocusHWnd(HWND /*hWndLose*/, HWND /*hWndGain*/)
  1016. {
  1017.   return false;
  1018. }
  1019.  
  1020. //
  1021. // handle WM_KILLFOCUS so that we can have a parent window hold onto our
  1022. // HWindow and possibly restore focus later.
  1023. //
  1024. void
  1025. TWindow::EvKillFocus(HWND getFocus)
  1026. {
  1027.   // Follow the parent chain back until a window volunteers to hold our handle
  1028.   //
  1029.   if (IsFlagSet(wfFullyCreated)) {
  1030.     TWindow* holdWin = Parent;
  1031.     while (holdWin && !holdWin->HoldFocusHWnd(HWindow, getFocus))
  1032.       holdWin = holdWin->Parent;
  1033.   }
  1034.  
  1035.   DefaultProcessing();
  1036. }
  1037.  
  1038. //
  1039. // response method for an incoming WM_SIZE message
  1040. //
  1041. // saves the normal size of the window in Attr
  1042. //
  1043. // also calls the SetPageSize() and SetBarRange() methods of the TWindow's
  1044. // scroller, if it has been constructed
  1045. //
  1046. void
  1047. TWindow::EvSize(uint sizeType, TSize&)
  1048. {
  1049.   if (Scroller && sizeType != SIZE_MINIMIZED) {
  1050.     Scroller->SetPageSize();
  1051.     Scroller->SetSBarRange();
  1052.   }
  1053.  
  1054.   if (sizeType == SIZE_RESTORED) {
  1055.     TRect  wndRect;
  1056.  
  1057.     GetWindowRect(wndRect);
  1058.     Attr.W = wndRect.Width();
  1059.     Attr.H = wndRect.Height();
  1060.   }
  1061.  
  1062.   // Added owl functionality: notify parent of a resize in case it wants to
  1063.   // adust accordingly
  1064.   //
  1065.   if (Parent && !(GetWindowLong(GWL_EXSTYLE)&WS_EX_NOPARENTNOTIFY))
  1066.     Parent->SendMessage(WM_PARENTNOTIFY, WM_SIZE, (uint)HWindow);
  1067.  
  1068.   DefaultProcessing();
  1069. }
  1070.  
  1071. //
  1072. // save the normal position of the window
  1073. //
  1074. // if IsIconic() or IsZoomed() ignore the position since it does not reflect the
  1075. // normal state
  1076. //
  1077. void
  1078. TWindow::EvMove(TPoint&)
  1079. {
  1080.   if (!IsIconic() && !IsZoomed()) {
  1081.     TRect wndRect;
  1082.  
  1083.     GetWindowRect(wndRect);
  1084.  
  1085.     if ((GetWindowLong(GWL_STYLE) & WS_CHILD) == WS_CHILD && Parent &&
  1086.          Parent->HWindow)
  1087.       Parent->ScreenToClient(wndRect.TopLeft());
  1088.  
  1089.     Attr.X = wndRect.left;
  1090.     Attr.Y = wndRect.top;
  1091.   }
  1092.  
  1093.   DefaultProcessing();
  1094. }
  1095.  
  1096. //
  1097. // handles WM_COMPAREITEM message (for owner draw controls) by forwarding
  1098. // message to control itself
  1099. //
  1100. LRESULT
  1101. TWindow::EvCompareItem(uint /*ctrlId*/, COMPAREITEMSTRUCT far& compareInfo)
  1102. {
  1103.   TWindow* win = GetWindowPtr(compareInfo.hwndItem);
  1104.   if (win)
  1105.     return win->ForwardMessage();
  1106.   return DefaultProcessing();
  1107. }
  1108.  
  1109. //
  1110. // handles WM_DELETEITEM message (for owner draw controls) by forwarding
  1111. // message to control itself
  1112. //
  1113. void
  1114. TWindow::EvDeleteItem(uint /*ctrlId*/, DELETEITEMSTRUCT far& deleteInfo)
  1115. {
  1116.   TWindow* win = GetWindowPtr(deleteInfo.hwndItem);
  1117.   if (deleteInfo.hwndItem != HWindow && win)
  1118.     win->ForwardMessage();
  1119.   else
  1120.     DefaultProcessing();
  1121. }
  1122.  
  1123. //
  1124. // handles WM_DRAWITEM message (for owner draw controls & menus) by forwarding
  1125. // message to control itself
  1126. //
  1127. void
  1128. TWindow::EvDrawItem(uint /*ctrlId*/, DRAWITEMSTRUCT far& drawInfo)
  1129. {
  1130.   if (drawInfo.CtlType == ODT_MENU) {
  1131.     // dispatch to menu...
  1132.     // TMenu* menu = ...
  1133.     // menu->DrawItem(drawInfo);
  1134.   }
  1135.   else {
  1136.     TWindow* win = GetWindowPtr(drawInfo.hwndItem);
  1137.     if (drawInfo.hwndItem != HWindow && win) {
  1138.       win->ForwardMessage();
  1139.       return;
  1140.     }
  1141.   }
  1142.   DefaultProcessing();
  1143. }
  1144.  
  1145. //
  1146. // handles WM_MEASUREITEM message (for owner draw controls & menus) by
  1147. // forwarding message to control itself
  1148. //
  1149. void
  1150. TWindow::EvMeasureItem(uint ctrlId, MEASUREITEMSTRUCT far& measureInfo)
  1151. {
  1152.   if (measureInfo.CtlType == ODT_MENU) {
  1153.     // dispatch to menu...
  1154.     // TMenu* menu = ...
  1155.     // menu->MeasureItem(measureInfo);
  1156.   }
  1157.   else {
  1158.     HWND hwndCtl = GetDlgItem(measureInfo.CtlID);  // hWnd not in struct, get
  1159.     TWindow* win = GetWindowPtr(hwndCtl);
  1160.     //
  1161.     // If the GetWindowPtr failed, & CreationWindow is non-zero, then this
  1162.     // WM_MEASUREITEM is probably for the ctrl which is not yet thunked.
  1163.     // route the message directly to creation window.
  1164.     //
  1165.     extern TWindow* _OWLDATA CreationWindow;
  1166.     if (!win) {             // sometimes this is send before Owl thunks ctl
  1167.       if (CreationWindow)
  1168.         win = CreationWindow;
  1169.       else
  1170.         win = ChildWithId(ctrlId);
  1171.     }
  1172.     if (win) {
  1173.       win->HandleMessage(WM_MEASUREITEM, ctrlId, LPARAM(&measureInfo));
  1174.       return;
  1175.     }
  1176.   }
  1177.   DefaultProcessing();
  1178. }
  1179.  
  1180. //
  1181. //
  1182. #if defined(BI_PLAT_WIN32)
  1183. LRESULT
  1184. TWindow::EvWin32CtlColor(WPARAM wParam, LPARAM lParam)
  1185. {
  1186.   int  ctlType = GetCurrentEvent().Message - WM_CTLCOLORMSGBOX;
  1187.  
  1188.   CHECK(ctlType >= CTLCOLOR_MSGBOX && ctlType <= CTLCOLOR_STATIC);
  1189.  
  1190.   TEventInfo  eventInfo(WM_CTLCOLOR);
  1191.  
  1192.   if (!Find(eventInfo))
  1193.     return DefWindowProc(GetCurrentEvent().Message, wParam, lParam);
  1194.  
  1195.   else {
  1196.     typedef HBRUSH(GENERIC::*CTL_COLOR_PMF)(HDC, HWND, uint);
  1197.  
  1198.     CTL_COLOR_PMF&  pmf = (CTL_COLOR_PMF&)(eventInfo.Entry->Pmf);
  1199.  
  1200.     return (LRESULT)(eventInfo.Object->*pmf)((HDC)wParam, (HWND)lParam, ctlType);
  1201.   }
  1202. }
  1203. #endif
  1204.  
  1205. //
  1206. // dispatches scroll messages as if they were Command messages.
  1207. //
  1208. void
  1209. TWindow::DispatchScroll(uint scrollCode, uint /*thumbPos*/, HWND hWndCtrl)
  1210. {
  1211.   if (hWndCtrl) {
  1212.     TWindow* win = GetWindowPtr(hWndCtrl);
  1213.     if (win)
  1214.       win->ForwardMessage();
  1215.  
  1216.     //
  1217.     // Adjust "CurrentEvent" to allow DefaultProcessing to work
  1218.     //
  1219.     uint16 id = (uint16)::GetDlgCtrlID(hWndCtrl);
  1220.     TCurrentEvent& currentEvent = GetCurrentEvent();
  1221.     currentEvent.Message = WM_COMMAND;
  1222. #if defined(BI_PLAT_WIN16)
  1223.       currentEvent.WParam = id;
  1224.       currentEvent.LParam = MAKELPARAM(hWndCtrl, scrollCode);
  1225. #else
  1226.       currentEvent.WParam = MAKEWPARAM(id, scrollCode);
  1227.       currentEvent.LParam = LPARAM(hWndCtrl);
  1228. #endif
  1229.  
  1230.     EvCommand(id, hWndCtrl, scrollCode);
  1231.     return;
  1232.   }
  1233.   DefaultProcessing();
  1234. }
  1235.  
  1236. //
  1237. // response method for an incoming WM_HSCROLL message
  1238. //
  1239. // if the message is from a scrollbar control, calls DispatchScroll()
  1240. // otherwise passes the message to the TWindow's scroller if it has been
  1241. // constructed, else calls DefaultProcessing()
  1242. //
  1243. // assumes, because of a Windows bug, that if the window has the scrollbar
  1244. // style, it will not have scrollbar controls
  1245. //
  1246. void
  1247. TWindow::EvHScroll(uint scrollCode, uint thumbPos, HWND hWndCtl)
  1248. {
  1249.   if (!(GetWindowLong(GWL_STYLE) & WS_HSCROLL))
  1250.     DispatchScroll(scrollCode, thumbPos, hWndCtl);  // from scrollbar control
  1251.  
  1252.   else if (Scroller)
  1253.     Scroller->HScroll(scrollCode, thumbPos);
  1254.  
  1255.   else
  1256.     DefaultProcessing();
  1257. }
  1258.  
  1259. //
  1260. // response method for an incoming WM_VSCROLL message
  1261. //
  1262. // if the message is from a scrollbar control, calls DispatchScroll()
  1263. // otherwise passes the message to the TWindow's scroller if it has been
  1264. // constructed, else calls DefaultProcessing()
  1265. //
  1266. // assumes, because of a Windows bug, that if the window has the scrollbar
  1267. // style, it will not have scrollbar controls
  1268. //
  1269. void
  1270. TWindow::EvVScroll(uint scrollCode, uint thumbPos, HWND hWndCtl)
  1271. {
  1272.   if (!(GetWindowLong(GWL_STYLE) & WS_VSCROLL))
  1273.     DispatchScroll(scrollCode, thumbPos, hWndCtl);
  1274.  
  1275.   else if (Scroller)
  1276.     Scroller->VScroll(scrollCode, thumbPos);
  1277.  
  1278.   else
  1279.     DefaultProcessing();
  1280. }
  1281.  
  1282. //
  1283. // response method for an incoming WM_ERASEBKGND message
  1284. //
  1285. bool
  1286. TWindow::EvEraseBkgnd(HDC hDC)
  1287. {
  1288.   // color set, we'll handle erasing (or doing nothing) here
  1289.   //
  1290.   if (BkgndColor != NoColor) {
  1291.  
  1292.     // if a color is set, blt out a rectangle of it, else don't erase & let
  1293.     // paint handle background
  1294.     //
  1295.     if (BkgndColor != NoErase) {
  1296.       SetBkColor(hDC, BkgndColor);
  1297.       ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &GetClientRect(), 0, 0, 0);
  1298.     }
  1299.     return true;
  1300.   }
  1301.  
  1302.   //
  1303.   // no color set, use default class brush
  1304.   //
  1305.   return (bool)DefaultProcessing();
  1306. }
  1307.  
  1308. //
  1309. // response method for an incoming WM_PAINT message
  1310. //
  1311. // calls Paint(), performing Windows-required paint setup and cleanup before
  1312. // and after. if the TWindow has a TScroller, also calls its BeginView() and
  1313. // EndView() methods before and after call to Paint()
  1314. //
  1315. void
  1316. TWindow::EvPaint()
  1317. {
  1318.   if (IsFlagSet(wfAlias))
  1319.     DefaultProcessing();  // use application-defined wndproc
  1320.  
  1321.   else {
  1322.     TPaintDC dc(*this);
  1323.     TRect&   rect = *(TRect*)&dc.Ps.rcPaint;
  1324.  
  1325.     if (Scroller)
  1326.       Scroller->BeginView(dc, rect);
  1327.  
  1328.     Paint(dc, dc.Ps.fErase, rect);
  1329.  
  1330.     if (Scroller)
  1331.       Scroller->EndView();
  1332.   }
  1333. }
  1334.  
  1335. //
  1336. // redraws the contents of the TWindow after a WM_PAINT message
  1337. // is received
  1338. //
  1339. // placeholder for descendant object types to redefine
  1340. //
  1341. void
  1342. TWindow::Paint(TDC&, bool /*erase*/, TRect&)
  1343. {
  1344. }
  1345.  
  1346. //
  1347. // response method for an incoming WM_SETCURSOR message
  1348. //
  1349. // if a cursor has been set for this window, & the mouse is over the
  1350. // client area, set the cursor
  1351. //
  1352. bool
  1353. TWindow::EvSetCursor(HWND hWndCursor, uint hitTest, uint /*mouseMsg*/)
  1354. {
  1355.   if (hWndCursor == HWindow && hitTest == HTCLIENT && HCursor) {
  1356.     ::SetCursor(HCursor);
  1357.     return true;
  1358.   }
  1359.   return (bool)DefaultProcessing();
  1360. }
  1361.  
  1362. //
  1363. // response method for an incoming WM_LBUTTONDOWN message
  1364. //
  1365. // if the TWindow's Scroller has been constructed and if auto-scrolling
  1366. // has been requested, captures mouse input, loops until a WM_LBUTTONUP
  1367. // message comes in calling the Scroller's AutoScroll method, and then
  1368. // releases capture on mouse input
  1369. // will also break if a WM_MOUSEMOVE comes in without the left button down
  1370. // indicating a lost WM_LBUTTONUP
  1371. //
  1372. void
  1373. TWindow::EvLButtonDown(uint /*modKeys*/, TPoint& /*pt*/)
  1374. {
  1375.   if (Scroller && Scroller->IsAutoMode()) {
  1376.     MSG  loopMsg;
  1377.  
  1378.     loopMsg.message = 0;
  1379.     SetCapture();
  1380.  
  1381.     while (loopMsg.message != WM_LBUTTONUP &&
  1382.           (loopMsg.message != WM_MOUSEMOVE || (loopMsg.wParam&MK_LBUTTON))) {
  1383.       if (::PeekMessage(&loopMsg, 0, 0, 0, PM_REMOVE)) {
  1384.         ::TranslateMessage(&loopMsg);
  1385.         ::DispatchMessage(&loopMsg);
  1386.       }
  1387.  
  1388.       Scroller->AutoScroll();
  1389.     }
  1390.  
  1391.     ReleaseCapture();
  1392.     GetApplication()->ResumeThrow();
  1393.   }
  1394.  
  1395.   DefaultProcessing();
  1396. }
  1397.  
  1398. //
  1399. void
  1400. DoEnableAutoCreate(TWindow* win, void* /*retVal*/)
  1401. {
  1402.   if (win->HWindow)
  1403.     win->EnableAutoCreate();
  1404. }
  1405.  
  1406. //
  1407. // Destroys an MS-Windows element associated with the TWindow.
  1408. // First, sets the wfAutoCreate flag to ON for each of the windows in the
  1409. // TWindow's ChildList to allow later re-creation.
  1410. //
  1411. void
  1412. TWindow::Destroy(int)
  1413. {
  1414.   if (HWindow) {
  1415.     ForEach(DoEnableAutoCreate, 0);
  1416.  
  1417.     if (::DestroyWindow(HWindow))
  1418.       HWindow = 0;
  1419.     GetApplication()->ResumeThrow();
  1420.     WARNX(OwlWin, HWindow, 0, "::DestroyWindow(" << (uint)HWindow << ") failed");
  1421.   }
  1422. }
  1423.  
  1424. //
  1425. // specifies registration attributes for the MS-Windows window class
  1426. // of the TWindow, allowing instances of TWindow to be registered
  1427. //
  1428. // sets the fields of the passed WNDCLASS parameter to the default
  1429. // attributes appropriate for a TWindow
  1430. //
  1431. void
  1432. TWindow::GetWindowClass(WNDCLASS& wndClass)
  1433. {
  1434.   wndClass.cbClsExtra = 0;
  1435.   wndClass.cbWndExtra = 0;
  1436.   wndClass.hInstance = *GetModule();
  1437.   wndClass.hIcon = 0;
  1438.   wndClass.hCursor = ::LoadCursor(0, IDC_ARROW);
  1439.   wndClass.hbrBackground = HBRUSH(COLOR_WINDOW + 1);
  1440.   wndClass.lpszMenuName = 0;
  1441.   wndClass.lpszClassName = GetClassName();
  1442.   wndClass.style = CS_DBLCLKS;
  1443.   wndClass.lpfnWndProc = ::InitWndProc;
  1444. }
  1445.  
  1446. //
  1447. // Returns a classname for a generic owl window. Builds the name based on the
  1448. // name part of the current module's filename with "Window" appended.
  1449. //
  1450. char far*
  1451. TWindow::GetClassName()
  1452. {
  1453. #if defined(_OWL_BUILDUP_CLASSNAME)
  1454.   static char classSuffix[] = "Window";
  1455.   static char className[_MAX_FNAME+sizeof(classSuffix)];
  1456.  
  1457.   if (!*className) {
  1458.     char filePath[_MAX_PATH];
  1459. #if (BI_PLAT_WIN16)
  1460.     int  len = GetModule()->GetModuleFileName(filePath, sizeof(filePath));
  1461. #else
  1462.     int  len = ::GetModuleFileName(0, filePath, sizeof(filePath));
  1463. #endif
  1464.     CHECK(len > 0);
  1465.     _splitpath(filePath, 0, 0, className, 0);
  1466.     strcat(className, classSuffix);
  1467.   }
  1468.   return className;
  1469. #else
  1470.   return "OwlWindow";  // assume application private class
  1471. #endif
  1472. }
  1473.  
  1474. //
  1475. void
  1476. TWindow::LoadAcceleratorTable()
  1477. {
  1478.   if (Attr.AccelTable) {
  1479.     hAccel = GetModule()->LoadAccelerators(Attr.AccelTable);
  1480.     WARNX(OwlWin, !hAccel, 0,
  1481.           "Unable to load accelerators " << Attr.AccelTable
  1482.           << " from " << *GetModule());
  1483.   }
  1484. }
  1485.  
  1486. //
  1487. // Perform MS Windows window creation
  1488. //
  1489. void
  1490. TWindow::PerformCreate(int menuOrId)
  1491. {
  1492.   HWindow = ::CreateWindowEx(Attr.ExStyle,
  1493.                            GetClassName(),
  1494.                            Title,
  1495.                            Attr.Style,
  1496.                            Attr.X, Attr.Y, Attr.W, Attr.H,
  1497.                            Parent ? Parent->HWindow : 0,
  1498.                            (HMENU)menuOrId,
  1499.                            *GetModule(),
  1500.                            Attr.Param);
  1501. }
  1502.  
  1503. //
  1504. // associates an MS-Windows interface element with the TWindow object,
  1505. // after creating the interface element if not already created
  1506. //
  1507. // when creating an element, uses the creation attributes previously set in
  1508. // the Attr data field(simply associates the TWindow with an already
  1509. // created interface element if the "FromResource" flag is set)
  1510. //
  1511. // Since this member function now throw an exception on error, it always
  1512. // returns true.
  1513. //
  1514. bool
  1515. TWindow::Create()
  1516. {
  1517.   if (HWindow)
  1518.     return true;
  1519.  
  1520.   int  menuOrId = 0;
  1521.  
  1522.   DisableAutoCreate();
  1523.  
  1524.   if (IsFlagSet(wfFromResource))
  1525.     HWindow = Parent ? Parent->GetDlgItem(Attr.Id) : 0;  // windows already created it
  1526.  
  1527.   else if (Register()) {
  1528.     ::SetCreationWindow(this);
  1529.  
  1530.     LoadAcceleratorTable();
  1531.  
  1532.     if (Attr.Menu) {
  1533.       menuOrId = (int)GetModule()->LoadMenu(Attr.Menu);
  1534.       WARNX(OwlWin, !menuOrId, 0, "Unable to load menu: " << Attr.Menu <<
  1535.             " from " << *GetModule());
  1536.     }
  1537.     else
  1538.       menuOrId = Attr.Id;
  1539.  
  1540.     PerformCreate(menuOrId);
  1541.     GetApplication()->ResumeThrow();
  1542.   }
  1543.   else
  1544.     THROW( TXWindow(this, IDS_CLASSREGISTERFAIL) );
  1545.  
  1546.   if (!HWindow) {
  1547.     if (Attr.Menu)  // && !IsFlagSet(wfFromResource) ?
  1548.       DestroyMenu(HMENU(menuOrId));
  1549.  
  1550.     THROW( TWindow::TXWindow(this, IDS_WINDOWCREATEFAIL) );
  1551.   }
  1552.  
  1553.   // predefined (non-owl) class--grab the state info that the class setup,
  1554.   // install the thunk & zero the creation window since InitWndProc never got
  1555.   // called, and set a flag
  1556.   //
  1557.   if (!GetWindowPtr(HWindow)) {
  1558.     GetWindowTextTitle();
  1559.     GetHWndState();
  1560.  
  1561.     SubclassWindowFunction();
  1562.     ::SetCreationWindow(0);
  1563.     SetFlag(wfPredefinedClass|wfFullyCreated);
  1564.  
  1565.     SetupWindow();
  1566.   }
  1567.  
  1568.   return true;
  1569. }
  1570.  
  1571. //
  1572. // performs setup following creation of an associated MS-Windows window.
  1573. // Iterates through the TWindow's ChildList, attempting to create
  1574. // an associated MS-Windows interface element for each child window
  1575. // object in the list (a child's Create method is not called if its
  1576. // wfAutoCreate flag is not set)
  1577. //
  1578. // calls TransferData to transfer data for its children for whom data transfer
  1579. // is enabled
  1580. //
  1581. // if the receiver has a TScroller object, calls the scroller's SetBarRange()
  1582. // method
  1583. //
  1584. // can be redefined in derived classes to perform additional special
  1585. // initialization that requires an HWND
  1586. //
  1587. void
  1588. TWindow::SetupWindow()
  1589. {
  1590.   if (Scroller)
  1591.     Scroller->SetSBarRange();
  1592.  
  1593.   if (!CreateChildren())
  1594.     THROW( TXWindow(0, IDS_CHILDCREATEFAIL) ); // not us, an unspecified child
  1595.  
  1596.   TransferData(tdSetData);
  1597. }
  1598.  
  1599. //
  1600. // always called just before HWindow goes away to give derived classes a
  1601. // chance to cleanup HWND related resources.
  1602. //
  1603. void
  1604. TWindow::CleanupWindow()
  1605. {
  1606. }
  1607.  
  1608. //
  1609. // transfer window 'data' to/from the passed data buffer.  Used to initialize
  1610. // windows and get data in or out of them.
  1611. //
  1612. // the direction passed specifies whether data is to be read from or
  1613. // written to the passed buffer, or whether the data element size is simply
  1614. // to be returned
  1615. //
  1616. // the return value is the size (in bytes) of the transfer data.  this method
  1617. // recursively calls transfer on all its children that have wfTransfer set.
  1618. //
  1619.  
  1620. struct TTransferIterInfo {
  1621.   void*               Data;
  1622.   TTransferDirection  Direction;
  1623. };
  1624.  
  1625. static void transferDatchild(TWindow* child, TTransferIterInfo* info) {
  1626.   if (child->IsFlagSet(wfTransfer))
  1627.     (char*)info->Data += child->Transfer(info->Data, info->Direction);
  1628. }
  1629.  
  1630. uint
  1631. TWindow::Transfer(void* buffer, TTransferDirection direction)
  1632. {
  1633.   if (buffer) {
  1634.     TTransferIterInfo info = { buffer, direction };
  1635.     ForEach((TActionFunc)transferDatchild, &info);
  1636.     return (char near*)info.Data - (char near*)buffer;
  1637.   }
  1638.   return 0;
  1639. }
  1640.  
  1641. //$---------------------------------------------------------------------------
  1642. //
  1643. // transfers data between the TWindow's data buffer and the child
  1644. // windows in its ChildList (data is not transfered between any child
  1645. // windows whose wfTransfer flag is not set)
  1646. //
  1647. void
  1648. TWindow::TransferData(TTransferDirection direction)
  1649. {
  1650.   if (TransferBuffer)
  1651.     Transfer(TransferBuffer, direction);
  1652. }
  1653.  
  1654. //
  1655. // registers the TWindow's MS-Windows, if not already registered
  1656. //
  1657. bool
  1658. TWindow::Register()
  1659. {
  1660.   // Only check for globally registered classes if not in an NT WoW box,
  1661.   // since WoW plays nasty games with class registration.
  1662.   //
  1663. #if defined(BI_PLAT_WIN32)
  1664.   static bool checkGlobal = ToBool(::GetVersion()&0x80000000);  // not NT
  1665. #else
  1666.   static bool checkGlobal = !ToBool(::GetWinFlags()&0x4000);  // not WoW box
  1667. #endif
  1668.  
  1669.   WNDCLASS  windowClass;
  1670.   bool gc;
  1671.   if (checkGlobal)
  1672.     gc = ToBool(::GetClassInfo(0, GetClassName(), &windowClass));
  1673.   else
  1674.     gc = false;
  1675.  
  1676.   if (!gc && !GetModule()->GetClassInfo(GetClassName(), &windowClass)) {
  1677.     GetWindowClass(windowClass);
  1678.     return ::RegisterClass(&windowClass);
  1679.   }
  1680.  
  1681.   return true;
  1682. }
  1683.  
  1684. //
  1685. // returns a bool indicating whether or not it is Ok to close the TWindow
  1686. //
  1687. // iterates through the TWindow's ChildList, calling the CanClose()
  1688. // method of each
  1689. //
  1690. // returns false if any of the child windows returns false
  1691. //
  1692.  
  1693. static bool cannotClose(TWindow* win, void*) {
  1694.   return win->HWindow && !win->CanClose();
  1695. }
  1696.  
  1697. bool
  1698. TWindow::CanClose()
  1699. {
  1700.   return !FirstThat(cannotClose);
  1701. }
  1702.  
  1703. //
  1704. // destroys the associated MS-Windows interface element
  1705. // after determining that it is Ok to do so
  1706. //
  1707. // if the TWindow is the main window of the application, calls the CanClose()
  1708. // method of the application, else calls this->CanClose(), before calling
  1709. // Destroy()
  1710. //
  1711. void
  1712. TWindow::CloseWindow(int retVal)
  1713. {
  1714.   bool  willClose;
  1715.  
  1716.   if (IsFlagSet(wfMainWindow))
  1717.     willClose = GetApplication()->CanClose();
  1718.  
  1719.   else
  1720.     willClose = CanClose();
  1721.  
  1722.   if (willClose)
  1723.     Destroy(retVal);
  1724. }
  1725.  
  1726. //
  1727. // the default response to a WM_CLOSE message is to call CloseWindow()
  1728. // and then have the window deleted if the HWindow was really destroyed.
  1729. //
  1730. void
  1731. TWindow::EvClose()
  1732. {
  1733.   if (IsFlagSet(wfAlias))
  1734.     DefaultProcessing();
  1735.  
  1736.   else {
  1737.     CloseWindow();
  1738.     if (!HWindow && IsFlagSet(wfDeleteOnClose))
  1739.       GetApplication()->Condemn(this);  // Assumes delete
  1740.   }
  1741. }
  1742.  
  1743. //
  1744. // responds to an incoming WM_DESTROY message
  1745. //
  1746. // Calls CleanupWindow() to let derived classes cleanup
  1747. // Clears the wfFullyCreated flag since this window is no longer fully created
  1748. //
  1749. // if the TWindow is the application's main window posts a 'quit' message to
  1750. // end the application, unless already in ~TApplication() (MainWindow == 0)
  1751. //
  1752. void
  1753. TWindow::EvDestroy()
  1754. {
  1755.   ClearFlag(wfFullyCreated);
  1756.   CleanupWindow();
  1757.  
  1758.   if (!IsFlagSet(wfAlias)) {
  1759.     if (IsFlagSet(wfMainWindow) && GetApplication()->IsRunning())
  1760.       ::PostQuitMessage(GetApplication()->Status);
  1761.   }
  1762.  
  1763.   DefaultProcessing();
  1764. }
  1765.  
  1766. //
  1767. // responds to an incoming WM_NCDESTROY message, the last message
  1768. // sent to an MS-Windows interface element
  1769. //
  1770. // sets the HWindow data member of the TWindow to zero to indicate that an
  1771. // interface element is no longer associated with the object
  1772. //
  1773. void
  1774. TWindow::EvNCDestroy()
  1775. {
  1776.   DefaultProcessing();
  1777.   HWindow = 0;
  1778. }
  1779.  
  1780. //
  1781. // respond to Windows attempt to close down
  1782. //
  1783. bool
  1784. TWindow::EvQueryEndSession()
  1785. {
  1786.   if (IsFlagSet(wfAlias))
  1787.     return (bool)DefaultProcessing();
  1788.  
  1789.   else if (IsFlagSet(wfMainWindow))
  1790.     return GetApplication()->CanClose();
  1791.  
  1792.   else
  1793.     return CanClose();
  1794. }
  1795.  
  1796. //
  1797. // if the window receives an Exit menu choice, it will attempt
  1798. // to close down the window
  1799. //
  1800. void
  1801. TWindow::CmExit()
  1802. {
  1803.   if (IsFlagSet(wfMainWindow))
  1804.     CloseWindow();
  1805.  
  1806.   else
  1807.     DefaultProcessing();
  1808. }
  1809.  
  1810. //
  1811. // Handle message posted to us by a control needing assistance in dealing with
  1812. // invalid inputs
  1813. //
  1814. void
  1815. TWindow::EvChildInvalid(HWND hWnd)
  1816. {
  1817.   ::SendMessage(hWnd, WM_CHILDINVALID, 0, 0);
  1818. }
  1819.  
  1820. //----------------------------------------------------------------------------
  1821. // Non-virtuals
  1822. //----------------------------------------------------------------------------
  1823.  
  1824. //$---------------------------------------------------------------------------
  1825. unsigned
  1826. TWindow::NumChildren()
  1827. {
  1828.   return IndexOf(ChildList) + 1;
  1829. }
  1830.  
  1831. //$---------------------------------------------------------------------------
  1832. //
  1833. //
  1834. void
  1835. TWindow::AssignZOrder()
  1836. {
  1837.   TWindow*  wnd;
  1838.   HWND      curWindow = HWindow;
  1839.  
  1840.   if (curWindow) {
  1841.     curWindow = ::GetWindow(curWindow, GW_CHILD);
  1842.  
  1843.     if (curWindow) {
  1844.       int  i = 1;
  1845.  
  1846.       for (curWindow = ::GetWindow(curWindow, GW_HWNDLAST);
  1847.            curWindow;
  1848.            curWindow = ::GetWindow(curWindow, GW_HWNDPREV))
  1849.       {
  1850.         wnd = GetWindowPtr(curWindow);
  1851.  
  1852.         if (wnd)
  1853.           wnd->ZOrder = (uint16)i++;
  1854.       }
  1855.     }
  1856.   }
  1857. }
  1858.  
  1859. //$---------------------------------------------------------------------------
  1860. //
  1861. // The private field ZOrder is used to ensure the Z-order is
  1862. // consistent through read and write of the object.
  1863. //
  1864. // When the object is written, parent->AssignZOrder will fill in this value
  1865. //
  1866. // ZOrder ranges from 1 to N where N is the number of objects and the top one.
  1867. // A ZOrder value of 0 means that the Z-ordering has not be recoreded.
  1868. //
  1869. bool
  1870. TWindow::OrderIsI(TWindow* win, void* position)
  1871. {
  1872.   return win->ZOrder == *(int*)position;
  1873. }
  1874.  
  1875. //$---------------------------------------------------------------------------
  1876. //
  1877. // returns true if the child was supposed to be created but couldn't be
  1878. //
  1879. static bool
  1880. cantCreate(TWindow* win, void*)
  1881. {
  1882.   if (win->HWindow)
  1883.     return false;
  1884.  
  1885.   bool autoCreate = win->IsFlagSet(wfAutoCreate);
  1886.  
  1887.   WARNX(OwlWin, !autoCreate, 0,
  1888.         "Child window(Id=" << win->GetId() << ") not autocreated");
  1889.   if (!autoCreate)
  1890.     return false;
  1891.  
  1892.   //
  1893.   // this call will only fail if a user-defined Create() returns false. Owl's
  1894.   // Creates always throw exceptions.
  1895.   //
  1896.   if (!win->Create())
  1897.     return true;
  1898.  
  1899.   if (win->IsIconic()) {
  1900.     int    textLen = ::GetWindowTextLength(win->HWindow);
  1901.     char*  text = new char[textLen + 1];
  1902.  
  1903.     ::GetWindowText(win->HWindow, text, textLen + 1);
  1904.     ::SetWindowText(win->HWindow, text);
  1905.     delete [] text;
  1906.   }
  1907.   return false;
  1908. }
  1909.  
  1910. //
  1911. // create the children of the object.  returns true if all the windows
  1912. // were successfully created
  1913. //
  1914. bool
  1915. TWindow::CreateChildren()
  1916. {
  1917.   if (FirstThat(cantCreate)) // create children first to restore created order
  1918.     return false;
  1919.   //
  1920.   // restore Z-ordering for children that have Z-ordering recorded
  1921.   //
  1922.   HWND above = HWND_TOP;
  1923.   for (int top = NumChildren(); top; top--) {
  1924.     TWindow* wnd = FirstThat(&TWindow::OrderIsI, &top);
  1925.     if (wnd) {
  1926.       wnd->SetWindowPos(above, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
  1927.       above = wnd->HWindow;
  1928.     }
  1929.   }
  1930.   return true;
  1931. }
  1932.  
  1933. //$---------------------------------------------------------------------------
  1934. //
  1935. // adds the passed pointer to a child window to the linked list
  1936. // of sibling windows which ChildList points to
  1937. //
  1938. void
  1939. TWindow::AddChild(TWindow* child)
  1940. {
  1941.   if (child)
  1942.     if (ChildList) {
  1943.       child->SiblingList = ChildList->SiblingList;
  1944.       ChildList->SiblingList = child;
  1945.       ChildList = child;
  1946.     }
  1947.     else {
  1948.       ChildList = child;
  1949.       child->SiblingList = child;
  1950.     }
  1951. }
  1952.  
  1953. //$---------------------------------------------------------------------------
  1954. //
  1955. // returns a pointer to the TWindow's previous sibling (the
  1956. // window previous to the TWindow in its parent's child window
  1957. // list)
  1958. //
  1959. // returns the sibling which points to the TWindow
  1960. //
  1961. // if the TWindow was the first child added to the list, returns
  1962. // a pointer to the last child added
  1963. //
  1964. TWindow*
  1965. TWindow::Previous()
  1966. {
  1967.   if (!SiblingList)
  1968.     return 0;
  1969.  
  1970.   else {
  1971.     TWindow*  CurrentIndex = this;
  1972.  
  1973.     while (CurrentIndex->Next() != this)
  1974.       CurrentIndex = CurrentIndex->Next();
  1975.  
  1976.     return CurrentIndex;
  1977.   }
  1978. }
  1979.  
  1980. //$---------------------------------------------------------------------------
  1981. //
  1982. // returns a pointer to the first TWindow in the ChildList that meets some
  1983. // specified criteria
  1984. //
  1985. // if no child in the list meets the criteria, 0 is returned
  1986. //
  1987. // the Test parameter which defines the criteria, is a pointer to a
  1988. // function that takes a TWindow pointer & a pointer to void
  1989. //
  1990. // the TWindow* will be used as the pointer to the child window and
  1991. // the void* as a pointer to the Test function's additional parameters
  1992. //
  1993. // the Test function must return a Boolean value indicating whether the
  1994. // child passed meets the criteria
  1995. //
  1996. TWindow*
  1997. TWindow::FirstThat(TCondFunc test, void* paramList)
  1998. {
  1999.   if (ChildList) {
  2000.     TWindow*  nextChild = ChildList->Next();
  2001.     TWindow*  curChild;
  2002.  
  2003.     do {
  2004.       curChild = nextChild;
  2005.       nextChild = nextChild->Next();
  2006.  
  2007.       //
  2008.       // Test curChild for okay
  2009.       //
  2010.       if (test(curChild, paramList))
  2011.         return curChild;
  2012.     } while (curChild != ChildList && ChildList);
  2013.   }
  2014.   return 0;
  2015. }
  2016.  
  2017. //$---------------------------------------------------------------------------
  2018. //
  2019. // iterates over each child window in the TWindow's ChildList,
  2020. // calling the procedure whose pointer is passed as the Action to be
  2021. // performed for each child
  2022. //
  2023. // a pointer to a child is passed as the first parameter to the iteration
  2024. // procedure
  2025. //
  2026. void
  2027. TWindow::ForEach(TActionFunc action, void* paramList)
  2028. {
  2029.   if (ChildList) {
  2030.     TWindow*  curChild;
  2031.     TWindow*  nextChild = ChildList->Next();  // allows ForEach to delete children
  2032.  
  2033.     do {
  2034.       curChild = nextChild;
  2035.       nextChild = nextChild->Next();
  2036.       action(curChild, paramList);
  2037.     } while (curChild != ChildList && ChildList);
  2038.   }
  2039. }
  2040.  
  2041. //$---------------------------------------------------------------------------
  2042. //
  2043. // returns a pointer to the first TWindow in the ChildList that
  2044. // meets some specified criteria
  2045. //
  2046. // if no child in the list meets the criteria, 0 is returned
  2047. //
  2048. // the Test parameter which defines the criteria, is a pointer to a member
  2049. // function (this is how it's different from FirstThat above) that takes a
  2050. // pointer to a TWindow & a pointer to void
  2051. //
  2052. // the TWindow pointer will be used as the pointer to the child window and the
  2053. // void pointer as a pointer to the Test function's additional parameters
  2054. //
  2055. // the Test function must return a Boolean value indicating whether the child
  2056. // passed meets the criteria
  2057. //
  2058. TWindow*
  2059. TWindow::FirstThat(TCondMemFunc test, void* paramList)
  2060. {
  2061.   if (ChildList) {
  2062.     TWindow*  nextChild = ChildList->Next();
  2063.     TWindow*  curChild;
  2064.  
  2065.     do {
  2066.       curChild = nextChild;
  2067.       nextChild = nextChild->Next();
  2068.  
  2069.       if ((this->*test)(curChild, paramList))
  2070.         return curChild;
  2071.     } while (curChild != ChildList && ChildList);
  2072.   }
  2073.   return 0;
  2074. }
  2075.  
  2076. //$---------------------------------------------------------------------------
  2077. //
  2078. // iterates over each child window in the TWindow's ChildList,
  2079. // calling the member function (unlike ForEach above which takes pointer
  2080. // to non-member function) whose pointer is passed as the Action to
  2081. // be performed for each child
  2082. //
  2083. // a pointer to a child is passed as the first parameter to the iteration
  2084. // procedure
  2085. //
  2086. void
  2087. TWindow::ForEach(TActionMemFunc action, void* paramList)
  2088. {
  2089.   if (ChildList) {
  2090.     TWindow*  nextChild = ChildList->Next();
  2091.     TWindow*  curChild;
  2092.  
  2093.     do {
  2094.       curChild = nextChild;
  2095.       nextChild = nextChild->Next();
  2096.       (this->*action)(curChild, paramList);
  2097.     } while (curChild != ChildList && ChildList);
  2098.   }
  2099. }
  2100.  
  2101. //$---------------------------------------------------------------------------
  2102. //
  2103. // returns the position at which the passed child window appears
  2104. // in the TWindow's ChildList
  2105. //
  2106. // if the child does not appear in the list, -1 is returned
  2107. //
  2108. // zero'th position is ChildList->Next
  2109. //
  2110. static int position;
  2111. static bool isItThisChild1(TWindow* win, void* child) {
  2112.   ++position;
  2113.   return win == (TWindow*)child;
  2114. }
  2115.  
  2116. int
  2117. TWindow::IndexOf(TWindow* child)
  2118. {
  2119.   position = -1;
  2120.   return FirstThat(isItThisChild1, child) ? position : -1;
  2121. }
  2122.  
  2123. //$---------------------------------------------------------------------------
  2124. //
  2125. // returns the child at the passed position in the TWindow's
  2126. // ChildList
  2127. //
  2128. // the ChildList is circularly-referent so that passing a position
  2129. // larger than the number of children will cause the traversal of the
  2130. // list to wrap
  2131. //
  2132. // zero'th position is ChildList->Next
  2133. //
  2134. TWindow*
  2135. TWindow::At(int position)
  2136. {
  2137.   if (position == -1)
  2138.     return 0;
  2139.  
  2140.   else {
  2141.     TWindow*  currentChild = ChildList;
  2142.  
  2143.     if (currentChild) {
  2144.       currentChild = ChildList->Next();
  2145.  
  2146.       while (position > 0) {
  2147.         currentChild = currentChild->Next();
  2148.         position--;
  2149.       }
  2150.     }
  2151.     return currentChild;
  2152.   }
  2153. }
  2154.  
  2155. //$---------------------------------------------------------------------------
  2156. //
  2157. // returns a pointer to the window in the ChildList with the passed Id
  2158. //
  2159. // if no child in the list has the passed Id, 0 is returned
  2160. //
  2161. static bool isItThisChild2(TWindow* win, void* id) {
  2162.   return win->GetId() == *(int*)id;
  2163. }
  2164.  
  2165. TWindow*
  2166. TWindow::ChildWithId(int id) const
  2167. {
  2168.   return ((TWindow*)this)->FirstThat(isItThisChild2, &id);
  2169. }
  2170.  
  2171. //$---------------------------------------------------------------------------
  2172. //
  2173. LRESULT
  2174. TWindow::SendMessage(uint msg, WPARAM wParam, LPARAM lParam) {
  2175.   LRESULT result = ::SendMessage(HWindow, msg, wParam, lParam);
  2176.   GetApplication()->ResumeThrow();
  2177.   return result;
  2178. }
  2179.  
  2180. //$---------------------------------------------------------------------------
  2181. //
  2182. LRESULT
  2183. TWindow::ForwardMessage(HWND hWnd, bool send)
  2184. {
  2185.   if (!hWnd)
  2186.     return 0;
  2187.   TCurrentEvent& currentEvent = GetCurrentEvent();
  2188.   if (send) {
  2189.     LRESULT result = ::SendMessage(hWnd, currentEvent.Message,
  2190.                                    currentEvent.WParam,
  2191.                                    currentEvent.LParam);
  2192.     GetApplication()->ResumeThrow();
  2193.     return result;
  2194.   }
  2195.   else
  2196.     return ::PostMessage(hWnd, currentEvent.Message,
  2197.                          currentEvent.WParam,
  2198.                          currentEvent.LParam);
  2199. }
  2200.  
  2201. //$---------------------------------------------------------------------------
  2202. //
  2203. // Forward a message to an Owl window. If send, then bypass windows directly
  2204. // and call the owl window's window proc.
  2205. //
  2206. LRESULT
  2207. TWindow::ForwardMessage(bool send)
  2208. {
  2209.   TCurrentEvent& currentEvent = GetCurrentEvent();
  2210.   if (send)
  2211.     return HandleMessage(currentEvent.Message, currentEvent.WParam,
  2212.                          currentEvent.LParam);
  2213.   return ForwardMessage(HWindow, send);
  2214. }
  2215.  
  2216. //$---------------------------------------------------------------------------
  2217. //
  2218. void
  2219. TWindow::ChildBroadcastMessage(uint msg, WPARAM wParam, LPARAM lParam)
  2220. {
  2221.   for (HWND hWndChild = GetWindow(GW_CHILD); hWndChild; ) {
  2222.     HWND hWndNext = ::GetWindow(hWndChild, GW_HWNDNEXT);
  2223.     ::SendMessage(hWndChild, msg, wParam, lParam);
  2224.     GetApplication()->ResumeThrow();
  2225.     hWndChild = hWndNext;
  2226.   }
  2227. }
  2228.  
  2229. //$---------------------------------------------------------------------------
  2230. //
  2231. // destroys the associated MS-Windows interface element and deletes the C++
  2232. // object unconditionally (without calling CanClose())
  2233. //
  2234. // This function is static to avoid side effects of deleting 'this'.
  2235. //
  2236. void
  2237. TWindow::ShutDownWindow(TWindow* win, int retVal)
  2238. {
  2239.   win->Destroy(retVal);
  2240.   delete win;
  2241. }
  2242.  
  2243. //$---------------------------------------------------------------------------
  2244. //
  2245. // displays the TWindow, after checking that it has a valid handle
  2246. //
  2247. void
  2248. TWindow::Show(int cmdShow)
  2249. {
  2250.   //
  2251.   // if the window is being minimzed send a WM_SYSCOMMAND; this way the
  2252.   // frame window focus saving works properly
  2253.   //
  2254.   if (HWindow)
  2255.     if (cmdShow == SW_MINIMIZE)
  2256.       HandleMessage(WM_SYSCOMMAND, SC_MINIMIZE);
  2257.  
  2258.     else
  2259.       ::ShowWindow(HWindow, cmdShow);
  2260. }
  2261.  
  2262. //$---------------------------------------------------------------------------
  2263. //
  2264. // sets the Title and caption of the TWindow
  2265. //
  2266. void
  2267. TWindow::SetCaption(const char far* title)
  2268. {
  2269.   if (Title != title) {
  2270.     if (HIWORD(Title))
  2271.       delete [] Title;
  2272.  
  2273.     Title = strnewdup(title);
  2274.   }
  2275.  
  2276.   if (HWindow)
  2277.     ::SetWindowText(HWindow, Title);
  2278. }
  2279.  
  2280. //$---------------------------------------------------------------------------
  2281. //
  2282. // gets the Title member var from the current window caption or text
  2283. //
  2284. void
  2285. TWindow::GetWindowTextTitle()
  2286. {
  2287.   if (LOWORD(Title) == 0xFFFF)  // ignore "don't-change" titles
  2288.     return;
  2289.  
  2290.   if (HIWORD(Title))
  2291.     delete [] Title;
  2292.  
  2293.   int titleLength = GetWindowTextLength();
  2294.   if (titleLength < 0)
  2295.     Title = strnewdup((const char far*)"");
  2296.  
  2297.   else {
  2298.     Title = new far char[titleLength + 1];
  2299.     Title[0] = 0;
  2300.     Title[titleLength] = 0;
  2301.     GetWindowText(Title, titleLength + 1);
  2302.   }
  2303. }
  2304.  
  2305. //$---------------------------------------------------------------------------
  2306. //
  2307. // copy over the styles, the coordinates & the id from the existing HWnd into
  2308. // the owl TWindow members.
  2309. // Note: the title is not copied here
  2310. //
  2311. void
  2312. TWindow::GetHWndState()
  2313. {
  2314.   //
  2315.   // retrieve Attr.Style and Attr.ExStyle
  2316.   //
  2317.   // NOTE: some windows controls (e.g. EDIT) change the style bits
  2318.   // (e.g. WS_BORDER) from their original values.  if we always reset
  2319.   // Attr.Style and Attr.ExStyle by extracting their values from
  2320.   // Windows, we will lose some of the style bits we supplied
  2321.   // in the CreateWindowEx call.  in the case of the ResourceId
  2322.   // constructors, of course, we must retrieve these values.
  2323.   //
  2324.   if (IsFlagSet(wfFromResource)) {
  2325.     Attr.Style = GetWindowLong(GWL_STYLE);
  2326.     Attr.ExStyle = GetWindowLong(GWL_EXSTYLE);
  2327.   }
  2328.  
  2329.   //
  2330.   // retrieve Attr.X, Attr.Y, Attr.W and Attr.H
  2331.   //
  2332.   TRect  wndRect;
  2333.  
  2334.   GetWindowRect(wndRect);
  2335.   Attr.H = wndRect.Height();
  2336.   Attr.W = wndRect.Width();
  2337.  
  2338.   HWND      hParent = GetParent();
  2339.   if ((Attr.Style & WS_CHILD) && hParent)
  2340.     ::ScreenToClient(hParent, &wndRect.TopLeft());
  2341.  
  2342.   Attr.X = wndRect.left;
  2343.   Attr.Y = wndRect.top;
  2344.  
  2345. #if defined(BI_PLAT_WIN16)
  2346.   Attr.Id = GetWindowWord(GWW_ID);
  2347. #else
  2348.   Attr.Id = GetWindowLong(GWL_ID);
  2349. #endif
  2350. }
  2351.  
  2352. //$---------------------------------------------------------------------------
  2353. //
  2354. // Set the cursor for this window given a TModule and a resId
  2355. // Updates the current cursor if it is over this window.
  2356. //
  2357. bool
  2358. TWindow::SetCursor(TModule* module, TResId resId)
  2359. {
  2360.   if (module == CursorModule && resId == CursorResId)
  2361.     return false;
  2362.  
  2363.   HCURSOR hOldCursor = (HCursor && CursorModule) ? HCursor : 0;
  2364.  
  2365.   CursorModule = module;
  2366.   CursorResId = resId;
  2367.   if (CursorResId)
  2368.     if (CursorModule)
  2369.       HCursor = CursorModule->LoadCursor(CursorResId);
  2370.     else
  2371.       HCursor = ::LoadCursor(0, CursorResId);
  2372.   else
  2373.     HCursor = 0;
  2374.  
  2375.   //
  2376.   // If the cursor is in our client window then set it now
  2377.   //
  2378.   if (HWindow) {
  2379.     TPoint p;
  2380.     GetCursorPos(p);
  2381.     ScreenToClient(p);
  2382.     if (GetClientRect().Contains(p))
  2383.       ::SetCursor(HCursor);
  2384.   }
  2385.  
  2386.   //
  2387.   // Destroy old cursor if there was one & it was not loaded from USER
  2388.   //
  2389.   if (hOldCursor)
  2390.     ::DestroyCursor(hOldCursor);
  2391.   return true;
  2392. }
  2393.  
  2394. //
  2395. // Handle WM_INITMENUPOPUP while embeded to generate command enable messages
  2396. // for our server menu items. Very similar to TFrameWindow::EvInitMenuPopup;
  2397. // could rearrange code to share better.
  2398. //
  2399. void
  2400. TWindow::EvInitMenuPopup(HMENU hPopupMenu, uint /*index*/, bool sysMenu)
  2401. {
  2402.   if (!sysMenu && hPopupMenu) {
  2403.     const int count = ::GetMenuItemCount(hPopupMenu);
  2404.  
  2405.     for (int pos = 0; pos < count; pos++) {
  2406.       uint  id;
  2407.  
  2408.       if (hPopupMenu == GetMenu()) // top level menu
  2409.         id = ::GetMenuItemID(hPopupMenu, pos);
  2410.  
  2411.       else {
  2412.         // For second level and below menus, return the implied id for popup
  2413.         // sub-menus. The implied id for a sub-menu is the id of the first item
  2414.         // in the popup sub-menu less 1. If there are more than one level of
  2415.         // popup menus, it will recursively look into those sub-menus as well.
  2416.         //
  2417.         TMenu popupMenu(hPopupMenu);
  2418.         id = popupMenu.GetMenuItemID(pos);
  2419.       }
  2420.  
  2421.       // ignore separators
  2422.       //
  2423.       if (id == 0 || id == uint(-1))
  2424.         continue;
  2425.  
  2426.       // Skip the rest if it is the mdi child list, or system commands
  2427.       //
  2428.       if (id == IDW_FIRSTMDICHILD || id > 0xF000)
  2429.         break;
  2430.  
  2431.       EvCommandEnable(TMenuItemEnabler(hPopupMenu, id, HWindow, pos));
  2432.     }
  2433.   }
  2434. }
  2435.  
  2436. //$---------------------------------------------------------------------------
  2437. //
  2438. // not inline to avoid requiring gdiobjec.h by window.h
  2439. //
  2440. bool
  2441. TWindow::GetUpdateRgn(TRegion& region, bool erase) const
  2442. {
  2443.   return ::GetUpdateRgn(HWindow, region, erase);
  2444. }
  2445.  
  2446. //$---------------------------------------------------------------------------
  2447. //
  2448. //
  2449. int
  2450. TWindow::MessageBox(const char far* text,
  2451.                     const char far* caption,
  2452.                     uint            type)
  2453. {
  2454.   if (GetApplication()->BWCCEnabled()) {
  2455.     static bool FAR PASCAL(*bwccMessageBox)(HWND, LPCSTR, LPCSTR, uint);
  2456.     if (!bwccMessageBox)
  2457.       (FARPROC)bwccMessageBox =
  2458.         GetApplication()->GetBWCCModule()->GetProcAddress("BWCCMessageBox");
  2459.     if (bwccMessageBox)
  2460.       return (*bwccMessageBox)(HWindow, text, caption, type);
  2461.     return 0;
  2462.   }
  2463.   else {
  2464.     GetApplication()->EnableCtl3dAutosubclass(true);
  2465.     int retValue = ::MessageBox(HWindow, text, caption, type);
  2466.     GetApplication()->EnableCtl3dAutosubclass(false);
  2467.     return retValue;
  2468.   }
  2469. }
  2470.  
  2471. #if defined(__TRACE) || defined(__WARN)
  2472.   ostream& operator <<(ostream& os, const TWindow& w)
  2473.   {
  2474.     os << '(';
  2475. #if !defined(BI_NO_RTTI)
  2476.       os << typeid(w).name() << ',';
  2477. #endif
  2478.     os << "0x" << hex << (unsigned)w.HWindow << ',';
  2479.     if (!w.Parent)
  2480.       os << '\'' << TResId(w.Title) << '\'';
  2481.     else
  2482.       os << "id=" << w.GetId();
  2483.     os << ')';
  2484.     return os;
  2485.   }
  2486. #endif
  2487.  
  2488. //$---------------------------------------------------------------------------
  2489. //
  2490. // static member function to construct base TXOwl object
  2491. //
  2492. string
  2493. TWindow::TXWindow::Msg(TWindow* win, uint resId)
  2494. {
  2495.   bool   found;  // did we locate the string
  2496.   string rscMsg = ResourceIdToString(&found, resId);
  2497.  
  2498.   if (found) {
  2499.     char buf[255];
  2500.  
  2501.     //
  2502.     // supply title of window if we have a valid window
  2503.     //
  2504.     wsprintf(buf, rscMsg.c_str(), win && HIWORD(win->Title) ? win->Title : "");
  2505.     return string(buf);
  2506.   }
  2507.   else
  2508.     return rscMsg;
  2509. }
  2510.  
  2511. //$---------------------------------------------------------------------------
  2512. //
  2513. // InvalidWindow exception constructor
  2514. //
  2515. TWindow::TXWindow::TXWindow(TWindow* win, uint resId)
  2516.   : TXOwl(Msg(win, resId), resId)
  2517. {
  2518.   Window = win;
  2519. }
  2520.  
  2521. TWindow::TXWindow::TXWindow(const TXWindow& src) : TXOwl(src),Window(src.Window)
  2522. {
  2523. }
  2524.  
  2525. int
  2526. TWindow::TXWindow::Unhandled(TModule* app, unsigned promptResId)
  2527. {
  2528.   delete Window;
  2529.   Window = 0;
  2530.   return TXOwl::Unhandled(app, promptResId);
  2531. }
  2532.  
  2533. TXOwl*
  2534. TWindow::TXWindow::Clone()
  2535. {
  2536.   return new TXWindow(*this);
  2537. }
  2538.